openzoo 0.51.14 → 0.51.15

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.
@@ -23,7 +23,9 @@
23
23
  "savings.cjs",
24
24
  "savings-preload.cjs",
25
25
  "savings.html",
26
- "savings-renderer.js"
26
+ "savings-renderer.js",
27
+ "savings-toggle.html",
28
+ "savings-toggle.js"
27
29
  ],
28
30
  "extraResources": [
29
31
  {
@@ -7,5 +7,8 @@ contextBridge.exposeInMainWorld('savings', {
7
7
  card: () => ipcRenderer.send("deposit-card"),
8
8
  wallet: fn => ipcRenderer.on("deposit-wallet", (_event, value) => fn(value)),
9
9
  copied: fn => ipcRenderer.on("deposit-copied", (_event, value) => fn(value)),
10
+ collapse: collapsed => ipcRenderer.send("collapse-savings", collapsed),
11
+ toggleVisibility: () => ipcRenderer.send("toggle-savings-visibility"),
12
+ visibility: fn => ipcRenderer.on("pill-visibility", (_event, visible) => fn(visible)),
10
13
  close: () => ipcRenderer.send('close-savings'),
11
14
  });
@@ -1,3 +1,15 @@
1
+ let collapsed = false;
2
+ function setCollapsed(value) {
3
+ collapsed=value;
4
+ document.querySelector('main').hidden=value;
5
+ const button=document.getElementById('collapse');
6
+ button.textContent=value ? '+' : '−';
7
+ button.setAttribute('aria-expanded', String(!value));
8
+ button.setAttribute('aria-label', value ? 'Expand savings' : 'Collapse savings');
9
+ button.title=value ? 'Expand' : 'Collapse';
10
+ window.savings.collapse(value);
11
+ }
12
+ document.getElementById('collapse').addEventListener('click',()=>setCollapsed(!collapsed));
1
13
  let onboarding = false;
2
14
  const finishOnboarding = () => {
3
15
  try { localStorage.setItem('openzoo-onboarded-v1','yes'); } catch {}
@@ -25,6 +37,7 @@ window.savings.listen(d => {
25
37
  document.getElementById('close').addEventListener('click',()=>window.savings.close());
26
38
 
27
39
  document.getElementById('deposit').addEventListener('click', () => {
40
+ if (collapsed) setCollapsed(false);
28
41
  try { onboarding = localStorage.getItem('openzoo-onboarded-v1') !== 'yes'; } catch {}
29
42
  document.getElementById('welcome').hidden=!onboarding;
30
43
  document.getElementById('totals').hidden=onboarding;
@@ -0,0 +1,3 @@
1
+ <!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'self'"><title>OpenZoo pill visibility</title><style>
2
+ *{box-sizing:border-box}body{margin:0;padding:4px;background:transparent}button{width:100%;height:32px;border:1px solid #ffffff35;border-radius:16px;background:#222225;color:#fff;font:12px system-ui;cursor:pointer}button:hover{background:#343437}button:focus-visible{outline:2px solid #e85d1c;outline-offset:-3px}.dot{color:#f16b24;margin-right:6px}
3
+ </style></head><body><button id="toggle" aria-label="Hide savings pill" aria-pressed="true"><span class="dot" aria-hidden="true">●</span><span id="label">Hide pill</span></button><script src="savings-toggle.js"></script></body></html>
@@ -0,0 +1,7 @@
1
+ document.getElementById('toggle').addEventListener('click', () => window.savings.toggleVisibility());
2
+ window.savings.visibility(visible => {
3
+ document.getElementById('label').textContent = visible ? 'Hide pill' : 'Show pill';
4
+ const button = document.getElementById('toggle');
5
+ button.setAttribute('aria-label', visible ? 'Hide savings pill' : 'Show savings pill');
6
+ button.setAttribute('aria-pressed', String(visible));
7
+ });
@@ -13,14 +13,36 @@ function showSavings(port = Number(process.env.OPENZOO_SAVINGS_PORT) || 8402) {
13
13
  pill.webContents.on('will-navigate', e => e.preventDefault());
14
14
  pill.loadFile(path.join(__dirname, 'savings.html'));
15
15
  const window = pill;
16
- let busy = false, last = null, wallet = null;
16
+ const visibilityControl = new BrowserWindow({ width: 136, height: 40,
17
+ x: x + 16, y: y + height - 100,
18
+ frame: false, transparent: true, alwaysOnTop: true, skipTaskbar: true, resizable: false,
19
+ webPreferences: { preload: path.join(__dirname, 'savings-preload.cjs'), contextIsolation: true, sandbox: true, nodeIntegration: false } });
20
+ visibilityControl.setAlwaysOnTop(true, 'floating');
21
+ visibilityControl.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
22
+ visibilityControl.webContents.setWindowOpenHandler(() => ({ action: 'deny' }));
23
+ visibilityControl.webContents.on('will-navigate', e => e.preventDefault());
24
+ visibilityControl.loadFile(path.join(__dirname, 'savings-toggle.html'));
25
+ const syncVisibility = () => {
26
+ if (!visibilityControl.isDestroyed()) visibilityControl.webContents.send('pill-visibility', window.isVisible());
27
+ };
28
+ const toggleVisibility = event => {
29
+ if (event.sender !== visibilityControl.webContents || window.isDestroyed()) return;
30
+ if (window.isVisible()) window.hide(); else window.showInactive();
31
+ syncVisibility();
32
+ };
33
+ ipcMain.on('toggle-savings-visibility', toggleVisibility);
34
+ window.on('show', syncVisibility);
35
+ window.on('hide', syncVisibility);
36
+ visibilityControl.webContents.on('did-finish-load', syncVisibility);
37
+ let busy = false, last = null, wallet = null, expandedHeight = 270;
17
38
  const resize = height => {
18
39
  const b = window.getBounds(), area = screen.getDisplayMatching(b).workArea;
19
40
  window.setBounds({ ...b, height: Math.min(height, area.height), y: Math.max(area.y, Math.min(b.y, area.y + area.height - height)) });
20
41
  };
21
42
  const deposit = async event => {
22
43
  if (event.sender !== window.webContents) return;
23
- resize(620);
44
+ expandedHeight = 620;
45
+ resize(expandedHeight);
24
46
  try {
25
47
  const r = await fetch(`http://127.0.0.1:${port}/v1/wallet`, { signal: AbortSignal.timeout(5000) });
26
48
  if (!r.ok) throw Error("Unavailable");
@@ -34,7 +56,11 @@ function showSavings(port = Number(process.env.OPENZOO_SAVINGS_PORT) || 8402) {
34
56
  window.webContents.send("deposit-copied", chain);
35
57
  };
36
58
  const card = event => { if (event.sender === window.webContents) shell.openExternal("https://whop.com/staccoverflow/openzoo"); };
37
- const back = event => { if (event.sender === window.webContents) resize(270); };
59
+ const back = event => { if (event.sender === window.webContents) { expandedHeight = 270; resize(expandedHeight); } };
60
+ const collapse = (event, collapsed) => {
61
+ if (event.sender === window.webContents && typeof collapsed === "boolean") resize(collapsed ? 68 : expandedHeight);
62
+ };
63
+ ipcMain.on("collapse-savings", collapse);
38
64
  ipcMain.on("open-deposit", deposit);
39
65
  ipcMain.on("copy-deposit", copy);
40
66
  ipcMain.on("deposit-card", card);
@@ -58,9 +84,9 @@ function showSavings(port = Number(process.env.OPENZOO_SAVINGS_PORT) || 8402) {
58
84
  }
59
85
  });
60
86
  const timer = setInterval(poll, 3000);
61
- const close = event => { if (event.sender === window.webContents) window.close(); };
87
+ const close = event => { if (event.sender === window.webContents) window.hide(); };
62
88
  ipcMain.on('close-savings', close);
63
- window.on('closed', () => { clearInterval(timer); for (const [name, fn] of [["open-deposit",deposit],["copy-deposit",copy],["deposit-card",card],["close-deposit",back]]) ipcMain.removeListener(name,fn); ipcMain.removeListener('close-savings', close); if (pill === window) pill = null; });
89
+ window.on('closed', () => { ipcMain.removeListener('toggle-savings-visibility', toggleVisibility); if (!visibilityControl.isDestroyed()) visibilityControl.destroy(); clearInterval(timer); for (const [name, fn] of [["collapse-savings",collapse],["open-deposit",deposit],["copy-deposit",copy],["deposit-card",card],["close-deposit",back]]) ipcMain.removeListener(name,fn); ipcMain.removeListener('close-savings', close); if (pill === window) pill = null; });
64
90
  return pill;
65
91
  }
66
92
  module.exports = { showSavings };
@@ -1,3 +1,3 @@
1
1
  <!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'self'"><title>OpenZoo savings</title><style>
2
2
  *{box-sizing:border-box}body{max-height:100vh;overflow-y:auto;margin:0;padding:8px;background:transparent;font:13px system-ui;color:#fff}.pill{border-radius:20px;background:#18181bf5;border:1px solid #ffffff25;overflow:hidden;box-shadow:0 4px 14px #0004}header{background:#e85d1c;padding:12px 16px;font-weight:650;-webkit-app-region:drag;display:flex;justify-content:space-between;align-items:center}button{-webkit-app-region:no-drag;border:0;background:none;color:inherit;font:18px system-ui;cursor:pointer;padding:0 5px}main{padding:12px 16px}dl{display:grid;grid-template-columns:1fr auto;margin:0;gap:7px}dd{margin:0;font-variant-numeric:tabular-nums}small{color:#bbb;display:block;margin-top:12px;font-size:11px}#state{color:#d8ff76}.actions{display:flex;align-items:center;gap:8px}#deposit,.fund-button{font-size:12px;border:1px solid #ffffff60;border-radius:7px;padding:5px 10px}.fund-button{display:block;width:100%;text-align:left;margin:9px 0;overflow-wrap:anywhere;line-height:1.5}.fund-button span{display:block;font-size:11px;color:#ccc;user-select:text}[hidden]{display:none!important}#welcome p{font-size:12px;line-height:1.5;color:#ccc}#funding{border-top:1px solid #ffffff25;margin-top:12px}#funding p{font-size:12px;color:#ccc}
3
- </style></head><body><div class="pill"><header><span>OpenZoo · costs & savings</span><div class="actions"><button id="deposit">Deposit</button><button id="close" aria-label="Close savings pill">×</button></div></header><main><section id="welcome" hidden><h3>Welcome to OpenZoo</h3><p>Pay per request from your OpenZoo wallet. Add funds before your first chat.</p><p>1. Choose card or crypto below.<br>2. Add funds to this wallet.<br>3. Once your balance appears, start chatting.</p></section><div id="totals"><dl><dt>Wallet balance</dt><dd id="wallet">—</dd><dt>Prepaid credit</dt><dd id="credit">—</dd><dt>Charged · verified calls</dt><dd id="spent">—</dd><dt>Direct cost · verified calls</dt><dd id="direct">—</dd><dt id="saving-label">Saved</dt><dd id="saved">—</dd></dl><small id="state" role="status">Checking your balance…</small><small>Savings cover matching verified calls only. Drag the orange bar to move.</small></div><section id="funding" hidden><h3>Add funds</h3><p id="fund-status" role="status">Loading your wallet…</p><button class="fund-button" id="copy-solana" disabled>Solana · USDC / TOKEN / LEOS<span id="solana-address"></span><span>Click to copy address</span></button><button class="fund-button" id="copy-evm" disabled>Base · USDC<span id="evm-address"></span><span>Click to copy address</span></button><button class="fund-button" id="card">Pay by card · Whop ↗</button><p>At checkout, paste your Solana address to link the purchase to this wallet. Your balance updates automatically after confirmation.</p><button class="fund-button" id="back">Back to savings</button><button class="fund-button" id="later" hidden>Set up later</button></section></main></div><script src="savings-renderer.js"></script></body></html>
3
+ </style></head><body><div class="pill"><header><span>OpenZoo · costs & savings</span><div class="actions"><button id="deposit">Deposit</button><button id="collapse" aria-label="Collapse savings" aria-expanded="true" title="Collapse">−</button><button id="close" aria-label="Close savings pill">×</button></div></header><main><section id="welcome" hidden><h3>Welcome to OpenZoo</h3><p>Pay per request from your OpenZoo wallet. Add funds before your first chat.</p><p>1. Choose card or crypto below.<br>2. Add funds to this wallet.<br>3. Once your balance appears, start chatting.</p></section><div id="totals"><dl><dt>Wallet balance</dt><dd id="wallet">—</dd><dt>Prepaid credit</dt><dd id="credit">—</dd><dt>Charged · verified calls</dt><dd id="spent">—</dd><dt>Direct cost · verified calls</dt><dd id="direct">—</dd><dt id="saving-label">Saved</dt><dd id="saved">—</dd></dl><small id="state" role="status">Checking your balance…</small><small>Savings cover matching verified calls only. Drag the orange bar to move.</small></div><section id="funding" hidden><h3>Add funds</h3><p id="fund-status" role="status">Loading your wallet…</p><button class="fund-button" id="copy-solana" disabled>Solana · USDC / TOKEN / LEOS<span id="solana-address"></span><span>Click to copy address</span></button><button class="fund-button" id="copy-evm" disabled>Base · USDC<span id="evm-address"></span><span>Click to copy address</span></button><button class="fund-button" id="card">Pay by card · Whop ↗</button><p>At checkout, paste your Solana address to link the purchase to this wallet. Your balance updates automatically after confirmation.</p><button class="fund-button" id="back">Back to savings</button><button class="fund-button" id="later" hidden>Set up later</button></section></main></div><script src="savings-renderer.js"></script></body></html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.51.14",
3
+ "version": "0.51.15",
4
4
  "description": "Local x402-paying proxy + MCP server for openzoo.fun — point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana and Base rails live; Robinhood experimental.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -17,7 +17,9 @@
17
17
  "launcher/savings.cjs",
18
18
  "launcher/savings.html",
19
19
  "launcher/savings-preload.cjs",
20
- "launcher/savings-renderer.js"
20
+ "launcher/savings-renderer.js",
21
+ "launcher/savings-toggle.html",
22
+ "launcher/savings-toggle.js"
21
23
  ],
22
24
  "engines": {
23
25
  "node": ">=18"