opencode-subagents-sidebar-plugin 0.1.3 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tui.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { use as _$use } from "@opentui/solid";
1
2
  import { createComponent as _$createComponent } from "@opentui/solid";
2
3
  import { effect as _$effect } from "@opentui/solid";
3
4
  import { memo as _$memo } from "@opentui/solid";
@@ -8,9 +9,16 @@ import { setProp as _$setProp } from "@opentui/solid";
8
9
  import { createElement as _$createElement } from "@opentui/solid";
9
10
  /** @jsxImportSource @opentui/solid */
10
11
 
11
- import { elapsedLabel } from "./activity.js";
12
+ import { Portal, useTerminalDimensions } from "@opentui/solid";
13
+ import { elapsedLabel, latestAssistantActivity } from "./activity.js";
14
+ import { placePopover } from "./popover.js";
12
15
  import { canOpenTaskSession, isTaskPart, isVisibleTask, resolveTaskModel, taskAgent, taskDescription, taskModeLabel, taskModelLabel, taskSessionID, taskStatusLabel, taskVariantLabel } from "./tasks.js";
13
16
  import { createEffect, createMemo, createSignal, For, on, onCleanup, onMount, Show } from "solid-js";
17
+ const POPOVER_SIZE = {
18
+ width: 52,
19
+ height: 14
20
+ };
21
+ const POPOVER_Z_INDEX = 100;
14
22
  function resolvedModel(api, part, session, sessionID) {
15
23
  return resolveTaskModel(part, session, sessionID ? api.state.session.messages(sessionID) : []);
16
24
  }
@@ -18,9 +26,15 @@ function modelName(api, model) {
18
26
  const name = model ? api.state.provider.find(provider => provider.id === model.providerID)?.models[model.modelID]?.name : undefined;
19
27
  return taskModelLabel(model, name);
20
28
  }
21
- function TaskDetails(props) {
29
+ function TaskPopover(props) {
30
+ const viewport = useTerminalDimensions();
31
+ const [hydratedChildMessages, setHydratedChildMessages] = createSignal();
32
+ const placement = createMemo(() => placePopover(props.ownership().anchor, viewport(), POPOVER_SIZE));
33
+ const callID = () => props.ownership().callID;
34
+ let portalWrapper;
35
+ let hydrationRequest = 0;
22
36
  const fallback = () => {
23
- const known = props.fallbackFor(props.callID);
37
+ const known = props.fallbackFor(callID());
24
38
  if (known) return known;
25
39
  const sessionID = taskSessionID(props.fallbackPart);
26
40
  return {
@@ -32,7 +46,7 @@ function TaskDetails(props) {
32
46
  };
33
47
  const part = createMemo(() => {
34
48
  props.revision();
35
- return props.tasksByCallID().get(props.callID) ?? fallback().part;
49
+ return props.tasksByCallID().get(callID()) ?? fallback().part;
36
50
  });
37
51
  const childSessionID = () => taskSessionID(part() ?? fallback().part) ?? fallback().sessionID;
38
52
  const childSession = () => {
@@ -45,7 +59,71 @@ function TaskDetails(props) {
45
59
  return resolvedModel(props.api, part() ?? fallback().part, childSession(), childSessionID());
46
60
  });
47
61
  const canOpen = () => canOpenTaskSession(childSessionID(), props.currentSessionID);
48
- const openSession = () => {
62
+ const liveChildMessages = createMemo(() => {
63
+ props.revision();
64
+ const sessionID = childSessionID();
65
+ if (!sessionID) return undefined;
66
+ const messages = props.api.state.session.messages(sessionID);
67
+ const parts = new Map();
68
+ for (const message of messages) {
69
+ const messageParts = props.api.state.part(message.id);
70
+ parts.set(message.id, messageParts);
71
+ }
72
+ return {
73
+ sessionID,
74
+ messages,
75
+ parts
76
+ };
77
+ });
78
+ const ownsHydration = (token, sessionID, request) => {
79
+ if (props.api.lifecycle.signal.aborted || request !== hydrationRequest) return false;
80
+ return props.ownership().token === token && childSessionID() === sessionID;
81
+ };
82
+ const hydrateChildMessages = async (token, sessionID, request) => {
83
+ try {
84
+ const result = await props.api.client.session.messages({
85
+ sessionID,
86
+ directory: props.api.state.path.directory
87
+ }, {
88
+ throwOnError: true
89
+ });
90
+ if (!ownsHydration(token, sessionID, request)) return;
91
+ setHydratedChildMessages({
92
+ token,
93
+ sessionID,
94
+ messages: result.data
95
+ });
96
+ } catch {}
97
+ };
98
+ createEffect(() => {
99
+ props.revision();
100
+ props.now();
101
+ const ownership = props.ownership();
102
+ const sessionID = childSessionID();
103
+ if (!sessionID) {
104
+ hydrationRequest += 1;
105
+ setHydratedChildMessages(undefined);
106
+ return;
107
+ }
108
+ const request = ++hydrationRequest;
109
+ void hydrateChildMessages(ownership.token, sessionID, request);
110
+ });
111
+ const latestResponse = createMemo(() => {
112
+ const ownership = props.ownership();
113
+ const sessionID = childSessionID();
114
+ if (!sessionID) return undefined;
115
+ const live = liveChildMessages();
116
+ const liveActivity = live ? latestAssistantActivity(live.messages, messageID => live.parts.get(messageID) ?? []) : undefined;
117
+ if (liveActivity) return liveActivity;
118
+ const hydrated = hydratedChildMessages();
119
+ if (!hydrated || hydrated.token !== ownership.token || hydrated.sessionID !== sessionID) return undefined;
120
+ const parts = new Map(hydrated.messages.map(message => [message.info.id, message.parts]));
121
+ return latestAssistantActivity(hydrated.messages.map(message => message.info), messageID => parts.get(messageID) ?? []);
122
+ });
123
+ const responseLabel = () => latestResponse() ?? (childSessionID() ? "Waiting for subagent activity" : "Waiting for child session");
124
+ const stopInside = event => event.stopPropagation();
125
+ const openSession = event => {
126
+ event.stopPropagation();
49
127
  const sessionID = childSessionID();
50
128
  if (!canOpen() || !sessionID) return;
51
129
  props.api.route.navigate("session", {
@@ -53,121 +131,260 @@ function TaskDetails(props) {
53
131
  });
54
132
  props.close();
55
133
  };
134
+ const closePopover = event => {
135
+ event.stopPropagation();
136
+ props.close();
137
+ };
138
+ const applyPortalOverlay = (next = viewport()) => {
139
+ const wrapper = portalWrapper;
140
+ if (!wrapper || wrapper.isDestroyed) return;
141
+ wrapper.position = "absolute";
142
+ wrapper.left = 0;
143
+ wrapper.top = 0;
144
+ wrapper.width = next.width;
145
+ wrapper.height = next.height;
146
+ wrapper.zIndex = POPOVER_Z_INDEX;
147
+ wrapper.focusable = false;
148
+ };
149
+ createEffect(() => applyPortalOverlay(viewport()));
150
+ onCleanup(() => {
151
+ hydrationRequest += 1;
152
+ portalWrapper = undefined;
153
+ });
154
+ return _$createComponent(Portal, {
155
+ get mount() {
156
+ return props.api.renderer.root;
157
+ },
158
+ ref: element => {
159
+ portalWrapper = element;
160
+ applyPortalOverlay();
161
+ },
162
+ get children() {
163
+ var _el$ = _$createElement("box"),
164
+ _el$2 = _$createElement("box"),
165
+ _el$3 = _$createElement("text"),
166
+ _el$4 = _$createElement("b"),
167
+ _el$6 = _$createElement("text"),
168
+ _el$7 = _$createTextNode(`Agent: `),
169
+ _el$8 = _$createElement("text"),
170
+ _el$9 = _$createTextNode(`Assigned: `),
171
+ _el$0 = _$createElement("text"),
172
+ _el$1 = _$createTextNode(`Current: `),
173
+ _el$10 = _$createElement("text"),
174
+ _el$11 = _$createTextNode(`Status: `),
175
+ _el$12 = _$createElement("text"),
176
+ _el$13 = _$createTextNode(`Model: `),
177
+ _el$14 = _$createTextNode(` · `),
178
+ _el$15 = _$createElement("text"),
179
+ _el$16 = _$createTextNode(` · `),
180
+ _el$17 = _$createElement("text"),
181
+ _el$18 = _$createTextNode(`Child: `),
182
+ _el$19 = _$createElement("box"),
183
+ _el$20 = _$createElement("box"),
184
+ _el$21 = _$createElement("text"),
185
+ _el$22 = _$createElement("b"),
186
+ _el$24 = _$createElement("box"),
187
+ _el$25 = _$createElement("text"),
188
+ _el$26 = _$createElement("b");
189
+ _$insertNode(_el$, _el$2);
190
+ _$setProp(_el$, "width", "100%");
191
+ _$setProp(_el$, "height", "100%");
192
+ _$setProp(_el$, "focusable", false);
193
+ _$insertNode(_el$2, _el$3);
194
+ _$insertNode(_el$2, _el$6);
195
+ _$insertNode(_el$2, _el$8);
196
+ _$insertNode(_el$2, _el$0);
197
+ _$insertNode(_el$2, _el$10);
198
+ _$insertNode(_el$2, _el$12);
199
+ _$insertNode(_el$2, _el$15);
200
+ _$insertNode(_el$2, _el$17);
201
+ _$insertNode(_el$2, _el$19);
202
+ _$setProp(_el$2, "position", "absolute");
203
+ _$setProp(_el$2, "focusable", false);
204
+ _$setProp(_el$2, "live", true);
205
+ _$setProp(_el$2, "border", true);
206
+ _$setProp(_el$2, "padding", 1);
207
+ _$setProp(_el$2, "flexDirection", "column");
208
+ _$setProp(_el$2, "overflow", "hidden");
209
+ _$setProp(_el$2, "onMouseUp", stopInside);
210
+ _$insertNode(_el$3, _el$4);
211
+ _$setProp(_el$3, "wrapMode", "none");
212
+ _$setProp(_el$3, "truncate", true);
213
+ _$insertNode(_el$4, _$createTextNode(`Subagent details`));
214
+ _$insertNode(_el$6, _el$7);
215
+ _$setProp(_el$6, "wrapMode", "none");
216
+ _$setProp(_el$6, "truncate", true);
217
+ _$insert(_el$6, () => taskAgent(part() ?? fallback().part), null);
218
+ _$insertNode(_el$8, _el$9);
219
+ _$setProp(_el$8, "wrapMode", "none");
220
+ _$setProp(_el$8, "truncate", true);
221
+ _$insert(_el$8, () => taskDescription(part() ?? fallback().part), null);
222
+ _$insertNode(_el$0, _el$1);
223
+ _$setProp(_el$0, "wrapMode", "none");
224
+ _$setProp(_el$0, "truncate", true);
225
+ _$insert(_el$0, responseLabel, null);
226
+ _$insertNode(_el$10, _el$11);
227
+ _$setProp(_el$10, "wrapMode", "none");
228
+ _$setProp(_el$10, "truncate", true);
229
+ _$insert(_el$10, () => taskStatusLabel(part() ?? fallback().part, childSessionID() ? props.api.state.session.status(childSessionID()) : undefined), null);
230
+ _$insertNode(_el$12, _el$13);
231
+ _$insertNode(_el$12, _el$14);
232
+ _$setProp(_el$12, "wrapMode", "none");
233
+ _$setProp(_el$12, "truncate", true);
234
+ _$insert(_el$12, () => modelName(props.api, model()), _el$14);
235
+ _$insert(_el$12, () => taskVariantLabel(model()) ?? "pending", null);
236
+ _$insertNode(_el$15, _el$16);
237
+ _$setProp(_el$15, "wrapMode", "none");
238
+ _$setProp(_el$15, "truncate", true);
239
+ _$insert(_el$15, () => taskModeLabel(part() ?? fallback().part), _el$16);
240
+ _$insert(_el$15, () => elapsedLabel(props.now() - props.startedAt(part() ?? fallback().part)), null);
241
+ _$insertNode(_el$17, _el$18);
242
+ _$setProp(_el$17, "wrapMode", "none");
243
+ _$setProp(_el$17, "truncate", true);
244
+ _$insert(_el$17, () => childSessionID() ?? "Waiting for child session", null);
245
+ _$insertNode(_el$19, _el$20);
246
+ _$insertNode(_el$19, _el$24);
247
+ _$setProp(_el$19, "flexDirection", "row");
248
+ _$setProp(_el$19, "gap", 2);
249
+ _$insertNode(_el$20, _el$21);
250
+ _$setProp(_el$20, "onMouseUp", openSession);
251
+ _$insertNode(_el$21, _el$22);
252
+ _$insertNode(_el$22, _$createTextNode(`Open Session`));
253
+ _$insertNode(_el$24, _el$25);
254
+ _$setProp(_el$24, "onMouseUp", closePopover);
255
+ _$insertNode(_el$25, _el$26);
256
+ _$insertNode(_el$26, _$createTextNode(`Close`));
257
+ _$effect(_p$ => {
258
+ var _v$ = props.close,
259
+ _v$2 = placement().left,
260
+ _v$3 = placement().top,
261
+ _v$4 = placement().width,
262
+ _v$5 = placement().height,
263
+ _v$6 = props.api.theme.current.borderActive,
264
+ _v$7 = props.api.theme.current.backgroundPanel,
265
+ _v$8 = props.api.theme.current.text,
266
+ _v$9 = props.api.theme.current.text,
267
+ _v$0 = props.api.theme.current.textMuted,
268
+ _v$1 = props.api.theme.current.textMuted,
269
+ _v$10 = props.api.theme.current.textMuted,
270
+ _v$11 = props.api.theme.current.textMuted,
271
+ _v$12 = props.api.theme.current.textMuted,
272
+ _v$13 = props.api.theme.current.textMuted,
273
+ _v$14 = canOpen() ? props.api.theme.current.primary : props.api.theme.current.textMuted,
274
+ _v$15 = props.api.theme.current.primary;
275
+ _v$ !== _p$.e && (_p$.e = _$setProp(_el$, "onMouseUp", _v$, _p$.e));
276
+ _v$2 !== _p$.t && (_p$.t = _$setProp(_el$2, "left", _v$2, _p$.t));
277
+ _v$3 !== _p$.a && (_p$.a = _$setProp(_el$2, "top", _v$3, _p$.a));
278
+ _v$4 !== _p$.o && (_p$.o = _$setProp(_el$2, "width", _v$4, _p$.o));
279
+ _v$5 !== _p$.i && (_p$.i = _$setProp(_el$2, "height", _v$5, _p$.i));
280
+ _v$6 !== _p$.n && (_p$.n = _$setProp(_el$2, "borderColor", _v$6, _p$.n));
281
+ _v$7 !== _p$.s && (_p$.s = _$setProp(_el$2, "backgroundColor", _v$7, _p$.s));
282
+ _v$8 !== _p$.h && (_p$.h = _$setProp(_el$3, "fg", _v$8, _p$.h));
283
+ _v$9 !== _p$.r && (_p$.r = _$setProp(_el$6, "fg", _v$9, _p$.r));
284
+ _v$0 !== _p$.d && (_p$.d = _$setProp(_el$8, "fg", _v$0, _p$.d));
285
+ _v$1 !== _p$.l && (_p$.l = _$setProp(_el$0, "fg", _v$1, _p$.l));
286
+ _v$10 !== _p$.u && (_p$.u = _$setProp(_el$10, "fg", _v$10, _p$.u));
287
+ _v$11 !== _p$.c && (_p$.c = _$setProp(_el$12, "fg", _v$11, _p$.c));
288
+ _v$12 !== _p$.w && (_p$.w = _$setProp(_el$15, "fg", _v$12, _p$.w));
289
+ _v$13 !== _p$.m && (_p$.m = _$setProp(_el$17, "fg", _v$13, _p$.m));
290
+ _v$14 !== _p$.f && (_p$.f = _$setProp(_el$21, "fg", _v$14, _p$.f));
291
+ _v$15 !== _p$.y && (_p$.y = _$setProp(_el$25, "fg", _v$15, _p$.y));
292
+ return _p$;
293
+ }, {
294
+ e: undefined,
295
+ t: undefined,
296
+ a: undefined,
297
+ o: undefined,
298
+ i: undefined,
299
+ n: undefined,
300
+ s: undefined,
301
+ h: undefined,
302
+ r: undefined,
303
+ d: undefined,
304
+ l: undefined,
305
+ u: undefined,
306
+ c: undefined,
307
+ w: undefined,
308
+ m: undefined,
309
+ f: undefined,
310
+ y: undefined
311
+ });
312
+ return _el$;
313
+ }
314
+ });
315
+ }
316
+ function ModelVariant(props) {
56
317
  return (() => {
57
- var _el$ = _$createElement("box"),
58
- _el$2 = _$createElement("text"),
59
- _el$3 = _$createElement("b"),
60
- _el$5 = _$createElement("box"),
61
- _el$6 = _$createElement("text"),
62
- _el$7 = _$createTextNode(`Agent: `),
63
- _el$8 = _$createElement("text"),
64
- _el$9 = _$createTextNode(`Assigned task: `),
65
- _el$0 = _$createElement("text"),
66
- _el$1 = _$createTextNode(`Status: `),
67
- _el$10 = _$createElement("text"),
68
- _el$11 = _$createTextNode(`Model: `),
69
- _el$12 = _$createElement("text"),
70
- _el$13 = _$createTextNode(`Effort/variant: `),
71
- _el$14 = _$createElement("text"),
72
- _el$15 = _$createTextNode(`Elapsed: `),
73
- _el$16 = _$createElement("text"),
74
- _el$17 = _$createTextNode(`Child session ID: `),
75
- _el$18 = _$createElement("text"),
76
- _el$19 = _$createTextNode(`Mode: `),
77
- _el$20 = _$createElement("box"),
78
- _el$21 = _$createElement("box"),
79
- _el$22 = _$createElement("text"),
80
- _el$23 = _$createElement("b"),
81
- _el$25 = _$createElement("box"),
82
- _el$26 = _$createElement("text"),
83
- _el$27 = _$createElement("b");
84
- _$insertNode(_el$, _el$2);
85
- _$insertNode(_el$, _el$5);
86
- _$insertNode(_el$, _el$20);
87
- _$setProp(_el$, "width", "100%");
88
- _$setProp(_el$, "flexDirection", "column");
89
- _$setProp(_el$, "padding", 1);
90
- _$setProp(_el$, "gap", 1);
91
- _$insertNode(_el$2, _el$3);
92
- _$insertNode(_el$3, _$createTextNode(`Subagent details`));
93
- _$insertNode(_el$5, _el$6);
94
- _$insertNode(_el$5, _el$8);
95
- _$insertNode(_el$5, _el$0);
96
- _$insertNode(_el$5, _el$10);
97
- _$insertNode(_el$5, _el$12);
98
- _$insertNode(_el$5, _el$14);
99
- _$insertNode(_el$5, _el$16);
100
- _$insertNode(_el$5, _el$18);
101
- _$setProp(_el$5, "flexDirection", "column");
102
- _$insertNode(_el$6, _el$7);
103
- _$insert(_el$6, () => taskAgent(part() ?? fallback().part), null);
104
- _$insertNode(_el$8, _el$9);
105
- _$setProp(_el$8, "wrapMode", "word");
106
- _$insert(_el$8, () => taskDescription(part() ?? fallback().part), null);
107
- _$insertNode(_el$0, _el$1);
108
- _$insert(_el$0, () => taskStatusLabel(part() ?? fallback().part, childSessionID() ? props.api.state.session.status(childSessionID()) : undefined), null);
109
- _$insertNode(_el$10, _el$11);
110
- _$insert(_el$10, () => modelName(props.api, model()), null);
111
- _$insertNode(_el$12, _el$13);
112
- _$insert(_el$12, () => taskVariantLabel(model()) ?? "pending", null);
113
- _$insertNode(_el$14, _el$15);
114
- _$insert(_el$14, () => elapsedLabel(props.now() - props.startedAt(part() ?? fallback().part)), null);
115
- _$insertNode(_el$16, _el$17);
116
- _$insert(_el$16, () => childSessionID() ?? "Waiting for child session", null);
117
- _$insertNode(_el$18, _el$19);
118
- _$insert(_el$18, () => taskModeLabel(part() ?? fallback().part), null);
119
- _$insertNode(_el$20, _el$21);
120
- _$insertNode(_el$20, _el$25);
121
- _$setProp(_el$20, "flexDirection", "row");
122
- _$setProp(_el$20, "gap", 2);
123
- _$insertNode(_el$21, _el$22);
124
- _$setProp(_el$21, "onMouseUp", openSession);
125
- _$insertNode(_el$22, _el$23);
126
- _$insertNode(_el$23, _$createTextNode(`Open Session`));
127
- _$insertNode(_el$25, _el$26);
128
- _$insertNode(_el$26, _el$27);
129
- _$insertNode(_el$27, _$createTextNode(`Close`));
130
- _$effect(_p$ => {
131
- var _v$ = props.api.theme.current.text,
132
- _v$2 = props.api.theme.current.text,
133
- _v$3 = props.api.theme.current.textMuted,
134
- _v$4 = props.api.theme.current.textMuted,
135
- _v$5 = props.api.theme.current.textMuted,
136
- _v$6 = props.api.theme.current.textMuted,
137
- _v$7 = props.api.theme.current.textMuted,
138
- _v$8 = props.api.theme.current.textMuted,
139
- _v$9 = props.api.theme.current.textMuted,
140
- _v$0 = canOpen() ? props.api.theme.current.primary : props.api.theme.current.textMuted,
141
- _v$1 = props.close,
142
- _v$10 = props.api.theme.current.primary;
143
- _v$ !== _p$.e && (_p$.e = _$setProp(_el$2, "fg", _v$, _p$.e));
144
- _v$2 !== _p$.t && (_p$.t = _$setProp(_el$6, "fg", _v$2, _p$.t));
145
- _v$3 !== _p$.a && (_p$.a = _$setProp(_el$8, "fg", _v$3, _p$.a));
146
- _v$4 !== _p$.o && (_p$.o = _$setProp(_el$0, "fg", _v$4, _p$.o));
147
- _v$5 !== _p$.i && (_p$.i = _$setProp(_el$10, "fg", _v$5, _p$.i));
148
- _v$6 !== _p$.n && (_p$.n = _$setProp(_el$12, "fg", _v$6, _p$.n));
149
- _v$7 !== _p$.s && (_p$.s = _$setProp(_el$14, "fg", _v$7, _p$.s));
150
- _v$8 !== _p$.h && (_p$.h = _$setProp(_el$16, "fg", _v$8, _p$.h));
151
- _v$9 !== _p$.r && (_p$.r = _$setProp(_el$18, "fg", _v$9, _p$.r));
152
- _v$0 !== _p$.d && (_p$.d = _$setProp(_el$22, "fg", _v$0, _p$.d));
153
- _v$1 !== _p$.l && (_p$.l = _$setProp(_el$25, "onMouseUp", _v$1, _p$.l));
154
- _v$10 !== _p$.u && (_p$.u = _$setProp(_el$26, "fg", _v$10, _p$.u));
155
- return _p$;
156
- }, {
157
- e: undefined,
158
- t: undefined,
159
- a: undefined,
160
- o: undefined,
161
- i: undefined,
162
- n: undefined,
163
- s: undefined,
164
- h: undefined,
165
- r: undefined,
166
- d: undefined,
167
- l: undefined,
168
- u: undefined
169
- });
170
- return _el$;
318
+ var _el$28 = _$createElement("box"),
319
+ _el$29 = _$createElement("text");
320
+ _$insertNode(_el$28, _el$29);
321
+ _$setProp(_el$28, "flexShrink", 1);
322
+ _$setProp(_el$28, "minWidth", 0);
323
+ _$setProp(_el$28, "maxWidth", 28);
324
+ _$setProp(_el$28, "marginLeft", 1);
325
+ _$setProp(_el$28, "flexDirection", "row");
326
+ _$setProp(_el$29, "flexGrow", 1);
327
+ _$setProp(_el$29, "flexShrink", 1);
328
+ _$setProp(_el$29, "minWidth", 0);
329
+ _$setProp(_el$29, "wrapMode", "none");
330
+ _$setProp(_el$29, "truncate", true);
331
+ _$insert(_el$29, () => modelName(props.api, props.model()));
332
+ _$insert(_el$28, _$createComponent(Show, {
333
+ get when() {
334
+ return taskVariantLabel(props.model());
335
+ },
336
+ get fallback() {
337
+ return _$createComponent(Show, {
338
+ get when() {
339
+ return props.model();
340
+ },
341
+ get children() {
342
+ return [(() => {
343
+ var _el$30 = _$createElement("text");
344
+ _$insertNode(_el$30, _$createTextNode(`·`));
345
+ _$setProp(_el$30, "flexShrink", 0);
346
+ _$setProp(_el$30, "marginLeft", 1);
347
+ _$setProp(_el$30, "wrapMode", "none");
348
+ _$effect(_$p => _$setProp(_el$30, "fg", props.api.theme.current.textMuted, _$p));
349
+ return _el$30;
350
+ })(), (() => {
351
+ var _el$32 = _$createElement("text"),
352
+ _el$33 = _$createElement("b");
353
+ _$insertNode(_el$32, _el$33);
354
+ _$setProp(_el$32, "flexShrink", 0);
355
+ _$setProp(_el$32, "marginLeft", 1);
356
+ _$setProp(_el$32, "wrapMode", "none");
357
+ _$setProp(_el$32, "truncate", true);
358
+ _$insertNode(_el$33, _$createTextNode(`pending`));
359
+ _$effect(_$p => _$setProp(_el$32, "fg", props.api.theme.current.warning, _$p));
360
+ return _el$32;
361
+ })()];
362
+ }
363
+ });
364
+ },
365
+ children: variant => [(() => {
366
+ var _el$35 = _$createElement("text");
367
+ _$insertNode(_el$35, _$createTextNode(`·`));
368
+ _$setProp(_el$35, "flexShrink", 0);
369
+ _$setProp(_el$35, "marginLeft", 1);
370
+ _$setProp(_el$35, "wrapMode", "none");
371
+ _$effect(_$p => _$setProp(_el$35, "fg", props.api.theme.current.textMuted, _$p));
372
+ return _el$35;
373
+ })(), (() => {
374
+ var _el$37 = _$createElement("text"),
375
+ _el$38 = _$createElement("b");
376
+ _$insertNode(_el$37, _el$38);
377
+ _$setProp(_el$37, "flexShrink", 0);
378
+ _$setProp(_el$37, "marginLeft", 1);
379
+ _$setProp(_el$37, "wrapMode", "none");
380
+ _$setProp(_el$37, "truncate", true);
381
+ _$insert(_el$38, variant);
382
+ _$effect(_$p => _$setProp(_el$37, "fg", props.api.theme.current.warning, _$p));
383
+ return _el$37;
384
+ })()]
385
+ }), null);
386
+ _$effect(_$p => _$setProp(_el$29, "fg", props.api.theme.current.text, _$p));
387
+ return _el$28;
171
388
  })();
172
389
  }
173
390
  function TaskCard(props) {
@@ -191,131 +408,116 @@ function TaskCard(props) {
191
408
  const current = part();
192
409
  return current ? elapsedLabel(props.now() - props.startedAt(current)) : "";
193
410
  };
411
+ let card;
412
+ let lastAnchor;
413
+ const anchor = () => card && {
414
+ x: card.screenX,
415
+ y: card.screenY,
416
+ width: card.width,
417
+ height: card.height
418
+ };
419
+ const updateAnchor = () => {
420
+ const current = anchor();
421
+ if (!current || lastAnchor && lastAnchor.x === current.x && lastAnchor.y === current.y && lastAnchor.width === current.width && lastAnchor.height === current.height) {
422
+ return;
423
+ }
424
+ lastAnchor = current;
425
+ props.updatePopupAnchor(props.callID, current);
426
+ };
427
+ onCleanup(() => {
428
+ // The popover owns its last reported rect, so losing this card must not
429
+ // reset its placement before the user explicitly closes it.
430
+ card = undefined;
431
+ lastAnchor = undefined;
432
+ });
194
433
  const openDetails = () => {
195
434
  if (props.api.renderer.getSelection()?.getSelectedText()) return;
196
435
  const part = livePart();
197
- if (!part) return;
436
+ const currentAnchor = anchor();
437
+ if (!part || !currentAnchor) return;
198
438
  props.remember(part);
199
- props.openPopup(props.callID, part);
439
+ props.openPopup(props.callID, currentAnchor);
200
440
  };
201
441
  return (() => {
202
- var _el$29 = _$createElement("box"),
203
- _el$30 = _$createElement("box"),
204
- _el$31 = _$createElement("text"),
205
- _el$32 = _$createElement("b"),
206
- _el$33 = _$createElement("box"),
207
- _el$34 = _$createElement("text"),
208
- _el$35 = _$createElement("box"),
209
- _el$36 = _$createElement("text"),
210
- _el$37 = _$createElement("box"),
211
- _el$38 = _$createElement("text");
212
- _$insertNode(_el$29, _el$30);
213
- _$insertNode(_el$29, _el$35);
214
- _$setProp(_el$29, "width", "100%");
215
- _$setProp(_el$29, "height", 2);
216
- _$setProp(_el$29, "overflow", "hidden");
217
- _$setProp(_el$29, "onMouseUp", openDetails);
218
- _$insertNode(_el$30, _el$31);
219
- _$insertNode(_el$30, _el$33);
220
- _$setProp(_el$30, "width", "100%");
221
- _$setProp(_el$30, "height", 1);
222
- _$setProp(_el$30, "flexDirection", "row");
223
- _$setProp(_el$30, "overflow", "hidden");
224
- _$insertNode(_el$31, _el$32);
225
- _$setProp(_el$31, "flexGrow", 1);
226
- _$setProp(_el$31, "flexShrink", 1);
227
- _$setProp(_el$31, "minWidth", 0);
228
- _$setProp(_el$31, "wrapMode", "none");
229
- _$setProp(_el$31, "truncate", true);
230
- _$insert(_el$32, (() => {
442
+ var _el$39 = _$createElement("box"),
443
+ _el$40 = _$createElement("box"),
444
+ _el$41 = _$createElement("text"),
445
+ _el$42 = _$createElement("b"),
446
+ _el$43 = _$createElement("box"),
447
+ _el$44 = _$createElement("text"),
448
+ _el$45 = _$createElement("box"),
449
+ _el$46 = _$createElement("text");
450
+ _$insertNode(_el$39, _el$40);
451
+ _$insertNode(_el$39, _el$43);
452
+ _$use(element => {
453
+ card = element;
454
+ updateAnchor();
455
+ }, _el$39);
456
+ _$setProp(_el$39, "onSizeChange", updateAnchor);
457
+ _$setProp(_el$39, "renderBefore", updateAnchor);
458
+ _$setProp(_el$39, "width", "100%");
459
+ _$setProp(_el$39, "height", 2);
460
+ _$setProp(_el$39, "flexDirection", "column");
461
+ _$setProp(_el$39, "overflow", "hidden");
462
+ _$setProp(_el$39, "onMouseUp", openDetails);
463
+ _$insertNode(_el$40, _el$41);
464
+ _$setProp(_el$40, "width", "100%");
465
+ _$setProp(_el$40, "height", 1);
466
+ _$setProp(_el$40, "flexDirection", "row");
467
+ _$setProp(_el$40, "overflow", "hidden");
468
+ _$insertNode(_el$41, _el$42);
469
+ _$setProp(_el$41, "flexGrow", 1);
470
+ _$setProp(_el$41, "flexShrink", 1);
471
+ _$setProp(_el$41, "minWidth", 0);
472
+ _$setProp(_el$41, "wrapMode", "none");
473
+ _$setProp(_el$41, "truncate", true);
474
+ _$insert(_el$42, (() => {
231
475
  var _c$ = _$memo(() => !!part());
232
476
  return () => _c$() ? taskAgent(part()) : "subagent";
233
477
  })());
234
- _$insertNode(_el$33, _el$34);
235
- _$setProp(_el$33, "flexShrink", 1);
236
- _$setProp(_el$33, "minWidth", 0);
237
- _$setProp(_el$33, "maxWidth", 28);
238
- _$setProp(_el$33, "marginLeft", 1);
239
- _$setProp(_el$33, "flexDirection", "row");
240
- _$setProp(_el$34, "flexGrow", 1);
241
- _$setProp(_el$34, "flexShrink", 1);
242
- _$setProp(_el$34, "minWidth", 0);
243
- _$setProp(_el$34, "wrapMode", "none");
244
- _$setProp(_el$34, "truncate", true);
245
- _$insert(_el$34, () => modelName(props.api, model()));
246
- _$insert(_el$33, _$createComponent(Show, {
247
- get when() {
248
- return taskVariantLabel(model());
249
- },
250
- get fallback() {
251
- return _$createComponent(Show, {
252
- get when() {
253
- return model();
254
- },
255
- get children() {
256
- var _el$39 = _$createElement("text");
257
- _$insertNode(_el$39, _$createTextNode(`· pending`));
258
- _$setProp(_el$39, "flexShrink", 0);
259
- _$setProp(_el$39, "marginLeft", 1);
260
- _$setProp(_el$39, "wrapMode", "none");
261
- _$setProp(_el$39, "truncate", true);
262
- _$effect(_$p => _$setProp(_el$39, "fg", props.api.theme.current.textMuted, _$p));
263
- return _el$39;
264
- }
265
- });
478
+ _$insert(_el$40, _$createComponent(ModelVariant, {
479
+ get api() {
480
+ return props.api;
266
481
  },
267
- children: variant => (() => {
268
- var _el$41 = _$createElement("text"),
269
- _el$42 = _$createTextNode(`· `);
270
- _$insertNode(_el$41, _el$42);
271
- _$setProp(_el$41, "flexShrink", 0);
272
- _$setProp(_el$41, "marginLeft", 1);
273
- _$setProp(_el$41, "wrapMode", "none");
274
- _$setProp(_el$41, "truncate", true);
275
- _$insert(_el$41, variant, null);
276
- _$effect(_$p => _$setProp(_el$41, "fg", props.api.theme.current.textMuted, _$p));
277
- return _el$41;
278
- })()
482
+ model: model
279
483
  }), null);
280
- _$insertNode(_el$35, _el$36);
281
- _$insertNode(_el$35, _el$37);
282
- _$setProp(_el$35, "width", "100%");
283
- _$setProp(_el$35, "height", 1);
284
- _$setProp(_el$35, "flexDirection", "row");
285
- _$setProp(_el$35, "overflow", "hidden");
286
- _$setProp(_el$36, "flexGrow", 1);
287
- _$setProp(_el$36, "flexShrink", 1);
288
- _$setProp(_el$36, "minWidth", 0);
289
- _$setProp(_el$36, "wrapMode", "none");
290
- _$setProp(_el$36, "truncate", true);
291
- _$insert(_el$36, (() => {
484
+ _$insertNode(_el$43, _el$44);
485
+ _$insertNode(_el$43, _el$45);
486
+ _$setProp(_el$43, "width", "100%");
487
+ _$setProp(_el$43, "height", 1);
488
+ _$setProp(_el$43, "flexDirection", "row");
489
+ _$setProp(_el$43, "overflow", "hidden");
490
+ _$setProp(_el$44, "flexGrow", 1);
491
+ _$setProp(_el$44, "flexShrink", 1);
492
+ _$setProp(_el$44, "minWidth", 0);
493
+ _$setProp(_el$44, "wrapMode", "none");
494
+ _$setProp(_el$44, "truncate", true);
495
+ _$insert(_el$44, (() => {
292
496
  var _c$2 = _$memo(() => !!part());
293
497
  return () => _c$2() ? taskDescription(part()) : "Working";
294
498
  })());
295
- _$insertNode(_el$37, _el$38);
296
- _$setProp(_el$37, "width", 8);
297
- _$setProp(_el$37, "flexShrink", 0);
298
- _$setProp(_el$37, "justifyContent", "flex-end");
299
- _$setProp(_el$38, "wrapMode", "none");
300
- _$setProp(_el$38, "truncate", true);
301
- _$insert(_el$38, elapsed);
499
+ _$insertNode(_el$45, _el$46);
500
+ _$setProp(_el$45, "width", 8);
501
+ _$setProp(_el$45, "flexShrink", 0);
502
+ _$setProp(_el$45, "flexDirection", "row");
503
+ _$setProp(_el$45, "justifyContent", "flex-end");
504
+ _$setProp(_el$46, "wrapMode", "none");
505
+ _$setProp(_el$46, "truncate", true);
506
+ _$insert(_el$46, elapsed);
302
507
  _$effect(_p$ => {
303
- var _v$11 = props.api.theme.current.text,
304
- _v$12 = props.api.theme.current.textMuted,
305
- _v$13 = props.api.theme.current.textMuted,
306
- _v$14 = props.api.theme.current.textMuted;
307
- _v$11 !== _p$.e && (_p$.e = _$setProp(_el$31, "fg", _v$11, _p$.e));
308
- _v$12 !== _p$.t && (_p$.t = _$setProp(_el$34, "fg", _v$12, _p$.t));
309
- _v$13 !== _p$.a && (_p$.a = _$setProp(_el$36, "fg", _v$13, _p$.a));
310
- _v$14 !== _p$.o && (_p$.o = _$setProp(_el$38, "fg", _v$14, _p$.o));
508
+ var _v$16 = props.api.theme.current.text,
509
+ _v$17 = props.api.theme.current.textMuted,
510
+ _v$18 = props.api.theme.current.textMuted;
511
+ _v$16 !== _p$.e && (_p$.e = _$setProp(_el$41, "fg", _v$16, _p$.e));
512
+ _v$17 !== _p$.t && (_p$.t = _$setProp(_el$44, "fg", _v$17, _p$.t));
513
+ _v$18 !== _p$.a && (_p$.a = _$setProp(_el$46, "fg", _v$18, _p$.a));
311
514
  return _p$;
312
515
  }, {
313
516
  e: undefined,
314
517
  t: undefined,
315
- a: undefined,
316
- o: undefined
518
+ a: undefined
317
519
  });
318
- return _el$29;
520
+ return _el$39;
319
521
  })();
320
522
  }
321
523
  function RunningSubagents(props) {
@@ -369,7 +571,10 @@ function RunningSubagents(props) {
369
571
  setRevision(value => value + 1);
370
572
  void loadMessages(parentSessionID, currentRequest);
371
573
  };
372
- createEffect(on(() => props.sessionID, sessionID => void selectSession(sessionID)));
574
+ createEffect(on(() => props.sessionID, (sessionID, previousSessionID) => {
575
+ if (previousSessionID !== undefined && sessionID !== previousSessionID) setOpenPopup(undefined);
576
+ void selectSession(sessionID);
577
+ }));
373
578
  const allTaskParts = createMemo(() => {
374
579
  revision();
375
580
  const sessionID = scopeSessionID();
@@ -420,37 +625,36 @@ function RunningSubagents(props) {
420
625
  firstSeen.set(part.callID, value);
421
626
  return value;
422
627
  };
423
- const releasePopup = ownership => {
424
- if (openPopup()?.token === ownership.token) setOpenPopup(undefined);
425
- };
426
628
  const closePopup = ownership => {
427
629
  if (openPopup()?.token !== ownership.token) return;
428
630
  setOpenPopup(undefined);
429
- props.api.ui.dialog.clear();
430
631
  };
431
- const showPopup = (callID, part) => {
632
+ const showPopup = (callID, anchor) => {
432
633
  const ownership = {
433
634
  callID,
434
- token: Symbol(callID)
635
+ token: Symbol(callID),
636
+ anchor
435
637
  };
436
638
  setOpenPopup(ownership);
437
- props.api.ui.dialog.replace(() => _$createComponent(TaskDetails, {
438
- get api() {
439
- return props.api;
440
- },
441
- callID: callID,
442
- get currentSessionID() {
443
- return props.sessionID;
444
- },
445
- fallbackPart: part,
446
- tasksByCallID: tasksByCallID,
447
- fallbackFor: id => fallbacks.get(id),
448
- revision: revision,
449
- now: now,
450
- startedAt: startedAt,
451
- close: () => closePopup(ownership)
452
- }), () => releasePopup(ownership));
453
639
  };
640
+ const updatePopupAnchor = (callID, anchor) => {
641
+ const ownership = openPopup();
642
+ if (!ownership || ownership.callID !== callID) return;
643
+ if (ownership.anchor.x === anchor.x && ownership.anchor.y === anchor.y && ownership.anchor.width === anchor.width && ownership.anchor.height === anchor.height) {
644
+ return;
645
+ }
646
+ setOpenPopup({
647
+ ...ownership,
648
+ anchor
649
+ });
650
+ };
651
+ const dismissForHostDialog = () => {
652
+ if (openPopup() && props.api.ui.dialog.open) setOpenPopup(undefined);
653
+ };
654
+ const dismissForHostDialogFrame = async () => {
655
+ dismissForHostDialog();
656
+ };
657
+ createEffect(dismissForHostDialog);
454
658
  const unregister = [];
455
659
  let timer;
456
660
  onMount(() => {
@@ -465,44 +669,52 @@ function RunningSubagents(props) {
465
669
  setRevision(value => value + 1);
466
670
  }
467
671
  };
468
- unregister.push(props.api.event.on("session.created", event => syncCurrentSession(event.properties.info)), props.api.event.on("session.updated", event => syncCurrentSession(event.properties.info)), props.api.event.on("message.updated", event => refreshSession(event.properties.sessionID)), props.api.event.on("message.part.updated", event => refreshSession(event.properties.sessionID)), props.api.event.on("message.part.removed", event => refreshSession(event.properties.sessionID)));
672
+ unregister.push(() => {
673
+ props.api.renderer.removeFrameCallback(dismissForHostDialogFrame);
674
+ }, props.api.event.on("session.created", event => syncCurrentSession(event.properties.info)), props.api.event.on("session.updated", event => syncCurrentSession(event.properties.info)), props.api.event.on("message.updated", event => refreshSession(event.properties.sessionID)), props.api.event.on("message.part.updated", event => refreshSession(event.properties.sessionID)), props.api.event.on("message.part.removed", event => refreshSession(event.properties.sessionID)));
675
+ props.api.renderer.setFrameCallback(dismissForHostDialogFrame);
469
676
  timer = setInterval(() => setNow(Date.now()), 1_000);
470
677
  });
471
678
  onCleanup(() => {
472
679
  sessionRequest += 1;
473
680
  if (timer) clearInterval(timer);
474
681
  for (const dispose of unregister) dispose();
475
- const ownership = openPopup();
476
- if (ownership) closePopup(ownership);
682
+ setOpenPopup(undefined);
477
683
  });
478
684
  return (() => {
479
- var _el$43 = _$createElement("box"),
480
- _el$44 = _$createElement("text"),
481
- _el$45 = _$createElement("b"),
482
- _el$47 = _$createElement("span");
483
- _$insertNode(_el$43, _el$44);
484
- _$setProp(_el$43, "overflow", "hidden");
485
- _$insertNode(_el$44, _el$45);
486
- _$insertNode(_el$44, _el$47);
487
- _$insertNode(_el$45, _$createTextNode(`Subagents`));
488
- _$insert(_el$47, (() => {
685
+ var _el$47 = _$createElement("box"),
686
+ _el$48 = _$createElement("text"),
687
+ _el$49 = _$createElement("b"),
688
+ _el$51 = _$createElement("span");
689
+ _$insertNode(_el$47, _el$48);
690
+ _$setProp(_el$47, "overflow", "hidden");
691
+ _$setProp(_el$47, "flexDirection", "column");
692
+ _$setProp(_el$47, "gap", 1);
693
+ _$insertNode(_el$48, _el$49);
694
+ _$insertNode(_el$48, _el$51);
695
+ _$insertNode(_el$49, _$createTextNode(`Subagents`));
696
+ _$insert(_el$51, (() => {
489
697
  var _c$3 = _$memo(() => !!visibleTaskParts().length);
490
698
  return () => _c$3() ? ` (${visibleTaskParts().length} running)` : "";
491
699
  })());
492
- _$insert(_el$43, _$createComponent(Show, {
700
+ _$insert(_el$47, _$createComponent(Show, {
493
701
  get when() {
494
702
  return taskCallIDs().length > 0;
495
703
  },
496
704
  get fallback() {
497
705
  return (() => {
498
- var _el$48 = _$createElement("text");
499
- _$insertNode(_el$48, _$createTextNode(`No subagent running`));
500
- _$effect(_$p => _$setProp(_el$48, "fg", props.api.theme.current.textMuted, _$p));
501
- return _el$48;
706
+ var _el$53 = _$createElement("text");
707
+ _$insertNode(_el$53, _$createTextNode(`No subagent running`));
708
+ _$effect(_$p => _$setProp(_el$53, "fg", props.api.theme.current.textMuted, _$p));
709
+ return _el$53;
502
710
  })();
503
711
  },
504
712
  get children() {
505
- return _$createComponent(For, {
713
+ var _el$52 = _$createElement("box");
714
+ _$setProp(_el$52, "width", "100%");
715
+ _$setProp(_el$52, "flexDirection", "column");
716
+ _$setProp(_el$52, "gap", 1);
717
+ _$insert(_el$52, _$createComponent(For, {
506
718
  get each() {
507
719
  return taskCallIDs();
508
720
  },
@@ -517,24 +729,49 @@ function RunningSubagents(props) {
517
729
  revision: revision,
518
730
  now: now,
519
731
  startedAt: startedAt,
520
- openPopup: showPopup
732
+ openPopup: showPopup,
733
+ updatePopupAnchor: updatePopupAnchor
521
734
  })
522
- });
735
+ }));
736
+ return _el$52;
523
737
  }
524
738
  }), null);
739
+ _$insert(_el$47, _$createComponent(Show, {
740
+ get when() {
741
+ return openPopup();
742
+ },
743
+ children: ownership => _$createComponent(TaskPopover, {
744
+ get api() {
745
+ return props.api;
746
+ },
747
+ ownership: ownership,
748
+ get currentSessionID() {
749
+ return props.sessionID;
750
+ },
751
+ get fallbackPart() {
752
+ return fallbacks.get(ownership().callID).part;
753
+ },
754
+ tasksByCallID: tasksByCallID,
755
+ fallbackFor: id => fallbacks.get(id),
756
+ revision: revision,
757
+ now: now,
758
+ startedAt: startedAt,
759
+ close: () => closePopup(ownership())
760
+ })
761
+ }), null);
525
762
  _$effect(_p$ => {
526
- var _v$15 = props.api.theme.current.text,
527
- _v$16 = {
763
+ var _v$19 = props.api.theme.current.text,
764
+ _v$20 = {
528
765
  fg: props.api.theme.current.textMuted
529
766
  };
530
- _v$15 !== _p$.e && (_p$.e = _$setProp(_el$44, "fg", _v$15, _p$.e));
531
- _v$16 !== _p$.t && (_p$.t = _$setProp(_el$47, "style", _v$16, _p$.t));
767
+ _v$19 !== _p$.e && (_p$.e = _$setProp(_el$48, "fg", _v$19, _p$.e));
768
+ _v$20 !== _p$.t && (_p$.t = _$setProp(_el$51, "style", _v$20, _p$.t));
532
769
  return _p$;
533
770
  }, {
534
771
  e: undefined,
535
772
  t: undefined
536
773
  });
537
- return _el$43;
774
+ return _el$47;
538
775
  })();
539
776
  }
540
777
  const tui = async api => {