* Add lowcache/claude-companion plugin for PR * claude-companion: address review feedback ... * Added tr & timeout to dependency list, and reduced thumbnail description * added missing thumbnail.webp
90 lines
3.9 KiB
Luau
90 lines
3.9 KiB
Luau
-- EXPERIMENTAL — prototyping always-visible bar-widget animation, since the desktop
|
|
-- orb only shows on bare desktop. The real pulse.luau bar is UNTOUCHED; this is a
|
|
-- throwaway comparison surface. The winning behaviour gets folded into pulse.luau.
|
|
--
|
|
-- Bar plugin widgets have NO per-frame tick (that's desktop-only) — they re-render on
|
|
-- a timer via noctalia.setUpdateInterval(ms) + a global update(). The only animatable
|
|
-- channel is the glyph colour. Rather than alpha (the bar didn't honour 8-digit
|
|
-- #RRGGBBAA), we breathe BRIGHTNESS: scale the accent RGB toward black (the near-black
|
|
-- surface) and emit a plain 6-digit "#RRGGBB" so the glyph glows brighter/dimmer while
|
|
-- keeping its hue. Two waveforms to compare by eye:
|
|
-- MODE "A" raised-cosine sine breath (smooth glow, like the orb)
|
|
-- MODE "B" square wave (discrete blink)
|
|
--
|
|
-- It reads state from the same claude.pulse rollup pulse.luau publishes (so it needs none
|
|
-- of the session bookkeeping). Flip MODE + reload to compare.
|
|
--
|
|
-- State RGB is hardcoded from the active dark palette (volnix:
|
|
-- mSecondary/mPrimary/mError) to validate the look fast.
|
|
|
|
local MODE = "A" -- "A" sine breath | "B" square blink
|
|
local INTERVAL_MS = 60 -- ~16 fps re-render (bars can't do 60 fps)
|
|
local BMIN, BMAX = 0.45, 1.00 -- brightness floor/ceiling the glyph breathes between
|
|
|
|
-- glyph + accent RGB (hex, no #) per state — same glyph/role map as the bar dot/orb.
|
|
local VISUAL = {
|
|
idle = { glyph = "robot", rgb = "EAFF00", period = 8.0 },
|
|
turn_start = { glyph = "brain", rgb = "B4FF00", period = 5.0 },
|
|
text = { glyph = "message-dots", rgb = "B4FF00", period = 4.5 },
|
|
tool_start = { glyph = "tool", rgb = "EAFF00", period = 5.0 },
|
|
needs_attention = { glyph = "bell-ringing", rgb = "FF4D1F", period = 3.0 },
|
|
turn_end = { glyph = "bell", rgb = "B4FF00", period = 5.5 },
|
|
error = { glyph = "alert-triangle", rgb = "FF4D1F", period = 3.5 },
|
|
}
|
|
|
|
local state = "idle"
|
|
local phase = 0.0
|
|
|
|
local function level_for(v) -- brightness scale in [BMIN, BMAX]
|
|
local t = phase % v.period
|
|
if MODE == "B" then
|
|
return (t < v.period * 0.5) and BMAX or BMIN -- bright for the first half, dim for the second
|
|
end
|
|
local s = 0.5 - 0.5 * math.cos((t / v.period) * 2 * math.pi)
|
|
return BMIN + (BMAX - BMIN) * s
|
|
end
|
|
|
|
-- Scale an "RRGGBB" accent toward black by factor b, returning "#RRGGBB".
|
|
local function dim(rgb, b)
|
|
local function ch(i)
|
|
local x = math.floor((tonumber(rgb:sub(i, i + 1), 16) or 0) * b + 0.5)
|
|
if x < 0 then x = 0 elseif x > 255 then x = 255 end
|
|
return x
|
|
end
|
|
return string.format("#%02X%02X%02X", ch(1), ch(3), ch(5))
|
|
end
|
|
|
|
local function paint()
|
|
local v = VISUAL[state] or VISUAL.idle
|
|
barWidget.setGlyph(v.glyph)
|
|
barWidget.setGlyphColor(dim(v.rgb, level_for(v)))
|
|
barWidget.setTooltip(string.format("barpulse [%s] · %s", MODE, state))
|
|
end
|
|
|
|
-- Pull the most-urgent state from the bar pulse's published rollup. Bar widgets don't
|
|
-- seem to receive noctalia.state.watch callbacks (the desktop orb does; this bar one
|
|
-- did not), so we POLL noctalia.state.get each tick instead — cheap, and the timer is
|
|
-- already running. Only adopt a recognised state so a nil/garbage read can't blank it.
|
|
local function refresh_state()
|
|
local snap = noctalia.state.get and noctalia.state.get("claude.pulse")
|
|
if type(snap) == "table" and type(snap.state) == "string" and VISUAL[snap.state] then
|
|
state = snap.state
|
|
elseif type(snap) == "string" and VISUAL[snap] then
|
|
state = snap
|
|
end
|
|
end
|
|
|
|
-- Timer loop. Re-arm the interval each tick (mirrors the scratchpad pattern), refresh
|
|
-- the state by polling, and advance the breath by the known step (no dt on bar widgets).
|
|
function update()
|
|
noctalia.setUpdateInterval(INTERVAL_MS)
|
|
refresh_state()
|
|
phase = phase + INTERVAL_MS / 1000
|
|
if phase > 1e6 then phase = 0 end
|
|
paint()
|
|
end
|
|
|
|
refresh_state()
|
|
noctalia.setUpdateInterval(INTERVAL_MS)
|
|
paint()
|