pasteup 0.1.0__tar.gz → 0.2.0__tar.gz

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.
Files changed (58) hide show
  1. {pasteup-0.1.0 → pasteup-0.2.0}/CHANGELOG.md +27 -1
  2. {pasteup-0.1.0 → pasteup-0.2.0}/PKG-INFO +214 -25
  3. {pasteup-0.1.0 → pasteup-0.2.0}/README.md +213 -24
  4. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/__init__.py +1 -1
  5. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/models/schemas.py +96 -0
  6. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/routes/jobs.py +142 -1
  7. pasteup-0.2.0/src/pasteup/services/folder_browser.py +152 -0
  8. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/services/folder_scanner.py +37 -4
  9. pasteup-0.2.0/src/pasteup/services/upscaler.py +152 -0
  10. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/static/css/styles.css +249 -5
  11. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/static/js/app.js +431 -34
  12. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/templates/index.html +67 -2
  13. pasteup-0.2.0/tests/test_browse_folder_api.py +165 -0
  14. pasteup-0.2.0/tests/test_macos_metadata_files.py +127 -0
  15. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_request_safety.py +1 -0
  16. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_ui_generation_modes.py +61 -3
  17. pasteup-0.2.0/tests/test_upscale_images.py +249 -0
  18. {pasteup-0.1.0 → pasteup-0.2.0}/.github/workflows/release.yml +0 -0
  19. {pasteup-0.1.0 → pasteup-0.2.0}/.github/workflows/tests.yml +0 -0
  20. {pasteup-0.1.0 → pasteup-0.2.0}/.gitignore +0 -0
  21. {pasteup-0.1.0 → pasteup-0.2.0}/LICENSE +0 -0
  22. {pasteup-0.1.0 → pasteup-0.2.0}/SECURITY.md +0 -0
  23. {pasteup-0.1.0 → pasteup-0.2.0}/pyproject.toml +0 -0
  24. {pasteup-0.1.0 → pasteup-0.2.0}/scripts/protect-repo.sh +0 -0
  25. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/__main__.py +0 -0
  26. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/cli.py +0 -0
  27. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/main.py +0 -0
  28. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/models/__init__.py +0 -0
  29. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/routes/__init__.py +0 -0
  30. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/routes/ui.py +0 -0
  31. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/services/__init__.py +0 -0
  32. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/services/cover_collage.py +0 -0
  33. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/services/exporter.py +0 -0
  34. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/services/grid_composer.py +0 -0
  35. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/services/image_batcher.py +0 -0
  36. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/services/video_exporter.py +0 -0
  37. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/shortcut.py +0 -0
  38. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/static/favicon.svg +0 -0
  39. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/static/fonts/OFL-Caveat.txt +0 -0
  40. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/static/fonts/OFL-PatrickHand.txt +0 -0
  41. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/static/fonts/caveat-latin-700.woff2 +0 -0
  42. {pasteup-0.1.0 → pasteup-0.2.0}/src/pasteup/static/fonts/patrick-hand-latin-400.woff2 +0 -0
  43. {pasteup-0.1.0 → pasteup-0.2.0}/tests/conftest.py +0 -0
  44. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_batch_cover_collage_api.py +0 -0
  45. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_cli.py +0 -0
  46. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_cover_collage.py +0 -0
  47. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_cover_collage_api.py +0 -0
  48. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_generate_all_folders.py +0 -0
  49. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_generate_single_folder_video_status.py +0 -0
  50. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_grid_composer.py +0 -0
  51. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_image_batcher.py +0 -0
  52. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_page_dpi_and_geometry.py +0 -0
  53. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_partial_page_and_generation_options.py +0 -0
  54. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_scan_folder_api.py +0 -0
  55. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_scan_selected_batch_folders.py +0 -0
  56. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_transparent_png_handling.py +0 -0
  57. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_ui_defaults.py +0 -0
  58. {pasteup-0.1.0 → pasteup-0.2.0}/tests/test_video_exporter.py +0 -0
@@ -7,6 +7,31 @@ All notable changes to Pasteup are recorded here. The format follows
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.0] - 2026-10-16
11
+
12
+ ### Added
13
+ - **Browse…** beside the folder field opens a folder browser, so a folder can be
14
+ picked instead of typed. It opens at the folder in the field, or at your home
15
+ folder, says how many images the folder you're in holds, and scans the one
16
+ you choose. Typing or pasting a path still works.
17
+ (`POST /api/jobs/browse-folder`)
18
+ - Upscaled images, from a tile of their own in step 3: every image of a bundle,
19
+ or of each picked bundle, made 2x, 3x or 4x bigger and saved in an
20
+ `Upscaled` folder as a PNG, which keeps any transparency, and a JPG on
21
+ white. (`POST /api/jobs/upscale-images`, `POST /api/jobs/upscale-images-batch`)
22
+
23
+ ### Fixed
24
+ - Hidden files are no longer counted as images. A Mac writes a hidden `._`
25
+ file beside the files it copies to an exFAT or FAT32 drive or to some network
26
+ shares, so such a bundle could scan as twice its size, and making sheets then
27
+ reported the extra files as unreadable.
28
+ - Hidden folders, such as the `.Spotlight-V100` and `.Trashes` a Mac keeps on
29
+ those drives, and the `__MACOSX` folder left by unpacking a zip made on a
30
+ Mac, are no longer offered as bundles. Covers no longer take images from
31
+ them, nor from a `---Created---` folder inside the bundle.
32
+ - After a scan or making images, keyboard focus returns to the button that
33
+ started it, instead of falling back to the top of the page.
34
+
10
35
  ## [0.1.0] - 2026-10-16
11
36
 
12
37
  The first release on PyPI.
@@ -29,5 +54,6 @@ The first release on PyPI.
29
54
  already running and moves to a free port when another program has 8000.
30
55
  `pasteup --shortcut` makes a double-click launcher.
31
56
 
32
- [Unreleased]: https://github.com/ftymoshchuk/pasteup/compare/v0.1.0...HEAD
57
+ [Unreleased]: https://github.com/ftymoshchuk/pasteup/compare/v0.2.0...HEAD
58
+ [0.2.0]: https://github.com/ftymoshchuk/pasteup/compare/v0.1.0...v0.2.0
33
59
  [0.1.0]: https://github.com/ftymoshchuk/pasteup/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pasteup
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Turn a folder of clip art into listing images: preview sheets, cover collages and preview videos.
5
5
  Project-URL: Source, https://github.com/ftymoshchuk/pasteup
6
6
  Project-URL: Issues, https://github.com/ftymoshchuk/pasteup/issues
@@ -50,6 +50,8 @@ publishing: cut out by hand and pasted onto a layout board.
50
50
  main image.
51
51
  - **Preview video:** an MP4 slideshow of the sheets, exactly as long as you ask
52
52
  (Etsy's listing videos run 5 to 15 s).
53
+ - **Upscaled images:** every image made 2x, 3x or 4x bigger, saved in an
54
+ `Upscaled` folder as a PNG, which keeps any transparency, and a JPG on white.
53
55
  - **Clean-up:** transparent PNGs land on white instead of black, phone photos
54
56
  are turned upright, and near-white backgrounds can be removed.
55
57
  - **Batch mode:** process every bundle folder under a parent folder in one go.
@@ -73,6 +75,21 @@ it without installing anything: `uvx pasteup`.
73
75
  For changes not released yet, install from GitHub instead:
74
76
  `pipx install git+https://github.com/ftymoshchuk/pasteup.git`.
75
77
 
78
+ ### No pipx yet?
79
+ Install pipx once (or uv, which needs nothing else), then open a new terminal
80
+ window so the command is found:
81
+
82
+ - **macOS:** `brew install pipx`, then `pipx ensurepath`. Without
83
+ [Homebrew](https://brew.sh), install uv instead:
84
+ `curl -LsSf https://astral.sh/uv/install.sh | sh`
85
+ - **Windows:** `py -m pip install --user pipx`, then `py -m pipx ensurepath`.
86
+ Or install uv from PowerShell:
87
+ `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`
88
+ - **Linux:** `sudo apt install pipx` (Debian, Ubuntu) or `sudo dnf install pipx`
89
+ (Fedora), then `pipx ensurepath`.
90
+
91
+ Then run `pipx install pasteup`, or `uv tool install pasteup` if you chose uv.
92
+
76
93
  ## Run
77
94
  ```bash
78
95
  pasteup
@@ -135,7 +152,8 @@ PyPI trusts `release.yml` running in this repository's `pypi` environment.
135
152
  Project layout:
136
153
  - `src/pasteup/main.py`: the FastAPI app
137
154
  - `src/pasteup/routes/`: the page and the `/api/jobs` endpoints
138
- - `src/pasteup/services/`: scanning, page composition, collages, export, video
155
+ - `src/pasteup/services/`: scanning and browsing folders, page composition,
156
+ collages, export, video
139
157
  - `src/pasteup/models/`: request and response schemas
140
158
  - `src/pasteup/templates/` and `src/pasteup/static/`: the UI
141
159
  - `src/pasteup/cli.py`: the `pasteup` command; `shortcut.py` makes its launchers
@@ -152,6 +170,12 @@ Input scan supports the following image extensions (case-insensitive):
152
170
 
153
171
  Notes:
154
172
  - Folder scan is **non-recursive** (only files directly inside the selected folder).
173
+ - Hidden files, whose names start with a dot, are skipped. Most are the `._`
174
+ files a Mac writes beside the files it copies to an exFAT or FAT32 drive or
175
+ to some network shares: they carry the image's extension but hold no image.
176
+ - Hidden folders, `__MACOSX` (left by unpacking a zip made on a Mac) and
177
+ `---Created---` are never offered as bundles, and covers don't look inside
178
+ them.
155
179
  - Export output format supports `PNG` and `JPG`.
156
180
 
157
181
  ---
@@ -170,12 +194,15 @@ Default UI values:
170
194
  - `background_tolerance`: `240`
171
195
  - `background_feather`: `1`
172
196
  - `include_partial_page`: `false`
197
+ - upscaling `scale`: `2` (also `3` or `4`)
173
198
 
174
199
  Behavior defaults:
175
200
  - Scan sample size: first `5` files (`sample_files`) in sorted order.
176
201
  - Images per page: `12` (fixed 4x3).
177
202
  - Export naming: `page_001`, `page_002`, ...
178
203
  - Export conflict handling: never overwrite existing `page_###`; continue at next index.
204
+ - Upscaled images keep their image's name, in `Upscaled/PNG` and `Upscaled/JPG`,
205
+ so upscaling again replaces the files the last run made.
179
206
 
180
207
  ---
181
208
 
@@ -187,7 +214,7 @@ Health check endpoint.
187
214
 
188
215
  **Response**
189
216
  ```json
190
- {"status": "ok", "app": "pasteup", "version": "0.1.0"}
217
+ {"status": "ok", "app": "pasteup", "version": "0.2.0"}
191
218
  ```
192
219
 
193
220
  ### `POST /api/jobs/scan-folder`
@@ -205,8 +232,40 @@ Validates a folder and scans for supported image files.
205
232
  - `image_count` (int): number of supported files found.
206
233
  - `sample_files` (list[str]): up to 5 sorted sample filenames.
207
234
  - `estimated_pages` (int): `image_count // 12` (whole pages only).
235
+ - `available_subfolders` (list): when the folder has no images of its own, the
236
+ folders inside it, each with `display_name` and `full_path`, for picking one
237
+ bundle or listing many. Hidden folders, `__MACOSX` and `---Created---` are
238
+ left out. Empty when the folder has images.
208
239
  - `message` (str): summary or error detail.
209
240
 
241
+ ### `POST /api/jobs/browse-folder`
242
+ Lists the folders inside a folder, for the folder browser behind **Browse…**. A
243
+ page can't read the path of a folder picked with the browser's own file
244
+ chooser, so the UI browses through this instead.
245
+
246
+ **Request body**
247
+ ```json
248
+ {
249
+ "folder_path": "~/Pictures"
250
+ }
251
+ ```
252
+ A blank `folder_path` lists the home folder.
253
+
254
+ **Response fields**
255
+ - `path` (str): the folder listed. One that does not exist or cannot be opened
256
+ gives way to the nearest folder above it that can, and `message` says why.
257
+ - `parent_path` (str | null): the folder above it; `null` at the top of a drive.
258
+ - `breadcrumbs` (list): `name` and `path` of each folder from the top of the
259
+ drive down to this one.
260
+ - `folders` (list): `name` and `path` of each folder inside, sorted, leaving
261
+ out hidden folders, `__MACOSX` and `---Created---`.
262
+ - `image_count` (int): supported images directly inside, counted as
263
+ `scan-folder` counts them.
264
+ - `places` (list): shortcuts: the home folder, its Desktop, Documents,
265
+ Downloads and Pictures folders when it has them, and each drive on Windows.
266
+ - `message` (str): empty unless a different folder was listed than the one
267
+ asked for.
268
+
210
269
  ### `POST /api/jobs/scan-selected-batch-folders`
211
270
  Validates selected batch subfolders under a scanned parent folder and reports which selected folders are ready for generation.
212
271
 
@@ -292,6 +351,7 @@ and the response message gives ffmpeg's error.
292
351
  - `output_files` (list[str]): generated file paths
293
352
  - `output_path` (str): normalized output folder path
294
353
  - `images_dropped` (int): trailing images left out because they did not fill a page
354
+ - `images_skipped` (int): files that could not be read as images
295
355
  - `backgrounds_cleaned` (int): images whose background was cleaned
296
356
  - `page_geometry` (str): resolved page size, e.g. `3000x2400px (10.00x8.00in at 300 DPI)`
297
357
  - `video_file` (str | null), `video_created` (bool): the preview video, when requested
@@ -324,13 +384,139 @@ Used by the UI in **Many bundles** mode. Iterates bundle folders under a parent
324
384
  - `bundle_results` (list): per-folder generation summaries
325
385
  - `message` (str): batch generation summary
326
386
 
387
+ ### `POST /api/jobs/generate-cover-collage`
388
+ Makes one cover image for a folder: its images arranged in one of three layouts.
389
+
390
+ **Request body**
391
+ ```json
392
+ {
393
+ "folder_path": "/path/to/images",
394
+ "output_path": "/path/to/output",
395
+ "page_width": 3000,
396
+ "page_height": 3000,
397
+ "format": "JPG",
398
+ "collage_template": "randomized-grid",
399
+ "selected_cover_images": ["cover001.jpg", "cover002.png"]
400
+ }
401
+ ```
402
+ - `collage_template` (`randomized-grid` | `feature-spread` | `center-stage`,
403
+ default `randomized-grid`): the layout.
404
+ - `image_fit` (`cover` | `contain`, default `cover`): `cover` fills each tile
405
+ and crops the overflow; `contain` fits the whole image and leaves margins.
406
+ - `selected_cover_images` (list of file names, optional): use only these files.
407
+ Names must match exactly, extension included; when none of them match, the
408
+ request fails and says so.
409
+ - The output quality fields above apply too.
410
+
411
+ **Response fields**
412
+ - `success` (bool)
413
+ - `total_images` (int): images in the collage
414
+ - `pages_generated` (int)
415
+ - `output_files` (list[str]): the cover's file path
416
+ - `output_path` (str)
417
+ - `page_geometry` (str): resolved page size
418
+ - `message` (str)
419
+
420
+ ### `POST /api/jobs/generate-cover-collage-batch`
421
+ Makes one cover per bundle folder, as `/generate-all-folders` does for sheets.
422
+
423
+ **Request body**
424
+ ```json
425
+ {
426
+ "parent_folder_path": "/path/to/parent-folder",
427
+ "created_output_root_path": "/path/to/created-output-root",
428
+ "selected_subfolders": ["/path/to/parent-folder/bundle-a", "/path/to/parent-folder/bundle-b"],
429
+ "page_width": 3000,
430
+ "page_height": 3000,
431
+ "format": "JPG",
432
+ "collage_template": "feature-spread"
433
+ }
434
+ ```
435
+ `collage_template`, `image_fit` and the output quality fields work as for
436
+ `/generate-cover-collage`.
437
+
438
+ **Response fields**
439
+ - `success` (bool)
440
+ - `created_output_root_path` (str)
441
+ - `processed_folders`, `selected_folders`, `total_detected_folders`,
442
+ `generated_folders` (int): as for `/generate-all-folders`
443
+ - `bundle_results` (list): per bundle, `folder_name`, `success`,
444
+ `pages_generated`, `total_images`, `output_path`, `output_files` and `message`
445
+ - `message` (str)
446
+
447
+ ### `POST /api/jobs/upscale-images`
448
+ Makes every image of a folder bigger, saving each as a PNG and as a JPG in an
449
+ `Upscaled` folder inside `output_path`: `Upscaled/PNG/<name>.png` and
450
+ `Upscaled/JPG/<name>.jpg`. The PNG keeps any transparency; the JPG is
451
+ flattened onto white. Images are resampled with Lanczos, which keeps edges
452
+ smooth but adds no detail.
453
+
454
+ **Request body**
455
+ ```json
456
+ {
457
+ "folder_path": "/path/to/images",
458
+ "output_path": "/path/to/output",
459
+ "scale": 2,
460
+ "dpi": 300
461
+ }
462
+ ```
463
+ - `scale` (`2` | `3` | `4`, default `2`): how many times bigger each side becomes.
464
+ - `dpi` (int, 1-1200, default `300`): stamped into every file.
465
+ - Each file keeps its image's name, so upscaling again replaces what the last
466
+ run made.
467
+ - Files sharing a name in different formats, such as `art.png` and `art.jpg`,
468
+ would be saved over each other, so one gives both formats: PNG first, then
469
+ WebP, then JPG.
470
+ - Upscaled images are capped at 100 megapixels; an image that would pass that
471
+ is left out and named in the response.
472
+
473
+ **Response fields**
474
+ - `success` (bool): whether any image was upscaled
475
+ - `scale` (int)
476
+ - `total_images` (int): images upscaled
477
+ - `output_path` (str): the `Upscaled` folder
478
+ - `png_folder`, `jpg_folder` (str): the two folders inside it
479
+ - `output_files` (list[str]): every file written
480
+ - `images_skipped` (int): files that could not be read as images
481
+ - `too_large_files` (list[str]): images left out for passing the cap
482
+ - `same_name_files` (list[str]): files left out for sharing a name with the
483
+ image that was used
484
+ - `message` (str)
485
+
486
+ ### `POST /api/jobs/upscale-images-batch`
487
+ Upscales each selected bundle into `<created_output_root_path>/<bundle>/Upscaled`.
488
+
489
+ **Request body**
490
+ ```json
491
+ {
492
+ "parent_folder_path": "/path/to/parent-folder",
493
+ "created_output_root_path": "/path/to/created-output-root",
494
+ "selected_subfolders": ["/path/to/parent-folder/bundle-a", "/path/to/parent-folder/bundle-b"],
495
+ "scale": 4,
496
+ "dpi": 300
497
+ }
498
+ ```
499
+
500
+ **Response fields**
501
+ - `success` (bool)
502
+ - `created_output_root_path` (str)
503
+ - `processed_folders`, `selected_folders`, `total_detected_folders`,
504
+ `generated_folders` (int): as for `/generate-all-folders`
505
+ - `scale` (int)
506
+ - `bundle_results` (list): per bundle, `folder_name`, `success`,
507
+ `total_images`, `output_path`, `images_skipped`, `too_large_files`,
508
+ `same_name_files` and `message`
509
+ - `message` (str)
510
+
327
511
  ---
328
512
 
329
513
  ## Using the app
330
- 1. Run `pasteup` and open `http://127.0.0.1:8000/`.
514
+ 1. Run `pasteup`; it opens in your browser.
331
515
  2. **Pick your clip art.** Choose **One bundle** (a folder of images) or **Many
332
- bundles** (a folder with one folder inside per bundle), enter the folder and
333
- click **Scan folder**. Results go in a `---Created---` folder next to it.
516
+ bundles** (a folder with one folder inside per bundle), then click
517
+ **Browse…**: open folders until you're in the right one, and **Use this
518
+ folder** scans it. Or type or paste its path and click **Scan folder**.
519
+ Results go in a `---Created---` folder next to it.
334
520
  - One bundle: **Folder check** says how many images it found and how many
335
521
  full sheets of 12 they make. If the folder holds only other folders, pick
336
522
  one under **Choose folders**.
@@ -338,8 +524,8 @@ Used by the UI in **Many bundles** mode. Iterates bundle folders under a parent
338
524
  Untick any you want to skip. A bundle that fails its check can be unticked,
339
525
  or fixed and checked again with **Check selected bundles**.
340
526
  3. **Set up the page.** Size, units, DPI and format apply to sheets and covers
341
- alike. **Clean up white backgrounds** can make near-white backgrounds pure
342
- white or transparent.
527
+ alike, and DPI to upscaled images too. **Clean up white backgrounds** can
528
+ make near-white backgrounds pure white or transparent.
343
529
  4. **Make your images.**
344
530
  - **Preview sheets:** every image, 12 to a page (`POST /api/jobs/generate`,
345
531
  or `POST /api/jobs/generate-all-folders` for many bundles), optionally with a
@@ -348,6 +534,10 @@ Used by the UI in **Many bundles** mode. Iterates bundle folders under a parent
348
534
  (`POST /api/jobs/generate-cover-collage`, or
349
535
  `POST /api/jobs/generate-cover-collage-batch`). **Only use certain images**
350
536
  limits it to the files you name.
537
+ - **Upscaled images:** every image 2x, 3x or 4x bigger, as a PNG and a JPG
538
+ in an `Upscaled` folder (`POST /api/jobs/upscale-images`, or
539
+ `POST /api/jobs/upscale-images-batch`). It runs on its own, with no
540
+ sheets or cover needed.
351
541
  5. **Your files** lists what was made and where it was saved; **Copy** puts a
352
542
  path on the clipboard.
353
543
 
@@ -360,27 +550,20 @@ Used by the UI in **Many bundles** mode. Iterates bundle folders under a parent
360
550
  - Trailing images that do not fill a complete page are left out by default; the
361
551
  response reports how many. Set `include_partial_page: true` to render that final
362
552
  page with its unused cells left white.
363
-
364
- ---
365
-
366
- ## Acceptance Checklist (MVP Scope)
367
- - [ ] App runs locally with FastAPI and serves UI.
368
- - [ ] Folder scan validates path and reports supported image count.
369
- - [ ] Supported file types: JPG/JPEG/PNG/WEBP.
370
- - [ ] Estimated pages computed using fixed 12 images/page.
371
- - [ ] Generation creates fixed 4x3 grids with centered, aspect-preserving fit.
372
- - [ ] Transparent PNGs composite onto white rather than black.
373
- - [ ] Exports carry DPI metadata so the page has a real physical size.
374
- - [ ] Background cleaning only touches near-white areas connected to the border.
375
- - [ ] Output exports as PNG or JPG.
376
- - [ ] Exports use sequential naming (`page_###`) and avoid overwriting existing files.
377
- - [ ] API returns clear success/error messages for scan and generation.
378
- - [ ] UI supports end-to-end flow: input paths → scan → generate.
553
+ - Upscaling resamples each image: edges stay smooth, but no detail is added,
554
+ so at 4x clip art looks soft. AI upscalers can invent detail; Pasteup has
555
+ none built in.
379
556
 
380
557
  ---
381
558
 
382
559
  ## Troubleshooting
383
560
 
561
+ ### `pasteup` runs an old version
562
+ An earlier `pip install` of Pasteup can come first on your PATH. List every
563
+ copy with `type -a pasteup` (macOS, Linux) or `where pasteup` (Windows), and
564
+ remove the old one with `pip uninstall pasteup` in the environment it belongs
565
+ to. `pasteup --version` then shows the version pipx or uv installed.
566
+
384
567
  ### Transparent PNGs used to come out with a black background
385
568
  Fixed. Transparent areas are composited onto the page's white rather than having
386
569
  the alpha channel dropped, which is what left `(0, 0, 0)` showing through. This
@@ -440,7 +623,7 @@ Symptoms:
440
623
  Fixes:
441
624
  - Confirm files are directly in the selected folder (non-recursive scan).
442
625
  - Confirm extensions are supported.
443
- - Check that files are not hidden/system placeholders.
626
+ - Check the files aren't hidden: names starting with a dot are skipped.
444
627
 
445
628
  ### Permission errors
446
629
  Symptoms:
@@ -452,6 +635,12 @@ Fixes:
452
635
  - Verify write permission on output folder.
453
636
  - Try a user-owned directory (for example inside your home directory).
454
637
 
638
+ ### "Pasteup isn't allowed to open" Desktop, Documents or Downloads on a Mac
639
+ macOS asks before a program reads these folders, and Pasteup reads them as the
640
+ terminal app it runs in (Terminal, for the `pasteup --shortcut` launcher). If
641
+ that was refused, turn the folder on for your terminal app under **System
642
+ Settings › Privacy & Security › Files and Folders**, then restart Pasteup.
643
+
455
644
  ---
456
645
 
457
646
  ## How images are loaded