@maccesar/titools 4.18.0 → 4.20.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/package.json +1 -1
- package/skills/purgetss/SKILL.md +9 -4
- package/skills/purgetss/references/animation-advanced.md +93 -226
- package/skills/purgetss/references/animation-system.md +26 -17
- package/skills/purgetss/references/app-branding.md +31 -8
- package/skills/purgetss/references/appearance-module.md +63 -9
- package/skills/purgetss/references/class-categories.md +2 -2
- package/skills/purgetss/references/class-index.md +1 -1
- package/skills/purgetss/references/classic-projects.md +24 -5
- package/skills/purgetss/references/cli-commands.md +59 -30
- package/skills/purgetss/references/custom-fonts.md +29 -2
- package/skills/purgetss/references/customization-deep-dive.md +20 -10
- package/skills/purgetss/references/dynamic-component-creation.md +1 -1
- package/skills/purgetss/references/icon-fonts.md +3 -1
- package/skills/purgetss/references/installation-setup.md +5 -1
- package/skills/purgetss/references/multi-density-images.md +35 -5
- package/skills/purgetss/references/purgetss-ui-classic.md +341 -0
- package/skills/purgetss/references/version-history.md +17 -0
- package/skills/ti-game/SKILL.md +5 -2
- package/skills/ti-game/references/api.md +38 -4
- package/skills/ti-game/references/debugging-performance.md +1 -1
- package/skills/ti-game/references/examples.md +6 -5
- package/skills/ti-game/references/patterns.md +1 -1
- package/skills/ti-game/references/project-setup.md +7 -3
- package/skills/ti-game/references/recipes-action.md +1 -1
- package/skills/ti-game/references/recipes-adventure-effects.md +1 -1
- package/skills/ti-game/references/roadmap.md +8 -4
- package/skills/ti-game/references/tilemaps.md +1 -1
|
@@ -178,7 +178,7 @@ After `purgetss build-fonts`, the generated `fonts.tss` includes the family clas
|
|
|
178
178
|
|
|
179
179
|
Two optional flags adjust what `build-fonts` generates:
|
|
180
180
|
|
|
181
|
-
- `-m, --module`: generates a CommonJS module in `app/lib/purgetss.fonts.js` (Alloy) or `Resources/lib/purgetss.fonts.js` (Classic)
|
|
181
|
+
- `-m, --module`: generates a CommonJS module in `app/lib/purgetss.fonts.js` (Alloy) or `Resources/lib/purgetss.fonts.js` (Classic). Its `families` object exposes the PostScript name of every processed TTF/OTF; icon CSS additionally populates `icons` with Unicode strings.
|
|
182
182
|
- `-f, --font-class-from-filename`: uses the font's filename as the font class name and icon prefix instead of the font family name. Useful when you want shorter prefixes. Replaces the old `-p` flag.
|
|
183
183
|
|
|
184
184
|
### Using `--module`
|
|
@@ -190,7 +190,7 @@ $ purgetss build-fonts --module
|
|
|
190
190
|
$ purgetss bf -m
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
The module is generated for text-only font collections too. By default, every TTF/OTF contributes a camel-cased PostScript-name key to `families`; with `--font-class-from-filename`, that key comes from the filename instead. Icon CSS can add a shorter family alias and a nested `icons` map. Both singular and plural export names remain available.
|
|
194
194
|
|
|
195
195
|
`app/lib/purgetss.fonts.js` (Alloy) or `Resources/lib/purgetss.fonts.js` (Classic)
|
|
196
196
|
```javascript
|
|
@@ -212,17 +212,40 @@ const icons = {
|
|
|
212
212
|
// ...
|
|
213
213
|
}
|
|
214
214
|
};
|
|
215
|
+
exports.icon = icons;
|
|
215
216
|
exports.icons = icons;
|
|
216
217
|
|
|
217
218
|
const families = {
|
|
219
|
+
'bevanItalic': 'Bevan-Italic',
|
|
220
|
+
'bevanRegular': 'Bevan-Regular',
|
|
221
|
+
'dancingScriptBold': 'DancingScript-Bold',
|
|
222
|
+
'dancingScriptMedium': 'DancingScript-Medium',
|
|
223
|
+
'dancingScriptRegular': 'DancingScript-Regular',
|
|
224
|
+
'dancingScriptSemiBold': 'DancingScript-SemiBold',
|
|
225
|
+
'mapIcons': 'map-icons',
|
|
226
|
+
'microns': 'microns',
|
|
218
227
|
// map-icons/map-icons.css
|
|
219
228
|
'mapIcon': 'map-icons',
|
|
220
229
|
// microns/microns.css
|
|
221
230
|
'mu': 'microns'
|
|
222
231
|
};
|
|
232
|
+
exports.family = families;
|
|
223
233
|
exports.families = families;
|
|
224
234
|
```
|
|
225
235
|
|
|
236
|
+
In Classic, load the generated file relative to `Resources/`:
|
|
237
|
+
|
|
238
|
+
```javascript
|
|
239
|
+
const customFonts = require('lib/purgetss.fonts')
|
|
240
|
+
|
|
241
|
+
const heading = Ti.UI.createLabel({
|
|
242
|
+
text: 'Custom typography',
|
|
243
|
+
font: { fontFamily: customFonts.families.dancingScriptSemiBold, fontSize: 28 }
|
|
244
|
+
})
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
See the complete [Classic module path table](./classic-projects.md#loading-generated-modules-in-classic).
|
|
248
|
+
|
|
226
249
|
### Using `--font-class-from-filename`
|
|
227
250
|
|
|
228
251
|
When you want the class name and the icon prefix to come from the **filename** of the `.ttf` and `.css` instead of the font family, use `-f`.
|
|
@@ -282,14 +305,18 @@ const icons = {
|
|
|
282
305
|
// ...
|
|
283
306
|
}
|
|
284
307
|
};
|
|
308
|
+
exports.icon = icons;
|
|
285
309
|
exports.icons = icons;
|
|
286
310
|
|
|
287
311
|
const families = {
|
|
312
|
+
'map': 'map-icons',
|
|
313
|
+
'mic': 'microns',
|
|
288
314
|
// map-icons/mp.css
|
|
289
315
|
'mp': 'map-icons',
|
|
290
316
|
// microns/mc.css
|
|
291
317
|
'mc': 'microns'
|
|
292
318
|
};
|
|
319
|
+
exports.family = families;
|
|
293
320
|
exports.families = families;
|
|
294
321
|
```
|
|
295
322
|
|
|
@@ -50,12 +50,14 @@ module.exports = {
|
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
brand: {
|
|
53
|
-
background: '#FFFFFF',
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
background: '#FFFFFF', // inherited by every piece that doesn't set its own
|
|
54
|
+
artworkCornerRadius: '0%', // rounded non-icon artwork: splashes, Feature Graphic and LaunchLogo
|
|
55
|
+
confirmOverwrites: true, // prompt before overwriting files (set false to skip)
|
|
56
|
+
optimize: false, // true = quantize the generated PNGs to a palette (lossy, ~71% smaller)
|
|
56
57
|
|
|
57
58
|
// One block per piece. Artwork comes from purgetss/brand/logo-<piece>.{svg,png};
|
|
58
59
|
// these keys are for numbers, colors and activation. Padding is never inherited.
|
|
60
|
+
// Only iosSplash, androidSplash, featureGraphic and launchLogo accept cornerRadius.
|
|
59
61
|
icon: { padding: '0%' }, // DefaultIcon.png + DefaultIcon-ios.png
|
|
60
62
|
dark: { background: null }, // DefaultIcon-Dark.png
|
|
61
63
|
tinted: {}, // DefaultIcon-Tinted.png
|
|
@@ -65,7 +67,7 @@ module.exports = {
|
|
|
65
67
|
featureGraphic: { padding: '12%' }, // MarketplaceArtworkFeature.png (1024×500)
|
|
66
68
|
adaptive: { padding: '18%' }, // ic_launcher_{foreground,background,monochrome}.png × 5 + ic_launcher.xml
|
|
67
69
|
legacyIcon: { padding: '10%' }, // ic_launcher.png × 5
|
|
68
|
-
appicon: {},
|
|
70
|
+
appicon: { padding: '10%' }, // appicon.png (128×128)
|
|
69
71
|
androidSplash: { padding: '26%' }, // assets/android/default.png + images/res-*/default.png × 11
|
|
70
72
|
|
|
71
73
|
// Opt-in: inert until you edit the Android theme / FCM meta-data by hand.
|
|
@@ -73,14 +75,19 @@ module.exports = {
|
|
|
73
75
|
notificationIcon: { enabled: false }, // drawable-*/ic_stat_notify.png × 5
|
|
74
76
|
ninePatch: { enabled: false } // background.9.png (not implemented yet)
|
|
75
77
|
},
|
|
78
|
+
// Sources in purgetss/images/ are 4x masters: a 1024px file yields
|
|
79
|
+
// 256 (mdpi/@1x), 384 (hdpi), 512 (xhdpi/@2x), 768 (xxhdpi/@3x), 1024 (xxxhdpi).
|
|
80
|
+
// There is no width to configure here — the source's own pixels decide.
|
|
81
|
+
// SVGs have no natural pixels; pin theirs in files: [] below.
|
|
76
82
|
images: {
|
|
77
|
-
|
|
78
|
-
quality: 85, // JPEG/WebP/AVIF quality (0-100)
|
|
83
|
+
quality: 85, // webp/jpeg/avif/tiff quality (0-100); PNG and GIF ignore it
|
|
79
84
|
format: null, // null = keep original; 'webp' | 'jpeg' | 'png' to convert every image
|
|
85
|
+
autoSync: true, // false = SVG pipeline computes dims but doesn't write to images.files
|
|
80
86
|
confirmOverwrites: true, // prompt before overwriting files (set false to skip)
|
|
81
|
-
files: [] // per-file overrides: [{ filename, width, height? }]
|
|
87
|
+
files: [] // per-file overrides: [{ filename: 'images/<sub>/<name>.<ext>', width, height? }]
|
|
82
88
|
// Note: --width (v7.8.0) and --opacity / --padding / --output (v7.10.0) are CLI-only
|
|
83
|
-
// by design — those decisions are per-asset, not project-wide.
|
|
89
|
+
// by design — those decisions are per-asset, not project-wide. Since v7.17.0
|
|
90
|
+
// writing any of them here aborts the run instead of being ignored.
|
|
84
91
|
},
|
|
85
92
|
theme: {
|
|
86
93
|
extend: {}
|
|
@@ -98,14 +105,17 @@ The config file has four main sections: `purge`, `brand`, `images`, and `theme`.
|
|
|
98
105
|
|
|
99
106
|
`brand:` and `images:` configure the matching CLI commands — see [CLI Commands: `brand`](./cli-commands.md#brand-command) and [CLI Commands: `images`](./cli-commands.md#images-command) for the full option lists. The rest of this page covers `purge` and `theme`.
|
|
100
107
|
|
|
101
|
-
For `brand`, the structure is **one block per piece of artwork** (v7.13.0), each accepting the same
|
|
108
|
+
For `brand`, the structure is **one block per piece of artwork** (v7.13.0), each accepting the same five keys where they apply:
|
|
102
109
|
|
|
103
110
|
- `logo`: path to this piece's artwork, when it lives outside `purgetss/brand/`
|
|
104
111
|
- `padding`: inset per side, as a number or a percentage string like `'19%'` — **never inherited**
|
|
112
|
+
- `cornerRadius`: integer or percentage string from `0` through `50`, valid only in `iosSplash`, `androidSplash`, `featureGraphic`, and `launchLogo`
|
|
105
113
|
- `background`: hex color, or `null` for transparent — inherited from `brand.background`
|
|
106
114
|
- `enabled`: `false` turns a default piece off, `true` turns an opt-in piece on
|
|
107
115
|
|
|
108
|
-
Plus `brand.background`, `brand.confirmOverwrites`, `brand.optimize`, `brand.logo` (the main logo) and `brand.monochromeLogo`.
|
|
116
|
+
Plus `brand.background`, shared `brand.artworkCornerRadius` (default `'0%'`), optional splash-only `brand.splashCornerRadius`, `brand.confirmOverwrites`, `brand.optimize`, `brand.logo` (the main logo) and `brand.monochromeLogo`.
|
|
117
|
+
|
|
118
|
+
Use flags for temporary artwork, geometry, the shared background, selection, activation, and optimization. Keep `confirmOverwrites`, permanent `enabled` values, and exceptional per-piece backgrounds in config. For radius, a piece-specific flag wins over the splash shortcut when applicable, then `--artwork-corner-radius`, piece config, `brand.splashCornerRadius` for splashes, `brand.artworkCornerRadius`, and `0%`.
|
|
109
119
|
|
|
110
120
|
> **INFO — Older `brand:` blocks update themselves**
|
|
111
121
|
> A `brand:` block written for an earlier PurgeTSS is rewritten **on disk** to this structure on the next run, carrying over every value that had been customized and printing each one it moved. Both earlier shapes are recognized: the original flat keys and the v7.7.0 grouped sections (`logos` / `padding` / `android` / `ios` / `colors`). This replaced the in-memory translation used from v7.10.2 to v7.12.1. A key that belongs to no structure at all — a typo — aborts the run with the list of valid ones instead of being ignored. See [App Icons & Branding → Older configs update themselves](./app-branding.md#older-configs-update-themselves).
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **SCOPE NOTE**
|
|
4
4
|
>
|
|
5
|
-
> This reference covers dynamic component creation using **Alloy's `$.UI.create()` helper** and **`Alloy.createStyle()` + `applyProperties()`**, both combined with PurgeTSS utility classes. It is an Alloy + PurgeTSS integration guide, **not** documentation for the `purgetss.ui` native module (see [animation-system.md](./animation-system.md) and [animation-advanced.md](./animation-advanced.md) for
|
|
5
|
+
> This reference covers dynamic component creation using **Alloy's `$.UI.create()` helper** and **`Alloy.createStyle()` + `applyProperties()`**, both combined with PurgeTSS utility classes. It is an Alloy + PurgeTSS integration guide, **not** documentation for the `purgetss.ui` native module (see [animation-system.md](./animation-system.md) and [animation-advanced.md](./animation-advanced.md) for Alloy animation usage, or [purgetss-ui-classic.md](./purgetss-ui-classic.md) for the generated Classic runtime).
|
|
6
6
|
>
|
|
7
7
|
> For general Alloy controller and view patterns, refer to the `alloy-guides` and `alloy-howtos` skills.
|
|
8
8
|
|
|
@@ -93,7 +93,7 @@ Each module exposes its existing icon lookup API plus a `families` object with `
|
|
|
93
93
|
| `framework7icons` | `fontFamily` |
|
|
94
94
|
|
|
95
95
|
```javascript
|
|
96
|
-
const fontAwesome = require('fontawesome')
|
|
96
|
+
const fontAwesome = require('lib/fontawesome')
|
|
97
97
|
|
|
98
98
|
const home = Ti.UI.createLabel({
|
|
99
99
|
text: fontAwesome.icons.home,
|
|
@@ -104,6 +104,8 @@ const home = Ti.UI.createLabel({
|
|
|
104
104
|
})
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
+
All generated modules live under `Resources/lib/`, so use the pattern shown above: include `lib/` in the path and omit both `Resources/` and `.js`. See the centralized [Classic module path table](./classic-projects.md#loading-generated-modules-in-classic) for all four filenames.
|
|
108
|
+
|
|
107
109
|
The direct alias and `families` values agree, for example `fontAwesome.solid === fontAwesome.families.solid`; `families.default` provides the library's default variant.
|
|
108
110
|
|
|
109
111
|
## Using icons in Alloy XML
|
|
@@ -61,7 +61,11 @@ When you run `purgetss` for the first time in your project, it does the followin
|
|
|
61
61
|
|
|
62
62
|
### 1. Auto-run Hook
|
|
63
63
|
|
|
64
|
-
PurgeTSS adds a task in `alloy.jmk` to run `purgetss` every time you compile your app.
|
|
64
|
+
PurgeTSS adds a task in `alloy.jmk` to run `purgetss` every time you compile your app. This works well with `liveview`.
|
|
65
|
+
|
|
66
|
+
With the default synchronous method, the hook inherits PurgeTSS output so XML validation and other diagnostics appear before Alloy's generic compiler failure. It then prints a final hint to run `purgetss` from the project root and rethrows the error.
|
|
67
|
+
|
|
68
|
+
Current `init` runs update an older active or disabled hook without changing its enabled state. Invoking `watch` normally enables a disabled hook.
|
|
65
69
|
|
|
66
70
|
### 2. `purgetss` Folder
|
|
67
71
|
|
|
@@ -189,25 +189,55 @@ This is a hint, **not an error**. The legacy 4×-from-viewBox behavior still run
|
|
|
189
189
|
On the first run, `purgetss images` injects an `images:` block into your existing `purgetss/config.cjs` (between `brand:` and `theme:`) with these defaults:
|
|
190
190
|
|
|
191
191
|
```javascript
|
|
192
|
+
// Sources in purgetss/images/ are 4x masters: a 1024px file yields
|
|
193
|
+
// 256 (mdpi/@1x), 384 (hdpi), 512 (xhdpi/@2x), 768 (xxhdpi/@3x), 1024 (xxxhdpi).
|
|
194
|
+
// There is no width to configure here — the source's own pixels decide.
|
|
195
|
+
// SVGs have no natural pixels; pin theirs in files: [] below.
|
|
192
196
|
images: {
|
|
193
|
-
|
|
194
|
-
quality: 85, // JPEG/WebP/AVIF quality (0-100)
|
|
197
|
+
quality: 85, // webp/jpeg/avif/tiff quality (0-100); PNG and GIF ignore it
|
|
195
198
|
format: null, // null = keep original; 'webp' | 'jpeg' | 'png' to convert every image
|
|
199
|
+
autoSync: true, // false = SVG pipeline computes dims but doesn't write to images.files
|
|
196
200
|
confirmOverwrites: true, // prompt before overwriting files (set false to skip)
|
|
197
201
|
files: [] // per-file overrides: [{ filename: 'images/<sub>/<name>.<ext>', width, height? }]
|
|
198
202
|
}
|
|
199
203
|
```
|
|
200
204
|
|
|
205
|
+
Since v7.17.0 the four comment lines above the section ship with the block itself, and the same text seeds through `init`. Reproduce the key order above when writing a config by hand — it is the order both generated copies use.
|
|
206
|
+
|
|
201
207
|
| Key | Default | Purpose |
|
|
202
208
|
| --- | --- | --- |
|
|
203
|
-
| `
|
|
204
|
-
| `quality` | `85` | Quality for lossy formats (JPEG, WebP, AVIF). Range `0–100`. |
|
|
209
|
+
| `quality` | `85` | Range `0–100`. Reaches `webp`, `jpeg`, `avif` and `tiff` only. PNG is written with `compressionLevel: 9` and GIF takes no quality parameter, so the value never applies to either. |
|
|
205
210
|
| `format` | `null` | `null` keeps each source's original format. Set `'webp'`, `'jpeg'`, or `'png'` to convert every output. |
|
|
211
|
+
| `autoSync` | `true` | When `false`, the SVG pipeline still computes dimensions and generates PNGs but never writes back to `images.files`. See [SVG-aware compile-time pipeline](./svg-pipeline.md). |
|
|
206
212
|
| `confirmOverwrites` | `true` | When `false`, the `[y/N/a]` prompt is skipped. |
|
|
207
213
|
| `files` | `[]` | Per-file dimension overrides. See [Per-file overrides](#per-file-overrides-with-imagesfiles). |
|
|
208
214
|
|
|
209
215
|
Change whatever you want to override globally; CLI flags still win for one-off runs. `autoSync` and `files` arrived in v7.11.0 to support the [SVG-aware compile-time pipeline](./svg-pipeline.md).
|
|
210
216
|
|
|
217
|
+
### Unknown keys are an error (v7.17.0)
|
|
218
|
+
|
|
219
|
+
Those five keys are the entire section. Since v7.17.0 anything else aborts the run before a single file is written, the same validation `brand:` has had since v7.13.0:
|
|
220
|
+
|
|
221
|
+
```text
|
|
222
|
+
> purgetss images
|
|
223
|
+
Error running images: Unknown key(s) in the images: section of purgetss/config.cjs:
|
|
224
|
+
• images.qualty
|
|
225
|
+
• images.files[0].widht
|
|
226
|
+
|
|
227
|
+
Top-level keys: quality, format, autoSync, confirmOverwrites, files
|
|
228
|
+
Inside files[]: filename, width, height
|
|
229
|
+
|
|
230
|
+
Check the spelling. No images were generated.
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
A `qualty: 95` that gets ignored is indistinguishable from the default, so before v7.17.0 the typo produced the wrong output silently. The check runs at both levels and reports every problem in one pass:
|
|
234
|
+
|
|
235
|
+
- **Top level** — only `quality`, `format`, `autoSync`, `confirmOverwrites`, `files`.
|
|
236
|
+
- **Inside each `files[]` entry** — only `filename`, `width`, `height`. `filename` is **required** and must be a string; an entry without one matches no file and used to be skipped in the same silence.
|
|
237
|
+
- `files` itself must be an array, and each entry an object.
|
|
238
|
+
|
|
239
|
+
Do not suggest `width`, `opacity`, `padding` or `output` as `images:` keys. They are CLI flags and were never part of the section — the validator now names them as errors instead of ignoring them. When one file needs a fixed width, pin it in `files`; the other three describe a single run.
|
|
240
|
+
|
|
211
241
|
## Per-file overrides with `images.files`
|
|
212
242
|
|
|
213
243
|
`images.files` (v7.11.0) is an array of per-file dimension pins. It does **not** filter which images get processed — `purgetss images` always walks every supported file under `purgetss/images/`. Entries in `files` only override the source and derived sizing for files whose `filename` matches.
|
|
@@ -484,7 +514,7 @@ If you only tweaked CSS classes (no image changes), you don't need to re-run `pu
|
|
|
484
514
|
| Flag | Purpose |
|
|
485
515
|
| --- | --- |
|
|
486
516
|
| `--format <ext>` | Convert all outputs to: `webp`, `jpeg`, `png`, `avif`, `gif`, `tiff`. Default: keep source format. |
|
|
487
|
-
| `--quality <n>` | Quality `0–100` for
|
|
517
|
+
| `--quality <n>` | Quality `0–100` for `webp`, `jpeg`, `avif` and `tiff`. PNG and GIF ignore it. Default `85`. |
|
|
488
518
|
| `--width <n>` | (v7.8.0) Pin `mdpi` / `@1x` output width to `n` pixels; `[1, 8192]`. Other densities derive from this base (×1.5 / ×2 / ×3 / ×4). Most useful for SVG sources with non-standard viewBoxes. CLI-only — no `config.cjs` equivalent because width is per-asset. |
|
|
489
519
|
| `--opacity <n>` | (v7.10.0) Multiply the alpha channel of every generated density by `n/100`. Range `[0, 100]`. Combine with `--format jpeg` and the alpha is flattened on white instead of producing a transparent JPEG. CLI-only. |
|
|
490
520
|
| `--padding <n>` | (v7.10.0) Shrink the rendered image inside each density canvas by `n%` symmetric borders. Range `[0, 40]`. CLI-only. |
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# `purgetss.ui` in Titanium Classic
|
|
2
|
+
|
|
3
|
+
Titanium Classic uses the same generated `purgetss.ui` runtime as Alloy, but it does not compile Alloy XML, TSS, utility classes, controllers, or `$.*` references. Configure the runtime with JavaScript objects containing native Titanium properties.
|
|
4
|
+
|
|
5
|
+
<!-- TOC-START -->
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- [Install and load](#install-and-load)
|
|
9
|
+
- [Public exports](#public-exports)
|
|
10
|
+
- [Quick start](#quick-start)
|
|
11
|
+
- [Animation method contract](#animation-method-contract)
|
|
12
|
+
- [Native animation configuration](#native-animation-configuration)
|
|
13
|
+
- [States and direct children](#states-and-direct-children)
|
|
14
|
+
- [Position helpers](#position-helpers)
|
|
15
|
+
- [Drag and collision configuration](#drag-and-collision-configuration)
|
|
16
|
+
- [Drag lifecycle and cleanup](#drag-lifecycle-and-cleanup)
|
|
17
|
+
- [Layout transitions](#layout-transitions)
|
|
18
|
+
- [Appearance in Classic](#appearance-in-classic)
|
|
19
|
+
- [Runtime utilities](#runtime-utilities)
|
|
20
|
+
- [Related references](#related-references)
|
|
21
|
+
|
|
22
|
+
<!-- TOC-END -->
|
|
23
|
+
|
|
24
|
+
## Install and load
|
|
25
|
+
|
|
26
|
+
Run this from the Classic project root:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
purgetss module
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The command creates `Resources/lib/purgetss.ui.js`. Load it from a file such as `Resources/app.js`:
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
const {
|
|
36
|
+
createAnimation,
|
|
37
|
+
Appearance,
|
|
38
|
+
deviceInfo,
|
|
39
|
+
saveComponent
|
|
40
|
+
} = require('lib/purgetss.ui')
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Use `createAnimation(args)` for ordinary Classic code. It returns a zero-size, touch-disabled `Ti.UI.View` decorated with animation methods. Treat this view as a behavior object; do not add it to the window.
|
|
44
|
+
|
|
45
|
+
## Public exports
|
|
46
|
+
|
|
47
|
+
| Export | Type and return | Contract |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| `AnimationProperties` | Constructor returning the decorated animation view | Public, but less clear than the factory in Classic |
|
|
50
|
+
| `createAnimation(args)` | Factory returning the decorated animation view | Preferred Classic entry point |
|
|
51
|
+
| `Appearance` | Singleton object | `init()`, `set()`, `get()`, `toggle()` |
|
|
52
|
+
| `deviceInfo()` | Function returning `undefined` | Logs platform and display information |
|
|
53
|
+
| `saveComponent({ source, directory? })` | Function returning `undefined` | Writes a PNG and invokes the photo-gallery API |
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
const { createAnimation, Appearance } = require('lib/purgetss.ui')
|
|
59
|
+
|
|
60
|
+
Appearance.init()
|
|
61
|
+
|
|
62
|
+
const window = Ti.UI.createWindow({ backgroundColor: 'surfaceColor' })
|
|
63
|
+
const card = Ti.UI.createView({
|
|
64
|
+
width: 220,
|
|
65
|
+
height: 120,
|
|
66
|
+
backgroundColor: 'accentColor',
|
|
67
|
+
opacity: 0,
|
|
68
|
+
transform: Ti.UI.createMatrix2D().scale(0.92)
|
|
69
|
+
})
|
|
70
|
+
const motion = createAnimation({
|
|
71
|
+
duration: 220,
|
|
72
|
+
curve: Ti.UI.ANIMATION_CURVE_EASE_OUT,
|
|
73
|
+
animationProperties: {
|
|
74
|
+
open: { opacity: 1, scale: 1 },
|
|
75
|
+
close: { opacity: 0, scale: 0.92 }
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
function onCardClick() { motion.play(card) }
|
|
80
|
+
function onWindowOpen() { motion.open(card) }
|
|
81
|
+
function disposeWindow() {
|
|
82
|
+
card.removeEventListener('click', onCardClick)
|
|
83
|
+
window.removeEventListener('open', onWindowOpen)
|
|
84
|
+
window.removeEventListener('close', disposeWindow)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
card.addEventListener('click', onCardClick)
|
|
88
|
+
window.add(card)
|
|
89
|
+
window.addEventListener('open', onWindowOpen)
|
|
90
|
+
window.addEventListener('close', disposeWindow)
|
|
91
|
+
window.open()
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`surfaceColor` and `accentColor` are names from `Resources/semantic.colors.json`; see [appearance-module.md#titanium-classic](./appearance-module.md#titanium-classic).
|
|
95
|
+
|
|
96
|
+
## Animation method contract
|
|
97
|
+
|
|
98
|
+
| Method | Signature | Return and callback |
|
|
99
|
+
| --- | --- | --- |
|
|
100
|
+
| `play` | `play(viewOrViews, callback?)` | `undefined`; callback once per view after its base animation completes |
|
|
101
|
+
| `toggle` | `toggle(viewOrViews, callback?)` | Exact alias of `play` |
|
|
102
|
+
| `apply` | `apply(viewOrViews, callback?)` | `undefined`; synchronous callback once per view |
|
|
103
|
+
| `open` | `open(viewOrViews, callback?)` | `undefined`; forces open state, callback once per view |
|
|
104
|
+
| `close` | `close(viewOrViews, callback?)` | `undefined`; forces close state, callback once per view |
|
|
105
|
+
| `draggable` | `draggable(viewOrViews)` | `undefined` |
|
|
106
|
+
| `undraggable` | `undraggable(viewOrViews)` | `undefined` |
|
|
107
|
+
| `detectCollisions` | `detectCollisions(views, dragCallback?, dropCallback?)` | `undefined` |
|
|
108
|
+
| `sequence` | `sequence(viewOrViews, callback?)` | `undefined`; callback once after the final view |
|
|
109
|
+
| `swap` | `swap(view1, view2)` | `undefined` |
|
|
110
|
+
| `pulse` | `pulse(view, count = 1)` | `undefined` |
|
|
111
|
+
| `shake` | `shake(view, intensity = 10)` | `undefined` |
|
|
112
|
+
| `snapTo` | `snapTo(view, targets)` | Selected target, `null` if none qualifies, or `undefined` for a missing source |
|
|
113
|
+
| `reorder` | `reorder(views, newOrder)` | `undefined` |
|
|
114
|
+
| `transition` | `transition(viewOrViews, layouts)` | `undefined` |
|
|
115
|
+
|
|
116
|
+
`play`, `toggle`, `apply`, `open`, and `close` accept one view or an array. The array callback runs once per view with:
|
|
117
|
+
|
|
118
|
+
- `type`: native completion type, or `'applied'` for `apply`.
|
|
119
|
+
- `bubbles` and `cancelBubble`: selected native primitives.
|
|
120
|
+
- `action`: `'play'` or `'apply'`.
|
|
121
|
+
- `state`: `'open'` or `'close'`.
|
|
122
|
+
- `id` and `targetId`.
|
|
123
|
+
- `index` and `total`.
|
|
124
|
+
- `getTarget()`: returns the affected view.
|
|
125
|
+
|
|
126
|
+
This is a newly constructed object, not the original Titanium event. For array playback, the configured base delay accumulates for successive views. `sequence()` instead waits for each native completion and invokes its callback once after the last view. Passing an empty array to `sequence()` produces no callback.
|
|
127
|
+
|
|
128
|
+
`animationProperties.complete` starts after the active `play`, `toggle`, `open`, or `close` base phase, and is applied immediately after `apply`. The public play callback does not wait for the second top-level `complete` animation.
|
|
129
|
+
|
|
130
|
+
## Native animation configuration
|
|
131
|
+
|
|
132
|
+
Pass native `Ti.UI.Animation` values directly:
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
const { createAnimation } = require('lib/purgetss.ui')
|
|
136
|
+
|
|
137
|
+
const motion = createAnimation({
|
|
138
|
+
id: 'cardMotion',
|
|
139
|
+
duration: 240,
|
|
140
|
+
delay: 40,
|
|
141
|
+
curve: Ti.UI.ANIMATION_CURVE_EASE_IN_OUT,
|
|
142
|
+
repeat: 1,
|
|
143
|
+
autoreverse: false,
|
|
144
|
+
opacity: 0.8,
|
|
145
|
+
backgroundColor: '#2563eb',
|
|
146
|
+
scale: 1.05,
|
|
147
|
+
rotate: 4
|
|
148
|
+
})
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The constructor removes `id` from the native animation object. It converts top-level `scale`, `rotate`, and `anchorPoint` into one `Ti.UI.Matrix2D`; it performs the same conversion separately for `animationProperties.open` and `.close`. Helpers such as `pulse`, `shake`, `swap`, `snapTo`, `reorder`, and `transition` can replace or reset that transform.
|
|
152
|
+
|
|
153
|
+
Most helpers spread the base object before their method-specific assignments, so later helper fields win. There is no shared `200ms` timing fallback for position/layout helpers. Only `shake()` computes `(duration ?? 400) / 6`, rounded to the nearest millisecond; `pulse()` supplies a default scale of `1.2`. Both force `ANIMATION_CURVE_EASE_IN_OUT`.
|
|
154
|
+
|
|
155
|
+
Titanium exposes `anchorPoint` differently across platforms. Test pivot-dependent animation on both platforms; in Classic, set the view's iOS anchor point before animation and pass an Android animation anchor point only inside a platform guard.
|
|
156
|
+
|
|
157
|
+
## States and direct children
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
const panelMotion = createAnimation({
|
|
161
|
+
duration: 220,
|
|
162
|
+
animationProperties: {
|
|
163
|
+
open: { opacity: 1, scale: 1 },
|
|
164
|
+
close: { opacity: 0, scale: 0.94 },
|
|
165
|
+
complete: { borderColor: '#22c55e' },
|
|
166
|
+
children: { duration: 160 }
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
titleLabel.animationProperties = {
|
|
171
|
+
child: { delay: 40 },
|
|
172
|
+
open: { opacity: 1, top: 16 },
|
|
173
|
+
close: { opacity: 0, top: 24 },
|
|
174
|
+
complete: { color: '#22c55e' }
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
panelMotion.open(panel)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
For a direct child, merge precedence is:
|
|
181
|
+
|
|
182
|
+
1. Parent `animationProperties.children`.
|
|
183
|
+
2. Child `animationProperties.child`.
|
|
184
|
+
3. Child's active `open`, `close`, or `complete` object.
|
|
185
|
+
|
|
186
|
+
Later values win. Nested descendants are not traversed automatically.
|
|
187
|
+
|
|
188
|
+
## Position helpers
|
|
189
|
+
|
|
190
|
+
- `swap()` exchanges two rendered positions, temporarily promotes both views, persists `top`/`left`, and updates private drag origins. It restores stacking from the current draggable registry order, not arbitrary original z-index values.
|
|
191
|
+
- `snapTo()` chooses the nearest target by center distance and returns it immediately. Views must be laid out because it reads `rect`.
|
|
192
|
+
- `reorder()` maps each view to the captured position at `newOrder[index]`. A length mismatch returns without changes.
|
|
193
|
+
- `pulse()` forces autoreverse, the supplied repeat count, and ease-in-out, then resets the transform.
|
|
194
|
+
- `shake()` starts at `-intensity`, animates to `+intensity` through six short phases, and resets the transform.
|
|
195
|
+
|
|
196
|
+
These methods do not expose completion callbacks. Configure duration, delay, and curve on the object passed to `createAnimation()`.
|
|
197
|
+
|
|
198
|
+
## Drag and collision configuration
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
const dragMotion = createAnimation({
|
|
202
|
+
duration: 160,
|
|
203
|
+
bounds: { top: 12, right: 12, bottom: 12, left: 12 },
|
|
204
|
+
draggingType: 'animate',
|
|
205
|
+
draggable: {
|
|
206
|
+
drag: { opacity: 0.7, scale: 1.04 },
|
|
207
|
+
drop: { opacity: 1, scale: 1 }
|
|
208
|
+
},
|
|
209
|
+
animationProperties: {
|
|
210
|
+
keepZIndex: true,
|
|
211
|
+
snap: { back: true, center: true }
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
piece.bounds = { bottom: 40 }
|
|
216
|
+
piece.constraint = 'horizontal'
|
|
217
|
+
piece.draggingType = 'apply'
|
|
218
|
+
piece.draggable = {
|
|
219
|
+
drag: { opacity: 0.5 },
|
|
220
|
+
drop: { opacity: 1 }
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
dragMotion.draggable(piece)
|
|
224
|
+
dragMotion.detectCollisions([piece, target], (source, hovered) => {
|
|
225
|
+
target.borderColor = hovered === target ? '#22c55e' : 'transparent'
|
|
226
|
+
}, (source, droppedOn) => {
|
|
227
|
+
Ti.API.info(`${source.id} dropped on ${droppedOn.id}`)
|
|
228
|
+
})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Runtime precedence and behavior:
|
|
232
|
+
|
|
233
|
+
- Global bounds are the base; `view.bounds` overrides individual edges.
|
|
234
|
+
- Axis restriction comes from `view.constraint`; a constructor-level constraint is not read.
|
|
235
|
+
- Per-view `draggingType` overrides the constructor value.
|
|
236
|
+
- Global and per-view `draggable.drag/drop` objects are merged; view fields win.
|
|
237
|
+
- Every resolved drag/drop property is forwarded, including size, transform, and anchor-point fields.
|
|
238
|
+
- `snap.center` calls `snapTo()` after a valid drop; `snap.back` returns a missed drop to its origin.
|
|
239
|
+
- There is no `snap.magnet` behavior.
|
|
240
|
+
- Collision detection tests the dragged view's center against registered targets. The last non-null hover target is a fallback at release.
|
|
241
|
+
|
|
242
|
+
Call drag/collision setup after the views are attached and laid out. `draggable(array)` assigns each view `zIndex` from its array index before registration. `keepZIndex` only prevents later touch-start promotion; register views individually if their existing z-index values must not be overwritten.
|
|
243
|
+
|
|
244
|
+
## Drag lifecycle and cleanup
|
|
245
|
+
|
|
246
|
+
Register each view once. The current runtime does not deduplicate repeated `draggable()` calls: they create additional listeners and registry entries. One `undraggable()` call removes only the latest stored listener set and the first matching registry entries.
|
|
247
|
+
|
|
248
|
+
`undraggable()` removes the stored `touchstart`, `touchmove`, `touchend`, and global `orientationchange` listeners and most private position/collision fields, but it leaves `_wasDragged`. `detectCollisions()` retains the last non-null callbacks on the animation object; passing `null` does not clear them.
|
|
249
|
+
|
|
250
|
+
```javascript
|
|
251
|
+
let dragReady = false
|
|
252
|
+
|
|
253
|
+
function enableDrag() {
|
|
254
|
+
if (dragReady) return
|
|
255
|
+
dragReady = true
|
|
256
|
+
dragMotion.draggable(piece)
|
|
257
|
+
dragMotion.detectCollisions([piece, target])
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function disposeWindow() {
|
|
261
|
+
dragMotion.undraggable([piece, target])
|
|
262
|
+
window.removeEventListener('open', enableDrag)
|
|
263
|
+
window.removeEventListener('close', disposeWindow)
|
|
264
|
+
dragReady = false
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
window.addEventListener('open', enableDrag)
|
|
268
|
+
window.addEventListener('close', disposeWindow)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Release view, callback, and animation references after close. Application code should not depend on private underscore fields.
|
|
272
|
+
|
|
273
|
+
## Layout transitions
|
|
274
|
+
|
|
275
|
+
`transition(views, layouts)` accepts these layout fields:
|
|
276
|
+
|
|
277
|
+
| Field | Default | Behavior |
|
|
278
|
+
| --- | --- | --- |
|
|
279
|
+
| `translation` | `{ x: 0, y: 0 }` | Matrix translation |
|
|
280
|
+
| `rotate` | `0` | Matrix rotation in degrees |
|
|
281
|
+
| `scale` | `1` | Matrix scale |
|
|
282
|
+
| `zIndex` | unchanged | Assigned before animation |
|
|
283
|
+
| `width`, `height` | unchanged | Optional size animation |
|
|
284
|
+
| `opacity` | unchanged | Optional opacity animation |
|
|
285
|
+
|
|
286
|
+
```javascript
|
|
287
|
+
const layoutMotion = createAnimation({
|
|
288
|
+
duration: 220,
|
|
289
|
+
curve: Ti.UI.ANIMATION_CURVE_EASE_IN_OUT
|
|
290
|
+
})
|
|
291
|
+
const fan = [
|
|
292
|
+
{ translation: { x: -120, y: 20 }, rotate: -15, scale: 0.8, zIndex: 1 },
|
|
293
|
+
{ translation: { x: 0, y: 0 }, rotate: 0, scale: 1, zIndex: 3 },
|
|
294
|
+
{ translation: { x: 120, y: 20 }, rotate: 15, scale: 0.8, zIndex: 2 }
|
|
295
|
+
]
|
|
296
|
+
|
|
297
|
+
layoutMotion.transition([cardA, cardB, cardC], fan)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
A view without a matching layout fades to opacity `0`, receives `zIndex: 0`, and has touch disabled. Extra layouts are ignored. A later matching layout fades a hidden view in and reenables touch. iOS preserves the last transform while hidden; Android resets transform, translation, rotation, and scale. Completion forces `touchEnabled: true` for every matched view, so reapply `false` afterward when a view must remain non-interactive.
|
|
301
|
+
|
|
302
|
+
On Mac Catalyst, use a fixed-size parent rather than `Ti.UI.FILL` for rotated layouts; a resizable parent can distort the matrix.
|
|
303
|
+
|
|
304
|
+
## Appearance in Classic
|
|
305
|
+
|
|
306
|
+
Put semantic colors in `Resources/semantic.colors.json`, use their names directly in native color properties, and call `Appearance.init()` before opening the first window. Classic does not need utility classes, TSS, `purgetss/config.cjs`, or Alloy runtime files. Run a full native build after adding or changing semantic keys; LiveView alone does not rebuild the native catalog.
|
|
307
|
+
|
|
308
|
+
See [appearance-module.md#titanium-classic](./appearance-module.md#titanium-classic) for setup and all four method contracts.
|
|
309
|
+
|
|
310
|
+
## Runtime utilities
|
|
311
|
+
|
|
312
|
+
```javascript
|
|
313
|
+
const { deviceInfo, saveComponent } = require('lib/purgetss.ui')
|
|
314
|
+
|
|
315
|
+
deviceInfo()
|
|
316
|
+
|
|
317
|
+
function savePreview() {
|
|
318
|
+
if (!Ti.Media.hasPhotoGalleryPermissions()) {
|
|
319
|
+
Ti.Media.requestPhotoGalleryPermissions(event => {
|
|
320
|
+
if (event.success) savePreview()
|
|
321
|
+
})
|
|
322
|
+
return
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
saveComponent({
|
|
326
|
+
source: preview,
|
|
327
|
+
directory: Ti.Filesystem.applicationDataDirectory
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
`deviceInfo()` logs values and returns `undefined`. It logs `xdpi` and `ydpi` only on Android. Its tablet flag recognizes iPad only, so Android tablets are currently logged as `isTablet: false` and `isHandheld: true`.
|
|
333
|
+
|
|
334
|
+
`saveComponent()` calls `source.toImage()`, derives an MD5-based `.png` filename, writes it to `directory` (default: `Ti.Filesystem.tempDirectory`), then invokes `Ti.Media.saveToPhotoGallery()`. It returns `undefined` and provides no completion or error callback. Check/request gallery permission before calling it; on iOS, declare the required photo-library add usage description.
|
|
335
|
+
|
|
336
|
+
## Related references
|
|
337
|
+
|
|
338
|
+
- [animation-system.md](./animation-system.md) — Alloy syntax and all animation methods.
|
|
339
|
+
- [animation-advanced.md](./animation-advanced.md) — generated timing classes and runtime rules.
|
|
340
|
+
- [appearance-module.md](./appearance-module.md) — Appearance workflow and API.
|
|
341
|
+
- [classic-projects.md](./classic-projects.md) — commands and capabilities available to Classic projects.
|
|
@@ -8,6 +8,23 @@ When in doubt about whether a class, flag, or config key exists in the user's in
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
## v7.17.0
|
|
12
|
+
- The `images:` section rejects unknown keys instead of ignoring them, at both levels: the five top-level keys (`quality`, `format`, `autoSync`, `confirmOverwrites`, `files`) and the three inside each `files[]` entry (`filename`, `width`, `height`, with `filename` required). Nothing is generated when validation fails. Never suggest `width`, `opacity`, `padding` or `output` as config keys — they are CLI flags and now abort the run. See [multi-density-images.md](multi-density-images.md).
|
|
13
|
+
- The generated `images:` block gained four comment lines explaining the 4× master convention, and the `quality` comment now names the formats it reaches: `webp`, `jpeg`, `avif`, `tiff`. PNG uses `compressionLevel: 9` and GIF takes no quality parameter, so neither is affected. Both copies changed — the one patched into an existing config and the one `init` seeds.
|
|
14
|
+
- `install-dependencies` and `create --dependencies` now install only `eslint` and `@eslint/js` and copy a flat `eslint.config.mjs`. `eslint-config-axway` and `eslint-plugin-alloy` are gone: the old `.eslintrc.js` could not run under ESLint 9. See [cli-commands.md](cli-commands.md#install-dependencies-command).
|
|
15
|
+
|
|
16
|
+
## v7.16.2
|
|
17
|
+
- `build-fonts --module` now exports every processed TTF/OTF PostScript name through `families`, including text-only collections; icon CSS additionally populates `icons`. Filename-derived keys such as `poppinsSemiBold` are supported. See [custom-fonts.md](custom-fonts.md).
|
|
18
|
+
- In clean Classic projects, saved `shades` palettes and `color-module` no longer scaffold empty brand, font, or image source folders. `shades` still refreshes an existing generated color module. See [classic-projects.md](classic-projects.md).
|
|
19
|
+
|
|
20
|
+
## v7.16.1
|
|
21
|
+
- Alloy auto-purge hooks now expose PurgeTSS output before Alloy's generic failure, print a command-line diagnostic hint, and migrate older active or disabled hooks without losing their state. See [installation-setup.md](installation-setup.md).
|
|
22
|
+
|
|
23
|
+
## v7.16.0
|
|
24
|
+
- `brand.artworkCornerRadius` and optional `brand.splashCornerRadius` add shared rounding for non-icon artwork; the iOS/Android splash, Feature Graphic, and LaunchLogo pieces also accept `cornerRadius`. Values are 0–50, and unsupported pieces reject the key before writing. Platform-masked and store icon outputs remain unrounded. See [app-branding.md](app-branding.md).
|
|
25
|
+
- Six CLI radius flags provide shared, splash-only, and per-piece one-run overrides. `--appicon-padding` completes the existing `brand.appicon.padding` contract, whose canonical default is `10%`.
|
|
26
|
+
- Branding summaries and `--dry-run` now report effective padding and corner radius for rounded pieces.
|
|
27
|
+
|
|
11
28
|
## v7.15.0
|
|
12
29
|
- Standalone `images`, `semantic`, `shades`, `color-module`, `module`, `icon-library`, and `build-fonts` now support Classic without installing the Alloy hook or utility-class lifecycle. Outputs route to native `Resources/` paths where appropriate. See [classic-projects.md](classic-projects.md).
|
|
13
30
|
- `images` follows `<deployment-targets>` from `tiapp.xml` by default; explicit `--android` / `--ios` override that selection.
|