--- name: screenshot-format description: Reference for the `.screenshot` document format used by ScreenShotComposer (the macOS package bundle, JSON manifest, per-platform composition lists, layer schema, asset packaging, export pixel sizes, and bezel catalog). Read this whenever authoring or editing a `.screenshot` file by hand, generating one from a script or test fixture, building a template that produces compositions, or debugging save/load issues for `CompositionRecord`, `CompositionLayerRecord`, `EmbeddedImageAsset`, `PlateAxisAlignedFrame`, or `ScreenshotPackageSerialization`. --- # `.screenshot` document format ScreenShotComposer documents are **macOS package bundles** with the UTI `com.garrabrant.screenshot` (conforms to `public.package`). Finder shows the bundle as a single file; "Show Package Contents" reveals plain JSON plus an `assets/` directory of raster files. Read/write goes through `ScreenShotComposer/ScreenshotPackageSerialization.swift` (CW-50). The on-disk JSON is **the source of truth** — base64 image bytes never go inside JSON. Use this skill when: - Hand-writing or scripting a `.screenshot` file (test fixtures, golden files, demo content). - Authoring a new code-side template (use the **template-author** skill, which depends on this reference). - Debugging migration/serialization (legacy `headline`/`caption`/`image` -> `layers`, plate-frame migration, asset rehydration). - Writing parsers/exporters in another language. ## Bundle layout ``` MyDocument.screenshot/ directory bundle (UTType .screenshotDocument) info.json manifest (schemaVersion, app, about, export presets, sidebar order) mac.json { "platform": "mac", "compositions": [ CompositionRecord, ... ] } iphone.json same shape, "platform": "iphone" ipad.json same shape, "platform": "ipad" assets/ mac-01--image-02.png raster bytes for one EmbeddedImageAsset iphone-01--background-01.jpg ... QuickLook/ Preview.png first non-empty image in sidebar order; lets Finder/Quick Look preview without an extension ``` Hard caps: **10 compositions per platform** (`ScreenshotFilePayload.maxCompositionsPerPlatform`). Each platform always has one `.json` (empty `compositions: []` is fine). ## Where the canonical types live | Concept | Swift type | File | |---------|-----------|------| | Top-level payload | `ScreenshotFilePayload` | `ScreenShotComposer/ScreenshotDocumentFormat.swift` | | One screenshot in a bucket | `CompositionRecord` | same | | One layer (text/image/background/group) | `CompositionLayerRecord` | same | | Pixel-space layer rectangle | `PlateAxisAlignedFrame` | same | | Plate gradient (2-stop) | `ExportPlateLinearGradientSettings` | same | | Plate gradient (multi-stop) | `ExportPlateAdvancedGradientSettings` | same | | Plate fill discriminator | `PlateBackgroundFillKind` | same | | Image bytes wrapper | `EmbeddedImageAsset` | same | | Bundle read/write | `ScreenshotPackageSerialization` | `ScreenShotComposer/ScreenshotPackageSerialization.swift` | | Mac plate sizes | `MacAppStoreExportPreset` | `ScreenShotComposer/MacAppStoreExportPreset.swift` | | iPhone/iPad plate sizes | `AppStoreMobileExportDimensions` | `ScreenShotComposer/AppStoreMobileExportDimensions.swift` | | Bezel catalog (PNG + inner-rect) | `ProductBezelCatalog`, `ProductBezelDefinition` | `ScreenShotComposer/ProductBezelCatalog.swift`, `ScreenShotComposer/ProductBezels/catalog.json` | Always defer to those types if this doc and the code disagree. ## Quick checklist for a hand-built file 1. Pick `schemaVersion: 4`, `app: "ScreenShotComposer"`. 2. Choose one `exportGroupPreset*` per platform from the allowed sets (see [export-sizes.md](references/export-sizes.md)). Every composition in a bucket inherits that pixel size; mismatched values get normalized away. 3. For each composition, position layers via `plateFrame` in **pixel coordinates** (top-left origin, may extend off-plate). Do **not** use the legacy `horizontalOffsetNormalized` / `textWrapWidthFraction` family — those are read-only migrations. 4. Always include exactly one `kind: "background"` layer at the lowest `sortOrder`. `ensureBackgroundLayerIfNeeded()` enforces this on load; new files should ship it directly so they round-trip cleanly. 5. For image layers, write `image: { mimeType, assetPath }` (no `dataBase64` field) and put the actual bytes in `assets/`. JSON encoder uses `[.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]`. 6. For text layers, set at minimum `kind`, `sortOrder`, `textContent`, `textStyle`, `plateFrame`. Defaults match `CompositionLayerRecord.init`; omit fields you want at default. 7. After serializing all platforms, regenerate `QuickLook/Preview.png` from the first non-empty image in `sidebarPlatformOrder` (or omit the directory if there is none). ## References - [references/package-bundle.md](references/package-bundle.md) — `info.json` + `.json` shapes, asset path slug rules, QuickLook preview rules. - [references/composition-record.md](references/composition-record.md) — Field-by-field schema for `CompositionRecord`, `CompositionLayerRecord`, gradients, alignment enums, defaults that may be omitted. - [references/export-sizes.md](references/export-sizes.md) — Allowed pixel tokens for `exportGroupPresetMac/IPhone/IPad`. - [references/bezels.md](references/bezels.md) — Catalog of `productBezelId` values, orientation buckets, inner-rect coordinates. ## Related skills and tools - **template-author** skill — uses this reference to add or edit code-side templates in `ComposerTemplateLibrary.swift`. - `bin/refresh-product-bezels` — rescans `bezels/PNG`, regenerates `ScreenShotComposer/ProductBezels/catalog.json`. - `bin/refresh-template-previews` — re-renders `--.png` thumbnails. - `bin/test-mac` — runs the round-trip serialization tests under `ScreenShotComposerTests/`.