fix: use relative PNG paths for idle state, transparent SVG for active

This commit is contained in:
2026-06-18 15:50:18 +08:00
parent ef79e67f0a
commit 4811fef4d2
6 changed files with 74 additions and 34 deletions
@@ -13,7 +13,7 @@ var AGENTS = {
codex: {
label: 'Codex',
scanDir: path.join(HOME, '.codex', 'sessions'),
logoFile: 'codex-icon.svg',
logoFile: 'codex.png',
normalize: function(obj) {
var t = obj.type || '', pt = (obj.payload || {}).type || '', it = ((obj.payload || {}).item || {}).type || '';
if (t === 'event_msg') {
@@ -40,7 +40,7 @@ var AGENTS = {
claude: {
label: 'Claude',
scanDir: path.join(HOME, '.claude', 'projects'),
logoFile: 'claude-icon.svg',
logoFile: 'claude.png',
normalize: function(obj) {
var t = obj.type || '';
if (t === 'user') return 'prompt';
@@ -61,7 +61,7 @@ var AGENTS = {
opencode: {
label: 'opencode',
scanDir: path.join(HOME, '.opencode', 'sessions'),
logoFile: 'opencode-icon.svg',
logoFile: 'opencode.png',
normalize: function(obj) {
var t = obj.type || '';
if (t === 'user') return 'prompt';
@@ -75,13 +75,6 @@ var AGENTS = {
}
};
// Load logos as data URIs
var LOGOS = {};
function loadLogo(filename) {
try { return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(fs.readFileSync(path.join(__dirname, '..', 'static', 'img', filename), 'utf-8')); }
catch (_) { return ''; }
}
Object.keys(AGENTS).forEach(function(k) { LOGOS[k] = loadLogo(AGENTS[k].logoFile); });
// Shared display machinery
var STATUS = {
@@ -100,15 +93,26 @@ function makeSvg(fill, label, blink, breathe) {
var anim = '';
if (blink) anim = '<animate attributeName="opacity" values="1;0.2;1" dur="0.8s" repeatCount="indefinite"/>';
else if (breathe) anim = '<animate attributeName="opacity" values="1;0.4;1" dur="2s" repeatCount="indefinite"/>';
return '<svg width="144" height="144" xmlns="http://www.w3.org/2000/svg"><rect width="144" height="144" rx="20" fill="none"/><circle cx="72" cy="58" r="32" fill="' + fill + '">' + anim + '</circle><text x="72" y="118" font-family="Arial" font-weight="bold" font-size="13" fill="white" text-anchor="middle">' + label + '</text></svg>';
return '<svg width="144" height="144" xmlns="http://www.w3.org/2000/svg"><rect width="144" height="144" rx="20" fill="#0d0d1a"/><circle cx="72" cy="58" r="32" fill="' + fill + '">' + anim + '</circle><text x="72" y="118" font-family="Arial" font-weight="bold" font-size="13" fill="white" text-anchor="middle">' + label + '</text></svg>';
}
function setDisplay(ctx, state) {
var d = STATUS[state] || STATUS['idle'];
var ss = states[ctx];
plugin.setTitle(ctx, d.title);
if (state === 'idle' && ss && ss.logoUri) { plugin.setTitle(ctx, ''); plugin.setImage(ctx, ss.logoUri); }
else plugin.setImage(ctx, 'data:image/svg+xml;charset=utf-8,' + makeSvg(d.fill, d.title, d.blink, d.breathe));
if (state === 'idle' && ss && ss.agent) {
// Use relative PNG path for idle
plugin.setTitle(ctx, '');
plugin.setImage(ctx, 'static/img/' + ss.agent.logoFile);
} else {
plugin.setTitle(ctx, d.title);
var svg = '<svg width="144" height="144" xmlns="http://www.w3.org/2000/svg"><rect width="144" height="144" rx="20" fill="none"/>';
var anim = '';
if (d.blink) anim = '<animate attributeName="opacity" values="1;0.2;1" dur="0.8s" repeatCount="indefinite"/>';
else if (d.breathe) anim = '<animate attributeName="opacity" values="1;0.4;1" dur="2s" repeatCount="indefinite"/>';
svg += '<circle cx="72" cy="58" r="32" fill="' + d.fill + '">' + anim + '</circle>';
svg += '<text x="72" y="118" font-family="Arial" font-weight="bold" font-size="13" fill="white" text-anchor="middle">' + d.title + '</text></svg>';
plugin.setImage(ctx, 'data:image/svg+xml;charset=utf-8,' + svg);
}
}
function setState(ctx, state) {
@@ -174,8 +178,7 @@ function makeActionFn(agentKey) {
default: { pollInterval: POLL_INTERVAL },
_willAppear: function(data) {
var ctx = data.context;
var logoUri = LOGOS[agentKey] || '';
states[ctx] = { agent: agent, currentState: 'idle', lastActivity: Date.now(), offsets: {}, timer: null, logoUri: logoUri };
states[ctx] = { agent: agent, currentState: 'idle', lastActivity: Date.now(), offsets: {}, timer: null };
setDisplay(ctx, 'idle');
poll(ctx);
states[ctx].timer = setInterval(function() { poll(ctx); }, POLL_INTERVAL);