opencode-extended-sidebar 0.2.90 → 0.2.91
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/.oesignore +17 -17
- package/CHANGELOG.md +96 -95
- package/LICENSE +21 -21
- package/README.md +150 -235
- package/oes.json +13 -11
- package/package.json +100 -100
- package/scripts/install-git-hooks.mjs +29 -29
- package/src/pware.oc.core/constants/index.ts +18 -18
- package/src/pware.oc.core/constants/pware.oc.core.constants.eventKind.ts +29 -29
- package/src/pware.oc.core/constants/pware.oc.core.constants.eventName.ts +15 -13
- package/src/pware.oc.core/constants/pware.oc.core.constants.eventType.ts +241 -241
- package/src/pware.oc.core/constants/pware.oc.core.constants.myWork.ts +55 -55
- package/src/pware.oc.core/constants/pware.oc.core.constants.partType.ts +65 -65
- package/src/pware.oc.core/constants/pware.oc.core.constants.phase.ts +58 -58
- package/src/pware.oc.core/constants/pware.oc.core.constants.pulse.ts +63 -63
- package/src/pware.oc.core/constants/pware.oc.core.constants.rowKind.ts +34 -34
- package/src/pware.oc.core/constants/pware.oc.core.constants.status.ts +74 -74
- package/src/pware.oc.core/constants/pware.oc.core.constants.toolName.ts +69 -69
- package/src/pware.oc.core/git/index.ts +7 -7
- package/src/pware.oc.core/git/pware.oc.core.git.ts +237 -237
- package/src/pware.oc.core/git/pware.oc.core.gitignore.ts +87 -87
- package/src/pware.oc.core/index.ts +19 -18
- package/src/pware.oc.core/pware.oc.core.bus.ts +42 -42
- package/src/pware.oc.core/pware.oc.core.cache.ts +48 -48
- package/src/pware.oc.core/pware.oc.core.clipboard.ts +97 -97
- package/src/pware.oc.core/pware.oc.core.debug.ts +280 -280
- package/src/pware.oc.core/pware.oc.core.events.ts +79 -79
- package/src/pware.oc.core/pware.oc.core.glyph.ts +135 -135
- package/src/pware.oc.core/pware.oc.core.layout.ts +234 -234
- package/src/pware.oc.core/pware.oc.core.oes.ts +99 -89
- package/src/pware.oc.core/pware.oc.core.paths.ts +240 -240
- package/src/pware.oc.core/pware.oc.core.preview.ts +74 -74
- package/src/pware.oc.core/pware.oc.core.pulse.ts +726 -726
- package/src/pware.oc.core/pware.oc.core.runtimeConfig.ts +41 -0
- package/src/pware.oc.core/pware.oc.core.sqlite.ts +164 -175
- package/src/pware.oc.core/pware.oc.core.sqliteGateway.ts +89 -0
- package/src/pware.oc.core/pware.oc.core.status.ts +214 -214
- package/src/pware.oc.core/pware.oc.core.timing.ts +63 -63
- package/src/pware.oc.core/pware.oc.core.width.ts +215 -215
- package/src/pware.oc.omo/constants/index.ts +15 -15
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.backgroundTask.ts +18 -18
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.boulderStatus.ts +49 -49
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.docKind.ts +29 -29
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.eventName.ts +11 -11
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.planStatus.ts +60 -60
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.reviewStatus.ts +49 -49
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.startWork.ts +25 -25
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.verdict.ts +38 -38
- package/src/pware.oc.omo/index.ts +7 -7
- package/src/pware.oc.omo/resolver/index.ts +19 -19
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approval.ts +179 -179
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approvalGroup.ts +116 -116
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approvalState.ts +34 -34
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.boulder.ts +503 -503
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.config.ts +40 -40
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.doc.ts +269 -269
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.draftFile.ts +18 -18
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.notepadsFile.ts +18 -18
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.plan.ts +155 -155
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.planFile.ts +363 -361
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.proofFile.ts +18 -18
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.rulesFile.ts +11 -11
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.runContinuationFile.ts +14 -14
- package/src/pware.oc.opencode/constants/index.ts +10 -10
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.eventName.ts +15 -15
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.fileTouch.ts +18 -18
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.questionKind.ts +45 -45
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.sessionStatus.ts +58 -58
- package/src/pware.oc.opencode/index.ts +8 -8
- package/src/pware.oc.opencode/pware.oc.opencode.events.ts +182 -182
- package/src/pware.oc.opencode/pware.oc.opencode.files.ts +399 -399
- package/src/pware.oc.opencode/resolver/index.ts +223 -170
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.file.ts +150 -162
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.question.ts +149 -185
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.session.ts +248 -211
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.todo.ts +22 -26
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.tool.ts +224 -236
- package/src/pware.oc.perf/index.ts +14 -14
- package/src/pware.oc.perf/pware.oc.perf.asciichart.d.ts +15 -15
- package/src/pware.oc.perf/pware.oc.perf.charts.ts +284 -284
- package/src/pware.oc.perf/pware.oc.perf.reader.ts +962 -927
- package/src/pware.oc.perf/pware.oc.perf.realtime.ts +88 -88
- package/src/pware.oc.perf/pware.oc.perf.realtimeBlock.ts +118 -118
- package/src/pware.oc.perf/pware.oc.perf.realtimeCpuRam.ts +79 -79
- package/src/pware.oc.perf/pware.oc.perf.realtimeSampler.ts +71 -71
- package/src/pware.oc.perf/pware.oc.perf.realtimeTimeline.ts +214 -214
- package/src/pware.oc.perf/pware.oc.perf.self.ts +179 -179
- package/src/pware.oc.perf/pware.oc.perf.view.tsx +463 -462
- package/src/pware.oc.runtime/index.ts +13 -13
- package/src/pware.oc.runtime/pware.oc.runtime.monitor.ts +142 -142
- package/src/pware.oc.runtime/pware.oc.runtime.mywork-enrich.ts +87 -79
- package/src/pware.oc.runtime/pware.oc.runtime.mywork.ts +282 -253
- package/src/pware.oc.runtime/pware.oc.runtime.omoRead.ts +37 -0
- package/src/pware.oc.runtime/pware.oc.runtime.questions.ts +83 -95
- package/src/pware.oc.runtime/pware.oc.runtime.snapshotClient.ts +130 -129
- package/src/pware.oc.runtime/pware.oc.runtime.source.ts +105 -105
- package/src/pware.oc.runtime/pware.oc.runtime.worker.ts +44 -43
- package/src/pware.oc.runtime/resolver/index.ts +268 -224
- package/src/pware.oc.runtime/resolver/pware.oc.runtime.resolver.delegate.ts +146 -146
- package/src/pware.oc.ui/index.ts +12 -12
- package/src/pware.oc.ui/pware.oc.ui.chrome.tsx +381 -339
- package/src/pware.oc.ui/pware.oc.ui.glyphs.tsx +144 -144
- package/src/pware.oc.ui/pware.oc.ui.host.tsx +111 -111
- package/src/pware.oc.ui/pware.oc.ui.live.tsx +82 -82
- package/src/pware.oc.ui/pware.oc.ui.menudialogs.tsx +843 -843
- package/src/pware.oc.ui/pware.oc.ui.sections.tsx +552 -526
- package/src/pware.oc.ui/pware.oc.ui.sidebar.tsx +1718 -1629
- package/src/pware.oc.ui.tsx +98 -98
|
@@ -1,339 +1,381 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/solid */
|
|
2
|
-
/** Shared sidebar chrome: brand tabs, theme colours, fold headers, diff stats, kv persistence. */
|
|
3
|
-
import { createSignal, For, Show, type JSX } from "solid-js"
|
|
4
|
-
import { TextAttributes } from "@opentui/core"
|
|
5
|
-
import type { TuiPluginApi } from "@opencode-ai/plugin/tui"
|
|
6
|
-
import type { GlyphSpec, ToneKey } from "../pware.oc.core/pware.oc.core.glyph.js"
|
|
7
|
-
import { CONTEXT_ACTION_COL_WIDTH } from "../pware.oc.core/pware.oc.core.layout.js"
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
{props.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
/** Shared sidebar chrome: brand tabs, theme colours, fold headers, diff stats, kv persistence. */
|
|
3
|
+
import { createSignal, For, Show, type JSX } from "solid-js"
|
|
4
|
+
import { TextAttributes } from "@opentui/core"
|
|
5
|
+
import type { TuiPluginApi } from "@opencode-ai/plugin/tui"
|
|
6
|
+
import type { GlyphSpec, ToneKey } from "../pware.oc.core/pware.oc.core.glyph.js"
|
|
7
|
+
import { CONTEXT_ACTION_COL_WIDTH } from "../pware.oc.core/pware.oc.core.layout.js"
|
|
8
|
+
import {
|
|
9
|
+
emptyRuntimeConfig,
|
|
10
|
+
parseRuntimeConfig,
|
|
11
|
+
pinSession,
|
|
12
|
+
unpinSession,
|
|
13
|
+
type RuntimeConfigDto,
|
|
14
|
+
} from "../pware.oc.core/pware.oc.core.runtimeConfig.js"
|
|
15
|
+
import { formatDismissed, parseDismissed } from "../pware.oc.runtime/pware.oc.runtime.mywork.js"
|
|
16
|
+
|
|
17
|
+
/** One clickable context action in the shared right rail. */
|
|
18
|
+
export type ContextAction = {
|
|
19
|
+
label: string
|
|
20
|
+
onPick: () => void
|
|
21
|
+
bold?: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** OpenTUI has no bold/underline props on `<text>` — only the `attributes` bitmask. */
|
|
25
|
+
export function textAttrs(bold?: boolean, underline?: boolean): number {
|
|
26
|
+
let a = 0
|
|
27
|
+
if (bold) a |= TextAttributes.BOLD
|
|
28
|
+
if (underline) a |= TextAttributes.UNDERLINE
|
|
29
|
+
return a
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Clickable label: underlined only while the pointer hovers it.
|
|
34
|
+
* OpenTUI fires `over`/`out` on hit-test change, so the underline
|
|
35
|
+
* tracks the mouse instead of being baked in. `underline` marks
|
|
36
|
+
* clickability; without it the label never underlines.
|
|
37
|
+
*/
|
|
38
|
+
export function ClickText(props: {
|
|
39
|
+
fg?: string
|
|
40
|
+
bold?: boolean
|
|
41
|
+
/** Clickable — the underline appears while the pointer is over the label. */
|
|
42
|
+
underline?: boolean
|
|
43
|
+
opacity?: number
|
|
44
|
+
onMouseUp?: () => void
|
|
45
|
+
children: JSX.Element
|
|
46
|
+
}): JSX.Element {
|
|
47
|
+
const [hovered, setHovered] = createSignal(false)
|
|
48
|
+
return (
|
|
49
|
+
<text
|
|
50
|
+
fg={props.fg}
|
|
51
|
+
opacity={props.opacity}
|
|
52
|
+
attributes={textAttrs(Boolean(props.bold), Boolean(props.underline) && hovered())}
|
|
53
|
+
onMouseUp={props.onMouseUp}
|
|
54
|
+
onMouseOver={() => setHovered(true)}
|
|
55
|
+
onMouseOut={() => setHovered(false)}
|
|
56
|
+
>
|
|
57
|
+
{props.children}
|
|
58
|
+
</text>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type ThemeColors = {
|
|
63
|
+
text: string
|
|
64
|
+
textMuted: string
|
|
65
|
+
success: string
|
|
66
|
+
primary?: string
|
|
67
|
+
warning?: string
|
|
68
|
+
error?: string
|
|
69
|
+
diffAdded?: string
|
|
70
|
+
diffRemoved?: string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function kvRead(api: TuiPluginApi, key: string, fallback: boolean): boolean {
|
|
74
|
+
try {
|
|
75
|
+
const kv = (api as TuiPluginApi & { kv?: { get: (k: string, d?: boolean) => boolean } }).kv
|
|
76
|
+
return kv?.get(key, fallback) ?? fallback
|
|
77
|
+
} catch {
|
|
78
|
+
return fallback
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function kvWrite(api: TuiPluginApi, key: string, value: boolean): void {
|
|
83
|
+
try {
|
|
84
|
+
const kv = (api as TuiPluginApi & { kv?: { set: (k: string, v: boolean) => void } }).kv
|
|
85
|
+
kv?.set(key, value)
|
|
86
|
+
} catch {
|
|
87
|
+
// no kv in older hosts
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Same store, string value. Anything outside `allowed` falls back. */
|
|
92
|
+
export function kvReadOne<T extends string>(
|
|
93
|
+
api: TuiPluginApi,
|
|
94
|
+
key: string,
|
|
95
|
+
fallback: T,
|
|
96
|
+
allowed: readonly T[],
|
|
97
|
+
): T {
|
|
98
|
+
try {
|
|
99
|
+
const kv = (api as TuiPluginApi & { kv?: { get: (k: string, d?: string) => unknown } }).kv
|
|
100
|
+
const raw = kv?.get(key, fallback)
|
|
101
|
+
return allowed.includes(raw as T) ? (raw as T) : fallback
|
|
102
|
+
} catch {
|
|
103
|
+
return fallback
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function kvWriteOne(api: TuiPluginApi, key: string, value: string): void {
|
|
108
|
+
try {
|
|
109
|
+
const kv = (api as TuiPluginApi & { kv?: { set: (k: string, v: string) => void } }).kv
|
|
110
|
+
kv?.set(key, value)
|
|
111
|
+
} catch {
|
|
112
|
+
// no kv in older hosts
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const KV_DISMISSED_QUESTIONS = "oes.dismissed.questions"
|
|
117
|
+
|
|
118
|
+
export function readDismissedQuestions(api: TuiPluginApi): ReadonlySet<string> {
|
|
119
|
+
try {
|
|
120
|
+
const kv = (api as TuiPluginApi & { kv?: { get: (k: string, d?: unknown) => unknown } }).kv
|
|
121
|
+
const raw = kv?.get(KV_DISMISSED_QUESTIONS, null)
|
|
122
|
+
return parseDismissed(typeof raw === "string" ? raw : null)
|
|
123
|
+
} catch {
|
|
124
|
+
return new Set()
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function dismissQuestion(api: TuiPluginApi, partId: string): void {
|
|
129
|
+
try {
|
|
130
|
+
const ids = new Set(readDismissedQuestions(api))
|
|
131
|
+
ids.add(partId)
|
|
132
|
+
const kv = (api as TuiPluginApi & { kv?: { set: (k: string, v: string) => void } }).kv
|
|
133
|
+
kv?.set(KV_DISMISSED_QUESTIONS, formatDismissed(ids))
|
|
134
|
+
} catch {
|
|
135
|
+
// no kv in older hosts
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const KV_RUNTIME_CONFIG = "oes.config"
|
|
140
|
+
|
|
141
|
+
type TuiKv = {
|
|
142
|
+
get: <T>(key: string, fallback?: T) => T
|
|
143
|
+
set: (key: string, value: unknown) => void
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Host kv-backed pinned-sessions manager: pin/unpin mutate the `oes.config` DTO. */
|
|
147
|
+
export function createPinnedSessions(api: TuiPluginApi): {
|
|
148
|
+
list: () => string[]
|
|
149
|
+
pin: (sessionId: string) => void
|
|
150
|
+
unpin: (sessionId: string) => void
|
|
151
|
+
} {
|
|
152
|
+
const kv = () => (api as TuiPluginApi & { kv?: TuiKv }).kv
|
|
153
|
+
const read = (): RuntimeConfigDto => {
|
|
154
|
+
try {
|
|
155
|
+
return parseRuntimeConfig(kv()?.get(KV_RUNTIME_CONFIG, undefined))
|
|
156
|
+
} catch {
|
|
157
|
+
return emptyRuntimeConfig()
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const write = (config: RuntimeConfigDto): void => {
|
|
161
|
+
try {
|
|
162
|
+
kv()?.set(KV_RUNTIME_CONFIG, config)
|
|
163
|
+
} catch {
|
|
164
|
+
// no kv in older hosts
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
list: () => read().pinned_sessions,
|
|
169
|
+
pin: (sessionId) => write(pinSession(read(), sessionId)),
|
|
170
|
+
unpin: (sessionId) => write(unpinSession(read(), sessionId)),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** The single mapping from a semantic tone to a concrete theme colour. */
|
|
175
|
+
export function toneColor(tone: ToneKey, colors: ThemeColors): string {
|
|
176
|
+
switch (tone) {
|
|
177
|
+
case "success":
|
|
178
|
+
return colors.success
|
|
179
|
+
case "warning":
|
|
180
|
+
return colors.warning || colors.text
|
|
181
|
+
case "primary":
|
|
182
|
+
return colors.primary || colors.text
|
|
183
|
+
case "error":
|
|
184
|
+
return colors.error || colors.text
|
|
185
|
+
case "text":
|
|
186
|
+
return colors.text
|
|
187
|
+
default:
|
|
188
|
+
return colors.textMuted
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function diffAddFg(colors: ThemeColors): string {
|
|
193
|
+
return colors.diffAdded || colors.success
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function diffDelFg(colors: ThemeColors): string {
|
|
197
|
+
return colors.diffRemoved || colors.error || colors.text
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function DiffStat(props: {
|
|
201
|
+
additions: number
|
|
202
|
+
deletions: number
|
|
203
|
+
colors: ThemeColors
|
|
204
|
+
}): JSX.Element {
|
|
205
|
+
const lead = () => (props.additions > 0 && props.deletions > 0 ? " " : "")
|
|
206
|
+
return (
|
|
207
|
+
<box flexDirection="row">
|
|
208
|
+
<Show when={props.additions > 0}>
|
|
209
|
+
<text fg={diffAddFg(props.colors)}>{`+${props.additions}`}</text>
|
|
210
|
+
</Show>
|
|
211
|
+
<Show when={props.deletions > 0}>
|
|
212
|
+
<text fg={diffDelFg(props.colors)}>{`${lead()}−${props.deletions}`}</text>
|
|
213
|
+
</Show>
|
|
214
|
+
</box>
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Right-rail action labels — fold headers, realtime `F`, and any future row
|
|
220
|
+
* actions share {@link CONTEXT_ACTION_COL_WIDTH} so the column lines up.
|
|
221
|
+
*/
|
|
222
|
+
export function ContextActions(props: {
|
|
223
|
+
actions: readonly ContextAction[]
|
|
224
|
+
colors: ThemeColors
|
|
225
|
+
width?: number
|
|
226
|
+
}): JSX.Element {
|
|
227
|
+
return (
|
|
228
|
+
<box
|
|
229
|
+
flexDirection="row"
|
|
230
|
+
width={props.width ?? CONTEXT_ACTION_COL_WIDTH}
|
|
231
|
+
justifyContent="flex-end"
|
|
232
|
+
flexShrink={0}
|
|
233
|
+
>
|
|
234
|
+
<For each={props.actions}>
|
|
235
|
+
{(action, i) => (
|
|
236
|
+
<>
|
|
237
|
+
<Show when={i() > 0}>
|
|
238
|
+
<text> </text>
|
|
239
|
+
</Show>
|
|
240
|
+
<ClickText
|
|
241
|
+
fg={props.colors.primary || props.colors.text}
|
|
242
|
+
bold={action.bold}
|
|
243
|
+
underline
|
|
244
|
+
onMouseUp={action.onPick}
|
|
245
|
+
>
|
|
246
|
+
{action.label}
|
|
247
|
+
</ClickText>
|
|
248
|
+
</>
|
|
249
|
+
)}
|
|
250
|
+
</For>
|
|
251
|
+
</box>
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** Header text for a fold row: title, optional `(N)` / `(live/N)` / `(countLabel)` parenthetical, optional suffix. */
|
|
256
|
+
export function foldHeaderTitle(
|
|
257
|
+
title: string,
|
|
258
|
+
opts: { count?: number; live?: number; countLabel?: string; suffix?: string } = {},
|
|
259
|
+
): string {
|
|
260
|
+
const { count, live = 0, countLabel, suffix } = opts
|
|
261
|
+
const stat = suffix ? ` ${suffix}` : ""
|
|
262
|
+
if (typeof count !== "number") return `${title}${stat}`
|
|
263
|
+
const extra = countLabel
|
|
264
|
+
? ` (${countLabel})`
|
|
265
|
+
: live > 0
|
|
266
|
+
? ` (${live}/${count})`
|
|
267
|
+
: ` (${count})`
|
|
268
|
+
return `${title}${extra}${stat}`
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function FoldHeader(props: {
|
|
272
|
+
title: string
|
|
273
|
+
open: boolean
|
|
274
|
+
count?: number
|
|
275
|
+
live?: number
|
|
276
|
+
/** Overrides the `(N)` / `(live/N)` parenthetical, e.g. `last 4`. */
|
|
277
|
+
countLabel?: string
|
|
278
|
+
suffix?: string
|
|
279
|
+
diff?: { additions: number; deletions: number }
|
|
280
|
+
colors: ThemeColors
|
|
281
|
+
onToggle: () => void
|
|
282
|
+
/** Title click opens a detail (fold stays on the chevron). */
|
|
283
|
+
onDetail?: () => void
|
|
284
|
+
/** Context actions in the shared right rail (`switch` / `new`, `view all`, …). */
|
|
285
|
+
actions?: readonly ContextAction[]
|
|
286
|
+
}): JSX.Element {
|
|
287
|
+
const rest = () => foldHeaderTitle(props.title, props)
|
|
288
|
+
const hasDiff = () =>
|
|
289
|
+
Boolean(props.diff && (props.diff.additions > 0 || props.diff.deletions > 0))
|
|
290
|
+
const chevron = () => (props.open ? "▼" : "▶")
|
|
291
|
+
const actions = () => props.actions ?? []
|
|
292
|
+
const split = Boolean(props.onDetail || actions().length > 0)
|
|
293
|
+
return (
|
|
294
|
+
<box flexDirection="row" width="100%" onMouseUp={split ? undefined : props.onToggle}>
|
|
295
|
+
<box flexDirection="row" flexGrow={1} flexShrink={1} minWidth={0}>
|
|
296
|
+
<ClickText
|
|
297
|
+
fg={props.colors.text}
|
|
298
|
+
underline={!props.onDetail}
|
|
299
|
+
onMouseUp={split ? props.onToggle : undefined}
|
|
300
|
+
>
|
|
301
|
+
{props.onDetail ? `${chevron()} ` : `${chevron()} ${rest()}`}
|
|
302
|
+
</ClickText>
|
|
303
|
+
<Show when={props.onDetail}>
|
|
304
|
+
<ClickText fg={props.colors.text} underline onMouseUp={props.onDetail}>
|
|
305
|
+
{rest()}
|
|
306
|
+
</ClickText>
|
|
307
|
+
</Show>
|
|
308
|
+
<Show when={hasDiff()}>
|
|
309
|
+
<text> </text>
|
|
310
|
+
<DiffStat
|
|
311
|
+
additions={props.diff?.additions ?? 0}
|
|
312
|
+
deletions={props.diff?.deletions ?? 0}
|
|
313
|
+
colors={props.colors}
|
|
314
|
+
/>
|
|
315
|
+
</Show>
|
|
316
|
+
</box>
|
|
317
|
+
<Show when={actions().length > 0}>
|
|
318
|
+
<ContextActions actions={actions()} colors={props.colors} />
|
|
319
|
+
</Show>
|
|
320
|
+
</box>
|
|
321
|
+
)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Brand + clickable tabs. Active tab is bold + primary; clickable labels
|
|
326
|
+
* underline on hover. `onBrand` makes the brand itself a control — the OMO
|
|
327
|
+
* group folds on it.
|
|
328
|
+
*/
|
|
329
|
+
export function BrandTabs(props: {
|
|
330
|
+
brand: string
|
|
331
|
+
tabs: readonly string[]
|
|
332
|
+
labels: Record<string, string>
|
|
333
|
+
active: string | null
|
|
334
|
+
colors: ThemeColors
|
|
335
|
+
onPick: (tab: string) => void
|
|
336
|
+
onBrand?: () => void
|
|
337
|
+
/**
|
|
338
|
+
* Per-tab leading glyph replacing the `|` separator between tabs — the tab's
|
|
339
|
+
* status light. The glyph doubles as spacing, so headers read `• My work • Session …`.
|
|
340
|
+
*/
|
|
341
|
+
glyph?: (tab: string) => GlyphSpec
|
|
342
|
+
}): JSX.Element {
|
|
343
|
+
const brand = () => props.colors.primary || props.colors.text
|
|
344
|
+
const tabFg = (tab: string) =>
|
|
345
|
+
props.active === tab ? props.colors.primary || props.colors.text : props.colors.textMuted
|
|
346
|
+
return (
|
|
347
|
+
<box flexDirection="row" gap={1}>
|
|
348
|
+
<Show when={props.brand}>
|
|
349
|
+
<box flexDirection="row" onMouseUp={props.onBrand}>
|
|
350
|
+
<ClickText fg={brand()} bold underline={Boolean(props.onBrand)}>
|
|
351
|
+
{props.brand}
|
|
352
|
+
</ClickText>
|
|
353
|
+
</box>
|
|
354
|
+
</Show>
|
|
355
|
+
<For each={props.tabs}>
|
|
356
|
+
{(tab: string, i) => {
|
|
357
|
+
// Read the glyph through an accessor invoked inside the JSX, never a
|
|
358
|
+
// plain body const: SolidJS only re-runs JSX expressions, so the
|
|
359
|
+
// My-work tab light (a live memo) updates only when it is read at the
|
|
360
|
+
// expression site. A body-level `const glyph = props.glyph(tab)` would
|
|
361
|
+
// freeze the header at its first render.
|
|
362
|
+
const glyph = () => (props.glyph ? props.glyph(tab) : null)
|
|
363
|
+
return (
|
|
364
|
+
<box flexDirection="row" gap={1} onMouseUp={() => props.onPick(tab)}>
|
|
365
|
+
{glyph() ? (
|
|
366
|
+
<text fg={toneColor(glyph()!.tone, props.colors)}>{`${glyph()!.char} `}</text>
|
|
367
|
+
) : (
|
|
368
|
+
<Show when={Boolean(props.brand) || i() > 0}>
|
|
369
|
+
<text fg={props.colors.textMuted}>|</text>
|
|
370
|
+
</Show>
|
|
371
|
+
)}
|
|
372
|
+
<ClickText fg={tabFg(tab)} bold={props.active === tab} underline>
|
|
373
|
+
{props.labels[tab] ?? tab}
|
|
374
|
+
</ClickText>
|
|
375
|
+
</box>
|
|
376
|
+
)
|
|
377
|
+
}}
|
|
378
|
+
</For>
|
|
379
|
+
</box>
|
|
380
|
+
)
|
|
381
|
+
}
|