Skip to content

Commit 93e663c

Browse files
Merge pull request #3 from James-HoneyBadger/feature/enhancements-v1.4
v1.4.0 — 20 enhancements + standalone application
2 parents b5e1dbe + 5a2d34a commit 93e663c

36 files changed

Lines changed: 3180 additions & 64 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ node_modules/
33

44
# Build output
55
dist/
6+
release/
67

78
# IDE
89
.idea/

README.md

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,18 +84,61 @@ const result = removeMetadataSync(imageBytes);
8484

8585
---
8686

87-
## Desktop App (Electron)
87+
## Standalone Installation (Linux)
8888

89-
HB_Scrub ships with a standalone Electron desktop application. No browser, no terminal, no configuration required.
89+
HB Scrub can be installed as a fully standalone application — both a system-wide CLI tool and a desktop GUI app under **Applications → Utility**. No `npm`, `node`, or development tools required after installation.
90+
91+
### Quick Install
9092

9193
```bash
92-
# Clone the repo and install dependencies
9394
git clone https://github.com/James-HoneyBadger/HB_Scrub.git
9495
cd HB_Scrub
95-
npm install
96+
npm install && npm run build
97+
npx @electron/packager . "HB Scrub" --platform=linux --arch=$(uname -m | sed 's/aarch64/arm64/;s/x86_64/x64/') --out=release --overwrite --asar
98+
sudo ./install.sh
99+
```
100+
101+
After installation:
102+
103+
```bash
104+
hb-scrub --help # CLI — available system-wide
105+
hb-scrub-gui # Launch the desktop GUI
106+
```
107+
108+
The desktop app also appears under **Applications → Utility** in your desktop environment.
109+
110+
### Uninstall
111+
112+
```bash
113+
sudo ./uninstall.sh
114+
```
115+
116+
### What gets installed
96117

97-
# Launch the desktop app
118+
| Component | Location |
119+
|---|---|
120+
| CLI wrapper | `/usr/local/bin/hb-scrub` |
121+
| GUI launcher | `/usr/local/bin/hb-scrub-gui` |
122+
| Application files | `/opt/hb-scrub/` |
123+
| Desktop entry | `/usr/share/applications/hb-scrub.desktop` |
124+
| Icons | `/usr/share/icons/hicolor/*/apps/hb-scrub.png` |
125+
126+
Reinstalling automatically removes the previous version first.
127+
128+
See [docs/installation.md](docs/installation.md) for detailed instructions including building from source and platform-specific notes.
129+
130+
---
131+
132+
## Desktop App (Electron)
133+
134+
HB_Scrub ships with a standalone Electron desktop application. No browser, no terminal, no configuration required.
135+
136+
```bash
137+
# Run from source (development)
98138
npm run electron
139+
140+
# Or launch the installed app
141+
hb-scrub-gui
99142
```
100143

101144
### Desktop App features
@@ -116,9 +159,11 @@ npm run electron
116159
To build a distributable package for your platform:
117160

118161
```bash
119-
npm run electron:build:linux # AppImage + .deb
120-
npm run electron:build:win # NSIS installer (.exe)
121-
npm run electron:build:mac # .dmg
162+
# Package the Electron app
163+
npx @electron/packager . "HB Scrub" --platform=linux --arch=arm64 --out=release --overwrite --asar
164+
165+
# Or run from source during development
166+
npm run electron
122167
```
123168

124169
Built packages are written to `release/`.

bench/bench.ts

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* Performance benchmarks for HB Scrub.
3+
*
4+
* Run:
5+
* npx tsx bench/bench.ts
6+
*
7+
* Generates synthetic test data of varying sizes and measures throughput
8+
* for the core operations: detectFormat, removeMetadata, verifyClean.
9+
*/
10+
11+
import { removeMetadataSync } from '../src/node.js';
12+
import { detectFormat } from '../src/detect.js';
13+
import { verifyCleanSync } from '../src/operations/verify.js';
14+
15+
// ─── Synthetic test image generators ─────────────────────────────────────────
16+
17+
/** Minimal valid JPEG with APP1 EXIF marker */
18+
function makeJpeg(payloadKB: number): Uint8Array {
19+
const exifPayload = new Uint8Array(payloadKB * 1024).fill(0x41);
20+
// SOI + APP1 marker
21+
const header = new Uint8Array([
22+
0xff, 0xd8, // SOI
23+
0xff, 0xe1, // APP1
24+
...uint16BE(exifPayload.length + 2 + 6), // length
25+
0x45, 0x78, 0x69, 0x66, 0x00, 0x00, // "Exif\0\0"
26+
]);
27+
// Minimal SOS + EOI
28+
const tail = new Uint8Array([0xff, 0xda, 0x00, 0x02, 0xff, 0xd9]);
29+
return concat(header, exifPayload, tail);
30+
}
31+
32+
/** Minimal valid PNG with tEXt chunk */
33+
function makePng(payloadKB: number): Uint8Array {
34+
const sig = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
35+
// Minimal IHDR
36+
const ihdr = pngChunk('IHDR', new Uint8Array([
37+
0, 0, 0, 1, 0, 0, 0, 1, 8, 2, 0, 0, 0,
38+
]));
39+
// tEXt chunk with payload
40+
const keyword = new TextEncoder().encode('Comment\0');
41+
const textData = new Uint8Array(payloadKB * 1024).fill(0x41);
42+
const text = pngChunk('tEXt', concat(keyword, textData));
43+
// IEND
44+
const iend = pngChunk('IEND', new Uint8Array(0));
45+
return concat(sig, ihdr, text, iend);
46+
}
47+
48+
// ─── Helpers ─────────────────────────────────────────────────────────────────
49+
50+
function uint16BE(n: number): number[] {
51+
return [(n >> 8) & 0xff, n & 0xff];
52+
}
53+
54+
function uint32BE(n: number): Uint8Array {
55+
return new Uint8Array([(n >>> 24) & 0xff, (n >>> 16) & 0xff, (n >>> 8) & 0xff, n & 0xff]);
56+
}
57+
58+
function concat(...bufs: Uint8Array[]): Uint8Array {
59+
const len = bufs.reduce((s, b) => s + b.length, 0);
60+
const out = new Uint8Array(len);
61+
let off = 0;
62+
for (const b of bufs) {
63+
out.set(b, off);
64+
off += b.length;
65+
}
66+
return out;
67+
}
68+
69+
function pngChunk(type: string, payload: Uint8Array): Uint8Array {
70+
const typeBytes = new TextEncoder().encode(type);
71+
const len = uint32BE(payload.length);
72+
// CRC placeholder (good enough for benchmarking)
73+
const crc = uint32BE(0);
74+
return concat(len, typeBytes, payload, crc);
75+
}
76+
77+
// ─── Benchmark runner ────────────────────────────────────────────────────────
78+
79+
interface BenchResult {
80+
name: string;
81+
opsPerSec: number;
82+
avgMs: number;
83+
runs: number;
84+
}
85+
86+
function bench(name: string, fn: () => void, durationMs = 2000): BenchResult {
87+
// Warm up
88+
for (let i = 0; i < 5; i++) fn();
89+
90+
let runs = 0;
91+
const start = performance.now();
92+
while (performance.now() - start < durationMs) {
93+
fn();
94+
runs++;
95+
}
96+
const elapsed = performance.now() - start;
97+
return {
98+
name,
99+
opsPerSec: Math.round((runs / elapsed) * 1000),
100+
avgMs: Number((elapsed / runs).toFixed(3)),
101+
runs,
102+
};
103+
}
104+
105+
function printResults(results: BenchResult[]): void {
106+
console.log('\n┌────────────────────────────────────────┬────────────┬──────────┬────────┐');
107+
console.log('│ Benchmark │ ops/sec │ avg ms │ runs │');
108+
console.log('├────────────────────────────────────────┼────────────┼──────────┼────────┤');
109+
for (const r of results) {
110+
console.log(
111+
`│ ${r.name.padEnd(38)}${String(r.opsPerSec).padStart(10)}${String(r.avgMs).padStart(8)}${String(r.runs).padStart(6)} │`
112+
);
113+
}
114+
console.log('└────────────────────────────────────────┴────────────┴──────────┴────────┘\n');
115+
}
116+
117+
// ─── Main ────────────────────────────────────────────────────────────────────
118+
119+
const sizes = [1, 10, 100, 500];
120+
const results: BenchResult[] = [];
121+
122+
for (const kb of sizes) {
123+
const jpegData = makeJpeg(kb);
124+
const pngData = makePng(kb);
125+
126+
results.push(bench(`detectFormat JPEG ${kb}KB`, () => detectFormat(jpegData)));
127+
results.push(bench(`detectFormat PNG ${kb}KB`, () => detectFormat(pngData)));
128+
results.push(bench(`removeSync JPEG ${kb}KB`, () => removeMetadataSync(jpegData)));
129+
results.push(bench(`removeSync PNG ${kb}KB`, () => removeMetadataSync(pngData)));
130+
results.push(bench(`verifyClean JPEG ${kb}KB`, () => verifyCleanSync(jpegData)));
131+
results.push(bench(`verifyClean PNG ${kb}KB`, () => verifyCleanSync(pngData)));
132+
}
133+
134+
printResults(results);

docs/installation.md

Lines changed: 80 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -76,51 +76,106 @@ Or if installed locally in a project:
7676

7777
---
7878

79-
## 3. Desktop App (Electron)
79+
## 3. Standalone Application (Linux)
8080

81-
HB_Scrub includes a standalone Electron desktop application — a full GUI with drag-and-drop, no browser or terminal required.
81+
HB Scrub can be installed as a fully standalone system application — a CLI accessible from any terminal and a desktop GUI appearing under **Applications → Utility**. Once installed, no `npm`, `node`, or development tools are required for the desktop GUI (the CLI wrapper still uses the system `node`).
82+
83+
### Prerequisites
84+
85+
- Node.js ≥ 20.0.0 (required only for building and for the CLI)
86+
- Git (to clone the repository)
87+
- A Linux desktop environment (KDE, GNOME, XFCE, etc.)
8288

83-
### Option A: Run from source
89+
### Step 1: Clone and build
8490

8591
```bash
86-
# Clone the repository
8792
git clone https://github.com/James-HoneyBadger/HB_Scrub.git
8893
cd HB_Scrub
89-
90-
# Install dependencies
9194
npm install
95+
npm run build
96+
```
9297

93-
# Build and launch the desktop app
94-
npm run electron
98+
### Step 2: Package the Electron app
99+
100+
```bash
101+
# Detect architecture automatically
102+
ARCH=$(uname -m | sed 's/aarch64/arm64/;s/x86_64/x64/')
103+
npx @electron/packager . "HB Scrub" --platform=linux --arch=$ARCH --out=release --overwrite --asar
95104
```
96105

97-
### Option B: Build a distributable package
106+
This creates a self-contained Electron application in `release/HB Scrub-linux-$ARCH/`.
107+
108+
### Step 3: Install
98109

99110
```bash
100-
# Linux — produces AppImage and .deb in release/
101-
npm run electron:build:linux
111+
sudo ./install.sh
112+
```
102113

103-
# Windows — produces NSIS installer (.exe) in release/
104-
npm run electron:build:win
114+
The install script:
115+
- Installs the GUI app to `/opt/hb-scrub/gui/`
116+
- Installs the CLI and dependencies to `/opt/hb-scrub/cli/`
117+
- Creates `/usr/local/bin/hb-scrub` (CLI) and `/usr/local/bin/hb-scrub-gui` (GUI)
118+
- Installs [hb-scrub.desktop](../hb-scrub.desktop) to `/usr/share/applications/`
119+
- Installs icons to `/usr/share/icons/hicolor/` (16px–512px + SVG)
120+
- Updates the icon cache and desktop database
105121

106-
# macOS — produces .dmg in release/
107-
npm run electron:build:mac
122+
If a previous installation exists, it is removed before the new one is installed.
108123

109-
# Current platform (auto-detect)
110-
npm run electron:build
124+
### Verify the installation
125+
126+
```bash
127+
# CLI
128+
hb-scrub --version
129+
hb-scrub --help
130+
131+
# GUI
132+
hb-scrub-gui
111133
```
112134

113-
Built packages are written to `release/`.
135+
The desktop app appears under **Applications → Utility** in your desktop environment's application menu.
136+
137+
### Uninstall
114138

115-
### Desktop app requirements
139+
```bash
140+
sudo ./uninstall.sh
141+
```
116142

117-
- **Linux**: No special deps. AppImage is portable; `.deb` installs via `dpkg -i`.
118-
- **Windows**: Installer handles all prerequisites.
119-
- **macOS**: Standard .dmg drag-to-Applications install.
143+
This removes all installed files, symlinks, icons, and the desktop entry.
144+
145+
### What gets installed
146+
147+
| Component | Location |
148+
|---|---|
149+
| CLI wrapper script | `/usr/local/bin/hb-scrub` |
150+
| GUI launcher script | `/usr/local/bin/hb-scrub-gui` |
151+
| CLI runtime (dist + node_modules) | `/opt/hb-scrub/cli/` |
152+
| Electron GUI app | `/opt/hb-scrub/gui/` |
153+
| Desktop entry | `/usr/share/applications/hb-scrub.desktop` |
154+
| Icons (PNG 16–512 + SVG) | `/usr/share/icons/hicolor/*/apps/hb-scrub.*` |
155+
156+
---
157+
158+
## 4. Desktop App (Electron) — Run from Source
159+
160+
HB_Scrub includes a standalone Electron desktop application — a full GUI with drag-and-drop, no browser or terminal required.
161+
162+
### Run from source (development)
163+
164+
```bash
165+
# Clone the repository
166+
git clone https://github.com/James-HoneyBadger/HB_Scrub.git
167+
cd HB_Scrub
168+
169+
# Install dependencies
170+
npm install
171+
172+
# Build and launch the desktop app
173+
npm run electron
174+
```
120175

121176
---
122177

123-
## 4. Local Web GUI (no Electron)
178+
## 5. Local Web GUI (no Electron)
124179

125180
If you prefer to run the GUI in a browser without the Electron wrapper:
126181

@@ -132,7 +187,7 @@ This builds the project and starts a local HTTP server at `http://localhost:3777
132187

133188
---
134189

135-
## 5. Build from Source
190+
## 6. Build from Source
136191

137192
```bash
138193
git clone https://github.com/James-HoneyBadger/HB_Scrub.git
@@ -269,4 +324,4 @@ Kill the conflicting process or change the port in `electron/main.cjs` and `src/
269324

270325
---
271326

272-
*Documentation for HB_Scrub v1.2.0 — © 2026 Honey Badger Universe*
327+
*Documentation for HB_Scrub v1.3.0 — © 2026 Honey Badger Universe*

electron/assets/icon.png

66.7 KB
Loading

0 commit comments

Comments
 (0)