test / unit + widget + golden + a11y (push) Failing after 37s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
GitController hydrates from git.status IPC and auto-refreshes on git.changed events. Panel shows staged/unstaged/untracked/conflict groups with per-file actions and an inline commit field. DiffController renders unified diffs with line numbers, addition/ removal colouring, and a staged/unstaged toggle. Both extensions upgraded from stubs to 0.1.0. ClideText gains an optional fontFamily parameter so diff lines can render in JetBrainsMono without breaking golden tests (font still inherits from ambient DefaultTextStyle by default). Co-Authored-By: Claude <noreply@anthropic.com>
83 lines
1.9 KiB
Dart
83 lines
1.9 KiB
Dart
/// State model for the diff workspace tab.
|
|
///
|
|
/// Holds the parsed diff data for a single file (or all files). Hydrates
|
|
/// via `git.diff` IPC, subscribes to `git.changed` events to refresh.
|
|
library;
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:clide_app/kernel/kernel.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
class DiffController extends ChangeNotifier {
|
|
DiffController({required this.ipc, required this.events}) {
|
|
_eventSub = events.on<DaemonEvent>().listen(_onEvent);
|
|
}
|
|
|
|
final DaemonClient ipc;
|
|
final EventBus events;
|
|
|
|
StreamSubscription<DaemonEvent>? _eventSub;
|
|
|
|
List<Map<String, Object?>> _diffs = const [];
|
|
List<Map<String, Object?>> get diffs => _diffs;
|
|
|
|
bool _staged = false;
|
|
bool get showStaged => _staged;
|
|
|
|
String? _error;
|
|
String? get error => _error;
|
|
|
|
bool _loading = false;
|
|
bool get loading => _loading;
|
|
|
|
/// Load diffs. Optionally filter to [paths] and toggle [staged].
|
|
Future<void> load({
|
|
bool staged = false,
|
|
List<String> paths = const [],
|
|
}) async {
|
|
_staged = staged;
|
|
_loading = true;
|
|
notifyListeners();
|
|
|
|
final r = await ipc.request('git.diff', args: {
|
|
'staged': staged,
|
|
if (paths.isNotEmpty) 'paths': paths,
|
|
});
|
|
|
|
_loading = false;
|
|
if (!r.ok) {
|
|
_error = r.error?.message ?? 'git.diff failed';
|
|
notifyListeners();
|
|
return;
|
|
}
|
|
|
|
_error = null;
|
|
_diffs = _castList(r.data['diffs']);
|
|
notifyListeners();
|
|
}
|
|
|
|
void toggleStaged() {
|
|
unawaited(load(staged: !_staged));
|
|
}
|
|
|
|
void _onEvent(DaemonEvent e) {
|
|
if (e.subsystem != 'git') return;
|
|
if (e.kind == 'git.changed') {
|
|
unawaited(load(staged: _staged));
|
|
}
|
|
}
|
|
|
|
static List<Map<String, Object?>> _castList(Object? raw) {
|
|
if (raw is! List) return const [];
|
|
return [for (final e in raw) (e as Map).cast<String, Object?>()];
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_eventSub?.cancel();
|
|
_eventSub = null;
|
|
super.dispose();
|
|
}
|
|
}
|