critique 0.1.9 → 0.1.11
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/AGENTS.md +6 -1
- package/CHANGELOG.md +22 -0
- package/README.md +18 -1
- package/package.json +1 -1
- package/screenshot.png +0 -0
- package/src/ansi-html.ts +22 -7
- package/src/cli.tsx +31 -16
- package/src/themes/opencode.json +2 -2
- package/src/themes.ts +1 -1
- package/diff-viewer-demo.png +0 -0
package/AGENTS.md
CHANGED
|
@@ -8,7 +8,12 @@ ALWAYS!
|
|
|
8
8
|
|
|
9
9
|
## bun
|
|
10
10
|
|
|
11
|
-
NEVER run
|
|
11
|
+
NEVER run the interactive TUI (e.g. `bun run src/cli.tsx` without arguments). It will hang. Instead ask the user to run it.
|
|
12
|
+
|
|
13
|
+
The `web` command is safe to run - it generates HTML and exits:
|
|
14
|
+
```bash
|
|
15
|
+
bun run src/cli.tsx web
|
|
16
|
+
```
|
|
12
17
|
|
|
13
18
|
NEVER use require. just import at the top of the file with esm
|
|
14
19
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
# 0.1.11
|
|
2
|
+
|
|
3
|
+
- All commands:
|
|
4
|
+
- Add support for passing file filters as positional args after `--` (e.g. `critique web -- src/cli.tsx`)
|
|
5
|
+
- Web command:
|
|
6
|
+
- Add `--title` option for custom HTML document title
|
|
7
|
+
- Add scrollbar styling to HTML output (dark/light mode aware)
|
|
8
|
+
|
|
9
|
+
# 0.1.10
|
|
10
|
+
|
|
11
|
+
- Default command:
|
|
12
|
+
- Add `--filter <pattern>` option to filter files by glob pattern (e.g. `critique --filter 'src/**/*.ts'`)
|
|
13
|
+
- Web command:
|
|
14
|
+
- Add support for comparing two refs: `critique web <base> <head>`
|
|
15
|
+
- Add `--filter <pattern>` option to filter files by glob pattern
|
|
16
|
+
- Add auto light/dark mode based on system preference (uses CSS `prefers-color-scheme`)
|
|
17
|
+
- Disabled when `--theme` is specified
|
|
18
|
+
- Fix browser rendering for Safari/Chrome subpixel issues
|
|
19
|
+
- Themes:
|
|
20
|
+
- Change default theme to `github` (dark)
|
|
21
|
+
- Fix opencode theme line number contrast (was nearly invisible on dark background)
|
|
22
|
+
|
|
1
23
|
# 0.1.9
|
|
2
24
|
|
|
3
25
|
- Performance:
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A beautiful terminal UI for reviewing git diffs with syntax highlighting, split view, and word-level diff.
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -27,16 +27,26 @@ critique
|
|
|
27
27
|
# View staged changes
|
|
28
28
|
critique --staged
|
|
29
29
|
|
|
30
|
+
# View the last commit (works whether pushed or unpushed)
|
|
31
|
+
critique HEAD
|
|
32
|
+
|
|
30
33
|
# View a specific commit
|
|
31
34
|
critique --commit HEAD~1
|
|
32
35
|
critique abc1234
|
|
33
36
|
|
|
37
|
+
# View combined changes from last N commits
|
|
38
|
+
critique HEAD~3 HEAD # shows all changes from 3 commits ago to now
|
|
39
|
+
|
|
34
40
|
# Compare two branches (PR-style, shows what head added since diverging from base)
|
|
35
41
|
critique main feature-branch # what feature-branch added vs main
|
|
36
42
|
critique main HEAD # what current branch added vs main
|
|
37
43
|
|
|
38
44
|
# Watch mode - auto-refresh on file changes
|
|
39
45
|
critique --watch
|
|
46
|
+
|
|
47
|
+
# Filter files by glob pattern (can be used multiple times)
|
|
48
|
+
critique --filter "src/**/*.ts"
|
|
49
|
+
critique --filter "src/**/*.ts" --filter "lib/**/*.js"
|
|
40
50
|
```
|
|
41
51
|
|
|
42
52
|
### Navigation
|
|
@@ -85,9 +95,15 @@ critique web
|
|
|
85
95
|
# View staged changes
|
|
86
96
|
critique web --staged
|
|
87
97
|
|
|
98
|
+
# View the last commit (works whether pushed or unpushed)
|
|
99
|
+
critique web HEAD
|
|
100
|
+
|
|
88
101
|
# View a specific commit
|
|
89
102
|
critique web --commit HEAD~1
|
|
90
103
|
|
|
104
|
+
# View combined changes from last N commits
|
|
105
|
+
critique web HEAD~3 HEAD
|
|
106
|
+
|
|
91
107
|
# Generate local HTML file instead of uploading
|
|
92
108
|
critique web --local
|
|
93
109
|
|
|
@@ -112,6 +128,7 @@ critique web --cols 100 --rows 2000
|
|
|
112
128
|
| `--cols <n>` | Terminal width for rendering | `240` |
|
|
113
129
|
| `--rows <n>` | Terminal height for rendering | `2000` |
|
|
114
130
|
| `--local` | Save HTML locally instead of uploading | - |
|
|
131
|
+
| `--filter <pattern>` | Filter files by glob (can be used multiple times) | - |
|
|
115
132
|
|
|
116
133
|
**Tips:**
|
|
117
134
|
|
package/package.json
CHANGED
package/screenshot.png
ADDED
|
Binary file
|
package/src/ansi-html.ts
CHANGED
|
@@ -13,6 +13,10 @@ export interface AnsiToHtmlOptions {
|
|
|
13
13
|
fontSize?: string
|
|
14
14
|
/** Trim empty lines from the end */
|
|
15
15
|
trimEmptyLines?: boolean
|
|
16
|
+
/** Enable auto light/dark mode based on system preference */
|
|
17
|
+
autoTheme?: boolean
|
|
18
|
+
/** HTML document title */
|
|
19
|
+
title?: string
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
/**
|
|
@@ -127,6 +131,7 @@ export function ansiToHtmlDocument(input: string | Buffer, options: AnsiToHtmlOp
|
|
|
127
131
|
textColor = "#1a1a1a",
|
|
128
132
|
fontFamily = "Monaco, Menlo, 'Ubuntu Mono', Consolas, monospace",
|
|
129
133
|
fontSize = "14px",
|
|
134
|
+
title = "Critique Diff",
|
|
130
135
|
} = options
|
|
131
136
|
|
|
132
137
|
const content = ansiToHtml(input, options)
|
|
@@ -141,7 +146,7 @@ export function ansiToHtmlDocument(input: string | Buffer, options: AnsiToHtmlOp
|
|
|
141
146
|
<head>
|
|
142
147
|
<meta charset="utf-8">
|
|
143
148
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
144
|
-
<title
|
|
149
|
+
<title>${escapeHtml(title)}</title>
|
|
145
150
|
<style>
|
|
146
151
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
147
152
|
html {
|
|
@@ -156,7 +161,10 @@ html, body {
|
|
|
156
161
|
color: ${textColor};
|
|
157
162
|
font-family: ${fontFamily};
|
|
158
163
|
font-size: ${fontSize};
|
|
159
|
-
line-height: 1.
|
|
164
|
+
line-height: 1.5;
|
|
165
|
+
-webkit-font-smoothing: antialiased;
|
|
166
|
+
-moz-osx-font-smoothing: grayscale;
|
|
167
|
+
text-rendering: optimizeLegibility;
|
|
160
168
|
}
|
|
161
169
|
body {
|
|
162
170
|
display: flex;
|
|
@@ -172,15 +180,19 @@ body {
|
|
|
172
180
|
white-space: pre;
|
|
173
181
|
display: flex;
|
|
174
182
|
content-visibility: auto;
|
|
175
|
-
contain-intrinsic-block-size: auto
|
|
183
|
+
contain-intrinsic-block-size: auto 1.5em;
|
|
176
184
|
background-color: ${backgroundColor};
|
|
185
|
+
transform: translateZ(0);
|
|
186
|
+
backface-visibility: hidden;
|
|
177
187
|
}
|
|
178
188
|
.line span {
|
|
179
189
|
white-space: pre;
|
|
180
190
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
191
|
+
${options.autoTheme ? `@media (prefers-color-scheme: light) {
|
|
192
|
+
html {
|
|
193
|
+
filter: invert(1) hue-rotate(180deg);
|
|
194
|
+
}
|
|
195
|
+
}` : ''}\nhtml{scrollbar-width:thin;scrollbar-color:#6b7280 #2d3748;}@media(prefers-color-scheme:light){html{scrollbar-color:#a0aec0 #edf2f7;}}::-webkit-scrollbar{width:12px;}::-webkit-scrollbar-track{background:#2d3748;}::-webkit-scrollbar-thumb{background:#6b7280;border-radius:6px;}::-webkit-scrollbar-thumb:hover{background:#a0aec0;}@media(prefers-color-scheme:light){::-webkit-scrollbar-track{background:#edf2f7;}::-webkit-scrollbar-thumb{background:#a0aec0;}::-webkit-scrollbar-thumb:hover{background:#cbd5e1;}}::-webkit-scrollbar {\n width: 12px;\n}\n::-webkit-scrollbar-track {\n background: #2d3748;\n}\n::-webkit-scrollbar-thumb {\n background: #6b7280;\n border-radius: 6px;\n}\n::-webkit-scrollbar-thumb:hover {\n background: #a0aec0;\n}\n@media (prefers-color-scheme: light) {\n ::-webkit-scrollbar-track {\n background: #edf2f7;\n }\n ::-webkit-scrollbar-thumb {\n background: #a0aec0;\n }\n ::-webkit-scrollbar-thumb:hover {\n background: #cbd5e1;\n }\n}\n</style>\n</head>\n<body>
|
|
184
196
|
<div id="content">
|
|
185
197
|
${content}
|
|
186
198
|
</div>
|
|
@@ -195,7 +207,10 @@ ${content}
|
|
|
195
207
|
function adjustFontSize() {
|
|
196
208
|
const viewportWidth = window.innerWidth;
|
|
197
209
|
const calculatedSize = (viewportWidth - padding) / (cols * charRatio);
|
|
198
|
-
|
|
210
|
+
// Round to nearest even integer to prevent subpixel rendering issues
|
|
211
|
+
// (with line-height: 1.5, even font-size always yields integer line-height)
|
|
212
|
+
const clamped = Math.max(minFontSize, Math.min(maxFontSize, calculatedSize));
|
|
213
|
+
const fontSize = Math.round(clamped / 2) * 2;
|
|
199
214
|
document.body.style.fontSize = fontSize + 'px';
|
|
200
215
|
}
|
|
201
216
|
|
package/src/cli.tsx
CHANGED
|
@@ -562,20 +562,26 @@ cli
|
|
|
562
562
|
.option("--commit <ref>", "Show changes from a specific commit")
|
|
563
563
|
.option("--watch", "Watch for file changes and refresh diff")
|
|
564
564
|
.option("--context <lines>", "Number of context lines (default: 3)")
|
|
565
|
+
.option("--filter <pattern>", "Filter files by glob pattern (can be used multiple times)")
|
|
565
566
|
.action(async (base, head, options) => {
|
|
566
567
|
try {
|
|
567
568
|
const contextArg = options.context ? `-U${options.context}` : "";
|
|
569
|
+
// Combine --filter options with positional args after --
|
|
570
|
+
const filterOptions = options.filter ? (Array.isArray(options.filter) ? options.filter : [options.filter]) : [];
|
|
571
|
+
const positionalFilters = options['--'] || [];
|
|
572
|
+
const filters = [...filterOptions, ...positionalFilters];
|
|
573
|
+
const filterArg = filters.length > 0 ? `-- ${filters.map((f: string) => `"${f}"`).join(" ")}` : "";
|
|
568
574
|
const gitCommand = (() => {
|
|
569
575
|
if (options.staged)
|
|
570
|
-
return `git diff --cached --no-prefix ${contextArg}`.trim();
|
|
576
|
+
return `git diff --cached --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
571
577
|
if (options.commit)
|
|
572
|
-
return `git show ${options.commit} --no-prefix ${contextArg}`.trim();
|
|
578
|
+
return `git show ${options.commit} --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
573
579
|
// Two refs: compare base...head (three-dot, shows changes since branches diverged, like GitHub PRs)
|
|
574
580
|
if (base && head)
|
|
575
|
-
return `git diff ${base}...${head} --no-prefix ${contextArg}`.trim();
|
|
581
|
+
return `git diff ${base}...${head} --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
576
582
|
// Single ref: show that commit's changes
|
|
577
|
-
if (base) return `git show ${base} --no-prefix ${contextArg}`.trim();
|
|
578
|
-
return `git add -N . && git diff --no-prefix ${contextArg}`.trim();
|
|
583
|
+
if (base) return `git show ${base} --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
584
|
+
return `git add -N . && git diff --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
579
585
|
})();
|
|
580
586
|
|
|
581
587
|
const shouldWatch = options.watch && !base && !head && !options.commit;
|
|
@@ -1010,7 +1016,7 @@ const WORKER_URL =
|
|
|
1010
1016
|
process.env.CRITIQUE_WORKER_URL || "https://critique.work";
|
|
1011
1017
|
|
|
1012
1018
|
cli
|
|
1013
|
-
.command("web [
|
|
1019
|
+
.command("web [base] [head]", "Generate web preview of diff")
|
|
1014
1020
|
.option("--staged", "Show staged changes")
|
|
1015
1021
|
.option("--commit <ref>", "Show changes from a specific commit")
|
|
1016
1022
|
.option(
|
|
@@ -1027,25 +1033,35 @@ cli
|
|
|
1027
1033
|
.option("--open", "Open in browser after generating")
|
|
1028
1034
|
.option("--context <lines>", "Number of context lines (default: 3)")
|
|
1029
1035
|
.option("--theme <name>", "Theme to use for rendering")
|
|
1030
|
-
.
|
|
1036
|
+
.option("--filter <pattern>", "Filter files by glob pattern (can be used multiple times)")
|
|
1037
|
+
.option("--title <title>", "HTML document title")
|
|
1038
|
+
.action(async (base, head, options) => {
|
|
1031
1039
|
const pty = await import("@xmorse/bun-pty");
|
|
1032
1040
|
const { ansiToHtmlDocument } = await import("./ansi-html.ts");
|
|
1033
1041
|
|
|
1034
1042
|
const contextArg = options.context ? `-U${options.context}` : "";
|
|
1043
|
+
// Combine --filter options with positional args after --
|
|
1044
|
+
const filterOptions = options.filter ? (Array.isArray(options.filter) ? options.filter : [options.filter]) : [];
|
|
1045
|
+
const positionalFilters = options['--'] || [];
|
|
1046
|
+
const filters = [...filterOptions, ...positionalFilters];
|
|
1047
|
+
const filterArg = filters.length > 0 ? `-- ${filters.map((f: string) => `"${f}"`).join(" ")}` : "";
|
|
1035
1048
|
const gitCommand = (() => {
|
|
1036
1049
|
if (options.staged)
|
|
1037
|
-
return `git diff --cached --no-prefix ${contextArg}`.trim();
|
|
1050
|
+
return `git diff --cached --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
1038
1051
|
if (options.commit)
|
|
1039
|
-
return `git show ${options.commit} --no-prefix ${contextArg}`.trim();
|
|
1040
|
-
|
|
1041
|
-
|
|
1052
|
+
return `git show ${options.commit} --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
1053
|
+
// Two refs: compare base...head (three-dot, shows changes since branches diverged, like GitHub PRs)
|
|
1054
|
+
if (base && head)
|
|
1055
|
+
return `git diff ${base}...${head} --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
1056
|
+
// Single ref: show that commit's changes
|
|
1057
|
+
if (base) return `git show ${base} --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
1058
|
+
return `git add -N . && git diff --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
1042
1059
|
})();
|
|
1043
1060
|
|
|
1044
1061
|
const desktopCols = parseInt(options.cols) || 240;
|
|
1045
1062
|
const mobileCols = parseInt(options.mobileCols) || 100;
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1048
|
-
: defaultThemeName;
|
|
1063
|
+
const customTheme = options.theme && themeNames.includes(options.theme);
|
|
1064
|
+
const themeName = customTheme ? options.theme : defaultThemeName;
|
|
1049
1065
|
|
|
1050
1066
|
console.log("Capturing diff output...");
|
|
1051
1067
|
|
|
@@ -1124,7 +1140,7 @@ cli
|
|
|
1124
1140
|
const backgroundColor = rgbaToHex(theme.background);
|
|
1125
1141
|
const textColor = rgbaToHex(theme.text);
|
|
1126
1142
|
|
|
1127
|
-
return ansiToHtmlDocument(ansiOutput, { cols, rows: renderRows, backgroundColor, textColor });
|
|
1143
|
+
return ansiToHtmlDocument(ansiOutput, { cols, rows: renderRows, backgroundColor, textColor, autoTheme: !customTheme, title: options.title });
|
|
1128
1144
|
}
|
|
1129
1145
|
|
|
1130
1146
|
// Generate desktop and mobile versions in parallel
|
|
@@ -1367,5 +1383,4 @@ cli
|
|
|
1367
1383
|
|
|
1368
1384
|
cli.help();
|
|
1369
1385
|
cli.version("1.0.0");
|
|
1370
|
-
// comment
|
|
1371
1386
|
cli.parse();
|
package/src/themes/opencode.json
CHANGED
package/src/themes.ts
CHANGED
|
@@ -237,7 +237,7 @@ export function getSyntaxTheme(
|
|
|
237
237
|
|
|
238
238
|
export const themeNames = Object.keys(THEME_FILES).sort();
|
|
239
239
|
|
|
240
|
-
export const defaultThemeName = "github
|
|
240
|
+
export const defaultThemeName = "github";
|
|
241
241
|
|
|
242
242
|
// Helper to convert RGBA to hex string
|
|
243
243
|
export function rgbaToHex(rgba: RGBA): string {
|
package/diff-viewer-demo.png
DELETED
|
Binary file
|