add pane subsystem to the daemon + IPC event broadcast
Implements the Tier-1 pane subsystem from D-006: spawn / list / focus / close / write / resize / tail commands, plus pane.spawned / output / exit / resized / focused / closed events. PaneRegistry owns per-pane PtySession lifecycles and id generation (p_N); a DaemonEventSink seam keeps pane code decoupled from the IPC server package. DaemonServer.broadcast() fans events out to every connected client. Per-client subsystem/id filtering (`tail --filter pane:p_7`) is deferred — Tier 1 broadcasts everything and the subscriber discards. Panes carry a `kind:` field (terminal | claude). Step 7 (builtin.claude) adds the claude-specific pane flow on top of this generic substrate — the subsystem itself stays neutral. 14 new core tests: registry unit coverage (spawn → pane.spawned event, output → base64 events, write/resize/close round-trips, idempotent close, claude kind on the wire) plus dispatcher coverage (argv validation, unknown-id → not-found, text vs bytes_b64, etc). All 37 core tests pass in ~3s under test-core. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
/// A single active pane in the daemon.
|
||||
///
|
||||
/// Owns a [PtySession] plus whatever pane-level metadata the UI +
|
||||
/// CLI need. The `kind:` field distinguishes general-purpose terminal
|
||||
/// panes from Claude panes (D-041, not yet landed) from whatever
|
||||
/// future pane-shaped surface Tier 1+ grows.
|
||||
library;
|
||||
|
||||
import '../pty/session.dart';
|
||||
|
||||
/// Kind of a pane. Keep this enum small and explicit — each kind
|
||||
/// typically pairs with a bundled extension that manages its
|
||||
/// lifecycle (`builtin.terminal`, `builtin.claude`).
|
||||
enum PaneKind {
|
||||
terminal,
|
||||
claude;
|
||||
|
||||
String get wire => name;
|
||||
|
||||
static PaneKind parse(String s) {
|
||||
return PaneKind.values.firstWhere(
|
||||
(v) => v.wire == s,
|
||||
orElse: () => throw ArgumentError.value(s, 'kind', 'unknown pane kind'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A live pane. Thin wrapper over [PtySession] — the registry is what
|
||||
/// owns the session lifecycle; consumers of this class read state and
|
||||
/// call [write] / [resize] via the session.
|
||||
class Pane {
|
||||
Pane({
|
||||
required this.id,
|
||||
required this.kind,
|
||||
required this.session,
|
||||
required this.argv,
|
||||
this.cwd,
|
||||
this.title,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final PaneKind kind;
|
||||
final PtySession session;
|
||||
final List<String> argv;
|
||||
final String? cwd;
|
||||
final String? title;
|
||||
|
||||
int get pid => session.pid;
|
||||
bool get isClosed => session.isClosed;
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'id': id,
|
||||
'kind': kind.wire,
|
||||
'pid': pid,
|
||||
'argv': argv,
|
||||
if (cwd != null) 'cwd': cwd,
|
||||
if (title != null) 'title': title,
|
||||
'closed': isClosed,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user