make sidebar filter boxes CLI-addressable via the MessageBus (T-270)
The sidebar/dock filter fields (the shared ClideFilterBox) had no CLI peer — a one-way, UI-only affordance that broke D-6 parity. Add the drive+observe verb `clide ui filter <address> [<text>]`, routed entirely through the kernel MessageBus pub/sub so a box reacts to a published message identically whether the trigger was a UI keystroke or the CLI — keeping extensions first-class (no dispatcher→widget wiring). - ClideFilterBox gains an `address`; when set it listens on `filter.set` for its address and republishes its value on `filter.state`. Null address keeps the box a kernel-free UI widget. - FilterStateCache (new kernel service) caches the latest `filter.state` per address — the bus has no retention, so this backs the observe-half. - ui.filter: with text → publishes `filter.set` (drive); without → reads the cache (observe). Honest toolError when there is no live UI. - Address every box: decisions/tickets/files/git/output/problems panes, the four search boxes, and the pql search/query/markdown inputs. Addresses are the ids from `clide pane list` (e.g. decisions.panel). settings.json: allow the `clide` CLI + relevant skills. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'message_bus.dart';
|
||||
|
||||
/// Caches the latest filter value per address, fed by `filter.state`
|
||||
/// messages on the [MessageBus] (T-270, D-6 parity).
|
||||
///
|
||||
/// Sidebar filter boxes are addressable from the CLI: `clide ui filter`
|
||||
/// with an address and text publishes a `filter.set` message the box
|
||||
/// reacts to (the drive-half). The observe-half — the same verb with no
|
||||
/// text — needs to read the box's *current* value back, but the bus is
|
||||
/// a plain broadcast stream with no retention. Each box republishes its
|
||||
/// value on the `filter.state` channel whenever it changes; this cache
|
||||
/// listens once and remembers the latest per address, giving the observe
|
||||
/// verb something to read.
|
||||
///
|
||||
/// Pure Dart — no Flutter import — so it serialises through the kernel and
|
||||
/// stays usable from `dart test`.
|
||||
class FilterStateCache {
|
||||
FilterStateCache({required MessageBus messages}) {
|
||||
_sub = messages.subscribe(channel: 'filter.state').listen((m) {
|
||||
_values[m.publisher] = m.data['query'] as String? ?? '';
|
||||
});
|
||||
}
|
||||
|
||||
final Map<String, String> _values = {};
|
||||
StreamSubscription<Message>? _sub;
|
||||
|
||||
/// The last reported filter value for [address], or null if no box at
|
||||
/// that address has reported yet.
|
||||
String? get(String address) => _values[address];
|
||||
|
||||
void dispose() {
|
||||
_sub?.cancel();
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import 'package:clide/kernel/src/commands/palette.dart';
|
||||
import 'package:clide/kernel/src/commands/registry.dart';
|
||||
import 'package:clide/kernel/src/dialog.dart';
|
||||
import 'package:clide/kernel/src/events/bus.dart';
|
||||
import 'package:clide/kernel/src/events/filter_state.dart';
|
||||
import 'package:clide/kernel/src/events/message_bus.dart';
|
||||
import 'package:clide/kernel/src/extensions_manager.dart';
|
||||
import 'package:clide/kernel/src/files.dart';
|
||||
@@ -47,6 +48,7 @@ class KernelServices {
|
||||
required this.settings,
|
||||
required this.events,
|
||||
required this.messages,
|
||||
required this.filterStates,
|
||||
required this.ipc,
|
||||
required this.theme,
|
||||
required this.i18n,
|
||||
@@ -82,6 +84,10 @@ class KernelServices {
|
||||
final SettingsStore settings;
|
||||
final DaemonBus events;
|
||||
final MessageBus messages;
|
||||
|
||||
/// Latest filter value per addressable box, fed by `filter.state`
|
||||
/// messages — backs the observe-half of `clide ui filter` (T-270).
|
||||
final FilterStateCache filterStates;
|
||||
final DaemonClient ipc;
|
||||
final ThemeController theme;
|
||||
final I18n i18n;
|
||||
@@ -135,6 +141,7 @@ class KernelServices {
|
||||
final log = Logger(sinks: [stderrSink, logRing.add]);
|
||||
final events = sharedBus ?? DaemonBus();
|
||||
final messages = MessageBus();
|
||||
final filterStates = FilterStateCache(messages: messages);
|
||||
|
||||
final settings = SettingsStore(appDir: appDir);
|
||||
await settings.load();
|
||||
@@ -236,6 +243,7 @@ class KernelServices {
|
||||
settings: settings,
|
||||
events: events,
|
||||
messages: messages,
|
||||
filterStates: filterStates,
|
||||
ipc: ipc,
|
||||
theme: theme,
|
||||
i18n: i18n,
|
||||
@@ -292,6 +300,7 @@ class KernelServices {
|
||||
keymap.dispose();
|
||||
textZoom.dispose();
|
||||
await log.dispose();
|
||||
filterStates.dispose();
|
||||
messages.dispose();
|
||||
await events.dispose();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user