fix: use relative PNG paths for idle state, transparent SVG for active
This commit is contained in:
@@ -2,13 +2,22 @@
|
|||||||
"Actions": [
|
"Actions": [
|
||||||
{
|
{
|
||||||
"UUID": "com.streamdock.ai-monitor.codex",
|
"UUID": "com.streamdock.ai-monitor.codex",
|
||||||
"Icon": "static/img/codex-icon.svg",
|
"Icon": "static/img/codex.png",
|
||||||
"Name": "Codex Desktop",
|
"Name": "Codex Desktop",
|
||||||
"States": [
|
"States": [
|
||||||
{ "Image": "static/img/codex-icon.svg", "TitleAlignment": "center", "FontSize": "11" }
|
{
|
||||||
|
"Image": "static/img/codex.png",
|
||||||
|
"TitleAlignment": "center",
|
||||||
|
"FontSize": "11"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Settings": {
|
||||||
|
"pollInterval": 1000
|
||||||
|
},
|
||||||
|
"Controllers": [
|
||||||
|
"Keypad",
|
||||||
|
"Information"
|
||||||
],
|
],
|
||||||
"Settings": { "pollInterval": 1000 },
|
|
||||||
"Controllers": ["Keypad", "Information"],
|
|
||||||
"UserTitleEnabled": true,
|
"UserTitleEnabled": true,
|
||||||
"SupportedInMultiActions": true,
|
"SupportedInMultiActions": true,
|
||||||
"Tooltip": "Codex Desktop 工作流状态监控",
|
"Tooltip": "Codex Desktop 工作流状态监控",
|
||||||
@@ -16,13 +25,22 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"UUID": "com.streamdock.ai-monitor.claude",
|
"UUID": "com.streamdock.ai-monitor.claude",
|
||||||
"Icon": "static/img/claude-icon.svg",
|
"Icon": "static/img/claude.png",
|
||||||
"Name": "Claude Desktop",
|
"Name": "Claude Desktop",
|
||||||
"States": [
|
"States": [
|
||||||
{ "Image": "static/img/claude-icon.svg", "TitleAlignment": "center", "FontSize": "11" }
|
{
|
||||||
|
"Image": "static/img/claude.png",
|
||||||
|
"TitleAlignment": "center",
|
||||||
|
"FontSize": "11"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Settings": {
|
||||||
|
"pollInterval": 1000
|
||||||
|
},
|
||||||
|
"Controllers": [
|
||||||
|
"Keypad",
|
||||||
|
"Information"
|
||||||
],
|
],
|
||||||
"Settings": { "pollInterval": 1000 },
|
|
||||||
"Controllers": ["Keypad", "Information"],
|
|
||||||
"UserTitleEnabled": true,
|
"UserTitleEnabled": true,
|
||||||
"SupportedInMultiActions": true,
|
"SupportedInMultiActions": true,
|
||||||
"Tooltip": "Claude Desktop 工作流状态监控",
|
"Tooltip": "Claude Desktop 工作流状态监控",
|
||||||
@@ -30,13 +48,22 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"UUID": "com.streamdock.ai-monitor.opencode",
|
"UUID": "com.streamdock.ai-monitor.opencode",
|
||||||
"Icon": "static/img/opencode-icon.svg",
|
"Icon": "static/img/opencode.png",
|
||||||
"Name": "opencode",
|
"Name": "opencode",
|
||||||
"States": [
|
"States": [
|
||||||
{ "Image": "static/img/opencode-icon.svg", "TitleAlignment": "center", "FontSize": "11" }
|
{
|
||||||
|
"Image": "static/img/opencode.png",
|
||||||
|
"TitleAlignment": "center",
|
||||||
|
"FontSize": "11"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Settings": {
|
||||||
|
"pollInterval": 1000
|
||||||
|
},
|
||||||
|
"Controllers": [
|
||||||
|
"Keypad",
|
||||||
|
"Information"
|
||||||
],
|
],
|
||||||
"Settings": { "pollInterval": 1000 },
|
|
||||||
"Controllers": ["Keypad", "Information"],
|
|
||||||
"UserTitleEnabled": true,
|
"UserTitleEnabled": true,
|
||||||
"SupportedInMultiActions": true,
|
"SupportedInMultiActions": true,
|
||||||
"Tooltip": "opencode 工作流状态监控",
|
"Tooltip": "opencode 工作流状态监控",
|
||||||
@@ -46,18 +73,28 @@
|
|||||||
"SDKVersion": 1,
|
"SDKVersion": 1,
|
||||||
"Author": "StreamDock",
|
"Author": "StreamDock",
|
||||||
"Name": "AI Monitor",
|
"Name": "AI Monitor",
|
||||||
"Icon": "static/img/codex-icon.svg",
|
"Icon": "static/img/ai.png",
|
||||||
"Category": "AI Monitor",
|
"Category": "AI Monitor",
|
||||||
"CategoryIcon": "static/img/codex-icon.svg",
|
"CategoryIcon": "static/img/ai.png",
|
||||||
"CodePathWin": "plugin/index.js",
|
"CodePathWin": "plugin/index.js",
|
||||||
"CodePathMac": "plugin/index.js",
|
"CodePathMac": "plugin/index.js",
|
||||||
"Description": "监控 Codex / Claude / opencode AI 工作流状态",
|
"Description": "监控 Codex / Claude / opencode AI 工作流状态",
|
||||||
"Version": "2.0.0",
|
"Version": "2.0.0",
|
||||||
"URL": "http://10.10.10.14:18003/v6ole/streamdock-ai-monitor",
|
"URL": "http://10.10.10.14:18003/v6ole/streamdock-ai-monitor",
|
||||||
"OS": [
|
"OS": [
|
||||||
{ "Platform": "windows", "MinimumVersion": "7" },
|
{
|
||||||
{ "Platform": "mac", "MinimumVersion": "10.11" }
|
"Platform": "windows",
|
||||||
|
"MinimumVersion": "7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Platform": "mac",
|
||||||
|
"MinimumVersion": "10.11"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"Software": { "MinimumVersion": "3.10.188.226" },
|
"Software": {
|
||||||
"Nodejs": { "Version": "20" }
|
"MinimumVersion": "3.10.188.226"
|
||||||
|
},
|
||||||
|
"Nodejs": {
|
||||||
|
"Version": "20"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ var AGENTS = {
|
|||||||
codex: {
|
codex: {
|
||||||
label: 'Codex',
|
label: 'Codex',
|
||||||
scanDir: path.join(HOME, '.codex', 'sessions'),
|
scanDir: path.join(HOME, '.codex', 'sessions'),
|
||||||
logoFile: 'codex-icon.svg',
|
logoFile: 'codex.png',
|
||||||
normalize: function(obj) {
|
normalize: function(obj) {
|
||||||
var t = obj.type || '', pt = (obj.payload || {}).type || '', it = ((obj.payload || {}).item || {}).type || '';
|
var t = obj.type || '', pt = (obj.payload || {}).type || '', it = ((obj.payload || {}).item || {}).type || '';
|
||||||
if (t === 'event_msg') {
|
if (t === 'event_msg') {
|
||||||
@@ -40,7 +40,7 @@ var AGENTS = {
|
|||||||
claude: {
|
claude: {
|
||||||
label: 'Claude',
|
label: 'Claude',
|
||||||
scanDir: path.join(HOME, '.claude', 'projects'),
|
scanDir: path.join(HOME, '.claude', 'projects'),
|
||||||
logoFile: 'claude-icon.svg',
|
logoFile: 'claude.png',
|
||||||
normalize: function(obj) {
|
normalize: function(obj) {
|
||||||
var t = obj.type || '';
|
var t = obj.type || '';
|
||||||
if (t === 'user') return 'prompt';
|
if (t === 'user') return 'prompt';
|
||||||
@@ -61,7 +61,7 @@ var AGENTS = {
|
|||||||
opencode: {
|
opencode: {
|
||||||
label: 'opencode',
|
label: 'opencode',
|
||||||
scanDir: path.join(HOME, '.opencode', 'sessions'),
|
scanDir: path.join(HOME, '.opencode', 'sessions'),
|
||||||
logoFile: 'opencode-icon.svg',
|
logoFile: 'opencode.png',
|
||||||
normalize: function(obj) {
|
normalize: function(obj) {
|
||||||
var t = obj.type || '';
|
var t = obj.type || '';
|
||||||
if (t === 'user') return 'prompt';
|
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
|
// Shared display machinery
|
||||||
var STATUS = {
|
var STATUS = {
|
||||||
@@ -100,15 +93,26 @@ function makeSvg(fill, label, blink, breathe) {
|
|||||||
var anim = '';
|
var anim = '';
|
||||||
if (blink) anim = '<animate attributeName="opacity" values="1;0.2;1" dur="0.8s" repeatCount="indefinite"/>';
|
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"/>';
|
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) {
|
function setDisplay(ctx, state) {
|
||||||
var d = STATUS[state] || STATUS['idle'];
|
var d = STATUS[state] || STATUS['idle'];
|
||||||
var ss = states[ctx];
|
var ss = states[ctx];
|
||||||
plugin.setTitle(ctx, d.title);
|
if (state === 'idle' && ss && ss.agent) {
|
||||||
if (state === 'idle' && ss && ss.logoUri) { plugin.setTitle(ctx, ''); plugin.setImage(ctx, ss.logoUri); }
|
// Use relative PNG path for idle
|
||||||
else plugin.setImage(ctx, 'data:image/svg+xml;charset=utf-8,' + makeSvg(d.fill, d.title, d.blink, d.breathe));
|
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) {
|
function setState(ctx, state) {
|
||||||
@@ -174,8 +178,7 @@ function makeActionFn(agentKey) {
|
|||||||
default: { pollInterval: POLL_INTERVAL },
|
default: { pollInterval: POLL_INTERVAL },
|
||||||
_willAppear: function(data) {
|
_willAppear: function(data) {
|
||||||
var ctx = data.context;
|
var ctx = data.context;
|
||||||
var logoUri = LOGOS[agentKey] || '';
|
states[ctx] = { agent: agent, currentState: 'idle', lastActivity: Date.now(), offsets: {}, timer: null };
|
||||||
states[ctx] = { agent: agent, currentState: 'idle', lastActivity: Date.now(), offsets: {}, timer: null, logoUri: logoUri };
|
|
||||||
setDisplay(ctx, 'idle');
|
setDisplay(ctx, 'idle');
|
||||||
poll(ctx);
|
poll(ctx);
|
||||||
states[ctx].timer = setInterval(function() { poll(ctx); }, POLL_INTERVAL);
|
states[ctx].timer = setInterval(function() { poll(ctx); }, POLL_INTERVAL);
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Reference in New Issue
Block a user