@maccesar/titools 2.10.0 → 2.10.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.
- package/lib/commands/update.js +4 -1
- package/package.json +1 -1
- package/skills/purgetss/SKILL.md +12 -0
- package/skills/purgetss/references/animation-advanced.md +0 -9
- package/skills/purgetss/references/app-branding.md +20 -0
- package/skills/purgetss/references/appearance-module.md +85 -27
- package/skills/purgetss/references/apply-directive.md +60 -0
- package/skills/purgetss/references/arbitrary-values.md +41 -0
- package/skills/purgetss/references/cli-commands.md +52 -17
- package/skills/purgetss/references/custom-rules.md +36 -19
- package/skills/purgetss/references/customization-deep-dive.md +37 -15
- package/skills/purgetss/references/grid-layout.md +45 -26
- package/skills/purgetss/references/icon-fonts.md +27 -25
- package/skills/purgetss/references/installation-setup.md +28 -2
- package/skills/purgetss/references/ios-large-titles.md +31 -15
- package/skills/purgetss/references/multi-density-images.md +57 -0
- package/skills/purgetss/references/opacity-modifier.md +11 -2
- package/skills/purgetss/references/semantic-colors.md +44 -1
- package/skills/purgetss/references/tikit-components.md +1 -3
package/lib/commands/update.js
CHANGED
|
@@ -30,6 +30,7 @@ import { formatList, isTitaniumProject } from '../utils.js';
|
|
|
30
30
|
import { getAgentsSkillsDir } from '../config.js';
|
|
31
31
|
import { existsSync } from 'fs';
|
|
32
32
|
import { join, resolve } from 'path';
|
|
33
|
+
import os from 'os';
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* Check if a platform has any skill symlinks installed
|
|
@@ -105,7 +106,9 @@ export async function updateCommand(options) {
|
|
|
105
106
|
if (!options.local) {
|
|
106
107
|
const projectDir = process.cwd();
|
|
107
108
|
const isProject = isTitaniumProject(projectDir);
|
|
108
|
-
|
|
109
|
+
// When cwd === home, "local" and "global" point to the same .agents/skills dir
|
|
110
|
+
const isHomeDir = projectDir === os.homedir();
|
|
111
|
+
const hasLocalSkills = !isHomeDir && isProject && hasSkillsAt(projectDir);
|
|
109
112
|
const hasGlobalSkills = hasSkillsAt(undefined);
|
|
110
113
|
|
|
111
114
|
// Only show prompt if BOTH local and global skills exist
|
package/package.json
CHANGED
package/skills/purgetss/SKILL.md
CHANGED
|
@@ -112,6 +112,18 @@ purgetss create 'MyApp' -d -v fa
|
|
|
112
112
|
# -v: Copy icon fonts (fa, mi, ms, f7)
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
## What's New in v7.9.0
|
|
116
|
+
|
|
117
|
+
- Opacity modifier on semantic colors — `bg-surface/65` syntax now works for any class mapped through `theme.extend.colors`. PurgeTSS auto-derives `<originalKey>_<alphaPercent>` entries in `semantic.colors.json` per mode (light/dark). See [Semantic Colors → Opacity modifier auto-derivation](references/semantic-colors.md#opacity-modifier-auto-derivation). **Native rebuild required** — Liveview hot-reload alone does not refresh `semantic.colors.json`
|
|
118
|
+
- `theme.Window` / `theme.View` / `theme.ImageView` presets now use **replace mode** — they no longer carry framework defaults (white background, `Ti.UI.SIZE`, iOS `hires: true`). This fixes gradient ghosting where a preset Window with `bg-gradient-*` apply was being covered by the implicit `backgroundColor: '#FFFFFF'`. Use `theme.extend.Window` / `theme.extend.View` / `theme.extend.ImageView` when you want **extend mode** (merge with defaults). See [Apply Directive → Extend mode vs replace mode](references/apply-directive.md#extend-mode-vs-replace-mode)
|
|
119
|
+
- Glossary path renamed: `purgetss/experimental/tailwind-classes/` → `purgetss/glossary/tailwind-classes/`
|
|
120
|
+
|
|
121
|
+
## What's New in v7.8.0
|
|
122
|
+
|
|
123
|
+
- `purgetss images --width <n>` — pin SVG output to a specific base width; PurgeTSS derives per-density assets from the multiplier scale (×1 / ×1.5 / ×2 / ×3 / ×4). Range `[1, 8192]`. CLI-only (no `images:` config equivalent — width is per-asset). See [Multi-Density Images → Pinning the output width with --width](references/multi-density-images.md#pinning-the-output-width-with---width)
|
|
124
|
+
- Class syntax pre-validation — the build now emits a `Class Syntax Error` block (file path + line + `Fix:` suggestion) for 5 detected patterns: inverted negative sign (`top-(-10)` → `-top-(10)`), Tailwind brackets (`top-[10px]` → `top-(10px)`), empty parens (`wh-()`), whitespace inside parens (`wh-( 200 )` → `wh-(200)`), redundant `px` unit (`top-(10px)` → `top-(10)`). See [Arbitrary Values → Class syntax pre-validation](references/arbitrary-values.md#class-syntax-pre-validation)
|
|
125
|
+
- Parser fix for negative values inside parentheses — `top-(-10)` is now correctly flagged as inverted-negative-sign instead of silently misparsed
|
|
126
|
+
|
|
115
127
|
## What's New in v7.7.0
|
|
116
128
|
|
|
117
129
|
- `brand:` config restructured — flat keys replaced by purpose-based groups: `brand.logos`, `brand.padding`, `brand.android`, `brand.ios`, `brand.colors`
|
|
@@ -238,15 +238,6 @@ const { saveComponent } = require('purgetss.ui')
|
|
|
238
238
|
saveComponent({ source: $.myView, directory: 'screenshots' })
|
|
239
239
|
```
|
|
240
240
|
|
|
241
|
-
### createAnimation(args)
|
|
242
|
-
|
|
243
|
-
Factory function to create Animation objects programmatically.
|
|
244
|
-
|
|
245
|
-
```javascript
|
|
246
|
-
const { createAnimation } = require('purgetss.ui')
|
|
247
|
-
const anim = createAnimation({ duration: 300, curve: Ti.UI.ANIMATION_CURVE_EASE_IN_OUT })
|
|
248
|
-
```
|
|
249
|
-
|
|
250
241
|
---
|
|
251
242
|
|
|
252
243
|
## Implementation Rules
|
|
@@ -301,6 +301,16 @@ brand: {
|
|
|
301
301
|
>
|
|
302
302
|
> 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`.
|
|
303
303
|
|
|
304
|
+
> **WARNING**
|
|
305
|
+
>
|
|
306
|
+
> Merge splash settings into the existing Android theme
|
|
307
|
+
> 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.
|
|
308
|
+
|
|
309
|
+
> **INFO**
|
|
310
|
+
>
|
|
311
|
+
> A brief flash on splash exit is usually the system, not your PNGs
|
|
312
|
+
> 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.
|
|
313
|
+
|
|
304
314
|
## Android legacy splash fallback
|
|
305
315
|
|
|
306
316
|
Since v7.7.0, PurgeTSS regenerates `app/assets/android/default.png` in Alloy projects and `Resources/android/default.png` in Classic projects.
|
|
@@ -411,6 +421,16 @@ purgetss brand --cleanup-legacy --aggressive
|
|
|
411
421
|
> Commit first
|
|
412
422
|
> `--cleanup-legacy` deletes files permanently. Commit your project to git before running without `--dry-run` so `git restore` is available as a rollback.
|
|
413
423
|
|
|
424
|
+
> **INFO**
|
|
425
|
+
>
|
|
426
|
+
> Files kept on purpose
|
|
427
|
+
> `--cleanup-legacy` intentionally does **not** remove the following files, even when modern adaptive launcher icons and splash assets are already in place:
|
|
428
|
+
>
|
|
429
|
+
> - `app/assets/android/default.png` (Alloy projects)
|
|
430
|
+
> - `Resources/android/default.png` (Classic projects)
|
|
431
|
+
>
|
|
432
|
+
> 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.
|
|
433
|
+
|
|
414
434
|
## Troubleshooting
|
|
415
435
|
|
|
416
436
|
### The icon looks cropped or cramped on my phone
|
|
@@ -2,15 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
The `Appearance` export from `purgetss.ui` (added in **v7.5.3**) handles Light / Dark / System mode switching and persists the user's choice across app restarts. It is a thin, deterministic wrapper around Titanium's native `Ti.UI.overrideUserInterfaceStyle` plus `Ti.App.Properties`, exposed as a singleton so every controller in the app reads and writes the same state.
|
|
4
4
|
|
|
5
|
-
This
|
|
5
|
+
This reference follows the same workflow-first / API-second split the official PurgeTSS documentation now uses:
|
|
6
|
+
|
|
7
|
+
- **Workflow** — initialize at startup, then build a Settings view that calls `Appearance.set(...)` (mirrors `best-practices/1-appearance-setup.md`).
|
|
8
|
+
- **API reference** — the four public methods, exact signatures, and lifecycle (mirrors `purgetss-ui/10-appearance.md`).
|
|
9
|
+
|
|
10
|
+
For the color wiring that actually makes the UI *respond* to mode changes, see [semantic-colors.md](./semantic-colors.md).
|
|
6
11
|
|
|
7
12
|
> **INFO**
|
|
8
13
|
>
|
|
9
14
|
> Requires PurgeTSS **>= 7.5.3**. On older versions the `Appearance` export does not exist — the `require('purgetss.ui')` call will succeed, but `Appearance` will be `undefined`. Run `purgetss --version` in the project root to confirm.
|
|
10
15
|
|
|
11
|
-
##
|
|
16
|
+
## How it fits together
|
|
17
|
+
|
|
18
|
+
The Appearance module sits between three moving parts: a JS singleton, the OS-level interface style, and the semantic colors generated by PurgeTSS. The diagram below shows how a single `Appearance.init()` call at startup wires them together, and what happens when the user later picks a mode in Settings.
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
┌─ app/lib/appearance.js (singleton from purgetss.ui) ─────────────────┐
|
|
22
|
+
│ │
|
|
23
|
+
│ Appearance.init() ────► reads 'userInterfaceStyle' │
|
|
24
|
+
│ from Ti.App.Properties │
|
|
25
|
+
│ applies Ti.UI.overrideUserInterfaceStyle │
|
|
26
|
+
│ │
|
|
27
|
+
│ Appearance.set(m) ────► writes Ti.App.Properties │
|
|
28
|
+
│ applies Ti.UI.overrideUserInterfaceStyle │
|
|
29
|
+
│ │
|
|
30
|
+
│ Appearance.get() ────► returns 'system' | 'light' | 'dark' │
|
|
31
|
+
│ Appearance.toggle()────► flips between 'light' and 'dark' │
|
|
32
|
+
│ │
|
|
33
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
34
|
+
│
|
|
35
|
+
▼
|
|
36
|
+
┌─ Ti.UI.semanticColorType (runtime) ──────────────────────────────────┐
|
|
37
|
+
│ │
|
|
38
|
+
│ Light mode ─► resolves bg-surface to surfaceColor.light │
|
|
39
|
+
│ resolves text-on-surface to textColor.light │
|
|
40
|
+
│ │
|
|
41
|
+
│ Dark mode ─► resolves bg-surface to surfaceColor.dark │
|
|
42
|
+
│ resolves text-on-surface to textColor.dark │
|
|
43
|
+
│ │
|
|
44
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
45
|
+
│
|
|
46
|
+
▼
|
|
47
|
+
┌─ Utility classes in views ───────────────────────────────────────────┐
|
|
48
|
+
│ │
|
|
49
|
+
│ bg-surface, text-on-surface, bg-border, bg-surface-high, ... │
|
|
50
|
+
│ Repaint instantly on every Appearance.set(...) call. │
|
|
51
|
+
│ │
|
|
52
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`Ti.App.Properties` is the persistence layer — the value written during `set(...)` is what `init()` reads on the next launch. Clearing app storage or reinstalling resets to `'system'`.
|
|
56
|
+
|
|
57
|
+
## Workflow
|
|
58
|
+
|
|
59
|
+
The fastest path from zero to a working Light / Dark toggle is three steps. Each one maps to one section of the official `best-practices/1-appearance-setup.md` guide.
|
|
12
60
|
|
|
13
|
-
|
|
61
|
+
### 1. Define semantic colors
|
|
62
|
+
|
|
63
|
+
Create `app/assets/semantic.colors.json` and register the color names under `theme.extend.colors` in `purgetss/config.cjs`. The class names you use in XML (`bg-surface`, `text-on-surface`, `bg-border`, ...) come from this mapping.
|
|
64
|
+
|
|
65
|
+
See [semantic-colors.md](./semantic-colors.md) for the JSON schema, the `config.cjs` mapping, nesting rules, and alpha transparency.
|
|
66
|
+
|
|
67
|
+
### 2. Initialize Appearance at startup
|
|
68
|
+
|
|
69
|
+
Call `Appearance.init()` **once** before opening the first window. The standard place is the top of `app/controllers/index.js`:
|
|
14
70
|
|
|
15
71
|
`app/controllers/index.js`
|
|
16
72
|
```js
|
|
@@ -27,22 +83,11 @@ $.navWin.open()
|
|
|
27
83
|
>
|
|
28
84
|
> Call `Appearance.init()` **before** `$.navWin.open()` (or whichever window you open first). If the window opens first, it will render once against the system default and then flicker to the saved mode when `init()` runs — a visible flash on cold launch.
|
|
29
85
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
| Method | Description |
|
|
33
|
-
| ------------ | --------------------------------------------------------------------------- |
|
|
34
|
-
| `init()` | Restore the saved mode from `Ti.App.Properties` and apply it via `Ti.UI.overrideUserInterfaceStyle`. Call once at app startup before opening the first window. |
|
|
35
|
-
| `set(mode)` | Apply and persist a mode. Accepts `'system'`, `'light'`, or `'dark'`. Any other value is silently ignored. |
|
|
36
|
-
| `get()` | Returns the current mode as a string: `'system'`, `'light'`, or `'dark'`. |
|
|
37
|
-
| `toggle()` | Switch between `'light'` and `'dark'`. Skips `'system'` — if current mode is `'system'` the next call produces `'dark'`. |
|
|
38
|
-
|
|
39
|
-
All four methods are **synchronous**. They do not fire events; if you need to react to a mode change, call your update logic right after `Appearance.set(...)`.
|
|
40
|
-
|
|
41
|
-
## Full Settings-view example
|
|
86
|
+
### 3. Build an Appearance toggle
|
|
42
87
|
|
|
43
|
-
|
|
88
|
+
Build a Settings view where users pick `System`, `Light`, or `Dark`. Tapping a row calls `Appearance.set(...)` and updates the check icons.
|
|
44
89
|
|
|
45
|
-
|
|
90
|
+
#### View
|
|
46
91
|
|
|
47
92
|
`app/views/settings.xml`
|
|
48
93
|
```xml
|
|
@@ -86,7 +131,7 @@ A three-row settings panel — System / Light / Dark — where each row shows a
|
|
|
86
131
|
</Alloy>
|
|
87
132
|
```
|
|
88
133
|
|
|
89
|
-
|
|
134
|
+
#### Controller
|
|
90
135
|
|
|
91
136
|
`app/controllers/settings.js`
|
|
92
137
|
```js
|
|
@@ -95,16 +140,16 @@ const { Appearance } = require('purgetss.ui')
|
|
|
95
140
|
// Sync check icons with the mode saved at app startup.
|
|
96
141
|
updateUI(Appearance.get())
|
|
97
142
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
143
|
+
function selectDark() { selectAppearance('dark') }
|
|
144
|
+
function selectLight() { selectAppearance('light') }
|
|
145
|
+
function selectSystem() { selectAppearance('system') }
|
|
101
146
|
|
|
102
|
-
|
|
147
|
+
function selectAppearance(value) {
|
|
103
148
|
Appearance.set(value)
|
|
104
149
|
updateUI(value)
|
|
105
150
|
}
|
|
106
151
|
|
|
107
|
-
|
|
152
|
+
function updateUI(value) {
|
|
108
153
|
$.themeDarkCheck.visible = (value === 'dark')
|
|
109
154
|
$.themeLightCheck.visible = (value === 'light')
|
|
110
155
|
$.themeSystemCheck.visible = (value === 'system')
|
|
@@ -113,9 +158,24 @@ const updateUI = (value) => {
|
|
|
113
158
|
|
|
114
159
|
The background, text, border, and icon colors in the XML come from semantic color classes (`bg-surface`, `bg-surface-high`, `text-on-surface`, `bg-border`, etc.) — see [semantic-colors.md](./semantic-colors.md) for how to wire those up so the whole view flips when `Appearance.set('dark')` fires.
|
|
115
160
|
|
|
116
|
-
|
|
161
|
+
> **Why named functions in Alloy controllers?** Alloy resolves `onClick="selectDark"` by looking up a top-level identifier on the controller scope. Named function declarations (`function selectDark() { ... }`) hoist and are reliably resolvable; arrow functions assigned to `const` are not hoisted and tend to bite you when an XML attribute fires before the binding has executed. The official setup guide uses named functions for this reason.
|
|
117
162
|
|
|
118
|
-
|
|
163
|
+
## API reference
|
|
164
|
+
|
|
165
|
+
### Methods
|
|
166
|
+
|
|
167
|
+
| Method | Description |
|
|
168
|
+
| ------------ | --------------------------------------------------------------------------- |
|
|
169
|
+
| `init()` | Restore the saved mode from `Ti.App.Properties` and apply it via `Ti.UI.overrideUserInterfaceStyle`. Call once at app startup before opening the first window. |
|
|
170
|
+
| `set(mode)` | Apply and persist a mode. Accepts `'system'`, `'light'`, or `'dark'`. Any other value is silently ignored. |
|
|
171
|
+
| `get()` | Returns the current mode as a string: `'system'`, `'light'`, or `'dark'`. |
|
|
172
|
+
| `toggle()` | Switch between `'light'` and `'dark'`. Skips `'system'` — if current mode is `'system'` the next call produces `'dark'`. |
|
|
173
|
+
|
|
174
|
+
All four methods are **synchronous**. They do not fire events; if you need to react to a mode change, call your update logic right after `Appearance.set(...)`.
|
|
175
|
+
|
|
176
|
+
### Lifecycle
|
|
177
|
+
|
|
178
|
+
How the four moving parts — `Appearance.init()`, `Ti.App.Properties`, `Ti.UI.overrideUserInterfaceStyle`, and semantic colors — cooperate during a typical session:
|
|
119
179
|
|
|
120
180
|
```
|
|
121
181
|
app startup
|
|
@@ -143,8 +203,6 @@ app startup
|
|
|
143
203
|
+-- Preference survives the next cold launch
|
|
144
204
|
```
|
|
145
205
|
|
|
146
|
-
`Ti.App.Properties` is the persistence layer — the value written during `set(...)` is what `init()` reads on the next launch. Clearing app storage or reinstalling resets to `'system'`.
|
|
147
|
-
|
|
148
206
|
> **INFO**
|
|
149
207
|
>
|
|
150
208
|
> `toggle()` intentionally skips `'system'`. If the current mode is `'system'`, calling `toggle()` produces `'dark'`. If you want a three-way cycle (System -> Light -> Dark -> System), build it in userland with `Appearance.get()` + `Appearance.set(...)`; do not expect `toggle()` to do it.
|
|
@@ -254,6 +254,66 @@ Window: { default: { apply: 'exit-on-close-false bg-blue-500' } }
|
|
|
254
254
|
|
|
255
255
|
Use the explicit `default:` wrapper when you also need platform blocks (`ios:`, `android:`) next to it. For the common case of one bundle of defaults, the shorthand reads better.
|
|
256
256
|
|
|
257
|
+
### Extend mode vs replace mode
|
|
258
|
+
|
|
259
|
+
> **HEADLINE CHANGE — v7.9.0**
|
|
260
|
+
> This is the headline change of PurgeTSS v7.9.0. Before v7.9.0, `theme.Window` / `theme.View` / `theme.ImageView` still had the framework defaults merged in, which caused gradient ghosting and other surprising overrides. v7.9.0 makes top-level (non-`extend`) configs behave as true replace mode.
|
|
261
|
+
>
|
|
262
|
+
> If you previously used `theme.Window` (no `extend`) and depended on the white background or the iOS `hires: true` / `Ti.UI.SIZE` defaults still being there, you will need to either move that config under `theme.extend.Window` (extend mode) or add the previously-implicit utilities back into your `apply` string.
|
|
263
|
+
|
|
264
|
+
PurgeTSS follows the Tailwind convention for the three Ti Elements that ship with framework defaults — `Window`, `View`, and `ImageView`:
|
|
265
|
+
|
|
266
|
+
- **Extend mode** — `theme.extend.Window`, `theme.extend.View`, `theme.extend.ImageView`. Your customization **merges** with the framework defaults. The white Window `backgroundColor`, `Ti.UI.SIZE` width/height on `View`, and iOS `hires: true` on `ImageView` stay in place unless you override them with `apply`. Use this when you want to add to the defaults, not replace them.
|
|
267
|
+
- **Replace mode** — `theme.Window`, `theme.View`, `theme.ImageView` (top level, no `extend`). Your config **replaces** the framework defaults entirely. The white Window background is omitted, the `Ti.UI.SIZE` width/height on `View` is omitted, and the iOS `hires: true` on `ImageView` is omitted. Your `apply` becomes the source of truth for that Ti Element.
|
|
268
|
+
|
|
269
|
+
Use replace mode when you want full control and do not want a preset mixed in. The canonical case is a Window declared at `theme.Window` with a `backgroundGradient`, where the previously-merged white `backgroundColor` would render on top of the gradient and produce a "ghost" white wash.
|
|
270
|
+
|
|
271
|
+
`./purgetss/config.cjs - replace mode (v7.9.0+)`
|
|
272
|
+
```javascript
|
|
273
|
+
module.exports = {
|
|
274
|
+
theme: {
|
|
275
|
+
Window: {
|
|
276
|
+
apply: 'bg-gradient-to-b from-blue-500 to-purple-600'
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
`./purgetss/styles/utilities.tss`
|
|
283
|
+
```tss
|
|
284
|
+
'Window': { backgroundGradient: { type: 'linear', colors: [...], startPoint: ..., endPoint: ... } }
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Note the missing `backgroundColor: '#FFFFFF'`. Replace mode skipped the framework default, so the gradient renders cleanly. Before v7.9.0 the same config produced:
|
|
288
|
+
|
|
289
|
+
```tss
|
|
290
|
+
/* Pre-v7.9.0 — ghost white background covering the gradient */
|
|
291
|
+
'Window': { backgroundColor: '#FFFFFF', backgroundGradient: { type: 'linear', colors: [...] } }
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
If you wanted the framework white background **and** something else on top, that is what extend mode is for:
|
|
295
|
+
|
|
296
|
+
`./purgetss/config.cjs - extend mode`
|
|
297
|
+
```javascript
|
|
298
|
+
module.exports = {
|
|
299
|
+
theme: {
|
|
300
|
+
extend: {
|
|
301
|
+
Window: {
|
|
302
|
+
apply: 'exit-on-close-false'
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
`./purgetss/styles/utilities.tss`
|
|
310
|
+
```tss
|
|
311
|
+
/* Framework default backgroundColor is preserved, exitOnClose merged in */
|
|
312
|
+
'Window': { backgroundColor: '#FFFFFF', exitOnClose: false }
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
The same extend-vs-replace distinction applies to `View` (`Ti.UI.SIZE` width/height defaults) and `ImageView` (iOS `hires: true` default).
|
|
316
|
+
|
|
257
317
|
### Apply Wins Over Static Defaults
|
|
258
318
|
|
|
259
319
|
If `apply` sets a property that the component already has as a built-in default, the applied value replaces the original instead of both ending up in the final TSS:
|
|
@@ -7,6 +7,47 @@ When you need a one-off value that is not in the defaults, use arbitrary values
|
|
|
7
7
|
>
|
|
8
8
|
> You cannot use square bracket notation like in Tailwind because Titanium handles platform and conditional statements in `.tss` files differently.
|
|
9
9
|
|
|
10
|
+
## Class syntax pre-validation
|
|
11
|
+
|
|
12
|
+
Starting with PurgeTSS v7.8.0, the build runs a pre-validation pass over class names found in XML views and JS controllers before purging. When it spots a malformed arbitrary-value utility, it stops and prints a structured `Class Syntax Error` block so the offending class can be fixed before any TSS is generated. If multiple errors are present, all of them are reported in the same run.
|
|
13
|
+
|
|
14
|
+
Each error block includes:
|
|
15
|
+
|
|
16
|
+
- The file path where the offending class lives
|
|
17
|
+
- The line number inside that file
|
|
18
|
+
- The offending class name (as authored)
|
|
19
|
+
- A `Fix:` suggestion with the corrected class name
|
|
20
|
+
|
|
21
|
+
Example shape:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Class Syntax Error
|
|
25
|
+
File: app/views/index.xml
|
|
26
|
+
Line: 12
|
|
27
|
+
Found: top-(-10)
|
|
28
|
+
Fix: -top-(10)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Detected patterns
|
|
32
|
+
|
|
33
|
+
The validator catches five narrow, actionable mistakes:
|
|
34
|
+
|
|
35
|
+
| Pattern | Offending input | Suggested fix | Notes |
|
|
36
|
+
| ----------------------------- | --------------- | ------------- | ---------------------------------------------------------------- |
|
|
37
|
+
| Inverted negative sign | `top-(-10)` | `-top-(10)` | The `-` prefix goes before the rule, not inside the value |
|
|
38
|
+
| Tailwind-style brackets | `top-[10px]` | `top-(10px)` | PurgeTSS uses parentheses, not square brackets, for arbitrary values |
|
|
39
|
+
| Empty parentheses | `wh-()` | (flagged, no auto-fix) | Add a value such as `wh-(10)` |
|
|
40
|
+
| Whitespace inside parentheses | `wh-( 200 )` | `wh-(200)` | No spaces allowed between `(`, the value, and `)` |
|
|
41
|
+
| Redundant `px` unit | `top-(10px)` | `top-(10)` | PurgeTSS treats unit-less arbitrary values as pixels |
|
|
42
|
+
|
|
43
|
+
### Unknown classes are still silently dropped
|
|
44
|
+
|
|
45
|
+
The pre-validator only fires on the five patterns above. Any other unknown class — typos, custom utilities not yet declared, vendor classes that are not enabled in `config.cjs` — is **not** reported as a `Class Syntax Error`. Those classes continue to flow into the `// Unused or unsupported classes` comment block in `app.tss`, exactly like before. This keeps the validator focused on actionable mistakes and avoids noise while you sketch out class names.
|
|
46
|
+
|
|
47
|
+
### v7.8.0 parser fix for negatives inside parentheses
|
|
48
|
+
|
|
49
|
+
Before v7.8.0, an inverted negative such as `top-(-10)` could be silently misparsed by the arbitrary-value pipeline (the `-` inside the parentheses confused the token matcher, producing wrong or missing TSS output without any warning). v7.8.0 hardens that path: the parser now correctly recognizes `top-(-10)` as authored, classifies it as an inverted-negative-sign error, and surfaces the `Class Syntax Error` block with the `-top-(10)` fix instead of producing silent garbage.
|
|
50
|
+
|
|
10
51
|
## Color Properties
|
|
11
52
|
|
|
12
53
|
You can set arbitrary color values for all available color properties using `hex`, `rgb`, or `rgba` values, directly in XML files or in `config.cjs`.
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# PurgeTSS CLI Commands
|
|
2
2
|
|
|
3
|
-
> **Info: What's new in v7.5.3 / v7.6.x / v7.7.0**
|
|
3
|
+
> **Info: What's new in v7.5.3 / v7.6.x / v7.7.0 / v7.8.0 / v7.9.0**
|
|
4
|
+
> - **Opacity modifiers on semantic colors (v7.9.0)** — writing `bg-surface/65` (or any opacity modifier on a class mapped to a name in `semantic.colors.json`) now produces a working rule with Light/Dark switching preserved. PurgeTSS auto-derives a `<originalKey>_<alphaPercent>` entry in `semantic.colors.json` with the original `light`/`dark` hex values plus the requested alpha for both modes, then emits the rule against the derived key. Re-runs are idempotent; manual edits with conflicting values halt the build with a `Conflict` error. New alpha entries require one full Titanium build to be picked up — Liveview hot-reload alone does not refresh `semantic.colors.json`.
|
|
5
|
+
> - **Gradient + Window/View/ImageView preset fixes (v7.9.0)** — `theme.Window` / `theme.View` / `theme.ImageView` no longer leak the framework presets (white background, `Ti.UI.SIZE`, iOS `hires: true`) when defined at the top level (replace mode), so a Window declared at `theme.Window` with a `backgroundGradient` no longer ghosts on top of a default `backgroundColor: '#FFFFFF'`. `theme.extend.Window` keeps merging with the defaults as before. Also fixed: tonal palette inversion bug, position-dependent `from`/`to` color ordering after `sort()`, and `bg-gradient-to-X` direction silently dropped when combined with `from-X to-Y` colors.
|
|
6
|
+
> - **Glossary path renamed (v7.9.0, breaking)** — the user-facing glossary output path was renamed from `purgetss/experimental/tailwind-classes/` to `purgetss/glossary/tailwind-classes/`. Tooling or CI that reads from the old path needs updating on upgrade — no transition shim. The `--glossary` flag and command surface are unchanged.
|
|
7
|
+
> - **`images --width <n>` flag (v7.8.0)** — pins Android `mdpi` (= iPhone `@1x`) to a specific pixel width, with larger scales deriving as ×1.5, ×2, ×3, ×4 from that base. Use it for SVG sources from vector editors with disproportionate viewBoxes (Affinity, Illustrator). CLI-only; there is no matching `images:` config property because the right width is per-asset. See [`images` Command](#images-command).
|
|
8
|
+
> - **Class syntax pre-validation (v7.8.0)** — `purgetss` now stops with a structured `Class Syntax Error` block (file + line + suggested fix) when it detects known class-name mistakes: inverted negative sign (`top-(-10)` → `-top-(10)`), Tailwind-style brackets (`top-[10px]` → `top-(10px)`), empty parentheses (`wh-()`), whitespace inside parentheses (`wh-( 200 )`), and redundant `px` unit (`top-(10px)` → `top-(10)`). All offenders are reported in one run. Generic unknown classes still flow into the `// Unused or unsupported classes` block in `app.tss`.
|
|
9
|
+
> - **Negative-values-in-parens parser fix (v7.8.0)** — classes like `top-(-10)`, `mt-(-5)`, and `origin-(-10,-20)` no longer crash with `Cannot read properties of null (reading 'pop')`. The parser now extracts the `(...)` portion first, so a `-` inside the value does not break the split.
|
|
4
10
|
> - **`brand:` config grouped (v7.7.0)** — the flat `brand:` block from v7.6.0 was reorganized into purpose-based sections: `brand.logos`, `brand.padding`, `brand.android`, `brand.ios`, and `brand.colors`. Old projects keep working — newly-generated configs use the grouped form.
|
|
5
11
|
> - **Separate Android brand inputs (v7.7.0)** — `brand` can now use one logo for the general brand set, another for Android launcher icons (`logos.androidLauncher` / `--icon-logo`), and another for Android 12+ splash artwork (`logos.androidSplash` / `--splash-logo`). Drop `logo-icon.*` and `logo-splash.*` into `purgetss/brand/` or set the paths in `config.cjs`.
|
|
6
12
|
> - **Legacy Android splash fallback (v7.7.0)** — `purgetss brand` now regenerates `app/assets/android/default.png` (Alloy) or `Resources/android/default.png` (Classic). `cleanup-legacy` no longer removes `default.png`.
|
|
@@ -91,15 +97,14 @@ module.exports = {
|
|
|
91
97
|
splash: false, // also generate splash_icon.png × 5
|
|
92
98
|
notification: false // also generate ic_stat_notify.png × 5
|
|
93
99
|
},
|
|
100
|
+
ios: {
|
|
101
|
+
dark: true, // emit DefaultIcon-Dark.png (set false to skip)
|
|
102
|
+
tinted: true, // emit DefaultIcon-Tinted.png (set false to skip)
|
|
103
|
+
darkBackground: null // null = transparent per Apple HIG; set a hex like '#111111' for an opaque dark bg
|
|
104
|
+
},
|
|
94
105
|
colors: {
|
|
95
106
|
background: '#FFFFFF' // Android adaptive bg + iOS/marketplace flatten
|
|
96
107
|
},
|
|
97
|
-
// Optional iOS overrides:
|
|
98
|
-
// ios: {
|
|
99
|
-
// dark: false,
|
|
100
|
-
// tinted: false,
|
|
101
|
-
// darkBackground: '#111111'
|
|
102
|
-
// },
|
|
103
108
|
confirmOverwrites: true // prompt before overwriting files (set false to skip)
|
|
104
109
|
},
|
|
105
110
|
images: {
|
|
@@ -283,15 +288,14 @@ brand: {
|
|
|
283
288
|
splash: false, // also generate splash_icon.png × 5
|
|
284
289
|
notification: false // also generate ic_stat_notify.png × 5
|
|
285
290
|
},
|
|
291
|
+
ios: {
|
|
292
|
+
dark: true, // emit DefaultIcon-Dark.png (set false to skip)
|
|
293
|
+
tinted: true, // emit DefaultIcon-Tinted.png (set false to skip)
|
|
294
|
+
darkBackground: null // null = transparent per Apple HIG; set a hex like '#111' for an opaque dark bg
|
|
295
|
+
},
|
|
286
296
|
colors: {
|
|
287
297
|
background: '#FFFFFF' // Android adaptive bg + iOS/marketplace flatten
|
|
288
298
|
},
|
|
289
|
-
// Optional iOS overrides:
|
|
290
|
-
// ios: {
|
|
291
|
-
// dark: false, // skip DefaultIcon-Dark.png
|
|
292
|
-
// tinted: false, // skip DefaultIcon-Tinted.png
|
|
293
|
-
// darkBackground: '#111' // opaque dark bg for DefaultIcon-Dark.png
|
|
294
|
-
// },
|
|
295
299
|
confirmOverwrites: true // prompt before overwriting files
|
|
296
300
|
}
|
|
297
301
|
```
|
|
@@ -344,6 +348,7 @@ purgetss images background/ # re-process one subfolder
|
|
|
344
348
|
| `--ios` | Only emit iPhone scale variants. Mutually exclusive with `--android`. |
|
|
345
349
|
| `--format <ext>` | Convert all outputs to `webp`, `jpeg`, `png`, `avif`, `gif`, or `tiff`. Default: keep source format. |
|
|
346
350
|
| `--quality <n>` | Quality `0-100` for lossy formats. Default `85`. |
|
|
351
|
+
| `--width <n>` | (v7.8.0) Pin Android `mdpi` (= iPhone `@1x`) to `<n>` pixels wide. Larger scales derive as ×1.5, ×2, ×3, ×4 from that base; height stays proportional to the source's aspect ratio. Integer in `[1, 8192]`. |
|
|
347
352
|
| `--dry-run` | Preview without writing any files. |
|
|
348
353
|
| `--project <path>` | Project root (defaults to cwd). |
|
|
349
354
|
| `-y, --yes` | Skip the overwrite confirmation prompt. |
|
|
@@ -353,6 +358,12 @@ purgetss images background/ # re-process one subfolder
|
|
|
353
358
|
|
|
354
359
|
- `[source]` (optional) — path to override auto-discovery. Resolves first against `purgetss/images/` (short paths like `buttons/btn.png`), then against cwd.
|
|
355
360
|
|
|
361
|
+
### When to use `--width`
|
|
362
|
+
|
|
363
|
+
Use `--width <n>` for SVG sources from vector editors with disproportionate viewBoxes — common in Affinity, Illustrator, and other design tools where the viewBox does not match the intended display size. Without the flag, every scale derives from the source's natural pixel size as a 4× master, which can produce unexpected output sizes.
|
|
364
|
+
|
|
365
|
+
When you pass an SVG without `--width`, the command prints a one-time hint and falls back to the legacy 4× behavior. This is CLI-only; there is no matching `images:` config property because the right width is per-asset.
|
|
366
|
+
|
|
356
367
|
### Config block
|
|
357
368
|
|
|
358
369
|
Defaults live under `images:` in `purgetss/config.cjs`:
|
|
@@ -375,6 +386,7 @@ purgetss images background/pink-texture.png # re-process one file (sh
|
|
|
375
386
|
purgetss images background/ # re-process one subfolder
|
|
376
387
|
purgetss images --android # only Android densities
|
|
377
388
|
purgetss images --format webp --quality 90 # convert all outputs to WebP
|
|
389
|
+
purgetss images logo.svg --width 256 # pin SVG output to 256 px @1x/mdpi
|
|
378
390
|
purgetss images --dry-run # preview
|
|
379
391
|
```
|
|
380
392
|
|
|
@@ -412,6 +424,29 @@ purgetss semantic --single '#000000' overlayColor --alpha 50
|
|
|
412
424
|
|
|
413
425
|
When `--dark` is omitted, it defaults to the light hex — useful for overlays/glass surfaces where alpha is the only variation.
|
|
414
426
|
|
|
427
|
+
### Customizing the class name
|
|
428
|
+
|
|
429
|
+
The auto-mapping uses the most literal Titanium-style transform: strip `Color`, then kebab-case the rest (e.g. `surfaceColor` → `surface`, `textSecondaryColor` → `text-secondary`). If your design system prefers different names — for example `on-surface` instead of `text`, or nesting the surface family under `DEFAULT` / `high` — edit `config.cjs` after running the `--single` batch:
|
|
430
|
+
|
|
431
|
+
`./purgetss/config.cjs`
|
|
432
|
+
```javascript
|
|
433
|
+
theme: {
|
|
434
|
+
extend: {
|
|
435
|
+
colors: {
|
|
436
|
+
surface: { DEFAULT: 'surfaceColor', high: 'surfaceHighColor' },
|
|
437
|
+
'on-surface': 'textColor',
|
|
438
|
+
'on-surface-variant': 'textSecondaryColor',
|
|
439
|
+
muted: 'textMutedColor',
|
|
440
|
+
border: 'borderColor',
|
|
441
|
+
accent: 'accentColor',
|
|
442
|
+
overlay: 'overlayColor'
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
The next `purgetss build` picks up the renamed classes. Editing one generated mapping is faster than typing the whole structure from scratch. See [semantic-colors.md](./semantic-colors.md) for the full nested-vs-flat discussion (including the `[object Object]` pitfall when nesting without `DEFAULT`).
|
|
449
|
+
|
|
415
450
|
### Smart in-place updates
|
|
416
451
|
|
|
417
452
|
If a `--single` name matches an existing palette shade — e.g. `purgetss semantic --single '#000' amazon500` while palette `amazon` exists — PurgeTSS narrows the operation to an in-place JSON value edit. The entry stays in its original position, and `config.cjs` is left untouched (the palette already maps to that key).
|
|
@@ -482,10 +517,10 @@ After copying the fonts, you can use them in Buttons and Labels. For example, fo
|
|
|
482
517
|
|
|
483
518
|
### Available Font Classes
|
|
484
519
|
|
|
485
|
-
- [fontawesome.tss](https://github.com/macCesar/purgeTSS/blob/
|
|
486
|
-
- [materialicons.tss](https://github.com/macCesar/purgeTSS/blob/
|
|
487
|
-
- [materialsymbols.tss](https://github.com/macCesar/purgeTSS/blob/
|
|
488
|
-
- [framework7icons.tss](https://github.com/macCesar/purgeTSS/blob/
|
|
520
|
+
- [fontawesome.tss](https://github.com/macCesar/purgeTSS/blob/main/dist/fontawesome.tss)
|
|
521
|
+
- [materialicons.tss](https://github.com/macCesar/purgeTSS/blob/main/dist/materialicons.tss)
|
|
522
|
+
- [materialsymbols.tss](https://github.com/macCesar/purgeTSS/blob/main/dist/materialsymbols.tss)
|
|
523
|
+
- [framework7icons.tss](https://github.com/macCesar/purgeTSS/blob/main/dist/framework7icons.tss)
|
|
489
524
|
|
|
490
525
|
### Copying Specific Font Vendors
|
|
491
526
|
|
|
@@ -1,35 +1,49 @@
|
|
|
1
1
|
# Custom Rules
|
|
2
2
|
|
|
3
|
-
Custom rules in PurgeTSS let you style Titanium elements, IDs, and classes
|
|
3
|
+
Custom rules in PurgeTSS let you style Titanium elements, IDs, and classes from `config.cjs`. They are the bridge between the utility-first classes you write in XML and the platform-, device-, or condition-specific overrides Titanium projects often need. Because everything lives in a single `config.cjs`, projects that span iOS and Android can keep one source of truth for styling instead of fragmenting rules across multiple `.tss` files.
|
|
4
|
+
|
|
5
|
+
This file mirrors the official rewrite — section names, examples, and ordering match the upstream docs so cross-referencing stays cheap.
|
|
4
6
|
|
|
5
7
|
## Classes, IDs, and Ti Elements
|
|
6
8
|
|
|
7
|
-
Whether you want to style a Ti Element (
|
|
9
|
+
Whether you want to style a Ti Element (a markup element such as `Label`), a custom ID prefixed with `#`, or a custom class prefixed with `.`, the structure inside `config.cjs` is the same. You always declare a key (the selector) whose value is an object containing one or more block keys (`DEFAULT`, `ios`, `android`, `tablet`, `handheld`, or `[if=...]`).
|
|
8
10
|
|
|
9
11
|
### Modifier Key
|
|
10
12
|
|
|
11
|
-
- For Titanium elements, use the exact name of the element, such as `Label`, `Button`, or `ScrollView`.
|
|
12
|
-
- For IDs, use `camelCase` to match
|
|
13
|
-
- For classes, use `kebab-case` to stay compatible with PurgeTSS v6.x and above.
|
|
14
|
-
|
|
15
|
-
> **CAUTION
|
|
16
|
-
>
|
|
13
|
+
- For Titanium elements, use the **exact name** of the element, such as `Label`, `Button`, or `ScrollView`. The casing must match Titanium's API (e.g. `TextField`, not `textfield` or `Text Field`).
|
|
14
|
+
- For IDs, use `camelCase` to match Alloy's JavaScript convention (e.g. `mainBanner` referenced in XML as `id="mainBanner"`). The selector key inside `config.cjs` includes the `#` prefix.
|
|
15
|
+
- For classes, use `kebab-case` to stay compatible with PurgeTSS v6.x and above. Use `.my-custom-class-name`, not `.myCustomClassName`. The kebab-case requirement is what most legacy projects trip over when they upgrade.
|
|
16
|
+
|
|
17
|
+
> **CAUTION — Migrating from PurgeTSS v4 or v5 to v7+**
|
|
18
|
+
>
|
|
19
|
+
> If your project started on PurgeTSS v5 or earlier and you are now on 7.x.x or later, the class-naming convention changed (`camelCase` -> `kebab-case`) and the config moved to ESM-style `config.cjs`. The recommended upgrade path is:
|
|
20
|
+
>
|
|
21
|
+
> 1. **Audit current usage.** Set `purge.options.missing` to `true` in `config.cjs`. PurgeTSS will append a list of missing classes at the end of `app.tss` after the next compile — every entry is a class name you still need to migrate.
|
|
22
|
+
> 2. **Rename selectors.** Convert every `.myCustomClassName` to `.my-custom-class-name` in both XML markup and `config.cjs`. The old camelCase names will not match anymore.
|
|
23
|
+
> 3. **Convert config to `.cjs`.** If your project still uses `purgetss.config.js` or a CommonJS-style file with the old layout, rename the file to `config.cjs` and ensure it sits in `./purgetss/`. The single export must be `module.exports = { theme: { ... } }`.
|
|
24
|
+
> 4. **Re-run `purgetss build`.** Verify the missing-classes list at the end of `app.tss` is empty before turning `missing` back off.
|
|
25
|
+
>
|
|
26
|
+
> Skipping any of these steps tends to surface as silently dropped styles at runtime — the app compiles, but selectors no longer match.
|
|
17
27
|
|
|
18
28
|
### Default, Platform, Device, or Conditional Blocks
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
30
|
+
Inside any selector you can declare one or more of the following block keys. They translate to TSS query suffixes at compile time:
|
|
31
|
+
|
|
32
|
+
- **Global style**: use either the lowercase `default` or the uppercase `DEFAULT` keyword. This becomes the bare selector with no platform/device qualifier.
|
|
33
|
+
- **Specific platform**: use `ios` or `android`. These compile to `[platform=ios]` and `[platform=android]` query suffixes.
|
|
34
|
+
- **Specific device**: use `tablet` or `handheld`. These compile to `[formFactor=tablet]` and `[formFactor=handheld]`.
|
|
35
|
+
- **Conditional via global variable**: use `[if=globalVariableName]`. The exact key is preserved in the generated TSS, so you can reference any `Alloy.Globals.*` boolean.
|
|
24
36
|
|
|
25
37
|
### Property Values
|
|
26
38
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- For `
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
- `
|
|
39
|
+
The way you write property values determines how PurgeTSS emits them in the generated TSS. Pay particular attention to quoting — it is the single most common source of confusion.
|
|
40
|
+
|
|
41
|
+
- For `Titanium` constants, `Alloy` configuration values, or global variables, **always enclose them in quotes** in `config.cjs` (e.g. `'Ti.UI.SIZE'`, `'Alloy.CFG.iPhoneXNotchSize'`). The quotes are stripped in the generated TSS so the constant resolves at runtime — without them, JavaScript would try to evaluate the constant inside `config.cjs`, which fails because Titanium's globals are not available there.
|
|
42
|
+
- For `color` values, you can use `hex`, `8-digit hex` (with alpha), `rgb(R,G,B)`, `rgba(R,G,B,A)`, `transparent`, or any standard color name. Prefer hex values when you plan to use opacity modifiers — `rgba()` and named colors interact awkwardly with opacity utilities.
|
|
43
|
+
- For `spacing` values you can mix unit types. Each unit follows a specific conversion rule:
|
|
44
|
+
- `%`, `px`, `cm`, and `in` are passed through to TSS without conversion.
|
|
45
|
+
- `em` and `rem` values are converted with the formula `value * 16` (so `1rem` becomes `16`).
|
|
46
|
+
- `dp` removes the unit and keeps the numeric value as-is, since Titanium treats unit-less numeric values as density-independent pixels by default.
|
|
33
47
|
|
|
34
48
|
## `config.cjs` File Example
|
|
35
49
|
|
|
@@ -81,7 +95,10 @@ module.exports = {
|
|
|
81
95
|
};
|
|
82
96
|
```
|
|
83
97
|
|
|
84
|
-
`./purgetss/styles/utilities.tss`
|
|
98
|
+
## Custom `./purgetss/styles/utilities.tss` File
|
|
99
|
+
|
|
100
|
+
PurgeTSS reads `config.cjs` and emits a single `utilities.tss` file that Alloy then merges with the rest of the project's styling. The file below shows what the example above generates — note how block keys translate into TSS query suffixes (`[platform=android]`, `[formFactor=tablet]`, `[if=...]`).
|
|
101
|
+
|
|
85
102
|
```tss
|
|
86
103
|
/* Property: TextField */
|
|
87
104
|
/* Description: A single line text field. */
|
|
@@ -35,15 +35,7 @@ module.exports = {
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
brand: {
|
|
38
|
-
logos: {
|
|
39
|
-
// Optional overrides. If omitted, PurgeTSS auto-discovers files from purgetss/brand/:
|
|
40
|
-
// primary: './docs/logo.svg',
|
|
41
|
-
// androidLauncher: './docs/app-icon.svg',
|
|
42
|
-
// androidSplash: './docs/splash.svg',
|
|
43
|
-
// monochrome: './docs/logo-mono.svg',
|
|
44
|
-
// iosDark: './docs/logo-dark.svg',
|
|
45
|
-
// iosTinted: './docs/logo-tinted.svg'
|
|
46
|
-
},
|
|
38
|
+
logos: {}, // empty = auto-discovers from purgetss/brand/
|
|
47
39
|
padding: {
|
|
48
40
|
ios: '4%', // iOS aesthetic padding. Range 2-8%.
|
|
49
41
|
androidLegacy: '10%', // legacy ic_launcher.png padding %
|
|
@@ -53,15 +45,14 @@ module.exports = {
|
|
|
53
45
|
splash: false, // also generate splash_icon.png × 5
|
|
54
46
|
notification: false // also generate ic_stat_notify.png × 5
|
|
55
47
|
},
|
|
48
|
+
ios: {
|
|
49
|
+
dark: true, // generate DefaultIcon-Dark.png
|
|
50
|
+
tinted: true, // generate DefaultIcon-Tinted.png
|
|
51
|
+
darkBackground: null // null = transparent per Apple HIG
|
|
52
|
+
},
|
|
56
53
|
colors: {
|
|
57
54
|
background: '#FFFFFF' // Android adaptive bg + iOS/marketplace flatten
|
|
58
55
|
},
|
|
59
|
-
// Optional iOS overrides:
|
|
60
|
-
// ios: {
|
|
61
|
-
// dark: false, // skip DefaultIcon-Dark.png
|
|
62
|
-
// tinted: false, // skip DefaultIcon-Tinted.png
|
|
63
|
-
// darkBackground: '#111' // opaque dark bg for DefaultIcon-Dark.png (null = transparent per Apple HIG)
|
|
64
|
-
// },
|
|
65
56
|
confirmOverwrites: true // prompt before overwriting files (set false to skip)
|
|
66
57
|
},
|
|
67
58
|
images: {
|
|
@@ -85,6 +76,37 @@ The config file has four main sections: `purge`, `brand`, `images`, and `theme`.
|
|
|
85
76
|
|
|
86
77
|
`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`.
|
|
87
78
|
|
|
79
|
+
For `brand`, the structure is grouped by purpose (introduced in v7.7.0, refined since):
|
|
80
|
+
|
|
81
|
+
- `logos`: optional path overrides when you do not want to rely on `purgetss/brand/` auto-discovery
|
|
82
|
+
- `padding`: visual sizing for iOS, Android legacy, and Android adaptive icons
|
|
83
|
+
- `android`: Android-only optional outputs such as `splash_icon.png` and notification icons
|
|
84
|
+
- `ios`: iOS-only variant toggles (`dark`, `tinted`) and the optional `darkBackground` color
|
|
85
|
+
- `colors`: shared color settings such as the adaptive background and iOS flatten color
|
|
86
|
+
|
|
87
|
+
For the property-by-property reference, see [Configurable Properties](./configurable-properties.md).
|
|
88
|
+
|
|
89
|
+
### Overriding logo paths
|
|
90
|
+
|
|
91
|
+
By default, PurgeTSS auto-discovers logo files from `purgetss/brand/`. If you want to use custom paths, add them to `brand.logos`:
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
module.exports = {
|
|
95
|
+
brand: {
|
|
96
|
+
logos: {
|
|
97
|
+
primary: './my-logos/main.svg', // overrides auto-discovered logo.svg
|
|
98
|
+
androidLauncher: './my-logos/icon.svg', // overrides auto-discovered logo-icon.svg
|
|
99
|
+
androidSplash: './my-logos/splash.svg', // overrides auto-discovered logo-splash.svg
|
|
100
|
+
monochrome: './my-logos/mono.svg', // overrides auto-discovered logo-mono.svg
|
|
101
|
+
iosDark: './my-logos/dark.svg', // overrides auto-discovered logo-dark.svg
|
|
102
|
+
iosTinted: './my-logos/tinted.svg' // overrides auto-discovered logo-tinted.svg
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
You only need to override the ones you're using. Missing overrides still auto-discover from `purgetss/brand/`.
|
|
109
|
+
|
|
88
110
|
### `purge` Section
|
|
89
111
|
|
|
90
112
|
The `purge` section controls how PurgeTSS removes unused classes or keeps the ones you want.
|
|
@@ -23,6 +23,9 @@ The snippet below shows the simplest layout. From there, you can mix columns and
|
|
|
23
23
|
</Alloy>
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
> **Visual Reference**
|
|
27
|
+
> The official PurgeTSS docs ship a screenshot at `images/grid-system-example.png` (in the docs source repo) that visualizes how columns, rows, and spans render in a real Alloy view. Consult it when you need a quick mental model of the layout primitives — it is not embedded here, only referenced.
|
|
28
|
+
|
|
26
29
|
## Column Grid
|
|
27
30
|
|
|
28
31
|
### `grid-cols-{n}`
|
|
@@ -49,21 +52,7 @@ If a view uses `.row-span-3`, you can add three more views of the same height to
|
|
|
49
52
|
|
|
50
53
|
## Available Utilities
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
### Gutter Utilities
|
|
55
|
-
|
|
56
|
-
- `gap-{size}`: Change the gap between rows and columns.
|
|
57
|
-
- `gap-x-{size}` and `gap-y-{size}`: Change the gap between rows and columns independently.
|
|
58
|
-
- `gap-{side}-{size}`: Change the gap on a specific side (t=top, r=right, b=bottom, l=left).
|
|
59
|
-
|
|
60
|
-
### Column Span Utilities
|
|
61
|
-
|
|
62
|
-
- `col-span-{n}`: Make an element span `n` columns.
|
|
63
|
-
|
|
64
|
-
### Row Span Utilities
|
|
65
|
-
|
|
66
|
-
- `row-span-{n}`: Make an element span `n` rows.
|
|
55
|
+
The official source organizes the grid utilities into these categories. The structure below mirrors the official rewrite so you can map directly between this reference and the upstream docs.
|
|
67
56
|
|
|
68
57
|
### Direction Utilities
|
|
69
58
|
|
|
@@ -78,18 +67,48 @@ These are the available utilities to control the grid.
|
|
|
78
67
|
|
|
79
68
|
- `grid-rows-{n}`: Create grids with `n` equally sized rows.
|
|
80
69
|
|
|
70
|
+
### Column Span Utilities
|
|
71
|
+
|
|
72
|
+
- `col-span-{n}`: Make an element span `n` columns inside a 12-column grid.
|
|
73
|
+
|
|
74
|
+
### Row Span Utilities
|
|
75
|
+
|
|
76
|
+
- `row-span-{n}`: Make an element span `n` rows inside a 12-row grid.
|
|
77
|
+
|
|
78
|
+
### Gutter Utilities
|
|
79
|
+
|
|
80
|
+
- `gap-{size}`: Change the gap between rows and columns.
|
|
81
|
+
- `gap-x-{size}` and `gap-y-{size}`: Change the gap between rows and columns independently.
|
|
82
|
+
- `gap-{side}-{size}`: Change the gap on a specific side (`t`=top, `r`=right, `b`=bottom, `l`=left).
|
|
83
|
+
|
|
81
84
|
### Row Placement Utilities
|
|
82
85
|
|
|
83
|
-
Control horizontal
|
|
86
|
+
Control horizontal placement of children inside a row:
|
|
84
87
|
|
|
85
|
-
| Class
|
|
86
|
-
|
|
|
87
|
-
| `start`
|
|
88
|
-
| `end`
|
|
89
|
-
| `center` | Align to the center of the row
|
|
88
|
+
| Class | Effect |
|
|
89
|
+
| -------- | --------------------------------- |
|
|
90
|
+
| `start` | Align to the start of the row |
|
|
91
|
+
| `end` | Align to the end of the row |
|
|
92
|
+
| `center` | Align to the center of the row |
|
|
90
93
|
|
|
91
94
|
These apply to child views inside a `grid-cols-*` container and control horizontal placement within the grid cell.
|
|
92
95
|
|
|
96
|
+
## Row Placement Use Cases
|
|
97
|
+
|
|
98
|
+
Combining `row-span-{n}` with `grid-rows-{n}` lets you describe how an element occupies vertical space inside a grid column. The table below covers the most common combinations a Titanium UI tends to need. Every entry is verified against the `grid-rows-{n}` and `row-span-{n}` utilities described in the official source.
|
|
99
|
+
|
|
100
|
+
| Pattern | Example classes | Result |
|
|
101
|
+
| ---------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
102
|
+
| Full-height column item | `grid-rows-1` + `row-span-1` | One element fills the entire vertical space of the column. |
|
|
103
|
+
| Two equal stacked items | `grid-rows-2` + two children no span | Two views stack vertically, each taking 50% of the column height. |
|
|
104
|
+
| Tall hero on top, short footer at bottom | `grid-rows-12` + `row-span-9`, `row-span-3` | Hero takes 75% of the column height, footer takes 25%. |
|
|
105
|
+
| 3-6-3 vertical split | `grid-rows-12` + `row-span-3`, `row-span-6`, `row-span-3` | Header / body / footer split. Mirrors the 3-6-3 horizontal split shown for `col-span-{n}`. |
|
|
106
|
+
| Sidebar that spans every row | `grid-rows-4` + child with `row-span-4` | Element occupies the full height of a 4-row column. Useful for vertical separators or full-height nav. |
|
|
107
|
+
| Two short rows above a tall row | `grid-rows-12` + `row-span-3`, `row-span-3`, `row-span-6` | Two preview cards above a larger detail panel. |
|
|
108
|
+
|
|
109
|
+
> **Note**
|
|
110
|
+
> Just like `col-span-{n}`, the spans you write must add up to the value declared in `grid-rows-{n}` for the layout to fill the column without leftover space.
|
|
111
|
+
|
|
93
112
|
## Community-Discovered Patterns
|
|
94
113
|
|
|
95
114
|
The following notes come from community experience using the PurgeTSS grid system in Titanium projects. They clarify how the grid relates to Titanium's layout engine (not CSS Grid) and offer common use cases for row placement.
|
|
@@ -99,8 +118,8 @@ The following notes come from community experience using the PurgeTSS grid syste
|
|
|
99
118
|
|
|
100
119
|
### Common Use Cases for Row Placement
|
|
101
120
|
|
|
102
|
-
| Class
|
|
103
|
-
|
|
|
104
|
-
| `start`
|
|
105
|
-
| `end`
|
|
106
|
-
| `center` | Centered content blocks
|
|
121
|
+
| Class | Use case |
|
|
122
|
+
| -------- | --------------------------------- |
|
|
123
|
+
| `start` | Left-aligned content |
|
|
124
|
+
| `end` | Right-aligned buttons or labels |
|
|
125
|
+
| `center` | Centered content blocks |
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
# Icon Font Libraries
|
|
2
2
|
|
|
3
|
-
> **Official Icon Fonts for PurgeTSS**
|
|
4
|
-
> Previous versions of PurgeTSS included several icon font libraries such as Bootstrap Icons, Boxicons, LineIcons, and Tabler Icons. The list was reduced to keep maintenance manageable.
|
|
3
|
+
> ℹ️ **INFO — Official Icon Fonts for PurgeTSS**
|
|
5
4
|
>
|
|
6
|
-
>
|
|
5
|
+
> Older versions of PurgeTSS bundled additional icon font libraries — Bootstrap Icons, Boxicons, LineIcons, and Tabler Icons among them. **Those libraries have been deprecated and removed from the official distribution** to keep maintenance under control. They are still usable, but you must rebuild them yourself with `build-fonts` (see below).
|
|
6
|
+
>
|
|
7
|
+
> The icon fonts officially supported and shipped by PurgeTSS today are:
|
|
7
8
|
>
|
|
8
9
|
> - [Font Awesome 7 Free](https://fontawesome.com) (upgrade with `purgetss il -v=fa`)
|
|
9
|
-
> - [
|
|
10
|
+
> - [Framework 7](https://framework7.io/icons/)
|
|
10
11
|
> - [Material Icons](https://fonts.google.com/icons?icon.set=Material+Icons)
|
|
11
12
|
> - [Material Symbols](https://fonts.google.com/icons?icon.set=Material+Symbols)
|
|
12
13
|
|
|
13
|
-
For the official install flow for
|
|
14
|
+
For the official install flow for the supported vendors, see [CLI Commands](./cli-commands.md#icon-library-command).
|
|
14
15
|
|
|
15
16
|
## Recreate Removed Libraries
|
|
16
17
|
|
|
17
|
-
You can recreate
|
|
18
|
+
You can recreate any deprecated library using the `build-fonts` command.
|
|
18
19
|
|
|
19
20
|
### 1. Download the Libraries
|
|
20
21
|
|
|
@@ -29,7 +30,7 @@ Start by downloading the libraries from their official websites:
|
|
|
29
30
|
|
|
30
31
|
Put the desired libraries in the `./purgetss/fonts` folder.
|
|
31
32
|
|
|
32
|
-
> **INFO**
|
|
33
|
+
> ℹ️ **INFO**
|
|
33
34
|
> Copy the TrueType or OpenType font files and the `.css` file.
|
|
34
35
|
|
|
35
36
|
```bash
|
|
@@ -67,14 +68,14 @@ The `build-fonts` command generates `./purgetss/styles/fonts.tss` with Unicode c
|
|
|
67
68
|
/* To use your Icon Fonts in Buttons AND Labels each class sets 'text' and 'title' properties */
|
|
68
69
|
|
|
69
70
|
/* boxicons.css */
|
|
70
|
-
'.bxl-meta': { text: '
|
|
71
|
-
'.bx-lemon': { text: '
|
|
72
|
-
|
|
71
|
+
'.bxl-meta': { text: '', title: '' }
|
|
72
|
+
'.bx-lemon': { text: '', title: '' }
|
|
73
|
+
/* ... */
|
|
73
74
|
|
|
74
75
|
/* lineicons.css */
|
|
75
|
-
'.lni-500px': { text: '
|
|
76
|
-
'.lni-add-files': { text: '
|
|
77
|
-
|
|
76
|
+
'.lni-500px': { text: '', title: '' }
|
|
77
|
+
'.lni-add-files': { text: '', title: '' }
|
|
78
|
+
/* ... */
|
|
78
79
|
```
|
|
79
80
|
|
|
80
81
|
### Rename the Style Rule Name
|
|
@@ -116,15 +117,15 @@ When you use the `--modules` option, it generates a `./app/lib/purgetss.fonts.js
|
|
|
116
117
|
const icons = {
|
|
117
118
|
/* boxicons */
|
|
118
119
|
boxicons: {
|
|
119
|
-
bxlMeta: '
|
|
120
|
-
bxLemon: '
|
|
121
|
-
|
|
120
|
+
bxlMeta: '',
|
|
121
|
+
bxLemon: ''
|
|
122
|
+
/* ... */
|
|
122
123
|
},
|
|
123
124
|
/* lineicons */
|
|
124
125
|
lni: {
|
|
125
|
-
'500px': '
|
|
126
|
-
addFiles: '
|
|
127
|
-
|
|
126
|
+
'500px': '',
|
|
127
|
+
addFiles: ''
|
|
128
|
+
/* ... */
|
|
128
129
|
}
|
|
129
130
|
};
|
|
130
131
|
exports.icons = icons;
|
|
@@ -147,9 +148,9 @@ New group prefix: `li`
|
|
|
147
148
|
`./purgetss/styles/fonts.tss`
|
|
148
149
|
```tss
|
|
149
150
|
/* lineicons/li.css */
|
|
150
|
-
'.li-zoom-out': { text: '
|
|
151
|
-
'.li-zoom-in': { text: '
|
|
152
|
-
|
|
151
|
+
'.li-zoom-out': { text: '', title: '' }
|
|
152
|
+
'.li-zoom-in': { text: '', title: '' }
|
|
153
|
+
/* ... */
|
|
153
154
|
```
|
|
154
155
|
|
|
155
156
|
`./app/lib/purgetss.fonts.js`
|
|
@@ -163,12 +164,13 @@ const icons = {
|
|
|
163
164
|
exports.icons = icons;
|
|
164
165
|
```
|
|
165
166
|
|
|
166
|
-
> **DANGER**
|
|
167
|
-
>
|
|
167
|
+
> 🛑 **DANGER**
|
|
168
|
+
>
|
|
169
|
+
> Make sure the new prefix stays unique so it does not collide with other class prefixes. A duplicate prefix will silently overwrite earlier rules in the generated `fonts.tss`, leaving you with icons that render the wrong glyph at runtime.
|
|
168
170
|
|
|
169
171
|
## Community-Discovered Patterns
|
|
170
172
|
|
|
171
173
|
The following note reflects community experience working with icon fonts that depend on multiple glyphs per icon.
|
|
172
174
|
|
|
173
|
-
> **Font Awesome Duotone**
|
|
175
|
+
> 🛑 **DANGER — Font Awesome Duotone**
|
|
174
176
|
> Titanium cannot render Font Awesome duotone icons correctly because each icon uses two glyphs. If you work with Font Awesome Pro, avoid documenting duotone as supported.
|
|
@@ -14,9 +14,35 @@ Install PurgeTSS globally on your machine using [NPM](https://www.npmjs.com/).
|
|
|
14
14
|
|
|
15
15
|
## XML Validation
|
|
16
16
|
|
|
17
|
-
PurgeTSS
|
|
17
|
+
Before purging, PurgeTSS pre-checks every XML file in your project. One case worth calling out: double dashes (`--`) are not allowed inside XML comments. That restriction comes from the XML spec itself, not from PurgeTSS, but many people only run into it once a tool actually parses the file.
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
### Bad comment example
|
|
20
|
+
|
|
21
|
+
```xml
|
|
22
|
+
<!-- Options: --flag or --value -->
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The `--flag` inside that comment is illegal. PurgeTSS stops with a pointer to the offending line:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
XML comment contains illegal "--" sequence ("--flag")
|
|
29
|
+
Fix: Replace "--" with an em dash or reword the comment to avoid double dashes
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Why it fails
|
|
33
|
+
|
|
34
|
+
Per the XML 1.0 specification, the two-character sequence `--` is reserved as the close-comment indicator (`-->`). It cannot appear anywhere inside a comment body, even mid-string. Any conforming XML parser will reject the document, so catching it up front is more helpful than a confusing TSS output later.
|
|
35
|
+
|
|
36
|
+
### How to fix
|
|
37
|
+
|
|
38
|
+
Pick whichever of these reads best:
|
|
39
|
+
|
|
40
|
+
- Replace `--` with a single Unicode em dash (`—`) so the sequence is no longer two ASCII hyphens.
|
|
41
|
+
- Spell out the dashes (for example, write `dash-dash flag` instead of `--flag`).
|
|
42
|
+
- Move the option list outside the comment — for instance, into a sibling `<!-- Options -->` header followed by documentation in your README or `config.cjs` notes.
|
|
43
|
+
- Rephrase so the two dashes simply do not sit next to each other.
|
|
44
|
+
|
|
45
|
+
Any of those approaches keeps the comment readable and produces a document that every XML parser will accept.
|
|
20
46
|
|
|
21
47
|
## Run PurgeTSS the First Time
|
|
22
48
|
|
|
@@ -2,13 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
Enabling Large Titles on a Window is not a single-property change. When you pair `largeTitleEnabled` with a `ScrollView` inside a `NavigationWindow` or `TabGroup`, **three interdependent iOS Window properties** must work together — otherwise you get either content hidden behind the nav bar or a visible rendering delay when the window opens.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
## The problem
|
|
6
|
+
|
|
7
|
+
Setting `largeTitleEnabled: true` in isolation looks like it should be enough — Apple's docs suggest a single switch. In practice, omitting the two companion properties produces three distinct visual bugs that are easy to attribute to "iOS being weird" but really come from the layout engine not having enough information:
|
|
8
|
+
|
|
9
|
+
1. **Content overlaps behind the nav bar.** The ScrollView starts at `y=0`, hidden under the translucent navigation bar. Users see the top of the list cut off and assume content is missing. This happens when `largeTitleEnabled` is set but `autoAdjustScrollViewInsets` is not.
|
|
10
|
+
2. **The large title flashes / renders with a delay.** On window open, the nav bar region paints empty for a frame or two, then the title pops in. With all three properties present, iOS has the layout it needs *before* the first paint, so the title is there from frame zero. Without `extendEdges`, there is a measurable shadow flash as the system recomputes the layout.
|
|
11
|
+
3. **Collapse glitches on scroll.** The large title shrinks to standard size as the user scrolls down — that is the whole point. But if `extendEdges` is missing, the collapse animation can stutter or skip frames because iOS has to recompute the safe-area inset for every scroll event instead of treating the nav bar as part of the content area.
|
|
12
|
+
|
|
13
|
+
The fix is not to pick the "right" property. It is to set all three together.
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
## The solution: 3 interdependent properties
|
|
16
|
+
|
|
17
|
+
| Property | Value | What it does |
|
|
18
|
+
| ---------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| `autoAdjustScrollViewInsets` | `true` | iOS automatically adjusts the ScrollView content insets so content starts below the nav bar instead of behind it. Without this, the ScrollView's first item sits hidden behind the translucent nav bar. |
|
|
20
|
+
| `extendEdges` | `[Ti.UI.EXTEND_EDGE_ALL]` | Content extends under the nav and tab bars, producing the translucent blur/refraction effect Large Titles depend on. Skipping this causes the shadow flash on open and stutters the collapse animation. |
|
|
21
|
+
| `largeTitleEnabled` | `true` | Shows the oversized title in the navigation bar that collapses to standard size as the user scrolls. This is the master switch — the other two are required for it to render correctly. |
|
|
22
|
+
|
|
23
|
+
Without `autoAdjustScrollViewInsets`, `extendEdges` pushes the content behind the nav bar without compensating the ScrollView insets. Without `extendEdges` + `autoAdjustScrollViewInsets`, using only `largeTitleEnabled` causes the large title to render with a visible delay: the empty nav bar area appears first, then the title draws. With all three properties, iOS calculates the layout before displaying the window.
|
|
12
24
|
|
|
13
25
|
## iOS-only — use the `ios:` modifier
|
|
14
26
|
|
|
@@ -39,7 +51,7 @@ Generated style in `./purgetss/styles/utilities.tss`:
|
|
|
39
51
|
|
|
40
52
|
Individual windows now only need to override `largeTitleDisplayMode` when they want different collapse behavior (e.g. detail windows).
|
|
41
53
|
|
|
42
|
-
> **Why an `ios:` block and not an inline `ios:` prefix?** `auto-adjust-scroll-view-insets` and `extend-edges-all` are platform-specific classes — they only exist with `[platform=ios]` suffix in `utilities.tss`. The `ios:` block ensures PurgeTSS resolves the platform-suffixed version. See [apply-directive.md](apply-directive.md) → "Platform-Specific Classes".
|
|
54
|
+
> **Why an `ios:` block and not an inline `ios:` prefix?** `auto-adjust-scroll-view-insets` and `extend-edges-all` are platform-specific classes — they only exist with `[platform=ios]` suffix in `utilities.tss`. The `ios:` block ensures PurgeTSS resolves the platform-suffixed version. See [apply-directive.md](./apply-directive.md) → "Platform-Specific Classes".
|
|
43
55
|
|
|
44
56
|
## NavigationWindow example
|
|
45
57
|
|
|
@@ -77,15 +89,15 @@ On iOS, `TabGroup` wraps each `Tab`'s Window in an **implicit NavigationWindow**
|
|
|
77
89
|
</Alloy>
|
|
78
90
|
```
|
|
79
91
|
|
|
80
|
-
## Controlling
|
|
92
|
+
## Controlling large title display
|
|
81
93
|
|
|
82
|
-
`largeTitleDisplayMode` controls how the title behaves in the navigation stack
|
|
94
|
+
`largeTitleDisplayMode` controls how the title behaves in the navigation stack — whether it stays large, always shrinks to standard size, or follows the previous window. Combine the three base properties (which make Large Titles render correctly at all) with `largeTitleDisplayMode` per window to get the per-screen behavior you want.
|
|
83
95
|
|
|
84
|
-
| Mode
|
|
85
|
-
|
|
|
86
|
-
| Automatic | `Ti.UI.iOS.LARGE_TITLE_DISPLAY_MODE_AUTOMATIC` | Inherits from previous window
|
|
87
|
-
| Always
|
|
88
|
-
| Never
|
|
96
|
+
| Mode | Constant | Behavior |
|
|
97
|
+
| --------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
98
|
+
| Automatic | `Ti.UI.iOS.LARGE_TITLE_DISPLAY_MODE_AUTOMATIC` | Inherits the display mode from the previous window in the navigation stack and collapses on scroll. The default. |
|
|
99
|
+
| Always | `Ti.UI.iOS.LARGE_TITLE_DISPLAY_MODE_ALWAYS` | Title stays large regardless of scroll position. Useful for top-level screens that should never collapse. |
|
|
100
|
+
| Never | `Ti.UI.iOS.LARGE_TITLE_DISPLAY_MODE_NEVER` | Always uses the standard (small) title size. The right choice for detail windows pushed via `openWindow()`. |
|
|
89
101
|
|
|
90
102
|
### Detail windows: use `large-title-display-mode-never`
|
|
91
103
|
|
|
@@ -126,6 +138,10 @@ Sizing the content height to `Ti.UI.SIZE` is what lets iOS determine whether the
|
|
|
126
138
|
| `content-w-screen` | `contentWidth` | `Ti.UI.FILL` |
|
|
127
139
|
| `content-h-auto` | `contentHeight` | `Ti.UI.SIZE` |
|
|
128
140
|
|
|
141
|
+
> 💡 **TIP**
|
|
142
|
+
>
|
|
143
|
+
> Set the three base properties (`autoAdjustScrollViewInsets`, `extendEdges`, `largeTitleEnabled`) as global defaults in `config.cjs`, then override `largeTitleDisplayMode` per window only when needed.
|
|
144
|
+
|
|
129
145
|
## Community-Discovered Patterns
|
|
130
146
|
|
|
131
147
|
### TabGroup implicit-NavigationWindow wrapping
|
|
@@ -138,4 +154,4 @@ When `theme.Window.ios.apply` sets `large-title-enabled` as a default, every pus
|
|
|
138
154
|
|
|
139
155
|
### Cross-reference
|
|
140
156
|
|
|
141
|
-
The three-property pairing (`autoAdjustScrollViewInsets` + `extendEdges` + `largeTitleEnabled`) is also documented as a reusable global-defaults recipe in [apply-directive.md](apply-directive.md) under *Community-Discovered Patterns → Global Window defaults for Large Titles + ScrollView (iOS)*. Prefer that recipe over per-window repetition whenever the whole app uses Large Titles.
|
|
157
|
+
The three-property pairing (`autoAdjustScrollViewInsets` + `extendEdges` + `largeTitleEnabled`) is also documented as a reusable global-defaults recipe in [apply-directive.md](./apply-directive.md) under *Community-Discovered Patterns → Global Window defaults for Large Titles + ScrollView (iOS)*. Prefer that recipe over per-window repetition whenever the whole app uses Large Titles.
|
|
@@ -101,6 +101,62 @@ Recommended sizes for common UI elements (in source pixels, assumed 4×):
|
|
|
101
101
|
- **List-row thumbnail**: at least `320×320`
|
|
102
102
|
- **Button background**: match the intended display size × 4
|
|
103
103
|
|
|
104
|
+
## Pinning the output width with `--width`
|
|
105
|
+
|
|
106
|
+
The 4× master convention works well for raster sources (`.png`, `.jpg`, `.webp`) because the file's pixel dimensions usually reflect the intended 4× size. **SVGs are different.** Their logical size comes from the `viewBox`, and vector editors (Affinity Designer, Illustrator, Figma exports) frequently emit viewBoxes in points or with disproportionate values — a logo can ship with `viewBox="0 0 29559 13542"` and `purgetss images` would happily scale every density from that base, producing files far too large for any UI surface.
|
|
107
|
+
|
|
108
|
+
`--width <n>` (added in PurgeTSS v7.8.0) is the escape hatch: it pins the **`mdpi` / `@1x`** output width to exactly `n` pixels, then derives every other density from that base. Height stays proportional to the source aspect ratio — you only specify width.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
purgetss images logo.svg --width 256
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Multiplier table
|
|
115
|
+
|
|
116
|
+
The pinned width drives the scale for every density:
|
|
117
|
+
|
|
118
|
+
| Scale | Multiplier | Width if `--width 256` |
|
|
119
|
+
| --- | --- | --- |
|
|
120
|
+
| `mdpi` / `@1x` | ×1 | 256 |
|
|
121
|
+
| `hdpi` | ×1.5 | 384 |
|
|
122
|
+
| `xhdpi` / `@2x` | ×2 | 512 |
|
|
123
|
+
| `xxhdpi` / `@3x` | ×3 | 768 |
|
|
124
|
+
| `xxxhdpi` | ×4 | 1024 |
|
|
125
|
+
|
|
126
|
+
### Validation
|
|
127
|
+
|
|
128
|
+
`--width` accepts integers in `[1, 8192]`. Out-of-range values are rejected immediately and the command exits without writing anything:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
purgetss images logo.svg --width 0
|
|
132
|
+
# Invalid --width '0'. Must be an integer between 1 and 8192.
|
|
133
|
+
|
|
134
|
+
purgetss images logo.svg --width 9000
|
|
135
|
+
# Invalid --width '9000'. Must be an integer between 1 and 8192.
|
|
136
|
+
|
|
137
|
+
purgetss images logo.svg --width abc
|
|
138
|
+
# Invalid --width 'NaN'. Must be an integer between 1 and 8192.
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The upper bound of `8192` exists because `--width 8192` already produces a `xxxhdpi` output of 32 768 px — that's Sharp's render ceiling and well beyond anything a Titanium UI needs.
|
|
142
|
+
|
|
143
|
+
### The hint message for unflagged SVGs
|
|
144
|
+
|
|
145
|
+
Whenever you run `purgetss images` against an SVG **without** `--width`, PurgeTSS prints a one-time hint:
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
⚠ SVG source detected without --width. Output sizes will be derived from
|
|
149
|
+
each SVG's viewBox (treated as a 4× master).
|
|
150
|
+
For SVGs from vector editors with disproportionate viewBoxes, pass
|
|
151
|
+
--width <n> (e.g. --width 256) to pin the @1x/mdpi width.
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
This is a hint, **not an error**. The legacy 4×-from-viewBox behavior still runs in the same invocation. If your SVG has a sensible viewBox (`300×150` for a 300px-wide logo at 1×, etc.), the default is fine. If the viewBox is in points or noticeably larger than expected, re-run with `--width <n>` for predictable scaling.
|
|
155
|
+
|
|
156
|
+
### Why CLI-only (no `images:` config equivalent)
|
|
157
|
+
|
|
158
|
+
`--width` deliberately has **no matching property** in the `images:` block of `purgetss/config.cjs`. The reason: width is a **per-asset** decision, not a project-wide setting. A hero illustration, an inline icon, and a logo each need different widths — pinning a single value globally would make most outputs wrong. Project-wide settings like `quality` or `format` belong in `config.cjs`; per-invocation values like `--width` only make sense as CLI flags passed against the specific source you're regenerating.
|
|
159
|
+
|
|
104
160
|
## The `images:` config section
|
|
105
161
|
|
|
106
162
|
On the first run, `purgetss images` injects an `images:` block into your existing `purgetss/config.cjs` (between `brand:` and `theme:`) with these defaults:
|
|
@@ -291,6 +347,7 @@ If you only tweaked CSS classes (no image changes), you don't need to re-run `pu
|
|
|
291
347
|
| --- | --- |
|
|
292
348
|
| `--format <ext>` | Convert all outputs to: `webp`, `jpeg`, `png`, `avif`, `gif`, `tiff`. Default: keep source format. |
|
|
293
349
|
| `--quality <n>` | Quality `0–100` for lossy formats. Default `85`. |
|
|
350
|
+
| `--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. |
|
|
294
351
|
|
|
295
352
|
**Project & output**
|
|
296
353
|
|
|
@@ -53,8 +53,17 @@ module.exports = {
|
|
|
53
53
|
'.bg-primary': { backgroundColor: '#ce10cc' }
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
## Semantic colors
|
|
57
|
+
|
|
58
|
+
Since v7.9.0, opacity modifiers also work on classes that resolve to a semantic color. Any class mapped to a semantic color entry can use `/<percent>` syntax.
|
|
59
|
+
|
|
60
|
+
```xml
|
|
61
|
+
<View class="bg-surface/65" />
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
PurgeTSS detects that `bg-surface` maps to the semantic name `surfaceColor`, then derives a new semantic key (`surfaceColor_65`) with the original `light` and `dark` hex values plus the requested alpha for both modes. It writes that key back to `semantic.colors.json` and emits the rule against the derived key. Light/Dark switching still works.
|
|
65
|
+
|
|
66
|
+
See [Semantic colors — Opacity modifier auto-derivation](./semantic-colors.md#opacity-modifier-auto-derivation) for the full mechanics.
|
|
58
67
|
|
|
59
68
|
## Community-Discovered Patterns
|
|
60
69
|
|
|
@@ -302,6 +302,48 @@ Once the JSON and the `config.cjs` mapping are in place, you use the semantic cl
|
|
|
302
302
|
|
|
303
303
|
When the appearance changes — whether from `Appearance.set(...)` or a system-level toggle — Titanium resolves each semantic color name to its `light` or `dark` value automatically. No event listeners, no manual repaint.
|
|
304
304
|
|
|
305
|
+
### Opacity modifier auto-derivation
|
|
306
|
+
|
|
307
|
+
As of PurgeTSS v7.9.0, you can apply the `/N` opacity modifier to **any class that resolves to a semantic name** in `semantic.colors.json`, and PurgeTSS will derive a new semantic key with that alpha pre-applied for both `light` and `dark` modes. This works for `bg-*`, `text-*`, `border-*`, and any other color-accepting utility whose class is mapped through `theme.extend.colors` in `config.cjs`.
|
|
308
|
+
|
|
309
|
+
```xml
|
|
310
|
+
<View class="bg-surface/65" />
|
|
311
|
+
<Label class="text-on-surface/80" text="Subtle" />
|
|
312
|
+
<View class="border border-accent/40" />
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
On the next `purgetss build` (or plain `purgetss`) run, the toolchain executes a three-step flow:
|
|
316
|
+
|
|
317
|
+
1. **Detects the mapping** — PurgeTSS sees that, for example, `bg-surface` is mapped to the semantic name `surfaceColor` via `config.cjs`.
|
|
318
|
+
2. **Derives a new key** — it adds `surfaceColor_65` (naming convention: `<originalKey>_<alphaPercent>`, underscore + integer percent) to `semantic.colors.json`, copying the original hex values for both modes and tagging each with `alpha: "65"`:
|
|
319
|
+
|
|
320
|
+
```json
|
|
321
|
+
"surfaceColor_65": {
|
|
322
|
+
"light": { "color": "#F9FAFB", "alpha": "65" },
|
|
323
|
+
"dark": { "color": "#0f172a", "alpha": "65" }
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
3. **Emits the rule against the derived key** — for example, `'.bg-surface/65': { backgroundColor: 'surfaceColor_65' }`. Light/Dark switching keeps working because Titanium handles the lookup like any other semantic color. The same flow runs for opacity inside an `apply:` string in `config.cjs`.
|
|
328
|
+
|
|
329
|
+
#### Idempotency and the `Conflict` error
|
|
330
|
+
|
|
331
|
+
Re-runs are idempotent: existing derived keys are reused, never duplicated. If you manually edit a derived key with values that disagree with what PurgeTSS would generate (different base color, different alpha, different shape), the next build halts with a `Conflict` error instead of silently overwriting your edits — you have to either revert the manual change or remove the derived key so it can be regenerated cleanly.
|
|
332
|
+
|
|
333
|
+
> **DANGER**
|
|
334
|
+
>
|
|
335
|
+
> **Native rebuild required for new alpha entries**
|
|
336
|
+
>
|
|
337
|
+
> `semantic.colors.json` is read at **native build time**, not at runtime. The first time a brand-new opacity variant is auto-derived (a class like `bg-surface/65` you've never used before), the running app **will not see it** until the next full Titanium build. Liveview hot-reload alone does **not** refresh `semantic.colors.json` for the running app — only the native binary does.
|
|
338
|
+
>
|
|
339
|
+
> In practice: after introducing a new opacity class, run `purgetss build` once, then start a fresh native build (`appc run` / `ti build`) before resuming your usual Liveview cycle. Subsequent runs of the *same* `/N` value reuse the existing derived key and need no extra rebuild.
|
|
340
|
+
|
|
341
|
+
#### Constraints
|
|
342
|
+
|
|
343
|
+
- **Alpha range**: integer `0–100`, matching the standard opacity modifier syntax. Values outside this range are rejected.
|
|
344
|
+
- **Base key must exist**: the semantic name behind the class (`surfaceColor` in the example) must already exist in `semantic.colors.json`. If it doesn't, PurgeTSS emits a warning for direct XML usage or throws an Error for `apply:` directives, with three concrete suggestions in the message.
|
|
345
|
+
- **Naming is fixed**: the derived key is always `<originalKey>_<alphaPercent>` — underscore + integer percent. This mirrors the `/65` you typed and stays quote-free in `config.cjs` if you ever need to reference it manually.
|
|
346
|
+
|
|
305
347
|
## Using semantic colors in controllers
|
|
306
348
|
|
|
307
349
|
Semantic colors also work from JavaScript. Three patterns cover the cases you'll hit.
|
|
@@ -368,10 +410,11 @@ A minimal semantic palette that covers most app surfaces:
|
|
|
368
410
|
| Cards / elevated | `surfaceHighColor` | `#FFFFFF` | `#1e293b` | `bg-surface-high` |
|
|
369
411
|
| Primary text | `textColor` | `#111827` | `#f1f5f9` | `text-on-surface` |
|
|
370
412
|
| Secondary text | `textSecondaryColor` | `#6B7280` | `#94a3b8` | `text-on-surface-variant` |
|
|
413
|
+
| Muted text | `textMutedColor` | `#9CA3AF` | `#64748b` | `text-muted` |
|
|
371
414
|
| Borders / dividers | `borderColor` | `#E5E7EB` | `#334155` | `bg-border` |
|
|
372
415
|
| Accent | `accentColor` | `#3B82F6` | `#60a5fa` | `text-accent`, `bg-accent` |
|
|
373
416
|
|
|
374
|
-
Start with these
|
|
417
|
+
Start with these 6-7 colors and add more only when the design requires it. Fewer semantic colors means easier maintenance.
|
|
375
418
|
|
|
376
419
|
## Related
|
|
377
420
|
|
|
@@ -316,9 +316,7 @@ Colors: `black`, `dark`, `light`, and `white` themes that affect background and
|
|
|
316
316
|
|
|
317
317
|
Use this for code examples. Set `copy="true"` to add a copy button.
|
|
318
318
|
|
|
319
|
-
|
|
320
|
-
>
|
|
321
|
-
> For best results, install a monospaced font (like Fira Code or JetBrains Mono) and configure `font-mono` in your PurgeTSS config.
|
|
319
|
+
**Pro tip — recommended monospace fonts:** For best results, install a monospaced font (like Fira Code or JetBrains Mono) and configure `font-mono` in your PurgeTSS config. (Source: official tikit docs, `tikit.md` line ~390.)
|
|
322
320
|
|
|
323
321
|
**Localization:** The copy button uses `L('copy', 'Copy')` for its title and `L('code_copied', 'Code copied!')` for the confirmation message. Add these keys to your `strings.xml` files for translation.
|
|
324
322
|
|