glassbox 0.3.0 → 0.3.2

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
@@ -65,13 +65,13 @@ Glassbox gives you a proper diff viewer with annotation categories designed for
65
65
  | **Note** | Context for the AI to consider. |
66
66
  | **Remember** | A rule to persist to the AI's long-term config. |
67
67
 
68
- When you're done, click **Complete Review** and tell your AI tool:
68
+ As you annotate, `.glassbox/latest-review.md` is updated automatically. You can tell your AI tool at any time:
69
69
 
70
70
  ```
71
71
  Read .glassbox/latest-review.md and apply the feedback.
72
72
  ```
73
73
 
74
- The AI gets a structured file with every annotation, organized by file and line number, with clear instructions on how to interpret each category. It fixes the bugs, applies your style preferences, avoids the anti-patterns, and updates its own config with your "remember" items.
74
+ The AI gets a structured file with every annotation, organized by file and line number, with clear instructions on how to interpret each category. It fixes the bugs, applies your style preferences, avoids the anti-patterns, and updates its own config with your "remember" items. You don't need to formally "complete" a review to share feedback — just annotate and switch to your AI tool.
75
75
 
76
76
  Then you run `glassbox` again. Your previous annotations carry forward — matched to the updated diff. Stale comments that no longer apply are flagged so you can keep or discard them. The loop continues until you're satisfied.
77
77
 
package/dist/cli.js CHANGED
@@ -3083,6 +3083,17 @@ async function generateReviewExport(reviewId, repoRoot, isCurrent) {
3083
3083
  return archivePath;
3084
3084
  }
3085
3085
 
3086
+ // src/export/auto-export.ts
3087
+ var debounceTimer = null;
3088
+ var DEBOUNCE_MS = 2e3;
3089
+ function scheduleAutoExport(reviewId, repoRoot) {
3090
+ if (debounceTimer !== null) clearTimeout(debounceTimer);
3091
+ debounceTimer = setTimeout(() => {
3092
+ debounceTimer = null;
3093
+ void generateReviewExport(reviewId, repoRoot, true);
3094
+ }, DEBOUNCE_MS);
3095
+ }
3096
+
3086
3097
  // src/git/image.ts
3087
3098
  import { execSync as execSync4 } from "child_process";
3088
3099
  import { readFileSync as readFileSync5 } from "fs";
@@ -3227,27 +3238,10 @@ async function extractMetadata(data, filePath) {
3227
3238
  exif: null
3228
3239
  };
3229
3240
  }
3230
- const sharp = (await import("sharp")).default;
3231
- const meta = await sharp(data).metadata();
3232
- let exif = null;
3233
- if (meta.exif) {
3234
- try {
3235
- exif = {};
3236
- if (meta.orientation) exif["Orientation"] = String(meta.orientation);
3237
- } catch {
3238
- }
3239
- }
3241
+ const parsed = parseImageHeaders(data, ext);
3240
3242
  return {
3241
- format: meta.format ?? ext.slice(1),
3242
- width: meta.width ?? null,
3243
- height: meta.height ?? null,
3244
- fileSize: data.length,
3245
- colorSpace: meta.space ?? null,
3246
- channels: meta.channels ?? null,
3247
- depth: meta.depth ?? null,
3248
- hasAlpha: meta.hasAlpha ?? null,
3249
- density: meta.density ?? null,
3250
- exif
3243
+ ...parsed,
3244
+ fileSize: data.length
3251
3245
  };
3252
3246
  }
3253
3247
  function formatMetadataLines(meta) {
@@ -3274,6 +3268,134 @@ function formatBytes(bytes) {
3274
3268
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
3275
3269
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
3276
3270
  }
3271
+ function parseImageHeaders(data, ext) {
3272
+ try {
3273
+ if (ext === ".png") return parsePng(data);
3274
+ if (ext === ".jpg" || ext === ".jpeg") return parseJpeg(data);
3275
+ if (ext === ".gif") return parseGif(data);
3276
+ if (ext === ".webp") return parseWebp(data);
3277
+ } catch {
3278
+ }
3279
+ return emptyMeta(ext.slice(1));
3280
+ }
3281
+ function emptyMeta(format) {
3282
+ return { format, width: null, height: null, colorSpace: null, channels: null, depth: null, hasAlpha: null, density: null, exif: null };
3283
+ }
3284
+ function parsePng(data) {
3285
+ if (data.length < 26) return emptyMeta("png");
3286
+ const width = data.readUInt32BE(16);
3287
+ const height = data.readUInt32BE(20);
3288
+ const bitDepth = data[24];
3289
+ const colorType = data[25];
3290
+ let colorSpace = null;
3291
+ let channels = null;
3292
+ let hasAlpha = null;
3293
+ switch (colorType) {
3294
+ case 0:
3295
+ colorSpace = "grayscale";
3296
+ channels = 1;
3297
+ hasAlpha = false;
3298
+ break;
3299
+ case 2:
3300
+ colorSpace = "srgb";
3301
+ channels = 3;
3302
+ hasAlpha = false;
3303
+ break;
3304
+ case 3:
3305
+ colorSpace = "indexed";
3306
+ channels = 1;
3307
+ hasAlpha = false;
3308
+ break;
3309
+ case 4:
3310
+ colorSpace = "grayscale";
3311
+ channels = 2;
3312
+ hasAlpha = true;
3313
+ break;
3314
+ case 6:
3315
+ colorSpace = "srgb";
3316
+ channels = 4;
3317
+ hasAlpha = true;
3318
+ break;
3319
+ }
3320
+ const density = parsePngDensity(data);
3321
+ return { format: "png", width, height, colorSpace, channels, depth: bitDepth ? String(bitDepth) : null, hasAlpha, density, exif: null };
3322
+ }
3323
+ function parsePngDensity(data) {
3324
+ const marker = Buffer.from("pHYs");
3325
+ const idx = data.indexOf(marker);
3326
+ if (idx === -1 || idx + 13 > data.length) return null;
3327
+ const unit = data[idx + 12];
3328
+ if (unit !== 1) return null;
3329
+ const ppuX = data.readUInt32BE(idx + 4);
3330
+ return Math.round(ppuX / 39.3701);
3331
+ }
3332
+ function parseJpeg(data) {
3333
+ let width = null;
3334
+ let height = null;
3335
+ let channels = null;
3336
+ let depth = null;
3337
+ let density = null;
3338
+ let i = 2;
3339
+ while (i < data.length - 1) {
3340
+ if (data[i] !== 255) {
3341
+ i++;
3342
+ continue;
3343
+ }
3344
+ const marker = data[i + 1];
3345
+ if (marker >= 192 && marker <= 195 || marker >= 197 && marker <= 199 || marker >= 201 && marker <= 203 || marker >= 205 && marker <= 207) {
3346
+ if (i + 9 < data.length) {
3347
+ depth = String(data[i + 4]);
3348
+ height = data.readUInt16BE(i + 5);
3349
+ width = data.readUInt16BE(i + 7);
3350
+ channels = data[i + 9];
3351
+ }
3352
+ break;
3353
+ }
3354
+ if (marker === 224 && i + 14 < data.length) {
3355
+ const units = data[i + 11];
3356
+ const xDensity = data.readUInt16BE(i + 12);
3357
+ if (units === 1) density = xDensity;
3358
+ else if (units === 2) density = Math.round(xDensity * 2.54);
3359
+ }
3360
+ if (i + 3 < data.length) {
3361
+ const len = data.readUInt16BE(i + 2);
3362
+ i += 2 + len;
3363
+ } else {
3364
+ break;
3365
+ }
3366
+ }
3367
+ const colorSpace = channels === 1 ? "grayscale" : channels === 3 ? "srgb" : null;
3368
+ return { format: "jpeg", width, height, colorSpace, channels, depth, hasAlpha: false, density, exif: null };
3369
+ }
3370
+ function parseGif(data) {
3371
+ if (data.length < 10) return emptyMeta("gif");
3372
+ const width = data.readUInt16LE(6);
3373
+ const height = data.readUInt16LE(8);
3374
+ return { format: "gif", width, height, colorSpace: "indexed", channels: null, depth: null, hasAlpha: null, density: null, exif: null };
3375
+ }
3376
+ function parseWebp(data) {
3377
+ if (data.length < 30) return emptyMeta("webp");
3378
+ let width = null;
3379
+ let height = null;
3380
+ let hasAlpha = null;
3381
+ const chunk = data.toString("ascii", 12, 16);
3382
+ if (chunk === "VP8 " && data.length >= 30) {
3383
+ width = data.readUInt16LE(26) & 16383;
3384
+ height = data.readUInt16LE(28) & 16383;
3385
+ hasAlpha = false;
3386
+ } else if (chunk === "VP8L" && data.length >= 25) {
3387
+ const bits = data.readUInt32LE(21);
3388
+ width = (bits & 16383) + 1;
3389
+ height = (bits >> 14 & 16383) + 1;
3390
+ hasAlpha = (bits >> 28 & 1) === 1;
3391
+ } else if (chunk === "VP8X" && data.length >= 30) {
3392
+ const flags = data[20];
3393
+ hasAlpha = (flags & 16) !== 0;
3394
+ width = (data[24] | data[25] << 8 | data[26] << 16) + 1;
3395
+ height = (data[27] | data[28] << 8 | data[29] << 16) + 1;
3396
+ }
3397
+ return { format: "webp", width, height, colorSpace: "srgb", channels: hasAlpha ? 4 : 3, depth: null, hasAlpha, density: null, exif: null };
3398
+ }
3277
3399
 
3278
3400
  // src/outline/parser.ts
3279
3401
  var BRACE_LANGS = /* @__PURE__ */ new Set([
@@ -3704,6 +3826,9 @@ apiRoutes.patch("/files/:fileId/status", async (c) => {
3704
3826
  await updateFileStatus(c.req.param("fileId"), status);
3705
3827
  return c.json({ ok: true });
3706
3828
  });
3829
+ function autoExport(c) {
3830
+ scheduleAutoExport(c.get("reviewId"), c.get("repoRoot"));
3831
+ }
3707
3832
  apiRoutes.post("/annotations", async (c) => {
3708
3833
  const body = await c.req.json();
3709
3834
  const annotation = await addAnnotation(
@@ -3713,34 +3838,41 @@ apiRoutes.post("/annotations", async (c) => {
3713
3838
  body.category,
3714
3839
  body.content
3715
3840
  );
3841
+ autoExport(c);
3716
3842
  return c.json(annotation, 201);
3717
3843
  });
3718
3844
  apiRoutes.patch("/annotations/:id", async (c) => {
3719
3845
  const { content, category } = await c.req.json();
3720
3846
  await updateAnnotation(c.req.param("id"), content, category);
3847
+ autoExport(c);
3721
3848
  return c.json({ ok: true });
3722
3849
  });
3723
3850
  apiRoutes.delete("/annotations/:id", async (c) => {
3724
3851
  await deleteAnnotation(c.req.param("id"));
3852
+ autoExport(c);
3725
3853
  return c.json({ ok: true });
3726
3854
  });
3727
3855
  apiRoutes.patch("/annotations/:id/move", async (c) => {
3728
3856
  const { lineNumber, side } = await c.req.json();
3729
3857
  await moveAnnotation(c.req.param("id"), lineNumber, side);
3858
+ autoExport(c);
3730
3859
  return c.json({ ok: true });
3731
3860
  });
3732
3861
  apiRoutes.post("/annotations/:id/keep", async (c) => {
3733
3862
  await markAnnotationCurrent(c.req.param("id"));
3863
+ autoExport(c);
3734
3864
  return c.json({ ok: true });
3735
3865
  });
3736
3866
  apiRoutes.post("/annotations/stale/delete-all", async (c) => {
3737
3867
  const reviewId = resolveReviewId(c);
3738
3868
  await deleteStaleAnnotations(reviewId);
3869
+ autoExport(c);
3739
3870
  return c.json({ ok: true });
3740
3871
  });
3741
3872
  apiRoutes.post("/annotations/stale/keep-all", async (c) => {
3742
3873
  const reviewId = resolveReviewId(c);
3743
3874
  await keepAllStaleAnnotations(reviewId);
3875
+ autoExport(c);
3744
3876
  return c.json({ ok: true });
3745
3877
  });
3746
3878
  apiRoutes.get("/annotations/all", async (c) => {
@@ -3955,16 +4087,14 @@ function ImageDiff({ file, diff }) {
3955
4087
  /* @__PURE__ */ jsx("div", { className: "slice-handle slice-handle-a" }),
3956
4088
  /* @__PURE__ */ jsx("div", { className: "slice-handle slice-handle-b" })
3957
4089
  ] }) }),
3958
- !hasComparison && /* @__PURE__ */ jsx("div", { className: "image-diff-single", children: [
3959
- /* @__PURE__ */ jsx(
3960
- "img",
3961
- {
3962
- src: `/api/image/${fileId}/${isAdded ? "new" : "old"}`,
3963
- alt: isAdded ? "New image" : "Deleted image"
3964
- }
3965
- ),
3966
- /* @__PURE__ */ jsx("p", { className: "image-diff-status", children: isAdded ? "New file" : "Deleted file" })
3967
- ] })
4090
+ !hasComparison && /* @__PURE__ */ jsx("div", { className: "image-diff-panel image-diff-visual", "data-panel": "image", children: /* @__PURE__ */ jsx("div", { className: "image-visual-canvas", "data-zoomable": "true", children: /* @__PURE__ */ jsx("div", { className: "image-zoom-wrap", children: /* @__PURE__ */ jsx(
4091
+ "img",
4092
+ {
4093
+ className: "image-layer image-layer-old",
4094
+ src: `/api/image/${fileId}/${isAdded ? "new" : "old"}`,
4095
+ alt: isAdded ? "New image" : "Deleted image"
4096
+ }
4097
+ ) }) }) })
3968
4098
  ]
3969
4099
  }
3970
4100
  );
@@ -4526,55 +4656,58 @@ pageRoutes.get("/", async (c) => {
4526
4656
  /* @__PURE__ */ jsx("button", { id: "update-banner-dismiss", className: "btn btn-sm", children: "Later" })
4527
4657
  ] })
4528
4658
  ] }),
4529
- /* @__PURE__ */ jsx("aside", { className: "sidebar", children: [
4530
- /* @__PURE__ */ jsx("div", { className: "sidebar-header", children: [
4531
- /* @__PURE__ */ jsx("h2", { children: review.repo_name }),
4532
- /* @__PURE__ */ jsx("span", { className: "review-mode", children: [
4533
- review.mode,
4534
- review.mode_args !== null && review.mode_args !== "" ? `: ${review.mode_args}` : ""
4659
+ /* @__PURE__ */ jsx("div", { className: "review-body", children: [
4660
+ /* @__PURE__ */ jsx("aside", { className: "sidebar", children: [
4661
+ /* @__PURE__ */ jsx("div", { className: "sidebar-header", children: [
4662
+ /* @__PURE__ */ jsx("h2", { children: review.repo_name }),
4663
+ /* @__PURE__ */ jsx("span", { className: "review-mode", children: [
4664
+ review.mode,
4665
+ review.mode_args !== null && review.mode_args !== "" ? `: ${review.mode_args}` : ""
4666
+ ] })
4667
+ ] }),
4668
+ /* @__PURE__ */ jsx("div", { className: "file-filter", children: /* @__PURE__ */ jsx("input", { type: "text", className: "file-filter-input", id: "file-filter", placeholder: "Filter files..." }) }),
4669
+ /* @__PURE__ */ jsx(FileList, { files, annotationCounts, staleCounts: {} }),
4670
+ /* @__PURE__ */ jsx("div", { className: "sidebar-footer", children: [
4671
+ /* @__PURE__ */ jsx("button", { className: "btn btn-primary btn-complete", id: "complete-review", children: "Complete Review" }),
4672
+ /* @__PURE__ */ jsx("a", { href: "/history", className: "btn btn-sm btn-link", children: "Review History" })
4535
4673
  ] })
4536
4674
  ] }),
4537
- /* @__PURE__ */ jsx("div", { className: "file-filter", children: /* @__PURE__ */ jsx("input", { type: "text", className: "file-filter-input", id: "file-filter", placeholder: "Filter files..." }) }),
4538
- /* @__PURE__ */ jsx(FileList, { files, annotationCounts, staleCounts: {} }),
4539
- /* @__PURE__ */ jsx("div", { className: "sidebar-footer", children: [
4540
- /* @__PURE__ */ jsx("button", { className: "btn btn-primary btn-complete", id: "complete-review", children: "Complete Review" }),
4541
- /* @__PURE__ */ jsx("a", { href: "/history", className: "btn btn-sm btn-link", children: "Review History" })
4542
- ] })
4543
- ] }),
4544
- /* @__PURE__ */ jsx("div", { className: "sidebar-resize", id: "sidebar-resize" }),
4545
- /* @__PURE__ */ jsx("main", { className: "main-content", children: [
4546
- /* @__PURE__ */ jsx("div", { className: "welcome-message", children: [
4547
- /* @__PURE__ */ jsx("h3", { children: "Select a file to begin reviewing" }),
4548
- /* @__PURE__ */ jsx("p", { children: [
4549
- files.length,
4550
- " file(s) to review"
4675
+ /* @__PURE__ */ jsx("div", { className: "sidebar-resize", id: "sidebar-resize" }),
4676
+ /* @__PURE__ */ jsx("main", { className: "main-content", children: [
4677
+ /* @__PURE__ */ jsx("div", { className: "welcome-message", children: [
4678
+ /* @__PURE__ */ jsx("h3", { children: "Select a file to begin reviewing" }),
4679
+ /* @__PURE__ */ jsx("p", { children: [
4680
+ files.length,
4681
+ " file(s) to review"
4682
+ ] }),
4683
+ /* @__PURE__ */ jsx("p", { className: "progress-summary", id: "progress-summary" })
4551
4684
  ] }),
4552
- /* @__PURE__ */ jsx("p", { className: "progress-summary", id: "progress-summary" })
4553
- ] }),
4554
- /* @__PURE__ */ jsx("div", { className: "diff-container", id: "diff-container", style: "display:none" }),
4555
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar", id: "diff-toolbar", style: "display:none", children: [
4556
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar-text", children: [
4557
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: [
4558
- /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
4559
- /* @__PURE__ */ jsx("button", { className: "segment active", "data-diff-mode": "split", children: "Split" }),
4560
- /* @__PURE__ */ jsx("button", { className: "segment", "data-diff-mode": "unified", children: "Unified" })
4685
+ /* @__PURE__ */ jsx("div", { className: "diff-container", id: "diff-container", style: "display:none" }),
4686
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar", id: "diff-toolbar", style: "display:none", children: [
4687
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar-text", children: [
4688
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: [
4689
+ /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
4690
+ /* @__PURE__ */ jsx("button", { className: "segment active", "data-diff-mode": "split", children: "Split" }),
4691
+ /* @__PURE__ */ jsx("button", { className: "segment", "data-diff-mode": "unified", children: "Unified" })
4692
+ ] }),
4693
+ /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "wrap-toggle", children: "Wrap" }),
4694
+ /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "whitespace-toggle", children: "Ignore Whitespace" })
4561
4695
  ] }),
4562
- /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "wrap-toggle", children: "Wrap" }),
4563
- /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "whitespace-toggle", children: "Ignore Whitespace" })
4696
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar-right", children: /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "language-btn", children: "Plain Text" }) })
4564
4697
  ] }),
4565
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar-right", children: /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "language-btn", children: "Plain Text" }) })
4566
- ] }),
4567
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar-image", style: "display:none", children: [
4568
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
4569
- /* @__PURE__ */ jsx("button", { className: "segment active", "data-image-mode": "metadata", children: "Metadata" }),
4570
- /* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "difference", children: "Difference" }),
4571
- /* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "slice", children: "Slice" })
4572
- ] }) }),
4573
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar-right", children: [
4574
- /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "out", title: "Zoom out", children: raw(zoomOutSvg) }),
4575
- /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "fit", title: "Fit to view", children: raw(fitSvg) }),
4576
- /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "actual", title: "Actual size (1:1)", children: raw(actualSizeSvg) }),
4577
- /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "in", title: "Zoom in", children: raw(zoomInSvg) })
4698
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar-image", style: "display:none", children: [
4699
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
4700
+ /* @__PURE__ */ jsx("button", { className: "segment active", "data-image-mode": "metadata", children: "Metadata" }),
4701
+ /* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "difference", children: "Difference" }),
4702
+ /* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "slice", children: "Slice" }),
4703
+ /* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "image", style: "display:none", children: "Image" })
4704
+ ] }) }),
4705
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar-right", children: [
4706
+ /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "out", title: "Zoom out", children: raw(zoomOutSvg) }),
4707
+ /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "fit", title: "Fit to view", children: raw(fitSvg) }),
4708
+ /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "actual", title: "Actual size (1:1)", children: raw(actualSizeSvg) }),
4709
+ /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "in", title: "Zoom in", children: raw(zoomInSvg) })
4710
+ ] })
4578
4711
  ] })
4579
4712
  ] })
4580
4713
  ] })
@@ -4626,56 +4759,59 @@ pageRoutes.get("/review/:reviewId", async (c) => {
4626
4759
  /* @__PURE__ */ jsx("button", { id: "update-banner-dismiss", className: "btn btn-sm", children: "Later" })
4627
4760
  ] })
4628
4761
  ] }),
4629
- /* @__PURE__ */ jsx("aside", { className: "sidebar", children: [
4630
- /* @__PURE__ */ jsx("div", { className: "sidebar-header", children: [
4631
- /* @__PURE__ */ jsx("h2", { children: review.repo_name }),
4632
- /* @__PURE__ */ jsx("span", { className: "review-mode", children: [
4633
- review.mode,
4634
- review.mode_args !== null && review.mode_args !== "" ? `: ${review.mode_args}` : ""
4762
+ /* @__PURE__ */ jsx("div", { className: "review-body", children: [
4763
+ /* @__PURE__ */ jsx("aside", { className: "sidebar", children: [
4764
+ /* @__PURE__ */ jsx("div", { className: "sidebar-header", children: [
4765
+ /* @__PURE__ */ jsx("h2", { children: review.repo_name }),
4766
+ /* @__PURE__ */ jsx("span", { className: "review-mode", children: [
4767
+ review.mode,
4768
+ review.mode_args !== null && review.mode_args !== "" ? `: ${review.mode_args}` : ""
4769
+ ] })
4770
+ ] }),
4771
+ /* @__PURE__ */ jsx("div", { className: "file-filter", children: /* @__PURE__ */ jsx("input", { type: "text", className: "file-filter-input", id: "file-filter", placeholder: "Filter files..." }) }),
4772
+ /* @__PURE__ */ jsx(FileList, { files, annotationCounts, staleCounts: {} }),
4773
+ /* @__PURE__ */ jsx("div", { className: "sidebar-footer", children: [
4774
+ review.status === "completed" ? /* @__PURE__ */ jsx("button", { className: "btn btn-primary", id: "reopen-review", children: "Reopen Review" }) : /* @__PURE__ */ jsx("button", { className: "btn btn-primary btn-complete", id: "complete-review", children: "Complete Review" }),
4775
+ /* @__PURE__ */ jsx("a", { href: "/history", className: "btn btn-sm btn-link", children: "Review History" }),
4776
+ /* @__PURE__ */ jsx("a", { href: "/", className: "btn btn-sm btn-link", children: "Back to current review" })
4635
4777
  ] })
4636
4778
  ] }),
4637
- /* @__PURE__ */ jsx("div", { className: "file-filter", children: /* @__PURE__ */ jsx("input", { type: "text", className: "file-filter-input", id: "file-filter", placeholder: "Filter files..." }) }),
4638
- /* @__PURE__ */ jsx(FileList, { files, annotationCounts, staleCounts: {} }),
4639
- /* @__PURE__ */ jsx("div", { className: "sidebar-footer", children: [
4640
- review.status === "completed" ? /* @__PURE__ */ jsx("button", { className: "btn btn-primary", id: "reopen-review", children: "Reopen Review" }) : /* @__PURE__ */ jsx("button", { className: "btn btn-primary btn-complete", id: "complete-review", children: "Complete Review" }),
4641
- /* @__PURE__ */ jsx("a", { href: "/history", className: "btn btn-sm btn-link", children: "Review History" }),
4642
- /* @__PURE__ */ jsx("a", { href: "/", className: "btn btn-sm btn-link", children: "Back to current review" })
4643
- ] })
4644
- ] }),
4645
- /* @__PURE__ */ jsx("div", { className: "sidebar-resize", id: "sidebar-resize" }),
4646
- /* @__PURE__ */ jsx("main", { className: "main-content", children: [
4647
- /* @__PURE__ */ jsx("div", { className: "welcome-message", children: [
4648
- /* @__PURE__ */ jsx("h3", { children: "Select a file to begin reviewing" }),
4649
- /* @__PURE__ */ jsx("p", { children: [
4650
- files.length,
4651
- " file(s) to review"
4779
+ /* @__PURE__ */ jsx("div", { className: "sidebar-resize", id: "sidebar-resize" }),
4780
+ /* @__PURE__ */ jsx("main", { className: "main-content", children: [
4781
+ /* @__PURE__ */ jsx("div", { className: "welcome-message", children: [
4782
+ /* @__PURE__ */ jsx("h3", { children: "Select a file to begin reviewing" }),
4783
+ /* @__PURE__ */ jsx("p", { children: [
4784
+ files.length,
4785
+ " file(s) to review"
4786
+ ] }),
4787
+ /* @__PURE__ */ jsx("p", { className: "progress-summary", id: "progress-summary" })
4652
4788
  ] }),
4653
- /* @__PURE__ */ jsx("p", { className: "progress-summary", id: "progress-summary" })
4654
- ] }),
4655
- /* @__PURE__ */ jsx("div", { className: "diff-container", id: "diff-container", style: "display:none" }),
4656
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar", id: "diff-toolbar", style: "display:none", children: [
4657
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar-text", children: [
4658
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: [
4659
- /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
4660
- /* @__PURE__ */ jsx("button", { className: "segment active", "data-diff-mode": "split", children: "Split" }),
4661
- /* @__PURE__ */ jsx("button", { className: "segment", "data-diff-mode": "unified", children: "Unified" })
4789
+ /* @__PURE__ */ jsx("div", { className: "diff-container", id: "diff-container", style: "display:none" }),
4790
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar", id: "diff-toolbar", style: "display:none", children: [
4791
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar-text", children: [
4792
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: [
4793
+ /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
4794
+ /* @__PURE__ */ jsx("button", { className: "segment active", "data-diff-mode": "split", children: "Split" }),
4795
+ /* @__PURE__ */ jsx("button", { className: "segment", "data-diff-mode": "unified", children: "Unified" })
4796
+ ] }),
4797
+ /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "wrap-toggle", children: "Wrap" }),
4798
+ /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "whitespace-toggle", children: "Ignore Whitespace" })
4662
4799
  ] }),
4663
- /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "wrap-toggle", children: "Wrap" }),
4664
- /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "whitespace-toggle", children: "Ignore Whitespace" })
4800
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar-right", children: /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "language-btn", children: "Plain Text" }) })
4665
4801
  ] }),
4666
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar-right", children: /* @__PURE__ */ jsx("button", { className: "toolbar-btn", id: "language-btn", children: "Plain Text" }) })
4667
- ] }),
4668
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar-image", style: "display:none", children: [
4669
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
4670
- /* @__PURE__ */ jsx("button", { className: "segment active", "data-image-mode": "metadata", children: "Metadata" }),
4671
- /* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "difference", children: "Difference" }),
4672
- /* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "slice", children: "Slice" })
4673
- ] }) }),
4674
- /* @__PURE__ */ jsx("div", { className: "diff-toolbar-right", children: [
4675
- /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "out", title: "Zoom out", children: raw(zoomOutSvg) }),
4676
- /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "fit", title: "Fit to view", children: raw(fitSvg) }),
4677
- /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "actual", title: "Actual size (1:1)", children: raw(actualSizeSvg) }),
4678
- /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "in", title: "Zoom in", children: raw(zoomInSvg) })
4802
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar-image", style: "display:none", children: [
4803
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
4804
+ /* @__PURE__ */ jsx("button", { className: "segment active", "data-image-mode": "metadata", children: "Metadata" }),
4805
+ /* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "difference", children: "Difference" }),
4806
+ /* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "slice", children: "Slice" }),
4807
+ /* @__PURE__ */ jsx("button", { className: "segment", "data-image-mode": "image", style: "display:none", children: "Image" })
4808
+ ] }) }),
4809
+ /* @__PURE__ */ jsx("div", { className: "diff-toolbar-right", children: [
4810
+ /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "out", title: "Zoom out", children: raw(zoomOutSvg) }),
4811
+ /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "fit", title: "Fit to view", children: raw(fitSvg) }),
4812
+ /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "actual", title: "Actual size (1:1)", children: raw(actualSizeSvg) }),
4813
+ /* @__PURE__ */ jsx("button", { className: "image-zoom-btn", "data-zoom-action": "in", title: "Zoom in", children: raw(zoomInSvg) })
4814
+ ] })
4679
4815
  ] })
4680
4816
  ] })
4681
4817
  ] })
@@ -5129,7 +5265,7 @@ async function main() {
5129
5265
  console.log("AI service test mode enabled \u2014 using mock AI responses");
5130
5266
  }
5131
5267
  if (debug) {
5132
- console.log(`[debug] Build timestamp: ${"2026-03-16T07:44:26.425Z"}`);
5268
+ console.log(`[debug] Build timestamp: ${"2026-03-16T09:50:09.819Z"}`);
5133
5269
  }
5134
5270
  if (projectDir) {
5135
5271
  process.chdir(projectDir);