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]);
|
|
|
|
|
|
2026-01-12 17:49:52 +00:00
|
|
|
// 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;
|