critique 0.1.12 → 0.1.13
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 +6 -0
- package/README.md +34 -15
- package/package.json +1 -1
- package/screenshot-web.png +0 -0
- package/src/worker.ts +13 -7
- package/tmp/playwriter-screenshot-1768239285679-m7ha.jpg +0 -0
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -86,7 +86,13 @@ Use the interactive UI to select files. Selected files are immediately applied a
|
|
|
86
86
|
|
|
87
87
|
### Web Preview
|
|
88
88
|
|
|
89
|
-
Generate a shareable web preview of your diff that you can send to anyone - no installation required
|
|
89
|
+
Generate a shareable web preview of your diff that you can send to anyone - no installation required.
|
|
90
|
+
|
|
91
|
+
**Example:** [critique.work/v/b8faf4362c247bfc46f5098a028e00f0](https://critique.work/v/b8faf4362c247bfc46f5098a028e00f0)
|
|
92
|
+
|
|
93
|
+
Great for background agents that can't render terminal UIs, like [kimaki.xyz](https://kimaki.xyz) which runs OpenCode in Discord.
|
|
94
|
+
|
|
95
|
+

|
|
90
96
|
|
|
91
97
|
```bash
|
|
92
98
|
# Upload to critique.work and get a shareable URL
|
|
@@ -95,29 +101,32 @@ critique web
|
|
|
95
101
|
# View staged changes
|
|
96
102
|
critique web --staged
|
|
97
103
|
|
|
98
|
-
# View the last commit
|
|
104
|
+
# View the last commit
|
|
99
105
|
critique web HEAD
|
|
100
106
|
|
|
101
107
|
# View a specific commit
|
|
102
108
|
critique web --commit HEAD~1
|
|
103
109
|
|
|
104
|
-
#
|
|
105
|
-
critique web
|
|
110
|
+
# Compare branches (PR-style diff)
|
|
111
|
+
critique web main feature-branch
|
|
112
|
+
|
|
113
|
+
# Filter specific files
|
|
114
|
+
critique web -- src/api.ts src/utils.ts
|
|
115
|
+
|
|
116
|
+
# Custom title for the HTML page
|
|
117
|
+
critique web --title "Fix authentication bug"
|
|
106
118
|
|
|
107
119
|
# Generate local HTML file instead of uploading
|
|
108
120
|
critique web --local
|
|
109
|
-
|
|
110
|
-
# Adjust rendering size (use ~100 cols for mobile-friendly output)
|
|
111
|
-
critique web --cols 100 --rows 2000
|
|
112
121
|
```
|
|
113
122
|
|
|
114
|
-
**
|
|
123
|
+
**Features:**
|
|
115
124
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
125
|
+
- **Mobile optimized** - Automatically detects mobile devices and serves a unified diff view optimized for smaller screens. Add `?v=mobile` to any URL to force mobile view.
|
|
126
|
+
- **Dark/Light mode** - Automatically adapts to your system's color scheme preference using CSS `prefers-color-scheme`.
|
|
127
|
+
- **Syntax highlighting** - Full syntax highlighting for 18+ languages, same as the terminal UI.
|
|
128
|
+
- **Split view** - Side-by-side diff on desktop, unified view on mobile.
|
|
129
|
+
- **Fast loading** - HTML is streamed for quick initial render, cached for 24 hours.
|
|
121
130
|
|
|
122
131
|
**Options:**
|
|
123
132
|
|
|
@@ -126,14 +135,24 @@ critique web --cols 100 --rows 2000
|
|
|
126
135
|
| `--staged` | Show staged changes | - |
|
|
127
136
|
| `--commit <ref>` | Show changes from a specific commit | - |
|
|
128
137
|
| `--cols <n>` | Terminal width for rendering | `240` |
|
|
129
|
-
| `--
|
|
138
|
+
| `--mobile-cols <n>` | Terminal width for mobile version | `100` |
|
|
130
139
|
| `--local` | Save HTML locally instead of uploading | - |
|
|
131
140
|
| `--filter <pattern>` | Filter files by glob (can be used multiple times) | - |
|
|
141
|
+
| `--title <text>` | Custom HTML document title | `Critique Diff` |
|
|
142
|
+
| `--theme <name>` | Theme for rendering (disables auto dark/light mode) | - |
|
|
143
|
+
|
|
144
|
+
**How it works:**
|
|
145
|
+
|
|
146
|
+
1. Captures the terminal UI output using a PTY (pseudo-terminal)
|
|
147
|
+
2. Converts ANSI escape codes to styled HTML with syntax highlighting
|
|
148
|
+
3. Generates both desktop (240 cols, split view) and mobile (100 cols, unified view) versions
|
|
149
|
+
4. Uploads to [critique.work](https://critique.work) (Cloudflare Worker + KV storage)
|
|
150
|
+
5. Returns a shareable URL that expires after 7 days
|
|
132
151
|
|
|
133
152
|
**Tips:**
|
|
134
153
|
|
|
135
|
-
- Use `--cols 100` for mobile-friendly output (switches to unified diff view instead of split view)
|
|
136
154
|
- The URL is based on a SHA-256 hash of the content, so identical diffs produce the same URL (deduplication)
|
|
155
|
+
- Use `?v=desktop` or `?v=mobile` query params to force a specific version
|
|
137
156
|
- If upload fails, critique automatically saves the HTML locally as a fallback
|
|
138
157
|
|
|
139
158
|
## Features
|
package/package.json
CHANGED
|
Binary file
|
package/src/worker.ts
CHANGED
|
@@ -80,7 +80,7 @@ app.post("/upload", async (c) => {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
const url = new URL(c.req.url)
|
|
83
|
-
const viewUrl = `${url.origin}/
|
|
83
|
+
const viewUrl = `${url.origin}/v/${id}`
|
|
84
84
|
|
|
85
85
|
return c.json({ id, url: viewUrl })
|
|
86
86
|
} catch (error) {
|
|
@@ -89,10 +89,10 @@ app.post("/upload", async (c) => {
|
|
|
89
89
|
})
|
|
90
90
|
|
|
91
91
|
// View HTML content with streaming
|
|
92
|
-
// GET /view/:id
|
|
92
|
+
// GET /v/:id (short) or /view/:id (legacy)
|
|
93
93
|
// Query params: ?v=desktop or ?v=mobile to select version
|
|
94
94
|
// Server redirects mobile devices to ?v=mobile, client JS also handles redirect
|
|
95
|
-
|
|
95
|
+
async function handleView(c: any) {
|
|
96
96
|
const id = c.req.param("id")
|
|
97
97
|
|
|
98
98
|
if (!id || !/^[a-f0-9]{16,32}$/.test(id)) {
|
|
@@ -144,7 +144,10 @@ app.get("/view/:id", async (c) => {
|
|
|
144
144
|
offset += chunkSize
|
|
145
145
|
}
|
|
146
146
|
})
|
|
147
|
-
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
app.get("/v/:id", handleView)
|
|
150
|
+
app.get("/view/:id", handleView)
|
|
148
151
|
|
|
149
152
|
// Get raw HTML content (for debugging/API access)
|
|
150
153
|
// GET /raw/:id
|
|
@@ -167,8 +170,8 @@ app.get("/raw/:id", async (c) => {
|
|
|
167
170
|
})
|
|
168
171
|
|
|
169
172
|
// Check if content exists
|
|
170
|
-
// HEAD /view/:id
|
|
171
|
-
|
|
173
|
+
// HEAD /v/:id or /view/:id
|
|
174
|
+
async function handleHead(c: any) {
|
|
172
175
|
const id = c.req.param("id")
|
|
173
176
|
|
|
174
177
|
if (!id || !/^[a-f0-9]{16,32}$/.test(id)) {
|
|
@@ -183,6 +186,9 @@ app.on("HEAD", "/view/:id", async (c) => {
|
|
|
183
186
|
|
|
184
187
|
c.header("Content-Length", String(html.length))
|
|
185
188
|
return c.body(null, 200)
|
|
186
|
-
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
app.on("HEAD", "/v/:id", handleHead)
|
|
192
|
+
app.on("HEAD", "/view/:id", handleHead)
|
|
187
193
|
|
|
188
194
|
export default app
|
|
Binary file
|