Improve the preview functionality (vibe-kanban) (#2010)

* preview styles

* All tasks are complete. Let me provide a summary of the changes made:

## Summary

I've implemented the preview functionality improvements as requested:

### Changes Made

**1. Rust Types (`crates/db/src/models/scratch.rs`)**
- Renamed `PreviewUrlOverrideData` → `PreviewSettingsData`
- Added new fields: `screen_size`, `responsive_width`, `responsive_height`
- Updated `ScratchPayload` and `ScratchType` enum to use `PreviewSettings`

**2. New Hook (`frontend/src/hooks/usePreviewSettings.ts`)**
- Created new `usePreviewSettings` hook (replaced `usePreviewUrlOverride`)
- Manages both URL override and screen size settings
- Persists screen size via the scratch system (per-workspace)
- Exports `ScreenSize` and `ResponsiveDimensions` types

**3. PreviewBrowserContainer (`frontend/src/components/ui-new/containers/PreviewBrowserContainer.tsx`)**
- Added URL controls (input, copy, open in new tab, refresh, clear override)
- Added stop button and related handlers
- Added screen size state management
- Added responsive dimensions resize handlers

**4. PreviewBrowser View (`frontend/src/components/ui-new/views/PreviewBrowser.tsx`)**
- Added floating toolbar above the iframe with:
  - URL input with action buttons
  - Screen size toggle (Desktop/Mobile/Responsive icons)
  - Dimensions display for responsive mode
  - Stop button
- Implemented three screen modes:
  - **Desktop**: Full width/height (current behavior)
  - **Mobile**: 390×844px centered with shadow
  - **Responsive**: Draggable resize handles on right, bottom, and corner edges

**5. PreviewControls View (`frontend/src/components/ui-new/views/PreviewControls.tsx`)**
- Simplified to show only logs
- Removed URL input row and all control buttons
- Renamed section header to "Dev Server Logs"

**6. PreviewControlsContainer (`frontend/src/components/ui-new/containers/PreviewControlsContainer.tsx`)**
- Removed all URL-related state and handlers
- Now only manages logs display and process tab switching

All type checks pass. The screen size preference is persisted per-workspace using the existing scratch system.

* Cleanup script changes for workspace dd65dbe3-2e31-4897-8aa4-b2f8f7971ab7

* All ESLint and TypeScript checks pass. The implementation is complete.

## Summary of Changes

**Files Modified:**

1. **`crates/db/src/models/scratch.rs`** - Renamed `PreviewUrlOverrideData` → `PreviewSettingsData` with new fields for screen size persistence

2. **`crates/server/src/bin/generate_types.rs`** - Updated type export to use new name

3. **`shared/types.ts`** - Regenerated with new types (`PreviewSettingsData`, `ScratchType.PREVIEW_SETTINGS`)

4. **`frontend/src/hooks/usePreviewSettings.ts`** (new) - Replaces `usePreviewUrlOverride.ts`, adds screen size management

5. **`frontend/src/hooks/usePreviewUrlOverride.ts`** - Deleted

6. **`frontend/src/components/ui-new/containers/PreviewBrowserContainer.tsx`** - Added URL controls, stop button, and resize state management

7. **`frontend/src/components/ui-new/views/PreviewBrowser.tsx`** - Added floating toolbar with:
   - URL input with copy/open/refresh/clear buttons
   - Screen size toggle (Desktop/Mobile/Responsive icons)
   - Responsive resize handles
   - Stop button

8. **`frontend/src/components/ui-new/containers/PreviewControlsContainer.tsx`** - Simplified to logs only

9. **`frontend/src/components/ui-new/views/PreviewControls.tsx`** - Simplified to logs only

* Cleanup script changes for workspace dd65dbe3-2e31-4897-8aa4-b2f8f7971ab7

* All checks pass. Here's a summary of the changes made:

## Summary of Changes

### 1. Fixed infinite loop bug (`usePreviewSettings.ts`)
- Added `useMemo` to stabilize the `responsiveDimensions` object reference
- This prevents the useEffect in the container from re-triggering on every render

### 2. Updated toolbar to always show with floating style (`PreviewBrowser.tsx`)
- Toolbar is now always visible (not conditional on `isServerRunning && hasUrl`)
- Changed styling from `border-b border-border` to `rounded-md shadow-md m-base` for a floating appearance
- URL input and action buttons are disabled (with `opacity-50 cursor-not-allowed`) when no URL is available
- Start/Stop button in toolbar changes based on server state:
  - Shows "Start" button when server is not running
  - Shows "Stop" button when server is running
- Simplified the empty state content area to just show a message pointing to the toolbar's Start button

* All checks pass. The fix:

1. **Changed from state to refs** for `isResizing` and `resizeDirection` - these now don't trigger re-renders when they change
2. **Added `localDimensionsRef`** to capture the current dimensions for the `handleEnd` callback without adding it to the effect's dependency array
3. **Effect now only depends on `setResponsiveDimensions`** (a stable callback), so event listeners are registered once on mount and cleaned up on unmount
4. **No more race conditions** - the same event handlers persist throughout the entire drag operation

The resize should now:
- Stop immediately when you release the mouse
- Be smooth without choppiness (no effect re-runs during drag)

* The responsive resize fix is complete. Here's a summary of the changes made:

**In `PreviewBrowserContainer.tsx`:**
1. Added `isResizing` state to track when resizing is active (for UI purposes)
2. Added `setResponsiveDimensionsRef` to store the callback in a ref, preventing the resize effect from re-running when the callback identity changes
3. Changed the resize effect to use an empty dependency array (`[]`) since all external values are now accessed via refs
4. Updated `handleResizeStart` to set `isResizing(true)`
5. Updated `handleEnd` to set `isResizing(false)` and use `setResponsiveDimensionsRef.current`
6. Passed `isResizing` prop to the `PreviewBrowser` view

**In `PreviewBrowser.tsx`:**
1. Added `isResizing` prop to the interface and destructured it
2. Applied `pointer-events-none` class to the iframe when `isResizing` is true - this prevents the iframe from capturing mouse events during resize, ensuring smooth dragging

These changes fix both issues:
- **Resize not stopping**: The effect no longer re-registers listeners when `setResponsiveDimensions` changes
- **Choppy dragging**: The iframe no longer captures mouse events during resize

* All checks pass. The fix is complete. The resize now uses delta-based calculation:

1. **`startPosRef`**: Captures the mouse position when resize starts
2. **`startDimensionsRef`**: Captures the iframe dimensions when resize starts
3. **`handleMove`**: Calculates delta (current position - start position) and applies it to the starting dimensions
4. **`handleEnd`**: Clears both refs when resize ends

This approach works regardless of where the iframe is positioned in the container, fixing both:
- The mouse jump offset (no longer tries to position based on container edge)
- The 50% distance issue (delta is 1:1 with mouse movement)

* Cleanup script changes for workspace dd65dbe3-2e31-4897-8aa4-b2f8f7971ab7

* Done. The resize should now move at 1:1 with the cursor. Since the centered element grows on both sides equally, doubling the delta ensures the edge under the cursor moves the full distance you drag.

* Done! I've added a minimal dark phone frame around the iframe when in mobile mode. The changes:

- **Mobile mode** now wraps the iframe in a phone skeleton with:
  - Dark bezel (`bg-gray-900`)
  - Rounded outer corners (`rounded-[3rem]`)
  - Padding for bezel width (`p-3`)
  - Shadow (`shadow-xl`)
  - Rounded inner screen area (`rounded-[2.5rem]`)

- **Desktop and responsive modes** remain unchanged

You can test it by running `pnpm run dev:qa` and switching to mobile view in the preview browser.

* styles

* I've completed the implementation. Here's a summary of the changes:

## Summary

Created two new reusable button components and updated PreviewBrowser.tsx to standardize button styles:

### New Components

1. **`IconButton.tsx`** (`frontend/src/components/ui-new/primitives/IconButton.tsx`)
   - Standalone icon button with consistent styling
   - Supports `default` and `tertiary` variants
   - Includes proper hover states (`bg-secondary/50`), disabled states, and transitions

2. **`IconButtonGroup.tsx`** (`frontend/src/components/ui-new/primitives/IconButtonGroup.tsx`)
   - Container component for grouped icon buttons with shared border
   - `IconButtonGroupItem` for individual buttons with `active` state support
   - Consistent styling with border, padding, and hover/active states

### Updated PreviewBrowser.tsx

- **URL action buttons** (Clear, Copy, Open in new tab, Refresh): Now use `IconButton` component with consistent hover backgrounds
- **Screen size toggle** (Desktop, Mobile, Responsive): Now uses `IconButtonGroup` and `IconButtonGroupItem` with proper active state styling
- **Start/Stop button**: Kept using `PrimaryButton` (already consistent)

### Visual Consistency Achieved

All toolbar buttons now share:
- Consistent padding (`p-half`)
- Consistent hover states (`bg-secondary/50` background)
- Consistent active states (`bg-secondary` background) for toggles
- Consistent disabled states (`opacity-40 cursor-not-allowed`)
- Consistent icon sizing (`size-icon-sm`) with `weight="bold"`
- Consistent border radius (`rounded-sm`)

* Done. The URL action buttons (Copy, Open in new tab, Refresh) are now grouped in an `IconButtonGroup` with a shared border, matching the visual style of the screen size toggle group.

The toolbar now has three visually consistent button groups:
1. **URL input** with optional clear override button
2. **URL actions group** (Copy, Open in new tab, Refresh) - bordered group
3. **Screen size toggle group** (Desktop, Mobile, Responsive) - bordered group
4. **Start/Stop button** (PrimaryButton)

* Done. All changes have been implemented:

1. **Added `iconClassName` prop to `IconButtonGroupItem`** - Supports passing custom classes to the icon (e.g., `animate-spin` for the spinner)

2. **Moved Clear button into URL Actions group** - The Clear Override button (X icon) is now inside the `IconButtonGroup` with Copy, Open, and Refresh buttons

3. **Replaced Pause button with `IconButtonGroupItem`** - The stop/pause button now uses the same `IconButtonGroup` styling as the other toolbar buttons, with spinner animation support when stopping

The toolbar now has consistent visual styling with three button groups:
- **URL Actions**: Clear (conditional), Copy, Open in new tab, Refresh
- **Screen Size Toggle**: Desktop, Mobile, Responsive
- **Stop button**: Pause icon (when server is running)
- **Start button**: Kept as `PrimaryButton` since it has a text label

* Cleanup script changes for workspace dd65dbe3-2e31-4897-8aa4-b2f8f7971ab7

* Done. The Start/Stop button is now a single `IconButtonGroup` with one `IconButtonGroupItem` that:
- Shows Play icon when server is stopped, Pause icon when running
- Shows Spinner icon with animation when starting or stopping
- Has the same size as all other toolbar button groups

* Cleanup script changes for workspace dd65dbe3-2e31-4897-8aa4-b2f8f7971ab7

* Done. Fixed the spinning Play icon bug by making the `iconClassName` logic match the icon selection logic exactly. Now `animate-spin` is only applied when actually showing the `SpinnerIcon`.

* Done. The entire toolbar (URL input, URL actions, screen size toggle) is now disabled when the dev server isn't running. Only the Play/Start button remains enabled so users can start the server.

* The changes compile correctly. Here's a summary of what was fixed:

## Summary

Fixed the "Fix Script" button showing at the wrong time in `PreviewBrowser.tsx`.

**Changes made:**

1. **`PreviewBrowser.tsx`** - Added `hasFailedDevServer?: boolean` prop and updated the condition on line 345 from `{handleFixDevScript && (` to `{hasFailedDevServer && handleFixDevScript && (`

2. **`PreviewBrowserContainer.tsx`** - Added failed dev server detection logic (lines 45-51) that checks for processes with `status === 'failed'` or `status === 'completed'` with non-zero exit code, and passed the `hasFailedDevServer` prop to the view

The "Fix Script" button will now only appear when the last dev server process actually failed, not when it was successful or killed.

* update styles

* All tasks completed. Here's a summary of the changes made:

## Summary

### 1. `IconButtonGroup.tsx` (lines 3, 50-65)
- Added import for `Tooltip` component
- Modified `IconButtonGroupItem` to wrap buttons with `<Tooltip>` when `title` prop is provided
- Removed native `title` attribute from button (tooltip handles it now)

### 2. `en/tasks.json` (lines 125-134)
Added new i18n keys under `preview.toolbar`:
- `clearUrlOverride`: "Clear URL override"
- `desktopView`: "Desktop view"
- `mobileView`: "Mobile view (390x844)"
- `responsiveView`: "Responsive view (resizable)"
- `startDevServer`: "Start dev server"

Added new `preview.loading` section:
- `startingServer`: "Starting dev server..."
- `waitingForServer`: "Waiting for server..."

### 3. `PreviewBrowser.tsx` (lines 160-254, 342-344)
Replaced all hardcoded strings with i18n translations using `t()`:
- URL action buttons (clear override, copy, open in tab, refresh)
- Screen size toggle buttons (desktop, mobile, responsive)
- Start/stop server button
- Loading state messages

All icon buttons now have proper accessible tooltips via the `Tooltip` component instead of native browser `title` attributes, and all strings are internationalized.

* style

* All i18n issues have been fixed. Here's a summary of the changes made:

**1. Added missing translation keys to all non-English locales (es, ja, ko, zh-Hans, zh-Hant):**
- `preview.loading.startingServer`
- `preview.loading.waitingForServer`
- `preview.toolbar.clearUrlOverride`
- `preview.toolbar.desktopView`
- `preview.toolbar.mobileView`
- `preview.toolbar.responsiveView`
- `preview.toolbar.startDevServer`

**2. Fixed hardcoded strings in `PreviewBrowser.tsx`:**
- Replaced dimension separator `x` with HTML entity `&times;`
- Internationalized the "no dev script" setup message with 3 new i18n keys:
  - `preview.noServer.setupTitle`
  - `preview.noServer.editDevScript`
  - `preview.noServer.learnMore`

**3. Added the 3 new keys to all locale files (en, es, ja, ko, zh-Hans, zh-Hant)**

The `check-i18n.sh` script now passes with:
- 128 violations (down from 131 on main - a net improvement!)
- No duplicate keys
- Translation keys consistent across all locales
This commit is contained in:
Louis Knight-Webb
2026-01-13 14:55:57 +00:00
committed by GitHub
parent 34e1b688b1
commit 33419c95f7
17 changed files with 919 additions and 378 deletions

View File

@@ -39,7 +39,7 @@ fn generate_types_content() -> String {
db::models::scratch::DraftFollowUpData::decl(),
db::models::scratch::DraftWorkspaceData::decl(),
db::models::scratch::DraftWorkspaceRepo::decl(),
db::models::scratch::PreviewUrlOverrideData::decl(),
db::models::scratch::PreviewSettingsData::decl(),
db::models::scratch::ScratchPayload::decl(),
db::models::scratch::ScratchType::decl(),
db::models::scratch::Scratch::decl(),