/// State model for the pql sidebar panel. /// /// Manages search, DSL query execution, and markdown file listing. /// All data comes through pql.* IPC verbs. library; import 'dart:async'; import 'package:clide/kernel/kernel.dart'; import 'package:flutter/foundation.dart'; enum PqlView { query, markdown } enum SearchMode { search, dsl } class PqlController extends ChangeNotifier { PqlController({required this.ipc}); final DaemonClient ipc; PqlView _view = PqlView.query; PqlView get view => _view; SearchMode _searchMode = SearchMode.search; SearchMode get searchMode => _searchMode; String? _error; String? get error => _error; bool _loading = false; bool get loading => _loading; List> _results = const []; List> get results => _results; Map _planStatus = const {}; Map get planStatus => _planStatus; void switchView(PqlView v) { if (_view == v) return; _view = v; _results = const []; _error = null; notifyListeners(); switch (v) { case PqlView.markdown: unawaited(loadMarkdownFiles()); case PqlView.query: break; } } void toggleSearchMode() { _searchMode = _searchMode == SearchMode.search ? SearchMode.dsl : SearchMode.search; _results = const []; _error = null; notifyListeners(); } Future search(String terms) async { if (terms.trim().isEmpty) { _results = const []; _error = null; notifyListeners(); return; } _loading = true; _error = null; notifyListeners(); final r = await ipc.request('pql.search', args: { 'terms': terms, 'limit': 50, }); _loading = false; if (!r.ok) { _error = r.error?.message; _results = const []; notifyListeners(); return; } _results = _castList(r.data['results']); notifyListeners(); } Future loadMarkdownFiles({String? glob}) async { _loading = true; notifyListeners(); final r = await ipc.request('pql.files', args: { 'glob': glob ?? '**/*.md', 'limit': 200, }); _loading = false; if (!r.ok) { _error = r.error?.message; notifyListeners(); return; } _error = null; _results = _castList(r.data['files']); notifyListeners(); } Future runQuery(String dsl) async { if (dsl.trim().isEmpty) return; _loading = true; _error = null; notifyListeners(); final r = await ipc.request('pql.query', args: { 'query': dsl, 'limit': 200, }); _loading = false; if (!r.ok) { _error = r.error?.message; _results = const []; notifyListeners(); return; } _results = _castList(r.data['results']); notifyListeners(); } Future loadPlanStatus() async { final r = await ipc.request('pql.plan.status'); if (r.ok) { _planStatus = r.data; notifyListeners(); } } void clearError() { if (_error == null) return; _error = null; notifyListeners(); } static List> _castList(Object? raw) { if (raw is! List) return const []; return [for (final e in raw) (e as Map).cast()]; } }