purgetss 7.5.2 → 7.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -0
- package/dist/purgetss.ui.js +43 -1
- package/dist/utilities.tss +8 -3
- package/experimental/completions2.js +1 -1
- package/lib/templates/purgetss.ui.js.cjs +42 -0
- package/package.json +1 -1
- package/src/cli/commands/purge.js +29 -3
- package/src/core/builders/tailwind-helpers.js +1 -1
- package/src/shared/helpers/typography.js +38 -3
package/README.md
CHANGED
|
@@ -240,6 +240,67 @@ Fallback defaults when not set: `swap`/`reorder`/`snapTo` → 200ms; `shake` →
|
|
|
240
240
|
|
|
241
241
|
See the full documentation at [purgetss.com/docs/animation-module/introduction](https://purgetss.com/docs/animation-module/introduction).
|
|
242
242
|
|
|
243
|
+
### Appearance management
|
|
244
|
+
|
|
245
|
+
Switch between Light, Dark, and System modes with automatic persistence:
|
|
246
|
+
|
|
247
|
+
```js
|
|
248
|
+
const { Appearance } = require('purgetss.ui')
|
|
249
|
+
|
|
250
|
+
// Call once at app startup (e.g., in index.js before opening the first window)
|
|
251
|
+
Appearance.init()
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
| Method | Description |
|
|
255
|
+
| ----------- | ------------------------------------------------------------ |
|
|
256
|
+
| `init()` | Restore the saved mode from `Ti.App.Properties` |
|
|
257
|
+
| `get()` | Returns the current mode string |
|
|
258
|
+
| `set(mode)` | Apply and persist a mode: `'system'`, `'light'`, or `'dark'` |
|
|
259
|
+
| `toggle()` | Switch between `'light'` and `'dark'` |
|
|
260
|
+
|
|
261
|
+
Use it from any controller to respond to user actions:
|
|
262
|
+
|
|
263
|
+
```js
|
|
264
|
+
const { Appearance } = require('purgetss.ui')
|
|
265
|
+
|
|
266
|
+
function selectDark() { Appearance.set('dark') }
|
|
267
|
+
function selectLight() { Appearance.set('light') }
|
|
268
|
+
function selectSystem() { Appearance.set('system') }
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Requires `semantic.colors.json` in `app/assets/` for views to respond to mode changes. See the [Titanium docs on semantic colors](https://titaniumsdk.com/guide/Titanium_SDK/Titanium_SDK_How-tos/User_Interface_Deep_Dives/iOS_Dark_Mode.html) for the file format.
|
|
272
|
+
|
|
273
|
+
### Default font families
|
|
274
|
+
|
|
275
|
+
PurgeTSS generates `font-sans`, `font-serif`, and `font-mono` classes automatically with platform-appropriate values:
|
|
276
|
+
|
|
277
|
+
| Class | iOS | Android |
|
|
278
|
+
| ------------ | ------------------ | ------------- |
|
|
279
|
+
| `font-sans` | `Helvetica Neue` | `sans-serif` |
|
|
280
|
+
| `font-serif` | `Georgia` | `serif` |
|
|
281
|
+
| `font-mono` | `monospace` | `monospace` |
|
|
282
|
+
|
|
283
|
+
Override or add families in `config.cjs`:
|
|
284
|
+
|
|
285
|
+
```js
|
|
286
|
+
// theme.extend.fontFamily → adds to defaults
|
|
287
|
+
extend: {
|
|
288
|
+
fontFamily: {
|
|
289
|
+
display: 'AlfaSlabOne-Regular',
|
|
290
|
+
body: 'BarlowSemiCondensed-Regular'
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// theme.fontFamily → replaces defaults entirely
|
|
295
|
+
theme: {
|
|
296
|
+
fontFamily: {
|
|
297
|
+
sans: 'System',
|
|
298
|
+
mono: 'Courier',
|
|
299
|
+
display: 'AlfaSlabOne-Regular'
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
243
304
|
---
|
|
244
305
|
|
|
245
306
|
## Customizing default components
|
package/dist/purgetss.ui.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// PurgeTSS v7.5.
|
|
1
|
+
// PurgeTSS v7.5.3
|
|
2
2
|
// Created by César Estrada
|
|
3
3
|
// https://purgetss.com
|
|
4
4
|
|
|
@@ -906,3 +906,45 @@ function saveComponent({ source, directory = Ti.Filesystem.tempDirectory }) {
|
|
|
906
906
|
exports.saveComponent = saveComponent
|
|
907
907
|
|
|
908
908
|
exports.createAnimation = (args) => new Animation(args)
|
|
909
|
+
|
|
910
|
+
// --- Appearance Management (Light/Dark/System) ---
|
|
911
|
+
function Appearance() {
|
|
912
|
+
const PROP_KEY = 'userInterfaceStyle'
|
|
913
|
+
const STYLES = {
|
|
914
|
+
dark: Ti.UI.USER_INTERFACE_STYLE_DARK,
|
|
915
|
+
light: Ti.UI.USER_INTERFACE_STYLE_LIGHT,
|
|
916
|
+
system: Ti.UI.USER_INTERFACE_STYLE_UNSPECIFIED
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
let currentMode = 'system'
|
|
920
|
+
|
|
921
|
+
function applyMode(mode) {
|
|
922
|
+
currentMode = mode
|
|
923
|
+
Ti.UI.overrideUserInterfaceStyle = STYLES[mode]
|
|
924
|
+
Ti.App.Properties.setInt(PROP_KEY, STYLES[mode])
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
return {
|
|
928
|
+
init() {
|
|
929
|
+
const saved = Ti.App.Properties.getInt(PROP_KEY, STYLES.system)
|
|
930
|
+
currentMode = Object.keys(STYLES).find(key => STYLES[key] === saved) || 'system'
|
|
931
|
+
Ti.UI.overrideUserInterfaceStyle = saved
|
|
932
|
+
},
|
|
933
|
+
|
|
934
|
+
set(mode) {
|
|
935
|
+
if (!STYLES.hasOwnProperty(mode)) return
|
|
936
|
+
applyMode(mode)
|
|
937
|
+
},
|
|
938
|
+
|
|
939
|
+
get() {
|
|
940
|
+
return currentMode
|
|
941
|
+
},
|
|
942
|
+
|
|
943
|
+
toggle() {
|
|
944
|
+
const next = (currentMode === 'dark') ? 'light' : 'dark'
|
|
945
|
+
applyMode(next)
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
exports.Appearance = Appearance()
|
package/dist/utilities.tss
CHANGED
|
@@ -2250,9 +2250,6 @@
|
|
|
2250
2250
|
'.rounded-full-2.5': { width: 10, height: 10, borderRadius: 5 }
|
|
2251
2251
|
'.rounded-full-3.5': { width: 14, height: 14, borderRadius: 7 }
|
|
2252
2252
|
|
|
2253
|
-
// Property(ies): fontFamily
|
|
2254
|
-
// Component(s): Ti.UI.ActivityIndicator, Ti.UI.Button, Ti.UI.Label, Ti.UI.ListItem, Ti.UI.Picker, Ti.UI.PickerColumn, Ti.UI.PickerRow, Ti.UI.ProgressBar, Ti.UI.Switch, Ti.UI.TableViewRow, Ti.UI.TextArea, Ti.UI.TextField
|
|
2255
|
-
|
|
2256
2253
|
// Property(ies): fontSize
|
|
2257
2254
|
// Component(s): Ti.UI.ActivityIndicator, Ti.UI.Button, Ti.UI.Label, Ti.UI.ListItem, Ti.UI.Picker, Ti.UI.PickerColumn, Ti.UI.PickerRow, Ti.UI.ProgressBar, Ti.UI.Switch, Ti.UI.TableViewRow, Ti.UI.TextArea, Ti.UI.TextField
|
|
2258
2255
|
'.text-xs': { font: { fontSize: 12 } }
|
|
@@ -2281,6 +2278,14 @@
|
|
|
2281
2278
|
'.font-extrabold': { font: { fontWeight: 'bold' } }
|
|
2282
2279
|
'.font-black': { font: { fontWeight: 'bold' } }
|
|
2283
2280
|
|
|
2281
|
+
// Property(ies): fontFamily
|
|
2282
|
+
// Component(s): Ti.UI.ActivityIndicator, Ti.UI.Button, Ti.UI.Label, Ti.UI.ListItem, Ti.UI.Picker, Ti.UI.PickerColumn, Ti.UI.PickerRow, Ti.UI.ProgressBar, Ti.UI.Switch, Ti.UI.TableViewRow, Ti.UI.TextArea, Ti.UI.TextField
|
|
2283
|
+
'.font-mono': { font: { fontFamily: 'monospace' } }
|
|
2284
|
+
'.font-sans[platform=ios]': { font: { fontFamily: 'Helvetica Neue' } }
|
|
2285
|
+
'.font-serif[platform=ios]': { font: { fontFamily: 'Georgia' } }
|
|
2286
|
+
'.font-sans[platform=android]': { font: { fontFamily: 'sans-serif' } }
|
|
2287
|
+
'.font-serif[platform=android]': { font: { fontFamily: 'serif' } }
|
|
2288
|
+
|
|
2284
2289
|
// Property(ies): top, right, bottom, left - Gap for Grid System
|
|
2285
2290
|
// Component(s): Ti.UI.ActivityIndicator, Ti.UI.Animation, Ti.UI.View, Ti.UI.Window
|
|
2286
2291
|
'.gap-0': { top: 0, right: 0, bottom: 0, left: 0 }
|
|
@@ -247,9 +247,9 @@ function processCompoundClasses({ ..._base }) {
|
|
|
247
247
|
// ! Configurables
|
|
248
248
|
compoundClasses += generateGlossary('borderRadius-alternative', helpers.borderRadius(_base.borderRadius))
|
|
249
249
|
compoundClasses += generateGlossary('borderRadius-full', helpers.borderRadiusFull(_base.borderRadius))
|
|
250
|
-
compoundClasses += generateGlossary('fontFamily', helpers.fontFamily(_base.fontFamily))
|
|
251
250
|
compoundClasses += generateGlossary('fontSize', helpers.fontSize(_base.fontSize))
|
|
252
251
|
compoundClasses += generateGlossary('fontWeight', helpers.fontWeight(_base.fontWeight))
|
|
252
|
+
compoundClasses += generateGlossary('fontFamily', helpers.fontFamily(_base.fontFamily))
|
|
253
253
|
compoundClasses += generateGlossary('margin-alternative', helpers.gap(_base.margin))
|
|
254
254
|
compoundClasses += generateGlossary('minimumFontSize', helpers.minimumFontSize(_base.fontSize))
|
|
255
255
|
compoundClasses += generateGlossary('padding-alternative', helpers.padding(_base.padding))
|
|
@@ -905,3 +905,45 @@ function saveComponent({ source, directory = Ti.Filesystem.tempDirectory }) {
|
|
|
905
905
|
exports.saveComponent = saveComponent
|
|
906
906
|
|
|
907
907
|
exports.createAnimation = (args) => new Animation(args)
|
|
908
|
+
|
|
909
|
+
// --- Appearance Management (Light/Dark/System) ---
|
|
910
|
+
function Appearance() {
|
|
911
|
+
const PROP_KEY = 'userInterfaceStyle'
|
|
912
|
+
const STYLES = {
|
|
913
|
+
dark: Ti.UI.USER_INTERFACE_STYLE_DARK,
|
|
914
|
+
light: Ti.UI.USER_INTERFACE_STYLE_LIGHT,
|
|
915
|
+
system: Ti.UI.USER_INTERFACE_STYLE_UNSPECIFIED
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
let currentMode = 'system'
|
|
919
|
+
|
|
920
|
+
function applyMode(mode) {
|
|
921
|
+
currentMode = mode
|
|
922
|
+
Ti.UI.overrideUserInterfaceStyle = STYLES[mode]
|
|
923
|
+
Ti.App.Properties.setInt(PROP_KEY, STYLES[mode])
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
return {
|
|
927
|
+
init() {
|
|
928
|
+
const saved = Ti.App.Properties.getInt(PROP_KEY, STYLES.system)
|
|
929
|
+
currentMode = Object.keys(STYLES).find(key => STYLES[key] === saved) || 'system'
|
|
930
|
+
Ti.UI.overrideUserInterfaceStyle = saved
|
|
931
|
+
},
|
|
932
|
+
|
|
933
|
+
set(mode) {
|
|
934
|
+
if (!STYLES.hasOwnProperty(mode)) return
|
|
935
|
+
applyMode(mode)
|
|
936
|
+
},
|
|
937
|
+
|
|
938
|
+
get() {
|
|
939
|
+
return currentMode
|
|
940
|
+
},
|
|
941
|
+
|
|
942
|
+
toggle() {
|
|
943
|
+
const next = (currentMode === 'dark') ? 'light' : 'dark'
|
|
944
|
+
applyMode(next)
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
exports.Appearance = Appearance()
|
package/package.json
CHANGED
|
@@ -191,7 +191,15 @@ function validateXML(xmlText, filePath) {
|
|
|
191
191
|
function findSuspectLine(lines) {
|
|
192
192
|
for (let i = 0; i < lines.length; i++) {
|
|
193
193
|
const trimmed = lines[i].trim()
|
|
194
|
-
if (!trimmed
|
|
194
|
+
if (!trimmed) continue
|
|
195
|
+
|
|
196
|
+
// "--" inside XML comments (illegal in XML spec)
|
|
197
|
+
if (trimmed.includes('<!--')) {
|
|
198
|
+
const commentBody = trimmed.replace(/<!--/, '').replace(/-->.*$/, '')
|
|
199
|
+
if (/--/.test(commentBody)) return i + 1
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (trimmed.startsWith('<!--')) continue
|
|
195
203
|
|
|
196
204
|
// Opening tag without tag name: "< class=..."
|
|
197
205
|
if (/^<\s+\w+=/.test(trimmed)) return i + 1
|
|
@@ -235,8 +243,26 @@ function preValidateXML(xmlText, filePath) {
|
|
|
235
243
|
const line = lines[i]
|
|
236
244
|
const trimmed = line.trim()
|
|
237
245
|
|
|
238
|
-
// Skip empty lines and
|
|
239
|
-
if (!trimmed || trimmed.startsWith('
|
|
246
|
+
// Skip empty lines and Alloy root tag
|
|
247
|
+
if (!trimmed || trimmed.startsWith('<Alloy')) {
|
|
248
|
+
continue
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Check for "--" inside XML comments (illegal in XML spec)
|
|
252
|
+
// e.g. <!-- Section: --modules Option --> is invalid because of the "--" before "modules"
|
|
253
|
+
if (trimmed.includes('<!--')) {
|
|
254
|
+
const commentBody = trimmed.replace(/<!--/, '').replace(/-->.*$/, '')
|
|
255
|
+
if (/--/.test(commentBody)) {
|
|
256
|
+
const dashMatch = commentBody.match(/--(\S*)/)
|
|
257
|
+
const offender = dashMatch ? `--${dashMatch[1]}` : '--'
|
|
258
|
+
throwPreValidationError({
|
|
259
|
+
relativePath,
|
|
260
|
+
lineNumber: i + 1,
|
|
261
|
+
lineContent: trimmed,
|
|
262
|
+
message: `XML comment contains illegal "--" sequence ("${offender}")`,
|
|
263
|
+
fix: `Replace "--" with "—" (em-dash) or reword the comment to avoid double dashes`
|
|
264
|
+
})
|
|
265
|
+
}
|
|
240
266
|
continue
|
|
241
267
|
}
|
|
242
268
|
|
|
@@ -383,7 +383,7 @@ export function combineAllValues(base, defaultTheme) {
|
|
|
383
383
|
allValues.contentWidth = combineKeys(configFile.theme, base.width, 'contentWidth')
|
|
384
384
|
allValues.countDownDuration = combineKeys(configFile.theme, base.spacing, 'countDownDuration')
|
|
385
385
|
allValues.elevation = combineKeys(configFile.theme, base.spacing, 'elevation')
|
|
386
|
-
allValues.fontFamily = combineKeys(configFile.theme,
|
|
386
|
+
allValues.fontFamily = combineKeys(configFile.theme, {}, 'fontFamily')
|
|
387
387
|
allValues.fontSize = combineKeys(configFile.theme, base.fontSize, 'fontSize')
|
|
388
388
|
allValues.fontWeight = combineKeys(configFile.theme, defaultTheme.fontWeight, 'fontWeight')
|
|
389
389
|
allValues.gap = combineKeys(configFile.theme, base.spacing, 'gap')
|
|
@@ -26,18 +26,53 @@ function removeFractions(modifiersAndValues, extras = []) {
|
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Font family property for text components
|
|
29
|
+
*
|
|
30
|
+
* Built-in platform defaults:
|
|
31
|
+
* - font-sans → Android: 'sans-serif', iOS: 'Helvetica Neue'
|
|
32
|
+
* - font-serif → Android: 'serif', iOS: 'Georgia'
|
|
33
|
+
* - font-mono → 'monospace' (both platforms)
|
|
34
|
+
*
|
|
35
|
+
* User values from config.cjs override defaults cross-platform.
|
|
36
|
+
*
|
|
29
37
|
* @param {Object} modifiersAndValues - Modifier and value pairs
|
|
30
38
|
* @returns {string} Generated styles
|
|
31
39
|
*/
|
|
32
40
|
export function fontFamily(modifiersAndValues) {
|
|
41
|
+
const platformDefaults = {
|
|
42
|
+
sans: { ios: 'Helvetica Neue', android: 'sans-serif' },
|
|
43
|
+
serif: { ios: 'Georgia', android: 'serif' }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const crossPlatformDefaults = { mono: 'monospace' }
|
|
47
|
+
|
|
48
|
+
const defaults = { ...modifiersAndValues }
|
|
49
|
+
const ios = {}
|
|
50
|
+
const android = {}
|
|
51
|
+
|
|
52
|
+
_.each(crossPlatformDefaults, (value, key) => {
|
|
53
|
+
if (!(key in defaults)) {
|
|
54
|
+
defaults[key] = value
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
_.each(platformDefaults, (platforms, key) => {
|
|
59
|
+
if (!(key in defaults)) {
|
|
60
|
+
ios[key] = platforms.ios
|
|
61
|
+
android[key] = platforms.android
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const selectorsAndValues = {}
|
|
66
|
+
if (!_.isEmpty(defaults)) selectorsAndValues.default = defaults
|
|
67
|
+
if (!_.isEmpty(ios)) selectorsAndValues.ios = ios
|
|
68
|
+
if (!_.isEmpty(android)) selectorsAndValues.android = android
|
|
69
|
+
|
|
33
70
|
return processProperties({
|
|
34
71
|
prop: 'fontFamily',
|
|
35
72
|
modules: 'Ti.UI.ActivityIndicator, Ti.UI.Button, Ti.UI.Label, Ti.UI.ListItem, Ti.UI.Picker, Ti.UI.PickerColumn, Ti.UI.PickerRow, Ti.UI.ProgressBar, Ti.UI.Switch, Ti.UI.TableViewRow, Ti.UI.TextArea, Ti.UI.TextField'
|
|
36
73
|
}, {
|
|
37
74
|
font: '{ font: { fontFamily: {value} } }'
|
|
38
|
-
},
|
|
39
|
-
default: modifiersAndValues
|
|
40
|
-
})
|
|
75
|
+
}, selectorsAndValues)
|
|
41
76
|
}
|
|
42
77
|
|
|
43
78
|
/**
|