| import 'package:jaspr/dom.dart'; |
| import 'package:jaspr/jaspr.dart'; |
| import 'package:web/web.dart' as web; |
| |
| import '../../../utils/provider.dart'; |
| import '../../../utils/styles.dart'; |
| import '../../agent/models/agent_run_state.dart'; |
| import '../../agent/view_models/agent_chat_view_model.dart'; |
| import 'icons.dart'; |
| |
| /// A reusable "Fix with AI" button used in the problems panel and |
| /// injected into CodeMirror lint tooltips. |
| /// |
| /// Uses the CSS class `fix-with-ai-btn` which is also applied by |
| /// `LintTooltipEnhancer` for raw DOM buttons in editor tooltips. |
| class FixWithAIButton extends StatelessComponent { |
| const FixWithAIButton({required this.onPressed, this.disabled, super.key}); |
| |
| /// Called when the button is clicked. |
| final void Function() onPressed; |
| |
| /// Optional override to manually disable the button. |
| final bool? disabled; |
| |
| /// Shared styles for the `fix-with-ai-btn` CSS class. |
| /// |
| /// These styles are intentionally global (not scoped to a parent selector) |
| /// so that they also apply to the raw DOM buttons injected by |
| /// `LintTooltipEnhancer` inside CodeMirror tooltips. |
| @css |
| static List<StyleRule> get styles => [ |
| css('.fix-with-ai-btn').styles( |
| display: .inlineFlex, |
| padding: .symmetric(vertical: 3.px, horizontal: 8.px), |
| border: .none, |
| radius: .circular(4.px), |
| cursor: .pointer, |
| transition: .combine([ |
| Transition('background-color', duration: 150.ms, curve: .ease), |
| Transition('color', duration: 150.ms, curve: .ease), |
| Transition('opacity', duration: 150.ms, curve: .ease), |
| ]), |
| alignItems: .center, |
| gap: Gap.all(4.px), |
| color: colorPrimary, |
| fontSize: 11.px, |
| fontWeight: .w500, |
| backgroundColor: Colors.transparent, |
| ), |
| css('.fix-with-ai-btn:not(:disabled):hover').styles( |
| color: colorOnPrimary, |
| backgroundColor: colorPrimary, |
| ), |
| css('.fix-with-ai-btn:disabled').styles( |
| opacity: .5, |
| cursor: .notAllowed, |
| color: colorOnSurfaceVariant, |
| backgroundColor: Colors.transparent, |
| ), |
| ]; |
| |
| @override |
| Component build(BuildContext context) { |
| final viewModel = context.watch<AgentChatViewModel>(); |
| final isRunning = viewModel.runState == AgentRunState.running; |
| final isDisabled = disabled ?? isRunning; |
| |
| return button( |
| classes: 'fix-with-ai-btn', |
| attributes: { |
| 'title': 'Fix with Agent', |
| if (isDisabled) 'disabled': '', |
| }, |
| events: { |
| 'click': (web.Event e) { |
| e.stopPropagation(); |
| if (!isDisabled) { |
| onPressed(); |
| } |
| }, |
| }, |
| [ |
| autoFixHighIcon(), |
| const .text('Fix with Agent'), |
| ], |
| ); |
| } |
| } |