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 CHANGED
@@ -8,7 +8,12 @@ ALWAYS!
8
8
 
9
9
  ## bun
10
10
 
11
- NEVER run bun run index.tsx. You cannot directly run the tui app. it will hang. instead ask me to do so.
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
@@ -2,7 +2,7 @@
2
2
  "name": "critique",
3
3
  "module": "src/diff.tsx",
4
4
  "type": "module",
5
- "version": "0.1.9",
5
+ "version": "0.1.10",
6
6
  "private": false,
7
7
  "bin": "./src/cli.tsx",
8
8
  "scripts": {
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.6;
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 1lh;
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
- const fontSize = Math.max(minFontSize, Math.min(maxFontSize, calculatedSize));
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 [ref]", "Generate web preview of diff")
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
- .action(async (ref, options) => {
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
- if (ref) return `git show ${ref} --no-prefix ${contextArg}`.trim();
1041
- return `git add -N . && git diff --no-prefix ${contextArg}`.trim();
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 themeName = options.theme && themeNames.includes(options.theme)
1047
- ? options.theme
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
@@ -138,8 +138,8 @@
138
138
  "light": "lightStep2"
139
139
  },
140
140
  "diffLineNumber": {
141
- "dark": "darkStep3",
142
- "light": "lightStep3"
141
+ "dark": "darkStep8",
142
+ "light": "lightStep8"
143
143
  },
144
144
  "diffAddedLineNumberBg": {
145
145
  "dark": "#1b2b34",
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-light";
240
+ export const defaultThemeName = "github";
241
241
 
242
242
  // Helper to convert RGBA to hex string
243
243
  export function rgbaToHex(rgba: RGBA): string {