Skip to content

Conversation

@moritzWa
Copy link
Contributor

@moritzWa moritzWa commented Jan 3, 2026

Summary

This PR improves form inputs and modal consistency across the app:

SlugInput Component

  • New SlugInput component that auto-normalizes values to valid slugs (branch names, repo names)
  • Converts spaces to hyphens as user types
  • Blocks invalid characters (only a-z, 0-9, - allowed)
  • Shows toast notification when invalid characters are attempted
  • Handles paste with normalization

RadioGroup Component

  • New shadcn RadioGroup component (uses @radix-ui/react-radio-group)
  • Replaces raw HTML radio inputs with proper design system component

TaskModal Refactor

The original TaskModal.tsx was 828 lines with a manual Card+createPortal implementation. This PR:

  • Refactors to use shadcn Dialog - consistent with NewProjectModal
  • Extracts TaskAdvancedSettings into separate component (487 lines)
  • TaskModal now 426 lines - focused on core modal logic
  • Uses DialogHeader, DialogFooter properly
  • Removes pr-12 hack and absolute X button positioning
  • Better accessibility (focus trap, escape key, etc.)

NewProjectModal Updates

  • Uses SlugInput for repository name (auto-normalizes)
  • Uses RadioGroup for visibility selector (proper design system)
  • Removes hardcoded border-gray-300 styling

Changes Summary

  • slug-input.tsx - New component
  • radio-group.tsx - New shadcn component
  • TaskModal.tsx - Refactored to Dialog (828 → 426 lines)
  • TaskAdvancedSettings.tsx - New extracted component (487 lines)
  • NewProjectModal.tsx - Uses SlugInput and RadioGroup

Test plan

  • Create a new task, verify spaces convert to hyphens in name field
  • Try typing special characters, verify toast appears and they're blocked
  • Expand "Advanced options" - verify accordion works within Dialog
  • Create a new project, verify SlugInput and RadioGroup work
  • Verify Dialog closes on Escape key and clicking outside
  • Test on both light and dark themes

Note

  • Headless agent orchestration: New src/main/services/HeadlessAgentService.ts to run Claude headless agents with streaming progress, timeouts, parallel runHeadlessAgents, getWorktreeDiff, and runJudge for A/B solution comparison.
  • Debate UI: New DebateProgressCard and DebateResultsView to visualize parallel agent runs and judging outcomes.
  • Task modal refactor: TaskModal rewritten to Dialog, with advanced options extracted to TaskAdvancedSettings and integration logic to hooks/useIntegrationStatus; adds experimental "debate mode" toggle for Claude.
  • New UI components: ui/slug-input (auto-normalized slugs) and ui/radio-group (Radix-based). NewProjectModal, RunLauncher, and several components (FeedbackModal, BrowserPane, BaseBranchControls, delete buttons) updated to use design-system Input/Textarea/Checkbox/RadioGroup and minor z-index fixes.
  • Deps: Adds @radix-ui/react-radio-group (and peer internals).

Written by Cursor Bugbot for commit 966a8eb. This will update automatically on new commits. Configure here.

- Create SlugInput component that auto-normalizes values to valid slugs:
  - Converts spaces to hyphens as user types
  - Blocks invalid characters (only a-z, 0-9, - allowed)
  - Shows toast notification when invalid characters are attempted
  - Handles paste with normalization

- Update TaskModal:
  - Use SlugInput for task name
  - Remove redundant branch name preview section (saves vertical space)

- Update NewProjectModal:
  - Use SlugInput for repository name
  - Auto-normalize input instead of disabling button on invalid chars
@vercel
Copy link

vercel bot commented Jan 3, 2026

@moritzWa is attempting to deploy a commit to the General Action Team on Vercel.

A member of the Team first needs to authorize it.

Use lenient normalization while typing (preserves trailing hyphens).
Full normalization only applied on submission.

- normalizeForTyping: used during input, allows trailing hyphens
- normalizeToSlug: full normalization for final values
- Add shadcn RadioGroup component (uses @radix-ui/react-radio-group)
- Replace raw radio inputs with RadioGroup in NewProjectModal
- Removes hardcoded border-gray-300 styling
Major refactor of TaskModal:
- Replace manual Card+createPortal+motion with shadcn Dialog
- Remove pr-12 hack and absolute X button (Dialog handles this)
- Extract TaskAdvancedSettings into separate component (~300 lines)

TaskModal.tsx: 828 → 426 lines
TaskAdvancedSettings.tsx: new file, 487 lines

Benefits:
- Consistent modal implementation with NewProjectModal
- Better separation of concerns
- Proper Dialog accessibility (focus trap, escape key, etc.)
- Expandable advanced section works correctly within Dialog
@moritzWa moritzWa changed the title feat(ui): add SlugInput component for constrained name inputs feat(ui): SlugInput, RadioGroup components + TaskModal Dialog refactor Jan 3, 2026
moritzWa and others added 13 commits January 3, 2026 18:00
…index

- Extract Linear/GitHub/Jira connection logic to useIntegrationStatus hook
- Reduces TaskModal from ~430 to ~315 lines
- Fix z-index for Popover, Select, AlertDialog (z-50 → z-[1000]/z-[999])
- Dropdowns now render above Dialog overlays
- Revert z-index changes to shadcn components (popover, select, alert-dialog)
- Use modal={false} on Dialog to allow Popover to render correctly
- Radix Dialog in modal mode blocks portal content outside dialog
- Replace native checkboxes with Checkbox in:
  - TaskAdvancedSettings (2)
  - ProjectDeleteButton
  - TaskDeleteButton
  - BrowserPreviewSettingsCard
- Replace native textareas with Textarea in:
  - TaskAdvancedSettings
  - FeedbackModal
  - RunLauncher
- Replace native inputs with Input in:
  - FeedbackModal
  - BaseBranchControls
  - BrowserPane
  - RunLauncher
- Replace native radio buttons with RadioGroup in:
  - RunLauncher
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the final PR Bugbot will review for you during this billing cycle

Your free Bugbot reviews will reset on February 7

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

}
},
[]
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jira credential property name mismatch breaks connection

The handleJiraConnect function passes credentials with site property, but the API at jiraSaveCredentials expects siteUrl. Looking at src/main/ipc/jiraIpc.ts, the handler reads args.siteUrl, so the site URL will always be empty, causing Jira connection to fail with a validation error. The old code in TaskModal.tsx correctly mapped the field to siteUrl.

Additional Locations (1)

Fix in Cursor Fix in Web

setIsCreating(false);
}
}, [hasAutoApproveSupport, autoApprove]);
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debate mode toggle has no effect on task creation

The debateMode state is used to change the button text from "Create" to "Start Debate", but the value is never passed to onCreateTask. When a user enables debate mode and clicks "Start Debate", the modal simply creates a normal task. The HeadlessAgentService for debate functionality exists but isn't wired up to this flow.

Additional Locations (1)

Fix in Cursor Fix in Web

elapsedMs,
error: code !== 0 ? `Process exited with code ${code}` : undefined,
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timeout causes spurious 'complete' progress event after resolution

When a timeout occurs, the promise is resolved with an error, then kill() terminates the process. However, the exit handler still fires afterward and emits a progress event with type: 'complete'. There's no flag to track that the promise was already resolved by timeout, so consumers receive a misleading 'complete' progress event after the timeout error was already reported. This would cause inconsistent state in the DebateProgressCard UI when debate mode is used.

Additional Locations (1)

Fix in Cursor Fix in Web

return () => {
cancel = true;
};
}, [isOpen]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jira connection check throws TypeError if API missing

The Jira connection check calls api?.jiraCheckConnection?.() which returns undefined if the API is missing, then immediately chains .then() on the result. This throws a TypeError when undefined.then() is called. The Linear connection check correctly guards against this with if (!api?.linearCheckConnection) { return; } before calling the API, but the Jira check is missing this guard.

Fix in Cursor Fix in Web

<AnimatePresence>
{linearSetupOpen ? (
<motion.div
className="fixed inset-0 z-[130] flex items-center justify-center px-3"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nested modals hidden behind Dialog due to z-index

The Linear and Jira setup modals use z-[130] but the refactored parent Dialog uses z-[998] for its overlay (in dialog.tsx). This is a regression from the old createPortal implementation which used z-[100] for the parent. The nested modals will render behind the Dialog overlay and be invisible/unreachable, preventing users from connecting Linear or Jira integrations from within the Task Modal.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants