@qilin-agent/client-ui-sidebar-tasks 0.0.0-stage → 3.1.4

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/lib/client.js ADDED
@@ -0,0 +1,2006 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@qilin-agent/client-ui-sidebar-tasks",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let _qilin_agent_client_ui_primitives = require("@qilin-agent/client-ui-primitives");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+ let react = require("react");
10
+ let _qilin_agent_client_store = require("@qilin-agent/client-store");
11
+ //#region lib/types/client/definition.js
12
+ /** The tab kind this package owns; `ctx.sidebarRight.openTab` names it. */
13
+ const TASKS_KIND = "tasks";
14
+ /** This implementation's identity in the tab system, and the key its body registers under. */
15
+ const TASKS_ID = "@qilin-agent/client-ui-sidebar-tasks";
16
+ /**
17
+ * The tasks type's registry definition.
18
+ * @param t - namespace-bound translate, read fresh on every label call.
19
+ * @returns the definition to register.
20
+ */
21
+ function tasksDefinition(t) {
22
+ return {
23
+ id: TASKS_ID,
24
+ kind: TASKS_KIND,
25
+ priority: "builtin",
26
+ label: () => t("type.label"),
27
+ icon: _qilin_agent_client_ui_primitives.IconChecklistOutline14,
28
+ single: true,
29
+ title: () => t("type.label"),
30
+ guide: [{
31
+ id: "tasks",
32
+ order: 40,
33
+ title: () => t("guide.title"),
34
+ description: () => t("guide.description"),
35
+ icon: _qilin_agent_client_ui_primitives.IconChecklistOutline14
36
+ }]
37
+ };
38
+ }
39
+ //#endregion
40
+ //#region lib/types/client/face.js
41
+ /**
42
+ * Bind the page's actions to the service and Remote they call.
43
+ * @param sessions - the Client Session service's navigation and catalog reads.
44
+ * @param subagents - the generated `subagents` Remote namespace.
45
+ * @param jobs - the client jobs roster and its watcher.
46
+ * @returns the Slot `inject` face the body receives.
47
+ */
48
+ function tasksFace(sessions, subagents, jobs) {
49
+ return {
50
+ ...jobs,
51
+ openChild(target) {
52
+ sessions.openSubagent(target);
53
+ },
54
+ refresh(parentSessionId) {
55
+ sessions.refreshProjections(parentSessionId);
56
+ },
57
+ interruptChild(childSessionId, parentSessionId) {
58
+ subagents.interruptByParent(childSessionId, parentSessionId, "continuable");
59
+ }
60
+ };
61
+ }
62
+ //#endregion
63
+ //#region lib/types/client/rows.js
64
+ /** Stable empty job list, so a Session without jobs keeps one array identity. */
65
+ const NO_JOBS = [];
66
+ /**
67
+ * The read state of one parent's catalog, as the page draws it. A snapshot that
68
+ * has not settled is `loading` while it carries no catalog value and `ready`
69
+ * once it does, so a Session the Host described as childless reads as a settled
70
+ * empty catalog rather than as a pending one.
71
+ * @param snapshot - the Session's projection snapshot.
72
+ * @returns the catalog read state.
73
+ */
74
+ function catalogState(snapshot) {
75
+ if (snapshot.state !== "idle") return snapshot.state;
76
+ return snapshot.values.subagentCatalog === void 0 ? "loading" : "ready";
77
+ }
78
+ /**
79
+ * Project the shared per-Session projection snapshots into the catalogs the page draws.
80
+ * @param projections - projection snapshots keyed by Session id.
81
+ * @param running - whether the Session list currently reports a Session as running.
82
+ * @returns one catalog per projected Session.
83
+ */
84
+ function catalogsOf(projections, running) {
85
+ const catalogs = {};
86
+ for (const [key, snapshot] of Object.entries(projections)) {
87
+ const id = key;
88
+ const entries = snapshot.values.subagentCatalog ?? [];
89
+ catalogs[id] = {
90
+ state: catalogState(snapshot),
91
+ error: snapshot.error,
92
+ entries: entries.map((entry) => {
93
+ const below = projections[entry.id];
94
+ return {
95
+ kind: "child",
96
+ id: entry.id,
97
+ mode: entry.mode,
98
+ ...entry.label === void 0 ? {} : { label: entry.label },
99
+ activity: running(entry.id) ? "running" : "inactive",
100
+ hasChildren: below === void 0 || catalogState(below) !== "ready" || (below.values.subagentCatalog?.length ?? 0) > 0
101
+ };
102
+ })
103
+ };
104
+ }
105
+ return catalogs;
106
+ }
107
+ /**
108
+ * One catalog entry as a row, under the parent whose catalog reported it.
109
+ * @param entry - the catalog's durable direct-child row.
110
+ * @param parentSessionId - the Session the entry is a direct child of.
111
+ * @param depth - nesting level under the section's root.
112
+ * @returns the row the section draws.
113
+ */
114
+ function rowOf(entry, parentSessionId, depth) {
115
+ if (entry.kind === "diagnostic") return {
116
+ kind: "diagnostic",
117
+ id: entry.id,
118
+ depth,
119
+ reason: entry.reason
120
+ };
121
+ const address = {
122
+ parentSessionId,
123
+ childSessionId: entry.id,
124
+ mode: entry.mode
125
+ };
126
+ return {
127
+ kind: "child",
128
+ id: entry.id,
129
+ address,
130
+ label: entry.label ?? entry.id,
131
+ depth,
132
+ running: entry.activity === "running"
133
+ };
134
+ }
135
+ /**
136
+ * Flatten one Session's subagent topology from the catalogs in hand.
137
+ *
138
+ * A level is one parent's catalog, and the walk descends only into children
139
+ * whose own catalog has been read; a child the page has never opened
140
+ * contributes its own row and no rows below it. Siblings are newest-first
141
+ * because the Host orders a catalog by durable creation time. The seen set
142
+ * makes a repeated or cyclic parent an ordinary skip rather than an endless
143
+ * walk.
144
+ * @param catalogs - direct-child catalogs keyed by parent Session id.
145
+ * @param rootSessionId - the Session the section is rooted at.
146
+ * @returns rows in display order, depth-first.
147
+ */
148
+ function subagentRows(catalogs, rootSessionId) {
149
+ const rows = [];
150
+ const seen = /* @__PURE__ */ new Set();
151
+ const visit = (parentSessionId, depth) => {
152
+ const catalog = catalogs[parentSessionId];
153
+ if (catalog === void 0) return;
154
+ for (const entry of [...catalog.entries].reverse()) {
155
+ if (seen.has(entry.id)) continue;
156
+ seen.add(entry.id);
157
+ rows.push(rowOf(entry, parentSessionId, depth));
158
+ if (entry.kind === "child" && entry.hasChildren) visit(entry.id, depth + 1);
159
+ }
160
+ };
161
+ visit(rootSessionId, 0);
162
+ return rows;
163
+ }
164
+ /**
165
+ * How many rows the subagents section counts as children. A diagnostic row is a
166
+ * child the Host could not describe, so it is drawn but never counted.
167
+ * @param rows - the section's rows.
168
+ * @returns the number of child rows.
169
+ */
170
+ function childRowCount(rows) {
171
+ return rows.filter((row) => row.kind === "child").length;
172
+ }
173
+ /**
174
+ * The subagents section's stated total: the direct children the catalog
175
+ * reports, or the descendants the summary lineage records, whichever is
176
+ * greater.
177
+ *
178
+ * The lineage reaches subagents below the direct children, and the catalog is
179
+ * authoritative while the lineage is still converging, so taking the greater
180
+ * of the two is never an undercount during that window.
181
+ * @param children - child rows the catalog reports.
182
+ * @param descendants - lineage totals keyed by possible parent.
183
+ * @param rootSessionId - the Session the section is rooted at.
184
+ * @returns the total the section header states.
185
+ */
186
+ function subagentTotal(children, descendants, rootSessionId) {
187
+ return Math.max(children, descendants.get(rootSessionId)?.count ?? 0);
188
+ }
189
+ /**
190
+ * Whether a job remains active in the registry.
191
+ * @param job - the Session job row.
192
+ * @returns whether the job is running or stopping.
193
+ */
194
+ function isLive(job) {
195
+ return job.status === "running" || job.status === "stopping";
196
+ }
197
+ /**
198
+ * Live rows first in start order, then settled rows newest-first. Two jobs that
199
+ * settled in the same millisecond fall back to the later start, which is the
200
+ * same newest-first reading the settled order already uses, so the sort never
201
+ * depends on the Host's map iteration.
202
+ * @param jobs - the Session's job rows.
203
+ * @returns a new array in display order.
204
+ */
205
+ function orderedJobs(jobs) {
206
+ return [...jobs].sort((left, right) => {
207
+ const liveLeft = isLive(left);
208
+ if (liveLeft !== isLive(right)) return liveLeft ? -1 : 1;
209
+ if (liveLeft) return left.startedAt - right.startedAt;
210
+ const settled = (right.finishedAt ?? right.startedAt) - (left.finishedAt ?? left.startedAt);
211
+ return settled !== 0 ? settled : right.startedAt - left.startedAt;
212
+ });
213
+ }
214
+ /**
215
+ * Elapsed milliseconds for one job row.
216
+ * @param job - the job.
217
+ * @param now - the clock sample a live duration measures against.
218
+ * @returns its span so far while it runs, its own span once it settled.
219
+ */
220
+ function jobElapsed(job, now) {
221
+ return isLive(job) ? now - job.startedAt : (job.finishedAt ?? job.startedAt) - job.startedAt;
222
+ }
223
+ /**
224
+ * Elapsed time in at most two adjacent units. A background job that outlives an
225
+ * hour is already exceptional, so hours is the widest unit.
226
+ * @param elapsedMs - elapsed milliseconds.
227
+ * @param t - namespace-bound translate.
228
+ * @returns the duration as the dictionary phrases it.
229
+ */
230
+ function formatDuration(elapsedMs, t) {
231
+ const total = Math.max(0, Math.floor(elapsedMs / 1e3));
232
+ const seconds = total % 60;
233
+ const minutes = Math.floor(total / 60) % 60;
234
+ const hours = Math.floor(total / 3600);
235
+ if (hours > 0) return t("tasks.duration.hours", {
236
+ hours,
237
+ minutes
238
+ });
239
+ if (minutes > 0) return t("tasks.duration.minutes", {
240
+ minutes,
241
+ seconds
242
+ });
243
+ return t("tasks.duration.seconds", { seconds });
244
+ }
245
+ /** Closed-union exhaustiveness fence for the wire status set. */
246
+ /* v8 ignore next 3 -- closed-union backstop; only reached if a status is forged */
247
+ function assertNever(value) {
248
+ throw new Error(`unhandled job status: ${JSON.stringify(value)}`);
249
+ }
250
+ /**
251
+ * Human status word for a row that carries no producer detail.
252
+ * @param status - the wire status.
253
+ * @param t - namespace-bound translate.
254
+ * @returns the word the row shows.
255
+ */
256
+ function jobStatusLabel(status, t) {
257
+ switch (status) {
258
+ case "running": return t("tasks.status.running");
259
+ case "stopping": return t("tasks.status.stopping");
260
+ case "completed": return t("tasks.status.completed");
261
+ case "killed": return t("tasks.status.killed");
262
+ case "failed": return t("tasks.status.failed");
263
+ /* v8 ignore next -- closed wire status union */
264
+ default: return assertNever(status);
265
+ }
266
+ }
267
+ /**
268
+ * Status marker semantics. `stopping` and `killed` share the attention colour:
269
+ * both mean the work ended (or is ending) on request rather than on its own.
270
+ * @param status - the wire status.
271
+ * @returns the state the dot draws.
272
+ */
273
+ function jobDotState(status) {
274
+ switch (status) {
275
+ case "running": return "ongoing";
276
+ case "stopping": return "warning";
277
+ case "completed": return "done";
278
+ case "killed": return "warning";
279
+ case "failed": return "error";
280
+ /* v8 ignore next -- closed wire status union */
281
+ default: return assertNever(status);
282
+ }
283
+ }
284
+ /**
285
+ * The work a chip badge counts for one Session: direct-child catalog rows whose
286
+ * activity is `running`, plus the jobs the registry still holds open.
287
+ *
288
+ * A chip re-renders on every strip pass, so this reads the two snapshots
289
+ * directly instead of walking the summary lineage; a running grandchild implies
290
+ * its parent is running too, so the direct children already account for work
291
+ * below them.
292
+ * @param catalogs - direct-child catalogs keyed by parent Session id.
293
+ * @param jobs - the Session's job rows.
294
+ * @param sessionId - the Session the chip belongs to.
295
+ * @returns the count, zero when nothing is active.
296
+ */
297
+ function activeWorkCount(catalogs, jobs, sessionId) {
298
+ return (catalogs[sessionId]?.entries ?? []).filter((entry) => entry.kind === "child" && entry.activity === "running").length + jobs.filter(isLive).length;
299
+ }
300
+ //#endregion
301
+ //#region lib/types/client/work-sources.js
302
+ /**
303
+ * The live sources both tasks surfaces read: the Session list's summaries and
304
+ * running-subagent catalogs, and this Session's background-job rows.
305
+ *
306
+ * The surface owns the roster subscription for as long as it draws, so the hook
307
+ * keeps it here rather than in either component.
308
+ */
309
+ /**
310
+ * Read the Session list and the job roster once per render and derive the
311
+ * running-work catalogs from them.
312
+ * @param sessionId - the Session whose job rows and subagent topology to read.
313
+ * @param seats - the standing Session list, the bound jobs roster, and its watcher.
314
+ * @returns this Session's job rows, the Session list summaries the surfaces fold over, and the running catalogs keyed by Session.
315
+ */
316
+ function useWorkSources(sessionId, seats) {
317
+ const { useSessions, useJobs, watchRows } = seats;
318
+ const projections = useSessions((state) => state.projectionsBySession);
319
+ const summaries = useSessions((state) => state.byId);
320
+ const jobs = useJobs((state) => state.rows[sessionId]) ?? NO_JOBS;
321
+ (0, react.useEffect)(() => watchRows(sessionId), [sessionId, watchRows]);
322
+ return {
323
+ jobs,
324
+ summaries,
325
+ catalogs: (0, react.useMemo)(() => catalogsOf(projections, (id) => summaries[id]?.running === true), [projections, summaries])
326
+ };
327
+ }
328
+ //#endregion
329
+ //#region lib/types/client/TasksBadge.js
330
+ /**
331
+ * Render the running-work count, or nothing when the Session is idle.
332
+ * @param props - the Session scope's standard props.
333
+ * @returns the count, or null when no subagent and no job is running.
334
+ */
335
+ function TasksBadge({ sessionId, useSessions, useJobs, watchRows }) {
336
+ const { catalogs, jobs } = useWorkSources(sessionId, {
337
+ useSessions,
338
+ useJobs,
339
+ watchRows
340
+ });
341
+ const active = activeWorkCount(catalogs, jobs, sessionId);
342
+ return active === 0 ? null : (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.Tag, {
343
+ tone: "outline",
344
+ children: active
345
+ });
346
+ }
347
+ //#endregion
348
+ //#region lib/types/client/locales.js
349
+ /** Dictionary namespace owned by this plugin. */
350
+ const NS = "sidebarTasks";
351
+ /** Simplified Chinese dictionary and key-set source of truth. */
352
+ const zh = {
353
+ "type.label": "任务",
354
+ "guide.title": "任务与子代理",
355
+ "guide.description": "查看子代理拓扑与后台任务",
356
+ empty: "这个会话还没有子代理或后台任务。",
357
+ "subagents.title": "子代理",
358
+ "subagents.count.one": "{count} 个子代理",
359
+ "subagents.count.other": "{count} 个子代理",
360
+ "subagents.loading": "正在读取子代理…",
361
+ "subagents.empty": "没有子代理",
362
+ "subagents.failed": "无法读取子代理目录",
363
+ "subagents.retry": "重试",
364
+ "subagents.refresh": "刷新子代理",
365
+ "subagents.interrupt": "中断",
366
+ "subagents.mode.oneShot": "一次性",
367
+ "subagents.mode.continuable": "可继续",
368
+ "subagents.diagnostic.corrupt": "这条子代理记录已损坏",
369
+ "subagents.diagnostic.unsupported": "这条子代理记录的版本不受支持",
370
+ "subagents.diagnostic.unavailable": "这条子代理记录暂不可读",
371
+ "tasks.title": "后台任务",
372
+ "tasks.count.one": "{count} 个后台任务",
373
+ "tasks.count.other": "{count} 个后台任务",
374
+ "tasks.empty": "没有后台任务",
375
+ "tasks.status.running": "运行中",
376
+ "tasks.status.stopping": "正在停止",
377
+ "tasks.status.completed": "已完成",
378
+ "tasks.status.killed": "已取消",
379
+ "tasks.status.failed": "已失败",
380
+ "tasks.duration.seconds": "{seconds}秒",
381
+ "tasks.duration.minutes": "{minutes}分{seconds}秒",
382
+ "tasks.duration.hours": "{hours}小时{minutes}分",
383
+ "tasks.duration.live": "已运行 {duration}",
384
+ "tasks.duration.done": "耗时 {duration}",
385
+ "more.expand": "展开更多(还有 {count} 条)",
386
+ "more.collapse": "收起",
387
+ "graph.toggle": "切换视图",
388
+ "graph.view": "图",
389
+ "graph.list": "列表",
390
+ "graph.zoomIn": "放大",
391
+ "graph.zoomOut": "缩小",
392
+ "graph.fit": "适配",
393
+ "graph.arrange": "自动整理",
394
+ "graph.mode.tree": "树形",
395
+ "graph.mode.compact": "紧凑",
396
+ "graph.mode.grid": "网格",
397
+ "graph.resetLayout": "重置布局",
398
+ "graph.status.running": "运行中",
399
+ "graph.status.idle": "空闲",
400
+ "graph.loading": "读取中…",
401
+ "graph.unphased": "未分相位",
402
+ "graph.badge.main": "主代理",
403
+ "graph.badge.subagent": "子代理",
404
+ "graph.badge.done": "已完成",
405
+ "graph.badge.standby": "待命",
406
+ "graph.badge.placeholder": "子代理",
407
+ "graph.badge.diagnostic": "不可读",
408
+ "graph.badge.run": "工作流",
409
+ "graph.badge.phase": "相位",
410
+ "graph.badge.member": "成员"
411
+ };
412
+ /** English dictionary, checked against the Chinese key set. */
413
+ const en = {
414
+ "type.label": "Tasks",
415
+ "guide.title": "Tasks and subagents",
416
+ "guide.description": "Review the subagent topology and background jobs",
417
+ empty: "This session has no subagents and no background jobs yet.",
418
+ "subagents.title": "Subagents",
419
+ "subagents.count.one": "{count} subagent",
420
+ "subagents.count.other": "{count} subagents",
421
+ "subagents.loading": "Reading subagents…",
422
+ "subagents.empty": "No subagents",
423
+ "subagents.failed": "Unable to read the subagent catalog",
424
+ "subagents.retry": "Retry",
425
+ "subagents.refresh": "Refresh subagents",
426
+ "subagents.interrupt": "Stop",
427
+ "subagents.mode.oneShot": "one-shot",
428
+ "subagents.mode.continuable": "continuable",
429
+ "subagents.diagnostic.corrupt": "This subagent record is damaged",
430
+ "subagents.diagnostic.unsupported": "This subagent record has an unsupported version",
431
+ "subagents.diagnostic.unavailable": "This subagent record is temporarily unreadable",
432
+ "tasks.title": "Background jobs",
433
+ "tasks.count.one": "{count} background job",
434
+ "tasks.count.other": "{count} background jobs",
435
+ "tasks.empty": "No background jobs",
436
+ "tasks.status.running": "running",
437
+ "tasks.status.stopping": "stopping",
438
+ "tasks.status.completed": "completed",
439
+ "tasks.status.killed": "cancelled",
440
+ "tasks.status.failed": "failed",
441
+ "tasks.duration.seconds": "{seconds}s",
442
+ "tasks.duration.minutes": "{minutes}m {seconds}s",
443
+ "tasks.duration.hours": "{hours}h {minutes}m",
444
+ "tasks.duration.live": "Running for {duration}",
445
+ "tasks.duration.done": "Took {duration}",
446
+ "more.expand": "Show more ({count} hidden)",
447
+ "more.collapse": "Show less",
448
+ "graph.toggle": "Switch view",
449
+ "graph.view": "Graph",
450
+ "graph.list": "List",
451
+ "graph.zoomIn": "Zoom in",
452
+ "graph.zoomOut": "Zoom out",
453
+ "graph.fit": "Fit",
454
+ "graph.arrange": "Arrange",
455
+ "graph.mode.tree": "Tree",
456
+ "graph.mode.compact": "Compact",
457
+ "graph.mode.grid": "Grid",
458
+ "graph.resetLayout": "Reset layout",
459
+ "graph.status.running": "running",
460
+ "graph.status.idle": "idle",
461
+ "graph.loading": "Loading…",
462
+ "graph.unphased": "Unphased",
463
+ "graph.badge.main": "Main agent",
464
+ "graph.badge.subagent": "Subagent",
465
+ "graph.badge.done": "Done",
466
+ "graph.badge.standby": "Standby",
467
+ "graph.badge.placeholder": "Subagent",
468
+ "graph.badge.diagnostic": "Unreadable",
469
+ "graph.badge.run": "Workflow",
470
+ "graph.badge.phase": "Phase",
471
+ "graph.badge.member": "Member"
472
+ };
473
+ //#endregion
474
+ //#region lib/types/client/tasks-graph-store.js
475
+ /**
476
+ * The tasks page's graph view state: which form the subagents section draws,
477
+ * which aggregate groups the user expanded, and the canvas's arrangement,
478
+ * manual node offsets, and camera — the state that survives the body's
479
+ * unmounts. One bucket per store instance; the registration is session-scoped,
480
+ * so a tree's framing comes back with its Session.
481
+ */
482
+ /**
483
+ * Declare the graph view's store.
484
+ * @returns the store handle the registration declares.
485
+ */
486
+ function createTasksGraphStore() {
487
+ return (0, _qilin_agent_client_store.defineStore)({
488
+ init: () => ({
489
+ view: "list",
490
+ expanded: {},
491
+ mode: "tree",
492
+ offsets: {},
493
+ camera: void 0
494
+ }),
495
+ actions: {
496
+ /**
497
+ * Switch the subagents section's form.
498
+ * @param d - draft state.
499
+ * @param view - the form to show.
500
+ */
501
+ viewSet: (d, view) => {
502
+ d.view = view;
503
+ },
504
+ /**
505
+ * Expand or collapse one aggregate group. Collapsing removes the key, so
506
+ * the record stays minimal over long sessions.
507
+ * @param d - draft state.
508
+ * @param key - the aggregate's fold key.
509
+ */
510
+ aggregateToggled: (d, key) => {
511
+ if (d.expanded[key] === true) Reflect.deleteProperty(d.expanded, key);
512
+ else d.expanded[key] = true;
513
+ },
514
+ /**
515
+ * Rearrange the canvas, dropping manual offsets and the camera.
516
+ * @param d - draft state.
517
+ * @param mode - the arrangement to apply.
518
+ */
519
+ modeArranged: (d, mode) => {
520
+ d.mode = mode;
521
+ d.offsets = {};
522
+ d.camera = void 0;
523
+ },
524
+ /**
525
+ * Record one node drag's offsets. Offsets equal field-for-field to the
526
+ * ones in force write nothing, so a drag frame that settles cannot loop
527
+ * re-publishing itself.
528
+ * @param d - draft state.
529
+ * @param offsets - the offsets in force after this frame.
530
+ */
531
+ offsetsDragged: (d, offsets) => {
532
+ const held = d.offsets;
533
+ const heldKeys = Object.keys(held);
534
+ const nextKeys = Object.keys(offsets);
535
+ if (heldKeys.length === nextKeys.length && heldKeys.every((key) => held[key]?.x === offsets[key]?.x && held[key]?.y === offsets[key]?.y)) return;
536
+ d.offsets = offsets;
537
+ },
538
+ /**
539
+ * Drop every manual offset.
540
+ * @param d - draft state.
541
+ */
542
+ offsetsReset: (d) => {
543
+ d.offsets = {};
544
+ },
545
+ /**
546
+ * Move the canvas camera; an identical frame writes nothing.
547
+ * @param d - draft state.
548
+ * @param camera - the camera to put in force.
549
+ */
550
+ cameraMoved: (d, camera) => {
551
+ const held = d.camera;
552
+ if (held !== void 0 && held.k === camera.k && held.tx === camera.tx && held.ty === camera.ty && held.userAdjusted === camera.userAdjusted) return;
553
+ d.camera = camera;
554
+ }
555
+ }
556
+ });
557
+ }
558
+ //#endregion
559
+ //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
560
+ function r(e) {
561
+ var t, f, n = "";
562
+ if ("string" == typeof e || "number" == typeof e) n += e;
563
+ else if ("object" == typeof e) if (Array.isArray(e)) {
564
+ var o = e.length;
565
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
566
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
567
+ return n;
568
+ }
569
+ function clsx() {
570
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
571
+ return n;
572
+ }
573
+ //#endregion
574
+ //#region lib/types/client/lineage.js
575
+ /** UI Sidebar Tasks-owned projection of descendant counts from Session summaries. */
576
+ /**
577
+ * Index uninterrupted subagent descendants under each ancestor.
578
+ * @param summaries - Session summaries keyed by id.
579
+ * @returns descendant totals keyed by possible parent id.
580
+ */
581
+ function indexSubagentDescendants(summaries) {
582
+ const indexed = /* @__PURE__ */ new Map();
583
+ for (const descendant of Object.values(summaries)) {
584
+ if (descendant.origin !== "subagent") continue;
585
+ const seen = /* @__PURE__ */ new Set();
586
+ let current = descendant;
587
+ while (current?.origin === "subagent" && current.parentId !== void 0 && !seen.has(current.id)) {
588
+ seen.add(current.id);
589
+ const aggregate = indexed.get(current.parentId);
590
+ if (aggregate === void 0) indexed.set(current.parentId, {
591
+ count: 1,
592
+ runningCount: descendant.running ? 1 : 0
593
+ });
594
+ else {
595
+ aggregate.count += 1;
596
+ if (descendant.running) aggregate.runningCount += 1;
597
+ }
598
+ current = summaries[current.parentId];
599
+ }
600
+ }
601
+ return indexed;
602
+ }
603
+ /**
604
+ * How many summary children one parent has, for placeholders and subtree
605
+ * counts. The summary lineage reaches children the catalogs have not read,
606
+ * which is exactly the count a placeholder stands in for.
607
+ * @param summaries - Session summaries keyed by id.
608
+ * @param parentId - the parent whose children to count.
609
+ * @returns the number of recorded subagent children.
610
+ */
611
+ function summaryChildCount(summaries, parentId) {
612
+ let count = 0;
613
+ for (const summary of Object.values(summaries)) if (summary.origin === "subagent" && summary.parentId === parentId) count += 1;
614
+ return count;
615
+ }
616
+ /**
617
+ * Build the tasks graph view model.
618
+ *
619
+ * A level is one parent's catalog, siblings in catalog order, and the walk
620
+ * descends only into a child whose own catalog has settled; a child the page
621
+ * has never read contributes one placeholder. The seen set makes a repeated
622
+ * or cyclic catalog an ordinary skip rather than an endless walk.
623
+ * @param input - the model's inputs.
624
+ * @returns pre-order nodes and the parent→children index.
625
+ */
626
+ function buildTasksGraphModel(input) {
627
+ const { rootId, catalogs, summaries, expanded, currentSessionId, runs = [], labelOf, secondaryOf } = input;
628
+ const nodes = [];
629
+ const childrenOf = {};
630
+ const seen = /* @__PURE__ */ new Set();
631
+ /**
632
+ * One catalog-backed subagent node with its subtree attached below it.
633
+ *
634
+ * `parentId` is where the node hangs (a phase box re-parents a real child);
635
+ * `navParent` stays the Session whose catalog the entry came from, because
636
+ * the address every navigation and interrupt travels through must name the
637
+ * real durable parent.
638
+ */
639
+ const buildSubtreeNode = (entry, parentId, navParent, depth) => {
640
+ const node = {
641
+ id: entry.id,
642
+ kind: "subagent",
643
+ parentId,
644
+ depth,
645
+ label: labelOf(entry, summaries[entry.id]),
646
+ secondary: secondaryOf(entry),
647
+ running: entry.activity === "running",
648
+ current: entry.id === currentSessionId,
649
+ address: {
650
+ parentSessionId: navParent,
651
+ childSessionId: entry.id,
652
+ mode: entry.mode
653
+ },
654
+ childCount: entry.hasChildren ? summaryChildCount(summaries, entry.id) : void 0,
655
+ aggregateKey: void 0
656
+ };
657
+ seen.add(entry.id);
658
+ nodes.push(node);
659
+ const below = catalogs[entry.id];
660
+ if (entry.hasChildren && below?.state !== "ready") {
661
+ const placeholder = {
662
+ id: `placeholder:${entry.id}`,
663
+ kind: "placeholder",
664
+ parentId: entry.id,
665
+ depth: depth + 1,
666
+ label: "",
667
+ secondary: "",
668
+ running: false,
669
+ current: false,
670
+ address: void 0,
671
+ childCount: summaryChildCount(summaries, entry.id),
672
+ aggregateKey: void 0
673
+ };
674
+ childrenOf[entry.id] = [placeholder];
675
+ nodes.push(placeholder);
676
+ } else if (entry.hasChildren) childrenOf[entry.id] = visit(entry.id, depth + 1);
677
+ return node;
678
+ };
679
+ /** One run node, its phase boxes, and their re-parented or synthesized members. */
680
+ const buildRun = (run, parentId, depth) => {
681
+ const memberCount = run.phases.reduce((sum, phase) => sum + phase.members.length, 0);
682
+ const runNode = {
683
+ id: `run:${run.runId}`,
684
+ kind: "run",
685
+ parentId,
686
+ depth,
687
+ label: run.name,
688
+ secondary: `${memberCount}`,
689
+ running: run.running,
690
+ current: false,
691
+ address: void 0,
692
+ childCount: memberCount,
693
+ aggregateKey: void 0
694
+ };
695
+ nodes.push(runNode);
696
+ const runChildren = [];
697
+ for (const phase of run.phases) {
698
+ const phaseNode = {
699
+ id: `phase:${run.runId}:${phase.phase ?? ""}`,
700
+ kind: "phase",
701
+ parentId: runNode.id,
702
+ depth: depth + 1,
703
+ label: phase.phase ?? "",
704
+ secondary: `${phase.members.length}`,
705
+ running: false,
706
+ current: false,
707
+ address: void 0,
708
+ childCount: phase.members.length,
709
+ aggregateKey: void 0
710
+ };
711
+ nodes.push(phaseNode);
712
+ runChildren.push(phaseNode);
713
+ const phaseChildren = [];
714
+ for (const member of phase.members) {
715
+ const real = catalogs[parentId]?.entries.find((entry) => entry.kind === "child" && entry.id === member.childId);
716
+ if (real !== void 0) {
717
+ if (!seen.has(member.childId)) phaseChildren.push(buildSubtreeNode(real, phaseNode.id, parentId, depth + 2));
718
+ continue;
719
+ }
720
+ const node = {
721
+ id: member.childId,
722
+ kind: "member",
723
+ parentId: phaseNode.id,
724
+ depth: depth + 2,
725
+ label: member.label,
726
+ secondary: member.outcome ?? "",
727
+ running: member.outcome === void 0,
728
+ current: member.childId === currentSessionId,
729
+ address: {
730
+ parentSessionId: parentId,
731
+ childSessionId: member.childId,
732
+ mode: "continuable"
733
+ },
734
+ childCount: void 0,
735
+ aggregateKey: void 0
736
+ };
737
+ nodes.push(node);
738
+ phaseChildren.push(node);
739
+ }
740
+ childrenOf[phaseNode.id] = phaseChildren;
741
+ }
742
+ childrenOf[runNode.id] = runChildren;
743
+ return runNode;
744
+ };
745
+ /** Walk one parent's catalog into that level's child nodes. */
746
+ const visit = (parentSessionId, depth) => {
747
+ const entries = catalogs[parentSessionId]?.entries ?? [];
748
+ const runsHere = runs.filter((run) => run.originSessionId === parentSessionId);
749
+ const claimed = /* @__PURE__ */ new Set();
750
+ for (const run of runsHere) for (const phase of run.phases) for (const member of phase.members) claimed.add(member.childId);
751
+ const live = [];
752
+ const done = [];
753
+ const standby = [];
754
+ for (const entry of entries) {
755
+ if (entry.kind === "diagnostic") {
756
+ live.push(entry);
757
+ continue;
758
+ }
759
+ if (claimed.has(entry.id)) continue;
760
+ if (entry.activity === "running") live.push(entry);
761
+ else if (entry.mode === "continuable") standby.push(entry);
762
+ else done.push(entry);
763
+ }
764
+ const children = [];
765
+ for (const entry of live) {
766
+ if (entry.kind === "diagnostic") {
767
+ const node = {
768
+ id: entry.id,
769
+ kind: "diagnostic",
770
+ parentId: parentSessionId,
771
+ depth,
772
+ label: entry.id,
773
+ secondary: "",
774
+ running: false,
775
+ current: false,
776
+ address: void 0,
777
+ childCount: void 0,
778
+ aggregateKey: void 0
779
+ };
780
+ seen.add(entry.id);
781
+ children.push(node);
782
+ nodes.push(node);
783
+ continue;
784
+ }
785
+ if (seen.has(entry.id)) continue;
786
+ children.push(buildSubtreeNode(entry, parentSessionId, parentSessionId, depth));
787
+ }
788
+ for (const group of [{
789
+ kind: "done-agg",
790
+ entries: done
791
+ }, {
792
+ kind: "standby-agg",
793
+ entries: standby
794
+ }]) {
795
+ const key = `${group.kind}:${parentSessionId}`;
796
+ if (group.entries.length < 6 || expanded.has(key)) {
797
+ for (const entry of group.entries) {
798
+ if (seen.has(entry.id)) continue;
799
+ children.push(buildSubtreeNode(entry, parentSessionId, parentSessionId, depth));
800
+ }
801
+ continue;
802
+ }
803
+ const names = group.entries.slice(0, 2).map((entry) => labelOf(entry, summaries[entry.id]));
804
+ const node = {
805
+ id: key,
806
+ kind: group.kind,
807
+ parentId: parentSessionId,
808
+ depth,
809
+ label: names.join(" · "),
810
+ secondary: `${group.entries.length}`,
811
+ running: false,
812
+ current: false,
813
+ address: void 0,
814
+ childCount: group.entries.length,
815
+ aggregateKey: key
816
+ };
817
+ children.push(node);
818
+ nodes.push(node);
819
+ }
820
+ for (const run of runsHere) children.push(buildRun(run, parentSessionId, depth));
821
+ childrenOf[parentSessionId] = children;
822
+ return children;
823
+ };
824
+ const rootSummary = summaries[rootId];
825
+ const rootNode = {
826
+ id: rootId,
827
+ kind: "main",
828
+ parentId: void 0,
829
+ depth: 0,
830
+ label: rootSummary === void 0 || rootSummary.displayTitle === "" ? rootId : rootSummary.displayTitle,
831
+ secondary: "",
832
+ running: rootSummary?.running === true,
833
+ current: currentSessionId === rootId,
834
+ address: void 0,
835
+ childCount: void 0,
836
+ aggregateKey: void 0
837
+ };
838
+ nodes.push(rootNode);
839
+ seen.add(rootId);
840
+ childrenOf[rootId] = visit(rootId, 1);
841
+ return {
842
+ nodes,
843
+ childrenOf
844
+ };
845
+ }
846
+ /** Per-mode gaps: horizontal between siblings / vertical between depth rows. */
847
+ const MODE_GAPS = {
848
+ tree: {
849
+ h: 36,
850
+ v: 64
851
+ },
852
+ compact: {
853
+ h: 14,
854
+ v: 38
855
+ },
856
+ grid: {
857
+ h: 10,
858
+ v: 26
859
+ }
860
+ };
861
+ /**
862
+ * Lay out the view model.
863
+ * @param model - the view model (pre-order nodes plus the children index).
864
+ * @param offsets - manual node offsets applied after auto placement.
865
+ * @param mode - the arrangement mode.
866
+ * @returns node boxes, edge paths, and the content box.
867
+ */
868
+ function layoutTasksGraph(model, offsets = {}, mode = "tree") {
869
+ const boxes = [];
870
+ const edges = [];
871
+ const boxOf = /* @__PURE__ */ new Map();
872
+ let maxDepth = 0;
873
+ const vGap = MODE_GAPS[mode].v;
874
+ const hGap = MODE_GAPS[mode].h;
875
+ const rowSpan = 66 + vGap;
876
+ /** Place one subtree at `offsetX`; returns the width it occupies. */
877
+ const placeAt = (node, depth, offsetX) => {
878
+ maxDepth = Math.max(maxDepth, depth);
879
+ const kids = model.childrenOf[node.id] ?? [];
880
+ const y = depth * rowSpan;
881
+ if (kids.length === 0) {
882
+ const box = {
883
+ node,
884
+ x: offsetX,
885
+ y,
886
+ w: 208,
887
+ h: 66
888
+ };
889
+ boxes.push(box);
890
+ boxOf.set(node.id, box);
891
+ return 208;
892
+ }
893
+ let childX = offsetX;
894
+ let childEnd = offsetX;
895
+ for (const kid of kids) {
896
+ const w = placeAt(kid, depth + 1, childX);
897
+ childX += w + hGap;
898
+ childEnd = Math.max(childEnd, childX - hGap);
899
+ }
900
+ const subtreeWidth = childEnd - offsetX;
901
+ const box = {
902
+ node,
903
+ x: offsetX + (subtreeWidth - 208) / 2,
904
+ y,
905
+ w: 208,
906
+ h: 66
907
+ };
908
+ boxes.push(box);
909
+ boxOf.set(node.id, box);
910
+ return subtreeWidth;
911
+ };
912
+ /** Pre-measure one subtree's occupied width, for root centering. */
913
+ const measure = (id) => {
914
+ const kids = model.childrenOf[id] ?? [];
915
+ if (kids.length === 0) return 208;
916
+ let w = 0;
917
+ for (const kid of kids) w += measure(kid.id) + hGap;
918
+ return Math.max(208, w - hGap);
919
+ };
920
+ const rootNode = model.nodes[0];
921
+ let width = 208;
922
+ if (mode === "grid") {
923
+ const byDepth = /* @__PURE__ */ new Map();
924
+ for (const node of model.nodes) {
925
+ const row = byDepth.get(node.depth);
926
+ if (row === void 0) byDepth.set(node.depth, [node]);
927
+ else row.push(node);
928
+ }
929
+ let cursorY = 0;
930
+ for (const depth of [...byDepth.keys()].sort((left, right) => left - right)) {
931
+ /* v8 ignore next -- every keyed depth has a row. */
932
+ const row = byDepth.get(depth) ?? [];
933
+ const cols = Math.min(6, row.length);
934
+ const rowWidth = cols * 208 + (cols - 1) * hGap;
935
+ row.forEach((node, index) => {
936
+ const col = index % cols;
937
+ const line = Math.floor(index / cols);
938
+ const box = {
939
+ node,
940
+ x: col * (208 + hGap),
941
+ y: cursorY + line * rowSpan,
942
+ w: 208,
943
+ h: 66
944
+ };
945
+ boxes.push(box);
946
+ boxOf.set(node.id, box);
947
+ });
948
+ width = Math.max(width, rowWidth);
949
+ cursorY += Math.ceil(row.length / cols) * rowSpan;
950
+ maxDepth = Math.max(maxDepth, depth);
951
+ }
952
+ } else if (rootNode !== void 0) {
953
+ const roots = model.childrenOf[rootNode.id] ?? [];
954
+ let span = 0;
955
+ for (const kid of roots) span += measure(kid.id) + hGap;
956
+ span = Math.max(span - hGap, 208);
957
+ placeAt(rootNode, 0, 0);
958
+ width = span + hGap * 2;
959
+ }
960
+ let minX = 0;
961
+ let minY = 0;
962
+ let maxX = 0;
963
+ let maxY = 0;
964
+ const placed = boxes.map((box) => {
965
+ const offset = offsets[box.node.id];
966
+ if (offset === void 0) return box;
967
+ return {
968
+ ...box,
969
+ x: box.x + offset.x,
970
+ y: box.y + offset.y
971
+ };
972
+ });
973
+ for (const box of placed) {
974
+ boxOf.set(box.node.id, box);
975
+ minX = Math.min(minX, box.x);
976
+ minY = Math.min(minY, box.y);
977
+ maxX = Math.max(maxX, box.x + box.w);
978
+ maxY = Math.max(maxY, box.y + box.h);
979
+ }
980
+ for (const box of placed) for (const kid of model.childrenOf[box.node.id] ?? []) {
981
+ const child = boxOf.get(kid.id);
982
+ /* v8 ignore next -- placement places every child before this walk. */
983
+ if (child === void 0) continue;
984
+ const x1 = box.x + box.w / 2;
985
+ const y1 = box.y + box.h;
986
+ const x2 = child.x + child.w / 2;
987
+ const y2 = child.y;
988
+ const bend = Math.max(vGap / 2, 18);
989
+ edges.push({
990
+ id: `${box.node.id}->${kid.id}`,
991
+ d: `M ${x1} ${y1} C ${x1} ${y1 + bend}, ${x2} ${y2 - bend}, ${x2} ${y2}`,
992
+ to: kid.id
993
+ });
994
+ }
995
+ const height = Math.max(maxY, (maxDepth + 1) * rowSpan);
996
+ return {
997
+ nodes: placed,
998
+ edges,
999
+ width: Math.max(width, maxX),
1000
+ height,
1001
+ minX,
1002
+ minY
1003
+ };
1004
+ }
1005
+ /**
1006
+ * Every id in one node's subtree, the node itself first.
1007
+ * @param model - the view model, for the children graph.
1008
+ * @param nodeId - the subtree root.
1009
+ * @returns the subtree ids in depth-first order.
1010
+ */
1011
+ function subtreeIds(model, nodeId) {
1012
+ const ids = [];
1013
+ const walk = (id) => {
1014
+ ids.push(id);
1015
+ for (const kid of model.childrenOf[id] ?? []) walk(kid.id);
1016
+ };
1017
+ walk(nodeId);
1018
+ return ids;
1019
+ }
1020
+ /**
1021
+ * The offsets after dragging one node by (dx, dy).
1022
+ *
1023
+ * Always computed from the offsets captured when the gesture started plus the
1024
+ * total delta, so a long drag cannot accumulate rounding drift. With
1025
+ * `subtree` the descendants ride along — the default, since a card that owns
1026
+ * other cards moves them naturally; the Alt key drags the single card.
1027
+ * @param model - the view model, for the children graph.
1028
+ * @param base - the offsets captured at gesture start.
1029
+ * @param nodeId - the dragged node.
1030
+ * @param dx - the total horizontal delta.
1031
+ * @param dy - the total vertical delta.
1032
+ * @param subtree - whether the node's descendants move too.
1033
+ * @returns the next offsets.
1034
+ */
1035
+ function dragOffsets(model, base, nodeId, dx, dy, subtree) {
1036
+ const next = { ...base };
1037
+ for (const id of subtree ? subtreeIds(model, nodeId) : [nodeId]) {
1038
+ const current = base[id] ?? {
1039
+ x: 0,
1040
+ y: 0
1041
+ };
1042
+ next[id] = {
1043
+ x: current.x + dx,
1044
+ y: current.y + dy
1045
+ };
1046
+ }
1047
+ return next;
1048
+ }
1049
+ /**
1050
+ * Whether any manual offset is in effect; drives the reset affordance.
1051
+ * @param offsets - the offsets in force.
1052
+ * @returns whether at least one node is displaced.
1053
+ */
1054
+ function hasOffsets(offsets) {
1055
+ return Object.keys(offsets).length > 0;
1056
+ }
1057
+ //#endregion
1058
+ //#region \0qilin-css:/home/runner/work/QiLin/QiLin/packages/client/ui-sidebar-tasks/src/client/TasksGraph.module.css.mjs
1059
+ const css$1 = ".IZCJ0W_wrap{cursor:grab;touch-action:none;border:.5px solid var(--qilin-alias-border-l3);border-radius:10px;flex:none;height:clamp(280px,60vh,520px);margin:0 10px;position:relative;overflow:hidden}.IZCJ0W_wrap:active{cursor:grabbing}.IZCJ0W_svg{user-select:none;width:100%;height:100%;display:block}.IZCJ0W_edge{fill:none;stroke:var(--qilin-alias-border-l1);stroke-width:1.5px;opacity:.9}.IZCJ0W_card{fill:#0000;stroke:var(--qilin-alias-border-l2);stroke-width:1px}.IZCJ0W_sep{stroke:var(--qilin-alias-border-l2);stroke-width:1px}.IZCJ0W_bar{fill:var(--qilin-alias-interactive-bg-hover)}.IZCJ0W_badge{fill:var(--qilin-alias-label-tertiary);letter-spacing:.04em;font-size:9px}.IZCJ0W_label{fill:var(--qilin-alias-label-primary);font-size:12px}.IZCJ0W_status{fill:var(--qilin-alias-label-secondary);font-size:10px}.IZCJ0W_dotRunning{fill:var(--qilin-alias-interactive-bg-hover-accent)}.IZCJ0W_dotIdle{fill:var(--qilin-alias-label-tertiary)}.IZCJ0W_nodeCurrent .IZCJ0W_card{stroke:var(--qilin-alias-interactive-bg-hover-accent);stroke-width:1.6px}.IZCJ0W_nodeDone .IZCJ0W_sep,.IZCJ0W_nodeDone .IZCJ0W_bar{opacity:.4}.IZCJ0W_nodeStandby .IZCJ0W_card{stroke-dasharray:2 2;opacity:.75}.IZCJ0W_nodeStandby .IZCJ0W_bar{opacity:.5}.IZCJ0W_nodePlaceholder .IZCJ0W_card{stroke-dasharray:4 3}.IZCJ0W_nodeRun .IZCJ0W_card{stroke-width:1.4px}.IZCJ0W_nodePhase .IZCJ0W_card{stroke-dasharray:3 2}.IZCJ0W_node{cursor:grab}.IZCJ0W_nodeDragging{cursor:grabbing}.IZCJ0W_nodeDragging .IZCJ0W_card{stroke:var(--qilin-alias-interactive-bg-hover-accent);stroke-width:1.6px}.IZCJ0W_controls{align-items:center;gap:4px;display:flex;position:absolute;bottom:8px;right:8px}.IZCJ0W_controls button{min-width:26px;height:24px;color:var(--qilin-alias-label-secondary);cursor:pointer;border:.5px solid var(--qilin-alias-border-l2);background:0 0;border-radius:6px;padding:0 8px;font-size:11px}.IZCJ0W_controls button:hover{color:var(--qilin-alias-label-primary);background:var(--qilin-alias-interactive-bg-hover)}.IZCJ0W_modes{background:var(--qilin-alias-interactive-bg-hover);border-radius:8px;gap:2px;padding:2px;display:flex}.IZCJ0W_mode{min-width:0;color:var(--qilin-alias-label-secondary);font:var(--qilin-font-xxxs-11);cursor:pointer;background:0 0;border:none;border-radius:6px;padding:2px 6px;line-height:14px}.IZCJ0W_mode:hover{color:var(--qilin-alias-label-primary)}.IZCJ0W_modeActive{color:var(--qilin-alias-label-primary);font:var(--qilin-font-xxxs-strong-11);background:var(--qilin-alias-bg-layer-2,var(--qilin-alias-bg-base))}.IZCJ0W_reset{min-width:0;color:var(--qilin-alias-label-secondary);font:var(--qilin-font-xxxs-11);cursor:pointer;background:0 0;border:none;border-radius:6px;padding:2px 6px;line-height:14px}.IZCJ0W_reset:hover{color:var(--qilin-alias-label-primary);background:var(--qilin-alias-interactive-bg-hover)}";
1060
+ const tagId$1 = "@qilin-agent/client-ui-sidebar-tasks/TasksGraph.module.css";
1061
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
1062
+ const tag = document.createElement("style");
1063
+ tag.dataset.plugin = "@qilin-agent/client-ui-sidebar-tasks";
1064
+ tag.dataset.pluginCss = tagId$1;
1065
+ tag.textContent = css$1;
1066
+ document.head.appendChild(tag);
1067
+ }
1068
+ var TasksGraph_module_css_default = {
1069
+ "badge": "IZCJ0W_badge",
1070
+ "bar": "IZCJ0W_bar",
1071
+ "card": "IZCJ0W_card",
1072
+ "controls": "IZCJ0W_controls",
1073
+ "dotIdle": "IZCJ0W_dotIdle",
1074
+ "dotRunning": "IZCJ0W_dotRunning",
1075
+ "edge": "IZCJ0W_edge",
1076
+ "label": "IZCJ0W_label",
1077
+ "mode": "IZCJ0W_mode",
1078
+ "modeActive": "IZCJ0W_modeActive",
1079
+ "modes": "IZCJ0W_modes",
1080
+ "node": "IZCJ0W_node",
1081
+ "nodeCurrent": "IZCJ0W_nodeCurrent",
1082
+ "nodeDone": "IZCJ0W_nodeDone",
1083
+ "nodeDragging": "IZCJ0W_nodeDragging",
1084
+ "nodePhase": "IZCJ0W_nodePhase",
1085
+ "nodePlaceholder": "IZCJ0W_nodePlaceholder",
1086
+ "nodeRun": "IZCJ0W_nodeRun",
1087
+ "nodeStandby": "IZCJ0W_nodeStandby",
1088
+ "reset": "IZCJ0W_reset",
1089
+ "sep": "IZCJ0W_sep",
1090
+ "status": "IZCJ0W_status",
1091
+ "svg": "IZCJ0W_svg",
1092
+ "wrap": "IZCJ0W_wrap"
1093
+ };
1094
+ //#endregion
1095
+ //#region lib/types/client/TasksGraphView.js
1096
+ /**
1097
+ * The tasks graph canvas: layered node cards joined by bezier edges, drag to
1098
+ * pan, wheel zoom-to-cursor, double-click to fit, node cards that drag off
1099
+ * their auto-layout spots, and a bottom-right control cluster (zoom, arrange,
1100
+ * fit, reset).
1101
+ *
1102
+ * Pure presentation over the view model: every shared fact — the section's
1103
+ * form, the expanded aggregates, the arrangement, the manual offsets, the
1104
+ * camera — lives in the declared graph store and reaches this component
1105
+ * through its `useStore` / `actions` shares. Gesture-internal records are
1106
+ * component-local refs.
1107
+ *
1108
+ * Interaction notes:
1109
+ * - a pan or node drag that travelled past {@link CLICK_SLOP} is a drag, not a
1110
+ * click: the node's click handler reads the gesture records and skips
1111
+ * navigation for a moved pointer;
1112
+ * - wheel zoom keeps the canvas point under the cursor stationary and needs a
1113
+ * non-passive native listener, attached through the wrap ref;
1114
+ * - the auto-fit frames the content until the user pans or zooms; an arrange
1115
+ * hands steering back by clearing the camera.
1116
+ */
1117
+ /** Drag distance under which a pointer sequence still counts as a click, in px. */
1118
+ const CLICK_SLOP = 4;
1119
+ /** Zoom clamps. */
1120
+ const K_MIN = .2;
1121
+ const K_MAX = 2.5;
1122
+ /** The widest zoom a fit frames at; deeper zoom is the user's choice. */
1123
+ const FIT_K_MAX = 1.25;
1124
+ /** The camera the canvas starts from, before the first fit. */
1125
+ const START_CAMERA = {
1126
+ k: 1,
1127
+ tx: 0,
1128
+ ty: 0,
1129
+ userAdjusted: false
1130
+ };
1131
+ /**
1132
+ * The badge word one node kind carries.
1133
+ * @param node - the node the card draws.
1134
+ * @param t - namespace-bound translate.
1135
+ * @returns the badge word.
1136
+ */
1137
+ function badgeOf(node, t) {
1138
+ switch (node.kind) {
1139
+ case "main": return t("graph.badge.main");
1140
+ case "subagent": return t("graph.badge.subagent");
1141
+ case "done-agg": return t("graph.badge.done");
1142
+ case "standby-agg": return t("graph.badge.standby");
1143
+ case "placeholder": return t("graph.badge.placeholder");
1144
+ case "diagnostic": return t("graph.badge.diagnostic");
1145
+ case "run": return t("graph.badge.run");
1146
+ case "phase": return t("graph.badge.phase");
1147
+ case "member": return t("graph.badge.member");
1148
+ }
1149
+ }
1150
+ /**
1151
+ * The label one card draws, with the synthesized stand-ins for unread and
1152
+ * unphased nodes.
1153
+ * @param node - the node the card draws.
1154
+ * @param t - namespace-bound translate.
1155
+ * @returns the label text.
1156
+ */
1157
+ function labelOf(node, t) {
1158
+ if (node.kind === "phase" && node.label === "") return t("graph.unphased");
1159
+ return node.label === "" ? t("graph.loading") : node.label;
1160
+ }
1161
+ /**
1162
+ * The status line one card draws in its bottom bar.
1163
+ * @param node - the node the card draws.
1164
+ * @param t - namespace-bound translate.
1165
+ * @returns the status text.
1166
+ */
1167
+ function statusOf(node, t) {
1168
+ switch (node.kind) {
1169
+ case "done-agg":
1170
+ case "standby-agg": return `${badgeOf(node, t)} ${node.childCount ?? ""}`.trim();
1171
+ case "placeholder": return t("graph.loading");
1172
+ case "phase": return `${node.childCount ?? 0}`;
1173
+ case "member":
1174
+ if (node.running) return t("graph.status.running");
1175
+ return node.secondary !== "" ? node.secondary : t("graph.badge.member");
1176
+ case "main":
1177
+ case "subagent":
1178
+ case "run":
1179
+ case "diagnostic": return node.running ? t("graph.status.running") : t("graph.status.idle");
1180
+ }
1181
+ }
1182
+ /**
1183
+ * Truncate a display string to roughly fit one card line.
1184
+ * @param text - the full text.
1185
+ * @param max - the character budget.
1186
+ * @returns the elided text.
1187
+ */
1188
+ function ellipsize(text, max) {
1189
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
1190
+ }
1191
+ /** The CSS class table indexes per key, so a class read may miss. */
1192
+ function kindClassOf(node) {
1193
+ switch (node.kind) {
1194
+ case "done-agg": return TasksGraph_module_css_default.nodeDone;
1195
+ case "standby-agg": return TasksGraph_module_css_default.nodeStandby;
1196
+ case "placeholder": return TasksGraph_module_css_default.nodePlaceholder;
1197
+ case "phase": return TasksGraph_module_css_default.nodePhase;
1198
+ case "run": return TasksGraph_module_css_default.nodeRun;
1199
+ default: return TasksGraph_module_css_default.node;
1200
+ }
1201
+ }
1202
+ /**
1203
+ * Whether a node activates: subagent cards and synthesized members navigate,
1204
+ * aggregates toggle their fold, the main card opens the root, and a
1205
+ * placeholder asks the page to read its level. Diagnostics, runs, and phases
1206
+ * draw but do not activate.
1207
+ * @param node - the node the card draws.
1208
+ * @returns whether the card activates on click or keyboard.
1209
+ */
1210
+ function clickableOf(node) {
1211
+ return node.kind === "main" || node.kind === "subagent" || node.kind === "member" || node.kind === "placeholder" || node.aggregateKey !== void 0;
1212
+ }
1213
+ /**
1214
+ * The tasks graph canvas.
1215
+ * @param props - the model, the node gesture, the copy, and the store shares.
1216
+ * @returns the canvas and its control cluster.
1217
+ */
1218
+ function TasksGraphView({ model, onNodeClick, useStore, actions, t }) {
1219
+ const wrapRef = (0, react.useRef)(null);
1220
+ const camera = useStore((state) => state.camera);
1221
+ const cameraRef = (0, react.useRef)(camera);
1222
+ cameraRef.current = camera;
1223
+ const offsets = useStore((state) => state.offsets);
1224
+ const mode = useStore((state) => state.mode);
1225
+ const [panning, setPanning] = (0, react.useState)(false);
1226
+ const [draggingNode, setDraggingNode] = (0, react.useState)(null);
1227
+ const panRef = (0, react.useRef)(null);
1228
+ const nodeDragRef = (0, react.useRef)(null);
1229
+ const layout = (0, react.useMemo)(() => layoutTasksGraph(model, offsets, mode), [
1230
+ model,
1231
+ offsets,
1232
+ mode
1233
+ ]);
1234
+ const view = camera ?? START_CAMERA;
1235
+ const userAdjusted = camera?.userAdjusted ?? false;
1236
+ /** Put one camera frame in force, ahead of the render and in the store. */
1237
+ const moveCamera = (0, react.useCallback)((next, adjusted) => {
1238
+ const frame = {
1239
+ ...next,
1240
+ userAdjusted: adjusted
1241
+ };
1242
+ cameraRef.current = frame;
1243
+ actions.cameraMoved(frame);
1244
+ }, [actions]);
1245
+ /** Center the content box in the viewport at a readable zoom. */
1246
+ const fit = (0, react.useCallback)(() => {
1247
+ const el = wrapRef.current;
1248
+ /* v8 ignore next -- fit runs only from events a mounted canvas raised. */
1249
+ const vw = el?.clientWidth ?? 0;
1250
+ /* v8 ignore next -- fit runs only from events a mounted canvas raised. */
1251
+ const vh = el?.clientHeight ?? 0;
1252
+ if (vw <= 0 || vh <= 0) return;
1253
+ const k = Math.min(FIT_K_MAX, Math.max(K_MIN, Math.min(vw / layout.width, vh / layout.height)));
1254
+ moveCamera({
1255
+ k,
1256
+ tx: (vw - layout.width * k) / 2 - layout.minX * k,
1257
+ ty: Math.max(8, (vh - layout.height * k) / 2) - layout.minY * k
1258
+ }, false);
1259
+ }, [layout, moveCamera]);
1260
+ (0, react.useEffect)(() => {
1261
+ if (userAdjusted) return;
1262
+ fit();
1263
+ }, [fit, userAdjusted]);
1264
+ (0, react.useEffect)(() => {
1265
+ const el = wrapRef.current;
1266
+ /* v8 ignore next -- the wrap ref is set before effects run. */
1267
+ if (el === null) return;
1268
+ /* v8 ignore next -- a real browser defines ResizeObserver. */
1269
+ if (typeof ResizeObserver === "undefined") return;
1270
+ const observer = new ResizeObserver(() => {
1271
+ if (!userAdjusted && panRef.current === null) fit();
1272
+ });
1273
+ observer.observe(el);
1274
+ return () => {
1275
+ observer.disconnect();
1276
+ };
1277
+ }, [fit, userAdjusted]);
1278
+ (0, react.useEffect)(() => {
1279
+ const el = wrapRef.current;
1280
+ /* v8 ignore next -- the wrap ref is set before effects run. */
1281
+ if (el === null) return;
1282
+ const onWheel = (event) => {
1283
+ event.preventDefault();
1284
+ const current = cameraRef.current ?? START_CAMERA;
1285
+ const k = Math.min(K_MAX, Math.max(K_MIN, current.k * Math.exp(-event.deltaY * .0015)));
1286
+ const rect = el.getBoundingClientRect();
1287
+ const px = event.clientX - rect.left;
1288
+ const py = event.clientY - rect.top;
1289
+ moveCamera({
1290
+ k,
1291
+ tx: px - (px - current.tx) / current.k * k,
1292
+ ty: py - (py - current.ty) / current.k * k
1293
+ }, true);
1294
+ };
1295
+ el.addEventListener("wheel", onWheel, { passive: false });
1296
+ return () => {
1297
+ el.removeEventListener("wheel", onWheel);
1298
+ };
1299
+ }, [moveCamera]);
1300
+ const onPointerDown = (0, react.useCallback)((event) => {
1301
+ if (event.button !== 0) return;
1302
+ panRef.current = {
1303
+ x: event.clientX,
1304
+ y: event.clientY,
1305
+ moved: 0
1306
+ };
1307
+ setPanning(true);
1308
+ }, []);
1309
+ (0, react.useEffect)(() => {
1310
+ if (!panning) return;
1311
+ const onMove = (event) => {
1312
+ const drag = panRef.current;
1313
+ /* v8 ignore next -- the effect subscribes only while the record exists. */
1314
+ if (drag === null) return;
1315
+ const dx = event.clientX - drag.x;
1316
+ const dy = event.clientY - drag.y;
1317
+ drag.moved += Math.abs(dx) + Math.abs(dy);
1318
+ drag.x = event.clientX;
1319
+ drag.y = event.clientY;
1320
+ if (drag.moved <= CLICK_SLOP) return;
1321
+ const current = cameraRef.current ?? START_CAMERA;
1322
+ moveCamera({
1323
+ k: current.k,
1324
+ tx: current.tx + dx,
1325
+ ty: current.ty + dy
1326
+ }, true);
1327
+ };
1328
+ const onUp = () => {
1329
+ setPanning(false);
1330
+ };
1331
+ window.addEventListener("pointermove", onMove);
1332
+ window.addEventListener("pointerup", onUp);
1333
+ window.addEventListener("pointercancel", onUp);
1334
+ return () => {
1335
+ window.removeEventListener("pointermove", onMove);
1336
+ window.removeEventListener("pointerup", onUp);
1337
+ window.removeEventListener("pointercancel", onUp);
1338
+ };
1339
+ }, [panning, moveCamera]);
1340
+ (0, react.useEffect)(() => {
1341
+ if (draggingNode === null) return;
1342
+ const onMove = (event) => {
1343
+ const drag = nodeDragRef.current;
1344
+ /* v8 ignore next -- the effect subscribes only while the record exists. */
1345
+ if (drag === null) return;
1346
+ const dx = event.clientX - drag.x;
1347
+ const dy = event.clientY - drag.y;
1348
+ drag.moved = Math.abs(dx) + Math.abs(dy);
1349
+ if (drag.moved < CLICK_SLOP) return;
1350
+ const k = (cameraRef.current ?? START_CAMERA).k;
1351
+ actions.offsetsDragged(dragOffsets(model, drag.base, drag.nodeId, dx / k, dy / k, drag.subtree));
1352
+ };
1353
+ const onUp = () => {
1354
+ setDraggingNode(null);
1355
+ };
1356
+ window.addEventListener("pointermove", onMove);
1357
+ window.addEventListener("pointerup", onUp);
1358
+ window.addEventListener("pointercancel", onUp);
1359
+ return () => {
1360
+ window.removeEventListener("pointermove", onMove);
1361
+ window.removeEventListener("pointerup", onUp);
1362
+ window.removeEventListener("pointercancel", onUp);
1363
+ };
1364
+ }, [
1365
+ draggingNode,
1366
+ model,
1367
+ actions
1368
+ ]);
1369
+ const startNodeDrag = (0, react.useCallback)((event, nodeId) => {
1370
+ if (event.button !== 0) return;
1371
+ event.stopPropagation();
1372
+ nodeDragRef.current = {
1373
+ nodeId,
1374
+ x: event.clientX,
1375
+ y: event.clientY,
1376
+ moved: 0,
1377
+ base: offsets,
1378
+ subtree: !event.altKey
1379
+ };
1380
+ setDraggingNode(nodeId);
1381
+ }, [offsets]);
1382
+ /** Zoom by one step around the viewport's center. */
1383
+ const zoomBy = (0, react.useCallback)((factor) => {
1384
+ const el = wrapRef.current;
1385
+ /* v8 ignore next -- a control click implies a mounted canvas. */
1386
+ const cx = (el?.clientWidth ?? 0) / 2;
1387
+ /* v8 ignore next -- a control click implies a mounted canvas. */
1388
+ const cy = (el?.clientHeight ?? 0) / 2;
1389
+ const current = cameraRef.current ?? START_CAMERA;
1390
+ const k = Math.min(K_MAX, Math.max(K_MIN, current.k * factor));
1391
+ moveCamera({
1392
+ k,
1393
+ tx: cx - (cx - current.tx) / current.k * k,
1394
+ ty: cy - (cy - current.ty) / current.k * k
1395
+ }, true);
1396
+ }, [moveCamera]);
1397
+ const arrange = (0, react.useCallback)((next) => {
1398
+ actions.modeArranged(next);
1399
+ }, [actions]);
1400
+ const resetOffsets = (0, react.useCallback)(() => {
1401
+ actions.offsetsReset();
1402
+ }, [actions]);
1403
+ return (0, react_jsx_runtime.jsxs)("div", {
1404
+ ref: wrapRef,
1405
+ className: TasksGraph_module_css_default.wrap,
1406
+ "data-tasks-graph": "",
1407
+ onPointerDown,
1408
+ onDoubleClick: fit,
1409
+ children: [(0, react_jsx_runtime.jsx)("svg", {
1410
+ className: TasksGraph_module_css_default.svg,
1411
+ children: (0, react_jsx_runtime.jsxs)("g", {
1412
+ "data-tasks-graph-frame": "",
1413
+ transform: `translate(${view.tx} ${view.ty}) scale(${view.k})`,
1414
+ children: [layout.edges.map((edge) => (0, react_jsx_runtime.jsx)("path", {
1415
+ d: edge.d,
1416
+ className: TasksGraph_module_css_default.edge
1417
+ }, edge.id)), layout.nodes.map((box) => {
1418
+ const node = box.node;
1419
+ const clickable = clickableOf(node);
1420
+ const name = `${node.label} ${node.secondary}`.trim();
1421
+ return (0, react_jsx_runtime.jsxs)("g", {
1422
+ "data-tasks-graph-node": node.id,
1423
+ transform: `translate(${box.x} ${box.y})`,
1424
+ className: clsx(kindClassOf(node), draggingNode === node.id && TasksGraph_module_css_default.nodeDragging),
1425
+ onPointerDown: (event) => {
1426
+ startNodeDrag(event, node.id);
1427
+ },
1428
+ onClick: () => {
1429
+ const panned = panRef.current?.moved ?? 0;
1430
+ const dragged = nodeDragRef.current?.moved ?? 0;
1431
+ panRef.current = null;
1432
+ nodeDragRef.current = null;
1433
+ if (panned >= CLICK_SLOP || dragged >= CLICK_SLOP) return;
1434
+ if (clickable) onNodeClick(node);
1435
+ },
1436
+ onKeyDown: clickable ? (event) => {
1437
+ if (event.key !== "Enter" && event.key !== " ") return;
1438
+ event.preventDefault();
1439
+ onNodeClick(node);
1440
+ } : void 0,
1441
+ role: clickable ? "button" : void 0,
1442
+ tabIndex: clickable ? 0 : void 0,
1443
+ "aria-label": name,
1444
+ "aria-current": node.current ? "true" : void 0,
1445
+ "aria-disabled": !clickable ? "true" : void 0,
1446
+ children: [
1447
+ (0, react_jsx_runtime.jsx)("rect", {
1448
+ width: 208,
1449
+ height: 66,
1450
+ rx: 8,
1451
+ className: TasksGraph_module_css_default.card
1452
+ }),
1453
+ (0, react_jsx_runtime.jsx)("line", {
1454
+ x1: 0,
1455
+ y1: 46,
1456
+ x2: 208,
1457
+ y2: 46,
1458
+ className: TasksGraph_module_css_default.sep
1459
+ }),
1460
+ (0, react_jsx_runtime.jsx)("text", {
1461
+ x: 10,
1462
+ y: 15,
1463
+ className: TasksGraph_module_css_default.badge,
1464
+ children: badgeOf(node, t)
1465
+ }),
1466
+ (0, react_jsx_runtime.jsxs)("text", {
1467
+ x: 10,
1468
+ y: 32,
1469
+ className: TasksGraph_module_css_default.label,
1470
+ children: [ellipsize(labelOf(node, t), 24), node.childCount !== void 0 ? ` +${node.childCount}` : ""]
1471
+ }),
1472
+ (0, react_jsx_runtime.jsx)("rect", {
1473
+ y: 46,
1474
+ width: 208,
1475
+ height: 20,
1476
+ className: TasksGraph_module_css_default.bar
1477
+ }),
1478
+ (0, react_jsx_runtime.jsx)("circle", {
1479
+ cx: 12,
1480
+ cy: 56,
1481
+ r: 3,
1482
+ className: node.running ? TasksGraph_module_css_default.dotRunning : TasksGraph_module_css_default.dotIdle
1483
+ }),
1484
+ (0, react_jsx_runtime.jsx)("text", {
1485
+ x: 20,
1486
+ y: 59.5,
1487
+ className: TasksGraph_module_css_default.status,
1488
+ children: statusOf(node, t)
1489
+ }),
1490
+ (0, react_jsx_runtime.jsx)("title", { children: name })
1491
+ ]
1492
+ }, node.id);
1493
+ })]
1494
+ })
1495
+ }), (0, react_jsx_runtime.jsxs)("div", {
1496
+ className: TasksGraph_module_css_default.controls,
1497
+ "data-tasks-graph-controls": "",
1498
+ onPointerDown: (event) => {
1499
+ event.stopPropagation();
1500
+ },
1501
+ children: [
1502
+ (0, react_jsx_runtime.jsx)("button", {
1503
+ type: "button",
1504
+ "aria-label": t("graph.zoomIn"),
1505
+ onClick: () => {
1506
+ zoomBy(1.25);
1507
+ },
1508
+ children: "+"
1509
+ }),
1510
+ (0, react_jsx_runtime.jsx)("button", {
1511
+ type: "button",
1512
+ "aria-label": t("graph.zoomOut"),
1513
+ onClick: () => {
1514
+ zoomBy(.8);
1515
+ },
1516
+ children: "-"
1517
+ }),
1518
+ (0, react_jsx_runtime.jsx)("span", {
1519
+ className: TasksGraph_module_css_default.modes,
1520
+ role: "group",
1521
+ "aria-label": t("graph.arrange"),
1522
+ children: [
1523
+ "tree",
1524
+ "compact",
1525
+ "grid"
1526
+ ].map((value) => (0, react_jsx_runtime.jsx)("button", {
1527
+ type: "button",
1528
+ "data-tasks-graph-mode": value,
1529
+ "aria-pressed": mode === value,
1530
+ className: clsx(TasksGraph_module_css_default.mode, mode === value && TasksGraph_module_css_default.modeActive),
1531
+ title: t("graph.arrange"),
1532
+ onClick: () => {
1533
+ arrange(value);
1534
+ },
1535
+ children: t(`graph.mode.${value}`)
1536
+ }, value))
1537
+ }),
1538
+ (0, react_jsx_runtime.jsx)("button", {
1539
+ type: "button",
1540
+ "aria-label": t("graph.fit"),
1541
+ onClick: fit,
1542
+ children: t("graph.fit")
1543
+ }),
1544
+ hasOffsets(offsets) && (0, react_jsx_runtime.jsxs)("button", {
1545
+ type: "button",
1546
+ className: TasksGraph_module_css_default.reset,
1547
+ "aria-label": t("graph.resetLayout"),
1548
+ title: t("graph.resetLayout"),
1549
+ "data-tasks-graph-reset": "",
1550
+ onClick: resetOffsets,
1551
+ children: ["⟲ ", t("graph.resetLayout")]
1552
+ })
1553
+ ]
1554
+ })]
1555
+ });
1556
+ }
1557
+ //#endregion
1558
+ //#region \0qilin-css:/home/runner/work/QiLin/QiLin/packages/client/ui-sidebar-tasks/src/client/TasksBody.module.css.mjs
1559
+ const css = ".vdi1YG_root{height:100%;min-height:0;color:var(--qilin-alias-label-primary);font-size:var(--qilin-content-font-size-secondary,13px);scrollbar-gutter:stable;flex-direction:column;flex:auto;line-height:1.5;display:flex;overflow:auto}.vdi1YG_sectionHeader{box-sizing:border-box;border-bottom:.5px solid var(--qilin-alias-border-l3);flex:none;align-items:center;gap:4px;height:38px;padding:0 6px 0 10px;display:flex}.vdi1YG_sectionToggle{min-width:0;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:8px;flex:auto;align-items:center;gap:6px;padding:4px 6px;display:flex}.vdi1YG_sectionToggle:hover{background:var(--qilin-alias-interactive-bg-hover)}.vdi1YG_chevron{color:var(--qilin-alias-label-tertiary);flex:none}.vdi1YG_sectionTitle{flex:none;font-weight:500}.vdi1YG_sectionCount{min-width:0;color:var(--qilin-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;flex:auto;font-size:12px;overflow:hidden}.vdi1YG_viewToggle{border:.5px solid var(--qilin-alias-border-l2);border-radius:6px;flex:none;display:inline-flex;overflow:hidden}.vdi1YG_viewToggle button{color:var(--qilin-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:2px 8px;font-size:11px;line-height:16px}.vdi1YG_viewToggle button[aria-pressed=true]{color:var(--qilin-alias-label-primary);background:var(--qilin-alias-interactive-bg-hover)}.vdi1YG_sectionAction{width:28px;height:28px;color:var(--qilin-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:28px;flex:none;justify-content:center;align-items:center;padding:6px;line-height:1;display:inline-flex}.vdi1YG_sectionAction svg{width:15px;height:15px}.vdi1YG_sectionAction:hover{color:var(--qilin-alias-label-primary);background:var(--qilin-alias-interactive-bg-hover)}.vdi1YG_sectionBody{flex-direction:column;flex:none;gap:2px;padding:6px 0 10px;display:flex}.vdi1YG_list{margin:0;padding:0;list-style:none}.vdi1YG_row{min-width:0;color:var(--qilin-alias-label-primary);align-items:center;gap:6px;padding-right:10px;display:flex}.vdi1YG_settled{color:var(--qilin-alias-label-tertiary)}.vdi1YG_disabled{color:var(--qilin-alias-label-tertiary);padding-left:10px}.vdi1YG_open{min-width:0;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:10px;flex:auto;align-items:center;gap:6px;padding:5px 10px;display:flex}.vdi1YG_open:hover{background:var(--qilin-alias-interactive-bg-hover)}.vdi1YG_action{color:var(--qilin-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;padding:3px 8px;font-size:12px;line-height:18px}.vdi1YG_action:hover{color:var(--qilin-alias-label-primary);background:var(--qilin-alias-interactive-bg-hover)}.vdi1YG_dot{flex:none}.vdi1YG_kind{background:var(--qilin-alias-fill-l2);color:var(--qilin-alias-label-secondary);border-radius:5px;flex:none;padding:0 6px;font-size:11px;line-height:18px}.vdi1YG_label{white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.vdi1YG_secondary{max-width:40%;color:var(--qilin-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;line-height:18px;overflow:hidden}.vdi1YG_duration{color:var(--qilin-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;font-size:11px;line-height:18px}.vdi1YG_note{color:var(--qilin-alias-label-tertiary);margin:0;padding:6px 10px;font-size:12px}.vdi1YG_failure{align-items:center;gap:8px;padding:6px 10px;display:flex}.vdi1YG_failureText{min-width:0;color:var(--qilin-alias-state-error-primary);flex:auto;font-size:12px}.vdi1YG_retry{color:var(--qilin-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;align-items:center;gap:4px;padding:3px 8px;font-size:12px;line-height:18px;display:inline-flex}.vdi1YG_retry:hover{color:var(--qilin-alias-label-primary);background:var(--qilin-alias-interactive-bg-hover)}.vdi1YG_retryIcon,.vdi1YG_refreshIcon{flex:none}.vdi1YG_more{color:var(--qilin-alias-label-secondary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:8px;margin:2px 10px;padding:4px 8px;font-size:12px;line-height:18px}.vdi1YG_more:hover{color:var(--qilin-alias-label-primary);background:var(--qilin-alias-interactive-bg-hover)}";
1560
+ const tagId = "@qilin-agent/client-ui-sidebar-tasks/TasksBody.module.css";
1561
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
1562
+ const tag = document.createElement("style");
1563
+ tag.dataset.plugin = "@qilin-agent/client-ui-sidebar-tasks";
1564
+ tag.dataset.pluginCss = tagId;
1565
+ tag.textContent = css;
1566
+ document.head.appendChild(tag);
1567
+ }
1568
+ var TasksBody_module_css_default = {
1569
+ "action": "vdi1YG_action",
1570
+ "chevron": "vdi1YG_chevron",
1571
+ "disabled": "vdi1YG_disabled",
1572
+ "dot": "vdi1YG_dot",
1573
+ "duration": "vdi1YG_duration",
1574
+ "failure": "vdi1YG_failure",
1575
+ "failureText": "vdi1YG_failureText",
1576
+ "kind": "vdi1YG_kind",
1577
+ "label": "vdi1YG_label",
1578
+ "list": "vdi1YG_list",
1579
+ "more": "vdi1YG_more",
1580
+ "note": "vdi1YG_note",
1581
+ "open": "vdi1YG_open",
1582
+ "refreshIcon": "vdi1YG_refreshIcon",
1583
+ "retry": "vdi1YG_retry",
1584
+ "retryIcon": "vdi1YG_retryIcon",
1585
+ "root": "vdi1YG_root",
1586
+ "row": "vdi1YG_row",
1587
+ "secondary": "vdi1YG_secondary",
1588
+ "sectionAction": "vdi1YG_sectionAction",
1589
+ "sectionBody": "vdi1YG_sectionBody",
1590
+ "sectionCount": "vdi1YG_sectionCount",
1591
+ "sectionHeader": "vdi1YG_sectionHeader",
1592
+ "sectionTitle": "vdi1YG_sectionTitle",
1593
+ "sectionToggle": "vdi1YG_sectionToggle",
1594
+ "settled": "vdi1YG_settled",
1595
+ "viewToggle": "vdi1YG_viewToggle"
1596
+ };
1597
+ /** The pixel indent one nesting level adds. */
1598
+ const INDENT_PX = 14;
1599
+ /** The clock interval a running job's duration needs. */
1600
+ const TICK_MS = 1e3;
1601
+ /**
1602
+ * One collapsible section header: its chevron, its title, its stated count, and
1603
+ * the section's own control in the header's trailing slot.
1604
+ */
1605
+ function SectionHeader({ section, title, count, expanded, onToggle, action }) {
1606
+ return (0, react_jsx_runtime.jsxs)("div", {
1607
+ className: TasksBody_module_css_default.sectionHeader,
1608
+ "data-tasks-section": section,
1609
+ children: [(0, react_jsx_runtime.jsxs)("button", {
1610
+ type: "button",
1611
+ className: TasksBody_module_css_default.sectionToggle,
1612
+ "aria-expanded": expanded,
1613
+ onClick: onToggle,
1614
+ children: [
1615
+ expanded ? (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconChevronDownOutline14, { className: TasksBody_module_css_default.chevron }) : (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconChevronRightOutline14, { className: TasksBody_module_css_default.chevron }),
1616
+ (0, react_jsx_runtime.jsx)("span", {
1617
+ className: TasksBody_module_css_default.sectionTitle,
1618
+ children: title
1619
+ }),
1620
+ (0, react_jsx_runtime.jsx)("span", {
1621
+ className: TasksBody_module_css_default.sectionCount,
1622
+ children: count
1623
+ })
1624
+ ]
1625
+ }), action]
1626
+ });
1627
+ }
1628
+ /**
1629
+ * Why one child carries no readable record.
1630
+ * @param reason - the catalog's diagnostic reason.
1631
+ * @param t - namespace-bound translate.
1632
+ * @returns the line the row shows.
1633
+ */
1634
+ function diagnosticLabel(reason, t) {
1635
+ switch (reason) {
1636
+ case "corrupt": return t("subagents.diagnostic.corrupt");
1637
+ case "unsupported": return t("subagents.diagnostic.unsupported");
1638
+ case "unavailable": return t("subagents.diagnostic.unavailable");
1639
+ }
1640
+ }
1641
+ /** One subagent row: the open gesture across the row, the interrupt beside it. */
1642
+ function SubagentRowView({ row, t, onOpen, onInterrupt }) {
1643
+ if (row.kind === "diagnostic") return (0, react_jsx_runtime.jsxs)("li", {
1644
+ className: clsx(TasksBody_module_css_default.row, TasksBody_module_css_default.disabled),
1645
+ "data-tasks-subagent": row.id,
1646
+ "data-tasks-depth": row.depth,
1647
+ children: [
1648
+ (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.StateDot, {
1649
+ state: "error",
1650
+ className: TasksBody_module_css_default.dot
1651
+ }),
1652
+ (0, react_jsx_runtime.jsx)("span", {
1653
+ className: TasksBody_module_css_default.label,
1654
+ children: row.id
1655
+ }),
1656
+ (0, react_jsx_runtime.jsx)("span", {
1657
+ className: TasksBody_module_css_default.secondary,
1658
+ children: diagnosticLabel(row.reason, t)
1659
+ })
1660
+ ]
1661
+ });
1662
+ return (0, react_jsx_runtime.jsxs)("li", {
1663
+ className: TasksBody_module_css_default.row,
1664
+ "data-tasks-subagent": row.id,
1665
+ "data-tasks-depth": row.depth,
1666
+ style: { paddingInlineStart: row.depth * INDENT_PX },
1667
+ children: [(0, react_jsx_runtime.jsxs)("button", {
1668
+ type: "button",
1669
+ className: TasksBody_module_css_default.open,
1670
+ onClick: () => {
1671
+ onOpen(row.address);
1672
+ },
1673
+ children: [
1674
+ (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.StateDot, {
1675
+ state: row.running ? "ongoing" : "done",
1676
+ className: TasksBody_module_css_default.dot
1677
+ }),
1678
+ (0, react_jsx_runtime.jsx)("span", {
1679
+ className: TasksBody_module_css_default.label,
1680
+ children: row.label
1681
+ }),
1682
+ (0, react_jsx_runtime.jsx)("span", {
1683
+ className: TasksBody_module_css_default.secondary,
1684
+ children: t(row.address.mode === "one-shot" ? "subagents.mode.oneShot" : "subagents.mode.continuable")
1685
+ })
1686
+ ]
1687
+ }), row.running && row.address.mode === "continuable" && (0, react_jsx_runtime.jsx)("button", {
1688
+ type: "button",
1689
+ className: TasksBody_module_css_default.action,
1690
+ onClick: () => {
1691
+ onInterrupt(row.address.childSessionId, row.address.parentSessionId);
1692
+ },
1693
+ children: t("subagents.interrupt")
1694
+ })]
1695
+ });
1696
+ }
1697
+ /** One job row: status marker, kind, label, the producer's detail, and elapsed time. */
1698
+ function TaskRowView({ job, now, t }) {
1699
+ const live = isLive(job);
1700
+ const duration = formatDuration(jobElapsed(job, now), t);
1701
+ const status = job.detail ?? jobStatusLabel(job.status, t);
1702
+ return (0, react_jsx_runtime.jsxs)("li", {
1703
+ className: clsx(TasksBody_module_css_default.row, !live && TasksBody_module_css_default.settled),
1704
+ "data-tasks-job": job.id,
1705
+ "data-tasks-status": job.status,
1706
+ children: [
1707
+ (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.StateDot, {
1708
+ state: jobDotState(job.status),
1709
+ className: TasksBody_module_css_default.dot
1710
+ }),
1711
+ (0, react_jsx_runtime.jsx)("span", {
1712
+ className: TasksBody_module_css_default.kind,
1713
+ children: job.kind
1714
+ }),
1715
+ (0, react_jsx_runtime.jsx)("span", {
1716
+ className: TasksBody_module_css_default.label,
1717
+ title: job.label,
1718
+ children: job.label
1719
+ }),
1720
+ (0, react_jsx_runtime.jsx)("span", {
1721
+ className: TasksBody_module_css_default.secondary,
1722
+ title: status,
1723
+ children: status
1724
+ }),
1725
+ (0, react_jsx_runtime.jsx)("span", {
1726
+ className: TasksBody_module_css_default.duration,
1727
+ title: t(live ? "tasks.duration.live" : "tasks.duration.done", { duration }),
1728
+ children: duration
1729
+ })
1730
+ ]
1731
+ });
1732
+ }
1733
+ /**
1734
+ * The page body.
1735
+ * @param props - the tab's runtime share, the injected face, the graph store
1736
+ * shares, and the copy.
1737
+ * @returns the two sections, or the single line a Session with no work shows.
1738
+ */
1739
+ function TasksBody({ sessionId, useSessions, useJobs, useStore, actions, watchRows, openChild, refresh, interruptChild, t }) {
1740
+ const { catalogs, jobs, summaries } = useWorkSources(sessionId, {
1741
+ useSessions,
1742
+ useJobs,
1743
+ watchRows
1744
+ });
1745
+ const catalog = catalogs[sessionId];
1746
+ const [sections, setSections] = (0, react.useState)({
1747
+ subagents: true,
1748
+ tasks: true
1749
+ });
1750
+ const [allSubagents, setAllSubagents] = (0, react.useState)(false);
1751
+ const [allTasks, setAllTasks] = (0, react.useState)(false);
1752
+ const [now, setNow] = (0, react.useState)(() => Date.now());
1753
+ const rows = (0, react.useMemo)(() => subagentRows(catalogs, sessionId), [catalogs, sessionId]);
1754
+ const total = (0, react.useMemo)(() => subagentTotal(childRowCount(rows), indexSubagentDescendants(summaries), sessionId), [
1755
+ rows,
1756
+ summaries,
1757
+ sessionId
1758
+ ]);
1759
+ const tasks = (0, react.useMemo)(() => orderedJobs(jobs), [jobs]);
1760
+ const liveTasks = (0, react.useMemo)(() => jobs.filter(isLive).length, [jobs]);
1761
+ const view = useStore((state) => state.view);
1762
+ const expandedKeys = useStore((state) => state.expanded);
1763
+ const graphModel = (0, react.useMemo)(() => buildTasksGraphModel({
1764
+ rootId: sessionId,
1765
+ catalogs,
1766
+ summaries,
1767
+ expanded: new Set(Object.keys(expandedKeys).filter((key) => expandedKeys[key] === true)),
1768
+ currentSessionId: sessionId,
1769
+ labelOf: (entry, summary) => entry.label ?? summary?.displayTitle ?? entry.id,
1770
+ secondaryOf: (entry) => t(entry.mode === "one-shot" ? "subagents.mode.oneShot" : "subagents.mode.continuable")
1771
+ }), [
1772
+ catalogs,
1773
+ summaries,
1774
+ expandedKeys,
1775
+ sessionId,
1776
+ t
1777
+ ]);
1778
+ /** One node activation: aggregates fold, placeholders read their level, cards navigate. */
1779
+ const onGraphNodeClick = (node) => {
1780
+ if (node.aggregateKey !== void 0) {
1781
+ actions.aggregateToggled(node.aggregateKey);
1782
+ return;
1783
+ }
1784
+ if (node.kind === "placeholder") {
1785
+ refresh(node.parentId);
1786
+ return;
1787
+ }
1788
+ if (node.kind === "main") {
1789
+ openChild(sessionId);
1790
+ return;
1791
+ }
1792
+ /* v8 ignore next -- the page's catalogs give every remaining card an address. */
1793
+ if (node.address !== void 0) openChild(node.address);
1794
+ };
1795
+ (0, react.useEffect)(() => {
1796
+ if (liveTasks === 0) return;
1797
+ setNow(Date.now());
1798
+ const timer = setInterval(() => {
1799
+ setNow(Date.now());
1800
+ }, TICK_MS);
1801
+ return () => {
1802
+ clearInterval(timer);
1803
+ };
1804
+ }, [liveTasks]);
1805
+ if (rows.length === 0 && tasks.length === 0 && catalog?.state === "ready") return (0, react_jsx_runtime.jsx)("div", {
1806
+ className: TasksBody_module_css_default.root,
1807
+ "data-tasks-state": "empty",
1808
+ children: (0, react_jsx_runtime.jsx)("p", {
1809
+ className: TasksBody_module_css_default.note,
1810
+ children: t("empty")
1811
+ })
1812
+ });
1813
+ const shownSubagents = allSubagents ? rows : rows.slice(0, 5);
1814
+ const shownTasks = allTasks ? tasks : tasks.slice(0, 3);
1815
+ const hiddenSubagents = rows.length - shownSubagents.length;
1816
+ const hiddenTasks = tasks.length - shownTasks.length;
1817
+ return (0, react_jsx_runtime.jsxs)("div", {
1818
+ className: TasksBody_module_css_default.root,
1819
+ "data-tasks-state": "ready",
1820
+ children: [
1821
+ (0, react_jsx_runtime.jsx)(SectionHeader, {
1822
+ section: "subagents",
1823
+ title: t("subagents.title"),
1824
+ count: t(total === 1 ? "subagents.count.one" : "subagents.count.other", { count: total }),
1825
+ expanded: sections.subagents,
1826
+ onToggle: () => {
1827
+ setSections((current) => ({
1828
+ ...current,
1829
+ subagents: !current.subagents
1830
+ }));
1831
+ },
1832
+ action: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("span", {
1833
+ className: TasksBody_module_css_default.viewToggle,
1834
+ role: "group",
1835
+ "aria-label": t("graph.toggle"),
1836
+ children: [(0, react_jsx_runtime.jsx)("button", {
1837
+ type: "button",
1838
+ "aria-pressed": view === "graph",
1839
+ "data-tasks-view": "graph",
1840
+ onClick: () => {
1841
+ actions.viewSet("graph");
1842
+ },
1843
+ children: t("graph.view")
1844
+ }), (0, react_jsx_runtime.jsx)("button", {
1845
+ type: "button",
1846
+ "aria-pressed": view === "list",
1847
+ "data-tasks-view": "list",
1848
+ onClick: () => {
1849
+ actions.viewSet("list");
1850
+ },
1851
+ children: t("graph.list")
1852
+ })]
1853
+ }), (0, react_jsx_runtime.jsx)("button", {
1854
+ type: "button",
1855
+ className: TasksBody_module_css_default.sectionAction,
1856
+ "aria-label": t("subagents.refresh"),
1857
+ title: t("subagents.refresh"),
1858
+ "data-tasks-refresh": true,
1859
+ onClick: () => {
1860
+ refresh(sessionId);
1861
+ },
1862
+ children: (0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconRefreshOutline16, { className: TasksBody_module_css_default.refreshIcon })
1863
+ })] })
1864
+ }),
1865
+ sections.subagents && (0, react_jsx_runtime.jsx)("div", {
1866
+ className: TasksBody_module_css_default.sectionBody,
1867
+ "data-tasks-panel": "subagents",
1868
+ children: catalog?.state === "error" ? (0, react_jsx_runtime.jsxs)("div", {
1869
+ className: TasksBody_module_css_default.failure,
1870
+ children: [(0, react_jsx_runtime.jsx)("span", {
1871
+ className: TasksBody_module_css_default.failureText,
1872
+ children: catalog.error?.message ?? t("subagents.failed")
1873
+ }), (0, react_jsx_runtime.jsxs)("button", {
1874
+ type: "button",
1875
+ className: TasksBody_module_css_default.retry,
1876
+ onClick: () => {
1877
+ refresh(sessionId);
1878
+ },
1879
+ children: [(0, react_jsx_runtime.jsx)(_qilin_agent_client_ui_primitives.IconRefreshOutline14, { className: TasksBody_module_css_default.retryIcon }), t("subagents.retry")]
1880
+ })]
1881
+ }) : view === "graph" && catalog?.state === "ready" ? (0, react_jsx_runtime.jsx)(TasksGraphView, {
1882
+ model: graphModel,
1883
+ onNodeClick: onGraphNodeClick,
1884
+ useStore,
1885
+ actions,
1886
+ t
1887
+ }) : rows.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
1888
+ className: TasksBody_module_css_default.note,
1889
+ children: catalog === void 0 || catalog.state === "loading" ? t("subagents.loading") : t("subagents.empty")
1890
+ }) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("ul", {
1891
+ className: TasksBody_module_css_default.list,
1892
+ children: shownSubagents.map((row) => (0, react_jsx_runtime.jsx)(SubagentRowView, {
1893
+ row,
1894
+ t,
1895
+ onOpen: openChild,
1896
+ onInterrupt: interruptChild
1897
+ }, row.id))
1898
+ }), rows.length > 5 && (0, react_jsx_runtime.jsx)("button", {
1899
+ type: "button",
1900
+ className: TasksBody_module_css_default.more,
1901
+ "aria-expanded": allSubagents,
1902
+ "data-tasks-more": "subagents",
1903
+ onClick: () => {
1904
+ setAllSubagents((current) => !current);
1905
+ },
1906
+ children: allSubagents ? t("more.collapse") : t("more.expand", { count: hiddenSubagents })
1907
+ })] })
1908
+ }),
1909
+ (0, react_jsx_runtime.jsx)(SectionHeader, {
1910
+ section: "tasks",
1911
+ title: t("tasks.title"),
1912
+ count: t(tasks.length === 1 ? "tasks.count.one" : "tasks.count.other", { count: tasks.length }),
1913
+ expanded: sections.tasks,
1914
+ onToggle: () => {
1915
+ setSections((current) => ({
1916
+ ...current,
1917
+ tasks: !current.tasks
1918
+ }));
1919
+ }
1920
+ }),
1921
+ sections.tasks && (0, react_jsx_runtime.jsx)("div", {
1922
+ className: TasksBody_module_css_default.sectionBody,
1923
+ "data-tasks-panel": "tasks",
1924
+ children: tasks.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
1925
+ className: TasksBody_module_css_default.note,
1926
+ children: t("tasks.empty")
1927
+ }) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("ul", {
1928
+ className: TasksBody_module_css_default.list,
1929
+ children: shownTasks.map((job) => (0, react_jsx_runtime.jsx)(TaskRowView, {
1930
+ job,
1931
+ now,
1932
+ t
1933
+ }, job.id))
1934
+ }), tasks.length > 3 && (0, react_jsx_runtime.jsx)("button", {
1935
+ type: "button",
1936
+ className: TasksBody_module_css_default.more,
1937
+ "aria-expanded": allTasks,
1938
+ "data-tasks-more": "tasks",
1939
+ onClick: () => {
1940
+ setAllTasks((current) => !current);
1941
+ },
1942
+ children: allTasks ? t("more.collapse") : t("more.expand", { count: hiddenTasks })
1943
+ })] })
1944
+ })
1945
+ ]
1946
+ });
1947
+ }
1948
+ //#endregion
1949
+ //#region lib/types/client/index.js
1950
+ /**
1951
+ * Required browser services: the tab registry, the keyed seats, the Session
1952
+ * service and its subagent Remote, and copy.
1953
+ */
1954
+ const inject = [
1955
+ "slots",
1956
+ "locale",
1957
+ "sessions",
1958
+ "sidebarRightTabs",
1959
+ "remote",
1960
+ "remote.subagents",
1961
+ "jobs"
1962
+ ];
1963
+ /**
1964
+ * Client plugin body: register the type, its dictionaries, its body, and the
1965
+ * chip's status pill.
1966
+ * @param ctx - client root context carrying the registry, the slots, the Session service, and the Remote face.
1967
+ */
1968
+ function apply(ctx) {
1969
+ ctx.effect(() => ctx.locale.register(NS, {
1970
+ zh,
1971
+ en
1972
+ }), "ui-sidebar-tasks: dictionaries");
1973
+ const t = ctx.locale.bind(NS);
1974
+ ctx.effect(() => ctx.sidebarRightTabs.register(tasksDefinition(t)), "ui-sidebar-tasks: tasks type");
1975
+ const jobsFace = {
1976
+ hooks: { jobs: ctx.jobs.state },
1977
+ watchRows: (sessionId) => ctx.jobs.watchRows(sessionId)
1978
+ };
1979
+ const face = tasksFace({
1980
+ openSubagent: (target) => {
1981
+ ctx.uiWorkspace.openSession(target);
1982
+ },
1983
+ refreshProjections: (parentSessionId) => ctx.sessions.refreshProjections(parentSessionId)
1984
+ }, ctx.remote.subagents, jobsFace);
1985
+ const graphStore = createTasksGraphStore();
1986
+ ctx.effect(() => ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
1987
+ name: "sidebar.right.pane.tab",
1988
+ key: TASKS_ID,
1989
+ locale: NS,
1990
+ store: graphStore,
1991
+ inject: () => face
1992
+ }, TasksBody)), "ui-sidebar-tasks: tasks tab body");
1993
+ ctx.effect(() => ctx.slots.inject("sidebar.right.pane.tab.badge", () => ctx.slots.register({
1994
+ name: "sidebar.right.pane.tab.badge",
1995
+ key: TASKS_ID,
1996
+ inject: () => jobsFace
1997
+ }, TasksBadge)), "ui-sidebar-tasks: tasks tab badge");
1998
+ }
1999
+ //#endregion
2000
+ exports.apply = apply;
2001
+ exports.inject = inject;
2002
+ return module.exports;
2003
+ }
2004
+ });
2005
+
2006
+ //# sourceMappingURL=client.js.map