buddy-workbench 0.1.69 → 0.1.70
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/package.json +1 -1
- package/server/routes/clipboard.js +9 -1
- package/server/routes/updates.js +48 -22
- package/server/services/clipboard-history.js +40 -19
- package/ui/dist/assets/{index-DoX1juq6.js → index-BosNUFdB.js} +100 -100
- package/ui/dist/assets/{index-D5QYI1v4.css → index-CZUbxhFl.css} +1 -1
- package/ui/dist/index.html +2 -2
- package/ui/dist/sw.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import express, { Router } from 'express';
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { clipboardDates, clipboardItems, clipboardOriginal, deleteClipboardItem, saveClipboardImage, clipboardImagePath, allTaggedClipboardItems, updateClipboardItemTags, markClipboardCopied } from '../services/clipboard-history.js';
|
|
3
|
+
import { clipboardDates, clipboardItems, clipboardOriginal, deleteClipboardItem, saveClipboardImage, clipboardImagePath, allTaggedClipboardItems, updateClipboardItemTags, markClipboardCopied, copyClipboardImage } from '../services/clipboard-history.js';
|
|
4
4
|
|
|
5
5
|
const router = Router();
|
|
6
6
|
router.get('/', (req, res) => res.json({ dates: clipboardDates(), items: clipboardItems(req.query.date) }));
|
|
@@ -48,5 +48,13 @@ router.post('/:date/:id/copied', async (req, res) => {
|
|
|
48
48
|
res.status(500).json({ error: error.message });
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
|
+
router.post('/:date/:id/copy-image', async (req, res) => {
|
|
52
|
+
try {
|
|
53
|
+
await copyClipboardImage(req.params.date, req.params.id);
|
|
54
|
+
res.json({ ok: true });
|
|
55
|
+
} catch (error) {
|
|
56
|
+
res.status(500).json({ error: error.message });
|
|
57
|
+
}
|
|
58
|
+
});
|
|
51
59
|
router.delete('/:date/:id', (req, res) => { if (!deleteClipboardItem(req.params.date, req.params.id)) return res.status(404).json({ error: 'Clipboard entry not found.' }); res.status(204).end(); });
|
|
52
60
|
export default router;
|
package/server/routes/updates.js
CHANGED
|
@@ -83,29 +83,55 @@ function buildReleaseList(metadata) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
async function readNpmPackageMetadata() {
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
86
|
+
// npm knows about the user's ~/.npmrc, including private registries,
|
|
87
|
+
// proxies, authentication and custom CA settings. This matters in the
|
|
88
|
+
// internal-network desktop build, where those settings are not necessarily
|
|
89
|
+
// exposed as npm_config_* environment variables.
|
|
90
|
+
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
91
|
+
try {
|
|
92
|
+
const { stdout } = await execFileAsync(
|
|
93
|
+
npmCommand,
|
|
94
|
+
['view', `${PACKAGE_NAME}@latest`, 'version', 'time', 'devbuddyChangelog', '--json'],
|
|
95
|
+
{
|
|
96
|
+
timeout: 8000,
|
|
97
|
+
maxBuffer: 1024 * 1024,
|
|
98
|
+
shell: process.platform === 'win32',
|
|
99
|
+
windowsHide: true
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
const metadata = JSON.parse(stdout);
|
|
103
|
+
if (!metadata || typeof metadata !== 'object' || !metadata.version) {
|
|
104
|
+
throw new Error('The configured npm registry did not return package version metadata.');
|
|
105
|
+
}
|
|
106
|
+
return metadata;
|
|
107
|
+
} catch (npmError) {
|
|
108
|
+
// Keep a fallback for desktop launchers whose reduced PATH cannot find
|
|
109
|
+
// npm. It is intentionally secondary so it cannot bypass ~/.npmrc.
|
|
110
|
+
const registry = String(process.env.npm_config_registry || 'https://registry.npmjs.org/')
|
|
111
|
+
.trim()
|
|
112
|
+
.replace(/\/+$/, '');
|
|
113
|
+
try {
|
|
114
|
+
const response = await fetch(`${registry}/${encodeURIComponent(PACKAGE_NAME)}`, {
|
|
115
|
+
headers: { Accept: 'application/json' },
|
|
116
|
+
signal: AbortSignal.timeout(8000)
|
|
117
|
+
});
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
throw new Error(`The configured npm registry returned HTTP ${response.status}.`);
|
|
120
|
+
}
|
|
121
|
+
const packageMetadata = await response.json();
|
|
122
|
+
const metadata = {
|
|
123
|
+
version: packageMetadata?.['dist-tags']?.latest,
|
|
124
|
+
time: packageMetadata?.time,
|
|
125
|
+
devbuddyChangelog: packageMetadata?.devbuddyChangelog
|
|
126
|
+
};
|
|
127
|
+
if (!metadata.version) {
|
|
128
|
+
throw new Error('The configured npm registry did not return package version metadata.');
|
|
129
|
+
}
|
|
130
|
+
return metadata;
|
|
131
|
+
} catch {
|
|
132
|
+
throw npmError;
|
|
133
|
+
}
|
|
107
134
|
}
|
|
108
|
-
return metadata;
|
|
109
135
|
}
|
|
110
136
|
|
|
111
137
|
async function checkForUpdates() {
|
|
@@ -13,6 +13,7 @@ const execFileAsync = promisify(execFile);
|
|
|
13
13
|
const previewLimit = 2000;
|
|
14
14
|
let lastValue = '';
|
|
15
15
|
let lastImageHash = '';
|
|
16
|
+
let suppressImageCaptureUntil = 0;
|
|
16
17
|
let mozjpegWasmModule = null;
|
|
17
18
|
|
|
18
19
|
async function compressMozjpeg(rgbaBuf, width, height, quality = 75) {
|
|
@@ -304,7 +305,7 @@ export async function captureClipboard() {
|
|
|
304
305
|
} catch {}
|
|
305
306
|
|
|
306
307
|
// 2. Image capture (Mac only & clipboardImageEnabled !== false)
|
|
307
|
-
if (process.platform === 'darwin' && settings.clipboardImageEnabled !== false) {
|
|
308
|
+
if (process.platform === 'darwin' && settings.clipboardImageEnabled !== false && Date.now() >= suppressImageCaptureUntil) {
|
|
308
309
|
try {
|
|
309
310
|
const imageData = await getMacClipboardImageData();
|
|
310
311
|
if (imageData && imageData.rgbaBuf.length > 0) {
|
|
@@ -389,24 +390,13 @@ export async function markClipboardCopied(date, id) {
|
|
|
389
390
|
if (!item) return null;
|
|
390
391
|
|
|
391
392
|
if (item.imageFile) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
? item.imageHashes
|
|
400
|
-
: [item.imageHash, item.fileHash].filter(Boolean);
|
|
401
|
-
|
|
402
|
-
if (!existingHashes.includes(currentHash)) {
|
|
403
|
-
const updatedItem = { ...item, imageHashes: [...existingHashes, currentHash] };
|
|
404
|
-
const nextItems = items.map((entry) => (entry.id === id ? updatedItem : entry));
|
|
405
|
-
writeJson(dayFile(targetDate), nextItems);
|
|
406
|
-
return updatedItem;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
} catch {}
|
|
393
|
+
// Set the monitor's last hash before the browser writes the image. This
|
|
394
|
+
// prevents copying an existing image from being captured as a new item.
|
|
395
|
+
suppressImageCaptureUntil = Date.now() + 5000;
|
|
396
|
+
const existingHashes = Array.isArray(item.imageHashes)
|
|
397
|
+
? item.imageHashes
|
|
398
|
+
: [item.imageHash, item.fileHash].filter(Boolean);
|
|
399
|
+
lastImageHash = existingHashes[0] || '';
|
|
410
400
|
} else {
|
|
411
401
|
const text = clipboardOriginal(targetDate, id) || item.text || item.preview || '';
|
|
412
402
|
if (text) {
|
|
@@ -416,6 +406,37 @@ export async function markClipboardCopied(date, id) {
|
|
|
416
406
|
return item;
|
|
417
407
|
}
|
|
418
408
|
|
|
409
|
+
export async function copyClipboardImage(date, id) {
|
|
410
|
+
if (process.platform !== 'darwin') throw new Error('Native image clipboard is only supported on macOS.');
|
|
411
|
+
|
|
412
|
+
let item = dayItems(date).find((entry) => entry.id === id);
|
|
413
|
+
if (!item) {
|
|
414
|
+
for (const d of clipboardDates()) {
|
|
415
|
+
item = dayItems(d).find((entry) => entry.id === id);
|
|
416
|
+
if (item) break;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (!item?.imageFile) throw new Error('Clipboard image entry not found.');
|
|
420
|
+
|
|
421
|
+
await markClipboardCopied(date, id);
|
|
422
|
+
const imagePath = clipboardImagePath(item.imageFile);
|
|
423
|
+
const swiftCode = `
|
|
424
|
+
import Cocoa
|
|
425
|
+
let path = CommandLine.arguments[1]
|
|
426
|
+
if let image = NSImage(contentsOfFile: path),
|
|
427
|
+
let tiff = image.tiffRepresentation,
|
|
428
|
+
let rep = NSBitmapImageRep(data: tiff),
|
|
429
|
+
let png = rep.representation(using: .png, properties: [:]) {
|
|
430
|
+
let pasteboard = NSPasteboard.general
|
|
431
|
+
pasteboard.clearContents()
|
|
432
|
+
if !pasteboard.setData(png, forType: .png) { exit(1) }
|
|
433
|
+
} else {
|
|
434
|
+
exit(1)
|
|
435
|
+
}
|
|
436
|
+
`;
|
|
437
|
+
await execFileAsync('swift', ['-e', swiftCode, imagePath], { timeout: 5000 });
|
|
438
|
+
}
|
|
439
|
+
|
|
419
440
|
export function saveClipboardImage(buffer, ext = 'png', imageHash = '') {
|
|
420
441
|
const date = today();
|
|
421
442
|
const id = randomUUID();
|