critique 0.1.3 → 0.1.5
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 +18 -0
- package/bun.lock +3 -3
- package/package.json +2 -2
- package/src/ansi-html.ts +8 -2
- package/src/cli.tsx +54 -38
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
# 0.1.5
|
|
2
|
+
|
|
3
|
+
- Web preview:
|
|
4
|
+
- Switch to `ghostty-opentui` for unlimited scrollback (fixes truncation on large diffs)
|
|
5
|
+
- Dynamically calculate rows from diff content instead of hardcoded limit
|
|
6
|
+
- Add `--open` flag to open browser (disabled by default)
|
|
7
|
+
- Center container horizontally with `max-width: 100vw` (fixes iPad overflow)
|
|
8
|
+
- Set line background color to reduce Safari content-visibility flicker
|
|
9
|
+
- Reduce min font size from 8px to 4px for better mobile fit
|
|
10
|
+
- Fix JSX syntax error (`treeSitterClient={undefined}`)
|
|
11
|
+
|
|
12
|
+
# 0.1.4
|
|
13
|
+
|
|
14
|
+
- Web preview:
|
|
15
|
+
- Fix syntax highlighting not appearing in web output
|
|
16
|
+
- Allow multiple renders before capturing (syntax highlighting is async)
|
|
17
|
+
- Use debounced exit (300ms after last render) instead of blocking re-renders
|
|
18
|
+
|
|
1
19
|
# 0.1.3
|
|
2
20
|
|
|
3
21
|
- Themes:
|
package/bun.lock
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"@xmorse/bun-pty": "0.4.0",
|
|
14
14
|
"cac": "^6.7.14",
|
|
15
15
|
"diff": "^8.0.2",
|
|
16
|
-
"opentui
|
|
16
|
+
"ghostty-opentui": "^1.3.11",
|
|
17
17
|
"react": "^19.2.0",
|
|
18
18
|
"react-error-boundary": "^6.0.0",
|
|
19
19
|
"shiki": "^3.20.0",
|
|
@@ -374,6 +374,8 @@
|
|
|
374
374
|
|
|
375
375
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
|
376
376
|
|
|
377
|
+
"ghostty-opentui": ["ghostty-opentui@1.3.11", "", { "dependencies": { "strip-ansi": "^7.1.2" }, "peerDependencies": { "@opentui/core": "*" }, "optionalPeers": ["@opentui/core"] }, "sha512-taKOhQD65dip/GBi2eDicyS6Z+m7T6CAWyUcFUVP3nX+JKTCiOwfncGqhnqtSa8VE3mG6VHaPzIimDVN7pdD6w=="],
|
|
378
|
+
|
|
377
379
|
"gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
|
|
378
380
|
|
|
379
381
|
"glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="],
|
|
@@ -432,8 +434,6 @@
|
|
|
432
434
|
|
|
433
435
|
"oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="],
|
|
434
436
|
|
|
435
|
-
"opentui-ansi-vt": ["opentui-ansi-vt@1.2.11", "", { "dependencies": { "strip-ansi": "^7.1.2" }, "peerDependencies": { "@opentui/core": "*" }, "optionalPeers": ["@opentui/core"] }, "sha512-T+uxxisV2m8LaYgnzvHqnJX6gLJZ5UrvMVpEwjC4ocxKP2DtY++//IRvVDo1db9c2nT3Sz8H/sw22X9CXW/Igg=="],
|
|
436
|
-
|
|
437
437
|
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
|
|
438
438
|
|
|
439
439
|
"parse-bmfont-ascii": ["parse-bmfont-ascii@1.0.6", "", {}, "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA=="],
|
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.5",
|
|
6
6
|
"private": false,
|
|
7
7
|
"bin": "./src/cli.tsx",
|
|
8
8
|
"scripts": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@xmorse/bun-pty": "0.4.0",
|
|
31
31
|
"cac": "^6.7.14",
|
|
32
32
|
"diff": "^8.0.2",
|
|
33
|
-
"opentui
|
|
33
|
+
"ghostty-opentui": "^1.3.11",
|
|
34
34
|
"react": "^19.2.0",
|
|
35
35
|
"react-error-boundary": "^6.0.0",
|
|
36
36
|
"shiki": "^3.20.0",
|
package/src/ansi-html.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ptyToJson, StyleFlags, type TerminalData, type TerminalLine, type TerminalSpan } from "opentui
|
|
1
|
+
import { ptyToJson, StyleFlags, type TerminalData, type TerminalLine, type TerminalSpan } from "ghostty-opentui"
|
|
2
2
|
|
|
3
3
|
export interface AnsiToHtmlOptions {
|
|
4
4
|
cols?: number
|
|
@@ -147,6 +147,8 @@ html {
|
|
|
147
147
|
}
|
|
148
148
|
html, body {
|
|
149
149
|
height: 100%;
|
|
150
|
+
max-width: 100vw;
|
|
151
|
+
overflow-x: hidden;
|
|
150
152
|
background-color: ${backgroundColor};
|
|
151
153
|
color: #c5c8c6;
|
|
152
154
|
font-family: ${fontFamily};
|
|
@@ -156,12 +158,16 @@ html, body {
|
|
|
156
158
|
#content {
|
|
157
159
|
padding: 16px;
|
|
158
160
|
overflow-x: auto;
|
|
161
|
+
max-width: 100vw;
|
|
162
|
+
margin: 0 auto;
|
|
163
|
+
box-sizing: border-box;
|
|
159
164
|
}
|
|
160
165
|
.line {
|
|
161
166
|
white-space: pre;
|
|
162
167
|
display: flex;
|
|
163
168
|
content-visibility: auto;
|
|
164
169
|
contain-intrinsic-block-size: auto 1lh;
|
|
170
|
+
background-color: ${backgroundColor};
|
|
165
171
|
}
|
|
166
172
|
.line span {
|
|
167
173
|
white-space: pre;
|
|
@@ -177,7 +183,7 @@ ${content}
|
|
|
177
183
|
const cols = ${cols};
|
|
178
184
|
const charRatio = ${charWidthRatio};
|
|
179
185
|
const padding = ${padding};
|
|
180
|
-
const minFontSize =
|
|
186
|
+
const minFontSize = 4;
|
|
181
187
|
const maxFontSize = 16;
|
|
182
188
|
|
|
183
189
|
function adjustFontSize() {
|
package/src/cli.tsx
CHANGED
|
@@ -225,6 +225,7 @@ function DiffView({ diff, view, filetype, themeName }: DiffViewProps) {
|
|
|
225
225
|
<diff
|
|
226
226
|
diff={diff}
|
|
227
227
|
view={view}
|
|
228
|
+
treeSitterClient={undefined}
|
|
228
229
|
filetype={filetype}
|
|
229
230
|
syntaxStyle={syntaxStyle}
|
|
230
231
|
showLineNumbers
|
|
@@ -998,8 +999,9 @@ cli
|
|
|
998
999
|
"Number of columns for rendering (use ~100 for mobile)",
|
|
999
1000
|
{ default: 240 },
|
|
1000
1001
|
)
|
|
1001
|
-
|
|
1002
|
-
.option("--local", "
|
|
1002
|
+
|
|
1003
|
+
.option("--local", "Save local preview instead of uploading")
|
|
1004
|
+
.option("--open", "Open in browser after generating")
|
|
1003
1005
|
.option("--context <lines>", "Number of context lines (default: 3)")
|
|
1004
1006
|
.action(async (ref, options) => {
|
|
1005
1007
|
const pty = await import("@xmorse/bun-pty");
|
|
@@ -1016,7 +1018,6 @@ cli
|
|
|
1016
1018
|
})();
|
|
1017
1019
|
|
|
1018
1020
|
const cols = parseInt(options.cols) || 240;
|
|
1019
|
-
const rows = parseInt(options.rows) || 2000;
|
|
1020
1021
|
|
|
1021
1022
|
console.log("Capturing diff output...");
|
|
1022
1023
|
|
|
@@ -1030,6 +1031,14 @@ cli
|
|
|
1030
1031
|
process.exit(0);
|
|
1031
1032
|
}
|
|
1032
1033
|
|
|
1034
|
+
// Calculate required rows from diff content
|
|
1035
|
+
const { parsePatch } = await import("diff");
|
|
1036
|
+
const files = parsePatch(gitDiff);
|
|
1037
|
+
const renderRows = files.reduce((sum, file) => {
|
|
1038
|
+
const diffLines = file.hunks.reduce((h, hunk) => h + hunk.lines.length, 0);
|
|
1039
|
+
return sum + diffLines + 5; // header + margin per file
|
|
1040
|
+
}, 100); // base padding
|
|
1041
|
+
|
|
1033
1042
|
// Write diff to temp file
|
|
1034
1043
|
const diffFile = join(tmpdir(), `critique-web-diff-${Date.now()}.patch`);
|
|
1035
1044
|
fs.writeFileSync(diffFile, gitDiff);
|
|
@@ -1045,12 +1054,12 @@ cli
|
|
|
1045
1054
|
"--cols",
|
|
1046
1055
|
String(cols),
|
|
1047
1056
|
"--rows",
|
|
1048
|
-
String(
|
|
1057
|
+
String(renderRows),
|
|
1049
1058
|
],
|
|
1050
1059
|
{
|
|
1051
1060
|
name: "xterm-256color",
|
|
1052
1061
|
cols: cols,
|
|
1053
|
-
rows:
|
|
1062
|
+
rows: renderRows,
|
|
1054
1063
|
|
|
1055
1064
|
cwd: process.cwd(),
|
|
1056
1065
|
env: { ...process.env, TERM: "xterm-256color" } as Record<
|
|
@@ -1088,25 +1097,27 @@ cli
|
|
|
1088
1097
|
}
|
|
1089
1098
|
|
|
1090
1099
|
// Convert ANSI to HTML document
|
|
1091
|
-
const html = ansiToHtmlDocument(ansiOutput, { cols, rows });
|
|
1100
|
+
const html = ansiToHtmlDocument(ansiOutput, { cols, rows: renderRows });
|
|
1092
1101
|
|
|
1093
1102
|
if (options.local) {
|
|
1094
|
-
// Save locally
|
|
1103
|
+
// Save locally
|
|
1095
1104
|
const htmlFile = join(tmpdir(), `critique-${Date.now()}.html`);
|
|
1096
1105
|
fs.writeFileSync(htmlFile, html);
|
|
1097
1106
|
console.log(`Saved to: ${htmlFile}`);
|
|
1098
1107
|
|
|
1099
|
-
//
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1108
|
+
// Open in browser if requested
|
|
1109
|
+
if (options.open) {
|
|
1110
|
+
const openCmd =
|
|
1111
|
+
process.platform === "darwin"
|
|
1112
|
+
? "open"
|
|
1113
|
+
: process.platform === "win32"
|
|
1114
|
+
? "start"
|
|
1115
|
+
: "xdg-open";
|
|
1116
|
+
try {
|
|
1117
|
+
await execAsync(`${openCmd} "${htmlFile}"`);
|
|
1118
|
+
} catch {
|
|
1119
|
+
console.log("Could not open browser automatically");
|
|
1120
|
+
}
|
|
1110
1121
|
}
|
|
1111
1122
|
process.exit(0);
|
|
1112
1123
|
}
|
|
@@ -1132,17 +1143,19 @@ cli
|
|
|
1132
1143
|
console.log(`\nPreview URL: ${result.url}`);
|
|
1133
1144
|
console.log(`(expires in 7 days)`);
|
|
1134
1145
|
|
|
1135
|
-
//
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
+
// Open in browser if requested
|
|
1147
|
+
if (options.open) {
|
|
1148
|
+
const openCmd =
|
|
1149
|
+
process.platform === "darwin"
|
|
1150
|
+
? "open"
|
|
1151
|
+
: process.platform === "win32"
|
|
1152
|
+
? "start"
|
|
1153
|
+
: "xdg-open";
|
|
1154
|
+
try {
|
|
1155
|
+
await execAsync(`${openCmd} "${result.url}"`);
|
|
1156
|
+
} catch {
|
|
1157
|
+
// Silent fail - user can copy URL
|
|
1158
|
+
}
|
|
1146
1159
|
}
|
|
1147
1160
|
} catch (error: any) {
|
|
1148
1161
|
console.error("Failed to upload:", error.message);
|
|
@@ -1164,7 +1177,7 @@ cli
|
|
|
1164
1177
|
.option("--rows <rows>", "Terminal rows", { default: 1000 })
|
|
1165
1178
|
.action(async (diffFile: string, options) => {
|
|
1166
1179
|
const cols = parseInt(options.cols) || 120;
|
|
1167
|
-
const rows = parseInt(options.rows) ||
|
|
1180
|
+
const rows = parseInt(options.rows) || 1000;
|
|
1168
1181
|
|
|
1169
1182
|
const { parsePatch, formatPatch } = await import("diff");
|
|
1170
1183
|
|
|
@@ -1201,7 +1214,7 @@ cli
|
|
|
1201
1214
|
process.exit(0);
|
|
1202
1215
|
}
|
|
1203
1216
|
|
|
1204
|
-
// Override terminal size
|
|
1217
|
+
// Override terminal size (rows calculated by caller from diff content)
|
|
1205
1218
|
process.stdout.columns = cols;
|
|
1206
1219
|
process.stdout.rows = rows;
|
|
1207
1220
|
|
|
@@ -1210,20 +1223,23 @@ cli
|
|
|
1210
1223
|
useAlternateScreen: false,
|
|
1211
1224
|
});
|
|
1212
1225
|
|
|
1213
|
-
//
|
|
1214
|
-
|
|
1226
|
+
// Wait for syntax highlighting to complete (it's async)
|
|
1227
|
+
// Allow multiple renders, then exit after highlighting is ready
|
|
1228
|
+
let renderCount = 0;
|
|
1215
1229
|
const originalRequestRender = renderer.root.requestRender.bind(
|
|
1216
1230
|
renderer.root,
|
|
1217
1231
|
);
|
|
1232
|
+
let exitTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
1218
1233
|
renderer.root.requestRender = function () {
|
|
1219
|
-
|
|
1220
|
-
hasRendered = true;
|
|
1234
|
+
renderCount++;
|
|
1221
1235
|
originalRequestRender();
|
|
1222
|
-
//
|
|
1223
|
-
|
|
1236
|
+
// Reset timeout on each render - exit 1s after last render
|
|
1237
|
+
// Tree-sitter highlighting is async and can take time for multiple files
|
|
1238
|
+
if (exitTimeout) clearTimeout(exitTimeout);
|
|
1239
|
+
exitTimeout = setTimeout(() => {
|
|
1224
1240
|
renderer.destroy();
|
|
1225
1241
|
process.exit(0);
|
|
1226
|
-
},
|
|
1242
|
+
}, 1000);
|
|
1227
1243
|
};
|
|
1228
1244
|
|
|
1229
1245
|
// Use unified diff for narrow viewports (mobile), split view for wider ones
|