@maccesar/titools 2.6.1 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -48,7 +48,9 @@ What you get:
48
48
  - ti-pro research agent
49
49
  - Session hook that auto-detects Titanium projects (`tiapp.xml`, Alloy, PurgeTSS)
50
50
  - Slash commands: `/ti-check`, `/ti-new-screen`, `/ti-audit`
51
- - Auto-updates via marketplace
51
+ - Auto-updates via marketplace (opt-in — see note below)
52
+
53
+ > **Note on auto-updates:** Claude Code disables auto-updates by default for third-party marketplaces like this one. To receive new versions automatically, open `/plugin` → **Marketplaces** tab and enable auto-update for `maccesar-titools`. When an update is applied, Claude Code prompts you to run `/reload-plugins` to activate the new code.
52
54
 
53
55
  ### Option B: CLI (Claude Code, Gemini CLI, Codex CLI)
54
56
 
@@ -89,15 +91,15 @@ Why NPM?
89
91
 
90
92
  ### Which option should I use?
91
93
 
92
- | | Plugin (Option A) | CLI (Option B) |
93
- |---|---|---|
94
- | **Claude Code** | Recommended | Supported |
95
- | **Gemini CLI** | Not available | Supported |
96
- | **Codex CLI** | Not available | Supported |
97
- | **Knowledge Index** | Not included | Included (`titools sync`) |
98
- | **Auto-updates** | Via marketplace | Via SessionStart hook |
99
- | **Slash commands** | `/ti-check`, `/ti-new-screen`, `/ti-audit` | Not available |
100
- | **Session hook** | Auto-detects Titanium projects | Auto-update only |
94
+ | | Plugin (Option A) | CLI (Option B) |
95
+ | ------------------- | ------------------------------------------ | ------------------------- |
96
+ | **Claude Code** | Recommended | Supported |
97
+ | **Gemini CLI** | Not available | Supported |
98
+ | **Codex CLI** | Not available | Supported |
99
+ | **Knowledge Index** | Not included | Included (`titools sync`) |
100
+ | **Auto-updates** | Marketplace (opt-in, enable in `/plugin`) | SessionStart hook (daily) |
101
+ | **Slash commands** | `/ti-check`, `/ti-new-screen`, `/ti-audit` | Not available |
102
+ | **Session hook** | Auto-detects Titanium projects | Auto-update only |
101
103
 
102
104
  Use **Option A** if you only use Claude Code. Use **Option B** if you use multiple AI assistants or want the Knowledge Index feature.
103
105
 
@@ -176,17 +178,17 @@ The knowledge index is based on the latest Titanium SDK documentation. If your p
176
178
 
177
179
  ## Skills overview
178
180
 
179
- | Skill | Purpose | Best For |
180
- | ------------ | ------------------------------- | --------------------------------------- |
181
- | alloy-guides | Alloy MVC reference | Models, Views, Controllers, Widgets |
182
- | alloy-howtos | Alloy CLI and debugging | Project setup, CLI commands, errors |
183
- | purgetss | Utility-first styling | UI styling and animations |
184
- | ti-api | Complete API reference | Properties, methods, events, constants |
181
+ | Skill | Purpose | Best For |
182
+ | ------------ | ------------------------------- | ------------------------------------------------------------------------------- |
183
+ | alloy-guides | Alloy MVC reference | Models, Views, Controllers, Widgets |
184
+ | alloy-howtos | Alloy CLI and debugging | Project setup, CLI commands, errors |
185
+ | purgetss | Utility-first styling | UI styling and animations |
186
+ | ti-api | Complete API reference | Properties, methods, events, constants |
185
187
  | ti-branding | Icon and splash generator | App icons, adaptive icons, splash, notifications, store artwork, legacy cleanup |
186
- | ti-expert | Architecture and implementation | Starting point for most tasks |
187
- | ti-guides | SDK fundamentals | Hyperloop, distribution, configuration |
188
- | ti-howtos | Native feature integration | Location, Push, Media, Platform APIs |
189
- | ti-ui | UI/UX patterns | Complex layouts, ListViews, platform UI |
188
+ | ti-expert | Architecture and implementation | Starting point for most tasks |
189
+ | ti-guides | SDK fundamentals | Hyperloop, distribution, configuration |
190
+ | ti-howtos | Native feature integration | Location, Push, Media, Platform APIs |
191
+ | ti-ui | UI/UX patterns | Complex layouts, ListViews, platform UI |
190
192
 
191
193
  Note: `ti-guides`, `ti-howtos`, `ti-ui`, `alloy-guides`, and `alloy-howtos` are based on official Titanium SDK and Alloy documentation. `purgetss` is opinionated in workflow and conventions, but its reference files are audited against the official PurgeTSS documentation for verifiable classes and behavior. `ti-expert` and `purgetss` reflect personal coding conventions (biased toward PurgeTSS).
192
194
 
@@ -200,11 +202,11 @@ In addition to skills, this repository includes sub-agents for Claude Code. Sub-
200
202
 
201
203
  Deep-dive research specialist that preloads all 8 titanium-related documentation skills.
202
204
 
203
- | Aspect | Details |
204
- | ---------------- | ------------------------------------------------- |
205
- | Location | `~/.claude/agents/ti-pro.md` |
206
- | Model | Sonnet (comprehensive analysis) |
207
- | Tools | Read-only (Read, Grep, Glob) |
205
+ | Aspect | Details |
206
+ | ---------------- | ------------------------------------------------------- |
207
+ | Location | `~/.claude/agents/ti-pro.md` |
208
+ | Model | Sonnet (comprehensive analysis) |
209
+ | Tools | Read-only (Read, Grep, Glob) |
208
210
  | Preloaded Skills | All 8 Titanium documentation skills injected at startup |
209
211
 
210
212
  When to use the agent vs skills:
@@ -238,11 +240,11 @@ Key difference:
238
240
 
239
241
  When installed as a plugin, TiTools provides slash commands for common tasks:
240
242
 
241
- | Command | Description |
242
- |---|---|
243
- | `/ti-check` | Verify project setup: SDK version, Alloy structure, PurgeTSS config, common issues |
243
+ | Command | Description |
244
+ | ----------------------- | ----------------------------------------------------------------------------------- |
245
+ | `/ti-check` | Verify project setup: SDK version, Alloy structure, PurgeTSS config, common issues |
244
246
  | `/ti-new-screen <name>` | Create a new Alloy screen (controller + view + style) following project conventions |
245
- | `/ti-audit` | Audit project for anti-patterns, memory leaks, and best practice violations |
247
+ | `/ti-audit` | Audit project for anti-patterns, memory leaks, and best practice violations |
246
248
 
247
249
  These commands automatically invoke the relevant skills:
248
250
  - `/ti-check` reads `tiapp.xml` and reports project health
@@ -660,8 +662,8 @@ titools auto-update --silent # No output (for hooks)
660
662
  ```
661
663
 
662
664
  Options:
663
- | Option | Description |
664
- | -------------- | ---------------------------------- |
665
+ | Option | Description |
666
+ | -------------- | --------------------------------- |
665
667
  | `-s, --silent` | Suppress all output except errors |
666
668
 
667
669
  What it does:
@@ -798,11 +800,11 @@ The TiTools skills include **Community-Discovered Patterns** — verified iOS be
798
800
 
799
801
  When using Large Titles with a ScrollView inside NavigationWindow or TabGroup, three Window properties must be set together. Without all three, the ScrollView content overlaps behind the navigation bar — or the large title renders with a visible delay.
800
802
 
801
- | Class | Property | Purpose |
802
- |---|---|---|
803
- | `auto-adjust-scroll-view-insets` | `autoAdjustScrollViewInsets` | Adjusts ScrollView insets to prevent content overlap |
804
- | `extend-edges-all` | `extendEdges` | Content extends behind bars (blur/translucent effect) |
805
- | `large-title-enabled` | `largeTitleEnabled` | Large title that collapses on scroll |
803
+ | Class | Property | Purpose |
804
+ | -------------------------------- | ---------------------------- | ----------------------------------------------------- |
805
+ | `auto-adjust-scroll-view-insets` | `autoAdjustScrollViewInsets` | Adjusts ScrollView insets to prevent content overlap |
806
+ | `extend-edges-all` | `extendEdges` | Content extends behind bars (blur/translucent effect) |
807
+ | `large-title-enabled` | `largeTitleEnabled` | Large title that collapses on scroll |
806
808
 
807
809
  **Recommended:** set the base properties as global Window defaults in `config.cjs`:
808
810
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maccesar/titools",
3
- "version": "2.6.1",
3
+ "version": "2.7.0",
4
4
  "description": "Titanium SDK skills and agents for AI coding assistants (Claude Code, Gemini CLI, Codex CLI)",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: ti-branding
3
- description: "Generate complete app branding assets for modern Titanium SDK 13.x projects — launcher icons, adaptive icons, splash-screen icons, notification icons, and marketplace artwork — from a single SVG or PNG master. Use when the user asks to brand their app, update the app icon, set up splash screens, prepare store assets, fix generic/placeholder icons, add a notification icon, generate a launcher for Android 8+/13+, replace the TiTools default icon, or anything related to visual identity of a Titanium app (Alloy or Classic). Auto-detects Alloy vs Classic layout. Outputs iOS `DefaultIcon-ios.png` (flattened), Android adaptive triplet (foreground + background + monochrome) across 5 densities, Android legacy launcher × 5, marketplace artwork (1024² + 512²), and optionally notification icons and Android 12+ splash icons. Targets SDK 13.0–13.2 minimums without legacy cruft (post-Xcode 14 iOS minimums, no 9-patch splash, no pre-adaptive Android icons). Delegates raster work to ImageMagick + librsvg (system tools) — zero npm/pip dependencies. Invoke this skill whenever the user mentions icons, splash screens, branding, launcher, adaptive icon, mipmap, appiconset, DefaultIcon, iTunesConnect, Play Store artwork, or any visual asset for a Titanium app."
4
- argument-hint: "[master-path] [--bg-color <hex>] [--padding <pct>] [--with-notification] [--with-splash-icon] [--cleanup-legacy] [--aggressive] [--dry-run]"
3
+ description: "Generate complete app branding assets for modern Titanium SDK 13.x projects — launcher icons, adaptive icons, splash-screen icons, notification icons, and marketplace artwork — from a single SVG or PNG master. Use when the user asks to brand their app, update the app icon, set up splash screens, prepare store assets, fix generic/placeholder icons, add a notification icon, generate a launcher for Android 8+/13+, replace the TiTools default icon, or anything related to visual identity of a Titanium app (Alloy or Classic). Auto-detects Alloy vs Classic layout. Outputs iOS `DefaultIcon.png` (alpha-preserved source) + `DefaultIcon-ios.png` (alpha-flattened for Apple), Android adaptive triplet (foreground + background + monochrome) across 5 densities, Android legacy launcher × 5, marketplace artwork (1024² + 512², alpha-preserved), and optionally notification icons and Android 12+ splash icons. Targets SDK 13.0–13.2 minimums without legacy cruft (post-Xcode 14 iOS minimums, no 9-patch splash, no pre-adaptive Android icons). Delegates raster work to ImageMagick + librsvg (system tools) — zero npm/pip dependencies. Invoke this skill whenever the user mentions icons, splash screens, branding, launcher, adaptive icon, mipmap, appiconset, DefaultIcon, iTunesConnect, Play Store artwork, or any visual asset for a Titanium app."
4
+ argument-hint: "[master-path] [--bg-color <hex>] [--padding <pct>] [--with-notification] [--with-splash-icon] [--cleanup-legacy] [--aggressive] [--in-place] [--monochrome-master <path>] [--notes] [--dry-run]"
5
5
  allowed-tools: Read, Glob, Bash, Write, Edit
6
6
  ---
7
7
 
@@ -29,9 +29,10 @@ Every invocation produces (in a staging directory, reviewed before copy):
29
29
 
30
30
  | Asset | Path in project | Why |
31
31
  |---|---|---|
32
- | `DefaultIcon-ios.png` (1024×1024, no alpha) | Project root | iOS SDK generates the full appiconset from this at build time. Flattened because Apple rejects alpha channels. |
33
- | `iTunesConnect.png` (1024×1024, no alpha) | Project root | App Store submission requirement. |
34
- | `MarketplaceArtwork.png` (512×512, no alpha) | Project root | Google Play submission requirement. |
32
+ | `DefaultIcon.png` (1024×1024, alpha preserved) | Project root | Universal source Titanium's `ti create` template ships this with alpha. Used for Android / Alloy references and as the alpha-intact source of truth. |
33
+ | `DefaultIcon-ios.png` (1024×1024, no alpha) | Project root | iOS SDK generates the full appiconset from this at build time. Flattened because Apple rejects alpha channels on App Store icon uploads. |
34
+ | `iTunesConnect.png` (1024×1024, alpha preserved) | Project root | App Store artwork template. Matches `ti create` default — alpha preserved so the developer can composite over any background before upload. |
35
+ | `MarketplaceArtwork.png` (512×512, alpha preserved) | Project root | Google Play feature graphic template. Matches `ti create` default — alpha preserved. |
35
36
  | `ic_launcher_foreground.png` × 5 densities | `app/platform/android/res/mipmap-{m,h,x,xx,xxx}hdpi/` | Adaptive icon foreground layer. Logo centered in 108dp canvas with configurable padding (default 22% ≈ 66dp safe-zone). |
36
37
  | `ic_launcher_background.png` × 5 densities | idem | Adaptive icon background layer — solid `--bg-color`. |
37
38
  | `ic_launcher_monochrome.png` × 5 densities | idem | Android 13+ themed icons (all non-transparent pixels → white). |
@@ -68,12 +69,15 @@ Options:
68
69
  | Flag | Default | Purpose |
69
70
  |---|---|---|
70
71
  | `--bg-color <hex>` | `#FFFFFF` | Solid color for adaptive background layer. Also used for iOS alpha flatten. |
71
- | `--padding <pct>` | `22` | Safe-zone padding per side (Android official minimum is 19.44% — 22% adds a small buffer against launcher masking). Range 0–40. |
72
+ | `--padding <pct>` | `20` | Safe-zone padding per side. Material spec floor is 19.44% — default of 20 gives a tiny buffer while keeping the logo visibly prominent across launcher masks. Range 0–40. |
72
73
  | `--with-notification` | off | Emit `ic_stat_notify.png` × 5 densities. |
73
74
  | `--with-splash-icon` | off | Emit `splash_icon.png` × 5 densities (Android 12+ SplashScreen API). |
74
75
  | `--cleanup-legacy` | off | After generating (or standalone), scan the project for legacy branding artifacts — iOS launch image PNGs, Android `default.png`, `appicon.png`, `res-long-*/res-notlong-*`, etc. — and remove them. Context-aware: reads `tiapp.xml` to decide what's safe. See `references/cleanup-legacy.md`. |
75
76
  | `--aggressive` | off | With `--cleanup-legacy`, also remove `ldpi` density folders (<1% of active devices globally in 2026). Off by default to be conservative. |
76
77
  | `--output <dir>` | `<project>/.ti-branding/` | Staging directory. Review before copying to `app/`. |
78
+ | `--in-place` | off | Skip the staging directory and write files directly into the project root. **Overwrites existing icons.** Ideal right after `ti create` when you want to replace the default Titanium/Alloy icons in one command. `--output` takes precedence if both are passed. |
79
+ | `--monochrome-master <path>` | — | Optional dedicated silhouette master for `ic_launcher_monochrome.png` + `ic_stat_notify.png`. Use this when your colored logo has multi-color detail that would collapse into a featureless blob when naively whitened (e.g. a painter's palette with 4 color dots — provide a monochrome variant with cutout holes instead so the detail survives in themed icons and notification status bar). Falls back to whitening the main master when not provided. |
80
+ | `--notes` | off | Print the full tiapp.xml snippets (iOS launch storyboard, Android launcher wiring, Android 12+ splash theme with Option A/B, FCM notification tint) and the padding tuning guide. Default output is a compact summary (~15 lines). |
77
81
  | `--dry-run` | off | Print what would be generated/removed without writing or deleting. Works with both generation and `--cleanup-legacy`. |
78
82
 
79
83
  ## Safety reminders
@@ -13,8 +13,9 @@
13
13
  gen_android_adaptive() {
14
14
  local master="$1"
15
15
  local bg="$2"
16
- local padding="$3" # percent per side
17
- local res_root="$4" # e.g. /path/app/platform/android/res
16
+ local padding="$3" # percent per side
17
+ local res_root="$4" # e.g. /path/app/platform/android/res
18
+ local mono_master="${5:-}" # optional dedicated silhouette master
18
19
 
19
20
  local densities=(mdpi hdpi xhdpi xxhdpi xxxhdpi)
20
21
  local sizes=(108 162 216 324 432)
@@ -38,11 +39,22 @@ gen_android_adaptive() {
38
39
  -strip \
39
40
  "$dir/ic_launcher_background.png"
40
41
 
41
- # Monochrome: foreground tinted to pure white, alpha preserved
42
- magick "$dir/ic_launcher_foreground.png" \
43
- -channel RGB -fill white -colorize 100 +channel \
44
- -strip \
45
- "$dir/ic_launcher_monochrome.png"
42
+ # Monochrome: white silhouette with alpha. If a dedicated mono master was
43
+ # provided, render it fresh with the same padding. Otherwise derive from
44
+ # the colored foreground (naive whitening may flatten complex logos).
45
+ if [[ -n "$mono_master" ]]; then
46
+ magick "$mono_master" \
47
+ -resize "${inner}x${inner}" \
48
+ -background none -gravity center -extent "${size}x${size}" \
49
+ -channel RGB -fill white -colorize 100 +channel \
50
+ -strip \
51
+ "$dir/ic_launcher_monochrome.png"
52
+ else
53
+ magick "$dir/ic_launcher_foreground.png" \
54
+ -channel RGB -fill white -colorize 100 +channel \
55
+ -strip \
56
+ "$dir/ic_launcher_monochrome.png"
57
+ fi
46
58
  done
47
59
 
48
60
  # anydpi-v26 binder directory (XML copied by caller from assets/ic_launcher.xml)
@@ -1,12 +1,17 @@
1
1
  #!/usr/bin/env bash
2
- # gen-ios.sh — produce DefaultIcon-ios.png (1024×1024, alpha flattened over bg-color).
2
+ # gen-ios.sh — produce Titanium's two root-level iOS/Android icons:
3
3
  #
4
- # Unlike Android adaptive icons, iOS app icons have no "safe zone" — the launcher
5
- # doesn't mask them. What you see is what ships. So padding is purely visual
6
- # breathing room around the logo. Default 8% per side (logo fills 84% of the
7
- # canvas), which matches Apple's HIG aesthetic for branded wordmarks.
4
+ # DefaultIcon.png 1024×1024, alpha preserved (universal / Android source)
5
+ # DefaultIcon-ios.png 1024×1024, alpha flattened over bg-color (iOS)
8
6
  #
9
- # iOS rejects icons with alpha, so the final output is flattened over bg-color.
7
+ # This matches what `titanium` / `alloy new` ships out of the box: a fresh
8
+ # Alloy project contains both files in the project root, with DefaultIcon.png
9
+ # keeping transparency and DefaultIcon-ios.png flattened (Apple rejects alpha
10
+ # on App Store icon uploads).
11
+ #
12
+ # Padding is purely visual breathing room — iOS app icons have no launcher
13
+ # mask. Default 8% per side (logo fills 84% of the canvas), which matches
14
+ # Apple's HIG aesthetic for branded wordmarks.
10
15
 
11
16
  gen_ios() {
12
17
  local tight="$1" # tight master (aspect-preserved, transparent bg)
@@ -19,6 +24,16 @@ gen_ios() {
19
24
  local size=1024
20
25
  local inner=$(( size * (100 - 2 * padding) / 100 ))
21
26
 
27
+ # DefaultIcon.png — alpha preserved (matches `ti create` default for the
28
+ # universal icon source; Alloy/Android use this directly).
29
+ magick "$tight" \
30
+ -resize "${inner}x${inner}" \
31
+ -background none -gravity center -extent "${size}x${size}" \
32
+ -strip \
33
+ "$out_root/DefaultIcon.png"
34
+
35
+ # DefaultIcon-ios.png — flattened over bg-color. This is the file iOS
36
+ # actually ships; Apple rejects alpha on App Store icon uploads.
22
37
  magick "$tight" \
23
38
  -resize "${inner}x${inner}" \
24
39
  -background none -gravity center -extent "${size}x${size}" \
@@ -26,5 +41,6 @@ gen_ios() {
26
41
  -strip \
27
42
  "$out_root/DefaultIcon-ios.png"
28
43
 
44
+ log_ok "DefaultIcon.png (1024×1024, ${padding}% padding, alpha preserved)"
29
45
  log_ok "DefaultIcon-ios.png (1024×1024, ${padding}% padding, alpha flattened over $bg)"
30
46
  }
@@ -1,28 +1,43 @@
1
1
  #!/usr/bin/env bash
2
2
  # gen-marketplace.sh — store-submission artwork.
3
3
  #
4
- # iTunesConnect.png: 1024×1024, no alpha (App Store)
5
- # MarketplaceArtwork.png: 512×512, no alpha (Google Play)
4
+ # iTunesConnect.png 1024×1024 (App Store)
5
+ # MarketplaceArtwork.png 512×512 (Google Play)
6
6
  #
7
- # Uses the same --ios-padding as the iOS master (default 8%). Store assets are
8
- # scrutinized at large sizes and need visual breathing room — no safe-zone to
9
- # comply with, but aesthetically they should match the DefaultIcon-ios look.
7
+ # Alpha handling depends on whether --bg-color was EXPLICITLY provided
8
+ # (tracked via the 5th argument, `flatten`):
9
+ # - flatten=0 (no --bg-color) alpha preserved, matches `ti create` default
10
+ # - flatten=1 (--bg-color X) → alpha flattened on bg-color. Prevents the
11
+ # dark-mode-muddy-icon issue on Play Store
12
+ # and macOS App Store when the master has
13
+ # significant transparent areas.
14
+ #
15
+ # Uses the same --ios-padding as the iOS master (default 8%) so the three
16
+ # square icons share the same visual weight.
10
17
 
11
18
  gen_marketplace() {
12
19
  local tight="$1"
13
20
  local bg="$2"
14
21
  local padding="$3" # ios-padding percent per side
15
22
  local out_root="$4"
23
+ local flatten="${5:-0}" # 1 when --bg-color was explicitly provided
16
24
 
17
25
  mkdir -p "$out_root"
18
26
 
27
+ local flatten_args=()
28
+ local alpha_label="alpha"
29
+ if [[ "$flatten" == "1" ]]; then
30
+ flatten_args=(-background "$bg" -alpha remove -alpha off)
31
+ alpha_label="flat on $bg"
32
+ fi
33
+
19
34
  # App Store (1024²)
20
35
  local size_ios=1024
21
36
  local inner_ios=$(( size_ios * (100 - 2 * padding) / 100 ))
22
37
  magick "$tight" \
23
38
  -resize "${inner_ios}x${inner_ios}" \
24
39
  -background none -gravity center -extent "${size_ios}x${size_ios}" \
25
- -background "$bg" -alpha remove -alpha off \
40
+ "${flatten_args[@]}" \
26
41
  -strip \
27
42
  "$out_root/iTunesConnect.png"
28
43
 
@@ -32,9 +47,9 @@ gen_marketplace() {
32
47
  magick "$tight" \
33
48
  -resize "${inner_play}x${inner_play}" \
34
49
  -background none -gravity center -extent "${size_play}x${size_play}" \
35
- -background "$bg" -alpha remove -alpha off \
50
+ "${flatten_args[@]}" \
36
51
  -strip \
37
52
  "$out_root/MarketplaceArtwork.png"
38
53
 
39
- log_ok "Marketplace artwork: iTunesConnect.png (1024², ${padding}% padding), MarketplaceArtwork.png (512², ${padding}% padding)"
54
+ log_ok "Marketplace artwork: iTunesConnect.png (1024², ${padding}% padding, ${alpha_label}), MarketplaceArtwork.png (512², ${padding}% padding, ${alpha_label})"
40
55
  }
@@ -4,11 +4,21 @@
4
4
  # all non-transparent pixels become white and color information is discarded.
5
5
  #
6
6
  # Sizes per Android spec: mdpi=24px, hdpi=36, xhdpi=48, xxhdpi=72, xxxhdpi=96.
7
+ #
8
+ # Notification icons are NOT masked by the launcher, so they render as drawn.
9
+ # We trim any transparent padding baked into the master first, then scale the
10
+ # logo to fill the canvas edge-to-edge on its longer axis (with a 1px
11
+ # anti-aliasing margin per side). This matches what first-party Android apps
12
+ # ship (Gmail, Slack, Chrome) — the logo reaches the edge instead of sitting
13
+ # in a visibly padded box in the status bar.
14
+ #
15
+ # Material's 22dp-inside-24dp "live area" is a conservative guideline; in
16
+ # practice notification icons benefit from going closer to the edge because
17
+ # the status bar is already tiny.
7
18
 
8
19
  gen_notification() {
9
20
  local master="$1"
10
- local padding="$2"
11
- local res_root="$3"
21
+ local res_root="$2"
12
22
 
13
23
  local densities=(mdpi hdpi xhdpi xxhdpi xxxhdpi)
14
24
  local sizes=(24 36 48 72 96)
@@ -16,13 +26,15 @@ gen_notification() {
16
26
  for i in "${!densities[@]}"; do
17
27
  local d="${densities[$i]}"
18
28
  local size="${sizes[$i]}"
19
- # Notification icons get pretty heavily masked in the status bar, so use
20
- # slightly tighter padding than the full safe-zone.
21
- local inner=$(( size * (100 - 2 * padding) / 100 ))
29
+ # Edge-to-edge with a 1px anti-aliasing margin per side.
30
+ local inner=$(( size - 2 ))
31
+ (( inner < 1 )) && inner=1
22
32
  local dir="$res_root/drawable-$d"
23
33
  mkdir -p "$dir"
24
34
 
35
+ # Trim baked-in transparent padding from the master, then scale to inner.
25
36
  magick "$master" \
37
+ -trim +repage \
26
38
  -resize "${inner}x${inner}" \
27
39
  -channel RGB -fill white -colorize 100 +channel \
28
40
  -background none -gravity center -extent "${size}x${size}" \
@@ -30,5 +42,5 @@ gen_notification() {
30
42
  "$dir/ic_stat_notify.png"
31
43
  done
32
44
 
33
- log_ok "Notification icons (white+alpha) × 5 densities"
45
+ log_ok "Notification icons (white+alpha, edge-to-edge) × 5 densities"
34
46
  }
@@ -34,15 +34,21 @@ source "$LIB_DIR/gen-splash-icon.sh"
34
34
  # -----------------------------------------------------------------------------
35
35
 
36
36
  MASTER=""
37
+ MONOCHROME_MASTER=""
37
38
  BG_COLOR="#FFFFFF"
38
- PADDING=22
39
+ BG_COLOR_EXPLICIT=0
40
+ PADDING=20
39
41
  IOS_PADDING=8
40
42
  WITH_NOTIFICATION=0
41
43
  WITH_SPLASH_ICON=0
42
44
  CLEANUP_LEGACY=0
43
45
  AGGRESSIVE=0
44
46
  OUTPUT=""
47
+ OUTPUT_EXPLICIT=0
45
48
  DRY_RUN=0
49
+ IN_PLACE=0
50
+ IS_IN_PLACE=0
51
+ NOTES=0
46
52
  PROJECT_ROOT="$(pwd)"
47
53
 
48
54
  # -----------------------------------------------------------------------------
@@ -82,8 +88,9 @@ ARGUMENTS:
82
88
  OPTIONS:
83
89
  --bg-color <hex> Background color for adaptive layer + iOS alpha flatten.
84
90
  Default: #FFFFFF
85
- --padding <pct> Android safe-zone padding per side (0–40). Default: 22
86
- (Android official minimum is 19.44%.)
91
+ --padding <pct> Android safe-zone padding per side (0–40). Default: 20
92
+ (Material spec floor is 19.44%; default 20 gives a
93
+ tiny buffer while keeping the logo visibly prominent.)
87
94
  --ios-padding <pct> iOS/marketplace aesthetic padding per side (0–40).
88
95
  Default: 8. iOS icons have no launcher mask — this
89
96
  is purely breathing room around the logo.
@@ -96,6 +103,17 @@ OPTIONS:
96
103
  (<1% global market in 2026). Off by default.
97
104
  --output <dir> Staging directory. Default: <project>/.ti-branding
98
105
  --project <dir> Project root (default: current working directory).
106
+ --in-place Skip the staging directory — write files directly into
107
+ the project root. OVERWRITES existing icons. --output
108
+ takes precedence when both are passed.
109
+ --monochrome-master <p> Optional dedicated silhouette master for
110
+ ic_launcher_monochrome.png + ic_stat_notify.png.
111
+ Use when the colored logo has multi-color detail
112
+ that would collapse into a featureless blob when
113
+ naively whitened. Falls back to main master if omitted.
114
+ --notes Print full tiapp.xml snippets + padding tuning guide
115
+ after the run. Default is a compact summary — use this
116
+ when you need the config snippets to paste into tiapp.xml.
99
117
  --dry-run Print what would be generated without writing files.
100
118
  -h, --help Show this help.
101
119
 
@@ -113,15 +131,18 @@ EOF
113
131
  while [[ $# -gt 0 ]]; do
114
132
  case "$1" in
115
133
  -h|--help) usage; exit 0 ;;
116
- --bg-color) BG_COLOR="$2"; shift 2 ;;
134
+ --bg-color) BG_COLOR="$2"; BG_COLOR_EXPLICIT=1; shift 2 ;;
117
135
  --padding) PADDING="$2"; shift 2 ;;
118
136
  --ios-padding) IOS_PADDING="$2"; shift 2 ;;
119
137
  --with-notification) WITH_NOTIFICATION=1; shift ;;
120
138
  --with-splash-icon) WITH_SPLASH_ICON=1; shift ;;
121
139
  --cleanup-legacy) CLEANUP_LEGACY=1; shift ;;
122
140
  --aggressive) AGGRESSIVE=1; shift ;;
123
- --output) OUTPUT="$2"; shift 2 ;;
141
+ --output) OUTPUT="$2"; OUTPUT_EXPLICIT=1; shift 2 ;;
124
142
  --project) PROJECT_ROOT="$2"; shift 2 ;;
143
+ --in-place) IN_PLACE=1; shift ;;
144
+ --monochrome-master) MONOCHROME_MASTER="$2"; shift 2 ;;
145
+ --notes) NOTES=1; shift ;;
125
146
  --dry-run) DRY_RUN=1; shift ;;
126
147
  --) shift; break ;;
127
148
  -*) log_err "Unknown option: $1"; usage >&2; exit 2 ;;
@@ -172,6 +193,14 @@ if [[ ! "$BG_COLOR" =~ ^#[0-9A-Fa-f]{6}$ ]]; then
172
193
  exit 2
173
194
  fi
174
195
 
196
+ # Resolve staging dir:
197
+ # explicit --output wins over --in-place (avoids ambiguity)
198
+ # --in-place (no --output) → write directly into the project root
199
+ # neither → default to .ti-branding/ inside the project
200
+ if [[ $IN_PLACE -eq 1 && $OUTPUT_EXPLICIT -eq 0 ]]; then
201
+ OUTPUT="$PROJECT_ROOT"
202
+ IS_IN_PLACE=1
203
+ fi
175
204
  OUTPUT="${OUTPUT:-$PROJECT_ROOT/.ti-branding}"
176
205
 
177
206
  # -----------------------------------------------------------------------------
@@ -191,11 +220,16 @@ if [[ -n "$MASTER" ]]; then
191
220
  log_info "Master: $MASTER"
192
221
  log_info "Background: $BG_COLOR"
193
222
  log_info "Padding: Android ${PADDING}% / iOS ${IOS_PADDING}% per side"
194
- log_info "Staging: $OUTPUT"
223
+ if [[ $IS_IN_PLACE -eq 1 ]]; then
224
+ log_info "Writing IN PLACE to: $PROJECT_ROOT"
225
+ else
226
+ log_info "Staging: $OUTPUT"
227
+ fi
195
228
  fi
196
229
  [[ $WITH_NOTIFICATION -eq 1 ]] && log_info "Extras: + notification icons"
197
230
  [[ $WITH_SPLASH_ICON -eq 1 ]] && log_info "Extras: + Android 12+ splash icon"
198
231
  [[ $CLEANUP_LEGACY -eq 1 ]] && log_info "Cleanup: --cleanup-legacy$([[ $AGGRESSIVE -eq 1 ]] && echo " --aggressive")"
232
+ [[ $IS_IN_PLACE -eq 1 && $DRY_RUN -eq 0 ]] && log_warn "⚠ --in-place mode: files in your project will be OVERWRITTEN. Commit first if you want a rollback."
199
233
  [[ $DRY_RUN -eq 1 ]] && log_warn "DRY RUN — no files will be written"
200
234
 
201
235
  # -----------------------------------------------------------------------------
@@ -227,13 +261,27 @@ log_step "3/6 Preparing masters (square for iOS, tight for Android)"
227
261
  PREPARED_BASE="$OUTPUT/_master"
228
262
  MASTER_SQUARE="${PREPARED_BASE}_square.png"
229
263
  MASTER_TIGHT="${PREPARED_BASE}_tight.png"
264
+ MONO_TIGHT=""
230
265
  if [[ $DRY_RUN -eq 0 ]]; then
231
266
  mkdir -p "$OUTPUT"
232
267
  prepare_master "$MASTER" "$PREPARED_BASE"
233
268
  log_ok "Square master: $MASTER_SQUARE"
234
269
  log_ok "Tight master: $MASTER_TIGHT"
270
+
271
+ # Optional: prepare a dedicated monochrome master if provided.
272
+ if [[ -n "$MONOCHROME_MASTER" ]]; then
273
+ if [[ ! -f "$MONOCHROME_MASTER" ]]; then
274
+ log_err "Monochrome master not found: $MONOCHROME_MASTER"
275
+ exit 2
276
+ fi
277
+ local_mono_base="$OUTPUT/_master_mono"
278
+ prepare_master "$MONOCHROME_MASTER" "$local_mono_base"
279
+ MONO_TIGHT="${local_mono_base}_tight.png"
280
+ log_ok "Monochrome master (for ic_launcher_monochrome + ic_stat_notify): $MONO_TIGHT"
281
+ fi
235
282
  else
236
283
  log_info "[dry-run] would prepare: $MASTER_SQUARE + $MASTER_TIGHT"
284
+ [[ -n "$MONOCHROME_MASTER" ]] && log_info "[dry-run] would prepare monochrome master from: $MONOCHROME_MASTER"
237
285
  fi
238
286
 
239
287
  # Resolve target paths based on project layout
@@ -247,14 +295,14 @@ ROOT_STAGING="$OUTPUT"
247
295
  log_step "4/6 Generating iOS master + marketplace artwork (square)"
248
296
  if [[ $DRY_RUN -eq 0 ]]; then
249
297
  gen_ios "$MASTER_TIGHT" "$BG_COLOR" "$IOS_PADDING" "$ROOT_STAGING"
250
- gen_marketplace "$MASTER_TIGHT" "$BG_COLOR" "$IOS_PADDING" "$ROOT_STAGING"
298
+ gen_marketplace "$MASTER_TIGHT" "$BG_COLOR" "$IOS_PADDING" "$ROOT_STAGING" "$BG_COLOR_EXPLICIT"
251
299
  else
252
- log_info "[dry-run] would emit DefaultIcon-ios.png, iTunesConnect.png, MarketplaceArtwork.png"
300
+ log_info "[dry-run] would emit DefaultIcon.png, DefaultIcon-ios.png, iTunesConnect.png, MarketplaceArtwork.png"
253
301
  fi
254
302
 
255
303
  log_step "5/6 Generating Android adaptive + legacy launcher icons (tight)"
256
304
  if [[ $DRY_RUN -eq 0 ]]; then
257
- gen_android_adaptive "$MASTER_TIGHT" "$BG_COLOR" "$PADDING" "$ANDROID_RES"
305
+ gen_android_adaptive "$MASTER_TIGHT" "$BG_COLOR" "$PADDING" "$ANDROID_RES" "$MONO_TIGHT"
258
306
  gen_android_legacy "$MASTER_TIGHT" "$BG_COLOR" "$PADDING" "$ANDROID_RES"
259
307
  cp "$ASSETS_DIR/ic_launcher.xml" "$ANDROID_RES/mipmap-anydpi-v26/ic_launcher.xml"
260
308
  log_ok "Adaptive icon XML bind: $ANDROID_RES/mipmap-anydpi-v26/ic_launcher.xml"
@@ -265,7 +313,8 @@ fi
265
313
  if [[ $WITH_NOTIFICATION -eq 1 || $WITH_SPLASH_ICON -eq 1 ]]; then
266
314
  log_step "6/6 Generating optional extras"
267
315
  if [[ $WITH_NOTIFICATION -eq 1 && $DRY_RUN -eq 0 ]]; then
268
- gen_notification "$MASTER_TIGHT" "$PADDING" "$ANDROID_RES"
316
+ # Use monochrome master if provided, else fall back to main (whitened by the generator).
317
+ gen_notification "${MONO_TIGHT:-$MASTER_TIGHT}" "$ANDROID_RES"
269
318
  elif [[ $WITH_NOTIFICATION -eq 1 ]]; then
270
319
  log_info "[dry-run] would emit ic_stat_notify.png × 5 densities"
271
320
  fi
@@ -285,11 +334,62 @@ fi
285
334
 
286
335
  log_step "Done"
287
336
  if [[ $DRY_RUN -eq 0 ]]; then
288
- log_ok "All assets staged at: $OUTPUT"
337
+ # In --in-place mode, intermediate master files (_master_square.png /
338
+ # _master_tight.png) land directly in the project root alongside the real
339
+ # branded assets. Clean them up so the user is only left with the 4 icons.
340
+ if [[ $IS_IN_PLACE -eq 1 ]]; then
341
+ rm -f "$OUTPUT/_master_square.png" "$OUTPUT/_master_tight.png"
342
+ log_ok "All assets written IN PLACE at: $PROJECT_ROOT"
343
+ else
344
+ log_ok "All assets staged at: $OUTPUT"
345
+ fi
289
346
 
290
347
  # ---------------------------------------------------------------------------
291
- # Post-generation notes (aclaraciones) help the user understand what was
292
- # made, how to adjust, and what tiapp.xml glue is still needed.
348
+ # Post-generation outputcompact summary by default, full notes with --notes.
349
+ # ---------------------------------------------------------------------------
350
+
351
+ if [[ $NOTES -eq 0 ]]; then
352
+ # Compact summary — only config/destination + 3-line next steps + hint.
353
+ echo
354
+ echo "${C_BOLD}Summary${C_RESET}"
355
+ echo " Background: ${BG_COLOR} Padding: Android ${PADDING}% / iOS ${IOS_PADDING}%"
356
+ if [[ $IS_IN_PLACE -eq 1 ]]; then
357
+ echo " Written in place to: $PROJECT_ROOT"
358
+ else
359
+ echo " Staged at: $OUTPUT"
360
+ fi
361
+ echo
362
+ echo "${C_BOLD}Next steps${C_RESET}"
363
+ if [[ $IS_IN_PLACE -eq 1 ]]; then
364
+ echo " • Preview the new icons in ${C_YELLOW}Preview.app${C_RESET}."
365
+ echo " • If something looks wrong: ${C_DIM}git checkout -- .${C_RESET}"
366
+ echo " • Rebuild: ${C_DIM}ti clean && ti build -p android -T emulator${C_RESET}"
367
+ else
368
+ case "$PROJECT_TYPE" in
369
+ alloy)
370
+ echo " • Preview in ${C_YELLOW}Preview.app${C_RESET}, then copy to project:"
371
+ echo " ${C_DIM}cp $OUTPUT/{DefaultIcon,DefaultIcon-ios,iTunesConnect,MarketplaceArtwork}.png $PROJECT_ROOT/${C_RESET}"
372
+ echo " ${C_DIM}cp -R $OUTPUT/app/platform/android/res/. $PROJECT_ROOT/app/platform/android/res/${C_RESET}"
373
+ ;;
374
+ classic)
375
+ echo " • Preview in ${C_YELLOW}Preview.app${C_RESET}, then copy to project:"
376
+ echo " ${C_DIM}cp $OUTPUT/{DefaultIcon,DefaultIcon-ios,iTunesConnect,MarketplaceArtwork}.png $PROJECT_ROOT/${C_RESET}"
377
+ echo " ${C_DIM}cp -R $OUTPUT/platform/android/res/. $PROJECT_ROOT/platform/android/res/${C_RESET}"
378
+ ;;
379
+ *)
380
+ echo " • Review ${C_YELLOW}$OUTPUT/${C_RESET} and copy files to their final paths manually."
381
+ ;;
382
+ esac
383
+ echo " • Cleanup staging: ${C_DIM}rm -rf $OUTPUT${C_RESET}"
384
+ fi
385
+ echo
386
+ log_info "Pass ${C_BOLD}--notes${C_RESET} to print tiapp.xml snippets + padding tuning guide."
387
+ echo
388
+ exit 0
389
+ fi
390
+
391
+ # ---------------------------------------------------------------------------
392
+ # Full notes (with --notes) — tiapp.xml snippets, padding tuning, etc.
293
393
  # ---------------------------------------------------------------------------
294
394
 
295
395
  echo
@@ -438,24 +538,31 @@ EOF
438
538
  echo
439
539
  echo "${C_BOLD}Next steps${C_RESET}"
440
540
  echo
441
- echo " 1. Preview in ${C_YELLOW}Preview.app${C_RESET} (never use ${C_DIM}open${C_RESET} without -a triggers heavy editors)"
541
+ if [[ $IS_IN_PLACE -eq 1 ]]; then
542
+ echo " 1. Preview the new icons in ${C_YELLOW}Preview.app${C_RESET} — the files in your project"
543
+ echo " were overwritten directly (no staging directory was used)."
544
+ echo " 2. If something looks wrong, restore from git: ${C_DIM}git checkout -- .${C_RESET}"
545
+ echo " 3. Rebuild: ${C_DIM}ti clean && ti build -p android -T emulator${C_RESET}"
546
+ else
547
+ echo " 1. Preview in ${C_YELLOW}Preview.app${C_RESET} (never use ${C_DIM}open${C_RESET} without -a — triggers heavy editors)"
442
548
  case "$PROJECT_TYPE" in
443
549
  alloy)
444
550
  echo " 2. If good, copy to project:"
445
- echo " cp $OUTPUT/DefaultIcon-ios.png $OUTPUT/iTunesConnect.png $OUTPUT/MarketplaceArtwork.png $PROJECT_ROOT/"
551
+ echo " cp $OUTPUT/DefaultIcon.png $OUTPUT/DefaultIcon-ios.png $OUTPUT/iTunesConnect.png $OUTPUT/MarketplaceArtwork.png $PROJECT_ROOT/"
446
552
  echo " cp -R $OUTPUT/app/platform/android/res/. $PROJECT_ROOT/app/platform/android/res/"
447
553
  ;;
448
554
  classic)
449
555
  echo " 2. If good, copy to project:"
450
- echo " cp $OUTPUT/DefaultIcon-ios.png $OUTPUT/iTunesConnect.png $OUTPUT/MarketplaceArtwork.png $PROJECT_ROOT/"
556
+ echo " cp $OUTPUT/DefaultIcon.png $OUTPUT/DefaultIcon-ios.png $OUTPUT/iTunesConnect.png $OUTPUT/MarketplaceArtwork.png $PROJECT_ROOT/"
451
557
  echo " cp -R $OUTPUT/platform/android/res/. $PROJECT_ROOT/platform/android/res/"
452
558
  ;;
453
559
  *)
454
560
  echo " 2. Review staging dir and copy files to their final paths manually."
455
561
  ;;
456
562
  esac
457
- echo " 3. Cleanup staging: rm -rf $OUTPUT"
458
- echo " 4. Rebuild: ti clean && ti build -p android -T emulator"
563
+ echo " 3. Cleanup staging: rm -rf $OUTPUT"
564
+ echo " 4. Rebuild: ${C_DIM}ti clean && ti build -p android -T emulator${C_RESET}"
565
+ fi
459
566
  echo
460
567
  else
461
568
  log_info "Dry-run complete. Re-run without --dry-run to write files."