Files
vibe-kanban/frontend/src/components/ui-new/NewDisplayConversationEntry.tsx

741 lines
18 KiB
TypeScript
Raw Normal View History

2026-01-08 22:14:38 +00:00
import { useMemo, useCallback, useLayoutEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import type { TFunction } from 'i18next';
import {
ActionType,
NormalizedEntry,
ToolStatus,
TodoItem,
type TaskWithAttemptStatus,
Script Fixer Dialog (Vibe Kanban) (#1968) * All checks pass. Let me provide a summary of what was implemented: ## Summary I've implemented the Script Fixer Dialog feature. Here's what was created and modified: ### New File Created: 1. **`frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx`** - A new dialog component that: - Shows the current script content in an editable textarea - Displays the last execution logs using `VirtualizedProcessLogs` - Provides "Save" and "Save and Test" buttons - Supports both setup scripts and dev server scripts - Handles multi-repo scenarios with a repo selector dropdown ### Files Modified: **New UI (Setup Script):** 1. **`ChatScriptEntry.tsx`** - Added `onFix` prop and a "Fix Script" button that appears when a script fails 2. **`NewDisplayConversationEntry.tsx`** - Added `ScriptEntryWithFix` wrapper component that provides the fix handler to `ChatScriptEntry` **Legacy UI (Setup Script):** 3. **`DisplayConversationEntry.tsx`** - Added `ScriptFixButton` component that renders a fix button for failed scripts in the legacy conversation view **Preview/Dev Server:** 4. **`PreviewPanel.tsx`** - Added "Fix Script" button to the troubleshooting alert when dev server has issues 5. **`PreviewBrowser.tsx`** - Added optional `handleFixDevScript` prop and "Fix Script" button alongside "Start Server" 6. **`PreviewBrowserContainer.tsx`** - Wired up the dialog opening for dev server script fixes 7. **`PreviewControls.tsx`** - Added `onFixScript` prop and "Fix Script" button in the dev server controls 8. **`PreviewControlsContainer.tsx`** - Wired up the dialog opening for the controls **Translations:** 9. **`tasks.json` (English)** - Added `scriptFixer` translation keys * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * Type check passes. I've updated the `ScriptFixerDialog` to also start the dev server when "Save and Test" is clicked for dev_server script type. * Done! I've updated the `ScriptFixerDialog` to: 1. **Keep the dialog open after "Save and Test"** - The dialog no longer closes after clicking "Save and Test", so the user can watch the logs update in real-time as the script executes 2. **Changed "Cancel" to "Close"** - The button now says "Close" (using `t('common:close')`) and is always enabled, making it clear the user can close the dialog at any time 3. **Logs update automatically** - The `useExecutionProcesses` and `useLogStream` hooks will automatically pick up the new execution process and stream its logs in real-time * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * Done! I've fixed the horizontal overflow by adding `min-w-0` to all flex containers and `overflow-hidden` to the `DialogContent`. This prevents flex children from overflowing their container horizontally. * The type check passes. The fix has been applied - the width constraints (`max-w-4xl w-[90vw]`) are now on the `Dialog` component instead of `DialogContent`. This will override the default `max-w-xl` from the Dialog component's base className, allowing the dialog to be wider and preventing horizontal overflow. * Done! I've added the status indicator to the ScriptFixerDialog. Here's what was implemented: **Changes made:** 1. **`ScriptFixerDialog.tsx`:** - Added import for `RunningDots` component - Added status computation logic to determine if the process is running, succeeded, failed, or killed - Added status indicator UI next to the "Last Execution Logs" label showing: - Animated dots + "Running..." when the script is executing - Green dot + "Completed successfully" when script succeeded (exit code 0) - Red dot + "Failed with exit code X" when script failed - Gray dot + "Process was killed" when killed 2. **`tasks.json` (translations):** - Added `statusRunning`, `statusSuccess`, `statusFailed`, and `statusKilled` translation keys The status indicator will update in real-time as the script executes since it's computed from `latestProcess` which is updated via the `useExecutionProcesses` hook. * Fixed! The issue was that `exit_code` can be `null` when the process completes successfully, and comparing `null !== BigInt(0)` returns `true`, which incorrectly triggered the error state. The fix: - Treat `exit_code === null` as success (same as exit code 0) - Only show error when `exit_code` is explicitly a non-zero value * Fixed. The issue was that `exit_code` comes back as a regular number `0` from the API, but the TypeScript type says `bigint`. Using `Number(exitCode) === 0` handles both cases correctly and satisfies the type checker. * Fixed! The translation key was missing the `buttons.` namespace prefix. It now uses `t('common:buttons.close')` which maps to "Close". * Done. Added `bg-error` and `text-error` classes alongside the `destructive` classes for the error state. * styles * Done! I've fixed the issue by passing `sessionId` to the `ScriptFixerDialog` in all three files: 1. **`PreviewPanel.tsx`** - Gets `sessionId` from `devServerProcesses[0]?.session_id` 2. **`PreviewBrowserContainer.tsx`** - Added `devServerProcesses` to destructuring from `usePreviewDevServer` and passes `sessionId` 3. **`PreviewControlsContainer.tsx`** - Already had `devServerProcesses`, just added `sessionId` to the dialog call Now when you click "Save and Test" for a dev server script, the dialog will receive the session ID, connect to the WebSocket stream, and show the execution logs in real-time. * rollback trigger the ScriptFixer for old UI * Done! I've extended the ScriptFixerDialog to support cleanup scripts. Here's a summary of the changes: **1. `frontend/src/i18n/locales/en/tasks.json`** - Added `cleanupScriptTitle: "Fix Cleanup Script"` translation key **2. `frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx`** - Updated `ScriptType` from `'setup' | 'dev_server'` to `'setup' | 'cleanup' | 'dev_server'` - Updated `latestProcess` filter to use `'cleanupscript'` run_reason for cleanup scripts - Updated script fetching logic to read `cleanup_script` field - Updated both `handleSave` and `handleSaveAndTest` to save `cleanup_script` field - Updated execution logic to call `attemptsApi.runCleanupScript(workspaceId)` for cleanup scripts - Updated dialog title to show "Fix Cleanup Script" for cleanup type - Updated placeholder text to show `#!/bin/bash\nrm -rf node_modules` for cleanup scripts **3. `frontend/src/components/ui-new/NewDisplayConversationEntry.tsx`** - Fixed the scriptType mapping so "Cleanup Script" maps to `'cleanup'` instead of incorrectly mapping to `'setup'` * All tasks are complete. Here's a summary of the changes I made: ## Summary I've implemented the ScriptFixerDialog in the old UI for both `DisplayConversationEntry.tsx` and `PreviewPanel.tsx`. ### Changes Made: **1. `DisplayConversationEntry.tsx`** (`frontend/src/components/NormalizedConversation/DisplayConversationEntry.tsx`): - Added imports for `ScriptFixerDialog`, `ScriptType`, `useAttemptRepo`, `Button`, and `Wrench` icon - Added `SCRIPT_TOOL_NAMES` constant to identify script entries - Added `getScriptType()` helper function to map tool names to script types - Created `ScriptToolCallCard` component that wraps `ToolCallCard` and adds a "Fix Script" button for failed scripts - Modified `renderToolUse()` to detect script entries and render them with `ScriptToolCallCard` **2. `PreviewPanel.tsx`** (`frontend/src/components/panels/PreviewPanel.tsx`): - Added imports for `ScriptFixerDialog`, `useAttemptRepo`, and `Wrench` icon - Added `useAttemptRepo(attemptId)` hook to get repos - Added `handleFixDevScript()` function that opens the ScriptFixerDialog - Added `canFixDevScript` computed value - Added "Fix Dev Script" button in the troubleshooting alert next to the "Stop and Edit" button **3. Translation Keys**: - Added `conversation.fixScript: "Fix Script"` in `en/common.json` - Added `preview.troubleAlert.fixScript: "Fix Dev Script"` in `en/tasks.json` The implementation follows the same pattern as the new UI's `ScriptEntryWithFix` component in `NewDisplayConversationEntry.tsx`, using `useAttemptRepo` hook to fetch repos instead of `useWorkspaceContext` (which is only available in the new design scope). * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * All tasks are complete. Here's a summary of the fixes made: ## Summary of Changes ### Problem The "Fix Dev Script" button was only showing in the troubleshooting alert, which only appears when a dev server is **running** but slow to respond. When the dev server script **fails to start** (exits with error), there are no running dev servers, so the user saw `NoServerContent` without any fix option. ### Solution **1. `PreviewPanel.tsx`** - Added failed dev server detection: ```typescript const failedDevServerProcess = devServerProcesses.find( (p) => p.status === 'failed' || (p.status === 'completed' && p.exit_code !== null && p.exit_code !== 0n) ); const hasFailedDevServer = Boolean(failedDevServerProcess); ``` And passed `hasFailedDevServer` and `onFixDevScript` to `NoServerContent`. **2. `NoServerContent.tsx`** - Added new props and "Fix Dev Script" button: - Added `hasFailedDevServer?: boolean` and `onFixDevScript?: () => void` props - Added fix button that shows when dev server has failed **3. Translation Keys** - Added `preview.noServer.fixScript: "Fix Dev Script"` to `en/tasks.json` Now when a user tries to start a dev server and it fails, they'll see the "Fix Dev Script" button in the `NoServerContent` view, allowing them to open the ScriptFixerDialog to fix the script. * Done! I've successfully added all the Script Fixer i18n translations to the 5 non-English locale files: **Summary of changes:** ### common.json files (5 files updated) Added `fixScript` key to the `conversation` section: - **es**: `"Corregir Script"` - **ja**: `"スクリプトを修正"` - **ko**: `"스크립트 수정"` - **zh-Hans**: `"修复脚本"` - **zh-Hant**: `"修復腳本"` ### tasks.json files (5 files updated) 1. Added `fixScript` to `preview.noServer` section 2. Added `fixScript` to `preview.troubleAlert` section 3. Added the entire `scriptFixer` section with 17 keys including: - `title`, `setupScriptTitle`, `cleanupScriptTitle`, `devServerTitle` - `scriptLabel`, `logsLabel` - `saveButton`, `saveAndTestButton`, `saving`, `testing` - `noLogs`, `selectRepo`, `fixScript` - `statusRunning`, `statusSuccess`, `statusFailed`, `statusKilled` All type checks (`pnpm run check`) passed successfully. * Both lint and type check pass now. Here's a summary of the lint fixes I made: **Fixed lint issues in:** 1. **`ScriptFixerDialog.tsx`** (lines 198 and 253): - Added missing `repos` dependency to both `handleSave` and `handleSaveAndTest` useCallback hooks - Removed unnecessary `modal` dependency from `handleSaveAndTest` 2. **`NewDisplayConversationEntry.tsx`** (line 658): - Used a `useRef` pattern to access `repos` inside the `handleFix` callback without including it in the dependency array, since the `repos` array is recreated on every render from context * All checks pass. The fix is complete. **Summary:** The "Fix Script" button in `PreviewControls` will now only appear when the latest dev server process has a status of `'failed'`. When the dev server is running, completed successfully, or was killed by the user, the button will be hidden.
2026-01-12 16:54:38 +00:00
type RepoWithTargetBranch,
2026-01-08 22:14:38 +00:00
} from 'shared/types';
import type { WorkspaceWithSession } from '@/types/attempt';
import { DiffLineType, parseInstance } from '@git-diff-view/react';
import {
usePersistedExpanded,
type PersistKey,
} from '@/stores/useUiPreferencesStore';
import DisplayConversationEntry from '@/components/NormalizedConversation/DisplayConversationEntry';
import { useMessageEditContext } from '@/contexts/MessageEditContext';
import { useFileNavigation } from '@/contexts/FileNavigationContext';
import { useLogNavigation } from '@/contexts/LogNavigationContext';
Script Fixer Dialog (Vibe Kanban) (#1968) * All checks pass. Let me provide a summary of what was implemented: ## Summary I've implemented the Script Fixer Dialog feature. Here's what was created and modified: ### New File Created: 1. **`frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx`** - A new dialog component that: - Shows the current script content in an editable textarea - Displays the last execution logs using `VirtualizedProcessLogs` - Provides "Save" and "Save and Test" buttons - Supports both setup scripts and dev server scripts - Handles multi-repo scenarios with a repo selector dropdown ### Files Modified: **New UI (Setup Script):** 1. **`ChatScriptEntry.tsx`** - Added `onFix` prop and a "Fix Script" button that appears when a script fails 2. **`NewDisplayConversationEntry.tsx`** - Added `ScriptEntryWithFix` wrapper component that provides the fix handler to `ChatScriptEntry` **Legacy UI (Setup Script):** 3. **`DisplayConversationEntry.tsx`** - Added `ScriptFixButton` component that renders a fix button for failed scripts in the legacy conversation view **Preview/Dev Server:** 4. **`PreviewPanel.tsx`** - Added "Fix Script" button to the troubleshooting alert when dev server has issues 5. **`PreviewBrowser.tsx`** - Added optional `handleFixDevScript` prop and "Fix Script" button alongside "Start Server" 6. **`PreviewBrowserContainer.tsx`** - Wired up the dialog opening for dev server script fixes 7. **`PreviewControls.tsx`** - Added `onFixScript` prop and "Fix Script" button in the dev server controls 8. **`PreviewControlsContainer.tsx`** - Wired up the dialog opening for the controls **Translations:** 9. **`tasks.json` (English)** - Added `scriptFixer` translation keys * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * Type check passes. I've updated the `ScriptFixerDialog` to also start the dev server when "Save and Test" is clicked for dev_server script type. * Done! I've updated the `ScriptFixerDialog` to: 1. **Keep the dialog open after "Save and Test"** - The dialog no longer closes after clicking "Save and Test", so the user can watch the logs update in real-time as the script executes 2. **Changed "Cancel" to "Close"** - The button now says "Close" (using `t('common:close')`) and is always enabled, making it clear the user can close the dialog at any time 3. **Logs update automatically** - The `useExecutionProcesses` and `useLogStream` hooks will automatically pick up the new execution process and stream its logs in real-time * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * Done! I've fixed the horizontal overflow by adding `min-w-0` to all flex containers and `overflow-hidden` to the `DialogContent`. This prevents flex children from overflowing their container horizontally. * The type check passes. The fix has been applied - the width constraints (`max-w-4xl w-[90vw]`) are now on the `Dialog` component instead of `DialogContent`. This will override the default `max-w-xl` from the Dialog component's base className, allowing the dialog to be wider and preventing horizontal overflow. * Done! I've added the status indicator to the ScriptFixerDialog. Here's what was implemented: **Changes made:** 1. **`ScriptFixerDialog.tsx`:** - Added import for `RunningDots` component - Added status computation logic to determine if the process is running, succeeded, failed, or killed - Added status indicator UI next to the "Last Execution Logs" label showing: - Animated dots + "Running..." when the script is executing - Green dot + "Completed successfully" when script succeeded (exit code 0) - Red dot + "Failed with exit code X" when script failed - Gray dot + "Process was killed" when killed 2. **`tasks.json` (translations):** - Added `statusRunning`, `statusSuccess`, `statusFailed`, and `statusKilled` translation keys The status indicator will update in real-time as the script executes since it's computed from `latestProcess` which is updated via the `useExecutionProcesses` hook. * Fixed! The issue was that `exit_code` can be `null` when the process completes successfully, and comparing `null !== BigInt(0)` returns `true`, which incorrectly triggered the error state. The fix: - Treat `exit_code === null` as success (same as exit code 0) - Only show error when `exit_code` is explicitly a non-zero value * Fixed. The issue was that `exit_code` comes back as a regular number `0` from the API, but the TypeScript type says `bigint`. Using `Number(exitCode) === 0` handles both cases correctly and satisfies the type checker. * Fixed! The translation key was missing the `buttons.` namespace prefix. It now uses `t('common:buttons.close')` which maps to "Close". * Done. Added `bg-error` and `text-error` classes alongside the `destructive` classes for the error state. * styles * Done! I've fixed the issue by passing `sessionId` to the `ScriptFixerDialog` in all three files: 1. **`PreviewPanel.tsx`** - Gets `sessionId` from `devServerProcesses[0]?.session_id` 2. **`PreviewBrowserContainer.tsx`** - Added `devServerProcesses` to destructuring from `usePreviewDevServer` and passes `sessionId` 3. **`PreviewControlsContainer.tsx`** - Already had `devServerProcesses`, just added `sessionId` to the dialog call Now when you click "Save and Test" for a dev server script, the dialog will receive the session ID, connect to the WebSocket stream, and show the execution logs in real-time. * rollback trigger the ScriptFixer for old UI * Done! I've extended the ScriptFixerDialog to support cleanup scripts. Here's a summary of the changes: **1. `frontend/src/i18n/locales/en/tasks.json`** - Added `cleanupScriptTitle: "Fix Cleanup Script"` translation key **2. `frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx`** - Updated `ScriptType` from `'setup' | 'dev_server'` to `'setup' | 'cleanup' | 'dev_server'` - Updated `latestProcess` filter to use `'cleanupscript'` run_reason for cleanup scripts - Updated script fetching logic to read `cleanup_script` field - Updated both `handleSave` and `handleSaveAndTest` to save `cleanup_script` field - Updated execution logic to call `attemptsApi.runCleanupScript(workspaceId)` for cleanup scripts - Updated dialog title to show "Fix Cleanup Script" for cleanup type - Updated placeholder text to show `#!/bin/bash\nrm -rf node_modules` for cleanup scripts **3. `frontend/src/components/ui-new/NewDisplayConversationEntry.tsx`** - Fixed the scriptType mapping so "Cleanup Script" maps to `'cleanup'` instead of incorrectly mapping to `'setup'` * All tasks are complete. Here's a summary of the changes I made: ## Summary I've implemented the ScriptFixerDialog in the old UI for both `DisplayConversationEntry.tsx` and `PreviewPanel.tsx`. ### Changes Made: **1. `DisplayConversationEntry.tsx`** (`frontend/src/components/NormalizedConversation/DisplayConversationEntry.tsx`): - Added imports for `ScriptFixerDialog`, `ScriptType`, `useAttemptRepo`, `Button`, and `Wrench` icon - Added `SCRIPT_TOOL_NAMES` constant to identify script entries - Added `getScriptType()` helper function to map tool names to script types - Created `ScriptToolCallCard` component that wraps `ToolCallCard` and adds a "Fix Script" button for failed scripts - Modified `renderToolUse()` to detect script entries and render them with `ScriptToolCallCard` **2. `PreviewPanel.tsx`** (`frontend/src/components/panels/PreviewPanel.tsx`): - Added imports for `ScriptFixerDialog`, `useAttemptRepo`, and `Wrench` icon - Added `useAttemptRepo(attemptId)` hook to get repos - Added `handleFixDevScript()` function that opens the ScriptFixerDialog - Added `canFixDevScript` computed value - Added "Fix Dev Script" button in the troubleshooting alert next to the "Stop and Edit" button **3. Translation Keys**: - Added `conversation.fixScript: "Fix Script"` in `en/common.json` - Added `preview.troubleAlert.fixScript: "Fix Dev Script"` in `en/tasks.json` The implementation follows the same pattern as the new UI's `ScriptEntryWithFix` component in `NewDisplayConversationEntry.tsx`, using `useAttemptRepo` hook to fetch repos instead of `useWorkspaceContext` (which is only available in the new design scope). * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * All tasks are complete. Here's a summary of the fixes made: ## Summary of Changes ### Problem The "Fix Dev Script" button was only showing in the troubleshooting alert, which only appears when a dev server is **running** but slow to respond. When the dev server script **fails to start** (exits with error), there are no running dev servers, so the user saw `NoServerContent` without any fix option. ### Solution **1. `PreviewPanel.tsx`** - Added failed dev server detection: ```typescript const failedDevServerProcess = devServerProcesses.find( (p) => p.status === 'failed' || (p.status === 'completed' && p.exit_code !== null && p.exit_code !== 0n) ); const hasFailedDevServer = Boolean(failedDevServerProcess); ``` And passed `hasFailedDevServer` and `onFixDevScript` to `NoServerContent`. **2. `NoServerContent.tsx`** - Added new props and "Fix Dev Script" button: - Added `hasFailedDevServer?: boolean` and `onFixDevScript?: () => void` props - Added fix button that shows when dev server has failed **3. Translation Keys** - Added `preview.noServer.fixScript: "Fix Dev Script"` to `en/tasks.json` Now when a user tries to start a dev server and it fails, they'll see the "Fix Dev Script" button in the `NoServerContent` view, allowing them to open the ScriptFixerDialog to fix the script. * Done! I've successfully added all the Script Fixer i18n translations to the 5 non-English locale files: **Summary of changes:** ### common.json files (5 files updated) Added `fixScript` key to the `conversation` section: - **es**: `"Corregir Script"` - **ja**: `"スクリプトを修正"` - **ko**: `"스크립트 수정"` - **zh-Hans**: `"修复脚本"` - **zh-Hant**: `"修復腳本"` ### tasks.json files (5 files updated) 1. Added `fixScript` to `preview.noServer` section 2. Added `fixScript` to `preview.troubleAlert` section 3. Added the entire `scriptFixer` section with 17 keys including: - `title`, `setupScriptTitle`, `cleanupScriptTitle`, `devServerTitle` - `scriptLabel`, `logsLabel` - `saveButton`, `saveAndTestButton`, `saving`, `testing` - `noLogs`, `selectRepo`, `fixScript` - `statusRunning`, `statusSuccess`, `statusFailed`, `statusKilled` All type checks (`pnpm run check`) passed successfully. * Both lint and type check pass now. Here's a summary of the lint fixes I made: **Fixed lint issues in:** 1. **`ScriptFixerDialog.tsx`** (lines 198 and 253): - Added missing `repos` dependency to both `handleSave` and `handleSaveAndTest` useCallback hooks - Removed unnecessary `modal` dependency from `handleSaveAndTest` 2. **`NewDisplayConversationEntry.tsx`** (line 658): - Used a `useRef` pattern to access `repos` inside the `handleFix` callback without including it in the dependency array, since the `repos` array is recreated on every render from context * All checks pass. The fix is complete. **Summary:** The "Fix Script" button in `PreviewControls` will now only appear when the latest dev server process has a status of `'failed'`. When the dev server is running, completed successfully, or was killed by the user, the button will be hidden.
2026-01-12 16:54:38 +00:00
import { useWorkspaceContext } from '@/contexts/WorkspaceContext';
2026-01-08 22:14:38 +00:00
import { cn } from '@/lib/utils';
Script Fixer Dialog (Vibe Kanban) (#1968) * All checks pass. Let me provide a summary of what was implemented: ## Summary I've implemented the Script Fixer Dialog feature. Here's what was created and modified: ### New File Created: 1. **`frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx`** - A new dialog component that: - Shows the current script content in an editable textarea - Displays the last execution logs using `VirtualizedProcessLogs` - Provides "Save" and "Save and Test" buttons - Supports both setup scripts and dev server scripts - Handles multi-repo scenarios with a repo selector dropdown ### Files Modified: **New UI (Setup Script):** 1. **`ChatScriptEntry.tsx`** - Added `onFix` prop and a "Fix Script" button that appears when a script fails 2. **`NewDisplayConversationEntry.tsx`** - Added `ScriptEntryWithFix` wrapper component that provides the fix handler to `ChatScriptEntry` **Legacy UI (Setup Script):** 3. **`DisplayConversationEntry.tsx`** - Added `ScriptFixButton` component that renders a fix button for failed scripts in the legacy conversation view **Preview/Dev Server:** 4. **`PreviewPanel.tsx`** - Added "Fix Script" button to the troubleshooting alert when dev server has issues 5. **`PreviewBrowser.tsx`** - Added optional `handleFixDevScript` prop and "Fix Script" button alongside "Start Server" 6. **`PreviewBrowserContainer.tsx`** - Wired up the dialog opening for dev server script fixes 7. **`PreviewControls.tsx`** - Added `onFixScript` prop and "Fix Script" button in the dev server controls 8. **`PreviewControlsContainer.tsx`** - Wired up the dialog opening for the controls **Translations:** 9. **`tasks.json` (English)** - Added `scriptFixer` translation keys * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * Type check passes. I've updated the `ScriptFixerDialog` to also start the dev server when "Save and Test" is clicked for dev_server script type. * Done! I've updated the `ScriptFixerDialog` to: 1. **Keep the dialog open after "Save and Test"** - The dialog no longer closes after clicking "Save and Test", so the user can watch the logs update in real-time as the script executes 2. **Changed "Cancel" to "Close"** - The button now says "Close" (using `t('common:close')`) and is always enabled, making it clear the user can close the dialog at any time 3. **Logs update automatically** - The `useExecutionProcesses` and `useLogStream` hooks will automatically pick up the new execution process and stream its logs in real-time * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * Done! I've fixed the horizontal overflow by adding `min-w-0` to all flex containers and `overflow-hidden` to the `DialogContent`. This prevents flex children from overflowing their container horizontally. * The type check passes. The fix has been applied - the width constraints (`max-w-4xl w-[90vw]`) are now on the `Dialog` component instead of `DialogContent`. This will override the default `max-w-xl` from the Dialog component's base className, allowing the dialog to be wider and preventing horizontal overflow. * Done! I've added the status indicator to the ScriptFixerDialog. Here's what was implemented: **Changes made:** 1. **`ScriptFixerDialog.tsx`:** - Added import for `RunningDots` component - Added status computation logic to determine if the process is running, succeeded, failed, or killed - Added status indicator UI next to the "Last Execution Logs" label showing: - Animated dots + "Running..." when the script is executing - Green dot + "Completed successfully" when script succeeded (exit code 0) - Red dot + "Failed with exit code X" when script failed - Gray dot + "Process was killed" when killed 2. **`tasks.json` (translations):** - Added `statusRunning`, `statusSuccess`, `statusFailed`, and `statusKilled` translation keys The status indicator will update in real-time as the script executes since it's computed from `latestProcess` which is updated via the `useExecutionProcesses` hook. * Fixed! The issue was that `exit_code` can be `null` when the process completes successfully, and comparing `null !== BigInt(0)` returns `true`, which incorrectly triggered the error state. The fix: - Treat `exit_code === null` as success (same as exit code 0) - Only show error when `exit_code` is explicitly a non-zero value * Fixed. The issue was that `exit_code` comes back as a regular number `0` from the API, but the TypeScript type says `bigint`. Using `Number(exitCode) === 0` handles both cases correctly and satisfies the type checker. * Fixed! The translation key was missing the `buttons.` namespace prefix. It now uses `t('common:buttons.close')` which maps to "Close". * Done. Added `bg-error` and `text-error` classes alongside the `destructive` classes for the error state. * styles * Done! I've fixed the issue by passing `sessionId` to the `ScriptFixerDialog` in all three files: 1. **`PreviewPanel.tsx`** - Gets `sessionId` from `devServerProcesses[0]?.session_id` 2. **`PreviewBrowserContainer.tsx`** - Added `devServerProcesses` to destructuring from `usePreviewDevServer` and passes `sessionId` 3. **`PreviewControlsContainer.tsx`** - Already had `devServerProcesses`, just added `sessionId` to the dialog call Now when you click "Save and Test" for a dev server script, the dialog will receive the session ID, connect to the WebSocket stream, and show the execution logs in real-time. * rollback trigger the ScriptFixer for old UI * Done! I've extended the ScriptFixerDialog to support cleanup scripts. Here's a summary of the changes: **1. `frontend/src/i18n/locales/en/tasks.json`** - Added `cleanupScriptTitle: "Fix Cleanup Script"` translation key **2. `frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx`** - Updated `ScriptType` from `'setup' | 'dev_server'` to `'setup' | 'cleanup' | 'dev_server'` - Updated `latestProcess` filter to use `'cleanupscript'` run_reason for cleanup scripts - Updated script fetching logic to read `cleanup_script` field - Updated both `handleSave` and `handleSaveAndTest` to save `cleanup_script` field - Updated execution logic to call `attemptsApi.runCleanupScript(workspaceId)` for cleanup scripts - Updated dialog title to show "Fix Cleanup Script" for cleanup type - Updated placeholder text to show `#!/bin/bash\nrm -rf node_modules` for cleanup scripts **3. `frontend/src/components/ui-new/NewDisplayConversationEntry.tsx`** - Fixed the scriptType mapping so "Cleanup Script" maps to `'cleanup'` instead of incorrectly mapping to `'setup'` * All tasks are complete. Here's a summary of the changes I made: ## Summary I've implemented the ScriptFixerDialog in the old UI for both `DisplayConversationEntry.tsx` and `PreviewPanel.tsx`. ### Changes Made: **1. `DisplayConversationEntry.tsx`** (`frontend/src/components/NormalizedConversation/DisplayConversationEntry.tsx`): - Added imports for `ScriptFixerDialog`, `ScriptType`, `useAttemptRepo`, `Button`, and `Wrench` icon - Added `SCRIPT_TOOL_NAMES` constant to identify script entries - Added `getScriptType()` helper function to map tool names to script types - Created `ScriptToolCallCard` component that wraps `ToolCallCard` and adds a "Fix Script" button for failed scripts - Modified `renderToolUse()` to detect script entries and render them with `ScriptToolCallCard` **2. `PreviewPanel.tsx`** (`frontend/src/components/panels/PreviewPanel.tsx`): - Added imports for `ScriptFixerDialog`, `useAttemptRepo`, and `Wrench` icon - Added `useAttemptRepo(attemptId)` hook to get repos - Added `handleFixDevScript()` function that opens the ScriptFixerDialog - Added `canFixDevScript` computed value - Added "Fix Dev Script" button in the troubleshooting alert next to the "Stop and Edit" button **3. Translation Keys**: - Added `conversation.fixScript: "Fix Script"` in `en/common.json` - Added `preview.troubleAlert.fixScript: "Fix Dev Script"` in `en/tasks.json` The implementation follows the same pattern as the new UI's `ScriptEntryWithFix` component in `NewDisplayConversationEntry.tsx`, using `useAttemptRepo` hook to fetch repos instead of `useWorkspaceContext` (which is only available in the new design scope). * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * All tasks are complete. Here's a summary of the fixes made: ## Summary of Changes ### Problem The "Fix Dev Script" button was only showing in the troubleshooting alert, which only appears when a dev server is **running** but slow to respond. When the dev server script **fails to start** (exits with error), there are no running dev servers, so the user saw `NoServerContent` without any fix option. ### Solution **1. `PreviewPanel.tsx`** - Added failed dev server detection: ```typescript const failedDevServerProcess = devServerProcesses.find( (p) => p.status === 'failed' || (p.status === 'completed' && p.exit_code !== null && p.exit_code !== 0n) ); const hasFailedDevServer = Boolean(failedDevServerProcess); ``` And passed `hasFailedDevServer` and `onFixDevScript` to `NoServerContent`. **2. `NoServerContent.tsx`** - Added new props and "Fix Dev Script" button: - Added `hasFailedDevServer?: boolean` and `onFixDevScript?: () => void` props - Added fix button that shows when dev server has failed **3. Translation Keys** - Added `preview.noServer.fixScript: "Fix Dev Script"` to `en/tasks.json` Now when a user tries to start a dev server and it fails, they'll see the "Fix Dev Script" button in the `NoServerContent` view, allowing them to open the ScriptFixerDialog to fix the script. * Done! I've successfully added all the Script Fixer i18n translations to the 5 non-English locale files: **Summary of changes:** ### common.json files (5 files updated) Added `fixScript` key to the `conversation` section: - **es**: `"Corregir Script"` - **ja**: `"スクリプトを修正"` - **ko**: `"스크립트 수정"` - **zh-Hans**: `"修复脚本"` - **zh-Hant**: `"修復腳本"` ### tasks.json files (5 files updated) 1. Added `fixScript` to `preview.noServer` section 2. Added `fixScript` to `preview.troubleAlert` section 3. Added the entire `scriptFixer` section with 17 keys including: - `title`, `setupScriptTitle`, `cleanupScriptTitle`, `devServerTitle` - `scriptLabel`, `logsLabel` - `saveButton`, `saveAndTestButton`, `saving`, `testing` - `noLogs`, `selectRepo`, `fixScript` - `statusRunning`, `statusSuccess`, `statusFailed`, `statusKilled` All type checks (`pnpm run check`) passed successfully. * Both lint and type check pass now. Here's a summary of the lint fixes I made: **Fixed lint issues in:** 1. **`ScriptFixerDialog.tsx`** (lines 198 and 253): - Added missing `repos` dependency to both `handleSave` and `handleSaveAndTest` useCallback hooks - Removed unnecessary `modal` dependency from `handleSaveAndTest` 2. **`NewDisplayConversationEntry.tsx`** (line 658): - Used a `useRef` pattern to access `repos` inside the `handleFix` callback without including it in the dependency array, since the `repos` array is recreated on every render from context * All checks pass. The fix is complete. **Summary:** The "Fix Script" button in `PreviewControls` will now only appear when the latest dev server process has a status of `'failed'`. When the dev server is running, completed successfully, or was killed by the user, the button will be hidden.
2026-01-12 16:54:38 +00:00
import {
ScriptFixerDialog,
type ScriptType,
} from '@/components/dialogs/scripts/ScriptFixerDialog';
2026-01-08 22:14:38 +00:00
import {
ChatToolSummary,
ChatTodoList,
ChatFileEntry,
ChatApprovalCard,
ChatUserMessage,
ChatAssistantMessage,
ChatSystemMessage,
ChatThinkingMessage,
ChatErrorMessage,
ChatScriptEntry,
} from './primitives/conversation';
import type { DiffInput } from './primitives/conversation/DiffViewCard';
type Props = {
entry: NormalizedEntry;
expansionKey: string;
executionProcessId?: string;
taskAttempt?: WorkspaceWithSession;
task?: TaskWithAttemptStatus;
};
type FileEditAction = Extract<ActionType, { action: 'file_edit' }>;
/**
* Parse unified diff to extract addition/deletion counts
*/
function parseDiffStats(unifiedDiff: string): {
additions: number;
deletions: number;
} {
let additions = 0;
let deletions = 0;
try {
const parsed = parseInstance.parse(unifiedDiff);
for (const h of parsed.hunks) {
for (const line of h.lines) {
if (line.type === DiffLineType.Add) additions++;
else if (line.type === DiffLineType.Delete) deletions++;
}
}
} catch {
// Fallback: count lines starting with + or -
const lines = unifiedDiff.split('\n');
for (const line of lines) {
if (line.startsWith('+') && !line.startsWith('+++')) additions++;
else if (line.startsWith('-') && !line.startsWith('---')) deletions++;
}
}
return { additions, deletions };
}
/**
* Generate tool summary text from action type
*/
function getToolSummary(
entryType: Extract<NormalizedEntry['entry_type'], { type: 'tool_use' }>,
t: TFunction<'common'>
): string {
const { action_type, tool_name } = entryType;
switch (action_type.action) {
case 'file_read':
return t('conversation.toolSummary.read', { path: action_type.path });
case 'search':
return t('conversation.toolSummary.searched', {
query: action_type.query,
});
case 'web_fetch':
return t('conversation.toolSummary.fetched', { url: action_type.url });
case 'command_run':
return action_type.command || t('conversation.toolSummary.ranCommand');
case 'task_create':
return t('conversation.toolSummary.createdTask', {
description: action_type.description,
});
case 'todo_management':
return t('conversation.toolSummary.todoOperation', {
operation: action_type.operation,
});
case 'tool':
return tool_name || t('conversation.tool');
default:
return tool_name || t('conversation.tool');
}
}
/**
* Extract the actual tool output from action_type.result
* The output location depends on the action type:
* - command_run: result.output
* - tool: result.value (JSON stringified if object)
* - others: fall back to entry.content
*/
function getToolOutput(
entryType: Extract<NormalizedEntry['entry_type'], { type: 'tool_use' }>,
entryContent: string
): string {
const { action_type } = entryType;
switch (action_type.action) {
case 'command_run':
return action_type.result?.output ?? entryContent;
case 'tool':
if (action_type.result?.value != null) {
return typeof action_type.result.value === 'string'
? action_type.result.value
: JSON.stringify(action_type.result.value, null, 2);
}
return entryContent;
default:
return entryContent;
}
}
/**
* Extract the command from action_type for command_run actions
*/
function getToolCommand(
entryType: Extract<NormalizedEntry['entry_type'], { type: 'tool_use' }>
): string | undefined {
const { action_type } = entryType;
if (action_type.action === 'command_run') {
return action_type.command;
}
return undefined;
}
/**
* Render tool_use entry types with appropriate components
*/
function renderToolUseEntry(
entryType: Extract<NormalizedEntry['entry_type'], { type: 'tool_use' }>,
props: Props,
t: TFunction<'common'>
): React.ReactNode {
const { expansionKey, executionProcessId, taskAttempt } = props;
const { action_type, status } = entryType;
// File edit - use ChatFileEntry
if (action_type.action === 'file_edit') {
const fileEditAction = action_type as FileEditAction;
return (
<>
{fileEditAction.changes.map((change, idx) => (
<FileEditEntry
key={idx}
path={fileEditAction.path}
change={change}
expansionKey={`edit:${expansionKey}:${idx}`}
status={status}
/>
))}
</>
);
}
// Plan presentation - use ChatApprovalCard
if (action_type.action === 'plan_presentation') {
return (
<PlanEntry
plan={action_type.plan}
expansionKey={expansionKey}
workspaceId={taskAttempt?.id}
status={status}
/>
);
}
// Todo management - use ChatTodoList
if (action_type.action === 'todo_management') {
return (
<TodoManagementEntry
todos={action_type.todos}
expansionKey={expansionKey}
/>
);
}
// Script entries (Setup Script, Cleanup Script, Tool Install Script)
const scriptToolNames = [
'Setup Script',
'Cleanup Script',
'Tool Install Script',
];
if (
action_type.action === 'command_run' &&
scriptToolNames.includes(entryType.tool_name)
) {
const exitCode =
action_type.result?.exit_status?.type === 'exit_code'
? action_type.result.exit_status.code
: null;
return (
Script Fixer Dialog (Vibe Kanban) (#1968) * All checks pass. Let me provide a summary of what was implemented: ## Summary I've implemented the Script Fixer Dialog feature. Here's what was created and modified: ### New File Created: 1. **`frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx`** - A new dialog component that: - Shows the current script content in an editable textarea - Displays the last execution logs using `VirtualizedProcessLogs` - Provides "Save" and "Save and Test" buttons - Supports both setup scripts and dev server scripts - Handles multi-repo scenarios with a repo selector dropdown ### Files Modified: **New UI (Setup Script):** 1. **`ChatScriptEntry.tsx`** - Added `onFix` prop and a "Fix Script" button that appears when a script fails 2. **`NewDisplayConversationEntry.tsx`** - Added `ScriptEntryWithFix` wrapper component that provides the fix handler to `ChatScriptEntry` **Legacy UI (Setup Script):** 3. **`DisplayConversationEntry.tsx`** - Added `ScriptFixButton` component that renders a fix button for failed scripts in the legacy conversation view **Preview/Dev Server:** 4. **`PreviewPanel.tsx`** - Added "Fix Script" button to the troubleshooting alert when dev server has issues 5. **`PreviewBrowser.tsx`** - Added optional `handleFixDevScript` prop and "Fix Script" button alongside "Start Server" 6. **`PreviewBrowserContainer.tsx`** - Wired up the dialog opening for dev server script fixes 7. **`PreviewControls.tsx`** - Added `onFixScript` prop and "Fix Script" button in the dev server controls 8. **`PreviewControlsContainer.tsx`** - Wired up the dialog opening for the controls **Translations:** 9. **`tasks.json` (English)** - Added `scriptFixer` translation keys * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * Type check passes. I've updated the `ScriptFixerDialog` to also start the dev server when "Save and Test" is clicked for dev_server script type. * Done! I've updated the `ScriptFixerDialog` to: 1. **Keep the dialog open after "Save and Test"** - The dialog no longer closes after clicking "Save and Test", so the user can watch the logs update in real-time as the script executes 2. **Changed "Cancel" to "Close"** - The button now says "Close" (using `t('common:close')`) and is always enabled, making it clear the user can close the dialog at any time 3. **Logs update automatically** - The `useExecutionProcesses` and `useLogStream` hooks will automatically pick up the new execution process and stream its logs in real-time * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * Done! I've fixed the horizontal overflow by adding `min-w-0` to all flex containers and `overflow-hidden` to the `DialogContent`. This prevents flex children from overflowing their container horizontally. * The type check passes. The fix has been applied - the width constraints (`max-w-4xl w-[90vw]`) are now on the `Dialog` component instead of `DialogContent`. This will override the default `max-w-xl` from the Dialog component's base className, allowing the dialog to be wider and preventing horizontal overflow. * Done! I've added the status indicator to the ScriptFixerDialog. Here's what was implemented: **Changes made:** 1. **`ScriptFixerDialog.tsx`:** - Added import for `RunningDots` component - Added status computation logic to determine if the process is running, succeeded, failed, or killed - Added status indicator UI next to the "Last Execution Logs" label showing: - Animated dots + "Running..." when the script is executing - Green dot + "Completed successfully" when script succeeded (exit code 0) - Red dot + "Failed with exit code X" when script failed - Gray dot + "Process was killed" when killed 2. **`tasks.json` (translations):** - Added `statusRunning`, `statusSuccess`, `statusFailed`, and `statusKilled` translation keys The status indicator will update in real-time as the script executes since it's computed from `latestProcess` which is updated via the `useExecutionProcesses` hook. * Fixed! The issue was that `exit_code` can be `null` when the process completes successfully, and comparing `null !== BigInt(0)` returns `true`, which incorrectly triggered the error state. The fix: - Treat `exit_code === null` as success (same as exit code 0) - Only show error when `exit_code` is explicitly a non-zero value * Fixed. The issue was that `exit_code` comes back as a regular number `0` from the API, but the TypeScript type says `bigint`. Using `Number(exitCode) === 0` handles both cases correctly and satisfies the type checker. * Fixed! The translation key was missing the `buttons.` namespace prefix. It now uses `t('common:buttons.close')` which maps to "Close". * Done. Added `bg-error` and `text-error` classes alongside the `destructive` classes for the error state. * styles * Done! I've fixed the issue by passing `sessionId` to the `ScriptFixerDialog` in all three files: 1. **`PreviewPanel.tsx`** - Gets `sessionId` from `devServerProcesses[0]?.session_id` 2. **`PreviewBrowserContainer.tsx`** - Added `devServerProcesses` to destructuring from `usePreviewDevServer` and passes `sessionId` 3. **`PreviewControlsContainer.tsx`** - Already had `devServerProcesses`, just added `sessionId` to the dialog call Now when you click "Save and Test" for a dev server script, the dialog will receive the session ID, connect to the WebSocket stream, and show the execution logs in real-time. * rollback trigger the ScriptFixer for old UI * Done! I've extended the ScriptFixerDialog to support cleanup scripts. Here's a summary of the changes: **1. `frontend/src/i18n/locales/en/tasks.json`** - Added `cleanupScriptTitle: "Fix Cleanup Script"` translation key **2. `frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx`** - Updated `ScriptType` from `'setup' | 'dev_server'` to `'setup' | 'cleanup' | 'dev_server'` - Updated `latestProcess` filter to use `'cleanupscript'` run_reason for cleanup scripts - Updated script fetching logic to read `cleanup_script` field - Updated both `handleSave` and `handleSaveAndTest` to save `cleanup_script` field - Updated execution logic to call `attemptsApi.runCleanupScript(workspaceId)` for cleanup scripts - Updated dialog title to show "Fix Cleanup Script" for cleanup type - Updated placeholder text to show `#!/bin/bash\nrm -rf node_modules` for cleanup scripts **3. `frontend/src/components/ui-new/NewDisplayConversationEntry.tsx`** - Fixed the scriptType mapping so "Cleanup Script" maps to `'cleanup'` instead of incorrectly mapping to `'setup'` * All tasks are complete. Here's a summary of the changes I made: ## Summary I've implemented the ScriptFixerDialog in the old UI for both `DisplayConversationEntry.tsx` and `PreviewPanel.tsx`. ### Changes Made: **1. `DisplayConversationEntry.tsx`** (`frontend/src/components/NormalizedConversation/DisplayConversationEntry.tsx`): - Added imports for `ScriptFixerDialog`, `ScriptType`, `useAttemptRepo`, `Button`, and `Wrench` icon - Added `SCRIPT_TOOL_NAMES` constant to identify script entries - Added `getScriptType()` helper function to map tool names to script types - Created `ScriptToolCallCard` component that wraps `ToolCallCard` and adds a "Fix Script" button for failed scripts - Modified `renderToolUse()` to detect script entries and render them with `ScriptToolCallCard` **2. `PreviewPanel.tsx`** (`frontend/src/components/panels/PreviewPanel.tsx`): - Added imports for `ScriptFixerDialog`, `useAttemptRepo`, and `Wrench` icon - Added `useAttemptRepo(attemptId)` hook to get repos - Added `handleFixDevScript()` function that opens the ScriptFixerDialog - Added `canFixDevScript` computed value - Added "Fix Dev Script" button in the troubleshooting alert next to the "Stop and Edit" button **3. Translation Keys**: - Added `conversation.fixScript: "Fix Script"` in `en/common.json` - Added `preview.troubleAlert.fixScript: "Fix Dev Script"` in `en/tasks.json` The implementation follows the same pattern as the new UI's `ScriptEntryWithFix` component in `NewDisplayConversationEntry.tsx`, using `useAttemptRepo` hook to fetch repos instead of `useWorkspaceContext` (which is only available in the new design scope). * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * All tasks are complete. Here's a summary of the fixes made: ## Summary of Changes ### Problem The "Fix Dev Script" button was only showing in the troubleshooting alert, which only appears when a dev server is **running** but slow to respond. When the dev server script **fails to start** (exits with error), there are no running dev servers, so the user saw `NoServerContent` without any fix option. ### Solution **1. `PreviewPanel.tsx`** - Added failed dev server detection: ```typescript const failedDevServerProcess = devServerProcesses.find( (p) => p.status === 'failed' || (p.status === 'completed' && p.exit_code !== null && p.exit_code !== 0n) ); const hasFailedDevServer = Boolean(failedDevServerProcess); ``` And passed `hasFailedDevServer` and `onFixDevScript` to `NoServerContent`. **2. `NoServerContent.tsx`** - Added new props and "Fix Dev Script" button: - Added `hasFailedDevServer?: boolean` and `onFixDevScript?: () => void` props - Added fix button that shows when dev server has failed **3. Translation Keys** - Added `preview.noServer.fixScript: "Fix Dev Script"` to `en/tasks.json` Now when a user tries to start a dev server and it fails, they'll see the "Fix Dev Script" button in the `NoServerContent` view, allowing them to open the ScriptFixerDialog to fix the script. * Done! I've successfully added all the Script Fixer i18n translations to the 5 non-English locale files: **Summary of changes:** ### common.json files (5 files updated) Added `fixScript` key to the `conversation` section: - **es**: `"Corregir Script"` - **ja**: `"スクリプトを修正"` - **ko**: `"스크립트 수정"` - **zh-Hans**: `"修复脚本"` - **zh-Hant**: `"修復腳本"` ### tasks.json files (5 files updated) 1. Added `fixScript` to `preview.noServer` section 2. Added `fixScript` to `preview.troubleAlert` section 3. Added the entire `scriptFixer` section with 17 keys including: - `title`, `setupScriptTitle`, `cleanupScriptTitle`, `devServerTitle` - `scriptLabel`, `logsLabel` - `saveButton`, `saveAndTestButton`, `saving`, `testing` - `noLogs`, `selectRepo`, `fixScript` - `statusRunning`, `statusSuccess`, `statusFailed`, `statusKilled` All type checks (`pnpm run check`) passed successfully. * Both lint and type check pass now. Here's a summary of the lint fixes I made: **Fixed lint issues in:** 1. **`ScriptFixerDialog.tsx`** (lines 198 and 253): - Added missing `repos` dependency to both `handleSave` and `handleSaveAndTest` useCallback hooks - Removed unnecessary `modal` dependency from `handleSaveAndTest` 2. **`NewDisplayConversationEntry.tsx`** (line 658): - Used a `useRef` pattern to access `repos` inside the `handleFix` callback without including it in the dependency array, since the `repos` array is recreated on every render from context * All checks pass. The fix is complete. **Summary:** The "Fix Script" button in `PreviewControls` will now only appear when the latest dev server process has a status of `'failed'`. When the dev server is running, completed successfully, or was killed by the user, the button will be hidden.
2026-01-12 16:54:38 +00:00
<ScriptEntryWithFix
2026-01-08 22:14:38 +00:00
title={entryType.tool_name}
processId={executionProcessId ?? ''}
exitCode={exitCode}
status={status}
Script Fixer Dialog (Vibe Kanban) (#1968) * All checks pass. Let me provide a summary of what was implemented: ## Summary I've implemented the Script Fixer Dialog feature. Here's what was created and modified: ### New File Created: 1. **`frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx`** - A new dialog component that: - Shows the current script content in an editable textarea - Displays the last execution logs using `VirtualizedProcessLogs` - Provides "Save" and "Save and Test" buttons - Supports both setup scripts and dev server scripts - Handles multi-repo scenarios with a repo selector dropdown ### Files Modified: **New UI (Setup Script):** 1. **`ChatScriptEntry.tsx`** - Added `onFix` prop and a "Fix Script" button that appears when a script fails 2. **`NewDisplayConversationEntry.tsx`** - Added `ScriptEntryWithFix` wrapper component that provides the fix handler to `ChatScriptEntry` **Legacy UI (Setup Script):** 3. **`DisplayConversationEntry.tsx`** - Added `ScriptFixButton` component that renders a fix button for failed scripts in the legacy conversation view **Preview/Dev Server:** 4. **`PreviewPanel.tsx`** - Added "Fix Script" button to the troubleshooting alert when dev server has issues 5. **`PreviewBrowser.tsx`** - Added optional `handleFixDevScript` prop and "Fix Script" button alongside "Start Server" 6. **`PreviewBrowserContainer.tsx`** - Wired up the dialog opening for dev server script fixes 7. **`PreviewControls.tsx`** - Added `onFixScript` prop and "Fix Script" button in the dev server controls 8. **`PreviewControlsContainer.tsx`** - Wired up the dialog opening for the controls **Translations:** 9. **`tasks.json` (English)** - Added `scriptFixer` translation keys * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * Type check passes. I've updated the `ScriptFixerDialog` to also start the dev server when "Save and Test" is clicked for dev_server script type. * Done! I've updated the `ScriptFixerDialog` to: 1. **Keep the dialog open after "Save and Test"** - The dialog no longer closes after clicking "Save and Test", so the user can watch the logs update in real-time as the script executes 2. **Changed "Cancel" to "Close"** - The button now says "Close" (using `t('common:close')`) and is always enabled, making it clear the user can close the dialog at any time 3. **Logs update automatically** - The `useExecutionProcesses` and `useLogStream` hooks will automatically pick up the new execution process and stream its logs in real-time * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * Done! I've fixed the horizontal overflow by adding `min-w-0` to all flex containers and `overflow-hidden` to the `DialogContent`. This prevents flex children from overflowing their container horizontally. * The type check passes. The fix has been applied - the width constraints (`max-w-4xl w-[90vw]`) are now on the `Dialog` component instead of `DialogContent`. This will override the default `max-w-xl` from the Dialog component's base className, allowing the dialog to be wider and preventing horizontal overflow. * Done! I've added the status indicator to the ScriptFixerDialog. Here's what was implemented: **Changes made:** 1. **`ScriptFixerDialog.tsx`:** - Added import for `RunningDots` component - Added status computation logic to determine if the process is running, succeeded, failed, or killed - Added status indicator UI next to the "Last Execution Logs" label showing: - Animated dots + "Running..." when the script is executing - Green dot + "Completed successfully" when script succeeded (exit code 0) - Red dot + "Failed with exit code X" when script failed - Gray dot + "Process was killed" when killed 2. **`tasks.json` (translations):** - Added `statusRunning`, `statusSuccess`, `statusFailed`, and `statusKilled` translation keys The status indicator will update in real-time as the script executes since it's computed from `latestProcess` which is updated via the `useExecutionProcesses` hook. * Fixed! The issue was that `exit_code` can be `null` when the process completes successfully, and comparing `null !== BigInt(0)` returns `true`, which incorrectly triggered the error state. The fix: - Treat `exit_code === null` as success (same as exit code 0) - Only show error when `exit_code` is explicitly a non-zero value * Fixed. The issue was that `exit_code` comes back as a regular number `0` from the API, but the TypeScript type says `bigint`. Using `Number(exitCode) === 0` handles both cases correctly and satisfies the type checker. * Fixed! The translation key was missing the `buttons.` namespace prefix. It now uses `t('common:buttons.close')` which maps to "Close". * Done. Added `bg-error` and `text-error` classes alongside the `destructive` classes for the error state. * styles * Done! I've fixed the issue by passing `sessionId` to the `ScriptFixerDialog` in all three files: 1. **`PreviewPanel.tsx`** - Gets `sessionId` from `devServerProcesses[0]?.session_id` 2. **`PreviewBrowserContainer.tsx`** - Added `devServerProcesses` to destructuring from `usePreviewDevServer` and passes `sessionId` 3. **`PreviewControlsContainer.tsx`** - Already had `devServerProcesses`, just added `sessionId` to the dialog call Now when you click "Save and Test" for a dev server script, the dialog will receive the session ID, connect to the WebSocket stream, and show the execution logs in real-time. * rollback trigger the ScriptFixer for old UI * Done! I've extended the ScriptFixerDialog to support cleanup scripts. Here's a summary of the changes: **1. `frontend/src/i18n/locales/en/tasks.json`** - Added `cleanupScriptTitle: "Fix Cleanup Script"` translation key **2. `frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx`** - Updated `ScriptType` from `'setup' | 'dev_server'` to `'setup' | 'cleanup' | 'dev_server'` - Updated `latestProcess` filter to use `'cleanupscript'` run_reason for cleanup scripts - Updated script fetching logic to read `cleanup_script` field - Updated both `handleSave` and `handleSaveAndTest` to save `cleanup_script` field - Updated execution logic to call `attemptsApi.runCleanupScript(workspaceId)` for cleanup scripts - Updated dialog title to show "Fix Cleanup Script" for cleanup type - Updated placeholder text to show `#!/bin/bash\nrm -rf node_modules` for cleanup scripts **3. `frontend/src/components/ui-new/NewDisplayConversationEntry.tsx`** - Fixed the scriptType mapping so "Cleanup Script" maps to `'cleanup'` instead of incorrectly mapping to `'setup'` * All tasks are complete. Here's a summary of the changes I made: ## Summary I've implemented the ScriptFixerDialog in the old UI for both `DisplayConversationEntry.tsx` and `PreviewPanel.tsx`. ### Changes Made: **1. `DisplayConversationEntry.tsx`** (`frontend/src/components/NormalizedConversation/DisplayConversationEntry.tsx`): - Added imports for `ScriptFixerDialog`, `ScriptType`, `useAttemptRepo`, `Button`, and `Wrench` icon - Added `SCRIPT_TOOL_NAMES` constant to identify script entries - Added `getScriptType()` helper function to map tool names to script types - Created `ScriptToolCallCard` component that wraps `ToolCallCard` and adds a "Fix Script" button for failed scripts - Modified `renderToolUse()` to detect script entries and render them with `ScriptToolCallCard` **2. `PreviewPanel.tsx`** (`frontend/src/components/panels/PreviewPanel.tsx`): - Added imports for `ScriptFixerDialog`, `useAttemptRepo`, and `Wrench` icon - Added `useAttemptRepo(attemptId)` hook to get repos - Added `handleFixDevScript()` function that opens the ScriptFixerDialog - Added `canFixDevScript` computed value - Added "Fix Dev Script" button in the troubleshooting alert next to the "Stop and Edit" button **3. Translation Keys**: - Added `conversation.fixScript: "Fix Script"` in `en/common.json` - Added `preview.troubleAlert.fixScript: "Fix Dev Script"` in `en/tasks.json` The implementation follows the same pattern as the new UI's `ScriptEntryWithFix` component in `NewDisplayConversationEntry.tsx`, using `useAttemptRepo` hook to fetch repos instead of `useWorkspaceContext` (which is only available in the new design scope). * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * All tasks are complete. Here's a summary of the fixes made: ## Summary of Changes ### Problem The "Fix Dev Script" button was only showing in the troubleshooting alert, which only appears when a dev server is **running** but slow to respond. When the dev server script **fails to start** (exits with error), there are no running dev servers, so the user saw `NoServerContent` without any fix option. ### Solution **1. `PreviewPanel.tsx`** - Added failed dev server detection: ```typescript const failedDevServerProcess = devServerProcesses.find( (p) => p.status === 'failed' || (p.status === 'completed' && p.exit_code !== null && p.exit_code !== 0n) ); const hasFailedDevServer = Boolean(failedDevServerProcess); ``` And passed `hasFailedDevServer` and `onFixDevScript` to `NoServerContent`. **2. `NoServerContent.tsx`** - Added new props and "Fix Dev Script" button: - Added `hasFailedDevServer?: boolean` and `onFixDevScript?: () => void` props - Added fix button that shows when dev server has failed **3. Translation Keys** - Added `preview.noServer.fixScript: "Fix Dev Script"` to `en/tasks.json` Now when a user tries to start a dev server and it fails, they'll see the "Fix Dev Script" button in the `NoServerContent` view, allowing them to open the ScriptFixerDialog to fix the script. * Done! I've successfully added all the Script Fixer i18n translations to the 5 non-English locale files: **Summary of changes:** ### common.json files (5 files updated) Added `fixScript` key to the `conversation` section: - **es**: `"Corregir Script"` - **ja**: `"スクリプトを修正"` - **ko**: `"스크립트 수정"` - **zh-Hans**: `"修复脚本"` - **zh-Hant**: `"修復腳本"` ### tasks.json files (5 files updated) 1. Added `fixScript` to `preview.noServer` section 2. Added `fixScript` to `preview.troubleAlert` section 3. Added the entire `scriptFixer` section with 17 keys including: - `title`, `setupScriptTitle`, `cleanupScriptTitle`, `devServerTitle` - `scriptLabel`, `logsLabel` - `saveButton`, `saveAndTestButton`, `saving`, `testing` - `noLogs`, `selectRepo`, `fixScript` - `statusRunning`, `statusSuccess`, `statusFailed`, `statusKilled` All type checks (`pnpm run check`) passed successfully. * Both lint and type check pass now. Here's a summary of the lint fixes I made: **Fixed lint issues in:** 1. **`ScriptFixerDialog.tsx`** (lines 198 and 253): - Added missing `repos` dependency to both `handleSave` and `handleSaveAndTest` useCallback hooks - Removed unnecessary `modal` dependency from `handleSaveAndTest` 2. **`NewDisplayConversationEntry.tsx`** (line 658): - Used a `useRef` pattern to access `repos` inside the `handleFix` callback without including it in the dependency array, since the `repos` array is recreated on every render from context * All checks pass. The fix is complete. **Summary:** The "Fix Script" button in `PreviewControls` will now only appear when the latest dev server process has a status of `'failed'`. When the dev server is running, completed successfully, or was killed by the user, the button will be hidden.
2026-01-12 16:54:38 +00:00
workspaceId={taskAttempt?.id}
sessionId={taskAttempt?.session?.id}
2026-01-08 22:14:38 +00:00
/>
);
}
// Generic tool pending approval - use plan-style card
if (status.status === 'pending_approval') {
return (
<GenericToolApprovalEntry
toolName={entryType.tool_name}
content={props.entry.content}
expansionKey={expansionKey}
workspaceId={taskAttempt?.id}
status={status}
/>
);
}
// Other tool uses - use ChatToolSummary
return (
<ToolSummaryEntry
summary={getToolSummary(entryType, t)}
expansionKey={expansionKey}
status={status}
content={getToolOutput(entryType, props.entry.content)}
toolName={entryType.tool_name}
command={getToolCommand(entryType)}
/>
);
}
function NewDisplayConversationEntry(props: Props) {
const { t } = useTranslation('common');
const { entry, expansionKey, executionProcessId, taskAttempt, task } = props;
const entryType = entry.entry_type;
switch (entryType.type) {
case 'tool_use':
return renderToolUseEntry(entryType, props, t);
case 'user_message':
return (
<UserMessageEntry
content={entry.content}
expansionKey={expansionKey}
workspaceId={taskAttempt?.id}
executionProcessId={executionProcessId}
/>
);
case 'assistant_message':
return (
<AssistantMessageEntry
content={entry.content}
workspaceId={taskAttempt?.id}
/>
);
case 'system_message':
return (
<SystemMessageEntry
content={entry.content}
expansionKey={expansionKey}
/>
);
case 'thinking':
return (
<ChatThinkingMessage
content={entry.content}
taskAttemptId={taskAttempt?.id}
/>
);
case 'error_message':
return (
<ErrorMessageEntry
content={entry.content}
expansionKey={expansionKey}
/>
);
case 'next_action':
// The new design doesn't need the next action bar
return null;
case 'user_feedback':
case 'loading':
// Fallback to legacy component for these entry types
return (
<DisplayConversationEntry
entry={entry}
expansionKey={expansionKey}
executionProcessId={executionProcessId}
taskAttempt={taskAttempt}
task={task}
/>
);
default: {
// Exhaustive check - TypeScript will error if a case is missing
const _exhaustiveCheck: never = entryType;
return _exhaustiveCheck;
}
}
}
/**
* File edit entry with expandable diff
*/
function FileEditEntry({
path,
change,
expansionKey,
status,
}: {
path: string;
change: FileEditAction['changes'][number];
expansionKey: string;
status: ToolStatus;
}) {
// Auto-expand when pending approval
const pendingApproval = status.status === 'pending_approval';
const [expanded, toggle] = usePersistedExpanded(
expansionKey as PersistKey,
pendingApproval
);
const { viewFileInChanges, diffPaths } = useFileNavigation();
// Calculate diff stats for edit changes
const { additions, deletions } = useMemo(() => {
if (change.action === 'edit' && change.unified_diff) {
return parseDiffStats(change.unified_diff);
}
return { additions: undefined, deletions: undefined };
}, [change]);
// For write actions, count as all additions
const writeAdditions =
change.action === 'write' ? change.content.split('\n').length : undefined;
// Build diff content for rendering when expanded
const diffContent: DiffInput | undefined = useMemo(() => {
if (change.action === 'edit' && change.unified_diff) {
return {
type: 'unified',
path,
unifiedDiff: change.unified_diff,
hasLineNumbers: change.has_line_numbers ?? true,
};
}
// For write actions, use content-based diff (empty old, new content)
if (change.action === 'write' && change.content) {
return {
type: 'content',
oldContent: '',
newContent: change.content,
newPath: path,
};
}
return undefined;
}, [change, path]);
// Only show "open in changes" button if the file exists in current diffs
const handleOpenInChanges = useCallback(() => {
viewFileInChanges(path);
}, [viewFileInChanges, path]);
const canOpenInChanges = diffPaths.has(path);
return (
<ChatFileEntry
filename={path}
additions={additions ?? writeAdditions}
deletions={deletions}
expanded={expanded}
onToggle={toggle}
status={status}
diffContent={diffContent}
onOpenInChanges={canOpenInChanges ? handleOpenInChanges : undefined}
/>
);
}
/**
* Plan entry with expandable content
*/
function PlanEntry({
plan,
expansionKey,
workspaceId,
status,
}: {
plan: string;
expansionKey: string;
workspaceId?: string;
status: ToolStatus;
}) {
const { t } = useTranslation('common');
// Expand plans by default when pending approval
const pendingApproval = status.status === 'pending_approval';
const [expanded, toggle] = usePersistedExpanded(
`plan:${expansionKey}`,
pendingApproval
);
// Extract title from plan content (first line or default)
const title = useMemo(() => {
const firstLine = plan.split('\n')[0];
// Remove markdown heading markers
const cleanTitle = firstLine.replace(/^#+\s*/, '').trim();
return cleanTitle || t('conversation.plan');
}, [plan, t]);
return (
<ChatApprovalCard
title={title}
content={plan}
expanded={expanded}
onToggle={toggle}
workspaceId={workspaceId}
status={status}
/>
);
}
/**
* Generic tool approval entry - renders with plan-style card when pending approval
*/
function GenericToolApprovalEntry({
toolName,
content,
expansionKey,
workspaceId,
status,
}: {
toolName: string;
content: string;
expansionKey: string;
workspaceId?: string;
status: ToolStatus;
}) {
const [expanded, toggle] = usePersistedExpanded(
`tool:${expansionKey}`,
true // auto-expand for pending approval
);
return (
<ChatApprovalCard
title={toolName}
content={content}
expanded={expanded}
onToggle={toggle}
workspaceId={workspaceId}
status={status}
/>
);
}
/**
* User message entry with expandable content
*/
function UserMessageEntry({
content,
expansionKey,
workspaceId,
executionProcessId,
}: {
content: string;
expansionKey: string;
workspaceId?: string;
executionProcessId?: string;
}) {
const [expanded, toggle] = usePersistedExpanded(`user:${expansionKey}`, true);
const { startEdit, isEntryGreyed, isInEditMode } = useMessageEditContext();
const isGreyed = isEntryGreyed(expansionKey);
const handleEdit = useCallback(() => {
if (executionProcessId) {
startEdit(expansionKey, executionProcessId, content);
}
}, [startEdit, expansionKey, executionProcessId, content]);
// Only show edit button if we have a process ID and not already in edit mode
const canEdit = !!executionProcessId && !isInEditMode;
return (
<ChatUserMessage
content={content}
expanded={expanded}
onToggle={toggle}
workspaceId={workspaceId}
onEdit={canEdit ? handleEdit : undefined}
isGreyed={isGreyed}
/>
);
}
/**
* Assistant message entry with expandable content
*/
function AssistantMessageEntry({
content,
workspaceId,
}: {
content: string;
workspaceId?: string;
}) {
return <ChatAssistantMessage content={content} workspaceId={workspaceId} />;
}
/**
* Tool summary entry with collapsible content for multi-line summaries
*/
function ToolSummaryEntry({
summary,
expansionKey,
status,
content,
toolName,
command,
}: {
summary: string;
expansionKey: string;
status: ToolStatus;
content: string;
toolName: string;
command?: string;
}) {
const [expanded, toggle] = usePersistedExpanded(
`tool:${expansionKey}`,
false
);
const { viewToolContentInPanel } = useLogNavigation();
const textRef = useRef<HTMLSpanElement>(null);
const [isTruncated, setIsTruncated] = useState(false);
useLayoutEffect(() => {
const el = textRef.current;
if (el && !expanded) {
setIsTruncated(el.scrollWidth > el.clientWidth);
}
}, [summary, expanded]);
// Any tool with output can open the logs panel
const hasOutput = content && content.trim().length > 0;
2026-01-08 22:14:38 +00:00
const handleViewContent = useCallback(() => {
viewToolContentInPanel(toolName, content, command);
}, [viewToolContentInPanel, toolName, content, command]);
return (
<ChatToolSummary
ref={textRef}
summary={summary}
expanded={expanded}
onToggle={toggle}
status={status}
onViewContent={hasOutput ? handleViewContent : undefined}
toolName={toolName}
isTruncated={isTruncated}
/>
);
}
/**
* Todo management entry with expandable list of todos
*/
function TodoManagementEntry({
todos,
expansionKey,
}: {
todos: TodoItem[];
expansionKey: string;
}) {
const [expanded, toggle] = usePersistedExpanded(
`todo:${expansionKey}`,
false
);
return <ChatTodoList todos={todos} expanded={expanded} onToggle={toggle} />;
}
/**
* System message entry with expandable content
*/
function SystemMessageEntry({
content,
expansionKey,
}: {
content: string;
expansionKey: string;
}) {
const [expanded, toggle] = usePersistedExpanded(
`system:${expansionKey}`,
false
);
return (
<ChatSystemMessage
content={content}
expanded={expanded}
onToggle={toggle}
/>
);
}
Script Fixer Dialog (Vibe Kanban) (#1968) * All checks pass. Let me provide a summary of what was implemented: ## Summary I've implemented the Script Fixer Dialog feature. Here's what was created and modified: ### New File Created: 1. **`frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx`** - A new dialog component that: - Shows the current script content in an editable textarea - Displays the last execution logs using `VirtualizedProcessLogs` - Provides "Save" and "Save and Test" buttons - Supports both setup scripts and dev server scripts - Handles multi-repo scenarios with a repo selector dropdown ### Files Modified: **New UI (Setup Script):** 1. **`ChatScriptEntry.tsx`** - Added `onFix` prop and a "Fix Script" button that appears when a script fails 2. **`NewDisplayConversationEntry.tsx`** - Added `ScriptEntryWithFix` wrapper component that provides the fix handler to `ChatScriptEntry` **Legacy UI (Setup Script):** 3. **`DisplayConversationEntry.tsx`** - Added `ScriptFixButton` component that renders a fix button for failed scripts in the legacy conversation view **Preview/Dev Server:** 4. **`PreviewPanel.tsx`** - Added "Fix Script" button to the troubleshooting alert when dev server has issues 5. **`PreviewBrowser.tsx`** - Added optional `handleFixDevScript` prop and "Fix Script" button alongside "Start Server" 6. **`PreviewBrowserContainer.tsx`** - Wired up the dialog opening for dev server script fixes 7. **`PreviewControls.tsx`** - Added `onFixScript` prop and "Fix Script" button in the dev server controls 8. **`PreviewControlsContainer.tsx`** - Wired up the dialog opening for the controls **Translations:** 9. **`tasks.json` (English)** - Added `scriptFixer` translation keys * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * Type check passes. I've updated the `ScriptFixerDialog` to also start the dev server when "Save and Test" is clicked for dev_server script type. * Done! I've updated the `ScriptFixerDialog` to: 1. **Keep the dialog open after "Save and Test"** - The dialog no longer closes after clicking "Save and Test", so the user can watch the logs update in real-time as the script executes 2. **Changed "Cancel" to "Close"** - The button now says "Close" (using `t('common:close')`) and is always enabled, making it clear the user can close the dialog at any time 3. **Logs update automatically** - The `useExecutionProcesses` and `useLogStream` hooks will automatically pick up the new execution process and stream its logs in real-time * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * Done! I've fixed the horizontal overflow by adding `min-w-0` to all flex containers and `overflow-hidden` to the `DialogContent`. This prevents flex children from overflowing their container horizontally. * The type check passes. The fix has been applied - the width constraints (`max-w-4xl w-[90vw]`) are now on the `Dialog` component instead of `DialogContent`. This will override the default `max-w-xl` from the Dialog component's base className, allowing the dialog to be wider and preventing horizontal overflow. * Done! I've added the status indicator to the ScriptFixerDialog. Here's what was implemented: **Changes made:** 1. **`ScriptFixerDialog.tsx`:** - Added import for `RunningDots` component - Added status computation logic to determine if the process is running, succeeded, failed, or killed - Added status indicator UI next to the "Last Execution Logs" label showing: - Animated dots + "Running..." when the script is executing - Green dot + "Completed successfully" when script succeeded (exit code 0) - Red dot + "Failed with exit code X" when script failed - Gray dot + "Process was killed" when killed 2. **`tasks.json` (translations):** - Added `statusRunning`, `statusSuccess`, `statusFailed`, and `statusKilled` translation keys The status indicator will update in real-time as the script executes since it's computed from `latestProcess` which is updated via the `useExecutionProcesses` hook. * Fixed! The issue was that `exit_code` can be `null` when the process completes successfully, and comparing `null !== BigInt(0)` returns `true`, which incorrectly triggered the error state. The fix: - Treat `exit_code === null` as success (same as exit code 0) - Only show error when `exit_code` is explicitly a non-zero value * Fixed. The issue was that `exit_code` comes back as a regular number `0` from the API, but the TypeScript type says `bigint`. Using `Number(exitCode) === 0` handles both cases correctly and satisfies the type checker. * Fixed! The translation key was missing the `buttons.` namespace prefix. It now uses `t('common:buttons.close')` which maps to "Close". * Done. Added `bg-error` and `text-error` classes alongside the `destructive` classes for the error state. * styles * Done! I've fixed the issue by passing `sessionId` to the `ScriptFixerDialog` in all three files: 1. **`PreviewPanel.tsx`** - Gets `sessionId` from `devServerProcesses[0]?.session_id` 2. **`PreviewBrowserContainer.tsx`** - Added `devServerProcesses` to destructuring from `usePreviewDevServer` and passes `sessionId` 3. **`PreviewControlsContainer.tsx`** - Already had `devServerProcesses`, just added `sessionId` to the dialog call Now when you click "Save and Test" for a dev server script, the dialog will receive the session ID, connect to the WebSocket stream, and show the execution logs in real-time. * rollback trigger the ScriptFixer for old UI * Done! I've extended the ScriptFixerDialog to support cleanup scripts. Here's a summary of the changes: **1. `frontend/src/i18n/locales/en/tasks.json`** - Added `cleanupScriptTitle: "Fix Cleanup Script"` translation key **2. `frontend/src/components/dialogs/scripts/ScriptFixerDialog.tsx`** - Updated `ScriptType` from `'setup' | 'dev_server'` to `'setup' | 'cleanup' | 'dev_server'` - Updated `latestProcess` filter to use `'cleanupscript'` run_reason for cleanup scripts - Updated script fetching logic to read `cleanup_script` field - Updated both `handleSave` and `handleSaveAndTest` to save `cleanup_script` field - Updated execution logic to call `attemptsApi.runCleanupScript(workspaceId)` for cleanup scripts - Updated dialog title to show "Fix Cleanup Script" for cleanup type - Updated placeholder text to show `#!/bin/bash\nrm -rf node_modules` for cleanup scripts **3. `frontend/src/components/ui-new/NewDisplayConversationEntry.tsx`** - Fixed the scriptType mapping so "Cleanup Script" maps to `'cleanup'` instead of incorrectly mapping to `'setup'` * All tasks are complete. Here's a summary of the changes I made: ## Summary I've implemented the ScriptFixerDialog in the old UI for both `DisplayConversationEntry.tsx` and `PreviewPanel.tsx`. ### Changes Made: **1. `DisplayConversationEntry.tsx`** (`frontend/src/components/NormalizedConversation/DisplayConversationEntry.tsx`): - Added imports for `ScriptFixerDialog`, `ScriptType`, `useAttemptRepo`, `Button`, and `Wrench` icon - Added `SCRIPT_TOOL_NAMES` constant to identify script entries - Added `getScriptType()` helper function to map tool names to script types - Created `ScriptToolCallCard` component that wraps `ToolCallCard` and adds a "Fix Script" button for failed scripts - Modified `renderToolUse()` to detect script entries and render them with `ScriptToolCallCard` **2. `PreviewPanel.tsx`** (`frontend/src/components/panels/PreviewPanel.tsx`): - Added imports for `ScriptFixerDialog`, `useAttemptRepo`, and `Wrench` icon - Added `useAttemptRepo(attemptId)` hook to get repos - Added `handleFixDevScript()` function that opens the ScriptFixerDialog - Added `canFixDevScript` computed value - Added "Fix Dev Script" button in the troubleshooting alert next to the "Stop and Edit" button **3. Translation Keys**: - Added `conversation.fixScript: "Fix Script"` in `en/common.json` - Added `preview.troubleAlert.fixScript: "Fix Dev Script"` in `en/tasks.json` The implementation follows the same pattern as the new UI's `ScriptEntryWithFix` component in `NewDisplayConversationEntry.tsx`, using `useAttemptRepo` hook to fetch repos instead of `useWorkspaceContext` (which is only available in the new design scope). * Cleanup script changes for workspace 5496d4c0-d1d3-4e6a-b9d0-6a421270eeac * All tasks are complete. Here's a summary of the fixes made: ## Summary of Changes ### Problem The "Fix Dev Script" button was only showing in the troubleshooting alert, which only appears when a dev server is **running** but slow to respond. When the dev server script **fails to start** (exits with error), there are no running dev servers, so the user saw `NoServerContent` without any fix option. ### Solution **1. `PreviewPanel.tsx`** - Added failed dev server detection: ```typescript const failedDevServerProcess = devServerProcesses.find( (p) => p.status === 'failed' || (p.status === 'completed' && p.exit_code !== null && p.exit_code !== 0n) ); const hasFailedDevServer = Boolean(failedDevServerProcess); ``` And passed `hasFailedDevServer` and `onFixDevScript` to `NoServerContent`. **2. `NoServerContent.tsx`** - Added new props and "Fix Dev Script" button: - Added `hasFailedDevServer?: boolean` and `onFixDevScript?: () => void` props - Added fix button that shows when dev server has failed **3. Translation Keys** - Added `preview.noServer.fixScript: "Fix Dev Script"` to `en/tasks.json` Now when a user tries to start a dev server and it fails, they'll see the "Fix Dev Script" button in the `NoServerContent` view, allowing them to open the ScriptFixerDialog to fix the script. * Done! I've successfully added all the Script Fixer i18n translations to the 5 non-English locale files: **Summary of changes:** ### common.json files (5 files updated) Added `fixScript` key to the `conversation` section: - **es**: `"Corregir Script"` - **ja**: `"スクリプトを修正"` - **ko**: `"스크립트 수정"` - **zh-Hans**: `"修复脚本"` - **zh-Hant**: `"修復腳本"` ### tasks.json files (5 files updated) 1. Added `fixScript` to `preview.noServer` section 2. Added `fixScript` to `preview.troubleAlert` section 3. Added the entire `scriptFixer` section with 17 keys including: - `title`, `setupScriptTitle`, `cleanupScriptTitle`, `devServerTitle` - `scriptLabel`, `logsLabel` - `saveButton`, `saveAndTestButton`, `saving`, `testing` - `noLogs`, `selectRepo`, `fixScript` - `statusRunning`, `statusSuccess`, `statusFailed`, `statusKilled` All type checks (`pnpm run check`) passed successfully. * Both lint and type check pass now. Here's a summary of the lint fixes I made: **Fixed lint issues in:** 1. **`ScriptFixerDialog.tsx`** (lines 198 and 253): - Added missing `repos` dependency to both `handleSave` and `handleSaveAndTest` useCallback hooks - Removed unnecessary `modal` dependency from `handleSaveAndTest` 2. **`NewDisplayConversationEntry.tsx`** (line 658): - Used a `useRef` pattern to access `repos` inside the `handleFix` callback without including it in the dependency array, since the `repos` array is recreated on every render from context * All checks pass. The fix is complete. **Summary:** The "Fix Script" button in `PreviewControls` will now only appear when the latest dev server process has a status of `'failed'`. When the dev server is running, completed successfully, or was killed by the user, the button will be hidden.
2026-01-12 16:54:38 +00:00
/**
* Script entry with fix button for failed scripts
*/
function ScriptEntryWithFix({
title,
processId,
exitCode,
status,
workspaceId,
sessionId,
}: {
title: string;
processId: string;
exitCode: number | null;
status: ToolStatus;
workspaceId?: string;
sessionId?: string;
}) {
// Try to get repos from workspace context - may not be available in all contexts
let repos: RepoWithTargetBranch[] = [];
try {
const workspaceContext = useWorkspaceContext();
repos = workspaceContext.repos;
} catch {
// Context not available, fix button won't be shown
}
// Use ref to access current repos without causing callback recreation
const reposRef = useRef(repos);
reposRef.current = repos;
const handleFix = useCallback(() => {
const currentRepos = reposRef.current;
if (!workspaceId || currentRepos.length === 0) return;
// Determine script type based on title
const scriptType: ScriptType =
title === 'Setup Script'
? 'setup'
: title === 'Cleanup Script'
? 'cleanup'
: 'dev_server';
ScriptFixerDialog.show({
scriptType,
repos: currentRepos,
workspaceId,
sessionId,
initialRepoId: currentRepos.length === 1 ? currentRepos[0].id : undefined,
});
}, [title, workspaceId, sessionId]);
// Only show fix button if we have the necessary context
const canFix = workspaceId && repos.length > 0;
return (
<ChatScriptEntry
title={title}
processId={processId}
exitCode={exitCode}
status={status}
onFix={canFix ? handleFix : undefined}
/>
);
}
2026-01-08 22:14:38 +00:00
/**
* Error message entry with expandable content
*/
function ErrorMessageEntry({
content,
expansionKey,
}: {
content: string;
expansionKey: string;
}) {
const [expanded, toggle] = usePersistedExpanded(
`error:${expansionKey}`,
false
);
return (
<ChatErrorMessage content={content} expanded={expanded} onToggle={toggle} />
);
}
const NewDisplayConversationEntrySpaced = (props: Props) => {
const { isEntryGreyed } = useMessageEditContext();
const isGreyed = isEntryGreyed(props.expansionKey);
return (
<div
className={cn(
'my-base px-double',
isGreyed && 'opacity-50 pointer-events-none'
)}
>
<NewDisplayConversationEntry {...props} />
</div>
);
};
export default NewDisplayConversationEntrySpaced;