cc-caffeine 0.2.0 → 0.3.0
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/CLAUDE.md +31 -4
- package/README.md +8 -0
- package/assets/icon-coffee-empty-mono.png +0 -0
- package/assets/icon-coffee-empty-mono.svg +10 -0
- package/assets/icon-coffee-full-mono.png +0 -0
- package/assets/icon-coffee-full-mono.svg +15 -0
- package/caffeine.js +24 -18
- package/package.json +4 -4
- package/src/commands.js +12 -3
- package/src/config.js +37 -0
- package/src/pid.js +5 -4
- package/src/session.js +6 -5
- package/src/system-tray.js +14 -4
package/CLAUDE.md
CHANGED
|
@@ -14,6 +14,7 @@ The system consists of a modular architecture with the following components:
|
|
|
14
14
|
4. **src/server.js** - Handles server process management and Electron integration
|
|
15
15
|
5. **src/system-tray.js** - Manages system tray functionality and sleep prevention
|
|
16
16
|
6. **src/electron.js** - Wraps Electron-specific functionality and provides cross-platform support
|
|
17
|
+
7. **src/config.js** - Reads user configuration from `~/.claude/plugins/cc-caffeine/config.json`
|
|
17
18
|
|
|
18
19
|
### User Commands
|
|
19
20
|
|
|
@@ -27,7 +28,7 @@ The system consists of a modular architecture with the following components:
|
|
|
27
28
|
- Cross-platform support (Linux, macOS, Windows)
|
|
28
29
|
- Headless Electron system tray (no windows, only system tray)
|
|
29
30
|
- JSON file for session persistence with proper-lockfile for concurrency
|
|
30
|
-
-
|
|
31
|
+
- Configurable session timeout (default: 15 minutes of inactivity)
|
|
31
32
|
- Auto-server startup when not running
|
|
32
33
|
- Multiple concurrent session support
|
|
33
34
|
- Real-time status monitoring
|
|
@@ -35,6 +36,24 @@ The system consists of a modular architecture with the following components:
|
|
|
35
36
|
- Native sleep prevention using Electron's powerSaveBlocker API
|
|
36
37
|
- Hidden from macOS dock using app.dock.hide()
|
|
37
38
|
|
|
39
|
+
## Configuration
|
|
40
|
+
|
|
41
|
+
User configuration is stored at `~/.claude/plugins/cc-caffeine/config.json`. All settings are optional and have sensible defaults.
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"session_timeout_minutes": 15,
|
|
46
|
+
"icon_theme": "orange"
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Options
|
|
51
|
+
|
|
52
|
+
| Setting | Default | Description |
|
|
53
|
+
|---------|---------|-------------|
|
|
54
|
+
| `session_timeout_minutes` | `15` | Minutes of inactivity before a session expires |
|
|
55
|
+
| `icon_theme` | `"orange"` | Tray icon theme: `"orange"` (colored) or `"monochrome"` (black/white, auto-adapts to macOS dark mode) |
|
|
56
|
+
|
|
38
57
|
## Technical Stack
|
|
39
58
|
|
|
40
59
|
- **Node.js 14+** - Runtime environment with modern JavaScript features
|
|
@@ -177,7 +196,7 @@ Sessions are removed automatically after 15 minutes of inactivity.
|
|
|
177
196
|
|
|
178
197
|
## Session Management
|
|
179
198
|
|
|
180
|
-
- Sessions auto-expire after 15 minutes of inactivity
|
|
199
|
+
- Sessions auto-expire after the configured timeout (default: 15 minutes of inactivity)
|
|
181
200
|
- Automatic cleanup of expired sessions during every add/remove operation
|
|
182
201
|
- Server polls JSON file every 10 seconds for active sessions (with file locking)
|
|
183
202
|
- Multiple sessions can be active simultaneously
|
|
@@ -235,9 +254,11 @@ npm run format # Format code with Prettier (if installed)
|
|
|
235
254
|
The application uses CommonJS modules with clear dependency hierarchy:
|
|
236
255
|
|
|
237
256
|
- `caffeine.js` imports from `src/commands.js` and `src/server.js`
|
|
238
|
-
- `src/commands.js` imports from `src/session.js`
|
|
257
|
+
- `src/commands.js` imports from `src/session.js` and `src/config.js`
|
|
239
258
|
- `src/server.js` imports from `src/session.js`, `src/system-tray.js`, and `src/electron.js`
|
|
240
|
-
- `src/system-tray.js` imports from `src/session.js` and `src/
|
|
259
|
+
- `src/system-tray.js` imports from `src/session.js`, `src/electron.js`, and `src/config.js`
|
|
260
|
+
- `src/session.js` imports from `src/config.js`
|
|
261
|
+
- `src/config.js` reads `~/.claude/plugins/cc-caffeine/config.json`
|
|
241
262
|
- `src/electron.js` provides Electron functionality on-demand
|
|
242
263
|
|
|
243
264
|
## Sleep Prevention
|
|
@@ -258,13 +279,19 @@ src/
|
|
|
258
279
|
└── server.js - Server process management and Electron integration
|
|
259
280
|
└── system-tray.js - System tray functionality and sleep prevention
|
|
260
281
|
└── electron.js - Electron-specific functionality wrapper
|
|
282
|
+
└── config.js - User configuration reader
|
|
261
283
|
package.json - Node.js dependencies and scripts
|
|
262
284
|
icon-coffee-full.png - Active caffeine icon (PNG)
|
|
263
285
|
icon-coffee-empty.png- Inactive caffeine icon (PNG)
|
|
264
286
|
icon-coffee-full.svg - Active caffeine icon (SVG)
|
|
265
287
|
icon-coffee-empty.svg- Inactive caffeine icon (SVG)
|
|
288
|
+
icon-coffee-full-mono.png - Active caffeine icon monochrome (PNG)
|
|
289
|
+
icon-coffee-empty-mono.png- Inactive caffeine icon monochrome (PNG)
|
|
290
|
+
icon-coffee-full-mono.svg - Active caffeine icon monochrome (SVG)
|
|
291
|
+
icon-coffee-empty-mono.svg- Inactive caffeine icon monochrome (SVG)
|
|
266
292
|
~/.claude/plugins/cc-caffeine/
|
|
267
293
|
└── sessions.json - JSON file with session data
|
|
294
|
+
└── config.json - User configuration (optional)
|
|
268
295
|
```
|
|
269
296
|
|
|
270
297
|
**Modular architecture benefits:**
|
package/README.md
CHANGED
|
@@ -15,6 +15,8 @@ Tired of your laptop going to sleep during that perfect coding session because y
|
|
|
15
15
|
- 🚴♂️ Pedal to the coworking space while maintaining your active connection
|
|
16
16
|
- 📱 Respond to your girlfriend calls during work hours, without Claude Code interruptions
|
|
17
17
|
|
|
18
|
+
<img width="4032" height="1152" alt="image" src="https://github.com/user-attachments/assets/e1db7f4c-bd49-4ec5-8da4-2595c7f9b80a" />
|
|
19
|
+
|
|
18
20
|
## 🎯 Installation
|
|
19
21
|
|
|
20
22
|
```bash
|
|
@@ -22,6 +24,12 @@ Tired of your laptop going to sleep during that perfect coding session because y
|
|
|
22
24
|
/plugin install cc-caffeine@samber
|
|
23
25
|
```
|
|
24
26
|
|
|
27
|
+
*cc-caffine status*:
|
|
28
|
+
|
|
29
|
+
 - Claude Code is idle
|
|
30
|
+
|
|
31
|
+
 - Claude Code is working hard
|
|
32
|
+
|
|
25
33
|
## 🌟 The Nomad Developer Manifesto
|
|
26
34
|
|
|
27
35
|
> "I'll never choose between coding and traveling again. With cc-caffeine, I can do both. My laptop will never sleep while I traverse cities in 5G, my Claude Code will stay connected in my backpack, and my productivity will soar. The future of mobile development is here, and it smells like coffee."
|
|
Binary file
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- Coffee cup outline - smaller cup with more margin -->
|
|
3
|
+
<path d="M8 10 L8 22 L10 24 L22 24 L24 22 L24 10 Z"
|
|
4
|
+
fill="none" stroke="#000000" stroke-width="2" stroke-linejoin="miter"/>
|
|
5
|
+
<!-- Coffee handle - smaller handle with margin -->
|
|
6
|
+
<path d="M24 14 L28 14 L28 18 L24 18 Z"
|
|
7
|
+
fill="none" stroke="#000000" stroke-width="2" stroke-linejoin="miter"/>
|
|
8
|
+
<!-- Coffee residue - smaller with more margin -->
|
|
9
|
+
<line x1="10" y1="22" x2="22" y2="22" stroke="#000000" stroke-width="1" opacity="0.5"/>
|
|
10
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- Coffee cup - smaller cup with more margin -->
|
|
3
|
+
<path d="M8 10 L8 22 L10 24 L22 24 L24 22 L24 10 Z"
|
|
4
|
+
fill="#000000"/>
|
|
5
|
+
<!-- Coffee liquid - smaller liquid with margin -->
|
|
6
|
+
<path d="M10 14 L10 18 L12 20 L20 20 L22 18 L22 14 Z"
|
|
7
|
+
fill="#333333"/>
|
|
8
|
+
<!-- Coffee handle - smaller handle with margin -->
|
|
9
|
+
<path d="M24 14 L28 14 L28 18 L24 18 Z"
|
|
10
|
+
fill="#000000"/>
|
|
11
|
+
<!-- Steam - simple flat lines with more margin -->
|
|
12
|
+
<line x1="12" y1="6" x2="12" y2="2" stroke="#000000" stroke-width="1" opacity="0.7"/>
|
|
13
|
+
<line x1="16" y1="6" x2="16" y2="2" stroke="#000000" stroke-width="1" opacity="0.7"/>
|
|
14
|
+
<line x1="20" y1="6" x2="20" y2="2" stroke="#000000" stroke-width="1" opacity="0.7"/>
|
|
15
|
+
</svg>
|
package/caffeine.js
CHANGED
|
@@ -11,7 +11,13 @@ const fs = require('fs');
|
|
|
11
11
|
* All functionality has been split into separate modules for better organization.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
const {
|
|
14
|
+
const {
|
|
15
|
+
handleCaffeinate,
|
|
16
|
+
handleUncaffeinate,
|
|
17
|
+
handleStatus,
|
|
18
|
+
handleVersion,
|
|
19
|
+
handleUsage
|
|
20
|
+
} = require('./src/commands');
|
|
15
21
|
const { handleServer } = require('./src/server');
|
|
16
22
|
|
|
17
23
|
const CONFIG_DIR = path.join(os.homedir(), '.claude', 'plugins', 'cc-caffeine');
|
|
@@ -36,23 +42,23 @@ const main = async () => {
|
|
|
36
42
|
const command = process.argv[2];
|
|
37
43
|
|
|
38
44
|
switch (command) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
case 'caffeinate':
|
|
46
|
+
await handleCaffeinate();
|
|
47
|
+
break;
|
|
48
|
+
case 'uncaffeinate':
|
|
49
|
+
await handleUncaffeinate();
|
|
50
|
+
break;
|
|
51
|
+
case 'server':
|
|
52
|
+
await handleServer();
|
|
53
|
+
break;
|
|
54
|
+
case 'status':
|
|
55
|
+
await handleStatus();
|
|
56
|
+
break;
|
|
57
|
+
case 'version':
|
|
58
|
+
await handleVersion();
|
|
59
|
+
break;
|
|
60
|
+
default:
|
|
61
|
+
await handleUsage();
|
|
56
62
|
}
|
|
57
63
|
};
|
|
58
64
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-caffeine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Prevents your computer from sleeping while Claude Code works hard",
|
|
5
5
|
"main": "caffeine.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
},
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"electron": "^
|
|
38
|
+
"electron": "^40.1.0",
|
|
39
39
|
"proper-lockfile": "^4.1.2"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"eslint": "^
|
|
42
|
+
"eslint": "^10.0.1",
|
|
43
43
|
"prettier": "^3.6.2"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
@@ -54,4 +54,4 @@
|
|
|
54
54
|
"linux",
|
|
55
55
|
"win32"
|
|
56
56
|
]
|
|
57
|
-
}
|
|
57
|
+
}
|
package/src/commands.js
CHANGED
|
@@ -7,9 +7,14 @@
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
|
|
10
|
-
const {
|
|
10
|
+
const {
|
|
11
|
+
addSessionWithLock,
|
|
12
|
+
removeSessionWithLock,
|
|
13
|
+
getActiveSessionsWithLock
|
|
14
|
+
} = require('./session');
|
|
11
15
|
const { isServerRunningWithLock } = require('./pid');
|
|
12
16
|
const { runServerProcessIfNotStarted } = require('./server');
|
|
17
|
+
const { getConfig } = require('./config');
|
|
13
18
|
|
|
14
19
|
/**
|
|
15
20
|
* Handle session commands with JSON input from Claude Code hooks
|
|
@@ -132,7 +137,9 @@ const handleStatus = async () => {
|
|
|
132
137
|
});
|
|
133
138
|
}
|
|
134
139
|
|
|
135
|
-
console.error(
|
|
140
|
+
console.error(
|
|
141
|
+
`\nSession timeout: ${getConfig().session_timeout_minutes} minutes of inactivity`
|
|
142
|
+
);
|
|
136
143
|
} catch (error) {
|
|
137
144
|
console.error('Error getting status:', error.message);
|
|
138
145
|
process.exit(1);
|
|
@@ -150,7 +157,9 @@ const handleUsage = () => {
|
|
|
150
157
|
console.error(' uncaffeinate [session_id] - Disable caffeine for current session');
|
|
151
158
|
console.error(' server - Start caffeine server with system tray');
|
|
152
159
|
console.error(' status - Show current status and active sessions');
|
|
153
|
-
console.error(
|
|
160
|
+
console.error(
|
|
161
|
+
' version - Show version information from package.json and plugin.json'
|
|
162
|
+
);
|
|
154
163
|
process.exit(1);
|
|
155
164
|
};
|
|
156
165
|
|
package/src/config.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config module - Reads user configuration from config.json
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
|
|
9
|
+
const CONFIG_DIR = path.join(os.homedir(), '.claude', 'plugins', 'cc-caffeine');
|
|
10
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
11
|
+
|
|
12
|
+
const DEFAULTS = {
|
|
13
|
+
session_timeout_minutes: 15,
|
|
14
|
+
icon_theme: 'orange' // 'orange' | 'monochrome'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
let cachedConfig = null;
|
|
18
|
+
|
|
19
|
+
const getConfig = () => {
|
|
20
|
+
if (cachedConfig) {
|
|
21
|
+
return cachedConfig;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let userConfig = {};
|
|
25
|
+
try {
|
|
26
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
27
|
+
userConfig = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
28
|
+
}
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error('Warning: Failed to read config file, using defaults:', error.message);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
cachedConfig = { ...DEFAULTS, ...userConfig };
|
|
34
|
+
return cachedConfig;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = { getConfig };
|
package/src/pid.js
CHANGED
|
@@ -18,7 +18,7 @@ const lockfile = require('proper-lockfile');
|
|
|
18
18
|
const CONFIG_DIR = path.join(os.homedir(), '.claude', 'plugins', 'cc-caffeine');
|
|
19
19
|
const PID_FILE = path.join(CONFIG_DIR, 'server.pid');
|
|
20
20
|
|
|
21
|
-
const withPidLock = async
|
|
21
|
+
const withPidLock = async fn => {
|
|
22
22
|
// create if not exists
|
|
23
23
|
try {
|
|
24
24
|
const fd = fs.openSync(PID_FILE, 'wx');
|
|
@@ -138,8 +138,8 @@ const validatePid = async pid => {
|
|
|
138
138
|
const isWindows = os.platform() === 'win32';
|
|
139
139
|
const psCommand = isWindows
|
|
140
140
|
? spawn('wmic', ['process', 'where', `processid=${pid}`, 'get', 'commandline'], {
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
stdio: 'pipe'
|
|
142
|
+
})
|
|
143
143
|
: spawn('ps', ['-p', pid, '-o', 'command='], { stdio: 'pipe' });
|
|
144
144
|
|
|
145
145
|
let output = '';
|
|
@@ -157,7 +157,8 @@ const validatePid = async pid => {
|
|
|
157
157
|
const commandLine = output.trim().toLowerCase();
|
|
158
158
|
for (const line of commandLine.split('\n')) {
|
|
159
159
|
// Check if command line contains both "caffeine" and "server"
|
|
160
|
-
const isCaffeineServer =
|
|
160
|
+
const isCaffeineServer =
|
|
161
|
+
line.includes('caffeine server') || line.includes('caffeine.js server');
|
|
161
162
|
const isElectron = line.includes('electron');
|
|
162
163
|
|
|
163
164
|
if (isCaffeineServer && isElectron) {
|
package/src/session.js
CHANGED
|
@@ -2,10 +2,11 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const lockfile = require('proper-lockfile');
|
|
5
|
+
const { getConfig } = require('./config');
|
|
5
6
|
|
|
6
7
|
const CONFIG_DIR = path.join(os.homedir(), '.claude', 'plugins', 'cc-caffeine');
|
|
7
8
|
const SESSIONS_FILE = path.join(CONFIG_DIR, 'sessions.json');
|
|
8
|
-
const
|
|
9
|
+
const getSessionTimeout = () => getConfig().session_timeout_minutes * 60 * 1000;
|
|
9
10
|
const MAX_RETRIES = 10;
|
|
10
11
|
|
|
11
12
|
const initSessionsFile = async () => {
|
|
@@ -66,7 +67,7 @@ const addSessionWithLock = async sessionId => {
|
|
|
66
67
|
const lastActivity = new Date(sessionData.last_activity);
|
|
67
68
|
const timeDiff = nowDate - lastActivity;
|
|
68
69
|
|
|
69
|
-
if (timeDiff >=
|
|
70
|
+
if (timeDiff >= getSessionTimeout()) {
|
|
70
71
|
delete data.sessions[existingSessionId];
|
|
71
72
|
removedCount++;
|
|
72
73
|
}
|
|
@@ -124,7 +125,7 @@ const removeSessionWithLock = async sessionId => {
|
|
|
124
125
|
const lastActivity = new Date(sessionData.last_activity);
|
|
125
126
|
const timeDiff = nowDate - lastActivity;
|
|
126
127
|
|
|
127
|
-
if (timeDiff >=
|
|
128
|
+
if (timeDiff >= getSessionTimeout()) {
|
|
128
129
|
delete data.sessions[existingSessionId];
|
|
129
130
|
cleanedCount++;
|
|
130
131
|
}
|
|
@@ -169,7 +170,7 @@ const getActiveSessionsWithLock = async () => {
|
|
|
169
170
|
const lastActivity = new Date(sessionData.last_activity);
|
|
170
171
|
const timeDiff = now - lastActivity;
|
|
171
172
|
|
|
172
|
-
if (timeDiff <
|
|
173
|
+
if (timeDiff < getSessionTimeout()) {
|
|
173
174
|
activeSessions.push({
|
|
174
175
|
id: sessionId,
|
|
175
176
|
...sessionData
|
|
@@ -200,7 +201,7 @@ const cleanupExpiredSessionsWithLock = async () => {
|
|
|
200
201
|
const lastActivity = new Date(sessionData.last_activity);
|
|
201
202
|
const timeDiff = now - lastActivity;
|
|
202
203
|
|
|
203
|
-
if (timeDiff >=
|
|
204
|
+
if (timeDiff >= getSessionTimeout()) {
|
|
204
205
|
delete data.sessions[sessionId];
|
|
205
206
|
removedCount++;
|
|
206
207
|
}
|
package/src/system-tray.js
CHANGED
|
@@ -6,6 +6,7 @@ const path = require('path');
|
|
|
6
6
|
|
|
7
7
|
const { getActiveSessionsWithLock, cleanupExpiredSessionsWithLock } = require('./session');
|
|
8
8
|
const { getElectron } = require('./electron');
|
|
9
|
+
const { getConfig } = require('./config');
|
|
9
10
|
const { removePidFileWithLock } = require('./pid');
|
|
10
11
|
const package = require('../package.json');
|
|
11
12
|
|
|
@@ -15,10 +16,19 @@ let trayState = null;
|
|
|
15
16
|
* Create icon for system tray
|
|
16
17
|
*/
|
|
17
18
|
const createIcon = isActive => {
|
|
18
|
-
const
|
|
19
|
+
const { icon_theme } = getConfig();
|
|
20
|
+
const isMono = icon_theme === 'monochrome';
|
|
21
|
+
const suffix = isMono ? '-mono' : '';
|
|
22
|
+
const icon = isActive
|
|
23
|
+
? `../assets/icon-coffee-full${suffix}.png`
|
|
24
|
+
: `../assets/icon-coffee-empty${suffix}.png`;
|
|
19
25
|
const iconPath = path.join(__dirname, icon);
|
|
20
26
|
const { nativeImage } = getElectron();
|
|
21
|
-
|
|
27
|
+
const image = nativeImage.createFromPath(iconPath);
|
|
28
|
+
if (isMono && process.platform === 'darwin') {
|
|
29
|
+
image.setTemplateImage(true);
|
|
30
|
+
}
|
|
31
|
+
return image;
|
|
22
32
|
};
|
|
23
33
|
|
|
24
34
|
/**
|
|
@@ -54,13 +64,13 @@ const createSystemTray = () => {
|
|
|
54
64
|
{
|
|
55
65
|
label: 'Github',
|
|
56
66
|
click: () => {
|
|
57
|
-
getElectron().shell.openExternal('https://github.com/samber/cc-caffeine')
|
|
67
|
+
getElectron().shell.openExternal('https://github.com/samber/cc-caffeine');
|
|
58
68
|
}
|
|
59
69
|
},
|
|
60
70
|
{
|
|
61
71
|
label: '💖 Sponsor',
|
|
62
72
|
click: () => {
|
|
63
|
-
getElectron().shell.openExternal('https://github.com/sponsors/samber')
|
|
73
|
+
getElectron().shell.openExternal('https://github.com/sponsors/samber');
|
|
64
74
|
}
|
|
65
75
|
},
|
|
66
76
|
{
|