critique 0.1.10 → 0.1.12
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 +16 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/screenshot.png +0 -0
- package/src/ansi-html.ts +16 -5
- package/src/cli.tsx +10 -4
- package/src/worker.ts +17 -9
- package/diff-viewer-demo.png +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
# 0.1.12
|
|
2
|
+
|
|
3
|
+
- Web preview:
|
|
4
|
+
- Add client-side mobile detection with redirect to `?v=mobile`
|
|
5
|
+
- Simplify worker: redirect mobile devices instead of content negotiation
|
|
6
|
+
- Remove `Vary` header - URL now determines content, better caching
|
|
7
|
+
- Increase cache max-age to 24h (was 1h)
|
|
8
|
+
|
|
9
|
+
# 0.1.11
|
|
10
|
+
|
|
11
|
+
- All commands:
|
|
12
|
+
- Add support for passing file filters as positional args after `--` (e.g. `critique web -- src/cli.tsx`)
|
|
13
|
+
- Web command:
|
|
14
|
+
- Add `--title` option for custom HTML document title
|
|
15
|
+
- Add scrollbar styling to HTML output (dark/light mode aware)
|
|
16
|
+
|
|
1
17
|
# 0.1.10
|
|
2
18
|
|
|
3
19
|
- Default command:
|
package/README.md
CHANGED
package/package.json
CHANGED
package/screenshot.png
ADDED
|
Binary file
|
package/src/ansi-html.ts
CHANGED
|
@@ -15,6 +15,8 @@ export interface AnsiToHtmlOptions {
|
|
|
15
15
|
trimEmptyLines?: boolean
|
|
16
16
|
/** Enable auto light/dark mode based on system preference */
|
|
17
17
|
autoTheme?: boolean
|
|
18
|
+
/** HTML document title */
|
|
19
|
+
title?: string
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
/**
|
|
@@ -129,6 +131,7 @@ export function ansiToHtmlDocument(input: string | Buffer, options: AnsiToHtmlOp
|
|
|
129
131
|
textColor = "#1a1a1a",
|
|
130
132
|
fontFamily = "Monaco, Menlo, 'Ubuntu Mono', Consolas, monospace",
|
|
131
133
|
fontSize = "14px",
|
|
134
|
+
title = "Critique Diff",
|
|
132
135
|
} = options
|
|
133
136
|
|
|
134
137
|
const content = ansiToHtml(input, options)
|
|
@@ -143,7 +146,7 @@ export function ansiToHtmlDocument(input: string | Buffer, options: AnsiToHtmlOp
|
|
|
143
146
|
<head>
|
|
144
147
|
<meta charset="utf-8">
|
|
145
148
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
146
|
-
<title
|
|
149
|
+
<title>${escapeHtml(title)}</title>
|
|
147
150
|
<style>
|
|
148
151
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
149
152
|
html {
|
|
@@ -189,10 +192,7 @@ ${options.autoTheme ? `@media (prefers-color-scheme: light) {
|
|
|
189
192
|
html {
|
|
190
193
|
filter: invert(1) hue-rotate(180deg);
|
|
191
194
|
}
|
|
192
|
-
}` : ''}
|
|
193
|
-
</style>
|
|
194
|
-
</head>
|
|
195
|
-
<body>
|
|
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>
|
|
196
196
|
<div id="content">
|
|
197
197
|
${content}
|
|
198
198
|
</div>
|
|
@@ -204,6 +204,17 @@ ${content}
|
|
|
204
204
|
const minFontSize = 4;
|
|
205
205
|
const maxFontSize = 16;
|
|
206
206
|
|
|
207
|
+
// Redirect mobile devices to ?v=mobile for optimized view
|
|
208
|
+
// Only redirect if not already on a forced version
|
|
209
|
+
const params = new URLSearchParams(window.location.search);
|
|
210
|
+
if (!params.has('v')) {
|
|
211
|
+
const isMobile = /Mobile|iP(hone|od|ad)|Android|BlackBerry|IEMobile|Kindle|Opera M(obi|ini)|Windows Phone|webOS/i.test(navigator.userAgent);
|
|
212
|
+
if (isMobile) {
|
|
213
|
+
params.set('v', 'mobile');
|
|
214
|
+
window.location.replace(window.location.pathname + '?' + params.toString());
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
207
218
|
function adjustFontSize() {
|
|
208
219
|
const viewportWidth = window.innerWidth;
|
|
209
220
|
const calculatedSize = (viewportWidth - padding) / (cols * charRatio);
|
package/src/cli.tsx
CHANGED
|
@@ -566,7 +566,10 @@ cli
|
|
|
566
566
|
.action(async (base, head, options) => {
|
|
567
567
|
try {
|
|
568
568
|
const contextArg = options.context ? `-U${options.context}` : "";
|
|
569
|
-
|
|
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];
|
|
570
573
|
const filterArg = filters.length > 0 ? `-- ${filters.map((f: string) => `"${f}"`).join(" ")}` : "";
|
|
571
574
|
const gitCommand = (() => {
|
|
572
575
|
if (options.staged)
|
|
@@ -1031,12 +1034,16 @@ cli
|
|
|
1031
1034
|
.option("--context <lines>", "Number of context lines (default: 3)")
|
|
1032
1035
|
.option("--theme <name>", "Theme to use for rendering")
|
|
1033
1036
|
.option("--filter <pattern>", "Filter files by glob pattern (can be used multiple times)")
|
|
1037
|
+
.option("--title <title>", "HTML document title")
|
|
1034
1038
|
.action(async (base, head, options) => {
|
|
1035
1039
|
const pty = await import("@xmorse/bun-pty");
|
|
1036
1040
|
const { ansiToHtmlDocument } = await import("./ansi-html.ts");
|
|
1037
1041
|
|
|
1038
1042
|
const contextArg = options.context ? `-U${options.context}` : "";
|
|
1039
|
-
|
|
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];
|
|
1040
1047
|
const filterArg = filters.length > 0 ? `-- ${filters.map((f: string) => `"${f}"`).join(" ")}` : "";
|
|
1041
1048
|
const gitCommand = (() => {
|
|
1042
1049
|
if (options.staged)
|
|
@@ -1133,7 +1140,7 @@ cli
|
|
|
1133
1140
|
const backgroundColor = rgbaToHex(theme.background);
|
|
1134
1141
|
const textColor = rgbaToHex(theme.text);
|
|
1135
1142
|
|
|
1136
|
-
return ansiToHtmlDocument(ansiOutput, { cols, rows: renderRows, backgroundColor, textColor, autoTheme: !customTheme });
|
|
1143
|
+
return ansiToHtmlDocument(ansiOutput, { cols, rows: renderRows, backgroundColor, textColor, autoTheme: !customTheme, title: options.title });
|
|
1137
1144
|
}
|
|
1138
1145
|
|
|
1139
1146
|
// Generate desktop and mobile versions in parallel
|
|
@@ -1376,5 +1383,4 @@ cli
|
|
|
1376
1383
|
|
|
1377
1384
|
cli.help();
|
|
1378
1385
|
cli.version("1.0.0");
|
|
1379
|
-
// comment
|
|
1380
1386
|
cli.parse();
|
package/src/worker.ts
CHANGED
|
@@ -90,7 +90,8 @@ app.post("/upload", async (c) => {
|
|
|
90
90
|
|
|
91
91
|
// View HTML content with streaming
|
|
92
92
|
// GET /view/:id
|
|
93
|
-
// Query params: ?v=desktop or ?v=mobile to
|
|
93
|
+
// Query params: ?v=desktop or ?v=mobile to select version
|
|
94
|
+
// Server redirects mobile devices to ?v=mobile, client JS also handles redirect
|
|
94
95
|
app.get("/view/:id", async (c) => {
|
|
95
96
|
const id = c.req.param("id")
|
|
96
97
|
|
|
@@ -98,12 +99,21 @@ app.get("/view/:id", async (c) => {
|
|
|
98
99
|
return c.text("Invalid ID", 400)
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
// Check for
|
|
102
|
-
const
|
|
103
|
-
|
|
102
|
+
// Check for version query param
|
|
103
|
+
const version = c.req.query("v")
|
|
104
|
+
|
|
105
|
+
// If no version specified and mobile device detected, redirect to ?v=mobile
|
|
106
|
+
// This is a fallback - client JS also handles this redirect
|
|
107
|
+
if (!version && isMobileDevice(c)) {
|
|
108
|
+
const url = new URL(c.req.url)
|
|
109
|
+
url.searchParams.set("v", "mobile")
|
|
110
|
+
return c.redirect(url.toString(), 302)
|
|
111
|
+
}
|
|
104
112
|
|
|
105
|
-
//
|
|
113
|
+
// Serve the appropriate version based on query param
|
|
114
|
+
const isMobile = version === "mobile"
|
|
106
115
|
let html: string | null = null
|
|
116
|
+
|
|
107
117
|
if (isMobile) {
|
|
108
118
|
// Try mobile version first, fall back to desktop
|
|
109
119
|
html = await c.env.CRITIQUE_KV.get(`${id}-mobile`)
|
|
@@ -120,11 +130,9 @@ app.get("/view/:id", async (c) => {
|
|
|
120
130
|
|
|
121
131
|
// Stream the HTML content for faster initial load
|
|
122
132
|
return stream(c, async (s) => {
|
|
123
|
-
// Set content type header
|
|
124
133
|
c.header("Content-Type", "text/html; charset=utf-8")
|
|
125
|
-
//
|
|
126
|
-
c.header("
|
|
127
|
-
c.header("Cache-Control", "public, max-age=3600")
|
|
134
|
+
// Cache is now safe - URL determines content, no Vary needed
|
|
135
|
+
c.header("Cache-Control", "public, max-age=86400")
|
|
128
136
|
|
|
129
137
|
// Stream in chunks for better performance
|
|
130
138
|
const chunkSize = 16 * 1024 // 16KB chunks
|
package/diff-viewer-demo.png
DELETED
|
Binary file
|