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:
2026-04-23 00:37:20 +02:00
co-authored by Claude Opus 4.6
parent a526c5b9b7
commit 46329700d5
394 changed files with 978 additions and 1090 deletions
+82
View File
@@ -0,0 +1,82 @@
/// 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/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();
}
}