critique 0.1.9 → 0.1.10
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 +14 -0
- package/README.md +17 -0
- package/package.json +1 -1
- package/src/ansi-html.ts +18 -3
- package/src/cli.tsx +24 -15
- package/src/themes/opencode.json +2 -2
- package/src/themes.ts +1 -1
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,17 @@
|
|
|
1
|
+
# 0.1.10
|
|
2
|
+
|
|
3
|
+
- Default command:
|
|
4
|
+
- Add `--filter <pattern>` option to filter files by glob pattern (e.g. `critique --filter 'src/**/*.ts'`)
|
|
5
|
+
- Web command:
|
|
6
|
+
- Add support for comparing two refs: `critique web <base> <head>`
|
|
7
|
+
- Add `--filter <pattern>` option to filter files by glob pattern
|
|
8
|
+
- Add auto light/dark mode based on system preference (uses CSS `prefers-color-scheme`)
|
|
9
|
+
- Disabled when `--theme` is specified
|
|
10
|
+
- Fix browser rendering for Safari/Chrome subpixel issues
|
|
11
|
+
- Themes:
|
|
12
|
+
- Change default theme to `github` (dark)
|
|
13
|
+
- Fix opencode theme line number contrast (was nearly invisible on dark background)
|
|
14
|
+
|
|
1
15
|
# 0.1.9
|
|
2
16
|
|
|
3
17
|
- Performance:
|
package/README.md
CHANGED
|
@@ -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/src/ansi-html.ts
CHANGED
|
@@ -13,6 +13,8 @@ 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
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
/**
|
|
@@ -156,7 +158,10 @@ html, body {
|
|
|
156
158
|
color: ${textColor};
|
|
157
159
|
font-family: ${fontFamily};
|
|
158
160
|
font-size: ${fontSize};
|
|
159
|
-
line-height: 1.
|
|
161
|
+
line-height: 1.5;
|
|
162
|
+
-webkit-font-smoothing: antialiased;
|
|
163
|
+
-moz-osx-font-smoothing: grayscale;
|
|
164
|
+
text-rendering: optimizeLegibility;
|
|
160
165
|
}
|
|
161
166
|
body {
|
|
162
167
|
display: flex;
|
|
@@ -172,12 +177,19 @@ body {
|
|
|
172
177
|
white-space: pre;
|
|
173
178
|
display: flex;
|
|
174
179
|
content-visibility: auto;
|
|
175
|
-
contain-intrinsic-block-size: auto
|
|
180
|
+
contain-intrinsic-block-size: auto 1.5em;
|
|
176
181
|
background-color: ${backgroundColor};
|
|
182
|
+
transform: translateZ(0);
|
|
183
|
+
backface-visibility: hidden;
|
|
177
184
|
}
|
|
178
185
|
.line span {
|
|
179
186
|
white-space: pre;
|
|
180
187
|
}
|
|
188
|
+
${options.autoTheme ? `@media (prefers-color-scheme: light) {
|
|
189
|
+
html {
|
|
190
|
+
filter: invert(1) hue-rotate(180deg);
|
|
191
|
+
}
|
|
192
|
+
}` : ''}
|
|
181
193
|
</style>
|
|
182
194
|
</head>
|
|
183
195
|
<body>
|
|
@@ -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,23 @@ 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
|
+
const filters = options.filter ? (Array.isArray(options.filter) ? options.filter : [options.filter]) : [];
|
|
570
|
+
const filterArg = filters.length > 0 ? `-- ${filters.map((f: string) => `"${f}"`).join(" ")}` : "";
|
|
568
571
|
const gitCommand = (() => {
|
|
569
572
|
if (options.staged)
|
|
570
|
-
return `git diff --cached --no-prefix ${contextArg}`.trim();
|
|
573
|
+
return `git diff --cached --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
571
574
|
if (options.commit)
|
|
572
|
-
return `git show ${options.commit} --no-prefix ${contextArg}`.trim();
|
|
575
|
+
return `git show ${options.commit} --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
573
576
|
// Two refs: compare base...head (three-dot, shows changes since branches diverged, like GitHub PRs)
|
|
574
577
|
if (base && head)
|
|
575
|
-
return `git diff ${base}...${head} --no-prefix ${contextArg}`.trim();
|
|
578
|
+
return `git diff ${base}...${head} --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
576
579
|
// 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();
|
|
580
|
+
if (base) return `git show ${base} --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
581
|
+
return `git add -N . && git diff --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
579
582
|
})();
|
|
580
583
|
|
|
581
584
|
const shouldWatch = options.watch && !base && !head && !options.commit;
|
|
@@ -1010,7 +1013,7 @@ const WORKER_URL =
|
|
|
1010
1013
|
process.env.CRITIQUE_WORKER_URL || "https://critique.work";
|
|
1011
1014
|
|
|
1012
1015
|
cli
|
|
1013
|
-
.command("web [
|
|
1016
|
+
.command("web [base] [head]", "Generate web preview of diff")
|
|
1014
1017
|
.option("--staged", "Show staged changes")
|
|
1015
1018
|
.option("--commit <ref>", "Show changes from a specific commit")
|
|
1016
1019
|
.option(
|
|
@@ -1027,25 +1030,31 @@ cli
|
|
|
1027
1030
|
.option("--open", "Open in browser after generating")
|
|
1028
1031
|
.option("--context <lines>", "Number of context lines (default: 3)")
|
|
1029
1032
|
.option("--theme <name>", "Theme to use for rendering")
|
|
1030
|
-
.
|
|
1033
|
+
.option("--filter <pattern>", "Filter files by glob pattern (can be used multiple times)")
|
|
1034
|
+
.action(async (base, head, options) => {
|
|
1031
1035
|
const pty = await import("@xmorse/bun-pty");
|
|
1032
1036
|
const { ansiToHtmlDocument } = await import("./ansi-html.ts");
|
|
1033
1037
|
|
|
1034
1038
|
const contextArg = options.context ? `-U${options.context}` : "";
|
|
1039
|
+
const filters = options.filter ? (Array.isArray(options.filter) ? options.filter : [options.filter]) : [];
|
|
1040
|
+
const filterArg = filters.length > 0 ? `-- ${filters.map((f: string) => `"${f}"`).join(" ")}` : "";
|
|
1035
1041
|
const gitCommand = (() => {
|
|
1036
1042
|
if (options.staged)
|
|
1037
|
-
return `git diff --cached --no-prefix ${contextArg}`.trim();
|
|
1043
|
+
return `git diff --cached --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
1038
1044
|
if (options.commit)
|
|
1039
|
-
return `git show ${options.commit} --no-prefix ${contextArg}`.trim();
|
|
1040
|
-
|
|
1041
|
-
|
|
1045
|
+
return `git show ${options.commit} --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
1046
|
+
// Two refs: compare base...head (three-dot, shows changes since branches diverged, like GitHub PRs)
|
|
1047
|
+
if (base && head)
|
|
1048
|
+
return `git diff ${base}...${head} --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
1049
|
+
// Single ref: show that commit's changes
|
|
1050
|
+
if (base) return `git show ${base} --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
1051
|
+
return `git add -N . && git diff --no-prefix ${contextArg} ${filterArg}`.trim();
|
|
1042
1052
|
})();
|
|
1043
1053
|
|
|
1044
1054
|
const desktopCols = parseInt(options.cols) || 240;
|
|
1045
1055
|
const mobileCols = parseInt(options.mobileCols) || 100;
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1048
|
-
: defaultThemeName;
|
|
1056
|
+
const customTheme = options.theme && themeNames.includes(options.theme);
|
|
1057
|
+
const themeName = customTheme ? options.theme : defaultThemeName;
|
|
1049
1058
|
|
|
1050
1059
|
console.log("Capturing diff output...");
|
|
1051
1060
|
|
|
@@ -1124,7 +1133,7 @@ cli
|
|
|
1124
1133
|
const backgroundColor = rgbaToHex(theme.background);
|
|
1125
1134
|
const textColor = rgbaToHex(theme.text);
|
|
1126
1135
|
|
|
1127
|
-
return ansiToHtmlDocument(ansiOutput, { cols, rows: renderRows, backgroundColor, textColor });
|
|
1136
|
+
return ansiToHtmlDocument(ansiOutput, { cols, rows: renderRows, backgroundColor, textColor, autoTheme: !customTheme });
|
|
1128
1137
|
}
|
|
1129
1138
|
|
|
1130
1139
|
// Generate desktop and mobile versions in parallel
|
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 {
|