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,79 +1,79 @@
|
|
|
1
|
-
/** Host event type + kind. Never reads request or response bodies. */
|
|
2
|
-
import {
|
|
3
|
-
EVENT_FILE_EDITED,
|
|
4
|
-
EVENT_MESSAGE_PART_UPDATED,
|
|
5
|
-
EVENT_PART_DELTA,
|
|
6
|
-
EVENT_REASONING_DELTA,
|
|
7
|
-
EVENT_REASONING_STARTED,
|
|
8
|
-
EVENT_SESSION_CREATED,
|
|
9
|
-
EVENT_SESSION_DIFF,
|
|
10
|
-
EVENT_SESSION_IDLE,
|
|
11
|
-
EVENT_SESSION_STATUS,
|
|
12
|
-
EVENT_SESSION_UPDATED,
|
|
13
|
-
EVENT_STEP_ENDED,
|
|
14
|
-
EVENT_STEP_FAILED,
|
|
15
|
-
EVENT_STEP_STARTED,
|
|
16
|
-
EVENT_TEXT_DELTA,
|
|
17
|
-
EVENT_TOOL_CALLED,
|
|
18
|
-
EVENT_TOOL_ENDED,
|
|
19
|
-
EVENT_TOOL_FAILED,
|
|
20
|
-
EVENT_TOOL_SUCCESS,
|
|
21
|
-
} from "./constants/pware.oc.core.constants.eventType.js"
|
|
22
|
-
|
|
23
|
-
import {
|
|
24
|
-
EVENT_KIND_DB_REFRESH,
|
|
25
|
-
EVENT_KIND_FILE,
|
|
26
|
-
EVENT_KIND_FLOW,
|
|
27
|
-
EVENT_KIND_TOOL,
|
|
28
|
-
type EventKind,
|
|
29
|
-
} from "./constants/pware.oc.core.constants.eventKind.js"
|
|
30
|
-
|
|
31
|
-
export type { EventKind }
|
|
32
|
-
|
|
33
|
-
export function eventType(evt: unknown): string {
|
|
34
|
-
if (!evt || typeof evt !== "object") return ""
|
|
35
|
-
return String((evt as Record<string, unknown>).type ?? "").toLowerCase()
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function eventKind(type: string): EventKind | null {
|
|
39
|
-
const t = (type || "").toLowerCase()
|
|
40
|
-
if (!t) return null
|
|
41
|
-
if (t.includes(EVENT_FILE_EDITED) || t.includes(EVENT_SESSION_DIFF)) return EVENT_KIND_FILE
|
|
42
|
-
if (
|
|
43
|
-
t.includes(EVENT_TOOL_CALLED) ||
|
|
44
|
-
t.includes(EVENT_TOOL_SUCCESS) ||
|
|
45
|
-
t.includes(EVENT_TOOL_FAILED) ||
|
|
46
|
-
t.includes(EVENT_TOOL_ENDED)
|
|
47
|
-
) {
|
|
48
|
-
return EVENT_KIND_TOOL
|
|
49
|
-
}
|
|
50
|
-
if (
|
|
51
|
-
t.includes(EVENT_SESSION_STATUS) ||
|
|
52
|
-
t.includes(EVENT_SESSION_IDLE) ||
|
|
53
|
-
t.includes(EVENT_SESSION_CREATED) ||
|
|
54
|
-
t.includes(EVENT_SESSION_UPDATED) ||
|
|
55
|
-
t.includes(EVENT_MESSAGE_PART_UPDATED) ||
|
|
56
|
-
t.includes(EVENT_STEP_STARTED) ||
|
|
57
|
-
t.includes(EVENT_STEP_ENDED) ||
|
|
58
|
-
t.includes(EVENT_STEP_FAILED)
|
|
59
|
-
) {
|
|
60
|
-
return EVENT_KIND_DB_REFRESH
|
|
61
|
-
}
|
|
62
|
-
if (
|
|
63
|
-
t.includes(EVENT_TEXT_DELTA) ||
|
|
64
|
-
t.includes(EVENT_REASONING_DELTA) ||
|
|
65
|
-
t.includes(EVENT_REASONING_STARTED) ||
|
|
66
|
-
t.includes(EVENT_PART_DELTA) ||
|
|
67
|
-
t.endsWith(".delta")
|
|
68
|
-
) {
|
|
69
|
-
return EVENT_KIND_FLOW
|
|
70
|
-
}
|
|
71
|
-
return null
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function shouldRefreshDb(type: string): boolean {
|
|
75
|
-
const t = (type || "").toLowerCase()
|
|
76
|
-
if (!t || t.includes(".delta")) return false
|
|
77
|
-
const kind = eventKind(t)
|
|
78
|
-
return kind === EVENT_KIND_TOOL || kind === EVENT_KIND_FILE || kind === EVENT_KIND_DB_REFRESH
|
|
79
|
-
}
|
|
1
|
+
/** Host event type + kind. Never reads request or response bodies. */
|
|
2
|
+
import {
|
|
3
|
+
EVENT_FILE_EDITED,
|
|
4
|
+
EVENT_MESSAGE_PART_UPDATED,
|
|
5
|
+
EVENT_PART_DELTA,
|
|
6
|
+
EVENT_REASONING_DELTA,
|
|
7
|
+
EVENT_REASONING_STARTED,
|
|
8
|
+
EVENT_SESSION_CREATED,
|
|
9
|
+
EVENT_SESSION_DIFF,
|
|
10
|
+
EVENT_SESSION_IDLE,
|
|
11
|
+
EVENT_SESSION_STATUS,
|
|
12
|
+
EVENT_SESSION_UPDATED,
|
|
13
|
+
EVENT_STEP_ENDED,
|
|
14
|
+
EVENT_STEP_FAILED,
|
|
15
|
+
EVENT_STEP_STARTED,
|
|
16
|
+
EVENT_TEXT_DELTA,
|
|
17
|
+
EVENT_TOOL_CALLED,
|
|
18
|
+
EVENT_TOOL_ENDED,
|
|
19
|
+
EVENT_TOOL_FAILED,
|
|
20
|
+
EVENT_TOOL_SUCCESS,
|
|
21
|
+
} from "./constants/pware.oc.core.constants.eventType.js"
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
EVENT_KIND_DB_REFRESH,
|
|
25
|
+
EVENT_KIND_FILE,
|
|
26
|
+
EVENT_KIND_FLOW,
|
|
27
|
+
EVENT_KIND_TOOL,
|
|
28
|
+
type EventKind,
|
|
29
|
+
} from "./constants/pware.oc.core.constants.eventKind.js"
|
|
30
|
+
|
|
31
|
+
export type { EventKind }
|
|
32
|
+
|
|
33
|
+
export function eventType(evt: unknown): string {
|
|
34
|
+
if (!evt || typeof evt !== "object") return ""
|
|
35
|
+
return String((evt as Record<string, unknown>).type ?? "").toLowerCase()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function eventKind(type: string): EventKind | null {
|
|
39
|
+
const t = (type || "").toLowerCase()
|
|
40
|
+
if (!t) return null
|
|
41
|
+
if (t.includes(EVENT_FILE_EDITED) || t.includes(EVENT_SESSION_DIFF)) return EVENT_KIND_FILE
|
|
42
|
+
if (
|
|
43
|
+
t.includes(EVENT_TOOL_CALLED) ||
|
|
44
|
+
t.includes(EVENT_TOOL_SUCCESS) ||
|
|
45
|
+
t.includes(EVENT_TOOL_FAILED) ||
|
|
46
|
+
t.includes(EVENT_TOOL_ENDED)
|
|
47
|
+
) {
|
|
48
|
+
return EVENT_KIND_TOOL
|
|
49
|
+
}
|
|
50
|
+
if (
|
|
51
|
+
t.includes(EVENT_SESSION_STATUS) ||
|
|
52
|
+
t.includes(EVENT_SESSION_IDLE) ||
|
|
53
|
+
t.includes(EVENT_SESSION_CREATED) ||
|
|
54
|
+
t.includes(EVENT_SESSION_UPDATED) ||
|
|
55
|
+
t.includes(EVENT_MESSAGE_PART_UPDATED) ||
|
|
56
|
+
t.includes(EVENT_STEP_STARTED) ||
|
|
57
|
+
t.includes(EVENT_STEP_ENDED) ||
|
|
58
|
+
t.includes(EVENT_STEP_FAILED)
|
|
59
|
+
) {
|
|
60
|
+
return EVENT_KIND_DB_REFRESH
|
|
61
|
+
}
|
|
62
|
+
if (
|
|
63
|
+
t.includes(EVENT_TEXT_DELTA) ||
|
|
64
|
+
t.includes(EVENT_REASONING_DELTA) ||
|
|
65
|
+
t.includes(EVENT_REASONING_STARTED) ||
|
|
66
|
+
t.includes(EVENT_PART_DELTA) ||
|
|
67
|
+
t.endsWith(".delta")
|
|
68
|
+
) {
|
|
69
|
+
return EVENT_KIND_FLOW
|
|
70
|
+
}
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function shouldRefreshDb(type: string): boolean {
|
|
75
|
+
const t = (type || "").toLowerCase()
|
|
76
|
+
if (!t || t.includes(".delta")) return false
|
|
77
|
+
const kind = eventKind(t)
|
|
78
|
+
return kind === EVENT_KIND_TOOL || kind === EVENT_KIND_FILE || kind === EVENT_KIND_DB_REFRESH
|
|
79
|
+
}
|
|
@@ -1,135 +1,135 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pware.oc.core.glyph
|
|
3
|
-
*
|
|
4
|
-
* Every visual glyph as one complete definition: a character plus a semantic
|
|
5
|
-
* tone (and an optional blink flag). A glyph's char and colour can never drift
|
|
6
|
-
* apart because they come from the same switch. `markTone` / `stateGlyph` /
|
|
7
|
-
* `directionGlyph` are pure and theme-independent — the ui layer resolves a
|
|
8
|
-
* `ToneKey` to an actual RGBA through `toneColor` in
|
|
9
|
-
* `pware.oc.ui/pware.oc.ui.chrome.tsx`.
|
|
10
|
-
*/
|
|
11
|
-
import { BLINK_TICKS } from "./pware.oc.core.timing.js"
|
|
12
|
-
import {
|
|
13
|
-
FLOW_RECV,
|
|
14
|
-
FLOW_TOOL,
|
|
15
|
-
FLOW_WAIT,
|
|
16
|
-
MARK_QUEUED,
|
|
17
|
-
PULSE_LIVE,
|
|
18
|
-
PULSE_STALE,
|
|
19
|
-
type AgentMark,
|
|
20
|
-
type FlowDir,
|
|
21
|
-
} from "./constants/pware.oc.core.constants.pulse.js"
|
|
22
|
-
import { STATUS_ERROR } from "./constants/pware.oc.core.constants.status.js"
|
|
23
|
-
import {
|
|
24
|
-
ROW_KIND_FILE,
|
|
25
|
-
ROW_KIND_GROUP,
|
|
26
|
-
type RowKind,
|
|
27
|
-
} from "./constants/pware.oc.core.constants.rowKind.js"
|
|
28
|
-
|
|
29
|
-
/** Semantic colour of a glyph or body — no theme values, just meaning. */
|
|
30
|
-
export type ToneKey = "text" | "textMuted" | "success" | "primary" | "warning" | "error"
|
|
31
|
-
|
|
32
|
-
/** One glyph as a complete definition: the character, its tone, and blink. */
|
|
33
|
-
export type GlyphSpec = {
|
|
34
|
-
char: string
|
|
35
|
-
tone: ToneKey
|
|
36
|
-
blink?: boolean
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Same braille set as OpenCode TUI thinking spinner (`opentui-spinner`). */
|
|
40
|
-
export const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const
|
|
41
|
-
|
|
42
|
-
/** Queued — waiting for a slot: the hourglass (the wait flow owns ◷). */
|
|
43
|
-
export const QUEUED_GLYPH = "⧗"
|
|
44
|
-
|
|
45
|
-
export function spinnerFrame(frame: number): string {
|
|
46
|
-
const i = ((frame % SPINNER_FRAMES.length) + SPINNER_FRAMES.length) % SPINNER_FRAMES.length
|
|
47
|
-
return SPINNER_FRAMES[i] ?? "⠋"
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Cold-start "engage" bar length in glyph frames (GLYPH_TICK_MS × 12 ≈ 1s). */
|
|
51
|
-
export const ENGAGE_MIN_FRAMES = 12
|
|
52
|
-
/** Hard ceiling so the boot bar can never strand (GLYPH_TICK_MS × 50 = 4s). */
|
|
53
|
-
export const ENGAGE_MAX_FRAMES = 50
|
|
54
|
-
|
|
55
|
-
/** Fill ratio of the cold-start engage bar: linear to 1 at `ENGAGE_MIN_FRAMES`. */
|
|
56
|
-
export function engageFill(frame: number): number {
|
|
57
|
-
return Math.min(1, Math.max(0, frame) / ENGAGE_MIN_FRAMES)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* The cold-start engage bar ends once the first real snapshot has landed
|
|
62
|
-
* (`ready`) after its minimum show time — or on the hard ceiling (no DB / no
|
|
63
|
-
* snapshot ever) so the row can never strand. Negative frames count as 0.
|
|
64
|
-
*/
|
|
65
|
-
export function engageDone(frame: number, ready: boolean): boolean {
|
|
66
|
-
const n = Math.max(0, frame)
|
|
67
|
-
return (ready && n >= ENGAGE_MIN_FRAMES) || n >= ENGAGE_MAX_FRAMES
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** Options for {@link sinPulseAlpha} — one full fade cycle spans `periodFrames` tick frames. */
|
|
71
|
-
export type SinPulseAlphaOpts = {
|
|
72
|
-
periodFrames?: number
|
|
73
|
-
min?: number
|
|
74
|
-
max?: number
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Sinusoidal fade pulse for glyph opacity: alpha eases `min → max → min` over one
|
|
79
|
-
* period. Defaults to `BLINK_TICKS * 2` frames (same wall-clock span as
|
|
80
|
-
* {@link flowBlinkOn}). Pure — inject the UI tick frame; the ui layer maps the
|
|
81
|
-
* result to OpenTUI `opacity` or a colour lerp.
|
|
82
|
-
*/
|
|
83
|
-
export function sinPulseAlpha(frame: number, opts: SinPulseAlphaOpts = {}): number {
|
|
84
|
-
const period = Math.max(1, Math.round(opts.periodFrames ?? BLINK_TICKS * 2))
|
|
85
|
-
const lo = Math.min(opts.min ?? 0.25, opts.max ?? 1)
|
|
86
|
-
const hi = Math.max(opts.min ?? 0.25, opts.max ?? 1)
|
|
87
|
-
const phase = (Math.abs(frame) % period) / period
|
|
88
|
-
const wave = Math.sin(phase * Math.PI)
|
|
89
|
-
return lo + wave * (hi - lo)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** ↑/↓ blink half-period in ticks (TICK_MS × BLINK_TICKS = 100ms). */
|
|
93
|
-
export function flowBlinkOn(frame: number): boolean {
|
|
94
|
-
return Math.floor(Math.abs(frame) / BLINK_TICKS) % 2 === 0
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** The tone a state mark carries: error red, queued/stale warning, live success, else muted. */
|
|
98
|
-
export function markTone(mark: AgentMark): ToneKey {
|
|
99
|
-
if (mark === STATUS_ERROR) return "error"
|
|
100
|
-
if (mark === MARK_QUEUED || mark === PULSE_STALE) return "warning"
|
|
101
|
-
if (mark === PULSE_LIVE) return "success"
|
|
102
|
-
return "textMuted"
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** The state glyph for an agent mark — spinner, cross, hourglass or dot, in one spec. */
|
|
106
|
-
export function stateGlyph(mark: AgentMark, frame = 0): GlyphSpec {
|
|
107
|
-
const char =
|
|
108
|
-
mark === STATUS_ERROR
|
|
109
|
-
? "×"
|
|
110
|
-
: mark === MARK_QUEUED
|
|
111
|
-
? QUEUED_GLYPH
|
|
112
|
-
: mark === PULSE_LIVE || mark === PULSE_STALE
|
|
113
|
-
? spinnerFrame(frame)
|
|
114
|
-
: "•"
|
|
115
|
-
return { char, tone: markTone(mark) }
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** The direction glyph for a flow — ◷ waiting, ← receiving, → tool; null when quiet. */
|
|
119
|
-
export function directionGlyph(flow: FlowDir | null): GlyphSpec | null {
|
|
120
|
-
if (!flow) return null
|
|
121
|
-
if (flow === FLOW_WAIT) return { char: "◷", tone: "warning", blink: true }
|
|
122
|
-
if (flow === FLOW_RECV) return { char: "←", tone: "success", blink: true }
|
|
123
|
-
return { char: "→", tone: "primary", blink: true }
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** Body text tone for a row kind: quiet for files/groups, otherwise the mark tone. */
|
|
127
|
-
export function defaultBodyTone(
|
|
128
|
-
kind: RowKind,
|
|
129
|
-
mark: AgentMark | undefined,
|
|
130
|
-
current: boolean,
|
|
131
|
-
): ToneKey {
|
|
132
|
-
if (kind === ROW_KIND_FILE || kind === ROW_KIND_GROUP) return "textMuted"
|
|
133
|
-
const tone = mark ? markTone(mark) : "textMuted"
|
|
134
|
-
return current && tone === "textMuted" ? "primary" : tone
|
|
135
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* pware.oc.core.glyph
|
|
3
|
+
*
|
|
4
|
+
* Every visual glyph as one complete definition: a character plus a semantic
|
|
5
|
+
* tone (and an optional blink flag). A glyph's char and colour can never drift
|
|
6
|
+
* apart because they come from the same switch. `markTone` / `stateGlyph` /
|
|
7
|
+
* `directionGlyph` are pure and theme-independent — the ui layer resolves a
|
|
8
|
+
* `ToneKey` to an actual RGBA through `toneColor` in
|
|
9
|
+
* `pware.oc.ui/pware.oc.ui.chrome.tsx`.
|
|
10
|
+
*/
|
|
11
|
+
import { BLINK_TICKS } from "./pware.oc.core.timing.js"
|
|
12
|
+
import {
|
|
13
|
+
FLOW_RECV,
|
|
14
|
+
FLOW_TOOL,
|
|
15
|
+
FLOW_WAIT,
|
|
16
|
+
MARK_QUEUED,
|
|
17
|
+
PULSE_LIVE,
|
|
18
|
+
PULSE_STALE,
|
|
19
|
+
type AgentMark,
|
|
20
|
+
type FlowDir,
|
|
21
|
+
} from "./constants/pware.oc.core.constants.pulse.js"
|
|
22
|
+
import { STATUS_ERROR } from "./constants/pware.oc.core.constants.status.js"
|
|
23
|
+
import {
|
|
24
|
+
ROW_KIND_FILE,
|
|
25
|
+
ROW_KIND_GROUP,
|
|
26
|
+
type RowKind,
|
|
27
|
+
} from "./constants/pware.oc.core.constants.rowKind.js"
|
|
28
|
+
|
|
29
|
+
/** Semantic colour of a glyph or body — no theme values, just meaning. */
|
|
30
|
+
export type ToneKey = "text" | "textMuted" | "success" | "primary" | "warning" | "error"
|
|
31
|
+
|
|
32
|
+
/** One glyph as a complete definition: the character, its tone, and blink. */
|
|
33
|
+
export type GlyphSpec = {
|
|
34
|
+
char: string
|
|
35
|
+
tone: ToneKey
|
|
36
|
+
blink?: boolean
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Same braille set as OpenCode TUI thinking spinner (`opentui-spinner`). */
|
|
40
|
+
export const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const
|
|
41
|
+
|
|
42
|
+
/** Queued — waiting for a slot: the hourglass (the wait flow owns ◷). */
|
|
43
|
+
export const QUEUED_GLYPH = "⧗"
|
|
44
|
+
|
|
45
|
+
export function spinnerFrame(frame: number): string {
|
|
46
|
+
const i = ((frame % SPINNER_FRAMES.length) + SPINNER_FRAMES.length) % SPINNER_FRAMES.length
|
|
47
|
+
return SPINNER_FRAMES[i] ?? "⠋"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Cold-start "engage" bar length in glyph frames (GLYPH_TICK_MS × 12 ≈ 1s). */
|
|
51
|
+
export const ENGAGE_MIN_FRAMES = 12
|
|
52
|
+
/** Hard ceiling so the boot bar can never strand (GLYPH_TICK_MS × 50 = 4s). */
|
|
53
|
+
export const ENGAGE_MAX_FRAMES = 50
|
|
54
|
+
|
|
55
|
+
/** Fill ratio of the cold-start engage bar: linear to 1 at `ENGAGE_MIN_FRAMES`. */
|
|
56
|
+
export function engageFill(frame: number): number {
|
|
57
|
+
return Math.min(1, Math.max(0, frame) / ENGAGE_MIN_FRAMES)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The cold-start engage bar ends once the first real snapshot has landed
|
|
62
|
+
* (`ready`) after its minimum show time — or on the hard ceiling (no DB / no
|
|
63
|
+
* snapshot ever) so the row can never strand. Negative frames count as 0.
|
|
64
|
+
*/
|
|
65
|
+
export function engageDone(frame: number, ready: boolean): boolean {
|
|
66
|
+
const n = Math.max(0, frame)
|
|
67
|
+
return (ready && n >= ENGAGE_MIN_FRAMES) || n >= ENGAGE_MAX_FRAMES
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Options for {@link sinPulseAlpha} — one full fade cycle spans `periodFrames` tick frames. */
|
|
71
|
+
export type SinPulseAlphaOpts = {
|
|
72
|
+
periodFrames?: number
|
|
73
|
+
min?: number
|
|
74
|
+
max?: number
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Sinusoidal fade pulse for glyph opacity: alpha eases `min → max → min` over one
|
|
79
|
+
* period. Defaults to `BLINK_TICKS * 2` frames (same wall-clock span as
|
|
80
|
+
* {@link flowBlinkOn}). Pure — inject the UI tick frame; the ui layer maps the
|
|
81
|
+
* result to OpenTUI `opacity` or a colour lerp.
|
|
82
|
+
*/
|
|
83
|
+
export function sinPulseAlpha(frame: number, opts: SinPulseAlphaOpts = {}): number {
|
|
84
|
+
const period = Math.max(1, Math.round(opts.periodFrames ?? BLINK_TICKS * 2))
|
|
85
|
+
const lo = Math.min(opts.min ?? 0.25, opts.max ?? 1)
|
|
86
|
+
const hi = Math.max(opts.min ?? 0.25, opts.max ?? 1)
|
|
87
|
+
const phase = (Math.abs(frame) % period) / period
|
|
88
|
+
const wave = Math.sin(phase * Math.PI)
|
|
89
|
+
return lo + wave * (hi - lo)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** ↑/↓ blink half-period in ticks (TICK_MS × BLINK_TICKS = 100ms). */
|
|
93
|
+
export function flowBlinkOn(frame: number): boolean {
|
|
94
|
+
return Math.floor(Math.abs(frame) / BLINK_TICKS) % 2 === 0
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The tone a state mark carries: error red, queued/stale warning, live success, else muted. */
|
|
98
|
+
export function markTone(mark: AgentMark): ToneKey {
|
|
99
|
+
if (mark === STATUS_ERROR) return "error"
|
|
100
|
+
if (mark === MARK_QUEUED || mark === PULSE_STALE) return "warning"
|
|
101
|
+
if (mark === PULSE_LIVE) return "success"
|
|
102
|
+
return "textMuted"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The state glyph for an agent mark — spinner, cross, hourglass or dot, in one spec. */
|
|
106
|
+
export function stateGlyph(mark: AgentMark, frame = 0): GlyphSpec {
|
|
107
|
+
const char =
|
|
108
|
+
mark === STATUS_ERROR
|
|
109
|
+
? "×"
|
|
110
|
+
: mark === MARK_QUEUED
|
|
111
|
+
? QUEUED_GLYPH
|
|
112
|
+
: mark === PULSE_LIVE || mark === PULSE_STALE
|
|
113
|
+
? spinnerFrame(frame)
|
|
114
|
+
: "•"
|
|
115
|
+
return { char, tone: markTone(mark) }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** The direction glyph for a flow — ◷ waiting, ← receiving, → tool; null when quiet. */
|
|
119
|
+
export function directionGlyph(flow: FlowDir | null): GlyphSpec | null {
|
|
120
|
+
if (!flow) return null
|
|
121
|
+
if (flow === FLOW_WAIT) return { char: "◷", tone: "warning", blink: true }
|
|
122
|
+
if (flow === FLOW_RECV) return { char: "←", tone: "success", blink: true }
|
|
123
|
+
return { char: "→", tone: "primary", blink: true }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Body text tone for a row kind: quiet for files/groups, otherwise the mark tone. */
|
|
127
|
+
export function defaultBodyTone(
|
|
128
|
+
kind: RowKind,
|
|
129
|
+
mark: AgentMark | undefined,
|
|
130
|
+
current: boolean,
|
|
131
|
+
): ToneKey {
|
|
132
|
+
if (kind === ROW_KIND_FILE || kind === ROW_KIND_GROUP) return "textMuted"
|
|
133
|
+
const tone = mark ? markTone(mark) : "textMuted"
|
|
134
|
+
return current && tone === "textMuted" ? "primary" : tone
|
|
135
|
+
}
|