Raise the declared minimums in pubspec.yaml to what our deps already require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist 0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is the binding floor. Pin the exact build toolchain in .fvmrc (Flutter 3.44.1). Moving to the Dart 3.9 language level switches `dart format` to the new "tall" style and enables two new lints. This commit is the resulting mechanical churn, isolated from any behaviour change: - whole-tree `dart format` reformat (tall style) - `dart fix` for unnecessary_underscores + use_null_aware_elements No runtime behaviour change; `make test` green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
131 lines
4.5 KiB
Dart
131 lines
4.5 KiB
Dart
/// Sidebar panel showing project diagnostics from pql.
|
|
library;
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:clide/kernel/kernel.dart';
|
|
import 'package:clide/widgets/widgets.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
import 'problems_controller.dart';
|
|
|
|
class ProblemsView extends StatefulWidget {
|
|
const ProblemsView({super.key});
|
|
|
|
@override
|
|
State<ProblemsView> createState() => _ProblemsViewState();
|
|
}
|
|
|
|
class _ProblemsViewState extends State<ProblemsView> {
|
|
ProblemsController? _controller;
|
|
String _filter = '';
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
if (_controller != null) return;
|
|
final kernel = ClideKernel.of(context);
|
|
_controller = ProblemsController(ipc: kernel.ipc);
|
|
unawaited(_controller!.refresh());
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_controller?.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final c = _controller;
|
|
if (c == null) return const SizedBox.shrink();
|
|
return ListenableBuilder(
|
|
listenable: c,
|
|
builder: (context, _) {
|
|
final tokens = ClideTheme.of(context).surface;
|
|
return Semantics(
|
|
label: 'problems panel',
|
|
container: true,
|
|
explicitChildNodes: true,
|
|
child: () {
|
|
final lf = _filter.toLowerCase();
|
|
final filtered = lf.isEmpty
|
|
? c.problems
|
|
: c.problems.where((p) => p.message.toLowerCase().contains(lf) || p.source.toLowerCase().contains(lf)).toList();
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
ClideFilterBox(address: 'problems.panel', hint: 'Filter problems…', onChanged: (v) => setState(() => _filter = v)),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 2),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: ClideText('Problems (${filtered.length})', fontSize: clideFontCaption, color: tokens.sidebarForeground),
|
|
),
|
|
Semantics(
|
|
button: true,
|
|
label: 'refresh problems',
|
|
child: GestureDetector(
|
|
onTap: () => unawaited(c.refresh()),
|
|
child: MouseRegion(
|
|
cursor: SystemMouseCursors.click,
|
|
child: ClideText('Refresh', fontSize: clideFontCaption, color: tokens.sidebarForeground),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
if (c.loading && c.problems.isEmpty) const Padding(padding: EdgeInsets.all(12), child: ClideText('Scanning…', muted: true)),
|
|
if (!c.loading && filtered.isEmpty) const Padding(padding: EdgeInsets.all(12), child: ClideText('No problems found.', muted: true)),
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [for (final p in filtered) _ProblemRow(problem: p)],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}(),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ProblemRow extends StatelessWidget {
|
|
const _ProblemRow({required this.problem});
|
|
final Problem problem;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final tokens = ClideTheme.of(context).surface;
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
ClideText(problem.source, fontSize: clideFontMono, color: tokens.statusWarning, fontFamily: clideMonoFamily),
|
|
const SizedBox(width: 6),
|
|
Expanded(child: ClideText(problem.message, color: tokens.sidebarForeground, maxLines: 2)),
|
|
],
|
|
),
|
|
if (problem.hint != null)
|
|
Padding(
|
|
padding: const EdgeInsets.only(left: 44, top: 2),
|
|
child: ClideText(problem.hint!, fontSize: clideFontMono, muted: true, fontFamily: clideMonoFamily),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|