@op1/threads 0.1.7 → 0.1.8
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/README.md +66 -4
- package/package.json +11 -3
- package/src/activity-model.ts +152 -0
- package/src/activity-picker.tsx +175 -0
- package/src/activity-rail.ts +135 -0
- package/src/activity-theme.ts +49 -0
- package/src/activity.ts +781 -0
- package/tui.ts +55 -8
package/tui.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { Plugin } from "@opencode/plugin/tui";
|
|
2
|
-
import {
|
|
2
|
+
import { getComponentCatalogue } from "@opentui/solid/components";
|
|
3
|
+
import { createEffect, createSignal } from "solid-js";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { ThreadsRpc } from "./src/rpc";
|
|
6
|
+
import { activity } from "./src/activity";
|
|
7
|
+
import { cleanRoleTitle } from "./src/activity-model";
|
|
8
|
+
import {
|
|
9
|
+
BoxRenderable,
|
|
10
|
+
ScrollBoxRenderable,
|
|
11
|
+
TextRenderable,
|
|
12
|
+
TextAttributes,
|
|
13
|
+
RGBA,
|
|
14
|
+
} from "@opentui/core";
|
|
5
15
|
|
|
6
16
|
const CoordinatorRef = z.object({ coordinatorID: z.string() });
|
|
7
17
|
|
|
@@ -9,15 +19,26 @@ export default Plugin.define({
|
|
|
9
19
|
id: "op-threads",
|
|
10
20
|
setup(ctx) {
|
|
11
21
|
const rpc = ctx.client.rpc(ThreadsRpc);
|
|
22
|
+
const sidebar = activity(
|
|
23
|
+
ctx,
|
|
24
|
+
{ BoxRenderable, ScrollBoxRenderable, TextRenderable, TextAttributes, RGBA },
|
|
25
|
+
{ createEffect, createSignal },
|
|
26
|
+
getComponentCatalogue().spinner,
|
|
27
|
+
);
|
|
28
|
+
const [cleaned, saveCleaned] = ctx.storage.store("role-title-cleanup", {
|
|
29
|
+
initial: { ids: [] as string[] },
|
|
30
|
+
});
|
|
12
31
|
const initial: { workerIDs: string[] } = { workerIDs: [] };
|
|
13
32
|
const [seen, updateSeen] = ctx.storage.memory("seen-workers", { initial });
|
|
14
33
|
const closing = new Set<string>();
|
|
34
|
+
const abort = new AbortController();
|
|
15
35
|
let stopped = false;
|
|
16
36
|
let running = false;
|
|
17
37
|
let reopenPending = false;
|
|
18
38
|
let lastError: string | undefined;
|
|
19
39
|
let movingFrom: string | undefined;
|
|
20
40
|
function groupTabs() {
|
|
41
|
+
if (sidebar.mounted()) return;
|
|
21
42
|
const tabs = ctx.ui.tabs.list().map((tab) => {
|
|
22
43
|
const projectID = ctx.data.session.get(tab.sessionID)?.projectID;
|
|
23
44
|
return {
|
|
@@ -80,11 +101,20 @@ export default Plugin.define({
|
|
|
80
101
|
if (!coordinatorIDs.length) return;
|
|
81
102
|
const { workers } = await (reopen ? rpc.restore : rpc.snapshot)(
|
|
82
103
|
{ coordinatorIDs },
|
|
83
|
-
{
|
|
104
|
+
{
|
|
105
|
+
location: ctx.location ?? ctx.data.location.default(),
|
|
106
|
+
signal: abort.signal,
|
|
107
|
+
},
|
|
84
108
|
);
|
|
109
|
+
if (reopen)
|
|
110
|
+
await sidebar.restore(workers.map((worker) => worker.workerID));
|
|
111
|
+
sidebar.updateWorkers(workers);
|
|
85
112
|
for (const worker of workers) {
|
|
86
113
|
if (stopped) return;
|
|
87
|
-
|
|
114
|
+
if (!reopen && sidebar.isDismissed(worker.workerID)) continue;
|
|
115
|
+
const tab = ctx.ui.tabs
|
|
116
|
+
.list()
|
|
117
|
+
.find((tab) => tab.sessionID === worker.workerID);
|
|
88
118
|
if (closing.has(worker.workerID) && tab) continue;
|
|
89
119
|
const closed = closing.delete(worker.workerID);
|
|
90
120
|
if (
|
|
@@ -107,7 +137,8 @@ export default Plugin.define({
|
|
|
107
137
|
}
|
|
108
138
|
continue;
|
|
109
139
|
}
|
|
110
|
-
if (!reopen && !closed && seen.workerIDs.includes(worker.workerID))
|
|
140
|
+
if (!reopen && !closed && seen.workerIDs.includes(worker.workerID))
|
|
141
|
+
continue;
|
|
111
142
|
await ctx.data.session.sync(worker.workerID);
|
|
112
143
|
if (
|
|
113
144
|
!stopped &&
|
|
@@ -129,10 +160,24 @@ export default Plugin.define({
|
|
|
129
160
|
if (stopped) return;
|
|
130
161
|
const role = roles.get(tab.sessionID);
|
|
131
162
|
const session = ctx.data.session.get(tab.sessionID);
|
|
132
|
-
if (!role || !session
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
163
|
+
if (!role || !session || cleaned.ids.includes(tab.sessionID))
|
|
164
|
+
continue;
|
|
165
|
+
const fresh = await ctx.client.session.get(
|
|
166
|
+
{ sessionID: tab.sessionID },
|
|
167
|
+
{ signal: abort.signal },
|
|
168
|
+
);
|
|
169
|
+
if (stopped) return;
|
|
170
|
+
const title = cleanRoleTitle(fresh.title ?? "", true);
|
|
171
|
+
if (title && title !== fresh.title)
|
|
172
|
+
await ctx.client.session.update(
|
|
173
|
+
{ sessionID: tab.sessionID, title },
|
|
174
|
+
{ signal: abort.signal },
|
|
175
|
+
);
|
|
176
|
+
if (stopped) return;
|
|
177
|
+
await saveCleaned((draft) => {
|
|
178
|
+
if (!draft.ids.includes(tab.sessionID))
|
|
179
|
+
draft.ids.push(tab.sessionID);
|
|
180
|
+
});
|
|
136
181
|
}
|
|
137
182
|
groupTabs();
|
|
138
183
|
}
|
|
@@ -182,6 +227,8 @@ export default Plugin.define({
|
|
|
182
227
|
refresh();
|
|
183
228
|
return () => {
|
|
184
229
|
stopped = true;
|
|
230
|
+
abort.abort();
|
|
231
|
+
sidebar.dispose();
|
|
185
232
|
clearInterval(timer);
|
|
186
233
|
stopEvents();
|
|
187
234
|
removeSlot();
|