glassbox 0.17.0 → 0.18.0-beta.1

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/README.md CHANGED
@@ -87,7 +87,10 @@ Then you run `glassbox` again. Your previous annotations carry forward — match
87
87
 
88
88
  - **Split and unified diffs** with syntax-colored add/remove/context lines
89
89
  - **Line-level annotations** — click any line to add feedback with a category
90
- - **Image and SVG diffs** — binary images and SVGs render side-by-side with a **difference overlay**, a **swipeable slice tool**, and pixel-precision zoom/pan. SVGs offer a *Code* / *Rendered* toggle so you can review either the source or the rasterized output.
90
+ - **Image and SVG diffs** — binary images and SVGs render in a **side-by-side** layout (with a left/right or over/under toggle), a **difference overlay**, a **swipeable slice tool**, and pixel-precision synced zoom/pan. SVGs offer a *Code* / *Rendered* toggle so you can review either the source or the rasterized output.
91
+ - **Comment on images** — leave a general comment on any image, or **draw a rectangle region** to anchor feedback to a specific spot (per A/B side), just like a line annotation.
92
+ - **Ground-truth image comparison** — `glassbox --ground-truth <manifest>` compares an **actual** render against an **expected** one (a design spec, a reference, or a previous baseline — even outside the repo), scores the perceptual difference, sorts most-changed-first, and lets you **promote** a new actual to the baseline. Great for catching visual regressions.
93
+ - **Attach files to any comment** — drag, paste, or browse to attach screenshots, logs, or any file to a line comment, image comment, or note reply. Image attachments show a thumbnail and open in a lightbox; the export lists each file's path so your AI tool can read it.
91
94
  - **Compare any two paths** — `glassbox --diff <A> <B>` reviews two arbitrary files or two arbitrary folders by path — no git repository required.
92
95
  - **Drag and drop** annotations to different lines
93
96
  - **Double-click** to edit, click the category badge to reclassify
@@ -96,13 +99,13 @@ Then you run `glassbox` again. Your previous annotations carry forward — match
96
99
  - **Resizable sidebar** and word wrap toggle
97
100
  - **Keyboard navigation** — `j`/`k` to move between files, `Cmd+Enter` to save
98
101
  - **Go-to-definition and a nav stack** — click any symbol to jump to its definition, with back/forward through your trail
99
- - **Themes** — built-in Dark, Light, High Contrast, Dracula, and Tokyo Night, plus a custom theme editor with live preview
102
+ - **Themes** — ten built-in themes (Dark, Light, High Contrast Dark/Light, Dracula, Tokyo Night, One Dark Pro, Solarized Dark/Light, Monokai), plus a custom theme editor with live preview
100
103
  - **Session persistence** — reviews survive restarts, pick up where you left off
101
104
  - **Smart review reuse** — re-running `glassbox` on the same commit updates diffs in place and migrates annotations to their new line positions
102
105
  - **Stale annotation detection** — comments that can't be matched to the updated diff are flagged with a visual indicator
103
106
  - **Review history** — browse, reopen, or delete past reviews
104
107
  - **Structured export** — markdown output with file paths, line numbers, categories, and instructions for AI consumption
105
- - **Automatic .gitignore prompt** — reminds you to exclude `.glassbox/` from version control
108
+ - **Automatic .gitignore** — keeps `.glassbox/` out of version control for you at launch, while leaving the per-project `.glassbox/settings.json` tracked
106
109
  - **Auto port selection** — if the default port is busy, it finds an open one
107
110
  - **Fully local** — no network calls (unless you opt into AI features), no accounts, no telemetry. Your code stays on your machine.
108
111
  - **AI review notes** _(optional)_ — render the generating AI's line-anchored rationale and proof, committed alongside the code in `.pr-notes/`, right in the diff — and reply to any note inline
@@ -110,6 +113,18 @@ Then you run `glassbox` again. Your previous annotations carry forward — match
110
113
 
111
114
  ---
112
115
 
116
+ ## Review more than code
117
+
118
+ Diffs aren't only text. Glassbox reviews **images** as a first-class diff: a side-by-side layout, a pixel **difference overlay**, and a swipeable slice tool — with synced zoom/pan. Leave a comment on the whole image or draw a region to anchor it to a spot.
119
+
120
+ <img src="assets/demo-image-comparison.png" alt="Image diff with a pixel difference overlay" width="720">
121
+
122
+ And when "correct" means "matches the design," **ground-truth comparison** (`glassbox --ground-truth <manifest>`) puts each **actual** render next to its **expected** one — a design spec, a reference render, or a previous baseline, even outside the repo. Glassbox scores the perceptual difference, hides identical pairs, sorts the rest most-changed-first, and lets you **promote** a new actual to the baseline once a change is intentional. Multi-step flows render as ordered, named sets.
123
+
124
+ <img src="assets/demo-ground-truth.png" alt="Ground-truth comparison: expected vs actual with a perceptual difference score" width="720">
125
+
126
+ ---
127
+
113
128
  ## AI-Powered Review Intelligence
114
129
 
115
130
  > Entirely optional. Glassbox is fully functional without it.
@@ -188,6 +203,12 @@ See [docs/20-ai-review-notes.md](docs/20-ai-review-notes.md) for the format and
188
203
 
189
204
  ---
190
205
 
206
+ ## For AI tools: integrating with Glassbox
207
+
208
+ If you're an AI coding tool or agent working in a project that uses Glassbox, **[docs/ai-integration.md](docs/ai-integration.md)** is the integration hub — how to launch a review, record line-anchored rationale/proof (`glassbox note`), export ground-truth image proof, consume the exported feedback (`.glassbox/latest-review.md`), and respond to the MCP/Claude channel. The repo's [AGENTS.md](AGENTS.md) is a one-screen router to it (and, separately, to the contributor docs for agents hacking on Glassbox itself).
209
+
210
+ ---
211
+
191
212
  ## Install
192
213
 
193
214
  ### Desktop app (recommended)
@@ -249,6 +270,9 @@ glassbox --all
249
270
  # Compare two arbitrary files or folders by path (no git repo required)
250
271
  glassbox --diff ./dist-old ./dist-new
251
272
 
273
+ # Compare actual images against expected/ground-truth images (no git repo required)
274
+ glassbox --ground-truth ./screenshots/manifest.json
275
+
252
276
  # Resume a previous review
253
277
  glassbox --resume
254
278
  ```
@@ -267,6 +291,7 @@ glassbox --resume
267
291
  | `--files <patterns>` | Specific files (comma-separated globs) |
268
292
  | `--all` | Entire codebase (all tracked files) |
269
293
  | `--diff <a> <b>` | Compare two arbitrary files or folders by path — no git repo required |
294
+ | `--ground-truth <m>` | Compare actual vs expected images from a manifest — no git repo required |
270
295
  | `--port <number>` | Port to run on (default: 4183) |
271
296
  | `--resume` | Resume the latest in-progress review for this mode |
272
297
  | `--browser` | Open in browser instead of desktop window |
package/dist/channel.js CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  #!/usr/bin/env node
3
+ import { createRequire as __createRequire } from 'module';
4
+ const require = __createRequire(import.meta.url);
3
5
 
4
6
  // src/channel.ts
5
7
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __createRequire } from 'module';
3
+ const require = __createRequire(import.meta.url);
2
4
  var __defProp = Object.defineProperty;
3
5
  var __export = (target2, all) => {
4
6
  for (var name in all)
@@ -14697,6 +14699,13 @@ function planSnapshot(local2, remote2, work, isFile, basename2) {
14697
14699
  ]
14698
14700
  };
14699
14701
  }
14702
+ function isDirDiffInvocation(local2, stat) {
14703
+ try {
14704
+ return stat(local2).isDirectory();
14705
+ } catch {
14706
+ return false;
14707
+ }
14708
+ }
14700
14709
  function launcherShimName(platform) {
14701
14710
  if (platform === "darwin") return "glassbox";
14702
14711
  if (platform === "linux") return "glassbox-linux";
@@ -14742,11 +14751,8 @@ if (args.length < 2 || args[0].startsWith("-") || args[1].startsWith("-")) {
14742
14751
  process.exit(1);
14743
14752
  }
14744
14753
  var [local, remote, merged, ...extra] = args;
14745
- var localIsFile = false;
14746
- try {
14747
- localIsFile = statSync2(local).isFile();
14748
- } catch {
14749
- }
14754
+ var isDirDiff = isDirDiffInvocation(local, statSync2);
14755
+ var isPerFile = !isDirDiff;
14750
14756
  var here = dirname(fileURLToPath(import.meta.url));
14751
14757
  var target = resolveLaunchTarget(
14752
14758
  here,
@@ -14779,7 +14785,7 @@ async function runThinClient(start, timeoutMs) {
14779
14785
  }
14780
14786
  function runBlockingLaunch() {
14781
14787
  const work = mkdtempSync(join3(tmpdir(), "glassbox-difftool-"));
14782
- const plan = planSnapshot(local, remote, work, localIsFile, basename);
14788
+ const plan = planSnapshot(local, remote, work, isPerFile, basename);
14783
14789
  const cleanup = () => {
14784
14790
  try {
14785
14791
  rmSync2(work, { recursive: true, force: true });
@@ -14819,7 +14825,7 @@ var onThinClientError = (err) => {
14819
14825
  console.error(err instanceof Error ? err.message : String(err));
14820
14826
  return process.exit(1);
14821
14827
  };
14822
- if (localIsFile && target.kind === "browser") {
14828
+ if (isPerFile && target.kind === "browser") {
14823
14829
  const cliPath = target.cli;
14824
14830
  void runThinClient(() => {
14825
14831
  spawnDetachedBrowserServer(cliPath, process.cwd());
@@ -14827,7 +14833,7 @@ if (localIsFile && target.kind === "browser") {
14827
14833
  onThinClientDone,
14828
14834
  onThinClientError
14829
14835
  );
14830
- } else if (localIsFile && target.kind === "desktop") {
14836
+ } else if (isPerFile && target.kind === "desktop") {
14831
14837
  const launcher = target.launcher;
14832
14838
  void runThinClient(
14833
14839
  () => {