opencode-subagents-sidebar-plugin 0.1.3 → 0.1.5

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
 
12
+ import { Portal, useTerminalDimensions } from "@opentui/solid";
11
13
  import { elapsedLabel } from "./activity.js";
12
- import { canOpenTaskSession, isTaskPart, isVisibleTask, resolveTaskModel, taskAgent, taskDescription, taskModeLabel, taskModelLabel, taskSessionID, taskStatusLabel, taskVariantLabel } from "./tasks.js";
14
+ import { placePopover } from "./popover.js";
15
+ import { canOpenTaskSession, isTaskPart, isVisibleTask, latestAssistantText, 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,16 @@ 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;
36
+ let requestedHydration;
22
37
  const fallback = () => {
23
- const known = props.fallbackFor(props.callID);
38
+ const known = props.fallbackFor(callID());
24
39
  if (known) return known;
25
40
  const sessionID = taskSessionID(props.fallbackPart);
26
41
  return {
@@ -32,7 +47,7 @@ function TaskDetails(props) {
32
47
  };
33
48
  const part = createMemo(() => {
34
49
  props.revision();
35
- return props.tasksByCallID().get(props.callID) ?? fallback().part;
50
+ return props.tasksByCallID().get(callID()) ?? fallback().part;
36
51
  });
37
52
  const childSessionID = () => taskSessionID(part() ?? fallback().part) ?? fallback().sessionID;
38
53
  const childSession = () => {
@@ -45,6 +60,80 @@ function TaskDetails(props) {
45
60
  return resolvedModel(props.api, part() ?? fallback().part, childSession(), childSessionID());
46
61
  });
47
62
  const canOpen = () => canOpenTaskSession(childSessionID(), props.currentSessionID);
63
+ const liveChildMessages = createMemo(() => {
64
+ props.revision();
65
+ const sessionID = childSessionID();
66
+ if (!sessionID) return undefined;
67
+ const messages = props.api.state.session.messages(sessionID);
68
+ const parts = new Map();
69
+ let hasParts = false;
70
+ for (const message of messages) {
71
+ const messageParts = props.api.state.part(message.id);
72
+ parts.set(message.id, messageParts);
73
+ if (messageParts.length) hasParts = true;
74
+ }
75
+ return {
76
+ sessionID,
77
+ messages,
78
+ parts,
79
+ hasParts
80
+ };
81
+ });
82
+ const ownsHydration = (token, sessionID, request) => {
83
+ if (props.api.lifecycle.signal.aborted || request !== hydrationRequest) return false;
84
+ return props.ownership().token === token && childSessionID() === sessionID;
85
+ };
86
+ const hydrateChildMessages = async (token, sessionID, request) => {
87
+ try {
88
+ const result = await props.api.client.session.messages({
89
+ sessionID,
90
+ directory: props.api.state.path.directory
91
+ }, {
92
+ throwOnError: true
93
+ });
94
+ if (!ownsHydration(token, sessionID, request)) return;
95
+ if (liveChildMessages()?.hasParts) return;
96
+ setHydratedChildMessages({
97
+ token,
98
+ sessionID,
99
+ messages: result.data
100
+ });
101
+ } catch {
102
+ if (ownsHydration(token, sessionID, request)) requestedHydration = undefined;
103
+ }
104
+ };
105
+ createEffect(() => {
106
+ const ownership = props.ownership();
107
+ const sessionID = childSessionID();
108
+ const live = liveChildMessages();
109
+ if (!sessionID || live?.hasParts) {
110
+ if (requestedHydration) {
111
+ requestedHydration = undefined;
112
+ hydrationRequest += 1;
113
+ }
114
+ return;
115
+ }
116
+ if (requestedHydration?.token === ownership.token && requestedHydration.sessionID === sessionID) return;
117
+ const request = ++hydrationRequest;
118
+ requestedHydration = {
119
+ token: ownership.token,
120
+ sessionID
121
+ };
122
+ setHydratedChildMessages(undefined);
123
+ void hydrateChildMessages(ownership.token, sessionID, request);
124
+ });
125
+ const latestResponse = createMemo(() => {
126
+ const ownership = props.ownership();
127
+ const sessionID = childSessionID();
128
+ if (!sessionID) return undefined;
129
+ const live = liveChildMessages();
130
+ if (live?.hasParts) return latestAssistantText(live.messages, messageID => live.parts.get(messageID) ?? []);
131
+ const hydrated = hydratedChildMessages();
132
+ if (!hydrated || hydrated.token !== ownership.token || hydrated.sessionID !== sessionID) return undefined;
133
+ const parts = new Map(hydrated.messages.map(message => [message.info.id, message.parts]));
134
+ return latestAssistantText(hydrated.messages.map(message => message.info), messageID => parts.get(messageID) ?? []);
135
+ });
136
+ const responseLabel = () => latestResponse() ?? (childSessionID() ? "Waiting for assistant response" : "Waiting for child session");
48
137
  const openSession = () => {
49
138
  const sessionID = childSessionID();
50
139
  if (!canOpen() || !sessionID) return;
@@ -53,122 +142,166 @@ function TaskDetails(props) {
53
142
  });
54
143
  props.close();
55
144
  };
56
- 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$;
171
- })();
145
+ const applyPortalPlacement = (next = placement()) => {
146
+ const wrapper = portalWrapper;
147
+ if (!wrapper || wrapper.isDestroyed) return;
148
+ wrapper.position = "absolute";
149
+ wrapper.left = next.left;
150
+ wrapper.top = next.top;
151
+ wrapper.width = next.width;
152
+ wrapper.height = next.height;
153
+ wrapper.zIndex = POPOVER_Z_INDEX;
154
+ wrapper.focusable = false;
155
+ };
156
+ createEffect(() => applyPortalPlacement(placement()));
157
+ onCleanup(() => {
158
+ hydrationRequest += 1;
159
+ requestedHydration = undefined;
160
+ portalWrapper = undefined;
161
+ });
162
+ return _$createComponent(Portal, {
163
+ get mount() {
164
+ return props.api.renderer.root;
165
+ },
166
+ ref: element => {
167
+ portalWrapper = element;
168
+ applyPortalPlacement();
169
+ },
170
+ get children() {
171
+ var _el$ = _$createElement("box"),
172
+ _el$2 = _$createElement("text"),
173
+ _el$3 = _$createElement("b"),
174
+ _el$5 = _$createElement("text"),
175
+ _el$6 = _$createTextNode(`Agent: `),
176
+ _el$7 = _$createElement("text"),
177
+ _el$8 = _$createTextNode(`Assigned: `),
178
+ _el$9 = _$createElement("text"),
179
+ _el$0 = _$createTextNode(`Latest: `),
180
+ _el$1 = _$createElement("text"),
181
+ _el$10 = _$createTextNode(`Status: `),
182
+ _el$11 = _$createElement("text"),
183
+ _el$12 = _$createTextNode(`Model: `),
184
+ _el$13 = _$createTextNode(` · `),
185
+ _el$14 = _$createElement("text"),
186
+ _el$15 = _$createTextNode(` · `),
187
+ _el$16 = _$createElement("text"),
188
+ _el$17 = _$createTextNode(`Child: `),
189
+ _el$18 = _$createElement("box"),
190
+ _el$19 = _$createElement("box"),
191
+ _el$20 = _$createElement("text"),
192
+ _el$21 = _$createElement("b"),
193
+ _el$23 = _$createElement("box"),
194
+ _el$24 = _$createElement("text"),
195
+ _el$25 = _$createElement("b");
196
+ _$insertNode(_el$, _el$2);
197
+ _$insertNode(_el$, _el$5);
198
+ _$insertNode(_el$, _el$7);
199
+ _$insertNode(_el$, _el$9);
200
+ _$insertNode(_el$, _el$1);
201
+ _$insertNode(_el$, _el$11);
202
+ _$insertNode(_el$, _el$14);
203
+ _$insertNode(_el$, _el$16);
204
+ _$insertNode(_el$, _el$18);
205
+ _$setProp(_el$, "width", "100%");
206
+ _$setProp(_el$, "height", "100%");
207
+ _$setProp(_el$, "focusable", false);
208
+ _$setProp(_el$, "live", true);
209
+ _$setProp(_el$, "border", true);
210
+ _$setProp(_el$, "padding", 1);
211
+ _$setProp(_el$, "flexDirection", "column");
212
+ _$setProp(_el$, "overflow", "hidden");
213
+ _$insertNode(_el$2, _el$3);
214
+ _$setProp(_el$2, "wrapMode", "none");
215
+ _$setProp(_el$2, "truncate", true);
216
+ _$insertNode(_el$3, _$createTextNode(`Subagent details`));
217
+ _$insertNode(_el$5, _el$6);
218
+ _$setProp(_el$5, "wrapMode", "none");
219
+ _$setProp(_el$5, "truncate", true);
220
+ _$insert(_el$5, () => taskAgent(part() ?? fallback().part), null);
221
+ _$insertNode(_el$7, _el$8);
222
+ _$setProp(_el$7, "wrapMode", "none");
223
+ _$setProp(_el$7, "truncate", true);
224
+ _$insert(_el$7, () => taskDescription(part() ?? fallback().part), null);
225
+ _$insertNode(_el$9, _el$0);
226
+ _$setProp(_el$9, "wrapMode", "none");
227
+ _$setProp(_el$9, "truncate", true);
228
+ _$insert(_el$9, responseLabel, null);
229
+ _$insertNode(_el$1, _el$10);
230
+ _$setProp(_el$1, "wrapMode", "none");
231
+ _$setProp(_el$1, "truncate", true);
232
+ _$insert(_el$1, () => taskStatusLabel(part() ?? fallback().part, childSessionID() ? props.api.state.session.status(childSessionID()) : undefined), null);
233
+ _$insertNode(_el$11, _el$12);
234
+ _$insertNode(_el$11, _el$13);
235
+ _$setProp(_el$11, "wrapMode", "none");
236
+ _$setProp(_el$11, "truncate", true);
237
+ _$insert(_el$11, () => modelName(props.api, model()), _el$13);
238
+ _$insert(_el$11, () => taskVariantLabel(model()) ?? "pending", null);
239
+ _$insertNode(_el$14, _el$15);
240
+ _$setProp(_el$14, "wrapMode", "none");
241
+ _$setProp(_el$14, "truncate", true);
242
+ _$insert(_el$14, () => taskModeLabel(part() ?? fallback().part), _el$15);
243
+ _$insert(_el$14, () => elapsedLabel(props.now() - props.startedAt(part() ?? fallback().part)), null);
244
+ _$insertNode(_el$16, _el$17);
245
+ _$setProp(_el$16, "wrapMode", "none");
246
+ _$setProp(_el$16, "truncate", true);
247
+ _$insert(_el$16, () => childSessionID() ?? "Waiting for child session", null);
248
+ _$insertNode(_el$18, _el$19);
249
+ _$insertNode(_el$18, _el$23);
250
+ _$setProp(_el$18, "flexDirection", "row");
251
+ _$setProp(_el$18, "gap", 2);
252
+ _$insertNode(_el$19, _el$20);
253
+ _$setProp(_el$19, "onMouseUp", openSession);
254
+ _$insertNode(_el$20, _el$21);
255
+ _$insertNode(_el$21, _$createTextNode(`Open Session`));
256
+ _$insertNode(_el$23, _el$24);
257
+ _$insertNode(_el$24, _el$25);
258
+ _$insertNode(_el$25, _$createTextNode(`Close`));
259
+ _$effect(_p$ => {
260
+ var _v$ = props.api.theme.current.borderActive,
261
+ _v$2 = props.api.theme.current.backgroundPanel,
262
+ _v$3 = props.api.theme.current.text,
263
+ _v$4 = props.api.theme.current.text,
264
+ _v$5 = props.api.theme.current.textMuted,
265
+ _v$6 = props.api.theme.current.textMuted,
266
+ _v$7 = props.api.theme.current.textMuted,
267
+ _v$8 = props.api.theme.current.textMuted,
268
+ _v$9 = props.api.theme.current.textMuted,
269
+ _v$0 = props.api.theme.current.textMuted,
270
+ _v$1 = canOpen() ? props.api.theme.current.primary : props.api.theme.current.textMuted,
271
+ _v$10 = props.close,
272
+ _v$11 = props.api.theme.current.primary;
273
+ _v$ !== _p$.e && (_p$.e = _$setProp(_el$, "borderColor", _v$, _p$.e));
274
+ _v$2 !== _p$.t && (_p$.t = _$setProp(_el$, "backgroundColor", _v$2, _p$.t));
275
+ _v$3 !== _p$.a && (_p$.a = _$setProp(_el$2, "fg", _v$3, _p$.a));
276
+ _v$4 !== _p$.o && (_p$.o = _$setProp(_el$5, "fg", _v$4, _p$.o));
277
+ _v$5 !== _p$.i && (_p$.i = _$setProp(_el$7, "fg", _v$5, _p$.i));
278
+ _v$6 !== _p$.n && (_p$.n = _$setProp(_el$9, "fg", _v$6, _p$.n));
279
+ _v$7 !== _p$.s && (_p$.s = _$setProp(_el$1, "fg", _v$7, _p$.s));
280
+ _v$8 !== _p$.h && (_p$.h = _$setProp(_el$11, "fg", _v$8, _p$.h));
281
+ _v$9 !== _p$.r && (_p$.r = _$setProp(_el$14, "fg", _v$9, _p$.r));
282
+ _v$0 !== _p$.d && (_p$.d = _$setProp(_el$16, "fg", _v$0, _p$.d));
283
+ _v$1 !== _p$.l && (_p$.l = _$setProp(_el$20, "fg", _v$1, _p$.l));
284
+ _v$10 !== _p$.u && (_p$.u = _$setProp(_el$23, "onMouseUp", _v$10, _p$.u));
285
+ _v$11 !== _p$.c && (_p$.c = _$setProp(_el$24, "fg", _v$11, _p$.c));
286
+ return _p$;
287
+ }, {
288
+ e: undefined,
289
+ t: undefined,
290
+ a: undefined,
291
+ o: undefined,
292
+ i: undefined,
293
+ n: undefined,
294
+ s: undefined,
295
+ h: undefined,
296
+ r: undefined,
297
+ d: undefined,
298
+ l: undefined,
299
+ u: undefined,
300
+ c: undefined
301
+ });
302
+ return _el$;
303
+ }
304
+ });
172
305
  }
173
306
  function TaskCard(props) {
174
307
  // The call ID is stable, while the actual ToolPart is re-read after each event.
@@ -191,59 +324,89 @@ function TaskCard(props) {
191
324
  const current = part();
192
325
  return current ? elapsedLabel(props.now() - props.startedAt(current)) : "";
193
326
  };
327
+ let card;
328
+ let lastAnchor;
329
+ const anchor = () => card && {
330
+ x: card.screenX,
331
+ y: card.screenY,
332
+ width: card.width,
333
+ height: card.height
334
+ };
335
+ const updateAnchor = () => {
336
+ const current = anchor();
337
+ if (!current || lastAnchor && lastAnchor.x === current.x && lastAnchor.y === current.y && lastAnchor.width === current.width && lastAnchor.height === current.height) {
338
+ return;
339
+ }
340
+ lastAnchor = current;
341
+ props.updatePopupAnchor(props.callID, current);
342
+ };
343
+ onCleanup(() => {
344
+ // The popover owns its last reported rect, so losing this card must not
345
+ // reset its placement before the user explicitly closes it.
346
+ card = undefined;
347
+ lastAnchor = undefined;
348
+ });
194
349
  const openDetails = () => {
195
350
  if (props.api.renderer.getSelection()?.getSelectedText()) return;
196
351
  const part = livePart();
197
- if (!part) return;
352
+ const currentAnchor = anchor();
353
+ if (!part || !currentAnchor) return;
198
354
  props.remember(part);
199
- props.openPopup(props.callID, part);
355
+ props.openPopup(props.callID, currentAnchor);
200
356
  };
201
357
  return (() => {
202
- var _el$29 = _$createElement("box"),
203
- _el$30 = _$createElement("box"),
204
- _el$31 = _$createElement("text"),
205
- _el$32 = _$createElement("b"),
358
+ var _el$27 = _$createElement("box"),
359
+ _el$28 = _$createElement("box"),
360
+ _el$29 = _$createElement("text"),
361
+ _el$30 = _$createElement("b"),
362
+ _el$31 = _$createElement("box"),
363
+ _el$32 = _$createElement("text"),
206
364
  _el$33 = _$createElement("box"),
207
365
  _el$34 = _$createElement("text"),
208
366
  _el$35 = _$createElement("box"),
209
- _el$36 = _$createElement("text"),
210
- _el$37 = _$createElement("box"),
211
- _el$38 = _$createElement("text");
367
+ _el$36 = _$createElement("text");
368
+ _$insertNode(_el$27, _el$28);
369
+ _$insertNode(_el$27, _el$33);
370
+ _$use(element => {
371
+ card = element;
372
+ updateAnchor();
373
+ }, _el$27);
374
+ _$setProp(_el$27, "onSizeChange", updateAnchor);
375
+ _$setProp(_el$27, "renderBefore", updateAnchor);
376
+ _$setProp(_el$27, "width", "100%");
377
+ _$setProp(_el$27, "height", 2);
378
+ _$setProp(_el$27, "flexDirection", "column");
379
+ _$setProp(_el$27, "overflow", "hidden");
380
+ _$setProp(_el$27, "onMouseUp", openDetails);
381
+ _$insertNode(_el$28, _el$29);
382
+ _$insertNode(_el$28, _el$31);
383
+ _$setProp(_el$28, "width", "100%");
384
+ _$setProp(_el$28, "height", 1);
385
+ _$setProp(_el$28, "flexDirection", "row");
386
+ _$setProp(_el$28, "overflow", "hidden");
212
387
  _$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, (() => {
388
+ _$setProp(_el$29, "flexGrow", 1);
389
+ _$setProp(_el$29, "flexShrink", 1);
390
+ _$setProp(_el$29, "minWidth", 0);
391
+ _$setProp(_el$29, "wrapMode", "none");
392
+ _$setProp(_el$29, "truncate", true);
393
+ _$insert(_el$30, (() => {
231
394
  var _c$ = _$memo(() => !!part());
232
395
  return () => _c$() ? taskAgent(part()) : "subagent";
233
396
  })());
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, {
397
+ _$insertNode(_el$31, _el$32);
398
+ _$setProp(_el$31, "flexShrink", 1);
399
+ _$setProp(_el$31, "minWidth", 0);
400
+ _$setProp(_el$31, "maxWidth", 28);
401
+ _$setProp(_el$31, "marginLeft", 1);
402
+ _$setProp(_el$31, "flexDirection", "row");
403
+ _$setProp(_el$32, "flexGrow", 1);
404
+ _$setProp(_el$32, "flexShrink", 1);
405
+ _$setProp(_el$32, "minWidth", 0);
406
+ _$setProp(_el$32, "wrapMode", "none");
407
+ _$setProp(_el$32, "truncate", true);
408
+ _$insert(_el$32, () => modelName(props.api, model()));
409
+ _$insert(_el$31, _$createComponent(Show, {
247
410
  get when() {
248
411
  return taskVariantLabel(model());
249
412
  },
@@ -253,61 +416,62 @@ function TaskCard(props) {
253
416
  return model();
254
417
  },
255
418
  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;
419
+ var _el$37 = _$createElement("text");
420
+ _$insertNode(_el$37, _$createTextNode(`· pending`));
421
+ _$setProp(_el$37, "flexShrink", 0);
422
+ _$setProp(_el$37, "marginLeft", 1);
423
+ _$setProp(_el$37, "wrapMode", "none");
424
+ _$setProp(_el$37, "truncate", true);
425
+ _$effect(_$p => _$setProp(_el$37, "fg", props.api.theme.current.textMuted, _$p));
426
+ return _el$37;
264
427
  }
265
428
  });
266
429
  },
267
430
  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;
431
+ var _el$39 = _$createElement("text"),
432
+ _el$40 = _$createTextNode(`· `);
433
+ _$insertNode(_el$39, _el$40);
434
+ _$setProp(_el$39, "flexShrink", 0);
435
+ _$setProp(_el$39, "marginLeft", 1);
436
+ _$setProp(_el$39, "wrapMode", "none");
437
+ _$setProp(_el$39, "truncate", true);
438
+ _$insert(_el$39, variant, null);
439
+ _$effect(_$p => _$setProp(_el$39, "fg", props.api.theme.current.textMuted, _$p));
440
+ return _el$39;
278
441
  })()
279
442
  }), null);
443
+ _$insertNode(_el$33, _el$34);
444
+ _$insertNode(_el$33, _el$35);
445
+ _$setProp(_el$33, "width", "100%");
446
+ _$setProp(_el$33, "height", 1);
447
+ _$setProp(_el$33, "flexDirection", "row");
448
+ _$setProp(_el$33, "overflow", "hidden");
449
+ _$setProp(_el$34, "flexGrow", 1);
450
+ _$setProp(_el$34, "flexShrink", 1);
451
+ _$setProp(_el$34, "minWidth", 0);
452
+ _$setProp(_el$34, "wrapMode", "none");
453
+ _$setProp(_el$34, "truncate", true);
454
+ _$insert(_el$34, (() => {
455
+ var _c$2 = _$memo(() => !!part());
456
+ return () => _c$2() ? taskDescription(part()) : "Working";
457
+ })());
280
458
  _$insertNode(_el$35, _el$36);
281
- _$insertNode(_el$35, _el$37);
282
- _$setProp(_el$35, "width", "100%");
283
- _$setProp(_el$35, "height", 1);
459
+ _$setProp(_el$35, "width", 8);
460
+ _$setProp(_el$35, "flexShrink", 0);
284
461
  _$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);
462
+ _$setProp(_el$35, "justifyContent", "flex-end");
289
463
  _$setProp(_el$36, "wrapMode", "none");
290
464
  _$setProp(_el$36, "truncate", true);
291
- _$insert(_el$36, (() => {
292
- var _c$2 = _$memo(() => !!part());
293
- return () => _c$2() ? taskDescription(part()) : "Working";
294
- })());
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);
465
+ _$insert(_el$36, elapsed);
302
466
  _$effect(_p$ => {
303
- var _v$11 = props.api.theme.current.text,
304
- _v$12 = props.api.theme.current.textMuted,
467
+ var _v$12 = props.api.theme.current.text,
305
468
  _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));
469
+ _v$14 = props.api.theme.current.textMuted,
470
+ _v$15 = props.api.theme.current.textMuted;
471
+ _v$12 !== _p$.e && (_p$.e = _$setProp(_el$29, "fg", _v$12, _p$.e));
472
+ _v$13 !== _p$.t && (_p$.t = _$setProp(_el$32, "fg", _v$13, _p$.t));
473
+ _v$14 !== _p$.a && (_p$.a = _$setProp(_el$34, "fg", _v$14, _p$.a));
474
+ _v$15 !== _p$.o && (_p$.o = _$setProp(_el$36, "fg", _v$15, _p$.o));
311
475
  return _p$;
312
476
  }, {
313
477
  e: undefined,
@@ -315,7 +479,7 @@ function TaskCard(props) {
315
479
  a: undefined,
316
480
  o: undefined
317
481
  });
318
- return _el$29;
482
+ return _el$27;
319
483
  })();
320
484
  }
321
485
  function RunningSubagents(props) {
@@ -369,7 +533,10 @@ function RunningSubagents(props) {
369
533
  setRevision(value => value + 1);
370
534
  void loadMessages(parentSessionID, currentRequest);
371
535
  };
372
- createEffect(on(() => props.sessionID, sessionID => void selectSession(sessionID)));
536
+ createEffect(on(() => props.sessionID, (sessionID, previousSessionID) => {
537
+ if (previousSessionID !== undefined && sessionID !== previousSessionID) setOpenPopup(undefined);
538
+ void selectSession(sessionID);
539
+ }));
373
540
  const allTaskParts = createMemo(() => {
374
541
  revision();
375
542
  const sessionID = scopeSessionID();
@@ -420,37 +587,36 @@ function RunningSubagents(props) {
420
587
  firstSeen.set(part.callID, value);
421
588
  return value;
422
589
  };
423
- const releasePopup = ownership => {
424
- if (openPopup()?.token === ownership.token) setOpenPopup(undefined);
425
- };
426
590
  const closePopup = ownership => {
427
591
  if (openPopup()?.token !== ownership.token) return;
428
592
  setOpenPopup(undefined);
429
- props.api.ui.dialog.clear();
430
593
  };
431
- const showPopup = (callID, part) => {
594
+ const showPopup = (callID, anchor) => {
432
595
  const ownership = {
433
596
  callID,
434
- token: Symbol(callID)
597
+ token: Symbol(callID),
598
+ anchor
435
599
  };
436
600
  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
601
  };
602
+ const updatePopupAnchor = (callID, anchor) => {
603
+ const ownership = openPopup();
604
+ if (!ownership || ownership.callID !== callID) return;
605
+ if (ownership.anchor.x === anchor.x && ownership.anchor.y === anchor.y && ownership.anchor.width === anchor.width && ownership.anchor.height === anchor.height) {
606
+ return;
607
+ }
608
+ setOpenPopup({
609
+ ...ownership,
610
+ anchor
611
+ });
612
+ };
613
+ const dismissForHostDialog = () => {
614
+ if (openPopup() && props.api.ui.dialog.open) setOpenPopup(undefined);
615
+ };
616
+ const dismissForHostDialogFrame = async () => {
617
+ dismissForHostDialog();
618
+ };
619
+ createEffect(dismissForHostDialog);
454
620
  const unregister = [];
455
621
  let timer;
456
622
  onMount(() => {
@@ -465,40 +631,42 @@ function RunningSubagents(props) {
465
631
  setRevision(value => value + 1);
466
632
  }
467
633
  };
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)));
634
+ unregister.push(() => {
635
+ props.api.renderer.removeFrameCallback(dismissForHostDialogFrame);
636
+ }, 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)));
637
+ props.api.renderer.setFrameCallback(dismissForHostDialogFrame);
469
638
  timer = setInterval(() => setNow(Date.now()), 1_000);
470
639
  });
471
640
  onCleanup(() => {
472
641
  sessionRequest += 1;
473
642
  if (timer) clearInterval(timer);
474
643
  for (const dispose of unregister) dispose();
475
- const ownership = openPopup();
476
- if (ownership) closePopup(ownership);
644
+ setOpenPopup(undefined);
477
645
  });
478
646
  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, (() => {
647
+ var _el$41 = _$createElement("box"),
648
+ _el$42 = _$createElement("text"),
649
+ _el$43 = _$createElement("b"),
650
+ _el$45 = _$createElement("span");
651
+ _$insertNode(_el$41, _el$42);
652
+ _$setProp(_el$41, "overflow", "hidden");
653
+ _$insertNode(_el$42, _el$43);
654
+ _$insertNode(_el$42, _el$45);
655
+ _$insertNode(_el$43, _$createTextNode(`Subagents`));
656
+ _$insert(_el$45, (() => {
489
657
  var _c$3 = _$memo(() => !!visibleTaskParts().length);
490
658
  return () => _c$3() ? ` (${visibleTaskParts().length} running)` : "";
491
659
  })());
492
- _$insert(_el$43, _$createComponent(Show, {
660
+ _$insert(_el$41, _$createComponent(Show, {
493
661
  get when() {
494
662
  return taskCallIDs().length > 0;
495
663
  },
496
664
  get fallback() {
497
665
  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;
666
+ var _el$46 = _$createElement("text");
667
+ _$insertNode(_el$46, _$createTextNode(`No subagent running`));
668
+ _$effect(_$p => _$setProp(_el$46, "fg", props.api.theme.current.textMuted, _$p));
669
+ return _el$46;
502
670
  })();
503
671
  },
504
672
  get children() {
@@ -517,24 +685,48 @@ function RunningSubagents(props) {
517
685
  revision: revision,
518
686
  now: now,
519
687
  startedAt: startedAt,
520
- openPopup: showPopup
688
+ openPopup: showPopup,
689
+ updatePopupAnchor: updatePopupAnchor
521
690
  })
522
691
  });
523
692
  }
524
693
  }), null);
694
+ _$insert(_el$41, _$createComponent(Show, {
695
+ get when() {
696
+ return openPopup();
697
+ },
698
+ children: ownership => _$createComponent(TaskPopover, {
699
+ get api() {
700
+ return props.api;
701
+ },
702
+ ownership: ownership,
703
+ get currentSessionID() {
704
+ return props.sessionID;
705
+ },
706
+ get fallbackPart() {
707
+ return fallbacks.get(ownership().callID).part;
708
+ },
709
+ tasksByCallID: tasksByCallID,
710
+ fallbackFor: id => fallbacks.get(id),
711
+ revision: revision,
712
+ now: now,
713
+ startedAt: startedAt,
714
+ close: () => closePopup(ownership())
715
+ })
716
+ }), null);
525
717
  _$effect(_p$ => {
526
- var _v$15 = props.api.theme.current.text,
527
- _v$16 = {
718
+ var _v$16 = props.api.theme.current.text,
719
+ _v$17 = {
528
720
  fg: props.api.theme.current.textMuted
529
721
  };
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));
722
+ _v$16 !== _p$.e && (_p$.e = _$setProp(_el$42, "fg", _v$16, _p$.e));
723
+ _v$17 !== _p$.t && (_p$.t = _$setProp(_el$45, "style", _v$17, _p$.t));
532
724
  return _p$;
533
725
  }, {
534
726
  e: undefined,
535
727
  t: undefined
536
728
  });
537
- return _el$43;
729
+ return _el$41;
538
730
  })();
539
731
  }
540
732
  const tui = async api => {