opencode-avatar 0.3.2 → 0.3.4

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/dist/electron.js CHANGED
@@ -524,8 +524,8 @@ var HTML_CONTENT = `<!DOCTYPE html>
524
524
  };
525
525
  }
526
526
 
527
- ipcRenderer.on('set-avatar', (event, avatarDataUrl) => {
528
- loadAvatar(avatarDataUrl);
527
+ ipcRenderer.on('set-avatar', (event, avatarPath) => {
528
+ loadAvatar('file://' + avatarPath);
529
529
  });
530
530
 
531
531
  // Fallback: load default avatar if no IPC message received
@@ -691,10 +691,8 @@ function startAvatarServer() {
691
691
  try {
692
692
  const { avatarPath } = JSON.parse(body);
693
693
  if (mainWindow && avatarPath) {
694
- const imageBuffer = fs.readFileSync(avatarPath);
695
- const base64 = imageBuffer.toString("base64");
696
- const dataUrl = `data:image/png;base64,${base64}`;
697
- mainWindow.webContents.send("set-avatar", dataUrl);
694
+ mainWindow.webContents.send("set-avatar", avatarPath);
695
+ updateTrayIcon(avatarPath);
698
696
  }
699
697
  res.writeHead(200, { "Content-Type": "application/json" });
700
698
  res.end(JSON.stringify({ success: true }));
@@ -718,10 +716,8 @@ function startAvatarServer() {
718
716
  }
719
717
  const avatarPath = await generateAvatarForPrompt(prompt);
720
718
  if (mainWindow) {
721
- const imageBuffer = fs.readFileSync(avatarPath);
722
- const base64 = imageBuffer.toString("base64");
723
- const dataUrl = `data:image/png;base64,${base64}`;
724
- mainWindow.webContents.send("set-avatar", dataUrl);
719
+ mainWindow.webContents.send("set-avatar", avatarPath);
720
+ updateTrayIcon(avatarPath);
725
721
  }
726
722
  res.writeHead(200, { "Content-Type": "application/json" });
727
723
  res.end(JSON.stringify({ success: true, avatarPath }));
@@ -782,10 +778,8 @@ function createWindow() {
782
778
  if (mainWindow) {
783
779
  const avatarPath = getAvatarPath();
784
780
  try {
785
- const imageBuffer = fs.readFileSync(avatarPath);
786
- const base64 = imageBuffer.toString("base64");
787
- const dataUrl = `data:image/png;base64,${base64}`;
788
- mainWindow.webContents.send("set-avatar", dataUrl);
781
+ mainWindow.webContents.send("set-avatar", avatarPath);
782
+ updateTrayIcon(avatarPath);
789
783
  setTimeout(() => {
790
784
  if (mainWindow && !mainWindow.isVisible()) {
791
785
  mainWindow.show();
@@ -809,10 +803,7 @@ function createTray() {
809
803
  let trayIcon;
810
804
  try {
811
805
  const pngPath = path.join(AVATAR_DIR, "avatar.png");
812
- trayIcon = nativeImage.createFromPath(pngPath);
813
- if (trayIcon.isEmpty()) {
814
- trayIcon = nativeImage.createFromPath(path.join(AVATAR_DIR, "avatar.svg"));
815
- }
806
+ trayIcon = processTrayIcon(pngPath);
816
807
  } catch (e) {
817
808
  const message = e instanceof Error ? e.message : String(e);
818
809
  trayIcon = nativeImage.createFromPath(path.join(AVATAR_DIR, "avatar.svg"));
@@ -836,6 +827,35 @@ function createTray() {
836
827
  tray.setContextMenu(contextMenu);
837
828
  tray.on("click", () => mainWindow?.isVisible() ? mainWindow.hide() : mainWindow?.show());
838
829
  }
830
+ function processTrayIcon(pngPath) {
831
+ let trayIcon = nativeImage.createFromPath(pngPath);
832
+ if (!trayIcon.isEmpty()) {
833
+ const size = trayIcon.getSize();
834
+ const bitmap = trayIcon.getBitmap();
835
+ const chromaR = bitmap[0];
836
+ const chromaG = bitmap[1];
837
+ const chromaB = bitmap[2];
838
+ const tolerance = 30;
839
+ for (let i = 0;i < bitmap.length; i += 4) {
840
+ const r = bitmap[i];
841
+ const g = bitmap[i + 1];
842
+ const b = bitmap[i + 2];
843
+ if (Math.abs(r - chromaR) <= tolerance && Math.abs(g - chromaG) <= tolerance && Math.abs(b - chromaB) <= tolerance) {
844
+ bitmap[i + 3] = 0;
845
+ }
846
+ }
847
+ trayIcon = nativeImage.createFromBitmap(bitmap, size);
848
+ } else {
849
+ trayIcon = nativeImage.createFromPath(path.join(AVATAR_DIR, "avatar.svg"));
850
+ }
851
+ return trayIcon;
852
+ }
853
+ function updateTrayIcon(avatarPath) {
854
+ if (tray) {
855
+ const trayIcon = processTrayIcon(avatarPath);
856
+ tray.setImage(trayIcon);
857
+ }
858
+ }
839
859
  app.commandLine.appendSwitch("enable-transparent-visuals");
840
860
  app.whenReady().then(async () => {
841
861
  fs.mkdirSync(AVATAR_DIR, { recursive: true });
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ var isThinking = false;
24
24
  var isToolActive = false;
25
25
  var isShuttingDown = false;
26
26
  var idleTriggered = false;
27
+ var currentRequestId = null;
27
28
  var heartbeatInterval = null;
28
29
  function sendHeartbeat() {
29
30
  const req = http.request({
@@ -219,6 +220,8 @@ var AvatarPlugin = async ({ client }) => {
219
220
  });
220
221
  };
221
222
  async function requestAvatarGeneration(prompt, showToasts = true, toolName) {
223
+ const requestId = `${Date.now()}-${Math.random()}`;
224
+ currentRequestId = requestId;
222
225
  if (showToasts) {
223
226
  showInfoToast(`Generating avatar: ${prompt}`);
224
227
  }
@@ -241,7 +244,7 @@ var AvatarPlugin = async ({ client }) => {
241
244
  if (showToasts) {
242
245
  showInfoToast(`Avatar ready: ${prompt}`);
243
246
  }
244
- if (!idleTriggered || showToasts) {
247
+ if (currentRequestId === requestId) {
245
248
  setAvatarViaHttp(prompt, toolName);
246
249
  }
247
250
  resolve();
@@ -299,6 +302,7 @@ var AvatarPlugin = async ({ client }) => {
299
302
  idleTriggered = true;
300
303
  isThinking = false;
301
304
  isToolActive = false;
305
+ currentRequestId = null;
302
306
  await setAvatarViaHttp();
303
307
  }
304
308
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-avatar",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Dynamic desktop avatar plugin for OpenCode that reacts to your coding activities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",