ikanban-web 0.2.7 → 0.2.9
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/assets/{ghostty-web-BnnMBzi5.js → ghostty-web-CveYg7XI.js} +1 -1
- package/dist/assets/{home-DeLBKxk3.js → home-OHGVA1hY.js} +1 -1
- package/dist/assets/{index-pnX-hTQP.css → index-B0TEjAod.css} +1 -1
- package/dist/assets/{index-ehO8js0v.js → index-BlcL7wgr.js} +132 -124
- package/dist/assets/session-CdIG8nTN.js +24 -0
- package/dist/index.html +19 -6
- package/package.json +1 -1
- package/src/components/dialog-session-timeline.tsx +85 -0
- package/src/components/prompt-input.tsx +3 -3
- package/src/components/settings-general.tsx +18 -93
- package/src/context/global-sync/child-store.ts +1 -0
- package/src/context/global-sync/event-reducer.test.ts +5 -0
- package/src/context/global-sync/event-reducer.ts +263 -183
- package/src/context/global-sync/types.ts +3 -0
- package/src/context/highlights.tsx +4 -176
- package/src/context/layout.tsx +12 -2
- package/src/context/sync.tsx +237 -2
- package/src/i18n/en.ts +11 -0
- package/src/i18n/zh.ts +22 -0
- package/src/pages/layout.tsx +11 -54
- package/src/pages/session/review-tab.tsx +7 -1
- package/src/pages/session/session-side-panel.tsx +17 -51
- package/src/pages/session/use-session-commands.tsx +91 -33
- package/src/pages/session.tsx +99 -16
- package/dist/assets/session-D7vMISoz.js +0 -24
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import { Binary } from "ikanban-utils/binary"
|
|
2
|
-
import {
|
|
1
|
+
import { Binary } from "ikanban-utils/binary";
|
|
2
|
+
import {
|
|
3
|
+
produce,
|
|
4
|
+
reconcile,
|
|
5
|
+
type SetStoreFunction,
|
|
6
|
+
type Store,
|
|
7
|
+
} from "solid-js/store";
|
|
3
8
|
import type {
|
|
4
9
|
FileDiff,
|
|
5
10
|
Message,
|
|
@@ -10,33 +15,36 @@ import type {
|
|
|
10
15
|
Session,
|
|
11
16
|
SessionStatus,
|
|
12
17
|
Todo,
|
|
13
|
-
} from "@opencode-ai/sdk/v2/client"
|
|
14
|
-
import type { State, VcsCache } from "./types"
|
|
15
|
-
import { trimSessions } from "./session-trim"
|
|
18
|
+
} from "@opencode-ai/sdk/v2/client";
|
|
19
|
+
import type { State, VcsCache } from "./types";
|
|
20
|
+
import { trimSessions } from "./session-trim";
|
|
16
21
|
|
|
17
22
|
export function applyGlobalEvent(input: {
|
|
18
|
-
event: { type: string; properties?: unknown }
|
|
19
|
-
project: Project[]
|
|
20
|
-
setGlobalProject: (next: Project[] | ((draft: Project[]) => void)) => void
|
|
21
|
-
refresh: () => void
|
|
23
|
+
event: { type: string; properties?: unknown };
|
|
24
|
+
project: Project[];
|
|
25
|
+
setGlobalProject: (next: Project[] | ((draft: Project[]) => void)) => void;
|
|
26
|
+
refresh: () => void;
|
|
22
27
|
}) {
|
|
23
|
-
if (
|
|
24
|
-
input.
|
|
25
|
-
|
|
28
|
+
if (
|
|
29
|
+
input.event.type === "global.disposed" ||
|
|
30
|
+
input.event.type === "server.connected"
|
|
31
|
+
) {
|
|
32
|
+
input.refresh();
|
|
33
|
+
return;
|
|
26
34
|
}
|
|
27
35
|
|
|
28
|
-
if (input.event.type !== "project.updated") return
|
|
29
|
-
const properties = input.event.properties as Project
|
|
30
|
-
const result = Binary.search(input.project, properties.id, (s) => s.id)
|
|
36
|
+
if (input.event.type !== "project.updated") return;
|
|
37
|
+
const properties = input.event.properties as Project;
|
|
38
|
+
const result = Binary.search(input.project, properties.id, (s) => s.id);
|
|
31
39
|
if (result.found) {
|
|
32
40
|
input.setGlobalProject((draft) => {
|
|
33
|
-
draft[result.index] = { ...draft[result.index], ...properties }
|
|
34
|
-
})
|
|
35
|
-
return
|
|
41
|
+
draft[result.index] = { ...draft[result.index], ...properties };
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
36
44
|
}
|
|
37
45
|
input.setGlobalProject((draft) => {
|
|
38
|
-
draft.splice(result.index, 0, properties)
|
|
39
|
-
})
|
|
46
|
+
draft.splice(result.index, 0, properties);
|
|
47
|
+
});
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
function cleanupSessionCaches(
|
|
@@ -45,302 +53,374 @@ function cleanupSessionCaches(
|
|
|
45
53
|
sessionID: string,
|
|
46
54
|
setSessionTodo?: (sessionID: string, todos: Todo[] | undefined) => void,
|
|
47
55
|
) {
|
|
48
|
-
if (!sessionID) return
|
|
56
|
+
if (!sessionID) return;
|
|
49
57
|
const hasAny =
|
|
50
58
|
store.message[sessionID] !== undefined ||
|
|
51
59
|
store.session_diff[sessionID] !== undefined ||
|
|
52
60
|
store.todo[sessionID] !== undefined ||
|
|
53
61
|
store.permission[sessionID] !== undefined ||
|
|
54
62
|
store.question[sessionID] !== undefined ||
|
|
55
|
-
store.session_status[sessionID] !== undefined
|
|
56
|
-
setSessionTodo?.(sessionID, undefined)
|
|
57
|
-
if (!hasAny) return
|
|
63
|
+
store.session_status[sessionID] !== undefined;
|
|
64
|
+
setSessionTodo?.(sessionID, undefined);
|
|
65
|
+
if (!hasAny) return;
|
|
58
66
|
setStore(
|
|
59
67
|
produce((draft) => {
|
|
60
|
-
const messages = draft.message[sessionID]
|
|
68
|
+
const messages = draft.message[sessionID];
|
|
61
69
|
if (messages) {
|
|
62
70
|
for (const message of messages) {
|
|
63
|
-
const id = message?.id
|
|
64
|
-
if (!id) continue
|
|
65
|
-
delete draft.part[id]
|
|
71
|
+
const id = message?.id;
|
|
72
|
+
if (!id) continue;
|
|
73
|
+
delete draft.part[id];
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
|
-
delete draft.message[sessionID]
|
|
69
|
-
delete draft.session_diff[sessionID]
|
|
70
|
-
delete draft.todo[sessionID]
|
|
71
|
-
delete draft.permission[sessionID]
|
|
72
|
-
delete draft.question[sessionID]
|
|
73
|
-
delete draft.session_status[sessionID]
|
|
76
|
+
delete draft.message[sessionID];
|
|
77
|
+
delete draft.session_diff[sessionID];
|
|
78
|
+
delete draft.todo[sessionID];
|
|
79
|
+
delete draft.permission[sessionID];
|
|
80
|
+
delete draft.question[sessionID];
|
|
81
|
+
delete draft.session_status[sessionID];
|
|
74
82
|
}),
|
|
75
|
-
)
|
|
83
|
+
);
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
export function applyDirectoryEvent(input: {
|
|
79
|
-
event: { type: string; properties?: unknown }
|
|
80
|
-
store: Store<State
|
|
81
|
-
setStore: SetStoreFunction<State
|
|
82
|
-
push: (directory: string) => void
|
|
83
|
-
directory: string
|
|
84
|
-
loadLsp: () => void
|
|
85
|
-
vcsCache?: VcsCache
|
|
86
|
-
setSessionTodo?: (sessionID: string, todos: Todo[] | undefined) => void
|
|
87
|
+
event: { type: string; properties?: unknown };
|
|
88
|
+
store: Store<State>;
|
|
89
|
+
setStore: SetStoreFunction<State>;
|
|
90
|
+
push: (directory: string) => void;
|
|
91
|
+
directory: string;
|
|
92
|
+
loadLsp: () => void;
|
|
93
|
+
vcsCache?: VcsCache;
|
|
94
|
+
setSessionTodo?: (sessionID: string, todos: Todo[] | undefined) => void;
|
|
87
95
|
}) {
|
|
88
|
-
const event = input.event
|
|
96
|
+
const event = input.event;
|
|
89
97
|
switch (event.type) {
|
|
90
98
|
case "server.instance.disposed": {
|
|
91
|
-
input.push(input.directory)
|
|
92
|
-
return
|
|
99
|
+
input.push(input.directory);
|
|
100
|
+
return;
|
|
93
101
|
}
|
|
94
102
|
case "session.created": {
|
|
95
|
-
const info = (event.properties as { info: Session }).info
|
|
96
|
-
const result = Binary.search(input.store.session, info.id, (s) => s.id)
|
|
103
|
+
const info = (event.properties as { info: Session }).info;
|
|
104
|
+
const result = Binary.search(input.store.session, info.id, (s) => s.id);
|
|
97
105
|
if (result.found) {
|
|
98
|
-
input.setStore("session", result.index, reconcile(info))
|
|
99
|
-
break
|
|
106
|
+
input.setStore("session", result.index, reconcile(info));
|
|
107
|
+
break;
|
|
100
108
|
}
|
|
101
|
-
const next = input.store.session.slice()
|
|
102
|
-
next.splice(result.index, 0, info)
|
|
103
|
-
const trimmed = trimSessions(next, {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
const next = input.store.session.slice();
|
|
110
|
+
next.splice(result.index, 0, info);
|
|
111
|
+
const trimmed = trimSessions(next, {
|
|
112
|
+
limit: input.store.limit,
|
|
113
|
+
permission: input.store.permission,
|
|
114
|
+
});
|
|
115
|
+
input.setStore("session", reconcile(trimmed, { key: "id" }));
|
|
116
|
+
if (!info.parentID) input.setStore("sessionTotal", (value) => value + 1);
|
|
117
|
+
break;
|
|
107
118
|
}
|
|
108
119
|
case "session.updated": {
|
|
109
|
-
const info = (event.properties as { info: Session }).info
|
|
110
|
-
const result = Binary.search(input.store.session, info.id, (s) => s.id)
|
|
120
|
+
const info = (event.properties as { info: Session }).info;
|
|
121
|
+
const result = Binary.search(input.store.session, info.id, (s) => s.id);
|
|
111
122
|
if (info.time.archived) {
|
|
112
123
|
if (result.found) {
|
|
113
124
|
input.setStore(
|
|
114
125
|
"session",
|
|
115
126
|
produce((draft) => {
|
|
116
|
-
draft.splice(result.index, 1)
|
|
127
|
+
draft.splice(result.index, 1);
|
|
117
128
|
}),
|
|
118
|
-
)
|
|
129
|
+
);
|
|
119
130
|
}
|
|
120
|
-
cleanupSessionCaches(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
131
|
+
cleanupSessionCaches(
|
|
132
|
+
input.store,
|
|
133
|
+
input.setStore,
|
|
134
|
+
info.id,
|
|
135
|
+
input.setSessionTodo,
|
|
136
|
+
);
|
|
137
|
+
if (info.parentID) break;
|
|
138
|
+
input.setStore("sessionTotal", (value) => Math.max(0, value - 1));
|
|
139
|
+
break;
|
|
124
140
|
}
|
|
125
141
|
if (result.found) {
|
|
126
|
-
input.setStore("session", result.index, reconcile(info))
|
|
127
|
-
break
|
|
142
|
+
input.setStore("session", result.index, reconcile(info));
|
|
143
|
+
break;
|
|
128
144
|
}
|
|
129
|
-
const next = input.store.session.slice()
|
|
130
|
-
next.splice(result.index, 0, info)
|
|
131
|
-
const trimmed = trimSessions(next, {
|
|
132
|
-
|
|
133
|
-
|
|
145
|
+
const next = input.store.session.slice();
|
|
146
|
+
next.splice(result.index, 0, info);
|
|
147
|
+
const trimmed = trimSessions(next, {
|
|
148
|
+
limit: input.store.limit,
|
|
149
|
+
permission: input.store.permission,
|
|
150
|
+
});
|
|
151
|
+
input.setStore("session", reconcile(trimmed, { key: "id" }));
|
|
152
|
+
break;
|
|
134
153
|
}
|
|
135
154
|
case "session.deleted": {
|
|
136
|
-
const info = (event.properties as { info: Session }).info
|
|
137
|
-
const result = Binary.search(input.store.session, info.id, (s) => s.id)
|
|
155
|
+
const info = (event.properties as { info: Session }).info;
|
|
156
|
+
const result = Binary.search(input.store.session, info.id, (s) => s.id);
|
|
138
157
|
if (result.found) {
|
|
139
158
|
input.setStore(
|
|
140
159
|
"session",
|
|
141
160
|
produce((draft) => {
|
|
142
|
-
draft.splice(result.index, 1)
|
|
161
|
+
draft.splice(result.index, 1);
|
|
143
162
|
}),
|
|
144
|
-
)
|
|
163
|
+
);
|
|
145
164
|
}
|
|
146
|
-
cleanupSessionCaches(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
165
|
+
cleanupSessionCaches(
|
|
166
|
+
input.store,
|
|
167
|
+
input.setStore,
|
|
168
|
+
info.id,
|
|
169
|
+
input.setSessionTodo,
|
|
170
|
+
);
|
|
171
|
+
if (info.parentID) break;
|
|
172
|
+
input.setStore("sessionTotal", (value) => Math.max(0, value - 1));
|
|
173
|
+
break;
|
|
150
174
|
}
|
|
151
175
|
case "session.diff": {
|
|
152
|
-
const props = event.properties as { sessionID: string; diff: FileDiff[] }
|
|
153
|
-
input.setStore(
|
|
154
|
-
|
|
176
|
+
const props = event.properties as { sessionID: string; diff: FileDiff[] };
|
|
177
|
+
input.setStore(
|
|
178
|
+
"session_diff",
|
|
179
|
+
props.sessionID,
|
|
180
|
+
reconcile(props.diff, { key: "file" }),
|
|
181
|
+
);
|
|
182
|
+
break;
|
|
155
183
|
}
|
|
156
184
|
case "todo.updated": {
|
|
157
|
-
const props = event.properties as { sessionID: string; todos: Todo[] }
|
|
158
|
-
input.setStore(
|
|
159
|
-
|
|
160
|
-
|
|
185
|
+
const props = event.properties as { sessionID: string; todos: Todo[] };
|
|
186
|
+
input.setStore(
|
|
187
|
+
"todo",
|
|
188
|
+
props.sessionID,
|
|
189
|
+
reconcile(props.todos, { key: "id" }),
|
|
190
|
+
);
|
|
191
|
+
input.setSessionTodo?.(props.sessionID, props.todos);
|
|
192
|
+
break;
|
|
161
193
|
}
|
|
162
194
|
case "session.status": {
|
|
163
|
-
const props = event.properties as {
|
|
164
|
-
|
|
165
|
-
|
|
195
|
+
const props = event.properties as {
|
|
196
|
+
sessionID: string;
|
|
197
|
+
status: SessionStatus;
|
|
198
|
+
};
|
|
199
|
+
if (props.status.type === "idle") {
|
|
200
|
+
input.setStore(
|
|
201
|
+
produce((draft) => {
|
|
202
|
+
delete draft.session_diff[props.sessionID];
|
|
203
|
+
delete draft.project_diff[input.directory];
|
|
204
|
+
}),
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
input.setStore(
|
|
208
|
+
"session_status",
|
|
209
|
+
props.sessionID,
|
|
210
|
+
reconcile(props.status),
|
|
211
|
+
);
|
|
212
|
+
break;
|
|
166
213
|
}
|
|
167
214
|
case "message.updated": {
|
|
168
|
-
const info = (event.properties as { info: Message }).info
|
|
169
|
-
const messages = input.store.message[info.sessionID]
|
|
215
|
+
const info = (event.properties as { info: Message }).info;
|
|
216
|
+
const messages = input.store.message[info.sessionID];
|
|
170
217
|
if (!messages) {
|
|
171
|
-
input.setStore("message", info.sessionID, [info])
|
|
172
|
-
break
|
|
218
|
+
input.setStore("message", info.sessionID, [info]);
|
|
219
|
+
break;
|
|
173
220
|
}
|
|
174
|
-
const result = Binary.search(messages, info.id, (m) => m.id)
|
|
221
|
+
const result = Binary.search(messages, info.id, (m) => m.id);
|
|
175
222
|
if (result.found) {
|
|
176
|
-
input.setStore(
|
|
177
|
-
|
|
223
|
+
input.setStore(
|
|
224
|
+
"message",
|
|
225
|
+
info.sessionID,
|
|
226
|
+
result.index,
|
|
227
|
+
reconcile(info),
|
|
228
|
+
);
|
|
229
|
+
break;
|
|
178
230
|
}
|
|
179
231
|
input.setStore(
|
|
180
232
|
"message",
|
|
181
233
|
info.sessionID,
|
|
182
234
|
produce((draft) => {
|
|
183
|
-
draft.splice(result.index, 0, info)
|
|
235
|
+
draft.splice(result.index, 0, info);
|
|
184
236
|
}),
|
|
185
|
-
)
|
|
186
|
-
break
|
|
237
|
+
);
|
|
238
|
+
break;
|
|
187
239
|
}
|
|
188
240
|
case "message.removed": {
|
|
189
|
-
const props = event.properties as {
|
|
241
|
+
const props = event.properties as {
|
|
242
|
+
sessionID: string;
|
|
243
|
+
messageID: string;
|
|
244
|
+
};
|
|
190
245
|
input.setStore(
|
|
191
246
|
produce((draft) => {
|
|
192
|
-
const messages = draft.message[props.sessionID]
|
|
247
|
+
const messages = draft.message[props.sessionID];
|
|
193
248
|
if (messages) {
|
|
194
|
-
const result = Binary.search(
|
|
195
|
-
|
|
249
|
+
const result = Binary.search(
|
|
250
|
+
messages,
|
|
251
|
+
props.messageID,
|
|
252
|
+
(m) => m.id,
|
|
253
|
+
);
|
|
254
|
+
if (result.found) messages.splice(result.index, 1);
|
|
196
255
|
}
|
|
197
|
-
delete draft.part[props.messageID]
|
|
256
|
+
delete draft.part[props.messageID];
|
|
198
257
|
}),
|
|
199
|
-
)
|
|
200
|
-
break
|
|
258
|
+
);
|
|
259
|
+
break;
|
|
201
260
|
}
|
|
202
261
|
case "message.part.updated": {
|
|
203
|
-
const part = (event.properties as { part: Part }).part
|
|
204
|
-
const parts = input.store.part[part.messageID]
|
|
262
|
+
const part = (event.properties as { part: Part }).part;
|
|
263
|
+
const parts = input.store.part[part.messageID];
|
|
205
264
|
if (!parts) {
|
|
206
|
-
input.setStore("part", part.messageID, [part])
|
|
207
|
-
break
|
|
265
|
+
input.setStore("part", part.messageID, [part]);
|
|
266
|
+
break;
|
|
208
267
|
}
|
|
209
|
-
const result = Binary.search(parts, part.id, (p) => p.id)
|
|
268
|
+
const result = Binary.search(parts, part.id, (p) => p.id);
|
|
210
269
|
if (result.found) {
|
|
211
|
-
input.setStore("part", part.messageID, result.index, reconcile(part))
|
|
212
|
-
break
|
|
270
|
+
input.setStore("part", part.messageID, result.index, reconcile(part));
|
|
271
|
+
break;
|
|
213
272
|
}
|
|
214
273
|
input.setStore(
|
|
215
274
|
"part",
|
|
216
275
|
part.messageID,
|
|
217
276
|
produce((draft) => {
|
|
218
|
-
draft.splice(result.index, 0, part)
|
|
277
|
+
draft.splice(result.index, 0, part);
|
|
219
278
|
}),
|
|
220
|
-
)
|
|
221
|
-
break
|
|
279
|
+
);
|
|
280
|
+
break;
|
|
222
281
|
}
|
|
223
282
|
case "message.part.removed": {
|
|
224
|
-
const props = event.properties as { messageID: string; partID: string }
|
|
225
|
-
const parts = input.store.part[props.messageID]
|
|
226
|
-
if (!parts) break
|
|
227
|
-
const result = Binary.search(parts, props.partID, (p) => p.id)
|
|
283
|
+
const props = event.properties as { messageID: string; partID: string };
|
|
284
|
+
const parts = input.store.part[props.messageID];
|
|
285
|
+
if (!parts) break;
|
|
286
|
+
const result = Binary.search(parts, props.partID, (p) => p.id);
|
|
228
287
|
if (result.found) {
|
|
229
288
|
input.setStore(
|
|
230
289
|
produce((draft) => {
|
|
231
|
-
const list = draft.part[props.messageID]
|
|
232
|
-
if (!list) return
|
|
233
|
-
const next = Binary.search(list, props.partID, (p) => p.id)
|
|
234
|
-
if (!next.found) return
|
|
235
|
-
list.splice(next.index, 1)
|
|
236
|
-
if (list.length === 0) delete draft.part[props.messageID]
|
|
290
|
+
const list = draft.part[props.messageID];
|
|
291
|
+
if (!list) return;
|
|
292
|
+
const next = Binary.search(list, props.partID, (p) => p.id);
|
|
293
|
+
if (!next.found) return;
|
|
294
|
+
list.splice(next.index, 1);
|
|
295
|
+
if (list.length === 0) delete draft.part[props.messageID];
|
|
237
296
|
}),
|
|
238
|
-
)
|
|
297
|
+
);
|
|
239
298
|
}
|
|
240
|
-
break
|
|
299
|
+
break;
|
|
241
300
|
}
|
|
242
301
|
case "message.part.delta": {
|
|
243
|
-
const props = event.properties as {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
302
|
+
const props = event.properties as {
|
|
303
|
+
messageID: string;
|
|
304
|
+
partID: string;
|
|
305
|
+
field: string;
|
|
306
|
+
delta: string;
|
|
307
|
+
};
|
|
308
|
+
const parts = input.store.part[props.messageID];
|
|
309
|
+
if (!parts) break;
|
|
310
|
+
const result = Binary.search(parts, props.partID, (p) => p.id);
|
|
311
|
+
if (!result.found) break;
|
|
248
312
|
input.setStore(
|
|
249
313
|
"part",
|
|
250
314
|
props.messageID,
|
|
251
315
|
produce((draft) => {
|
|
252
|
-
const part = draft[result.index]
|
|
253
|
-
const field = props.field as keyof typeof part
|
|
254
|
-
const existing = part[field] as string | undefined
|
|
255
|
-
|
|
316
|
+
const part = draft[result.index];
|
|
317
|
+
const field = props.field as keyof typeof part;
|
|
318
|
+
const existing = part[field] as string | undefined;
|
|
319
|
+
(part[field] as string) = (existing ?? "") + props.delta;
|
|
256
320
|
}),
|
|
257
|
-
)
|
|
258
|
-
break
|
|
321
|
+
);
|
|
322
|
+
break;
|
|
259
323
|
}
|
|
260
324
|
case "vcs.branch.updated": {
|
|
261
|
-
const props = event.properties as { branch: string }
|
|
262
|
-
if (input.store.vcs?.branch === props.branch) break
|
|
263
|
-
const next = { branch: props.branch }
|
|
264
|
-
input.setStore("vcs", next)
|
|
265
|
-
if (input.vcsCache) input.vcsCache.setStore("value", next)
|
|
266
|
-
break
|
|
325
|
+
const props = event.properties as { branch: string };
|
|
326
|
+
if (input.store.vcs?.branch === props.branch) break;
|
|
327
|
+
const next = { branch: props.branch };
|
|
328
|
+
input.setStore("vcs", next);
|
|
329
|
+
if (input.vcsCache) input.vcsCache.setStore("value", next);
|
|
330
|
+
break;
|
|
267
331
|
}
|
|
268
332
|
case "permission.asked": {
|
|
269
|
-
const permission = event.properties as PermissionRequest
|
|
270
|
-
const permissions = input.store.permission[permission.sessionID]
|
|
333
|
+
const permission = event.properties as PermissionRequest;
|
|
334
|
+
const permissions = input.store.permission[permission.sessionID];
|
|
271
335
|
if (!permissions) {
|
|
272
|
-
input.setStore("permission", permission.sessionID, [permission])
|
|
273
|
-
break
|
|
336
|
+
input.setStore("permission", permission.sessionID, [permission]);
|
|
337
|
+
break;
|
|
274
338
|
}
|
|
275
|
-
const result = Binary.search(permissions, permission.id, (p) => p.id)
|
|
339
|
+
const result = Binary.search(permissions, permission.id, (p) => p.id);
|
|
276
340
|
if (result.found) {
|
|
277
|
-
input.setStore(
|
|
278
|
-
|
|
341
|
+
input.setStore(
|
|
342
|
+
"permission",
|
|
343
|
+
permission.sessionID,
|
|
344
|
+
result.index,
|
|
345
|
+
reconcile(permission),
|
|
346
|
+
);
|
|
347
|
+
break;
|
|
279
348
|
}
|
|
280
349
|
input.setStore(
|
|
281
350
|
"permission",
|
|
282
351
|
permission.sessionID,
|
|
283
352
|
produce((draft) => {
|
|
284
|
-
draft.splice(result.index, 0, permission)
|
|
353
|
+
draft.splice(result.index, 0, permission);
|
|
285
354
|
}),
|
|
286
|
-
)
|
|
287
|
-
break
|
|
355
|
+
);
|
|
356
|
+
break;
|
|
288
357
|
}
|
|
289
358
|
case "permission.replied": {
|
|
290
|
-
const props = event.properties as {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
359
|
+
const props = event.properties as {
|
|
360
|
+
sessionID: string;
|
|
361
|
+
requestID: string;
|
|
362
|
+
};
|
|
363
|
+
const permissions = input.store.permission[props.sessionID];
|
|
364
|
+
if (!permissions) break;
|
|
365
|
+
const result = Binary.search(permissions, props.requestID, (p) => p.id);
|
|
366
|
+
if (!result.found) break;
|
|
295
367
|
input.setStore(
|
|
296
368
|
"permission",
|
|
297
369
|
props.sessionID,
|
|
298
370
|
produce((draft) => {
|
|
299
|
-
draft.splice(result.index, 1)
|
|
371
|
+
draft.splice(result.index, 1);
|
|
300
372
|
}),
|
|
301
|
-
)
|
|
302
|
-
break
|
|
373
|
+
);
|
|
374
|
+
break;
|
|
303
375
|
}
|
|
304
376
|
case "question.asked": {
|
|
305
|
-
const question = event.properties as QuestionRequest
|
|
306
|
-
const questions = input.store.question[question.sessionID]
|
|
377
|
+
const question = event.properties as QuestionRequest;
|
|
378
|
+
const questions = input.store.question[question.sessionID];
|
|
307
379
|
if (!questions) {
|
|
308
|
-
input.setStore("question", question.sessionID, [question])
|
|
309
|
-
break
|
|
380
|
+
input.setStore("question", question.sessionID, [question]);
|
|
381
|
+
break;
|
|
310
382
|
}
|
|
311
|
-
const result = Binary.search(questions, question.id, (q) => q.id)
|
|
383
|
+
const result = Binary.search(questions, question.id, (q) => q.id);
|
|
312
384
|
if (result.found) {
|
|
313
|
-
input.setStore(
|
|
314
|
-
|
|
385
|
+
input.setStore(
|
|
386
|
+
"question",
|
|
387
|
+
question.sessionID,
|
|
388
|
+
result.index,
|
|
389
|
+
reconcile(question),
|
|
390
|
+
);
|
|
391
|
+
break;
|
|
315
392
|
}
|
|
316
393
|
input.setStore(
|
|
317
394
|
"question",
|
|
318
395
|
question.sessionID,
|
|
319
396
|
produce((draft) => {
|
|
320
|
-
draft.splice(result.index, 0, question)
|
|
397
|
+
draft.splice(result.index, 0, question);
|
|
321
398
|
}),
|
|
322
|
-
)
|
|
323
|
-
break
|
|
399
|
+
);
|
|
400
|
+
break;
|
|
324
401
|
}
|
|
325
402
|
case "question.replied":
|
|
326
403
|
case "question.rejected": {
|
|
327
|
-
const props = event.properties as {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
404
|
+
const props = event.properties as {
|
|
405
|
+
sessionID: string;
|
|
406
|
+
requestID: string;
|
|
407
|
+
};
|
|
408
|
+
const questions = input.store.question[props.sessionID];
|
|
409
|
+
if (!questions) break;
|
|
410
|
+
const result = Binary.search(questions, props.requestID, (q) => q.id);
|
|
411
|
+
if (!result.found) break;
|
|
332
412
|
input.setStore(
|
|
333
413
|
"question",
|
|
334
414
|
props.sessionID,
|
|
335
415
|
produce((draft) => {
|
|
336
|
-
draft.splice(result.index, 1)
|
|
416
|
+
draft.splice(result.index, 1);
|
|
337
417
|
}),
|
|
338
|
-
)
|
|
339
|
-
break
|
|
418
|
+
);
|
|
419
|
+
break;
|
|
340
420
|
}
|
|
341
421
|
case "lsp.updated": {
|
|
342
|
-
input.loadLsp()
|
|
343
|
-
break
|
|
422
|
+
input.loadLsp();
|
|
423
|
+
break;
|
|
344
424
|
}
|
|
345
425
|
}
|
|
346
426
|
}
|