carvus-lens 1.0.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/main.js ADDED
@@ -0,0 +1,202 @@
1
+ const { app, BrowserWindow, ipcMain, desktopCapturer, screen, globalShortcut, Tray, Menu, nativeImage } = require('electron');
2
+ const path = require('path');
3
+
4
+ let mainWindow = null;
5
+ let tray = null;
6
+ let isOverlayActive = false;
7
+
8
+ // ─── Create the transparent overlay window ───────────────────────────────────
9
+ function createOverlayWindow() {
10
+ if (mainWindow && !mainWindow.isDestroyed()) {
11
+ mainWindow.destroy();
12
+ mainWindow = null;
13
+ }
14
+
15
+ const primaryDisplay = screen.getPrimaryDisplay();
16
+ const { width, height } = primaryDisplay.size;
17
+ const scaleFactor = primaryDisplay.scaleFactor;
18
+
19
+ mainWindow = new BrowserWindow({
20
+ x: primaryDisplay.bounds.x,
21
+ y: primaryDisplay.bounds.y,
22
+ width: width,
23
+ height: height,
24
+ frame: false,
25
+ transparent: true,
26
+ alwaysOnTop: true,
27
+ skipTaskbar: true,
28
+ resizable: false,
29
+ fullscreenable: true,
30
+ hasShadow: false,
31
+ show: false,
32
+ type: 'toolbar', // Helps on some Linux WMs
33
+ webPreferences: {
34
+ nodeIntegration: true,
35
+ contextIsolation: false,
36
+ webSecurity: false,
37
+ webviewTag: true,
38
+ backgroundThrottling: false
39
+ }
40
+ });
41
+
42
+ mainWindow.loadFile('index.html');
43
+ mainWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
44
+
45
+ mainWindow.on('closed', () => {
46
+ mainWindow = null;
47
+ isOverlayActive = false;
48
+ });
49
+
50
+ return mainWindow;
51
+ }
52
+
53
+ // ─── Trigger the overlay (Super+C action) ────────────────────────────────────
54
+ async function triggerOverlay() {
55
+ if (isOverlayActive) {
56
+ // If already active, dismiss it
57
+ if (mainWindow && !mainWindow.isDestroyed()) {
58
+ mainWindow.destroy();
59
+ mainWindow = null;
60
+ }
61
+ isOverlayActive = false;
62
+ return;
63
+ }
64
+
65
+ isOverlayActive = true;
66
+ createOverlayWindow();
67
+ }
68
+
69
+ // ─── Register the global shortcut ────────────────────────────────────────────
70
+ const SHORTCUT_COMBOS = ['Super+C', 'Ctrl+Shift+S', 'Ctrl+Alt+C'];
71
+ let registeredShortcut = null;
72
+
73
+ function registerShortcut() {
74
+ for (const combo of SHORTCUT_COMBOS) {
75
+ try { globalShortcut.unregister(combo); } catch (e) {}
76
+ }
77
+
78
+ for (const combo of SHORTCUT_COMBOS) {
79
+ try {
80
+ const registered = globalShortcut.register(combo, () => {
81
+ triggerOverlay();
82
+ });
83
+ if (registered) {
84
+ registeredShortcut = combo;
85
+ console.log(`[Carvus Lens] ${combo} registered successfully.`);
86
+ return;
87
+ }
88
+ } catch (e) {}
89
+ }
90
+
91
+ console.warn('[Carvus Lens] All shortcut registrations failed. Retrying in 3s...');
92
+ setTimeout(registerShortcut, 3000);
93
+ }
94
+
95
+ // ─── Create system tray for background service ──────────────────────────────
96
+ function createTray() {
97
+ // Create a small tray icon programmatically
98
+ const iconSize = 16;
99
+ const canvas = nativeImage.createFromBuffer(
100
+ Buffer.alloc(iconSize * iconSize * 4, 0),
101
+ { width: iconSize, height: iconSize }
102
+ );
103
+
104
+ // Try to use a proper icon, fallback to empty
105
+ try {
106
+ const iconPath = path.join(__dirname, 'icon.png');
107
+ tray = new Tray(iconPath);
108
+ } catch (e) {
109
+ tray = new Tray(canvas);
110
+ }
111
+
112
+ const contextMenu = Menu.buildFromTemplate([
113
+ { label: '🔍 Carvus Lens', enabled: false },
114
+ { type: 'separator' },
115
+ { label: 'Trigger (Super+C)', click: () => triggerOverlay() },
116
+ { type: 'separator' },
117
+ { label: 'Quit', click: () => { app.quit(); } }
118
+ ]);
119
+
120
+ tray.setToolTip('Carvus Lens — Press Super+C or Ctrl+Shift+S');
121
+ tray.setContextMenu(contextMenu);
122
+
123
+ tray.on('click', () => {
124
+ triggerOverlay();
125
+ });
126
+ }
127
+
128
+ // ─── App lifecycle ───────────────────────────────────────────────────────────
129
+ app.whenReady().then(() => {
130
+ createTray();
131
+ registerShortcut();
132
+
133
+ // Launch overlay immediately on first run
134
+ setTimeout(() => triggerOverlay(), 500);
135
+
136
+ // Re-register shortcut periodically in case it gets unregistered by the WM
137
+ setInterval(() => {
138
+ const anyRegistered = SHORTCUT_COMBOS.some(c => globalShortcut.isRegistered(c));
139
+ if (!anyRegistered) {
140
+ registerShortcut();
141
+ }
142
+ }, 8000);
143
+
144
+ const shortcutHint = registeredShortcut || 'Ctrl+Shift+S or Ctrl+Alt+C';
145
+ console.log(`[Carvus Lens] Background service running. Press ${shortcutHint} to activate.`);
146
+ });
147
+
148
+ app.on('will-quit', () => {
149
+ globalShortcut.unregisterAll();
150
+ });
151
+
152
+ // Keep the app running in the background — do NOT quit when all windows close
153
+ app.on('window-all-closed', (e) => {
154
+ // Prevent default quit behavior
155
+ });
156
+
157
+ // ─── IPC Handlers ────────────────────────────────────────────────────────────
158
+
159
+ // Get desktop stream source ID for screen capture
160
+ ipcMain.handle('get-desktop-stream-id', async () => {
161
+ try {
162
+ const sources = await desktopCapturer.getSources({
163
+ types: ['screen'],
164
+ thumbnailSize: { width: 1, height: 1 }
165
+ });
166
+ if (sources.length > 0) {
167
+ return sources[0].id;
168
+ }
169
+ throw new Error('No screen sources found');
170
+ } catch (err) {
171
+ console.error('[Carvus Lens] Failed to get desktop stream:', err);
172
+ throw err;
173
+ }
174
+ });
175
+
176
+ // Show the overlay window after screen capture is ready
177
+ ipcMain.on('show-window', () => {
178
+ if (mainWindow && !mainWindow.isDestroyed()) {
179
+ mainWindow.show();
180
+ mainWindow.focus();
181
+ mainWindow.setAlwaysOnTop(true, 'screen-saver');
182
+ }
183
+ });
184
+
185
+ // Dismiss the overlay
186
+ ipcMain.on('dismiss-overlay', () => {
187
+ if (mainWindow && !mainWindow.isDestroyed()) {
188
+ mainWindow.destroy();
189
+ mainWindow = null;
190
+ }
191
+ isOverlayActive = false;
192
+ });
193
+
194
+ // Quit the app entirely
195
+ ipcMain.on('quit-app', () => {
196
+ app.quit();
197
+ });
198
+
199
+ // Get display scale factor
200
+ ipcMain.handle('get-scale-factor', () => {
201
+ return screen.getPrimaryDisplay().scaleFactor;
202
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "carvus-lens",
3
+ "version": "1.0.0",
4
+ "description": "Circle-to-Search for desktop — draw a circle on your screen to instantly search Google Lens, get AI answers, and translate text. Powered by Tesseract OCR and Groq AI.",
5
+ "main": "main.js",
6
+ "bin": {
7
+ "carvus-lens": "./bin/cli.js"
8
+ },
9
+ "scripts": {
10
+ "start": "electron .",
11
+ "dev": "electron ."
12
+ },
13
+ "keywords": [
14
+ "circle-to-search",
15
+ "google-lens",
16
+ "screen-search",
17
+ "ocr",
18
+ "tesseract",
19
+ "groq",
20
+ "ai",
21
+ "electron",
22
+ "desktop",
23
+ "visual-search",
24
+ "carvus-lens"
25
+ ],
26
+ "author": "Aadil Fazal",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/aadilfazal/carvus-lens"
31
+ },
32
+ "dependencies": {
33
+ "electron": "^30.0.0",
34
+ "tesseract.js": "^7.0.0"
35
+ }
36
+ }