critique 0.1.40 → 0.1.42
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/CHANGELOG.md +22 -0
- package/bun.lock +34 -10
- package/package.json +7 -3
- package/scripts/preview-review.tsx +3 -3
- package/src/ansi-html.ts +4 -0
- package/src/cli.tsx +149 -6
- package/src/components/diff-view.tsx +3 -1
- package/src/components/index.ts +3 -1
- package/src/diff-utils.ts +3 -1
- package/src/dropdown.tsx +4 -0
- package/src/image.ts +304 -0
- package/src/logger.ts +3 -1
- package/src/monochrome.ts +4 -0
- package/src/monotone.ts +4 -0
- package/src/review/acp-client.ts +83 -90
- package/src/review/acp-stream-display.ts +3 -2
- package/src/review/diagram-parser.ts +3 -2
- package/src/review/hunk-parser.ts +3 -1
- package/src/review/index.ts +3 -2
- package/src/review/review-app.test.tsx +224 -0
- package/src/review/review-app.tsx +3 -1
- package/src/review/session-context.ts +3 -1
- package/src/review/storage.ts +11 -5
- package/src/review/stream-display.tsx +3 -2
- package/src/review/types.ts +3 -1
- package/src/review/yaml-watcher.ts +3 -1
- package/src/store.ts +3 -2
- package/src/themes.ts +3 -2
- package/src/utils.ts +3 -0
- package/src/web-utils.ts +3 -2
- package/src/worker.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
# 0.1.42
|
|
2
|
+
|
|
3
|
+
- New `--image` flag for all diff commands:
|
|
4
|
+
- Generates WebP images of terminal output (saved to /tmp)
|
|
5
|
+
- Splits long diffs into multiple images (70 lines per image)
|
|
6
|
+
- Uses takumi for high-performance image rendering
|
|
7
|
+
- `@takumi-rs/core` and `@takumi-rs/helpers` added as optional dependencies
|
|
8
|
+
- Library export: `import { renderTerminalToImages } from "critique/src/image.ts"`
|
|
9
|
+
- Web output: Use default theme to enable dark/light mode switching based on system preference
|
|
10
|
+
- `review` command:
|
|
11
|
+
- Improved AI prompt: order hunks by code flow, think upfront before writing, split heavy logic across sections
|
|
12
|
+
- Dependencies:
|
|
13
|
+
- Update opentui to `367a9408`
|
|
14
|
+
|
|
15
|
+
# 0.1.41
|
|
16
|
+
|
|
17
|
+
- `review` command:
|
|
18
|
+
- Filter `--resume` reviews by current working directory (only shows reviews from cwd or subdirectories)
|
|
19
|
+
- Use ACP `unstable_listSessions` for OpenCode instead of parsing JSON files directly
|
|
20
|
+
- Falls back to file-based parsing for Claude Code when ACP method unavailable
|
|
21
|
+
- Add instruction to always close code blocks before new text (fixes unclosed diagram blocks)
|
|
22
|
+
|
|
1
23
|
# 0.1.40
|
|
2
24
|
|
|
3
25
|
- `review` command:
|
package/bun.lock
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@agentclientprotocol/sdk": "^0.12.0",
|
|
9
9
|
"@clack/prompts": "1.0.0-alpha.9",
|
|
10
|
-
"@opentui/core": "https://pkg.pr.new/anomalyco/opentui/@opentui/core@
|
|
11
|
-
"@opentui/react": "https://pkg.pr.new/anomalyco/opentui/@opentui/react@
|
|
10
|
+
"@opentui/core": "https://pkg.pr.new/anomalyco/opentui/@opentui/core@367a94087821b3b5feedd35bbb57df43b10a286e",
|
|
11
|
+
"@opentui/react": "https://pkg.pr.new/anomalyco/opentui/@opentui/react@367a94087821b3b5feedd35bbb57df43b10a286e",
|
|
12
12
|
"@parcel/watcher": "^2.5.1",
|
|
13
13
|
"bun-pty": "^0.4.7",
|
|
14
14
|
"cac": "^6.7.14",
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
"typescript": "^5.9.3",
|
|
28
28
|
"wrangler": "^4.19.1",
|
|
29
29
|
},
|
|
30
|
+
"optionalDependencies": {
|
|
31
|
+
"@takumi-rs/core": "^0.65.0",
|
|
32
|
+
"@takumi-rs/helpers": "^0.65.0",
|
|
33
|
+
},
|
|
30
34
|
},
|
|
31
35
|
},
|
|
32
36
|
"packages": {
|
|
@@ -222,21 +226,21 @@
|
|
|
222
226
|
|
|
223
227
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="],
|
|
224
228
|
|
|
225
|
-
"@opentui/core": ["@opentui/core@https://pkg.pr.new/anomalyco/opentui/@opentui/core@
|
|
229
|
+
"@opentui/core": ["@opentui/core@https://pkg.pr.new/anomalyco/opentui/@opentui/core@367a94087821b3b5feedd35bbb57df43b10a286e", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "https://pkg.pr.new/anomalyco/opentui/@opentui/core-darwin-arm64@367a94087821b3b5feedd35bbb57df43b10a286e", "@opentui/core-darwin-x64": "https://pkg.pr.new/anomalyco/opentui/@opentui/core-darwin-x64@367a94087821b3b5feedd35bbb57df43b10a286e", "@opentui/core-linux-arm64": "https://pkg.pr.new/anomalyco/opentui/@opentui/core-linux-arm64@367a94087821b3b5feedd35bbb57df43b10a286e", "@opentui/core-linux-x64": "https://pkg.pr.new/anomalyco/opentui/@opentui/core-linux-x64@367a94087821b3b5feedd35bbb57df43b10a286e", "@opentui/core-win32-arm64": "https://pkg.pr.new/anomalyco/opentui/@opentui/core-win32-arm64@367a94087821b3b5feedd35bbb57df43b10a286e", "@opentui/core-win32-x64": "https://pkg.pr.new/anomalyco/opentui/@opentui/core-win32-x64@367a94087821b3b5feedd35bbb57df43b10a286e", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }],
|
|
226
230
|
|
|
227
|
-
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@https://pkg.pr.new/anomalyco/opentui/@opentui/core-darwin-arm64@
|
|
231
|
+
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@https://pkg.pr.new/anomalyco/opentui/@opentui/core-darwin-arm64@367a94087821b3b5feedd35bbb57df43b10a286e", {}],
|
|
228
232
|
|
|
229
|
-
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@https://pkg.pr.new/anomalyco/opentui/@opentui/core-darwin-x64@
|
|
233
|
+
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@https://pkg.pr.new/anomalyco/opentui/@opentui/core-darwin-x64@367a94087821b3b5feedd35bbb57df43b10a286e", {}],
|
|
230
234
|
|
|
231
|
-
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@https://pkg.pr.new/anomalyco/opentui/@opentui/core-linux-arm64@
|
|
235
|
+
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@https://pkg.pr.new/anomalyco/opentui/@opentui/core-linux-arm64@367a94087821b3b5feedd35bbb57df43b10a286e", {}],
|
|
232
236
|
|
|
233
|
-
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@https://pkg.pr.new/anomalyco/opentui/@opentui/core-linux-x64@
|
|
237
|
+
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@https://pkg.pr.new/anomalyco/opentui/@opentui/core-linux-x64@367a94087821b3b5feedd35bbb57df43b10a286e", {}],
|
|
234
238
|
|
|
235
|
-
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@https://pkg.pr.new/anomalyco/opentui/@opentui/core-win32-arm64@
|
|
239
|
+
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@https://pkg.pr.new/anomalyco/opentui/@opentui/core-win32-arm64@367a94087821b3b5feedd35bbb57df43b10a286e", {}],
|
|
236
240
|
|
|
237
|
-
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@https://pkg.pr.new/anomalyco/opentui/@opentui/core-win32-x64@
|
|
241
|
+
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@https://pkg.pr.new/anomalyco/opentui/@opentui/core-win32-x64@367a94087821b3b5feedd35bbb57df43b10a286e", {}],
|
|
238
242
|
|
|
239
|
-
"@opentui/react": ["@opentui/react@https://pkg.pr.new/anomalyco/opentui/@opentui/react@
|
|
243
|
+
"@opentui/react": ["@opentui/react@https://pkg.pr.new/anomalyco/opentui/@opentui/react@367a94087821b3b5feedd35bbb57df43b10a286e", { "dependencies": { "@opentui/core": "https://pkg.pr.new/anomalyco/opentui/@opentui/core@367a94087821b3b5feedd35bbb57df43b10a286e", "react-reconciler": "^0.32.0" }, "peerDependencies": { "react": ">=19.0.0", "react-devtools-core": "^7.0.1", "ws": "^8.18.0" } }],
|
|
240
244
|
|
|
241
245
|
"@parcel/watcher": ["@parcel/watcher@2.5.4", "", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.3" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.4", "@parcel/watcher-darwin-arm64": "2.5.4", "@parcel/watcher-darwin-x64": "2.5.4", "@parcel/watcher-freebsd-x64": "2.5.4", "@parcel/watcher-linux-arm-glibc": "2.5.4", "@parcel/watcher-linux-arm-musl": "2.5.4", "@parcel/watcher-linux-arm64-glibc": "2.5.4", "@parcel/watcher-linux-arm64-musl": "2.5.4", "@parcel/watcher-linux-x64-glibc": "2.5.4", "@parcel/watcher-linux-x64-musl": "2.5.4", "@parcel/watcher-win32-arm64": "2.5.4", "@parcel/watcher-win32-ia32": "2.5.4", "@parcel/watcher-win32-x64": "2.5.4" } }, "sha512-WYa2tUVV5HiArWPB3ydlOc4R2ivq0IDrlqhMi3l7mVsFEXNcTfxYFPIHXHXIh/ca/y/V5N4E1zecyxdIBjYnkQ=="],
|
|
242
246
|
|
|
@@ -276,6 +280,26 @@
|
|
|
276
280
|
|
|
277
281
|
"@speed-highlight/core": ["@speed-highlight/core@1.2.14", "", {}, "sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA=="],
|
|
278
282
|
|
|
283
|
+
"@takumi-rs/core": ["@takumi-rs/core@0.65.0", "", { "optionalDependencies": { "@takumi-rs/core-darwin-arm64": "0.65.0", "@takumi-rs/core-darwin-x64": "0.65.0", "@takumi-rs/core-linux-arm64-gnu": "0.65.0", "@takumi-rs/core-linux-arm64-musl": "0.65.0", "@takumi-rs/core-linux-x64-gnu": "0.65.0", "@takumi-rs/core-linux-x64-musl": "0.65.0", "@takumi-rs/core-win32-arm64-msvc": "0.65.0", "@takumi-rs/core-win32-x64-msvc": "0.65.0" } }, "sha512-lBNG+NRc602ul7Kxy9UohlbnFLVyloQP/DTxQzyNH+8khpdaIQTxpdHouzRzxf6upRDVDIVX5TG8oT16jKUAvQ=="],
|
|
284
|
+
|
|
285
|
+
"@takumi-rs/core-darwin-arm64": ["@takumi-rs/core-darwin-arm64@0.65.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Ii6ILOANG6IeGThsuYjG5azlHXyHWWdVbM1ps3+SJyUg2g4Qn+nTruKGqHQOLNdZ5+37vpTg4PWh79XrUvjXpw=="],
|
|
286
|
+
|
|
287
|
+
"@takumi-rs/core-darwin-x64": ["@takumi-rs/core-darwin-x64@0.65.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-SJGeFVQ2foVHpYL6EiD+jNJnvQmegDLbbJ/SzbaYemJ/kjza2vNDg251urXne6daQ+HoGXLjhQtzW1C5Nyyf5g=="],
|
|
288
|
+
|
|
289
|
+
"@takumi-rs/core-linux-arm64-gnu": ["@takumi-rs/core-linux-arm64-gnu@0.65.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-YbwZzQTugFwvYErXs2W/QsngPSU5H6ZtYJNdbz9/qk8EEU6eB593XiBxE9/1vbq6Sb3mH84mPXElf1BmWJlvzw=="],
|
|
290
|
+
|
|
291
|
+
"@takumi-rs/core-linux-arm64-musl": ["@takumi-rs/core-linux-arm64-musl@0.65.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-iZpzfnD1/Gyof3p+fYUIBxNkDH9vQviOeihZ7yAtPbGmY4AbnOjoUBHDyJoKWE0el/x1W17efvNE7FuvzP0O5Q=="],
|
|
292
|
+
|
|
293
|
+
"@takumi-rs/core-linux-x64-gnu": ["@takumi-rs/core-linux-x64-gnu@0.65.0", "", { "os": "linux", "cpu": "x64" }, "sha512-kL4VclPYz7nmQuadCVSrAFvdy8LaJX5RPymG3upaHPcz0i7A8KmWY8jnQcDDkGQfPIi2lFncnt/sknCiaJ1t4Q=="],
|
|
294
|
+
|
|
295
|
+
"@takumi-rs/core-linux-x64-musl": ["@takumi-rs/core-linux-x64-musl@0.65.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2way2sF3p5bMlhGsDMmQGTPxtc37241POzAJ0bPRKR1fumylEAaADuXvN95qM4x9751iTke/wVhJOL28nV7asw=="],
|
|
296
|
+
|
|
297
|
+
"@takumi-rs/core-win32-arm64-msvc": ["@takumi-rs/core-win32-arm64-msvc@0.65.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-yCZqiu1b3XXhDUE7OaA5RdVg/nYcZU1nzToY7ViE4fR8PqOK54Ad8qOAKYZCTr4FwCB4sWo88/R7K1n0nPFpfA=="],
|
|
298
|
+
|
|
299
|
+
"@takumi-rs/core-win32-x64-msvc": ["@takumi-rs/core-win32-x64-msvc@0.65.0", "", { "os": "win32", "cpu": "x64" }, "sha512-SAw224AhLcKa9+ttO4IxeTquOvyz8pnDeGugzmQZerYO18kOuOCiP1g4VRhM1drpEFN7bbnhSnIvrewg5m4akw=="],
|
|
300
|
+
|
|
301
|
+
"@takumi-rs/helpers": ["@takumi-rs/helpers@0.65.0", "", {}, "sha512-3BsiW0iP3Y7xUPmT/tJBClb59qTxtHEYHQUznXdtDA0qyN6YAWABA6U0DTd3la6XHMEDyKaHV263OL679FyPaA=="],
|
|
302
|
+
|
|
279
303
|
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
|
|
280
304
|
|
|
281
305
|
"@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="],
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "critique",
|
|
3
3
|
"module": "src/diff.tsx",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.42",
|
|
6
6
|
"private": false,
|
|
7
7
|
"bin": "./src/cli.tsx",
|
|
8
8
|
"scripts": {
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@agentclientprotocol/sdk": "^0.12.0",
|
|
26
26
|
"@clack/prompts": "1.0.0-alpha.9",
|
|
27
|
-
"@opentui/core": "https://pkg.pr.new/anomalyco/opentui/@opentui/core@
|
|
28
|
-
"@opentui/react": "https://pkg.pr.new/anomalyco/opentui/@opentui/react@
|
|
27
|
+
"@opentui/core": "https://pkg.pr.new/anomalyco/opentui/@opentui/core@367a94087821b3b5feedd35bbb57df43b10a286e",
|
|
28
|
+
"@opentui/react": "https://pkg.pr.new/anomalyco/opentui/@opentui/react@367a94087821b3b5feedd35bbb57df43b10a286e",
|
|
29
29
|
"@parcel/watcher": "^2.5.1",
|
|
30
30
|
"bun-pty": "^0.4.7",
|
|
31
31
|
"cac": "^6.7.14",
|
|
@@ -35,5 +35,9 @@
|
|
|
35
35
|
"picocolors": "^1.1.1",
|
|
36
36
|
"react": "^19.2.0",
|
|
37
37
|
"zustand": "^5.0.8"
|
|
38
|
+
},
|
|
39
|
+
"optionalDependencies": {
|
|
40
|
+
"@takumi-rs/core": "^0.65.0",
|
|
41
|
+
"@takumi-rs/helpers": "^0.65.0"
|
|
38
42
|
}
|
|
39
43
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// Development preview script for testing ReviewApp styles without running AI.
|
|
3
|
+
// Renders example hunks and review data to preview TUI appearance.
|
|
4
|
+
// Run with: bun run scripts/preview-review.tsx (TUI) or --web (HTML upload).
|
|
5
5
|
|
|
6
6
|
import { createCliRenderer } from "@opentui/core"
|
|
7
7
|
import { createRoot } from "@opentui/react"
|
package/src/ansi-html.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// ANSI terminal output to HTML converter for web preview generation.
|
|
2
|
+
// Uses ghostty-opentui to parse PTY output and generates responsive HTML documents
|
|
3
|
+
// with proper font scaling to fit terminal content within viewport width.
|
|
4
|
+
|
|
1
5
|
import { ptyToJson, StyleFlags, type TerminalData, type TerminalLine, type TerminalSpan } from "ghostty-opentui"
|
|
2
6
|
|
|
3
7
|
export interface AnsiToHtmlOptions {
|
package/src/cli.tsx
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
// CLI entrypoint for the critique diff viewer.
|
|
3
|
+
// Provides TUI diff viewing, AI-powered review generation, and web preview upload.
|
|
4
|
+
// Commands: default (diff), review (AI analysis), web (HTML upload), pick (cherry-pick files).
|
|
5
|
+
|
|
2
6
|
import { cac } from "cac";
|
|
3
7
|
import {
|
|
4
8
|
createRoot,
|
|
@@ -500,7 +504,8 @@ async function runReviewMode(
|
|
|
500
504
|
|
|
501
505
|
// Write hunks to temp file for the render command
|
|
502
506
|
const hunksFile = writeTempFile(JSON.stringify(hunks), "critique-hunks", ".json");
|
|
503
|
-
|
|
507
|
+
// For web, always use default theme (with auto dark/light inversion) unless explicitly overridden
|
|
508
|
+
const themeName = defaultThemeName;
|
|
504
509
|
|
|
505
510
|
// Calculate rows needed based on hunks
|
|
506
511
|
const totalLines = hunks.reduce((sum, h) => sum + h.lines.length, 0);
|
|
@@ -667,12 +672,12 @@ async function runResumeMode(options: ResumeModeOptions) {
|
|
|
667
672
|
|
|
668
673
|
let reviewId = options.reviewId;
|
|
669
674
|
|
|
670
|
-
// If no ID provided, show select
|
|
675
|
+
// If no ID provided, show select (filtered to current cwd and children)
|
|
671
676
|
if (!reviewId) {
|
|
672
|
-
const reviews = listReviews();
|
|
677
|
+
const reviews = listReviews(process.cwd());
|
|
673
678
|
|
|
674
679
|
if (reviews.length === 0) {
|
|
675
|
-
clack.log.warn("No saved reviews found");
|
|
680
|
+
clack.log.warn("No saved reviews found for this directory");
|
|
676
681
|
clack.outro("");
|
|
677
682
|
process.exit(0);
|
|
678
683
|
}
|
|
@@ -830,7 +835,8 @@ async function runResumeMode(options: ResumeModeOptions) {
|
|
|
830
835
|
}).join("\n");
|
|
831
836
|
const yamlFile = writeTempFile(yamlContent, "critique-review", ".yaml");
|
|
832
837
|
|
|
833
|
-
|
|
838
|
+
// For web, always use default theme (with auto dark/light inversion) unless explicitly overridden
|
|
839
|
+
const themeName = defaultThemeName;
|
|
834
840
|
const totalLines = review.hunks.reduce((sum, h) => sum + h.lines.length, 0);
|
|
835
841
|
const baseRows = Math.max(200, totalLines * 2 + 100);
|
|
836
842
|
|
|
@@ -914,6 +920,16 @@ interface WebModeOptions {
|
|
|
914
920
|
'--'?: string[];
|
|
915
921
|
}
|
|
916
922
|
|
|
923
|
+
// Image mode handler
|
|
924
|
+
interface ImageModeOptions {
|
|
925
|
+
staged?: boolean;
|
|
926
|
+
commit?: string;
|
|
927
|
+
context?: string;
|
|
928
|
+
filter?: string;
|
|
929
|
+
theme?: string;
|
|
930
|
+
'--'?: string[];
|
|
931
|
+
}
|
|
932
|
+
|
|
917
933
|
async function runWebMode(
|
|
918
934
|
base: string | undefined,
|
|
919
935
|
head: string | undefined,
|
|
@@ -939,9 +955,10 @@ async function runWebMode(
|
|
|
939
955
|
|
|
940
956
|
const desktopCols = options.cols || 230;
|
|
941
957
|
const mobileCols = options.mobileCols || 100;
|
|
958
|
+
// For web, always use default theme (with auto dark/light inversion) unless explicitly overridden via --theme
|
|
942
959
|
const themeName = options.theme && themeNames.includes(options.theme)
|
|
943
960
|
? options.theme
|
|
944
|
-
:
|
|
961
|
+
: defaultThemeName;
|
|
945
962
|
|
|
946
963
|
console.log("Capturing diff output...");
|
|
947
964
|
|
|
@@ -1005,6 +1022,118 @@ async function runWebMode(
|
|
|
1005
1022
|
}
|
|
1006
1023
|
}
|
|
1007
1024
|
|
|
1025
|
+
async function runImageMode(
|
|
1026
|
+
base: string | undefined,
|
|
1027
|
+
head: string | undefined,
|
|
1028
|
+
options: ImageModeOptions
|
|
1029
|
+
) {
|
|
1030
|
+
const { renderTerminalToImages } = await import("./image.ts");
|
|
1031
|
+
const { writeTempFile, cleanupTempFile } = await import("./web-utils.ts");
|
|
1032
|
+
|
|
1033
|
+
const gitCommand = buildGitCommand({
|
|
1034
|
+
staged: options.staged,
|
|
1035
|
+
commit: options.commit,
|
|
1036
|
+
base,
|
|
1037
|
+
head,
|
|
1038
|
+
context: options.context,
|
|
1039
|
+
filter: options.filter,
|
|
1040
|
+
positionalFilters: options['--'],
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
const themeName = options.theme && themeNames.includes(options.theme)
|
|
1044
|
+
? options.theme
|
|
1045
|
+
: persistedState.themeName ?? defaultThemeName;
|
|
1046
|
+
|
|
1047
|
+
console.log("Capturing diff output...");
|
|
1048
|
+
|
|
1049
|
+
// Get the git diff
|
|
1050
|
+
const { stdout: gitDiff } = await execAsync(gitCommand, {
|
|
1051
|
+
encoding: "utf-8",
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
if (!gitDiff.trim()) {
|
|
1055
|
+
console.log("No changes to display");
|
|
1056
|
+
process.exit(0);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// Write diff to temp file
|
|
1060
|
+
const diffFile = writeTempFile(gitDiff, "critique-image-diff", ".patch");
|
|
1061
|
+
|
|
1062
|
+
// Build render command for image capture
|
|
1063
|
+
const renderCommand = [
|
|
1064
|
+
process.argv[1]!, // path to cli.tsx
|
|
1065
|
+
"web-render",
|
|
1066
|
+
diffFile,
|
|
1067
|
+
"--theme",
|
|
1068
|
+
themeName,
|
|
1069
|
+
"--cols",
|
|
1070
|
+
"120",
|
|
1071
|
+
"--rows",
|
|
1072
|
+
"10000",
|
|
1073
|
+
];
|
|
1074
|
+
|
|
1075
|
+
console.log("Rendering to images...");
|
|
1076
|
+
|
|
1077
|
+
try {
|
|
1078
|
+
// Capture PTY output
|
|
1079
|
+
const decoder = new TextDecoder();
|
|
1080
|
+
let ansiOutput = "";
|
|
1081
|
+
const cols = 120;
|
|
1082
|
+
const rows = 10000;
|
|
1083
|
+
|
|
1084
|
+
const proc = Bun.spawn(["bun", ...renderCommand], {
|
|
1085
|
+
cwd: process.cwd(),
|
|
1086
|
+
env: {
|
|
1087
|
+
...process.env,
|
|
1088
|
+
TERM: "xterm-256color",
|
|
1089
|
+
},
|
|
1090
|
+
terminal: {
|
|
1091
|
+
cols,
|
|
1092
|
+
rows,
|
|
1093
|
+
data(terminal, data) {
|
|
1094
|
+
ansiOutput += decoder.decode(data, { stream: true });
|
|
1095
|
+
},
|
|
1096
|
+
},
|
|
1097
|
+
});
|
|
1098
|
+
|
|
1099
|
+
await proc.exited;
|
|
1100
|
+
proc.terminal?.close();
|
|
1101
|
+
ansiOutput += decoder.decode();
|
|
1102
|
+
|
|
1103
|
+
// Clean up diff temp file
|
|
1104
|
+
cleanupTempFile(diffFile);
|
|
1105
|
+
|
|
1106
|
+
if (!ansiOutput.trim()) {
|
|
1107
|
+
console.error("No output captured");
|
|
1108
|
+
process.exit(1);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// Strip terminal cleanup sequences
|
|
1112
|
+
const clearIdx = ansiOutput.lastIndexOf("\x1b[H\x1b[J");
|
|
1113
|
+
if (clearIdx > 0) {
|
|
1114
|
+
ansiOutput = ansiOutput.slice(0, clearIdx);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// Render to images
|
|
1118
|
+
const result = await renderTerminalToImages(ansiOutput, {
|
|
1119
|
+
cols,
|
|
1120
|
+
themeName,
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
console.log(`\nGenerated ${result.imageCount} image${result.imageCount === 1 ? "" : "s"}:`);
|
|
1124
|
+
for (const path of result.paths) {
|
|
1125
|
+
console.log(` ${path}`);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
process.exit(0);
|
|
1129
|
+
} catch (error: unknown) {
|
|
1130
|
+
cleanupTempFile(diffFile);
|
|
1131
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1132
|
+
console.error("Failed to generate images:", message);
|
|
1133
|
+
process.exit(1);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1008
1137
|
// Error boundary component
|
|
1009
1138
|
interface ErrorBoundaryProps {
|
|
1010
1139
|
children: React.ReactNode;
|
|
@@ -1350,6 +1479,7 @@ cli
|
|
|
1350
1479
|
.option("--filter <pattern>", "Filter files by glob pattern (can be used multiple times)")
|
|
1351
1480
|
.option("--theme <name>", "Theme to use for rendering")
|
|
1352
1481
|
.option("--web [title]", "Generate web preview instead of TUI")
|
|
1482
|
+
.option("--image", "Generate images instead of TUI (saved to /tmp)")
|
|
1353
1483
|
.option("--open", "Open in browser (with --web)")
|
|
1354
1484
|
.option("--cols <cols>", "Desktop columns for web render", { default: 240 })
|
|
1355
1485
|
.option("--mobile-cols <cols>", "Mobile columns for web render", { default: 100 })
|
|
@@ -1407,6 +1537,19 @@ cli
|
|
|
1407
1537
|
return;
|
|
1408
1538
|
}
|
|
1409
1539
|
|
|
1540
|
+
// If --image flag, delegate to image generation logic
|
|
1541
|
+
if (options.image) {
|
|
1542
|
+
await runImageMode(base, head, {
|
|
1543
|
+
staged: options.staged,
|
|
1544
|
+
commit: options.commit,
|
|
1545
|
+
context: options.context,
|
|
1546
|
+
filter: options.filter,
|
|
1547
|
+
theme: options.theme,
|
|
1548
|
+
'--': options['--'],
|
|
1549
|
+
});
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1410
1553
|
try {
|
|
1411
1554
|
const gitCommand = buildGitCommand({
|
|
1412
1555
|
staged: options.staged,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
// Shared DiffView component for rendering git diffs with syntax highlighting
|
|
1
|
+
// Shared DiffView component for rendering git diffs with syntax highlighting.
|
|
2
|
+
// Wraps opentui's <diff> element with theme-aware colors and syntax styles.
|
|
3
|
+
// Supports split and unified view modes with line numbers.
|
|
2
4
|
|
|
3
5
|
import * as React from "react"
|
|
4
6
|
import { SyntaxStyle } from "@opentui/core"
|
package/src/components/index.ts
CHANGED
package/src/diff-utils.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
// Shared utilities for diff processing
|
|
1
|
+
// Shared utilities for git diff processing across CLI commands.
|
|
2
|
+
// Builds git commands, parses diff files, detects filetypes for syntax highlighting,
|
|
3
|
+
// and provides helpers for unified/split view mode selection.
|
|
2
4
|
|
|
3
5
|
export const IGNORED_FILES = [
|
|
4
6
|
"pnpm-lock.yaml",
|
package/src/dropdown.tsx
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// Searchable dropdown component for file and theme selection in TUI.
|
|
2
|
+
// Supports keyboard navigation, fuzzy search filtering, and mouse interaction.
|
|
3
|
+
// Used by main diff view for file picker and theme picker overlays.
|
|
4
|
+
|
|
1
5
|
import React, { useState, useEffect, useRef, useCallback, type ReactNode } from "react";
|
|
2
6
|
import { useKeyboard } from "@opentui/react";
|
|
3
7
|
import { TextAttributes, TextareaRenderable } from "@opentui/core";
|