dissolve app/ into repo root (D-056)
Single Flutter package at the repo root. All code, tests, assets, and platform directories moved from app/ to root. Package renamed from clide_app to clide — all imports rewritten. Merged pubspec combines core (ffi) and app (flutter, yaml, xterm) dependencies. Makefile simplified: no APP_PRESENT conditionals, no cd, no daemon lifecycle. 317 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
/// 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;
|
||||
|
||||
@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: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 6),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ClideText(
|
||||
'Problems (${c.problems.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 && c.problems.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 c.problems) _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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user