paper-snapshot-cli 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jeremy Osih
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,187 @@
1
+ # paper-snapshot
2
+
3
+ Paper Snapshot as a CLI. Copy and paste your real site directly into Paper as editable layers. No more screenshots.
4
+
5
+ Give paper-snapshot a URL, a local dev server, or a tab an agent already clicked its way to. It
6
+ loads the page, captures it (or a single element) with [Paper Snapshot](https://paper.design/snapshot-extension)'s
7
+ own serializer, and writes the result into the Paper file you have open as a new artboard: real
8
+ text, real layout, real colors and images, every layer editable. It is the Paper Snapshot
9
+ extension without the clicking, so an agent can bring a whole app into Paper in one command and
10
+ refresh any screen when the app changes.
11
+
12
+ ## Install
13
+
14
+ paper-snapshot needs Node 22 or later, the [Paper](https://paper.design/downloads) app running
15
+ (for pushing into a file), and a Chromium-based browser. An installed Chrome, Edge, Brave or
16
+ Chromium is found automatically; with none, a headless Chromium is downloaded once.
17
+
18
+ Run it without installing anything:
19
+
20
+ ```sh
21
+ npx paper-snapshot-cli https://linear.app
22
+ ```
23
+
24
+ Or install the `paper-snapshot` command:
25
+
26
+ ```sh
27
+ npm install -g paper-snapshot-cli
28
+ ```
29
+
30
+ The first run downloads the Paper Snapshot extension from the Chrome Web Store and caches it in
31
+ `~/.cache/paper-snapshot`. Nothing from Paper ships in this repository.
32
+
33
+ ## Agents
34
+
35
+ Install the agent skill for Claude Code, OpenCode, Codex and other agents:
36
+
37
+ ```sh
38
+ npx skills add jeremyosih/paper-snapshot --skill paper-snapshot -g
39
+ ```
40
+
41
+ With the skill, an agent picks the source (a URL, or the browser it drove), sets up state such as
42
+ sign-in, theme and open menus with flags instead of scripts, places the artboards, and runs with
43
+ `--json`. Each capture prints one line with the artboard id, position, size and `warnings`, a count
44
+ of anything that won't import cleanly, so the agent can screenshot the artboards that need a look
45
+ and report the rest. The skill also tells the agent which steps wait on you, such as Chrome's
46
+ **Allow** dialog, so you hear about them before the command blocks.
47
+
48
+ ## Usage
49
+
50
+ ```sh
51
+ # A whole page, into the Paper file you have open
52
+ paper-snapshot https://linear.app
53
+
54
+ # One element of a local dev server, at phone size
55
+ paper-snapshot http://localhost:3000 -s "#pricing" --width 390 --height 844
56
+
57
+ # Several screens side by side (paths resolve against the first URL)
58
+ paper-snapshot http://localhost:3000/ /pricing /settings --layout row
59
+
60
+ # A batch from a manifest, one JSON line per artboard
61
+ paper-snapshot --manifest screens.json --json
62
+
63
+ # Refresh an existing artboard after the app changed (same id, position and name)
64
+ paper-snapshot http://localhost:3000/settings --replace 1A-0
65
+
66
+ # App state without scripting: dark theme kept in storage, a session cookie, an open menu
67
+ paper-snapshot http://localhost:3000 --local-storage theme=dark --color-scheme dark \
68
+ --cookie "session=…" --click "[data-testid=account-menu]"
69
+
70
+ # The tab agent-browser is on, after it logged in and opened a dialog
71
+ paper-snapshot
72
+
73
+ # A tab in your everyday Chrome (click Allow when Chrome asks)
74
+ paper-snapshot --source chrome --tab localhost:3000
75
+
76
+ # Just the HTML, or the extension's exact clipboard payload for a manual ⌘V
77
+ paper-snapshot https://example.com --out snap.html
78
+ paper-snapshot https://example.com --clipboard
79
+ ```
80
+
81
+ Every behaviour is a flag with a default: viewport, color scheme, page state, timeouts, scrolling,
82
+ fidelity passes, and the target file, page and position in Paper. `paper-snapshot --help` lists
83
+ them all.
84
+
85
+ ## Browsers
86
+
87
+ Pages come from one of five places, chosen with `--source`:
88
+
89
+ | Source | Page comes from | What you do |
90
+ | --------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
91
+ | `launch` | A headless copy of your installed Chrome. The default when a URL is given | Nothing |
92
+ | `agent-browser` | The tab [agent-browser](https://github.com/vercel-labs/agent-browser) is driving. The default without a URL | Keep an agent-browser session open |
93
+ | `chrome` | Your everyday Chrome, with your logins. Tried after agent-browser when no URL is given | Turn on `chrome://inspect/#remote-debugging` once, then click **Allow** on each run |
94
+ | `playwriter` | Your Chrome through the [Playwriter](https://github.com/remorses/playwriter) extension | Connect the extension to its relay |
95
+ | `cdp` | Any Chrome DevTools Protocol endpoint (`--cdp`) | Start that browser with remote debugging |
96
+
97
+ Attaching lets an agent click, type and sign in first, then capture exactly that state. Attached
98
+ browsers are never closed. Passing a URL while attached opens a temporary tab and closes it after;
99
+ without a URL, `--tab <text>` picks the tab whose URL or title contains the text, and otherwise
100
+ the visible one.
101
+
102
+ With `--source chrome`, Chrome shows **Allow remote debugging?** each time paper-snapshot connects,
103
+ on the machine Chrome runs on, and keeps a "controlled by automated test software" banner up while
104
+ it's attached. The command waits for you, up to `--chrome-approval-timeout` (2 minutes); **Cancel**
105
+ fails it. A batch connects once, so it asks once. paper-snapshot reads Chrome's port from its
106
+ `DevToolsActivePort` file; when macOS hides that file from the calling app, it connects to
107
+ `127.0.0.1:9222` instead (`--chrome-port`).
108
+
109
+ ## Batches and placement
110
+
111
+ Several URLs, or a `--manifest` JSON file, run as one batch over one browser and one Paper
112
+ connection. Each capture prints its own JSON line, and a failed capture prints
113
+ `{"index", "url", "error"}` without stopping the rest; the exit code is 1 if any failed.
114
+
115
+ A manifest item is a URL, or an object whose fields mirror the flags in camelCase.
116
+ `{ "defaults": {…}, "items": […] }` shares fields:
117
+
118
+ ```json
119
+ {
120
+ "defaults": { "width": 1440, "storageState": "member.json" },
121
+ "items": [
122
+ { "url": "/", "name": "Home · light" },
123
+ {
124
+ "url": "/",
125
+ "name": "Home · dark",
126
+ "colorScheme": "dark",
127
+ "localStorage": { "theme": "dark" }
128
+ },
129
+ { "url": "/settings", "replace": "1A-0" }
130
+ ]
131
+ }
132
+ ```
133
+
134
+ New artboards follow `--layout row|column|grid` (with `--columns` and `--gap`) from the first
135
+ one, which `--x/--y`, `--below <artboardId>` or `--right-of <artboardId>` anchor. Without an
136
+ anchor, Paper picks an empty spot. `--replace <artboardId>` re-captures into an existing artboard
137
+ and keeps its id, position and name; `--file-id` and `--page-id` choose where new ones go.
138
+
139
+ ## Fidelity
140
+
141
+ Before serializing, paper-snapshot adjusts the page so it imports the way it renders, and undoes
142
+ every change afterwards:
143
+
144
+ | Problem | What paper-snapshot does | Flag |
145
+ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------ | --------------------- |
146
+ | App shells (`h-screen` with an `overflow: auto` main) cut off below the fold | Grows the viewport until no scroll container clips | `--full-page` |
147
+ | Icons from external SVG sprites import empty | Fetches the sprite and inlines the symbol with its computed colors | `--inline-sprites` |
148
+ | A `colspan` cell imports one column wide | Freezes the table into rows and cells at their rendered widths | `--fix-colspans` |
149
+ | `height: auto` images collapse | Pins rendered image heights | `--pin-image-heights` |
150
+ | Paper can't fetch images from localhost | Inlines them as data URIs | `--inline-images` |
151
+
152
+ Each pass is on by default; `--no-<flag>` turns one off. Whatever still won't import cleanly is
153
+ counted in the `warnings` of each JSON line: `emptySvgs`, `unresolvedSprites`,
154
+ `clippedScrollContainers`, `unresolvedImages` and `lostColspans`.
155
+
156
+ `--color-scheme` only flips the `prefers-color-scheme` media query. Apps that keep their theme in
157
+ storage also need `--local-storage <key>=dark`.
158
+
159
+ ## How it works
160
+
161
+ The Paper Snapshot extension walks the element you pick, keeps only the computed styles that
162
+ differ from the browser's defaults, folds in pseudo-elements, canvas and video frames, form state
163
+ and same-document SVG references, and copies the resulting HTML to the clipboard wrapped in
164
+ `<x-paper-html>`. Paper turns that paste into layers.
165
+
166
+ paper-snapshot downloads the extension from the Chrome Web Store, evaluates its scripts in a
167
+ sandbox, and finds that serializer by what it does, not by its minified name, so new releases of
168
+ the extension keep working. It runs the serializer in the page through the Chrome DevTools
169
+ Protocol, then writes the HTML into a new artboard through the MCP relay that ships with the Paper
170
+ app (`~/.paper/bin/paper mcp`). `--clipboard` writes the extension's exact clipboard payload
171
+ instead.
172
+
173
+ ## Development
174
+
175
+ ```sh
176
+ bun install && bun run setup && bun run hooks:setup # dependencies, Effect diagnostics, git hooks
177
+ bun run check # oxfmt and oxlint with anti-slop (vite-plus), tsc with Effect diagnostics, knip
178
+ bun run build # dist/bin.mjs, committed so installs from GitHub need no build
179
+ bun run dev -- https://example.com --out -
180
+ ```
181
+
182
+ Built with [Effect](https://effect.website) 4 and its CLI module. Paper and Paper Snapshot are
183
+ products of Paper; this project is not affiliated with them.
184
+
185
+ ## License
186
+
187
+ MIT