diff --git a/backend/src/bin/generate_types.rs b/backend/src/bin/generate_types.rs index e980ff84..ef01140d 100644 --- a/backend/src/bin/generate_types.rs +++ b/backend/src/bin/generate_types.rs @@ -107,6 +107,7 @@ fn main() { vibe_kanban::models::task_attempt_activity::TaskAttemptActivityWithPrompt::decl(), vibe_kanban::models::task_attempt_activity::CreateTaskAttemptActivity::decl(), vibe_kanban::routes::filesystem::DirectoryEntry::decl(), + vibe_kanban::routes::filesystem::DirectoryListResponse::decl(), vibe_kanban::models::task_attempt::DiffChunkType::decl(), vibe_kanban::models::task_attempt::DiffChunk::decl(), vibe_kanban::models::task_attempt::FileDiff::decl(), @@ -127,6 +128,7 @@ fn main() { vibe_kanban::executor::NormalizedEntry::decl(), vibe_kanban::executor::NormalizedEntryType::decl(), vibe_kanban::executor::ActionType::decl(), + vibe_kanban::routes::auth::DeviceStartResponse::decl(), ]; // 4. Friendly banner diff --git a/backend/src/routes/auth.rs b/backend/src/routes/auth.rs index 78631a3b..89909b61 100644 --- a/backend/src/routes/auth.rs +++ b/backend/src/routes/auth.rs @@ -18,13 +18,14 @@ pub fn auth_router() -> Router { #[derive(serde::Deserialize)] struct DeviceStartRequest {} -#[derive(serde::Serialize)] -struct DeviceStartResponse { - device_code: String, - user_code: String, - verification_uri: String, - expires_in: u64, - interval: u64, +#[derive(serde::Serialize, ts_rs::TS)] +#[ts(export)] +pub struct DeviceStartResponse { + pub device_code: String, + pub user_code: String, + pub verification_uri: String, + pub expires_in: u32, + pub interval: u32, } #[derive(serde::Deserialize)] @@ -84,8 +85,8 @@ async fn device_start() -> ResponseJson> { device_code: device_code.to_string(), user_code: user_code.to_string(), verification_uri: verification_uri.to_string(), - expires_in, - interval, + expires_in: expires_in.try_into().unwrap_or(600), + interval: interval.try_into().unwrap_or(5), }), message: None, }) diff --git a/frontend/src/components/GitHubLoginDialog.tsx b/frontend/src/components/GitHubLoginDialog.tsx index 0c414333..8c6f23f2 100644 --- a/frontend/src/components/GitHubLoginDialog.tsx +++ b/frontend/src/components/GitHubLoginDialog.tsx @@ -12,7 +12,7 @@ import { useConfig } from './config-provider'; import { Check, Clipboard } from 'lucide-react'; import { Loader } from './ui/loader'; import { githubAuthApi } from '../lib/api'; -import { StartGitHubDeviceFlowType } from 'shared/types.ts'; +import { DeviceStartResponse } from 'shared/types.ts'; export function GitHubLoginDialog({ open, @@ -24,8 +24,9 @@ export function GitHubLoginDialog({ const { config, loading, githubTokenInvalid } = useConfig(); const [fetching, setFetching] = useState(false); const [error, setError] = useState(null); - const [deviceState, setDeviceState] = - useState(null); + const [deviceState, setDeviceState] = useState( + null + ); const [polling, setPolling] = useState(false); const [copied, setCopied] = useState(false); diff --git a/frontend/src/components/context/TaskDetailsContextProvider.tsx b/frontend/src/components/context/TaskDetailsContextProvider.tsx index 68a83f00..5ae4392f 100644 --- a/frontend/src/components/context/TaskDetailsContextProvider.tsx +++ b/frontend/src/components/context/TaskDetailsContextProvider.tsx @@ -10,7 +10,6 @@ import { useState, } from 'react'; import type { - AttemptData, EditorType, ExecutionProcess, TaskAttempt, @@ -18,6 +17,7 @@ import type { TaskWithAttemptStatus, WorktreeDiff, } from 'shared/types.ts'; +import type { AttemptData } from './taskDetailsContext'; import { attemptsApi, executionProcessesApi } from '@/lib/api.ts'; import { TaskAttemptDataContext, diff --git a/frontend/src/components/context/taskDetailsContext.ts b/frontend/src/components/context/taskDetailsContext.ts index 96aff9b6..25c3b60d 100644 --- a/frontend/src/components/context/taskDetailsContext.ts +++ b/frontend/src/components/context/taskDetailsContext.ts @@ -1,13 +1,22 @@ import { createContext, Dispatch, SetStateAction } from 'react'; import type { - AttemptData, EditorType, TaskAttempt, TaskAttemptState, TaskWithAttemptStatus, WorktreeDiff, + TaskAttemptActivityWithPrompt, + ExecutionProcessSummary, + ExecutionProcess, } from 'shared/types.ts'; +// Frontend-only type for combining attempt data +export interface AttemptData { + activities: TaskAttemptActivityWithPrompt[]; + processes: ExecutionProcessSummary[]; + runningProcessDetails: Record; +} + export interface TaskDetailsContextValue { task: TaskWithAttemptStatus; projectId: string; diff --git a/frontend/src/components/tasks/TaskDetails/DiffChunkSection.tsx b/frontend/src/components/tasks/TaskDetails/DiffChunkSection.tsx index 2a793e1a..3aabc65a 100644 --- a/frontend/src/components/tasks/TaskDetails/DiffChunkSection.tsx +++ b/frontend/src/components/tasks/TaskDetails/DiffChunkSection.tsx @@ -1,6 +1,7 @@ import { Button } from '@/components/ui/button.tsx'; import { ChevronDown, ChevronUp } from 'lucide-react'; -import type { DiffChunkType, ProcessedSection } from 'shared/types.ts'; +import type { DiffChunkType } from 'shared/types.ts'; +import type { ProcessedSection } from './DiffFile'; import { Dispatch, SetStateAction } from 'react'; type Props = { diff --git a/frontend/src/components/tasks/TaskDetails/DiffFile.tsx b/frontend/src/components/tasks/TaskDetails/DiffFile.tsx index 90bd88ce..ecb8ec76 100644 --- a/frontend/src/components/tasks/TaskDetails/DiffFile.tsx +++ b/frontend/src/components/tasks/TaskDetails/DiffFile.tsx @@ -1,11 +1,23 @@ import { Button } from '@/components/ui/button.tsx'; import { ChevronDown, ChevronUp, Trash2 } from 'lucide-react'; import DiffChunkSection from '@/components/tasks/TaskDetails/DiffChunkSection.tsx'; -import { - FileDiff, - type ProcessedLine, - type ProcessedSection, -} from 'shared/types.ts'; +import { FileDiff, DiffChunkType } from 'shared/types.ts'; + +// Types for processing diff content in the frontend +export interface ProcessedLine { + content: string; + chunkType: DiffChunkType; + oldLineNumber?: number; + newLineNumber?: number; +} + +export interface ProcessedSection { + type: 'context' | 'change' | 'expanded'; + lines: ProcessedLine[]; + expandKey?: string; + expandedAbove?: boolean; + expandedBelow?: boolean; +} import { Dispatch, SetStateAction, @@ -73,6 +85,8 @@ function DiffFile({ const processedLine: ProcessedLine = { content: line, chunkType: chunk.chunk_type, + oldLineNumber: undefined, + newLineNumber: undefined, }; switch (chunk.chunk_type) { @@ -121,12 +135,18 @@ function DiffFile({ sections.push({ type: 'context', lines: lines.slice(i, nextChangeIndex), + expandKey: undefined, + expandedAbove: undefined, + expandedBelow: undefined, }); } else { if (hasPrevChange) { sections.push({ type: 'context', lines: lines.slice(i, i + CONTEXT_LINES), + expandKey: undefined, + expandedAbove: undefined, + expandedBelow: undefined, }); i += CONTEXT_LINES; } @@ -144,12 +164,16 @@ function DiffFile({ type: 'expanded', lines: lines.slice(expandStart, expandEnd), expandKey, + expandedAbove: undefined, + expandedBelow: undefined, }); } else { sections.push({ type: 'context', lines: [], expandKey, + expandedAbove: undefined, + expandedBelow: undefined, }); } } @@ -160,11 +184,17 @@ function DiffFile({ nextChangeIndex - CONTEXT_LINES, nextChangeIndex ), + expandKey: undefined, + expandedAbove: undefined, + expandedBelow: undefined, }); } else if (!hasPrevChange) { sections.push({ type: 'context', lines: lines.slice(i, i + CONTEXT_LINES), + expandKey: undefined, + expandedAbove: undefined, + expandedBelow: undefined, }); } } @@ -179,6 +209,9 @@ function DiffFile({ sections.push({ type: 'change', lines: lines.slice(changeStart, i), + expandKey: undefined, + expandedAbove: undefined, + expandedBelow: undefined, }); } } diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 0ea7744a..5918d506 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -15,7 +15,7 @@ import { NormalizedConversation, Project, ProjectWithBranch, - StartGitHubDeviceFlowType, + DeviceStartResponse, Task, TaskAttempt, TaskAttemptActivityWithPrompt, @@ -514,11 +514,11 @@ export const githubAuthApi = { return undefined; } }, - start: async (): Promise => { + start: async (): Promise => { const response = await makeRequest('/api/auth/github/device/start', { method: 'POST', }); - return handleApiResponse(response); + return handleApiResponse(response); }, poll: async (device_code: string): Promise => { const response = await makeRequest('/api/auth/github/device/poll', { diff --git a/shared/types.ts b/shared/types.ts index 0801b0c7..fb0c2968 100644 --- a/shared/types.ts +++ b/shared/types.ts @@ -70,29 +70,10 @@ export type TaskAttemptActivityWithPrompt = { id: string, execution_process_id: export type CreateTaskAttemptActivity = { execution_process_id: string, status: TaskAttemptStatus | null, note: string | null, }; -export type AttemptData = { - activities: TaskAttemptActivityWithPrompt[]; - processes: ExecutionProcessSummary[]; - runningProcessDetails: Record; -} - -export interface ProcessedLine { - content: string; - chunkType: DiffChunkType; - oldLineNumber?: number; - newLineNumber?: number; -} - -export interface ProcessedSection { - type: 'context' | 'change' | 'expanded'; - lines: ProcessedLine[]; - expandKey?: string; - expandedAbove?: boolean; - expandedBelow?: boolean; -} - export type DirectoryEntry = { name: string, path: string, is_directory: boolean, is_git_repo: boolean, }; +export type DirectoryListResponse = { entries: Array, current_path: string, }; + export type DiffChunkType = "Equal" | "Insert" | "Delete"; export type DiffChunk = { chunk_type: DiffChunkType, content: string, }; @@ -133,65 +114,59 @@ export type NormalizedEntryType = { "type": "user_message" } | { "type": "assist export type ActionType = { "action": "file_read", path: string, } | { "action": "file_write", path: string, } | { "action": "command_run", command: string, } | { "action": "search", query: string, } | { "action": "web_fetch", url: string, } | { "action": "task_create", description: string, } | { "action": "other", description: string, }; -export type StartGitHubDeviceFlowType = { - device_code: string; - user_code: string; - verification_uri: string; - expires_in: number; - interval: number; -}; +export type DeviceStartResponse = { device_code: string, user_code: string, verification_uri: string, expires_in: number, interval: number, }; // Generated constants export const EXECUTOR_TYPES: string[] = [ - "echo", - "claude", - "amp", - "gemini", - "charmopencode" + "echo", + "claude", + "amp", + "gemini", + "charmopencode" ]; export const EDITOR_TYPES: EditorType[] = [ - "vscode", - "cursor", - "windsurf", - "intellij", - "zed", - "custom" + "vscode", + "cursor", + "windsurf", + "intellij", + "zed", + "custom" ]; export const EXECUTOR_LABELS: Record = { - "echo": "Echo (Test Mode)", - "claude": "Claude", - "amp": "Amp", - "gemini": "Gemini", - "charmopencode": "Charm Opencode" + "echo": "Echo (Test Mode)", + "claude": "Claude", + "amp": "Amp", + "gemini": "Gemini", + "charmopencode": "Charm Opencode" }; export const EDITOR_LABELS: Record = { - "vscode": "VS Code", - "cursor": "Cursor", - "windsurf": "Windsurf", - "intellij": "IntelliJ IDEA", - "zed": "Zed", - "custom": "Custom" + "vscode": "VS Code", + "cursor": "Cursor", + "windsurf": "Windsurf", + "intellij": "IntelliJ IDEA", + "zed": "Zed", + "custom": "Custom" }; export const SOUND_FILES: SoundFile[] = [ - "abstract-sound1", - "abstract-sound2", - "abstract-sound3", - "abstract-sound4", - "cow-mooing", - "phone-vibration", - "rooster" + "abstract-sound1", + "abstract-sound2", + "abstract-sound3", + "abstract-sound4", + "cow-mooing", + "phone-vibration", + "rooster" ]; export const SOUND_LABELS: Record = { - "abstract-sound1": "Gentle Chime", - "abstract-sound2": "Soft Bell", - "abstract-sound3": "Digital Tone", - "abstract-sound4": "Subtle Alert", - "cow-mooing": "Cow Mooing", - "phone-vibration": "Phone Vibration", - "rooster": "Rooster Call" -}; + "abstract-sound1": "Gentle Chime", + "abstract-sound2": "Soft Bell", + "abstract-sound3": "Digital Tone", + "abstract-sound4": "Subtle Alert", + "cow-mooing": "Cow Mooing", + "phone-vibration": "Phone Vibration", + "rooster": "Rooster Call" +}; \ No newline at end of file