opencode-extended-sidebar 0.2.90

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.
Files changed (105) hide show
  1. package/.oesignore +17 -0
  2. package/CHANGELOG.md +95 -0
  3. package/LICENSE +21 -0
  4. package/README.md +235 -0
  5. package/oes.json +11 -0
  6. package/package.json +100 -0
  7. package/scripts/install-git-hooks.mjs +29 -0
  8. package/src/pware.oc.core/constants/index.ts +18 -0
  9. package/src/pware.oc.core/constants/pware.oc.core.constants.eventKind.ts +29 -0
  10. package/src/pware.oc.core/constants/pware.oc.core.constants.eventName.ts +13 -0
  11. package/src/pware.oc.core/constants/pware.oc.core.constants.eventType.ts +241 -0
  12. package/src/pware.oc.core/constants/pware.oc.core.constants.myWork.ts +55 -0
  13. package/src/pware.oc.core/constants/pware.oc.core.constants.partType.ts +65 -0
  14. package/src/pware.oc.core/constants/pware.oc.core.constants.phase.ts +58 -0
  15. package/src/pware.oc.core/constants/pware.oc.core.constants.pulse.ts +63 -0
  16. package/src/pware.oc.core/constants/pware.oc.core.constants.rowKind.ts +34 -0
  17. package/src/pware.oc.core/constants/pware.oc.core.constants.status.ts +74 -0
  18. package/src/pware.oc.core/constants/pware.oc.core.constants.toolName.ts +69 -0
  19. package/src/pware.oc.core/git/index.ts +7 -0
  20. package/src/pware.oc.core/git/pware.oc.core.git.ts +237 -0
  21. package/src/pware.oc.core/git/pware.oc.core.gitignore.ts +87 -0
  22. package/src/pware.oc.core/index.ts +18 -0
  23. package/src/pware.oc.core/pware.oc.core.bus.ts +42 -0
  24. package/src/pware.oc.core/pware.oc.core.cache.ts +48 -0
  25. package/src/pware.oc.core/pware.oc.core.clipboard.ts +97 -0
  26. package/src/pware.oc.core/pware.oc.core.debug.ts +280 -0
  27. package/src/pware.oc.core/pware.oc.core.events.ts +79 -0
  28. package/src/pware.oc.core/pware.oc.core.glyph.ts +135 -0
  29. package/src/pware.oc.core/pware.oc.core.layout.ts +234 -0
  30. package/src/pware.oc.core/pware.oc.core.oes.ts +89 -0
  31. package/src/pware.oc.core/pware.oc.core.paths.ts +240 -0
  32. package/src/pware.oc.core/pware.oc.core.preview.ts +74 -0
  33. package/src/pware.oc.core/pware.oc.core.pulse.ts +726 -0
  34. package/src/pware.oc.core/pware.oc.core.sqlite.ts +175 -0
  35. package/src/pware.oc.core/pware.oc.core.status.ts +214 -0
  36. package/src/pware.oc.core/pware.oc.core.timing.ts +63 -0
  37. package/src/pware.oc.core/pware.oc.core.width.ts +215 -0
  38. package/src/pware.oc.omo/constants/index.ts +15 -0
  39. package/src/pware.oc.omo/constants/pware.oc.omo.constants.backgroundTask.ts +18 -0
  40. package/src/pware.oc.omo/constants/pware.oc.omo.constants.boulderStatus.ts +49 -0
  41. package/src/pware.oc.omo/constants/pware.oc.omo.constants.docKind.ts +29 -0
  42. package/src/pware.oc.omo/constants/pware.oc.omo.constants.eventName.ts +11 -0
  43. package/src/pware.oc.omo/constants/pware.oc.omo.constants.planStatus.ts +60 -0
  44. package/src/pware.oc.omo/constants/pware.oc.omo.constants.reviewStatus.ts +49 -0
  45. package/src/pware.oc.omo/constants/pware.oc.omo.constants.startWork.ts +25 -0
  46. package/src/pware.oc.omo/constants/pware.oc.omo.constants.verdict.ts +38 -0
  47. package/src/pware.oc.omo/index.ts +7 -0
  48. package/src/pware.oc.omo/resolver/index.ts +19 -0
  49. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approval.ts +179 -0
  50. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approvalGroup.ts +116 -0
  51. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approvalState.ts +34 -0
  52. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.boulder.ts +503 -0
  53. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.config.ts +40 -0
  54. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.doc.ts +269 -0
  55. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.draftFile.ts +18 -0
  56. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.notepadsFile.ts +18 -0
  57. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.plan.ts +155 -0
  58. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.planFile.ts +361 -0
  59. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.proofFile.ts +18 -0
  60. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.rulesFile.ts +11 -0
  61. package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.runContinuationFile.ts +14 -0
  62. package/src/pware.oc.opencode/constants/index.ts +10 -0
  63. package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.eventName.ts +15 -0
  64. package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.fileTouch.ts +18 -0
  65. package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.questionKind.ts +45 -0
  66. package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.sessionStatus.ts +58 -0
  67. package/src/pware.oc.opencode/index.ts +8 -0
  68. package/src/pware.oc.opencode/pware.oc.opencode.events.ts +182 -0
  69. package/src/pware.oc.opencode/pware.oc.opencode.files.ts +399 -0
  70. package/src/pware.oc.opencode/resolver/index.ts +170 -0
  71. package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.file.ts +162 -0
  72. package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.question.ts +185 -0
  73. package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.session.ts +211 -0
  74. package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.todo.ts +26 -0
  75. package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.tool.ts +236 -0
  76. package/src/pware.oc.perf/index.ts +14 -0
  77. package/src/pware.oc.perf/pware.oc.perf.asciichart.d.ts +15 -0
  78. package/src/pware.oc.perf/pware.oc.perf.charts.ts +284 -0
  79. package/src/pware.oc.perf/pware.oc.perf.reader.ts +927 -0
  80. package/src/pware.oc.perf/pware.oc.perf.realtime.ts +88 -0
  81. package/src/pware.oc.perf/pware.oc.perf.realtimeBlock.ts +118 -0
  82. package/src/pware.oc.perf/pware.oc.perf.realtimeCpuRam.ts +79 -0
  83. package/src/pware.oc.perf/pware.oc.perf.realtimeSampler.ts +71 -0
  84. package/src/pware.oc.perf/pware.oc.perf.realtimeTimeline.ts +214 -0
  85. package/src/pware.oc.perf/pware.oc.perf.self.ts +179 -0
  86. package/src/pware.oc.perf/pware.oc.perf.view.tsx +462 -0
  87. package/src/pware.oc.runtime/index.ts +13 -0
  88. package/src/pware.oc.runtime/pware.oc.runtime.monitor.ts +142 -0
  89. package/src/pware.oc.runtime/pware.oc.runtime.mywork-enrich.ts +79 -0
  90. package/src/pware.oc.runtime/pware.oc.runtime.mywork.ts +253 -0
  91. package/src/pware.oc.runtime/pware.oc.runtime.questions.ts +95 -0
  92. package/src/pware.oc.runtime/pware.oc.runtime.snapshotClient.ts +129 -0
  93. package/src/pware.oc.runtime/pware.oc.runtime.source.ts +105 -0
  94. package/src/pware.oc.runtime/pware.oc.runtime.worker.ts +43 -0
  95. package/src/pware.oc.runtime/resolver/index.ts +224 -0
  96. package/src/pware.oc.runtime/resolver/pware.oc.runtime.resolver.delegate.ts +146 -0
  97. package/src/pware.oc.ui/index.ts +12 -0
  98. package/src/pware.oc.ui/pware.oc.ui.chrome.tsx +339 -0
  99. package/src/pware.oc.ui/pware.oc.ui.glyphs.tsx +144 -0
  100. package/src/pware.oc.ui/pware.oc.ui.host.tsx +111 -0
  101. package/src/pware.oc.ui/pware.oc.ui.live.tsx +82 -0
  102. package/src/pware.oc.ui/pware.oc.ui.menudialogs.tsx +843 -0
  103. package/src/pware.oc.ui/pware.oc.ui.sections.tsx +526 -0
  104. package/src/pware.oc.ui/pware.oc.ui.sidebar.tsx +1629 -0
  105. package/src/pware.oc.ui.tsx +98 -0
@@ -0,0 +1,284 @@
1
+ import { plot } from "asciichart"
2
+ import { chart, renderToString, sparkBar, sparkHistogram, sparkGauge, sparkDonut } from "@crafter/charts"
3
+ import { quantileSorted, sampleStandardDeviation } from "simple-statistics"
4
+
5
+ /**
6
+ * Pure series-pipeline helpers for the Perf charts.
7
+ *
8
+ * Stateless and self-contained: no TUI, host, db, or `core` imports — every
9
+ * helper turns a plain array (or string) into a plain array (or string), so
10
+ * each is unit-testable in isolation and safe to run on the render path.
11
+ *
12
+ * Todo 3 builds the chart/stat render helpers (`asciiTrend`, `shareBar`, …)
13
+ * on top of these in this same file.
14
+ */
15
+
16
+ /**
17
+ * Fill nulls so the series is dense: leading nulls become the first known
18
+ * value, trailing nulls become the last known value, and interior runs are
19
+ * linearly interpolated between the nearest known neighbours on each side.
20
+ * Empty input or all-null returns `[]`.
21
+ */
22
+ export function interpolateSeries(values: Array<number | null>): number[] {
23
+ if (values.length === 0) return []
24
+ const first = values.findIndex((v) => v !== null)
25
+ if (first < 0) return []
26
+
27
+ const out = new Array<number>(values.length)
28
+ let prevIdx = first
29
+ let prevVal = values[first]!
30
+
31
+ for (let i = 0; i < first; i += 1) out[i] = prevVal
32
+ out[first] = prevVal
33
+
34
+ for (let i = first + 1; i < values.length; i += 1) {
35
+ const v = values[i]
36
+ if (v === null) continue
37
+ for (let j = prevIdx + 1; j < i; j += 1) {
38
+ out[j] = prevVal + ((v - prevVal) * (j - prevIdx)) / (i - prevIdx)
39
+ }
40
+ out[i] = v
41
+ prevIdx = i
42
+ prevVal = v
43
+ }
44
+
45
+ for (let i = prevIdx + 1; i < values.length; i += 1) out[i] = prevVal
46
+
47
+ return out
48
+ }
49
+
50
+ /**
51
+ * Centered moving average over an odd window. Edge-clamped: at the ends the
52
+ * mean uses only the neighbours that exist, so the first and last points are
53
+ * averaged over fewer values. Length shorter than `window` returns a shallow
54
+ * copy unchanged.
55
+ */
56
+ export function smoothSeries(values: number[], window = 3): number[] {
57
+ if (values.length < window) return [...values]
58
+ const half = Math.floor(window / 2)
59
+ const out = new Array<number>(values.length)
60
+ for (let i = 0; i < values.length; i += 1) {
61
+ const lo = Math.max(0, i - half)
62
+ const hi = Math.min(values.length - 1, i + half)
63
+ let sum = 0
64
+ for (let j = lo; j <= hi; j += 1) sum += values[j]!
65
+ out[i] = sum / (hi - lo + 1)
66
+ }
67
+ return out
68
+ }
69
+
70
+ /**
71
+ * Bucket-average down to at most `width` points. When the series already fits
72
+ * (`values.length <= width`) a copy is returned unchanged. Each output point is
73
+ * the mean of its bucket; empty buckets (possible when `width` is 0) are never
74
+ * emitted.
75
+ */
76
+ export function downsampleAvg(values: number[], width: number): number[] {
77
+ if (values.length <= width || width < 1) return [...values]
78
+ const size = Math.ceil(values.length / width)
79
+ const out: number[] = []
80
+ for (let i = 0; i < values.length; i += size) {
81
+ const end = Math.min(i + size, values.length)
82
+ let sum = 0
83
+ for (let j = i; j < end; j += 1) sum += values[j]!
84
+ out.push(sum / (end - i))
85
+ }
86
+ return out
87
+ }
88
+
89
+ /**
90
+ * Strip ANSI SGR sequences (`\x1b[31m`, `\x1b[0m`, …), leaving the plain text.
91
+ * OpenTUI renders colours via props, never escape codes.
92
+ */
93
+ export function stripAnsi(s: string): string {
94
+ return s.replace(/\x1B\[[0-9;]*[A-Za-z]/g, "")
95
+ }
96
+
97
+ /**
98
+ * Chart y-axis label: an integer for `|x| ≥ 10`, else one decimal. Drops
99
+ * asciichart's default `toFixed(2)` trailing zeros (e.g. `4652.00` → `4652`).
100
+ */
101
+ export function axisLabel(x: number): string {
102
+ return Math.abs(x) >= 10 ? String(Math.round(x)) : x.toFixed(1)
103
+ }
104
+
105
+ /**
106
+ * Plain-ASCII trend line for a series: interpolate nulls → smooth (window 3)
107
+ * → downsample to at most `width` points → asciichart `plot`. No `colors`
108
+ * config is passed, so the output is ANSI-free. Y-axis labels use `axisLabel`
109
+ * (rounded) instead of asciichart's `toFixed(2)`. Empty or all-null input
110
+ * returns `""`. Optional `min` / `max` pin the y-domain (the OES dialog
111
+ * charts leave them unset so a high flat series does not draw a dummy zero
112
+ * baseline).
113
+ *
114
+ * Each output row starts with an 11-column min/max label plus asciichart's
115
+ * 3-column offset; joined length is plot-points + {@link ASCII_TREND_AXIS_COLS}.
116
+ * Callers that have a hard line budget (the dialog) must pass
117
+ * `asciiTrendPlotWidth(lineBudget)` as `width`, or the labels wrap onto the
118
+ * next row and read as a second series.
119
+ */
120
+ export const ASCII_TREND_AXIS_COLS = 13
121
+
122
+ /** Plot-point count that keeps an asciiTrend row within `lineWidth` columns. */
123
+ export function asciiTrendPlotWidth(lineWidth: number): number {
124
+ return Math.max(8, Math.floor(lineWidth) - ASCII_TREND_AXIS_COLS)
125
+ }
126
+
127
+ export function asciiTrend(
128
+ values: Array<number | null>,
129
+ opts: { width: number; height?: number; min?: number; max?: number },
130
+ ): string {
131
+ const dense = interpolateSeries(values)
132
+ if (dense.length === 0) return ""
133
+ const smoothed = smoothSeries(dense, 3)
134
+ const ds = downsampleAvg(smoothed, Math.max(1, opts.width))
135
+ const pad = " " // 11 spaces — asciichart's label field, keeps the axis aligned
136
+ return plot(ds, {
137
+ height: opts.height ?? 3,
138
+ format: (x: number) => (pad + axisLabel(x)).slice(-pad.length),
139
+ ...(opts.min !== undefined ? { min: opts.min } : {}),
140
+ ...(opts.max !== undefined ? { max: opts.max } : {}),
141
+ })
142
+ }
143
+
144
+ /**
145
+ * Single horizontal share bar (`█`-filled) for a fraction in `[0, 1]`.
146
+ * `null` renders an empty bar. Output is plain (no colour config passed).
147
+ */
148
+ export function shareBar(share: number | null, width: number): string {
149
+ return sparkBar(share ?? 0, 1, { width })
150
+ }
151
+
152
+ /**
153
+ * One summary line of a distribution: `p50`/`p95`/`p99` via `quantileSorted`
154
+ * and the sample standard deviation, each formatted through the injected
155
+ * `fmt`. Fewer than two finite values renders `${label} —`.
156
+ */
157
+ export function perfStatLine(
158
+ label: string,
159
+ values: Array<number | null>,
160
+ fmt: (n: number) => string,
161
+ ): string {
162
+ const known = values.filter(
163
+ (v): v is number => v != null && Number.isFinite(v),
164
+ )
165
+ if (known.length < 2) return `${label} —`
166
+ const sorted = [...known].sort((a, b) => a - b)
167
+ const p50 = quantileSorted(sorted, 0.5)
168
+ const p95 = quantileSorted(sorted, 0.95)
169
+ const p99 = quantileSorted(sorted, 0.99)
170
+ const sd = sampleStandardDeviation(known)
171
+ return `${label} p50 ${fmt(p50)} · p95 ${fmt(p95)} · p99 ${fmt(p99)} · σ ${fmt(sd)}`
172
+ }
173
+
174
+ /**
175
+ * Binned distribution histogram of the known (finite) values, with per-bin
176
+ * counts. Fewer than one known value returns `""`. Plain (no colour config).
177
+ */
178
+ export function waitHistogram(
179
+ values: Array<number | null>,
180
+ opts?: { width?: number; height?: number; bins?: number },
181
+ ): string {
182
+ const known = values.filter(
183
+ (v): v is number => v != null && Number.isFinite(v),
184
+ )
185
+ if (known.length < 1) return ""
186
+ return sparkHistogram(known, {
187
+ width: opts?.width ?? 60,
188
+ height: opts?.height ?? 8,
189
+ bins: opts?.bins ?? 10,
190
+ showCounts: true,
191
+ })
192
+ }
193
+
194
+ /**
195
+ * Radial gauge for a fraction in `[0, 1]`. `sparkGauge` colours internally;
196
+ * `stripAnsi` removes the escape codes so the returned string is ANSI-free.
197
+ */
198
+ export function shareGauge(
199
+ share: number | null,
200
+ opts?: { width?: number; label?: string },
201
+ ): string {
202
+ return stripAnsi(
203
+ sparkGauge(share ?? 0, 1, { width: opts?.width ?? 40, label: opts?.label }),
204
+ )
205
+ }
206
+
207
+ /**
208
+ * Donut for a fraction in `[0, 1]`, ANSI-stripped (see `shareGauge`).
209
+ */
210
+ export function shareDonut(
211
+ share: number | null,
212
+ opts?: { label?: string },
213
+ ): string {
214
+ return stripAnsi(sparkDonut(share ?? 0, 1, { label: opts?.label }))
215
+ }
216
+
217
+ /**
218
+ * Box line sparkline for the OES status bar's live token rate. Returns one
219
+ * string per row (empty input → `[]`), `height` rows tall (default 2). The
220
+ * series is first bucket-averaged to `width` (`downsampleAvg`) then smoothed
221
+ * (window 3) so a spiky raw rate renders as a continuous curve instead of an
222
+ * aliased sawtooth. Renders through `@crafter/charts` `chart().line()` (box
223
+ * charset), pins the y-domain to `[0, max]` (rates are never negative),
224
+ * and drops the 8-column y-axis gutter so the line spans the full requested
225
+ * width — the TUI colours it via the `fg` prop, no ANSI.
226
+ */
227
+ export function rateSparkline(
228
+ values: number[],
229
+ opts: { width: number; height?: number; charset?: "braille" | "block" | "ascii" | "box" },
230
+ ): string[] {
231
+ if (values.length === 0) return []
232
+ const height = Math.max(2, Math.round(opts.height ?? 2))
233
+ const downsampled = downsampleAvg(values, opts.width)
234
+ const smoothed = smoothSeries(downsampled, 3)
235
+ const max = Math.max(1, ...smoothed)
236
+ const rows = smoothed.map((v, i) => ({ x: i, y: v }))
237
+ const out = renderToString(
238
+ chart({ width: opts.width + 8, height, charset: opts.charset ?? "braille" })
239
+ .data(rows, { xKey: "x" })
240
+ .line({ key: "y" })
241
+ .yDomain([0, max]),
242
+ )
243
+ return out.split("\n").map((line) => line.slice(8))
244
+ }
245
+
246
+ /**
247
+ * One ASCII trend (`asciiTrend` / asciichart) per realtime series for the
248
+ * fullscreen Perf modal. Braille is reserved for the compact sidebar sparkline
249
+ * — `@crafter/charts` line marks always paint a braille canvas (even with
250
+ * `charset: "ascii"`), which reads as scattered dots at dialog size. Callers
251
+ * pick the body height (`RT_DIALOG_CHART_ROWS`). Each entry is independent;
252
+ * empty series are skipped. Y-domain follows the series (not pinned to
253
+ * `[0, max]`) so a high flat reading does not draw a dummy zero baseline that
254
+ * reads as a second series. `opts.width` is the **line budget** (dialog inner
255
+ * columns): plot points are `asciiTrendPlotWidth(width)` so the min/max labels
256
+ * at the start of each row stay on that row. Output is clipped to `width` as
257
+ * a last wrap guard. ANSI-free; the TUI colours it via the `fg` prop.
258
+ */
259
+ export function realtimeSeriesLines(
260
+ series: { key: string; label: string; unit: string; values: number[] }[],
261
+ opts: { width: number; height: number },
262
+ ): { key: string; label: string; unit: string; lines: string[] }[] {
263
+ const out: { key: string; label: string; unit: string; lines: string[] }[] = []
264
+ const lineWidth = Math.max(8, Math.floor(opts.width))
265
+ const plotWidth = asciiTrendPlotWidth(lineWidth)
266
+ const plotHeight = Math.max(1, opts.height - 1)
267
+ for (const s of series) {
268
+ if (s.values.length === 0) continue
269
+ const rendered = asciiTrend(s.values, {
270
+ width: plotWidth,
271
+ height: plotHeight,
272
+ })
273
+ const lines = rendered === "" ? [] : rendered.split("\n")
274
+ while (lines.length < opts.height) lines.push("")
275
+ if (lines.length > opts.height) lines.length = opts.height
276
+ out.push({
277
+ key: s.key,
278
+ label: s.label,
279
+ unit: s.unit,
280
+ lines: lines.map((line) => (line.length <= lineWidth ? line : line.slice(0, lineWidth))),
281
+ })
282
+ }
283
+ return out
284
+ }