mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-31 18:13:34 +00:00
fix(tui): reduce tab pulse allocations (#39433)
This commit is contained in:
parent
5504245f7b
commit
b47cfbee7c
2 changed files with 57 additions and 12 deletions
|
|
@ -1,6 +1,5 @@
|
|||
import { OptimizedBuffer, Renderable, RGBA, type RenderableOptions, type RenderContext } from "@opentui/core"
|
||||
import { extend } from "@opentui/solid"
|
||||
import { tint } from "../theme/color"
|
||||
|
||||
type TabPulseOptions = RenderableOptions<TabPulseRenderable> & {
|
||||
enabled?: boolean
|
||||
|
|
@ -23,6 +22,7 @@ const COMPLETION_DURATION = 900
|
|||
const COMPLETION_ATTACK = 0.16
|
||||
const GLOW_TAIL = 12
|
||||
const GLOW_OPACITY = 0.16
|
||||
const DEFAULT_FOREGROUND = RGBA.defaultForeground()
|
||||
const intensityAt = (index: number, front: number, head: number, tail: number) => {
|
||||
const distance = front - index
|
||||
return distance < 0 ? smootherstep(clamp(1 + distance / head)) : smootherstep(clamp(1 - distance / tail))
|
||||
|
|
@ -41,6 +41,26 @@ export const unreadGlowIntensity = (index: number, width: number) => {
|
|||
const tail = Math.min(GLOW_TAIL, Math.max(1, width - 2))
|
||||
return smootherstep(clamp(1 - Math.max(0, index - 1) / tail))
|
||||
}
|
||||
export function blendTabPulseColor(
|
||||
output: RGBA,
|
||||
background: RGBA,
|
||||
glowColor: RGBA,
|
||||
runningColor: RGBA,
|
||||
completionColor: RGBA,
|
||||
glow: number,
|
||||
running: number,
|
||||
completion: number,
|
||||
) {
|
||||
output.r = background.r + (glowColor.r - background.r) * glow
|
||||
output.g = background.g + (glowColor.g - background.g) * glow
|
||||
output.b = background.b + (glowColor.b - background.b) * glow
|
||||
output.r += (runningColor.r - output.r) * running
|
||||
output.g += (runningColor.g - output.g) * running
|
||||
output.b += (runningColor.b - output.b) * running
|
||||
output.r += (completionColor.r - output.r) * completion
|
||||
output.g += (completionColor.g - output.g) * completion
|
||||
output.b += (completionColor.b - output.b) * completion
|
||||
}
|
||||
class TabPulseRenderable extends Renderable {
|
||||
private _enabled: boolean
|
||||
private _active: boolean
|
||||
|
|
@ -54,6 +74,7 @@ class TabPulseRenderable extends Renderable {
|
|||
private fadeClock: number | undefined
|
||||
private completionClock: number | undefined
|
||||
private completionPending = false
|
||||
private renderColor = RGBA.fromInts(0, 0, 0)
|
||||
|
||||
constructor(ctx: RenderContext, options: TabPulseOptions = {}) {
|
||||
const enabled = options.enabled ?? true
|
||||
|
|
@ -191,17 +212,20 @@ class TabPulseRenderable extends Renderable {
|
|||
intensityAt(index, front, RUN_HEAD, RUN_TAIL),
|
||||
intensityAt(index, secondFront, RUN_HEAD, RUN_TAIL),
|
||||
)
|
||||
const background = this._glow
|
||||
? tint(this._backgroundColor, this._glowColor, unreadGlowIntensity(index, this.width) * GLOW_OPACITY)
|
||||
: this._backgroundColor
|
||||
const running = tint(background, this._color, intensity * 0.14 * runningOpacity)
|
||||
buffer.setCell(
|
||||
this.screenX + index,
|
||||
this.screenY,
|
||||
" ",
|
||||
RGBA.defaultForeground(),
|
||||
tint(running, this._completionColor, completionOpacity * 0.18),
|
||||
const glow = this._glow ? unreadGlowIntensity(index, this.width) * GLOW_OPACITY : 0
|
||||
const running = intensity * 0.14 * runningOpacity
|
||||
const completion = completionOpacity * 0.18
|
||||
blendTabPulseColor(
|
||||
this.renderColor,
|
||||
this._backgroundColor,
|
||||
this._glowColor,
|
||||
this._color,
|
||||
this._completionColor,
|
||||
glow,
|
||||
running,
|
||||
completion,
|
||||
)
|
||||
buffer.setCell(this.screenX + index, this.screenY, " ", DEFAULT_FOREGROUND, this.renderColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { expect, test } from "bun:test"
|
||||
import { completionPulseOpacity, unreadGlowIntensity } from "../../src/component/tab-pulse"
|
||||
import { RGBA } from "@opentui/core"
|
||||
import { blendTabPulseColor, completionPulseOpacity, unreadGlowIntensity } from "../../src/component/tab-pulse"
|
||||
import { tint } from "../../src/theme/color"
|
||||
|
||||
test("completion pulse rises quickly and fades over the remaining duration", () => {
|
||||
expect(completionPulseOpacity(0)).toBe(0)
|
||||
|
|
@ -24,3 +26,22 @@ test("unread glow reaches the normal background on compact tabs", () => {
|
|||
expect(unreadGlowIntensity(0, 8)).toBe(1)
|
||||
expect(unreadGlowIntensity(7, 8)).toBe(0)
|
||||
})
|
||||
|
||||
test("reuses a color while preserving the original glow and pulse blend stages", () => {
|
||||
const output = RGBA.fromInts(0, 0, 0)
|
||||
const background = RGBA.fromHex("#1a1b26")
|
||||
const glowColor = RGBA.fromHex("#82aaff")
|
||||
const runningColor = RGBA.fromHex("#c8d3f5")
|
||||
const completionColor = RGBA.fromHex("#ff9e64")
|
||||
|
||||
for (const glow of [0, 0.08, 0.16]) {
|
||||
for (const running of [0, 0.01, 0.07, 0.14]) {
|
||||
for (const completion of [0, 0.03, 0.09, 0.18]) {
|
||||
blendTabPulseColor(output, background, glowColor, runningColor, completionColor, glow, running, completion)
|
||||
expect(output.buffer).toEqual(
|
||||
tint(tint(tint(background, glowColor, glow), runningColor, running), completionColor, completion).buffer,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue