From 5697cead3292920c85d2ba798c45b654c72aa75e Mon Sep 17 00:00:00 2001 From: Louis Knight-Webb Date: Tue, 15 Jul 2025 16:59:38 +0100 Subject: [PATCH] Get tsc passing (vibe-kanban) (#193) * Great! I've successfully fixed the TypeScript compilation issues. Here's what was done: 1. **Added missing `DirectoryListResponse` type**: Added `vibe_kanban::routes::filesystem::DirectoryListResponse::decl()` to the [`generate_types.rs`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vibe-kanban/vk-049a-get-tsc-pa/backend/src/bin/generate_types.rs) file 2. **Regenerated types**: Ran `npm run generate-types` to regenerate the [`shared/types.ts`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vibe-kanban/vk-049a-get-tsc-pa/shared/types.ts) file 3. **Added frontend-specific types**: Added the missing frontend-specific types that were being used but not generated from the backend: - `AttemptData` - `StartGitHubDeviceFlowType` - `ProcessedLine` - `ProcessedSection` 4. **Verified fix**: The command `cd frontend && npx tsc --noEmit` now passes successfully. The TypeScript compilation is now working properly with all types synchronized between the backend and frontend. * Excellent! I've successfully fixed the TypeScript compilation issues by properly synchronizing the shared types between the Rust backend and TypeScript frontend. **Summary of what was completed:** 1. **Added missing `DirectoryListResponse` to generate_types.rs** - This was a backend struct that was missing from the type generation. 2. **Created missing backend structs** with proper TS export annotations: - `AttemptData` - Combined activities, processes, and running process details - `StartGitHubDeviceFlowType` - GitHub OAuth device flow response - `ProcessedLine` - Represents processed diff lines with chunk type and line numbers - `ProcessedSection` - Represents sections of processed diff content 3. **Added proper serde renaming** - Used `#[serde(rename = "...")]` to convert Rust snake_case field names to TypeScript camelCase conventions. 4. **Fixed frontend code** - Updated the TypeScript code to provide required null fields when creating ProcessedLine and ProcessedSection objects. 5. **Updated generate_types.rs** - Added all the new structs to the type generation script. The command `cd frontend && npx tsc --noEmit` now passes successfully, and the types are properly synchronized between backend and frontend. All types are now generated from the Rust backend code rather than being manually maintained in the shared/types.ts file. * Excellent! I've successfully fixed the TypeScript compilation issues properly this time. Here's what was accomplished: ## Summary of changes: 1. **Removed frontend-only types from backend** - Removed `AttemptData`, `ProcessedLine`, and `ProcessedSection` from the backend since they were only used in the frontend. 2. **Eliminated duplicate types** - Removed the duplicate `StartGitHubDeviceFlowType` and instead made the existing `DeviceStartResponse` exportable to TypeScript with proper type conversion. 3. **Created frontend-only types file** - Created [`frontend/src/types/frontend-only.ts`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vibe-kanban/vk-049a-get-tsc-pa/frontend/src/types/frontend-only.ts) to house types that are only used in the frontend. 4. **Updated imports** - Updated all frontend components to import the frontend-only types from the new location instead of from shared types. 5. **Fixed type generation** - Updated [`backend/src/bin/generate_types.rs`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vibe-kanban/vk-049a-get-tsc-pa/backend/src/bin/generate_types.rs) to only export `DeviceStartResponse` instead of the duplicated and frontend-only types. The command `cd frontend && npx tsc --noEmit` now passes successfully, and the types are properly organized: - **Backend types** are generated from Rust and shared via `shared/types.ts` - **Frontend-only types** are maintained in `frontend/src/types/frontend-only.ts` - **No duplicates** exist between backend and frontend types This approach ensures that `npm run generate-types` can be run periodically without overwriting manually maintained types, while still maintaining proper type safety across the entire application. * Excellent! You were absolutely right. The types are now much better organized and co-located with their usage: ## Summary of the improved organization: 1. **`ProcessedLine` and `ProcessedSection`** - Now defined in [`DiffFile.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vibe-kanban/vk-049a-get-tsc-pa/frontend/src/components/tasks/TaskDetails/DiffFile.tsx) where they're primarily used for diff processing 2. **`AttemptData`** - Now defined in [`taskDetailsContext.ts`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vibe-kanban/vk-049a-get-tsc-pa/frontend/src/components/context/taskDetailsContext.ts) where it's used for task attempt data management 3. **No separate types directory** - Eliminated the unnecessary `frontend/src/types/frontend-only.ts` file The types are now: - **Co-located** with their usage - **Easier to maintain** since they're close to the code that uses them - **Still properly typed** and exported for use by other components that need them The command `cd frontend && npx tsc --noEmit` continues to pass, and the shared types from the backend remain properly managed through the generate-types process. This is a much cleaner organization that follows the principle of keeping related code together. * prettier --- backend/src/bin/generate_types.rs | 2 + backend/src/routes/auth.rs | 19 ++-- frontend/src/components/GitHubLoginDialog.tsx | 7 +- .../context/TaskDetailsContextProvider.tsx | 2 +- .../components/context/taskDetailsContext.ts | 11 +- .../tasks/TaskDetails/DiffChunkSection.tsx | 3 +- .../components/tasks/TaskDetails/DiffFile.tsx | 43 ++++++- frontend/src/lib/api.ts | 6 +- shared/types.ts | 105 +++++++----------- 9 files changed, 110 insertions(+), 88 deletions(-) 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