@maccesar/titools 4.5.0 → 4.6.1

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.
@@ -1,32 +1,38 @@
1
1
  # App Icons & Branding
2
2
 
3
- The `purgetss brand` command (introduced in PurgeTSS v7.6.0, restructured in v7.7.0, extended in v7.10.0 with the Google Play Feature Graphic) generates the complete Titanium branding set from a single SVG or PNG logo — with optional Android-specific overrides when you need them: launcher icons across every Android density, the adaptive-icon trio (foreground + background + monochrome), iOS 18+ Dark and Tinted variants, marketplace artwork (including the 1024×500 Feature Graphic for Google Play), and optional notification/splash icons. Alloy and Classic layouts are auto-detected.
3
+ The `purgetss brand` command (introduced in v7.6.0, grouped config in v7.7.0, restructured into per-piece blocks in v7.13.0) regenerates **every image the Titanium template ships**, from one main SVG or PNG logo: launcher icons, adaptive icons, iOS 18+ Dark/Tinted variants, marketplace artwork, and both the iOS and Android splash sets. Per-piece overrides are there when you need them. Alloy and Classic layouts are auto-detected.
4
+
5
+ The rule is simple: **if the template ships the file, `brand` updates it.** You should never have to know which API still reads which file to avoid shipping an app with the grey Alloy logo somewhere in it.
4
6
 
5
7
  For the terse flag reference, see the [`brand` command reference](./cli-commands.md#brand-command). For sibling UI assets, see [Multi-Density Images](./multi-density-images.md).
6
8
 
7
- > **INFO**
9
+ > **WARNING**
8
10
  >
9
- > The `brand` command at a glance
10
- > `purgetss brand` turns one logo into every asset a Titanium app needs to ship — launcher icons, adaptive icons, iOS 18+ Dark/Tinted variants, marketplace artwork, and optional notification/splash icons. Works on both Alloy and Classic projects.
11
+ > Breaking changes in v7.13.0
12
+ > `--splash` → `--splash-icon`, `--notification` → `--notification-icon`, `--splash-logo` → `--splash-icon-logo`, `--feature-logo` → `--feature-graphic-logo`. `--icon-logo` now feeds the `icon` piece (the Android launcher source is `--adaptive-logo`). `--legacy-splash` is gone: the per-qualifier Android splashes belong to `android-splash` and are always generated. **No flag aliases were kept.** The `brand:` config block also changed shape, but that one updates itself — see [Older configs update themselves](#older-configs-update-themselves).
11
13
 
12
14
  <!-- TOC-START -->
13
15
  ## Contents
14
16
 
15
17
  - [Quick start](#quick-start)
16
18
  - [The `purgetss/brand/` convention](#the-purgetssbrand-convention)
17
- - [The `brand:` config section (v7.7.0 grouped structure)](#the-brand-config-section-v770-grouped-structure)
19
+ - [The `brand:` config section](#the-brand-config-section)
20
+ - [Brand config reference](#brand-config-reference)
18
21
  - [Overwrite confirmation](#overwrite-confirmation)
19
22
  - [What gets generated](#what-gets-generated)
23
+ - [Shrinking the generated files](#shrinking-the-generated-files)
24
+ - [Regenerating a single piece with `--only`](#regenerating-a-single-piece-with---only)
20
25
  - [Android dark mode](#android-dark-mode)
21
26
  - [Android 12+ splash artwork](#android-12-splash-artwork)
22
- - [Android legacy splash fallback](#android-legacy-splash-fallback)
27
+ - [Android pre-12 splash](#android-pre-12-splash)
28
+ - [The iOS launch screen and LaunchLogo.png](#the-ios-launch-screen-and-launchlogopng)
29
+ - [iPhone launch images](#iphone-launch-images)
23
30
  - [iOS 18+ Dark and Tinted variants](#ios-18-dark-and-tinted-variants)
24
- - [Google Play Feature Graphic (v7.10.0)](#google-play-feature-graphic-v7100)
25
31
  - [Brand color](#brand-color)
26
32
  - [Padding guidance](#padding-guidance)
27
33
  - [Cleanup legacy branding artifacts](#cleanup-legacy-branding-artifacts)
28
34
  - [Troubleshooting](#troubleshooting)
29
- - [Full flag reference](#full-flag-reference)
35
+ - [Flag reference](#flag-reference)
30
36
  - [Community-Discovered Patterns](#community-discovered-patterns)
31
37
  - [See also](#see-also)
32
38
 
@@ -34,7 +40,7 @@ For the terse flag reference, see the [`brand` command reference](./cli-commands
34
40
 
35
41
  ## Quick start
36
42
 
37
- Drop a logo file into `purgetss/brand/`, then run the command. That's the whole workflow.
43
+ Drop a logo file into `purgetss/brand/`, then run the command.
38
44
 
39
45
  ```bash
40
46
  mkdir -p purgetss/brand # if the folder doesn't exist yet
@@ -59,45 +65,48 @@ purgetss brand --dry-run
59
65
 
60
66
  `init` creates `purgetss/brand/` (alongside `fonts/` and `images/`) so the folder is already there the first time you look for it, even before you've dropped in a logo.
61
67
 
62
- PurgeTSS auto-discovers logo files under this folder, the same way `purgetss/fonts/` works for fonts. Drop files with these names and you're done — no flags, no config:
68
+ PurgeTSS auto-discovers logo files under this folder, the same way `purgetss/fonts/` works for fonts. The naming rule is `logo-<piece>`, using the same piece names the CLI and the config use. Drop a file in and you're done:
63
69
 
64
70
  ```text
65
71
  purgetss/brand/
66
- ├── logo.svg required — main logo (or logo.png)
67
- ├── logo-icon.svg optional — square Android launcher mark
68
- ├── logo-mono.svg optional — monochrome layer + notifications
69
- ├── logo-dark.svg optional — iOS 18+ dark variant
70
- ├── logo-splash.svg optional — Android 12+ splash icon override
71
- ├── logo-tinted.svg optional — iOS 18+ tinted variant
72
- └── logo-feature.svg optional — Google Play Feature Graphic (1024×500) override
72
+ ├── logo.svg required — main logo (or logo.png), source for every piece
73
+ ├── logo-mono.svg optional — monochrome layer + notification icons
74
+ ├── logo-icon.svg optional — DefaultIcon.png + DefaultIcon-ios.png
75
+ ├── logo-adaptive.svg optional — square Android launcher mark
76
+ ├── logo-dark.svg optional — iOS 18+ dark variant
77
+ ├── logo-tinted.svg optional — iOS 18+ tinted variant
78
+ ├── logo-launch.svg optional — iOS launch screen logotype (LaunchLogo.png)
79
+ ├── logo-splash-icon.svg optional — Android 12+ splash icon override
80
+ ├── logo-android-splash.svg optional — Android <12 splash artwork
81
+ ├── logo-ios-splash.svg optional — iPhone launch images
82
+ ├── logo-marketplace.svg optional — App Store / Play Store artwork
83
+ ├── logo-feature-graphic.svg optional — Google Play Feature Graphic override
84
+ ├── logo-legacy-icon.svg optional — legacy ic_launcher.png
85
+ ├── logo-appicon.svg optional — appicon.png
86
+ └── logo-notification-icon.svg optional — notification icons
73
87
  ```
74
88
 
75
- Only `logo.svg` (or `logo.png`) is required. Everything else is **opt-in refinement**:
76
-
77
- | File | Required? | What it's for | Fallback when omitted |
78
- | --- | --- | --- | --- |
79
- | `logo.svg` / `logo.png` | **Required** | Main colored logo. Feeds every density and variant. | — |
80
- | `logo-icon.svg` / `.png` | Optional | Square Android launcher mark — use when `logo.svg` is a wide wordmark or non-square lockup. | Main logo is reused for Android launcher icons. |
81
- | `logo-mono.svg` / `.png` | Optional | Android adaptive monochrome layer (themed icons on Android 13+) and notification icons. | Main logo is whitened automatically. |
82
- | `logo-splash.svg` / `.png` | Optional | Android 12+ `splash_icon.png` artwork (only used when `--splash` / `android.splash: true`). | Falls back to the launcher artwork. |
83
- | `logo-dark.svg` / `.png` | Optional | iOS 18+ Dark appearance variant. | Main logo on a transparent background (Apple HIG default). |
84
- | `logo-tinted.svg` / `.png` | Optional | iOS 18+ Tinted appearance variant. | Grayscale of the main logo on black. |
85
- | `logo-feature.svg` / `.png` | Optional | Google Play Feature Graphic (1024×500). Use when the Play Store banner should differ from the main app icon (e.g. logo + tagline lockup that fills the wider rectangular canvas). | Main logo is centered inside the banner with configured vertical padding. |
86
-
87
- Provide a dedicated `logo-icon` when the main logo is a horizontal wordmark, a vertical lockup, or anything else that looks fine in a 1024×1024 branding canvas but feels cramped inside an Android launcher mask.
89
+ Only `logo.svg` (or `logo.png`) is required. Every other file is an override for one piece, and dropping it in is all it takes. There is no syntax to remember, and opening the folder shows what has been customized.
88
90
 
89
- Provide a dedicated `logo-mono` when the colored logo has detail that would collapse into a featureless blob under naive whitening (e.g. a painter's palette with colored dots — the monochrome variant should have cutouts instead).
91
+ Every one of them has a CLI equivalent, `--<piece>-logo <path>`, and a config equivalent, `brand.<piece>.logo`, for artwork that lives outside `purgetss/brand/`.
90
92
 
91
- Provide a dedicated `logo-splash` when the Android 12+ splash should use a different composition than the launcher icon. PurgeTSS generates the file, but Titanium still needs a custom Android splash theme if you want the system splash to use it instead of `ic_launcher`.
93
+ The ones worth knowing about:
92
94
 
93
- Provide a dedicated `logo-dark` when dark-mode brand guidelines use a different lockup or color treatment; provide `logo-tinted` when you want a pre-simplified silhouette that tints better than a naive grayscale of the colored version.
94
-
95
- Provide a dedicated `logo-feature` when the Google Play Feature Graphic (1024×500) should use a different composition than the main app icon — for example a logo-plus-tagline lockup or a wider artwork that takes advantage of the rectangular canvas instead of being constrained to the centered square.
95
+ | File | What it's for | Fallback when omitted |
96
+ | --- | --- | --- |
97
+ | `logo-icon` | Alternate artwork for `DefaultIcon.png` / `DefaultIcon-ios.png`, when the universal icon should differ from the main logo. | Main logo. |
98
+ | `logo-adaptive` | A separate **square** mark for Android launcher icons. Use it when the main logo is a horizontal wordmark, a vertical lockup, or anything else that looks fine on a 1024×1024 canvas but feels cramped inside an Android launcher mask. | Main logo. |
99
+ | `logo-mono` | Silhouette for the Android adaptive monochrome layer (themed icons on Android 13+) and for notification icons. | Main logo, whitened automatically. |
100
+ | `logo-dark` | iOS 18+ dark mode. Provide your own when dark-mode brand guidelines use a different lockup or color treatment. | Main logo on a transparent background (Apple's recommended approach). |
101
+ | `logo-tinted` | iOS 18+ tinted mode. Provide your own when a simpler silhouette tints better than a grayscale of the colored version. | Grayscale of the main logo. |
102
+ | `logo-launch` | The only file that also **activates** a piece. Drop it in and `brand` writes `LaunchLogo.png`, so the iOS launch screen shows your logotype instead of the app icon. See [The iOS launch screen and LaunchLogo.png](#the-ios-launch-screen-and-launchlogopng). | Piece is not generated. |
103
+ | `logo-splash-icon` | Alternate artwork for Android 12+ `splash_icon.png`, when the splash should use a different composition than the launcher icon. | Launcher artwork. |
104
+ | `logo-feature-graphic` | Alternate logo for the Google Play Feature Graphic (1024×500 banner) — a logo-plus-tagline lockup, say, or wider artwork that uses the rectangular canvas instead of staying inside the centered square. | Main logo, centered with the configured vertical padding. |
96
105
 
97
106
  > **INFO**
98
107
  >
99
108
  > Prefer SVG for the master
100
- > SVG scales losslessly to every density Sharp needs to emit. A single `logo.svg` rasterizes perfectly at every `res-*dpi` output. PNG masters should be at least **1024×1024** to avoid upscaling artifacts.
109
+ > SVG scales cleanly to every density Sharp needs to emit. A single `logo.svg` can be rasterized at every `res-*dpi` output. PNG masters should be at least **1024×1024** to avoid upscaling artifacts.
101
110
 
102
111
  ### Overriding auto-discovery
103
112
 
@@ -107,143 +116,146 @@ You can also pass a path directly or point to a logo from the config. Useful whe
107
116
  purgetss brand ./docs/snap-logo.svg
108
117
  ```
109
118
 
110
- Or in `purgetss/config.cjs`:
119
+ Or in `purgetss/config.cjs`, where each piece takes its own `logo`:
111
120
 
112
121
  ```javascript
113
122
  brand: {
114
- logos: {
115
- primary: './docs/snap-logo.svg',
116
- androidLauncher: './docs/snap-app-icon.svg',
117
- monochrome: './docs/snap-logo-mono.svg',
118
- featureGraphic: './docs/snap-feature.svg' // optional — Google Play Feature Graphic
119
- }
123
+ adaptive: { logo: './docs/snap-app-icon.svg' },
124
+ featureGraphic: { logo: './docs/snap-feature.svg' },
125
+ monochromeLogo: './docs/snap-logo-mono.svg'
120
126
  }
121
127
  ```
122
128
 
123
129
  Precedence: **CLI flags win over config values, and config values win over auto-discovery.**
124
130
 
125
- ## The `brand:` config section (v7.7.0 grouped structure)
131
+ ### How the work is divided between files and config
132
+
133
+ - **Files decide the artwork.** Dropping `logo-dark.svg` next to `logo.svg` is enough; open the folder and you can see what has been customized. This is the main path.
134
+ - **Config decides numbers, colors and activation**, none of which can be expressed with a filename.
135
+ - `logo:` inside a piece is there for artwork that lives outside `purgetss/brand/`.
136
+
137
+ ## The `brand:` config section
126
138
 
127
- On the first run, `purgetss brand` injects a `brand:` block into your existing `purgetss/config.cjs` (between `purge:` and `theme:`) with these defaults. The structure is grouped by purpose:
139
+ On the first run, `purgetss brand` adds a `brand:` block to your existing `purgetss/config.cjs`, between `purge:` and `theme:`. It has **one block per piece**:
128
140
 
129
141
  ```javascript
130
142
  brand: {
131
- logos: {
132
- // Optional overrides. If omitted, PurgeTSS auto-discovers files from purgetss/brand/:
133
- // primary: './docs/logo.svg',
134
- // androidLauncher: './docs/app-icon.svg',
135
- // androidSplash: './docs/splash.svg',
136
- // monochrome: './docs/logo-mono.svg',
137
- // iosDark: './docs/logo-dark.svg',
138
- // iosTinted: './docs/logo-tinted.svg'
139
- },
140
- padding: {
141
- ios: '4%', // iOS aesthetic padding. Range 2-8%. No launcher mask.
142
- androidLegacy: '10%', // legacy ic_launcher.png padding %
143
- androidAdaptive: '19%', // adaptive icon safe-zone %. Spec floor 19.44%.
144
- featureGraphic: '12%' // vertical padding for MarketplaceArtworkFeature.png (1024×500)
145
- },
146
- android: {
147
- splash: false, // also generate splash_icon.png × 5
148
- notification: false // also generate ic_stat_notify.png × 5
149
- },
150
- colors: {
151
- background: '#FFFFFF' // Android adaptive bg + iOS/marketplace flatten
152
- },
153
- // Optional iOS overrides:
154
- // ios: {
155
- // dark: false, // skip DefaultIcon-Dark.png
156
- // tinted: false, // skip DefaultIcon-Tinted.png
157
- // darkBackground: '#111' // opaque dark bg for DefaultIcon-Dark.png (null = transparent per Apple HIG)
158
- // },
159
- confirmOverwrites: true // prompt before overwriting files (set false to skip)
143
+ background: '#FFFFFF', // inherited by every piece that doesn't set its own
144
+ confirmOverwrites: true, // prompt before overwriting files (set false to skip)
145
+ optimize: false, // true = quantize the generated PNGs to a palette (lossy, ~71% smaller)
146
+
147
+ // One block per piece. Artwork comes from purgetss/brand/logo-<piece>.{svg,png};
148
+ // these keys are for numbers, colors and activation. Padding is never inherited.
149
+ icon: { padding: '4%' }, // DefaultIcon.png + DefaultIcon-ios.png
150
+ dark: { background: null }, // DefaultIcon-Dark.png
151
+ tinted: {}, // DefaultIcon-Tinted.png
152
+ iosSplash: { padding: '26%' }, // assets/iphone/Default*.png × 16
153
+ launchLogo: { padding: '12%' }, // LaunchLogo.png (1024×1024)
154
+ marketplace: {}, // iTunesConnect.png + MarketplaceArtwork.png
155
+ featureGraphic: { padding: '12%' }, // MarketplaceArtworkFeature.png (1024×500)
156
+ adaptive: { padding: '18%' }, // ic_launcher_{foreground,background,monochrome}.png × 5 + ic_launcher.xml
157
+ legacyIcon: { padding: '10%' }, // ic_launcher.png × 5
158
+ appicon: {}, // appicon.png (128×128)
159
+ androidSplash: { padding: '26%' }, // assets/android/default.png + images/res-*/default.png × 11
160
+
161
+ // Opt-in: inert until you edit the Android theme / FCM meta-data by hand.
162
+ splashIcon: { enabled: false }, // drawable-*/splash_icon.png × 5
163
+ notificationIcon: { enabled: false }, // drawable-*/ic_stat_notify.png × 5
164
+ ninePatch: { enabled: false } // background.9.png (not implemented yet)
160
165
  }
161
166
  ```
162
167
 
163
- The recommended workflow is convention-first: drop files in `purgetss/brand/`, let auto-discovery pick them up, and only set `brand.logos.*` when you have a persistent override. CLI flags still win for one-off runs.
168
+ Change only what you want to keep as a project default. CLI flags still win for one-off runs.
164
169
 
165
- ### `brand.logos`
170
+ ### Older configs update themselves
166
171
 
167
- All `logos.*` keys are optional path overrides. If you omit them, PurgeTSS auto-discovers files from `purgetss/brand/`.
172
+ PurgeTSS keeps `config.cjs` current the same way it renamed `config.js` to `config.cjs`: **on the file, once**. When the `brand:` block uses a shape from an earlier version, the next run rewrites it to the per-piece structure and carries over everything that had been customized (paddings, colors, logo paths, enabled flags), then lists each value it moved:
168
173
 
169
- | Key | Auto-discovers | Purpose |
170
- | --- | --- | --- |
171
- | `logos.primary` | `purgetss/brand/logo.svg` | Main brand source. |
172
- | `logos.androidLauncher` | `purgetss/brand/logo-icon.svg` | Square Android launcher mark — use when the main logo is a wordmark. |
173
- | `logos.androidSplash` | `purgetss/brand/logo-splash.svg` | Android 12+ splash artwork. |
174
- | `logos.monochrome` | `purgetss/brand/logo-mono.svg` | Android themed icons + notification icons. |
175
- | `logos.iosDark` | `purgetss/brand/logo-dark.svg` | iOS dark variant. |
176
- | `logos.iosTinted` | `purgetss/brand/logo-tinted.svg` | iOS tinted variant. |
177
- | `logos.featureGraphic` | `purgetss/brand/logo-feature.svg` | Google Play Feature Graphic source (1024×500 rectangular canvas). |
178
-
179
- ### `brand.padding`
180
-
181
- All padding values accept either numbers or percentage strings like `'19%'`.
174
+ ```text
175
+ ::PurgeTSS:: Updated the brand: structure in ./purgetss/config.cjs.
176
+ Your values were carried over:
177
+ • brand.logos.androidLauncher → brand.adaptive.logo
178
+ • brand.padding.androidAdaptive → brand.adaptive.padding
179
+ • brand.android.splash → brand.splashIcon.enabled
180
+ • brand.colors.background → brand.background
181
+ ```
182
182
 
183
- | Key | Default | Purpose |
184
- | --- | --- | --- |
185
- | `padding.ios` | `'4%'` | Visual inset for `DefaultIcon-ios.png`, `DefaultIcon-Dark.png`, `DefaultIcon-Tinted.png`, marketplace artwork. |
186
- | `padding.androidLegacy` | `'10%'` | Visual inset for legacy `ic_launcher.png`. |
187
- | `padding.androidAdaptive` | `'19%'` | Visual inset for adaptive Android foreground (`ic_launcher_foreground.png`). Adjust this first when icons look cropped inside launcher masks. |
188
- | `padding.featureGraphic` | `'12%'` | Vertical padding (top + bottom) for `MarketplaceArtworkFeature.png` (1024×500 Google Play banner). The logo is rendered as a square block of side `500 - 2 × pad` centered horizontally and vertically. Lower it for a more impactful banner; raise it if the logo looks cramped against the top or bottom edge on smaller Play Store crops. |
183
+ This happens on `purgetss brand`, and on any command that goes through the config: `build`, `watch`, `purge`, `shades`. Values that already matched a default are not written, so a block that was never customized comes out clean rather than cluttered with redundant keys.
189
184
 
190
- ### `brand.android`
185
+ Both earlier shapes are recognized: the original flat keys (`brand.padding` as a number, `brand.iosPadding`, `brand.bgColor`, `brand.darkBgColor`, top-level `brand.notification` / `brand.splash`) and the grouped sections from v7.7.0 (`logos` / `padding` / `android` / `ios` / `colors`). One key is dropped rather than moved: `brand.android.legacySplash`, because the per-qualifier splashes belong to `androidSplash` now and are always generated. The run says so when it happens.
191
186
 
192
- | Key | Default | Purpose |
193
- | --- | --- | --- |
194
- | `android.splash` | `false` | When `true`, also generates `drawable-*/splash_icon.png`. |
195
- | `android.notification` | `false` | When `true`, also generates `drawable-*/ic_stat_notify.png`. |
187
+ The command itself understands exactly one structure. Nothing translates on the fly, so the day this migration is removed, nothing else changes.
196
188
 
197
- ### `brand.ios` (optional)
189
+ > **INFO**
190
+ >
191
+ > This replaced the in-memory translation that ran from v7.10.2 to v7.12.1. Back then `normalizeLegacyBrand()` re-translated the old shape on **every** config read — every purge, watch and build, forever. Now the file is fixed once and every command afterwards reads one shape.
198
192
 
199
- If omitted, PurgeTSS behaves as if `ios.dark = true`, `ios.tinted = true`, `ios.darkBackground = null`.
193
+ ### Unknown keys are an error
200
194
 
201
- | Key | Default | Purpose |
202
- | --- | --- | --- |
203
- | `ios.dark` | `true` | Set to `false` to skip `DefaultIcon-Dark.png`. |
204
- | `ios.tinted` | `true` | Set to `false` to skip `DefaultIcon-Tinted.png`. |
205
- | `ios.darkBackground` | `null` | When `null`, `DefaultIcon-Dark.png` stays transparent per Apple HIG. Set a hex to bake in an opaque dark background. |
195
+ A key `brand:` does not define stops the run before a single file is written, at both levels:
206
196
 
207
- ### `brand.colors`
197
+ ```text
198
+ Unknown key(s) in the brand: section of purgetss/config.cjs:
199
+ • brand.adaptive.paddig
208
200
 
209
- | Key | Default | Purpose |
210
- | --- | --- | --- |
211
- | `colors.background` | `'#FFFFFF'` | Triple-purpose: Android adaptive background layer, iOS alpha flatten for `DefaultIcon-ios.png`, marketplace flatten for `iTunesConnect.png` / `MarketplaceArtwork.png` when overridden. |
201
+ Top-level keys: background, confirmOverwrites, logo, monochromeLogo
202
+ Piece blocks: icon, dark, tinted, iosSplash, launchLogo, marketplace, featureGraphic,
203
+ adaptive, legacyIcon, appicon, androidSplash, splashIcon,
204
+ notificationIcon, ninePatch
205
+ Inside a piece: logo, padding, background, enabled
212
206
 
213
- ### `brand.confirmOverwrites`
207
+ Check the spelling. Nothing was written.
208
+ ```
214
209
 
215
- | Key | Default | Purpose |
216
- | --- | --- | --- |
217
- | `confirmOverwrites` | `true` | When `false`, the `[y/N/a]` prompt is skipped. |
210
+ A typo is deliberately **not** treated as an old structure: rewriting the block would drop it silently. And ignoring a misspelled `paddig` would be worse than stopping — the whole icon set would render at the wrong size and look perfectly plausible.
218
211
 
219
- ### Upgrading from pre-7.7.0 configs
212
+ ## Brand config reference
220
213
 
221
- In v7.7.0, the `brand:` block was reorganized into grouped subsections (`logos`, `padding`, `android`, `ios`, `colors`). Since **v7.10.2**, if your `config.cjs` still uses the original flat layout, PurgeTSS auto-migrates it **in memory on every run** — your old config keeps working without any change on disk.
214
+ Every piece accepts the same four keys, where they apply:
222
215
 
223
- | Pre-7.7.0 flat key | Current grouped key |
216
+ | Key | What it does |
224
217
  | --- | --- |
225
- | `brand.padding: <number\|string>` (single value) | `brand.padding.androidLegacy` **and** `brand.padding.androidAdaptive` (same value applied to both) |
226
- | `brand.iosPadding` | `brand.padding.ios` |
227
- | `brand.bgColor` | `brand.colors.background` |
228
- | `brand.darkBgColor` | `brand.ios.darkBackground` |
229
- | `brand.notification` | `brand.android.notification` |
230
- | `brand.splash` | `brand.android.splash` |
231
-
232
- If both legacy and grouped keys are present for the same property, the **grouped key wins**.
218
+ | `logo` | Path to this piece's artwork, when it lives outside `purgetss/brand/`. |
219
+ | `padding` | Inset per side, as a number or a percentage string like `'19%'`. **Never inherited.** |
220
+ | `background` | Hex color, or `null` for transparent. Inherited from `brand.background`. |
221
+ | `enabled` | `false` turns a default piece off; `true` turns an opt-in piece on. |
233
222
 
234
- On first encounter per session, PurgeTSS prints a one-time deprecation notice listing the legacy keys it migrated:
223
+ And these live at the top level:
235
224
 
236
- ```text
237
- ::PurgeTSS:: Legacy brand: schema detected in purgetss/config.cjs — auto-migrated in memory:
238
- • brand.padding: 15 → brand.padding.androidLegacy + brand.padding.androidAdaptive
239
- • brand.iosPadding → brand.padding.ios
240
- • brand.bgColor → brand.colors.background
241
- Update purgetss/config.cjs to the new grouped schema to silence this warning.
242
- ```
243
-
244
- The notice is suppressed once you update the file to the grouped layout. Auto-migration is purely transitional and may be removed in a future major version, so a one-time update to your `config.cjs` is recommended.
245
-
246
- Prior to v7.10.2, projects on the flat layout crashed auto-purge with `TypeError: Cannot create property 'ios' on number '15'` because `brand.padding` was a number rather than an object. v7.10.2 normalizes the shape before defaults are applied.
225
+ | Key | Default | Purpose |
226
+ | --- | --- | --- |
227
+ | `background` | `'#FFFFFF'` | The background every piece inherits unless it declares its own: the Android adaptive background layer, the `DefaultIcon-ios.png` flatten, the splash canvases, and the marketplace flatten when a background is explicitly configured. |
228
+ | `confirmOverwrites` | `true` | Whether `brand` asks before overwriting project files in place. |
229
+ | `optimize` | `false` | Quantize the generated PNGs to a palette. Lossy — see [Shrinking the generated files](#shrinking-the-generated-files). |
230
+ | `logo` | — | Path override for the main logo. |
231
+ | `monochromeLogo` | — | Path override for the silhouette shared by the adaptive monochrome layer and the notification icons. |
232
+
233
+ ### The pieces
234
+
235
+ | Piece | Config key | Generates | Default padding | On by default |
236
+ | --- | --- | --- | --- | --- |
237
+ | `icon` | `icon` | `DefaultIcon.png` + `DefaultIcon-ios.png` | `4%` | yes |
238
+ | `dark` | `dark` | `DefaultIcon-Dark.png` | `4%` | yes |
239
+ | `tinted` | `tinted` | `DefaultIcon-Tinted.png` | `4%` | yes |
240
+ | `ios-splash` | `iosSplash` | `assets/iphone/Default*.png` × 16 | `26%` | yes |
241
+ | `launch-logo` | `launchLogo` | `LaunchLogo.png` (1024×1024) | `12%` | when `logo-launch.*` exists |
242
+ | `marketplace` | `marketplace` | `iTunesConnect.png` + `MarketplaceArtwork.png` | `4%` | yes |
243
+ | `feature-graphic` | `featureGraphic` | `MarketplaceArtworkFeature.png` (1024×500) | `12%` | yes |
244
+ | `adaptive` | `adaptive` | `ic_launcher_{foreground,background,monochrome}.png` × 5 + `ic_launcher.xml` | `18%` | yes |
245
+ | `legacy-icon` | `legacyIcon` | `ic_launcher.png` × 5 | `10%` | yes |
246
+ | `appicon` | `appicon` | `appicon.png` (128×128) | `10%` | yes |
247
+ | `android-splash` | `androidSplash` | `assets/android/default.png` + `images/res-*/default.png` × 11 | `26%` | yes |
248
+ | `splash-icon` | `splashIcon` | `drawable-*/splash_icon.png` × 5 | — | `--splash-icon` |
249
+ | `notification-icon` | `notificationIcon` | `drawable-*/ic_stat_notify.png` × 5 | — | `--notification-icon` |
250
+ | `nine-patch` | `ninePatch` | `background.9.png` | — | `--nine-patch` (not implemented yet) |
251
+
252
+ `ic_launcher.xml` always travels inside `adaptive`; it is never generated on its own.
253
+
254
+ Only three pieces are opt-in, and for one reason: they produce nothing useful until you edit XML by hand. `splash_icon.png` is inert without `windowSplashScreenAnimatedIcon` in the theme, and `ic_stat_notify.png` is inert without the FCM `meta-data` entry.
255
+
256
+ ### `background` is inherited, `padding` is not
257
+
258
+ Set `brand.background` once and every piece picks it up. Padding works the other way on purpose: the defaults answer to different constraints. The adaptive `18%` answers to the Android launcher mask, while the iOS `4%` is an aesthetic choice with no mask behind it. A single inherited number would let `8%` quietly break the launcher mask, so padding is set per piece or not at all.
247
259
 
248
260
  ## Overwrite confirmation
249
261
 
@@ -279,24 +291,29 @@ The output is automatically routed to the right directory for your project layou
279
291
  ```text
280
292
  <project>/
281
293
  ├── DefaultIcon.png ← 1024×1024, universal fallback (Android-safe padding)
282
- ├── DefaultIcon-ios.png ← 1024×1024, iOS flattened on bgColor
294
+ ├── DefaultIcon-ios.png ← 1024×1024, iOS flattened on brand.background
283
295
  ├── DefaultIcon-Dark.png ← 1024×1024, iOS 18+ dark (transparent per Apple HIG)
284
296
  ├── DefaultIcon-Tinted.png ← 1024×1024, iOS 18+ tinted (grayscale on black)
285
297
  ├── iTunesConnect.png ← 1024×1024, App Store submission
286
298
  ├── MarketplaceArtwork.png ← 512×512, Google Play submission
287
- ├── MarketplaceArtworkFeature.png ← 1024×500, Google Play Feature Graphic (v7.10.0)
299
+ ├── MarketplaceArtworkFeature.png ← 1024×500, Google Play Feature Graphic
288
300
  └── app/
289
- └── assets/android/
290
- ├── default.png ← legacy Titanium Android splash fallback (v7.7.0)
291
- └── res/
292
- ├── mipmap-mdpi/ ← 108×108 foreground + background + monochrome + legacy
293
- ├── mipmap-hdpi/ ← 162×162
294
- ├── mipmap-xhdpi/ ← 216×216
295
- ├── mipmap-xxhdpi/ ← 324×324
296
- ├── mipmap-xxxhdpi/ ← 432×432
297
- ├── drawable-*/ ← optional splash_icon.png when --splash is enabled
298
- └── mipmap-anydpi-v26/
299
- └── ic_launcher.xml ← adaptive-icon binder
301
+ ├── assets/iphone/
302
+ │ ├── Default*.png ← the 16 launch images the template ships
303
+ │ └── LaunchLogo.png ← 1024×1024, only when logo-launch.* exists
304
+ ├── assets/android/
305
+ │ ├── appicon.png ← 128×128
306
+ │ ├── default.png ← Android <12 splash
307
+ │ └── images/res-*/default.png ← the 11 per-qualifier splashes
308
+ └── platform/android/res/
309
+ ├── mipmap-mdpi/ ← 108×108 foreground + background + monochrome + legacy
310
+ ├── mipmap-hdpi/ ← 162×162
311
+ ├── mipmap-xhdpi/ ← 216×216
312
+ ├── mipmap-xxhdpi/ ← 324×324
313
+ ├── mipmap-xxxhdpi/ ← 432×432
314
+ ├── drawable-*/ ← optional splash_icon.png with --splash-icon
315
+ └── mipmap-anydpi-v26/
316
+ └── ic_launcher.xml ← adaptive-icon binder
300
317
  ```
301
318
 
302
319
  **Classic layout:**
@@ -304,21 +321,102 @@ The output is automatically routed to the right directory for your project layou
304
321
  ```text
305
322
  <project>/
306
323
  ├── DefaultIcon.png DefaultIcon-ios.png ... ← same root-level files as Alloy
307
- ├── MarketplaceArtworkFeature.png ← 1024×500, Google Play Feature Graphic (v7.10.0)
324
+ ├── MarketplaceArtworkFeature.png ← 1024×500, Google Play Feature Graphic
308
325
  ├── Resources/
309
- │ └── android/default.png ← legacy Titanium Android splash fallback (v7.7.0)
326
+ │ ├── iphone/Default*.png ← the launch images the template ships
327
+ │ └── android/
328
+ │ ├── appicon.png ← 128×128
329
+ │ └── default.png ← Android <12 splash
310
330
  └── platform/
311
331
  └── android/res/
312
332
  ├── mipmap-*/ ← same 5 densities as Alloy
313
- ├── drawable-*/ ← optional splash_icon.png when --splash is enabled
333
+ ├── drawable-*/ ← optional splash_icon.png with --splash-icon
314
334
  └── mipmap-anydpi-v26/ic_launcher.xml
315
335
  ```
316
336
 
337
+ A Classic project has no `images/res-*` folders, so the per-qualifier splashes are simply skipped there.
338
+
317
339
  The Android outputs are related, but they are not interchangeable:
318
340
 
319
- - `ic_launcher*` drives the app icon, and by default also feeds the Android 12+ system splash
320
- - `splash_icon.png` is only generated when you ask for it with `--splash`
321
- - `default.png` is the older Titanium Android splash fallback (regenerated since v7.7.0)
341
+ - `ic_launcher*` drives the app icon, and by default it also feeds the Android 12+ system splash
342
+ - `splash_icon.png` is only generated when you ask for it with `--splash-icon`
343
+ - `default.png` and `images/res-*/default.png` are the Android <12 splash path
344
+ - `appicon.png` is the fallback Titanium uses for `tiapp.xml`'s `<icon>` when the manifest declares no `android:icon`
345
+
346
+ ### Why files that "no longer matter" are regenerated
347
+
348
+ Several of these are read by nothing in a modern build. With `<enable-launch-screen-storyboard>` enabled — the default since Titanium 8 — iOS draws the storyboard and never opens the 16 `Default*.png`. `appicon.png` only comes into play when the Android manifest declares no `android:icon`.
349
+
350
+ They are regenerated anyway, because the alternative is worse: the template ships them with the grey Alloy logo, and deciding which ones to skip would mean asking you to keep track of which Titanium and OS version reads which file. If it is in the project tree, it carries your logo.
351
+
352
+ To skip the ones you know you don't need, use [`--only`](#regenerating-a-single-piece-with---only). To delete them outright, see [Cleanup legacy branding artifacts](#cleanup-legacy-branding-artifacts).
353
+
354
+ ## Shrinking the generated files
355
+
356
+ `brand` writes truecolor PNGs. Logos are flat artwork with few distinct colors, which is exactly the case where a 256-color palette is indistinguishable from truecolor at a fraction of the size — the same trick TinyPNG and pngquant use.
357
+
358
+ It is off by default because it is lossy. Turn it on per run or for the project:
359
+
360
+ ```bash
361
+ purgetss brand --optimize
362
+ ```
363
+
364
+ ```javascript
365
+ brand: {
366
+ optimize: true
367
+ }
368
+ ```
369
+
370
+ `--no-optimize` skips the pass on a single run even when the config asks for it.
371
+
372
+ On the reference project, the full set of 56 PNGs goes from **1.6 MB to 476 KB, 71% smaller**. Measured on the visible pixels of the generated icons, the difference against the truecolor version averages 0.08–0.19 out of 255, with no channel exceeding 16/255: indistinguishable in practice on flat artwork. Transparency survives too — `DefaultIcon-Dark.png` keeps its alpha channel and the same 67% transparent pixels.
373
+
374
+ ### What "lossy" means here, in numbers
375
+
376
+ The loss is real: the generated icons carry between 950 and 4,300 distinct visible colors, not because the logo has that many, but because every curved edge and every letter of small text produces hundreds of intermediate tones through antialiasing. Quantization reduces all of that to at most 256.
377
+
378
+ Those intermediate tones are gradations between two or three base colors, which is exactly what a palette approximates well, and Sharp dithers the result. Measured on the visible pixels:
379
+
380
+ | Source | Colors | Average difference | Channels over 16/255 |
381
+ | --- | --- | --- | --- |
382
+ | Flat logo (the usual case) | 950–4,300 | 0.08–0.19 / 255 | 0% |
383
+ | Three-stop gradient (stress test) | 1,215 | 0.74 / 255 | 0.01% |
384
+
385
+ Even a full-circle gradient does not band measurably. Still, quantization is lossy by definition, so if your mark leans on wide, smooth gradients, compare a generated file before turning it on for the whole project. A file is only ever rewritten when the palette version comes out smaller.
386
+
387
+ > **WARNING**
388
+ >
389
+ > This is tuned for logo artwork. It is not meant for photographic sources.
390
+
391
+ For context on what the platforms already do: iOS re-encodes every PNG in the bundle with `pngcrush -iphone` when it packages the app, but that is **lossless**, so this saving is not something the SDK would have done for you. On Android, nothing in Titanium touches these files.
392
+
393
+ ## Regenerating a single piece with `--only`
394
+
395
+ A full run rewrites every branding file. That is what you want the first time, and it is exactly what you don't want when only one piece changed and the others were tweaked by hand.
396
+
397
+ `--only` takes a comma-separated list of pieces, groups, or both:
398
+
399
+ ```bash
400
+ purgetss brand # everything
401
+ purgetss brand --only icon # just the DefaultIcon pair
402
+ purgetss brand --only ios # icon, dark, tinted, ios-splash
403
+ purgetss brand --only ios,notification-icon
404
+ ```
405
+
406
+ Groups are shorthand for the obvious sets:
407
+
408
+ | Group | Expands to |
409
+ | --- | --- |
410
+ | `ios` | `icon`, `dark`, `tinted`, `ios-splash` |
411
+ | `store` | `marketplace`, `feature-graphic` |
412
+ | `android` | `adaptive`, `legacy-icon`, `appicon`, `android-splash` |
413
+
414
+ Details worth knowing:
415
+
416
+ - Naming a piece generates it **even when its opt-in flag is absent**: `--only notification-icon` is enough on its own.
417
+ - A name that doesn't exist aborts the run before anything is written, printing the valid pieces and groups.
418
+ - `--dry-run` honors the same filter, so you can check the plan first.
419
+ - The order you type doesn't matter; generation always follows the pipeline order.
322
420
 
323
421
  ## Android dark mode
324
422
 
@@ -340,49 +438,80 @@ The monochrome layer is pure white (`RGB 255,255,255`) with alpha preserved. And
340
438
 
341
439
  ## Android 12+ splash artwork
342
440
 
343
- If you pass `--splash`, PurgeTSS generates `drawable-*/splash_icon.png` across Android densities.
441
+ If you pass `--splash-icon`, PurgeTSS generates `drawable-*/splash_icon.png` across Android densities.
344
442
 
345
443
  ```bash
346
- purgetss brand --splash
444
+ purgetss brand --splash-icon
347
445
  ```
348
446
 
349
- If you want that artwork to differ from the launcher icon, provide `logo-splash.svg` or set `brand.logos.androidSplash`:
447
+ If you want that artwork to differ from the launcher icon, provide `logo-splash-icon.svg` or set `brand.splashIcon.logo`:
350
448
 
351
449
  ```javascript
352
450
  brand: {
353
- android: {
354
- splash: true
355
- },
356
- logos: {
357
- androidSplash: './docs/snap-splash-mark.svg'
451
+ splashIcon: {
452
+ enabled: true,
453
+ logo: './docs/snap-splash-mark.svg'
358
454
  }
359
455
  }
360
456
  ```
361
457
 
362
458
  > **INFO**
363
459
  >
364
- > Generating `splash_icon.png` does not automatically switch Titanium to use it for the Android 12+ system splash. Titanium still needs a custom splash theme that points `android:windowSplashScreenAnimatedIcon` to `@drawable/splash_icon`. If you do nothing, Android keeps using `ic_launcher`.
460
+ > Generating `splash_icon.png` does not automatically switch Titanium to use it for the Android 12+ system splash. Titanium still needs a custom splash theme that points `android:windowSplashScreenAnimatedIcon` to `@drawable/splash_icon`. If you do nothing, Android keeps using `ic_launcher`. That inertness is exactly why the piece is opt-in.
461
+
462
+ Keep the theme already assigned to `<application>`. Define a launcher-only theme that inherits from `Theme.Titanium`, then assign it to Titanium's generated launcher Activity. The complete setup is in [Matching the launch background](#matching-the-launch-background).
365
463
 
366
464
  > **WARNING**
367
465
  >
368
- > Merge splash settings into the existing Android theme
369
- > If your app already has a custom Android theme block in `tiapp.xml`, **merge** the new splash settings (the `android:windowSplashScreenAnimatedIcon` entry and any sibling theme attributes) into that existing theme. Do **not** append a second `<application>` element or a duplicate theme block — Titanium will only honor one theme definition, and the duplicate silently shadows or overrides the original, leading to "my settings were ignored" debugging sessions. Always edit the existing `<application>` / theme block in place.
466
+ > Android masks this icon into a circle
467
+ > The Android 12+ splash icon is drawn inside a circular mask. A wide wordmark that fills the canvas loses its corners. Use a square mark for `logo-splash-icon`, the same advice that applies to launcher icons.
370
468
 
371
- > **INFO**
372
- >
373
- > A brief flash on splash exit is usually the system, not your PNGs
374
- > If you still see a brief flash or abrupt exit transition during splash dismissal even with correct assets in place, **do not assume the PNGs PurgeTSS generates are wrong**. That artifact commonly comes from Android 12+'s system splash exit transition (or from Titanium's splash theme handoff to your first window), not from the splash icon files themselves. Regenerating `splash_icon.png` will not change it — the fix lives in the splash theme animation, not in the icon assets.
469
+ Also, if you still see a brief flash during splash exit even with correct assets, do not assume the PNGs are wrong. That artifact can come from Titanium's splash theme or the system splash transition itself.
470
+
471
+ ## Android pre-12 splash
472
+
473
+ Below Android 12 there is no system splash: the launch screen comes from the image Titanium maps into `drawable-*/background.png`, and that image comes from the project's own splash artwork.
375
474
 
376
- ## Android legacy splash fallback
475
+ `brand` regenerates the whole set on every run. It is the `android-splash` piece, on by default:
377
476
 
378
- Since v7.7.0, PurgeTSS regenerates `app/assets/android/default.png` in Alloy projects and `Resources/android/default.png` in Classic projects.
477
+ - `app/assets/android/default.png` (`Resources/android/default.png` in Classic)
478
+ - `app/assets/android/images/res-*/default.png` — the 11 per-qualifier images the Alloy template ships
379
479
 
380
- That file still matters as a fallback on older Titanium Android splash paths, which is why `cleanup-legacy` no longer removes it.
480
+ Earlier versions regenerated only the first file and hid the other 11 behind a `--legacy-splash` flag, which is why a freshly branded project could still flash the grey Alloy logo on an older phone. That flag is gone: its output is part of `android-splash` and always generated.
381
481
 
382
482
  > **INFO**
383
483
  >
384
- > Older Android splash theme assets are left out on purpose
385
- > The older Android splash **theme** assets `background.png` / `background.9.png` are intentionally **not** part of the normal `brand` flow. `brand` targets the modern Titanium icon pipeline (iOS app icons, Android adaptive icons, optional Android 12+ splash artwork), so if a project still depends on those legacy nine-patch theme assets, manage them manually. (Source: upstream `app-assets/1-app-icons-and-branding.md` ~lines 334, 386.)
484
+ > A solid `windowBackground` wins
485
+ > If the launch theme sets `android:windowBackground` to a plain color — which is what [Matching the launch background](#matching-the-launch-background) recommends — that color takes precedence over this artwork on Android <12. Drop the `windowBackground` item if you want the image to show instead.
486
+
487
+ ## The iOS launch screen and LaunchLogo.png
488
+
489
+ Titanium builds `LaunchLogo.imageset` itself on every iOS build, resizing one source into the five sizes it needs. It looks for `LaunchLogo.png` first and falls back to `DefaultIcon.png`.
490
+
491
+ So there is nothing for PurgeTSS to generate there, but there is something to choose. With only `DefaultIcon.png` around, the launch screen shows your app icon, safe-zone padding and all. Dropping a `logo-launch.svg` (or `.png`) into `purgetss/brand/` makes `brand` write a `LaunchLogo.png`, and the launch screen shows the full logotype instead:
492
+
493
+ ```bash
494
+ cp docs/my-wordmark.svg purgetss/brand/logo-launch.svg
495
+ purgetss brand
496
+ ```
497
+
498
+ The file is written at exactly **1024×1024**. That is not a style choice: the SDK validates the size and discards the file with a warning when it does not match.
499
+
500
+ The piece activates by convention (the presence of `logo-launch.*`) rather than through a flag, because `--<piece>-logo` already means "the source for this piece" everywhere else in the command. To generate it from a path without adding the file:
501
+
502
+ ```bash
503
+ purgetss brand --launch-logo docs/my-wordmark.svg
504
+ purgetss brand --only launch-logo
505
+ purgetss brand --launch-logo-padding 18 # more breathing room around the logotype
506
+ ```
507
+
508
+ The output keeps its alpha, so the storyboard's `<default-background-color>` shows through.
509
+
510
+ ## iPhone launch images
511
+
512
+ The Alloy and Classic templates ship 16 `Default*.png` launch images under `assets/iphone/`, from the 320×480 original iPhone size up to 2688×1242. `brand` regenerates all of them, scaling the logo against the **shorter side** of each canvas so portrait and landscape carry the same visual weight.
513
+
514
+ With `<enable-launch-screen-storyboard>` enabled (the default), iOS never reads these files. They are regenerated because they are in your project, they ship with the Alloy logo, and no one should have to audit their `tiapp.xml` to know whether that matters. If you are sure your project doesn't need them, [`--cleanup-legacy`](#cleanup-legacy-branding-artifacts) deletes them instead.
386
515
 
387
516
  ## iOS 18+ Dark and Tinted variants
388
517
 
@@ -410,99 +539,109 @@ purgetss brand --no-dark --no-tinted
410
539
  >
411
540
  > Until that PR lands, after your first iOS build you may need to add the two PNGs manually into `build/iphone/Assets.xcassets/AppIcon.appiconset/` in Xcode (via the "Appearance" column in the asset catalog editor). Once #14122 merges, the command becomes fully end-to-end.
412
541
 
413
- ## Google Play Feature Graphic (v7.10.0)
414
-
415
- Since v7.10.0, `purgetss brand` also generates `MarketplaceArtworkFeature.png` — the **1024×500** banner that Google Play shows above the app description on the Play Store listing. It's submission artwork only: the file is written to the project root for upload via Play Console; it is **not** bundled into the APK.
416
-
417
- ### How the source is chosen
542
+ ## Brand color
418
543
 
419
- PurgeTSS resolves the Feature Graphic source in this order (first match wins):
544
+ The `--bg-color` flag (or `brand.background` in config) controls the background across the generated branding assets:
420
545
 
421
- 1. CLI `--feature-logo <path>` for the current run.
422
- 2. `brand.logos.featureGraphic` in `config.cjs`.
423
- 3. Auto-discovered `purgetss/brand/logo-feature.{svg,png}`.
424
- 4. The main logo (centered inside the 1024×500 canvas with the configured vertical padding).
546
+ 1. The **Android adaptive background layer**: a solid color that fills the full 108dp canvas behind your logo.
547
+ 2. The **iOS alpha flatten** for `DefaultIcon-ios.png`. Apple rejects transparent App Store icons, so the logo is flattened on this color.
548
+ 3. The **marketplace flatten** for `iTunesConnect.png`, `MarketplaceArtwork.png`, and `MarketplaceArtworkFeature.png`.
549
+ 4. **Every splash canvas**: `default.png`, the 11 `res-*/default.png`, and the 16 iPhone launch images.
550
+ 5. `appicon.png`.
425
551
 
426
552
  ```bash
427
- purgetss brand --feature-logo ./docs/feature.svg
553
+ purgetss brand --bg-color "#0B1326"
428
554
  ```
429
555
 
430
- Drop a dedicated logo when you want a logo + tagline lockup or a wider artwork that takes advantage of the rectangular canvas. Otherwise the main `logo.svg` is centered as a square block — fine for most apps.
556
+ Any piece can opt out with its own `background`. That is what `dark: { background: null }` does in the default config, which keeps `DefaultIcon-Dark.png` transparent per Apple HIG.
431
557
 
432
- ### Padding
558
+ If you never pass the flag, background stays `#FFFFFF`. `iTunesConnect.png` and `MarketplaceArtwork.png` keep their alpha channel to match Titanium's default; `MarketplaceArtworkFeature.png` is always flattened for Google Play.
433
559
 
434
- The default vertical padding is `12%` (top + bottom). Override per run with `--feature-graphic-padding <n>` (integer `0-40`), or persist it in `brand.padding.featureGraphic`:
560
+ ### Matching the launch background
435
561
 
436
- ```bash
437
- purgetss brand --feature-graphic-padding 8
438
- ```
562
+ `brand.background` bakes into the generated image pixels. It does **not** edit `tiapp.xml` or an Android theme. Carrying the same color into the native launch surfaces takes an iOS `<default-background-color>` entry plus a launcher-only Android theme — the complete, copy-ready setup is in [Matching the Launch Background](./launch-background.md).
439
563
 
440
- ```javascript
441
- brand: {
442
- padding: {
443
- featureGraphic: '8%'
444
- }
445
- }
446
- ```
564
+ `purgetss brand --notes` prints that setup with the project's current `brand.background` already filled in.
447
565
 
448
- The logo is rendered as a square block of side `500 - 2 × pad` and centered horizontally on the 1024-wide canvas. Lower the padding for a more impactful banner; raise it if the logo looks cramped against the top or bottom edge on smaller Play Store crops.
449
566
 
450
- ## Brand color
567
+ ## Padding guidance
451
568
 
452
- The `--bg-color` flag (or `brand.colors.background` in config) controls three things at once:
569
+ Padding belongs to the piece, not to the project. Each piece has its own key and its own flag:
453
570
 
454
- 1. The **Android adaptive background layer**: a solid color that fills the full 108dp canvas behind your logo.
455
- 2. The **iOS alpha flatten** for `DefaultIcon-ios.png`. Apple rejects transparent App Store icons, so the logo is flattened on this color.
456
- 3. The **marketplace flatten** for `iTunesConnect.png` and `MarketplaceArtwork.png` when you pass a non-default value explicitly.
571
+ | Piece | Config | Flag | Default |
572
+ | --- | --- | --- | --- |
573
+ | `adaptive` | `brand.adaptive.padding` | `--android-adaptive-padding` | `18%` |
574
+ | `legacy-icon` | `brand.legacyIcon.padding` | `--android-legacy-padding` | `10%` |
575
+ | `icon` | `brand.icon.padding` | `--ios-padding` | `4%` |
576
+ | `feature-graphic` | `brand.featureGraphic.padding` | `--feature-graphic-padding` | `12%` |
577
+ | `launch-logo` | `brand.launchLogo.padding` | `--launch-logo-padding` | `12%` |
578
+ | `android-splash` | `brand.androidSplash.padding` | `--android-splash-padding` | `26%` |
579
+ | `ios-splash` | `brand.iosSplash.padding` | `--ios-splash-padding` | `26%` |
457
580
 
458
- ```bash
459
- purgetss brand --bg-color "#0B1326"
581
+ `--padding` is a shortcut for the two Android launcher paddings in a single run, and `--splash-padding` for the two splash paddings. `--ios-padding` moves the four square iOS/marketplace pieces together (`icon`, `dark`, `tinted`, `marketplace`); in config each of them has its own key.
582
+
583
+ There is deliberately **no global padding value that cascades down**. The defaults answer to different constraints: `18%` answers to the Android launcher mask, while `4%` is an aesthetic choice for a canvas with no mask behind it. One inherited number would let a reasonable-looking `8%` silently break the launcher mask. `background`, which has no such trap, is inherited from `brand.background`.
584
+
585
+ ### How the source is read, and how sharp the output is
586
+
587
+ Two things are worth knowing about what happens to your `logo.svg` or `logo.png` before any padding is applied.
588
+
589
+ **The container is what counts, not the artwork's bounding box.** An SVG is read at its `viewBox`, a raster at its full canvas, and neither is trimmed to where the pixels actually are. So whatever margin a designer baked into the file **adds** to the padding configured per piece. A round logo exported inside a 2048×2048 PNG with 25% of its own air, generated at `adaptive: { padding: '18%' }`, ends up covering about 32% of the icon canvas, not 64%. If a mark comes out smaller than the numbers suggest, that is almost always why: crop the source or lower the padding.
590
+
591
+ **The masters are sized to the run.** The source is rasterized once into two intermediate masters, and every piece scales down from them, so their resolution is the ceiling on output sharpness. Rather than a fixed size, `brand` measures the largest number of pixels any selected piece will ask for and builds the masters at exactly that. A default run reports it:
592
+
593
+ ```text
594
+ • Masters at 942 px — the largest any selected piece asks for
460
595
  ```
461
596
 
462
- If you never pass the flag, background stays `#FFFFFF` and the marketplace artwork keeps its alpha channel (matches Titanium's default).
597
+ Lower a padding and the figure rises with it (`--splash-padding 4` needs 1413 px), so output never goes soft against a fixed ceiling. Every destination is a reduction, never an upscale.
463
598
 
464
- ## Padding guidance
599
+ The one case this cannot fix is a raster source that is simply too small: a 512-px PNG cannot produce a sharp 942-px icon. Prefer SVG, or a PNG of at least 1024×1024.
465
600
 
466
- Since v7.7.0, PurgeTSS treats Android adaptive and Android legacy launcher icons as two related but different cases:
601
+ ### Splash padding
467
602
 
468
- - `brand.padding.androidAdaptive` or `--android-adaptive-padding` for the adaptive foreground
469
- - `brand.padding.androidLegacy` or `--android-legacy-padding` for `ic_launcher.png`
470
- - `--padding` is a one-shot **shortcut** that sets both Android paddings to the same value for one run
603
+ The 28 splash images (`default.png`, the 11 `res-*`, and the 16 iPhone launch images) share one rule: the logo is fitted into a square whose side is a share of the canvas's **shorter** side.
471
604
 
472
- The adaptive default is `19%`, which stays close to the Android safe-zone. The legacy default is `10%`, so the flat `ic_launcher.png` can keep a little more visual weight.
605
+ Measuring against the shorter side is what lets a single number work across canvases as different as 1440×2560 and 800×480: at 800×480 the limit comes from the height, at 240×400 from the width, and the logo keeps the same visual weight in portrait and in landscape.
473
606
 
474
- ### Adaptive icon padding
607
+ | `androidSplash.padding` / `iosSplash.padding` | Logo | `default.png` (1440×2560) | `res-notlong-port-mdpi` (320×480) |
608
+ | --- | --- | --- | --- |
609
+ | `20%` | 60% of the shorter side | 864 px | 192 px |
610
+ | `26%` (default) | 48% | 691 px | 153 px |
611
+ | `30%` | 40% | 576 px | 128 px |
612
+ | `35%` | 30% | 432 px | 96 px |
475
613
 
476
- | Padding | Logo fill | When to use |
477
- | ------- | --------- | ------------------------------------------------------------------------------------------------------ |
478
- | `15%` | 70% | Aggressive. Better for square symbols with lots of built-in breathing room. |
479
- | `18%` | 64% | Defensive: for intricate logos, fine serifs, multi-element designs. |
480
- | `19%` | 62% | **Default**. Close to the Android safe-zone and safer for adaptive masks. |
481
- | `20%` | 60% | Conservative, spec-compliant. Safe on every launcher, including aggressive masks. |
614
+ The `26%` default is calibrated against the Titanium template itself: the Alloy logo in the stock `default.png` measures 665×488 px on a 1440×2560 canvas, so `26%` lands within 4% of the size Titanium ships.
482
615
 
483
- A useful visual check is the "corners" heuristic: imagine a circle inscribed in your 1024×1024 canvas with the given padding. If your logo's outermost corners fit inside that circle, you're safe on circular launchers (Pixel default, Oppo Android 15). If they poke out, they'll be clipped.
616
+ Before v7.13.0 none of this was configurable: `default.png` used a hardcoded box of 72% × 26% of its own canvas, and the `res-*` set a separate hardcoded 60%. Two rules for the same piece, neither adjustable.
484
617
 
485
- The official Android spec floor is `19.44%` (108dp canvas, 66dp inscribed safe-zone circle). That is the theoretical worst-case for aggressive adaptive masks, which is why the adaptive default now sits close to it.
618
+ ### Adaptive icon padding
486
619
 
487
- ### Legacy icon padding
620
+ Android's adaptive canvas is 108 dp. The mask leaves roughly 72 dp visible, and the **guaranteed** safe area is a 66 dp circle inscribed in it. What each padding means in those terms:
621
+
622
+ | Padding | Logo | vs. the 66 dp safe circle | vs. the ~72 dp the mask shows |
623
+ | --- | --- | --- | --- |
624
+ | `15%` | 75.6 dp | outside | **outside** — clipped on any launcher |
625
+ | `16%` | 73.4 dp | outside | **outside** |
626
+ | `18%` | 69.1 dp | corners outside | inside — **the default** |
627
+ | `19.44%` | 66.0 dp | exactly on it | inside |
628
+ | `20%` | 64.8 dp | inside | inside — most conservative |
488
629
 
489
- Legacy `ic_launcher.png` does not go through the same adaptive mask, so it can usually run tighter. That is why the default for `brand.padding.androidLegacy` is `10%`.
630
+ `18%` sits between the guaranteed circle and the mask edge: a logo that carries its own margin never reaches those corners, which is why it is a safe default in practice. Drop to `20%` if your mark runs edge to edge and you see clipping on a circular launcher.
490
631
 
491
- ### iOS padding
632
+ A useful visual check is the "corners" heuristic: imagine a circle inscribed in your 1024×1024 canvas with the given padding. If your logo's outermost corners fit inside that circle, you're safe on circular launchers (Pixel default, Oppo Android 15). If they poke out, they'll be clipped.
492
633
 
493
- `--ios-padding` is a separate lever — iOS has no launcher mask, so the range is different:
634
+ The official Android spec floor is `19.44%` (108dp canvas, 66dp inscribed safe-zone circle). That is the theoretical worst-case for aggressive adaptive masks, which is why the adaptive default sits close to it.
494
635
 
495
- | iOS padding | When to use |
496
- | --- | --- |
497
- | `2%` | Bold, edge-to-edge logos. |
498
- | `4%` | **Default**. Balanced aesthetic. |
499
- | `8%` | Conservative, generous whitespace around the mark. |
636
+ ### Legacy icon padding
637
+
638
+ Legacy `ic_launcher.png` does not go through the same adaptive mask, so it can usually run tighter. That is why the default for `brand.legacyIcon.padding` is `10%`.
500
639
 
501
640
  ## Cleanup legacy branding artifacts
502
641
 
503
- Projects that predate Android adaptive icons (API 26+) or modern iOS launch storyboards often accumulate obsolete assets: `res-long-*/res-notlong-*` qualifiers dead since Android 3.0, legacy `Default-*.png` launch images ignored when the storyboard is enabled, pre-adaptive `appicon.png`, and so on.
642
+ Projects that predate Android adaptive icons (API 26+) or modern iOS launch storyboards often accumulate obsolete assets: `res-long-*/res-notlong-*` qualifiers dead since Android 3.0, legacy `Default-*.png` launch images ignored when the storyboard is enabled, pre-adaptive `appicon.png`, and similar files.
504
643
 
505
- The `--cleanup-legacy` flag removes them with context-aware safety rules: it reads `tiapp.xml` to decide what's safe to delete based on your project's configuration. Always preview first:
644
+ The `--cleanup-legacy` flag removes them with context-aware safety rules: it reads `tiapp.xml` to decide what is actually safe to delete for your project. Always preview first:
506
645
 
507
646
  ```bash
508
647
  purgetss brand --cleanup-legacy --dry-run
@@ -525,15 +664,23 @@ purgetss brand --cleanup-legacy --aggressive
525
664
  > Commit first
526
665
  > `--cleanup-legacy` deletes files permanently. Commit your project to git before running without `--dry-run` so `git restore` is available as a rollback.
527
666
 
667
+ Files kept on purpose:
668
+
669
+ - `app/assets/android/default.png` in Alloy projects
670
+ - `Resources/android/default.png` in Classic projects
671
+
672
+ Those files are still valid Android <12 splash artwork.
673
+
528
674
  > **INFO**
529
675
  >
530
- > Files kept on purpose
531
- > `--cleanup-legacy` intentionally does **not** remove the following files, even when modern adaptive launcher icons and splash assets are already in place:
676
+ > `--cleanup-legacy` never deletes what the same run just generated
677
+ > Three cleanup rules target files `brand` now regenerates by default: the `res-long-*`/`res-notlong-*` folders (`android-splash`), the iPhone `Default*.png` images (`ios-splash`) and `appicon.png` (`appicon`). When `--cleanup-legacy` runs as part of a generating pass, those paths are skipped and the run reports how many it spared:
532
678
  >
533
- > - `app/assets/android/default.png` (Alloy projects)
534
- > - `Resources/android/default.png` (Classic projects)
679
+ > ```text
680
+ > Keeping 28 path(s) this run just regenerated with your artwork.
681
+ > ```
535
682
  >
536
- > These remain because they are still required by Titanium framework defaults — older Android splash code paths fall back to `default.png`, and removing it can cause build warnings or a missing-asset error on certain Titanium SDK versions even when modern splash assets exist. Treat them as part of the baseline asset set, not as legacy clutter.
683
+ > To actually delete them, exclude the piece that writes them. For example, `purgetss brand --only android --cleanup-legacy` regenerates the Android icons while letting cleanup remove the iPhone launch images.
537
684
 
538
685
  ## Troubleshooting
539
686
 
@@ -549,9 +696,7 @@ Or set it in the config:
549
696
 
550
697
  ```javascript
551
698
  brand: {
552
- padding: {
553
- androidAdaptive: '20%'
554
- }
699
+ adaptive: { padding: '20%' }
555
700
  }
556
701
  ```
557
702
 
@@ -565,7 +710,7 @@ purgetss brand --android-adaptive-padding 17
565
710
 
566
711
  ### The monochrome version looks like a white blob
567
712
 
568
- Your colored logo likely has multi-color detail that doesn't survive a naive whitening. Provide a dedicated silhouette:
713
+ Your colored logo likely has multi-color detail that does not survive automatic whitening. Provide a dedicated silhouette:
569
714
 
570
715
  ```bash
571
716
  cp docs/my-logo-mono.svg purgetss/brand/logo-mono.svg
@@ -574,7 +719,7 @@ purgetss brand
574
719
 
575
720
  ### iOS rejects the app icon upload ("contains transparency")
576
721
 
577
- That's Apple's rule: App Store icons must have no alpha channel. `DefaultIcon-ios.png` is always flattened on `bgColor` for that reason. If you edited the file manually and reintroduced alpha, re-run `purgetss brand` to regenerate.
722
+ Apple requires App Store icons to have no alpha channel. `DefaultIcon-ios.png` is always flattened on `brand.background` for that reason. If you edited the file manually and reintroduced alpha, re-run `purgetss brand`.
578
723
 
579
724
  ### The dark variant doesn't show on my iPhone
580
725
 
@@ -582,101 +727,39 @@ Dark variants require iOS 18+ and Titanium SDK automatic wiring (tracked upstrea
582
727
 
583
728
  ### I get "Input image exceeds pixel limit" on an SVG from Affinity / Illustrator
584
729
 
585
- Affinity Designer and Adobe Illustrator often bake transforms into the exported SVG's `viewBox`, so the intrinsic dimensions end up at something absurd like `29559×13542 pt`. Rasterized at 1× density, that blows past Sharp's pixel limit and the command crashes.
730
+ Affinity Designer and Adobe Illustrator often bake transforms into the exported SVG's `viewBox`, so the intrinsic dimensions can end up at something like `29559×13542 pt`. Rasterized at 1× density, that exceeds Sharp's pixel limit and the command crashes.
586
731
 
587
732
  PurgeTSS checks the `viewBox` on every SVG. When either side is over `4096 pt`, it prints a warning with the actual dimensions and switches to an adaptive density that caps the output pixel count regardless of input size. The warning tells you the source is oversized; the command still finishes.
588
733
 
589
734
  If you want to clean up the source, re-export from the vector editor with a canvas-sized viewBox (`0 0 1024 1024`, for example). The rasterized output is identical either way, but a normalized viewBox keeps the SVG portable for other tools.
590
735
 
736
+ ### A key in `brand:` aborts the run
737
+
738
+ `brand` rejects unknown keys instead of ignoring them. Check the spelling against the piece table above — see [Unknown keys are an error](#unknown-keys-are-an-error).
739
+
591
740
  ### I changed my bg color — do I need to regenerate the Android densities too?
592
741
 
593
- Yes. `bgColor` bakes into every Android background layer and the iOS flatten. Re-run:
742
+ Yes. `brand.background` bakes into every Android background layer, every splash canvas, and the iOS flatten. Re-run:
594
743
 
595
744
  ```bash
596
745
  purgetss brand --bg-color "#NEW_COLOR"
597
746
  ```
598
747
 
599
- All 5 Android densities, marketplace artwork, and iOS variants regenerate in one pass.
600
-
601
- ## Full flag reference
602
-
603
- **Project & output**
604
-
605
- | Flag | Purpose |
606
- | --- | --- |
607
- | `--project <path>` | Project root (defaults to cwd). |
608
- | `--dry-run` | Preview what would be generated without writing any files. |
609
- | `--output <dir>` | Stage into `<dir>` instead of writing in place. |
610
- | `-y`, `--yes` | Skip the overwrite confirmation prompt for this invocation. |
611
-
612
- **Visual customization**
613
-
614
- | Flag | Purpose |
615
- | --- | --- |
616
- | `--bg-color <hex>` | Background color for Android adaptive + iOS/marketplace flatten. |
617
- | `--padding <n>` | Shortcut: sets BOTH Android paddings to the same value for one run. Fixed in v7.10.0 — previously only fed `androidAdaptivePadding`, leaving `androidLegacyPadding` at its own config value. |
618
- | `--android-adaptive-padding <n>` | Adaptive icon safe-zone % (default `19`). |
619
- | `--android-legacy-padding <n>` | Legacy `ic_launcher.png` padding % (default `10`). |
620
- | `--ios-padding <n>` | iOS aesthetic padding % (range `2–8`, default `4`). |
621
- | `--feature-graphic-padding <n>` | (v7.10.0) Vertical padding for `MarketplaceArtworkFeature.png` (range `0-40`, default `12`). |
622
-
623
- **Optional asset types**
624
-
625
- | Flag | Purpose |
626
- | --- | --- |
627
- | `--notification` | Also emit `ic_stat_notify.png × 5`. |
628
- | `--splash` | Also emit `splash_icon.png × 5`. |
629
-
630
- **Logo variants & overrides**
631
-
632
- | Flag | Purpose |
633
- | --- | --- |
634
- | `--icon-logo <path>` | Override `purgetss/brand/logo-icon.{svg,png}` for Android launcher icons. |
635
- | `--monochrome-logo <path>` | Override `purgetss/brand/logo-mono.{svg,png}`. |
636
- | `--dark-logo <path>` | Override `purgetss/brand/logo-dark.{svg,png}`. |
637
- | `--dark-bg-color <hex>` | Opaque dark bg for `DefaultIcon-Dark.png` (default: transparent per Apple HIG). |
638
- | `--splash-logo <path>` | Override `purgetss/brand/logo-splash.{svg,png}` for Android 12+ splash artwork. |
639
- | `--tinted-logo <path>` | Override `purgetss/brand/logo-tinted.{svg,png}`. |
640
- | `--feature-logo <path>` | (v7.10.0) Override `purgetss/brand/logo-feature.{svg,png}` for the Google Play Feature Graphic. |
641
- | `--no-dark` | Skip `DefaultIcon-Dark.png`. |
642
- | `--no-tinted` | Skip `DefaultIcon-Tinted.png`. |
643
-
644
- **Legacy cleanup**
645
-
646
- | Flag | Purpose |
647
- | --- | --- |
648
- | `--cleanup-legacy` | Remove obsolete branding artifacts (reads `tiapp.xml` for safety rules). Keeps `default.png` on purpose. |
649
- | `--aggressive` | With `--cleanup-legacy`, also remove `ldpi` density folders. |
748
+ All 5 Android densities, marketplace artwork, splash canvases, and iOS variants regenerate in one pass.
650
749
 
651
- **Diagnostics**
750
+ ## Flag reference
652
751
 
653
- | Flag | Purpose |
654
- | --- | --- |
655
- | `--notes` | Print full `tiapp.xml` snippets + padding tuning guide. |
656
- | `--debug` | Print extra diagnostics. |
657
-
658
- ### Examples
659
-
660
- ```bash
661
- purgetss brand # uses purgetss/brand/logo.svg + config
662
- purgetss brand --bg-color "#0B1326" # override bg color
663
- purgetss brand --icon-logo ./docs/app-icon.svg # dedicated square Android launcher mark
664
- purgetss brand --splash --splash-logo ./docs/splash.svg # custom Android 12+ splash artwork
665
- purgetss brand --feature-logo ./docs/feature.svg # custom Google Play Feature Graphic (v7.10.0)
666
- purgetss brand --feature-graphic-padding 8 # tighter Feature Graphic padding (v7.10.0)
667
- purgetss brand --notification --splash # add notification + splash
668
- purgetss brand --no-tinted # skip iOS 18+ tinted variant
669
- purgetss brand --dry-run # preview without writing
670
- purgetss brand --cleanup-legacy --dry-run # preview legacy cleanup
671
- ```
752
+ Every flag lives in the terse reference: [`brand` command](./cli-commands.md#brand-command) — project & output, `--only`, padding, optional asset types, the `--<piece>-logo` overrides, `--optimize`, appearance, legacy cleanup and diagnostics, with examples.
672
753
 
673
754
  ## Community-Discovered Patterns
674
755
 
675
- - **Titanium SDK wiring lag for iOS 18+ variants.** Titanium SDK currently wires `DefaultIcon-ios.png` into the generated appiconset automatically, but `DefaultIcon-Dark.png` and `DefaultIcon-Tinted.png` are not picked up yet. Until [tidev/titanium-sdk#14122](https://github.com/tidev/titanium-sdk/issues/14122) merges, the practical workaround is: run `ti build -p ios` once, then open `build/iphone/Assets.xcassets/AppIcon.appiconset/` in Xcode and drag the two PNGs into the "Appearance" column of the asset catalog editor. The generated PNGs are already named and sized correctly — no resizing needed.
676
- - **Wordmark logos need a separate launcher mark.** When `logo.svg` is a wide wordmark or vertical lockup, it tends to look cramped inside Android launcher masks. Drop a square `logo-icon.svg` (or set `brand.logos.androidLauncher`) and the launcher icons get the dedicated mark while the rest of the brand set still uses the main logo. Same idea applies to Android 12+ splash with `logo-splash.svg`.
677
- - **Three Android assets, three different jobs.** `ic_launcher*` drives the app icon and the default Android 12+ splash; `splash_icon.png` is generated only when you pass `--splash` and Titanium needs an explicit splash theme to actually use it; `default.png` is the older Titanium Android splash fallback (regenerated since v7.7.0, intentionally kept by `cleanup-legacy`). Knowing which file does what saves a lot of "but the icon didn't change" debugging.
756
+ - **Wordmark logos need a separate launcher mark.** When `logo.svg` is a wide wordmark or vertical lockup, it tends to look cramped inside Android launcher masks. Drop a square `logo-adaptive.svg` (or set `brand.adaptive.logo`) and the launcher icons get the dedicated mark while the rest of the brand set still uses the main logo. The same idea applies to the Android 12+ splash with `logo-splash-icon.svg`, which Android additionally masks into a circle. **Note the v7.13.0 rename:** the file that used to do this was `logo-icon.svg`; that name now feeds `DefaultIcon.png` instead.
757
+ - **Verify with `--dry-run` before a real run on a project with hand-tweaked assets.** A full `brand` run rewrites every branding file, so any icon touched by hand outside PurgeTSS is overwritten. Since v7.13.0 the surgical alternative is `--only <piece>`, which honors `--dry-run` too. Before v7.13.0 the only options were a full run or nothing.
758
+ - **The `[y/N/a]` prompt disappears in non-TTY contexts, including the `alloy.jmk` hook.** That is by design, but it means a `brand` invocation wired into the build pipeline silently overwrites without asking. Keep the project committed before wiring `brand` into `alloy.jmk`.
678
759
 
679
760
  ## See also
680
761
 
681
762
  - [`brand` command reference](./cli-commands.md#brand-command) — terse flag list.
682
763
  - [Multi-Density Images](./multi-density-images.md) — sibling `images` command for UI assets.
764
+ - [Customization Deep Dive](./customization-deep-dive.md) — the full `config.cjs` structure.
765
+ - [Version History](./version-history.md) — what changed in each release.