Files
AeroFetch/src/main/badge.ts
T
debont80 1376c2dee8 Harden audit findings: correctness, type-safety, Windows conventions & polish
Audit-pass over CODE-AUDIT.md (~48 items closed this pass; all verified —
typecheck + 234 tests + eslint + prettier green).

Correctness / bugs:
- B3: match the release checksum to the asset's filename line (no wrong-hash verify)
- B4: newline-safe metadata probe (one --print with a unit-separator delimiter)
- B5 / L88: guard the meta event against canceled items; progress no longer promotes
  a queued item outside pump()
- B7: cookie-login promise always resolves (handles destroy-without-close)
- L146: trim parser rejects >2 colon-group times; M36: Library selection counts only
  actionable rows
- L11 / L50 / L156 / L57 / L159 / L15 / L3: live queue count, empty-cookie message,
  schedule picker min, dead-code/comment cleanup

Type safety:
- Enable noUncheckedIndexedAccess + noFallthroughCasesInSwitch (15 real edge cases fixed)

Resilience / Windows / metadata:
- R5: settings write failure handled (no unhandled IPC rejection; reconciles to truth)
- W1 / W5 / W6: min window size, seeded folder picker, parented sign-in window;
  L147 dead macOS branches removed
- CL1: shared stdout markers; package/builder metadata (license, homepage, repository,
  copyright, tsbuildinfo glob)

Copy / docs / tests:
- M37 / SR9 dev-jargon cleanup in hints; M8 / M25 / M26 / L66 / L80 / L81 reconciled
- New unit tests for L35 (isValidMediaItem) and L36 (compareVersions)

This commit also checkpoints the previously-uncommitted feat/tray-background-clipboard
work it builds on: background running + auto-download, library clipboard detection,
tray, binary management & library scale, credential encryption at rest, the shared
jsonStore and ui/ primitives, and the eslint/prettier tooling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 13:02:54 -04:00

88 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { deflateSync } from 'zlib'
import { nativeImage, type NativeImage } from 'electron'
// CRC32 table polynomial used by PNG.
function crc32(buf: Buffer): number {
let crc = 0xffffffff
for (const b of buf) {
crc ^= b
for (let i = 0; i < 8; i++) crc = crc & 1 ? (crc >>> 1) ^ 0xedb88320 : crc >>> 1
}
return (crc ^ 0xffffffff) >>> 0
}
function pngChunk(type: string, data: Buffer): Buffer {
const t = Buffer.from(type, 'ascii')
const len = Buffer.alloc(4)
len.writeUInt32BE(data.length)
const crc = Buffer.alloc(4)
crc.writeUInt32BE(crc32(Buffer.concat([t, data])))
return Buffer.concat([len, t, data, crc])
}
/**
* Build a 16×16 RGBA PNG containing a solid-colour filled circle. Used for the
* Windows taskbar overlay badge. No external deps — pure Node.js (Buffer + zlib).
*/
function makeDotPng(r: number, g: number, b: number): NativeImage {
const W = 16,
H = 16
const px = Buffer.alloc(W * H * 4, 0) // transparent bg
const cx = W / 2,
cy = H / 2,
rad = W / 2 - 1.5
for (let y = 0; y < H; y++) {
for (let x = 0; x < W; x++) {
const dx = x + 0.5 - cx,
dy = y + 0.5 - cy
if (dx * dx + dy * dy <= rad * rad) {
const i = (y * W + x) * 4
px[i] = r
px[i + 1] = g
px[i + 2] = b
px[i + 3] = 255
}
}
}
// PNG: IHDR (8-bit RGBA, no interlace), filter-0 rows, deflated IDAT, IEND.
const ihdr = Buffer.alloc(13)
ihdr.writeUInt32BE(W, 0)
ihdr.writeUInt32BE(H, 4)
ihdr[8] = 8 // bit depth
ihdr[9] = 6 // colour type: RGBA
const rows = Buffer.alloc(H * (1 + W * 4))
for (let y = 0; y < H; y++) {
rows[y * (1 + W * 4)] = 0 // filter type: None
px.copy(rows, y * (1 + W * 4) + 1, y * W * 4, (y + 1) * W * 4)
}
const sig = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])
const png = Buffer.concat([
sig,
pngChunk('IHDR', ihdr),
pngChunk('IDAT', deflateSync(rows)),
pngChunk('IEND', Buffer.alloc(0))
])
return nativeImage.createFromBuffer(png)
}
// Lazy-initialised singletons — created on first use so this module is safe to
// import before the app is ready (nativeImage.createFromBuffer is fine post-ready).
let activeBadge: NativeImage | null = null
let errorBadge: NativeImage | null = null
/** Green dot badge for the taskbar overlay — shown when downloads are active. */
export function getActiveBadge(): NativeImage {
if (!activeBadge) activeBadge = makeDotPng(58, 185, 108) // #3ab96c
return activeBadge
}
/** Red dot badge for the taskbar overlay — shown when a download has errored. */
export function getErrorBadge(): NativeImage {
if (!errorBadge) errorBadge = makeDotPng(217, 48, 37) // #d93025
return errorBadge
}