purgetss 7.5.2 → 7.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +93 -11
  2. package/bin/purgetss +140 -1
  3. package/dist/purgetss.ui.js +65 -26
  4. package/dist/utilities.tss +21 -4
  5. package/experimental/completions2.js +1 -1
  6. package/lib/completions/titanium/completions-v3.json +62 -1
  7. package/lib/templates/purgetss.config.js.cjs +15 -1
  8. package/lib/templates/purgetss.ui.js.cjs +64 -25
  9. package/package.json +3 -1
  10. package/src/cli/commands/brand.js +69 -0
  11. package/src/cli/commands/create.js +11 -7
  12. package/src/cli/commands/fonts.js +9 -9
  13. package/src/cli/commands/icon-library.js +18 -16
  14. package/src/cli/commands/images.js +116 -0
  15. package/src/cli/commands/init.js +4 -0
  16. package/src/cli/commands/module.js +4 -2
  17. package/src/cli/commands/purge.js +77 -101
  18. package/src/cli/commands/semantic.js +180 -0
  19. package/src/cli/commands/shades.js +332 -13
  20. package/src/cli/utils/project-detection.js +4 -2
  21. package/src/core/analyzers/class-extractor.js +110 -3
  22. package/src/core/branding/brand-config.js +111 -0
  23. package/src/core/branding/branding-logger.js +40 -0
  24. package/src/core/branding/cleanup-legacy.js +220 -0
  25. package/src/core/branding/ensure-brand-section.js +80 -0
  26. package/src/core/branding/gen-android-adaptive.js +116 -0
  27. package/src/core/branding/gen-android-legacy.js +63 -0
  28. package/src/core/branding/gen-ic-launcher-xml.js +29 -0
  29. package/src/core/branding/gen-ios-dark.js +70 -0
  30. package/src/core/branding/gen-ios-tinted.js +55 -0
  31. package/src/core/branding/gen-ios.js +69 -0
  32. package/src/core/branding/gen-marketplace.js +71 -0
  33. package/src/core/branding/gen-notification.js +76 -0
  34. package/src/core/branding/gen-splash.js +64 -0
  35. package/src/core/branding/index.js +336 -0
  36. package/src/core/branding/post-gen-notes.js +145 -0
  37. package/src/core/branding/prepare-master.js +108 -0
  38. package/src/core/branding/tiapp-reader.js +110 -0
  39. package/src/core/builders/tailwind-helpers.js +1 -1
  40. package/src/core/images/ensure-images-section.js +57 -0
  41. package/src/core/images/gen-scales.js +181 -0
  42. package/src/core/images/index.js +171 -0
  43. package/src/shared/config-manager.js +46 -0
  44. package/src/shared/config-writer.js +84 -0
  45. package/src/shared/constants.js +3 -0
  46. package/src/shared/helpers/typography.js +38 -3
  47. package/src/shared/logger.js +69 -4
  48. package/src/shared/prompt.js +64 -0
  49. package/src/shared/svg-utils.js +80 -0
  50. package/src/shared/utils.js +8 -4
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sdkVersion": "13.1.0.GA",
3
+ "sdkVersion": "13.2.0.GA",
4
4
  "properties": {
5
5
  "ACCURACY_AUTHORIZATION_FULL": {
6
6
  "description": "The user authorized the app to access location data with full accuracy.",
@@ -12808,6 +12808,15 @@
12808
12808
  "readOnly": true,
12809
12809
  "values": []
12810
12810
  },
12811
+ "hideKeyboardAccessoryView": {
12812
+ "description": "A Boolean value indicating whether to hide the keyboard accessory bar.",
12813
+ "type": "Boolean",
12814
+ "readOnly": true,
12815
+ "values": [
12816
+ "true",
12817
+ "false"
12818
+ ]
12819
+ },
12811
12820
  "hideLoadIndicator": {
12812
12821
  "description": "Hides activity indicator when loading remote URL.",
12813
12822
  "type": "Boolean",
@@ -13914,6 +13923,15 @@
13914
13923
  "readOnly": true,
13915
13924
  "values": []
13916
13925
  },
13926
+ "keepHardwareMode": {
13927
+ "description": "A value indicating the render mode of the View",
13928
+ "type": "Boolean",
13929
+ "readOnly": true,
13930
+ "values": [
13931
+ "true",
13932
+ "false"
13933
+ ]
13934
+ },
13917
13935
  "keepScreenOn": {
13918
13936
  "description": "Determines whether to keep the device screen on.",
13919
13937
  "type": "Boolean",
@@ -20916,6 +20934,7 @@
20916
20934
  "horizontalMotionEffect",
20917
20935
  "id",
20918
20936
  "left",
20937
+ "keepHardwareMode",
20919
20938
  "layout",
20920
20939
  "opacity",
20921
20940
  "overrideCurrentAnimation",
@@ -25472,6 +25491,7 @@
25472
25491
  "horizontalMotionEffect",
25473
25492
  "id",
25474
25493
  "left",
25494
+ "keepHardwareMode",
25475
25495
  "layout",
25476
25496
  "opacity",
25477
25497
  "overrideCurrentAnimation",
@@ -26212,6 +26232,7 @@
26212
26232
  "horizontalMotionEffect",
26213
26233
  "id",
26214
26234
  "left",
26235
+ "keepHardwareMode",
26215
26236
  "previewContext",
26216
26237
  "right",
26217
26238
  "rotation",
@@ -26284,6 +26305,7 @@
26284
26305
  "hiddenBehavior",
26285
26306
  "horizontalMotionEffect",
26286
26307
  "id",
26308
+ "keepHardwareMode",
26287
26309
  "previewContext",
26288
26310
  "rotation",
26289
26311
  "rotationX",
@@ -26517,6 +26539,7 @@
26517
26539
  "hiddenBehavior",
26518
26540
  "id",
26519
26541
  "left",
26542
+ "keepHardwareMode",
26520
26543
  "layout",
26521
26544
  "opacity",
26522
26545
  "overrideCurrentAnimation",
@@ -26584,6 +26607,7 @@
26584
26607
  "accessibilityDisableLongPress",
26585
26608
  "height",
26586
26609
  "id",
26610
+ "keepHardwareMode",
26587
26611
  "tooltip",
26588
26612
  "top",
26589
26613
  "barColor",
@@ -26662,6 +26686,7 @@
26662
26686
  "hiddenBehavior",
26663
26687
  "id",
26664
26688
  "left",
26689
+ "keepHardwareMode",
26665
26690
  "layout",
26666
26691
  "opacity",
26667
26692
  "overrideCurrentAnimation",
@@ -26760,6 +26785,7 @@
26760
26785
  "hiddenBehavior",
26761
26786
  "id",
26762
26787
  "left",
26788
+ "keepHardwareMode",
26763
26789
  "right",
26764
26790
  "rotation",
26765
26791
  "rotationX",
@@ -26807,6 +26833,7 @@
26807
26833
  "filterTouchesWhenObscured",
26808
26834
  "hiddenBehavior",
26809
26835
  "id",
26836
+ "keepHardwareMode",
26810
26837
  "rotation",
26811
26838
  "rotationX",
26812
26839
  "rotationY",
@@ -26887,6 +26914,7 @@
26887
26914
  "hiddenBehavior",
26888
26915
  "id",
26889
26916
  "left",
26917
+ "keepHardwareMode",
26890
26918
  "layout",
26891
26919
  "opacity",
26892
26920
  "overrideCurrentAnimation",
@@ -26970,6 +26998,7 @@
26970
26998
  "filterTouchesWhenObscured",
26971
26999
  "hiddenBehavior",
26972
27000
  "id",
27001
+ "keepHardwareMode",
26973
27002
  "rotation",
26974
27003
  "rotationX",
26975
27004
  "rotationY",
@@ -27160,6 +27189,7 @@
27160
27189
  "horizontalMotionEffect",
27161
27190
  "id",
27162
27191
  "left",
27192
+ "keepHardwareMode",
27163
27193
  "layout",
27164
27194
  "opacity",
27165
27195
  "overrideCurrentAnimation",
@@ -27288,6 +27318,7 @@
27288
27318
  "horizontalMotionEffect",
27289
27319
  "id",
27290
27320
  "left",
27321
+ "keepHardwareMode",
27291
27322
  "layout",
27292
27323
  "opacity",
27293
27324
  "overrideCurrentAnimation",
@@ -27561,6 +27592,7 @@
27561
27592
  "hiddenBehavior",
27562
27593
  "horizontalMotionEffect",
27563
27594
  "id",
27595
+ "keepHardwareMode",
27564
27596
  "previewContext",
27565
27597
  "rotation",
27566
27598
  "rotationX",
@@ -27664,6 +27696,7 @@
27664
27696
  "horizontalMotionEffect",
27665
27697
  "id",
27666
27698
  "left",
27699
+ "keepHardwareMode",
27667
27700
  "layout",
27668
27701
  "opacity",
27669
27702
  "overrideCurrentAnimation",
@@ -27795,6 +27828,7 @@
27795
27828
  "horizontalMotionEffect",
27796
27829
  "id",
27797
27830
  "left",
27831
+ "keepHardwareMode",
27798
27832
  "layout",
27799
27833
  "opacity",
27800
27834
  "overrideCurrentAnimation",
@@ -28058,6 +28092,7 @@
28058
28092
  "horizontalMotionEffect",
28059
28093
  "id",
28060
28094
  "left",
28095
+ "keepHardwareMode",
28061
28096
  "layout",
28062
28097
  "opacity",
28063
28098
  "overrideCurrentAnimation",
@@ -28286,6 +28321,7 @@
28286
28321
  "horizontalMotionEffect",
28287
28322
  "id",
28288
28323
  "left",
28324
+ "keepHardwareMode",
28289
28325
  "layout",
28290
28326
  "opacity",
28291
28327
  "overrideCurrentAnimation",
@@ -28495,6 +28531,7 @@
28495
28531
  "horizontalMotionEffect",
28496
28532
  "id",
28497
28533
  "left",
28534
+ "keepHardwareMode",
28498
28535
  "layout",
28499
28536
  "opacity",
28500
28537
  "overrideCurrentAnimation",
@@ -28683,6 +28720,7 @@
28683
28720
  "horizontalMotionEffect",
28684
28721
  "id",
28685
28722
  "left",
28723
+ "keepHardwareMode",
28686
28724
  "layout",
28687
28725
  "opacity",
28688
28726
  "overrideCurrentAnimation",
@@ -28758,6 +28796,7 @@
28758
28796
  "hiddenBehavior",
28759
28797
  "horizontalMotionEffect",
28760
28798
  "id",
28799
+ "keepHardwareMode",
28761
28800
  "previewContext",
28762
28801
  "rotation",
28763
28802
  "rotationX",
@@ -28865,6 +28904,7 @@
28865
28904
  "horizontalMotionEffect",
28866
28905
  "id",
28867
28906
  "left",
28907
+ "keepHardwareMode",
28868
28908
  "opacity",
28869
28909
  "overrideCurrentAnimation",
28870
28910
  "pullBackgroundColor",
@@ -28959,6 +28999,7 @@
28959
28999
  "accessibilityDisableLongPress",
28960
29000
  "filterTouchesWhenObscured",
28961
29001
  "id",
29002
+ "keepHardwareMode",
28962
29003
  "tooltip",
28963
29004
  "rowCount",
28964
29005
  "rows",
@@ -29030,6 +29071,7 @@
29030
29071
  "horizontalMotionEffect",
29031
29072
  "id",
29032
29073
  "left",
29074
+ "keepHardwareMode",
29033
29075
  "layout",
29034
29076
  "opacity",
29035
29077
  "overrideCurrentAnimation",
@@ -29142,6 +29184,7 @@
29142
29184
  "horizontalMotionEffect",
29143
29185
  "id",
29144
29186
  "left",
29187
+ "keepHardwareMode",
29145
29188
  "layout",
29146
29189
  "opacity",
29147
29190
  "overrideCurrentAnimation",
@@ -29303,6 +29346,7 @@
29303
29346
  "horizontalMotionEffect",
29304
29347
  "id",
29305
29348
  "left",
29349
+ "keepHardwareMode",
29306
29350
  "layout",
29307
29351
  "opacity",
29308
29352
  "overrideCurrentAnimation",
@@ -29446,6 +29490,7 @@
29446
29490
  "horizontalMotionEffect",
29447
29491
  "id",
29448
29492
  "left",
29493
+ "keepHardwareMode",
29449
29494
  "layout",
29450
29495
  "opacity",
29451
29496
  "overrideCurrentAnimation",
@@ -29489,6 +29534,7 @@
29489
29534
  "preferredIndicatorImage",
29490
29535
  "pagingControlHeight",
29491
29536
  "pageIndicatorColor",
29537
+ "scrollType",
29492
29538
  "showPagingControl",
29493
29539
  "pagingControlTimeout",
29494
29540
  "pagingControlAlpha",
@@ -29577,6 +29623,7 @@
29577
29623
  "horizontalMotionEffect",
29578
29624
  "id",
29579
29625
  "left",
29626
+ "keepHardwareMode",
29580
29627
  "layout",
29581
29628
  "opacity",
29582
29629
  "overrideCurrentAnimation",
@@ -29780,6 +29827,7 @@
29780
29827
  "horizontalMotionEffect",
29781
29828
  "id",
29782
29829
  "left",
29830
+ "keepHardwareMode",
29783
29831
  "layout",
29784
29832
  "opacity",
29785
29833
  "overrideCurrentAnimation",
@@ -29915,6 +29963,7 @@
29915
29963
  "horizontalMotionEffect",
29916
29964
  "id",
29917
29965
  "left",
29966
+ "keepHardwareMode",
29918
29967
  "layout",
29919
29968
  "opacity",
29920
29969
  "overrideCurrentAnimation",
@@ -30023,6 +30072,7 @@
30023
30072
  "hiddenBehavior",
30024
30073
  "horizontalMotionEffect",
30025
30074
  "id",
30075
+ "keepHardwareMode",
30026
30076
  "overrideCurrentAnimation",
30027
30077
  "pullBackgroundColor",
30028
30078
  "previewContext",
@@ -30135,6 +30185,7 @@
30135
30185
  "hiddenBehavior",
30136
30186
  "horizontalMotionEffect",
30137
30187
  "id",
30188
+ "keepHardwareMode",
30138
30189
  "overrideCurrentAnimation",
30139
30190
  "pullBackgroundColor",
30140
30191
  "previewContext",
@@ -30336,6 +30387,7 @@
30336
30387
  "horizontalMotionEffect",
30337
30388
  "id",
30338
30389
  "left",
30390
+ "keepHardwareMode",
30339
30391
  "layout",
30340
30392
  "opacity",
30341
30393
  "overrideCurrentAnimation",
@@ -30458,6 +30510,7 @@
30458
30510
  "horizontalMotionEffect",
30459
30511
  "id",
30460
30512
  "left",
30513
+ "keepHardwareMode",
30461
30514
  "layout",
30462
30515
  "opacity",
30463
30516
  "overrideCurrentAnimation",
@@ -30662,6 +30715,7 @@
30662
30715
  "horizontalMotionEffect",
30663
30716
  "id",
30664
30717
  "left",
30718
+ "keepHardwareMode",
30665
30719
  "layout",
30666
30720
  "opacity",
30667
30721
  "overrideCurrentAnimation",
@@ -30833,6 +30887,7 @@
30833
30887
  "horizontalMotionEffect",
30834
30888
  "id",
30835
30889
  "left",
30890
+ "keepHardwareMode",
30836
30891
  "layout",
30837
30892
  "opacity",
30838
30893
  "overrideCurrentAnimation",
@@ -31000,6 +31055,7 @@
31000
31055
  "horizontalMotionEffect",
31001
31056
  "id",
31002
31057
  "left",
31058
+ "keepHardwareMode",
31003
31059
  "layout",
31004
31060
  "opacity",
31005
31061
  "overrideCurrentAnimation",
@@ -31184,6 +31240,7 @@
31184
31240
  "horizontalMotionEffect",
31185
31241
  "id",
31186
31242
  "left",
31243
+ "keepHardwareMode",
31187
31244
  "opacity",
31188
31245
  "overrideCurrentAnimation",
31189
31246
  "pullBackgroundColor",
@@ -31735,6 +31792,7 @@
31735
31792
  "horizontalMotionEffect",
31736
31793
  "id",
31737
31794
  "left",
31795
+ "keepHardwareMode",
31738
31796
  "layout",
31739
31797
  "opacity",
31740
31798
  "overrideCurrentAnimation",
@@ -31784,6 +31842,8 @@
31784
31842
  "assetsDirectory",
31785
31843
  "html",
31786
31844
  "keyboardDisplayRequiresUserAction",
31845
+ "hideKeyboardAccessoryView",
31846
+ "autoAdjustScrollViewInsets",
31787
31847
  "ignoreSslError",
31788
31848
  "loading",
31789
31849
  "onCreateWindow",
@@ -32016,6 +32076,7 @@
32016
32076
  "horizontalMotionEffect",
32017
32077
  "id",
32018
32078
  "left",
32079
+ "keepHardwareMode",
32019
32080
  "layout",
32020
32081
  "opacity",
32021
32082
  "overrideCurrentAnimation",
@@ -1,4 +1,4 @@
1
- // ./purgetss/config.js
1
+ // ./purgetss/config.cjs
2
2
  module.exports = {
3
3
  purge: {
4
4
  mode: 'all',
@@ -12,6 +12,20 @@ module.exports = {
12
12
  plugins: [] // Array of properties to ignore
13
13
  }
14
14
  },
15
+ brand: {
16
+ splash: false, // also generate splash_icon.png × 5
17
+ padding: '15%', // Android safe-zone. Range: 12% tight (mature logos) — 20% conservative. Spec floor 19.44%.
18
+ iosPadding: '4%', // iOS aesthetic. Range: 2% bold — 8% conservative. No launcher mask.
19
+ darkBgColor: null, // opaque dark bg for DefaultIcon-Dark.png (null = transparent per Apple HIG)
20
+ bgColor: '#FFFFFF', // Android adaptive bg + iOS/marketplace flatten
21
+ notification: false, // also generate ic_stat_notify.png × 5
22
+ confirmOverwrites: true // prompt before overwriting files (set false to skip)
23
+ },
24
+ images: {
25
+ quality: 85, // JPEG/WebP/AVIF quality (0-100)
26
+ format: null, // null = keep original; 'webp' | 'jpeg' | 'png' to convert every image
27
+ confirmOverwrites: true // prompt before overwriting files (set false to skip)
28
+ },
15
29
  theme: {
16
30
  extend: {}
17
31
  }
@@ -315,7 +315,13 @@ function Animation(args = {}) {
315
315
 
316
316
  if (!layout) {
317
317
  view.animate(Ti.UI.createAnimation({ ...args, zIndex: 0, opacity: 0 }), () => {
318
- view.applyProperties({ zIndex: 0, opacity: 0, transform: Ti.UI.createMatrix2D(), translation: { x: 0, y: 0 }, rotate: 0, scale: 1 })
318
+ if (params.isIOS) {
319
+ // Preserve transform so next fade-in continues from last visual state
320
+ view.applyProperties({ zIndex: 0, opacity: 0, touchEnabled: false })
321
+ } else {
322
+ // Android: reset transform to avoid animator glitches on next fade-in
323
+ view.applyProperties({ zIndex: 0, opacity: 0, touchEnabled: false, transform: Ti.UI.createMatrix2D(), translation: { x: 0, y: 0 }, rotate: 0, scale: 1 })
324
+ }
319
325
  })
320
326
  return
321
327
  }
@@ -342,7 +348,7 @@ function Animation(args = {}) {
342
348
  if (layout.opacity !== undefined) animation.opacity = layout.opacity
343
349
 
344
350
  view.animate(animation, () => {
345
- const props = { transform, translation: { x: tx, y: ty }, rotate, scale, zIndex: layout.zIndex }
351
+ const props = { transform, translation: { x: tx, y: ty }, rotate, scale, zIndex: layout.zIndex, touchEnabled: true }
346
352
  if (needsFadeIn) props.opacity = 1
347
353
  view.applyProperties(props)
348
354
  })
@@ -467,13 +473,6 @@ function Animation(args = {}) {
467
473
  const target = directTarget ?? params.lastKnownTarget
468
474
  logger(` -> collision check: ${draggableView.id} | direct: ${directTarget?.id ?? 'null'} | lastKnown: ${params.lastKnownTarget?.id ?? 'null'} | final: ${target?.id ?? 'null'}`)
469
475
  if (target) {
470
- // On Android, consolidate drag position before snap to avoid animation conflict
471
- if (!params.isIOS) {
472
- draggableView.applyProperties({
473
- top: draggableView._visualTop ?? draggableView.top,
474
- left: draggableView._visualLeft ?? draggableView.left
475
- })
476
- }
477
476
  if (args.animationProperties?.snap?.center) {
478
477
  logger(` -> snap-center: ${draggableView.id} to ${target.id}`)
479
478
  animationView.snapTo(draggableView, [target])
@@ -485,14 +484,6 @@ function Animation(args = {}) {
485
484
  } else if (!target && args.animationProperties?.snap?.back) {
486
485
  logger(` -> bounce-back: ${draggableView.id} to (${draggableView._originTop}, ${draggableView._originLeft})`)
487
486
 
488
- // On Android, consolidate drag position before bounce-back to avoid animation conflict
489
- if (!params.isIOS) {
490
- draggableView.applyProperties({
491
- top: draggableView._visualTop ?? draggableView.top,
492
- left: draggableView._visualLeft ?? draggableView.left
493
- })
494
- }
495
-
496
487
  draggableView._bouncingBack = true
497
488
 
498
489
  draggableView.animate({
@@ -581,8 +572,8 @@ function Animation(args = {}) {
581
572
  draggableView.applyProperties({ duration: 0, transform: Ti.UI.createMatrix2D().translate(x, y) })
582
573
  }
583
574
  } else {
584
- const r = draggableView.rotate ?? 0
585
575
  const s = draggableView.scale ?? 1
576
+ const r = draggableView.rotate ?? 0
586
577
 
587
578
  if (r !== 0 || s !== 1) {
588
579
  // Delta-based drag for transformed views on Android
@@ -592,14 +583,20 @@ function Animation(args = {}) {
592
583
  translation.x += deltaX
593
584
  translation.y += deltaY
594
585
  draggableView.animate(Ti.UI.createAnimation({
595
- transform: Ti.UI.createMatrix2D().translate(translation.x, translation.y).rotate(r).scale(s),
596
- duration: 0
597
- }))
598
- draggableView.translation = translation
599
- draggableView.rotate = r
600
- draggableView.scale = s
586
+ duration: 0,
587
+ transform: Ti.UI.createMatrix2D().translate(translation.x, translation.y).rotate(r).scale(s)
588
+ }), () => {
589
+ draggableView.applyProperties({ translation: translation, rotate: r, scale: s })
590
+ })
601
591
  } else {
602
- draggableView.animate(Ti.UI.createAnimation({ top, left, duration: 0 }))
592
+ // Transform-based (mirror of iOS Rama B) — keeps top/left untouched so snapTo/transition stay coherent
593
+ const { x, y } = calculateTranslation(draggableView, draggableView.parent.rect, left, top)
594
+ draggableView.animate(Ti.UI.createAnimation({
595
+ duration: 0,
596
+ transform: Ti.UI.createMatrix2D().translate(x, y)
597
+ }), () => {
598
+ draggableView.applyProperties({ translation: { x, y }, rotate: 0, scale: 1 })
599
+ })
603
600
  }
604
601
  }
605
602
 
@@ -905,3 +902,45 @@ function saveComponent({ source, directory = Ti.Filesystem.tempDirectory }) {
905
902
  exports.saveComponent = saveComponent
906
903
 
907
904
  exports.createAnimation = (args) => new Animation(args)
905
+
906
+ // --- Appearance Management (Light/Dark/System) ---
907
+ function Appearance() {
908
+ const PROP_KEY = 'userInterfaceStyle'
909
+ const STYLES = {
910
+ dark: Ti.UI.USER_INTERFACE_STYLE_DARK,
911
+ light: Ti.UI.USER_INTERFACE_STYLE_LIGHT,
912
+ system: Ti.UI.USER_INTERFACE_STYLE_UNSPECIFIED
913
+ }
914
+
915
+ let currentMode = 'system'
916
+
917
+ function applyMode(mode) {
918
+ currentMode = mode
919
+ Ti.UI.overrideUserInterfaceStyle = STYLES[mode]
920
+ Ti.App.Properties.setInt(PROP_KEY, STYLES[mode])
921
+ }
922
+
923
+ return {
924
+ init() {
925
+ const saved = Ti.App.Properties.getInt(PROP_KEY, STYLES.system)
926
+ currentMode = Object.keys(STYLES).find(key => STYLES[key] === saved) || 'system'
927
+ Ti.UI.overrideUserInterfaceStyle = saved
928
+ },
929
+
930
+ set(mode) {
931
+ if (!STYLES.hasOwnProperty(mode)) return
932
+ applyMode(mode)
933
+ },
934
+
935
+ get() {
936
+ return currentMode
937
+ },
938
+
939
+ toggle() {
940
+ const next = (currentMode === 'dark') ? 'light' : 'dark'
941
+ applyMode(next)
942
+ }
943
+ }
944
+ }
945
+
946
+ exports.Appearance = Appearance()
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "purgetss",
4
- "version": "7.5.2",
4
+ "version": "7.6.1",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
7
  "purgetss": "bin/purgetss"
@@ -68,6 +68,7 @@
68
68
  },
69
69
  "homepage": "https://github.com/macCesar/purgeTSS#readme",
70
70
  "dependencies": {
71
+ "acorn": "^8.11.0",
71
72
  "chalk": "^5.6.2",
72
73
  "chroma-js": "^3.1.2",
73
74
  "command-exists": "^1.2.9",
@@ -79,6 +80,7 @@
79
80
  "lodash": "^4.17.21",
80
81
  "ntcjs": "^1.1.3",
81
82
  "path": "^0.12.7",
83
+ "sharp": "^0.33.5",
82
84
  "tailwindcss": "^3.4.17",
83
85
  "traverse": "^0.6.11",
84
86
  "update-notifier": "^7.3.1",
@@ -0,0 +1,69 @@
1
+ /**
2
+ * PurgeTSS - Brand Command
3
+ *
4
+ * Generates the complete Titanium branding set (icons + splash + marketplace)
5
+ * from logos auto-discovered in `./purgetss/brand/`, with optional overrides
6
+ * from `purgetss/config.cjs` (brand section) and CLI flags.
7
+ *
8
+ * Default behavior writes directly into the project (in-place) because
9
+ * purgetss commands always operate on the current project. Use --output to
10
+ * stage elsewhere, or --dry-run to preview without writing.
11
+ *
12
+ * @fileoverview Brand command entry point
13
+ * @author César Estrada
14
+ */
15
+
16
+ import path from 'path'
17
+ import chalk from 'chalk'
18
+ import { runBranding } from '../../core/branding/index.js'
19
+ import { logger } from '../../core/branding/branding-logger.js'
20
+ import { resolveBrandConfig } from '../../core/branding/brand-config.js'
21
+ import { ensureBrandSection } from '../../core/branding/ensure-brand-section.js'
22
+
23
+ export async function brand(cliLogo, options = {}) {
24
+ if (options.debug) logger.setDebugMode(true)
25
+
26
+ const projectRoot = options.project ? path.resolve(options.project) : process.cwd()
27
+
28
+ // Backfill the `brand:` block into older config.cjs files. No-op when
29
+ // config is missing or already has the section. Only runs against the
30
+ // project cwd, not against --project overrides (keeping ops localized).
31
+ if (!options.project) ensureBrandSection()
32
+
33
+ const resolved = resolveBrandConfig(options, cliLogo, projectRoot)
34
+
35
+ // Helpful error when no logo is found and we're not in cleanup-only mode.
36
+ if (!resolved.logo && !resolved.cleanupLegacy) {
37
+ printMissingLogoHelp(projectRoot)
38
+ process.exit(1)
39
+ }
40
+
41
+ try {
42
+ await runBranding({
43
+ ...resolved,
44
+ // Always in-place unless --output <dir> is given (which runBranding handles).
45
+ inPlace: !resolved.output,
46
+ yes: Boolean(options.yes)
47
+ })
48
+ } catch (err) {
49
+ logger.error(err.message)
50
+ if (options.debug) console.error(err.stack)
51
+ process.exit(1)
52
+ }
53
+ }
54
+
55
+ function printMissingLogoHelp(projectRoot) {
56
+ const rel = (p) => path.relative(projectRoot, p) || '.'
57
+ const brandDir = path.join(projectRoot, 'purgetss', 'brand')
58
+
59
+ logger.error('No logo image found.')
60
+ console.log()
61
+ console.log(` Expected ${chalk.cyan(rel(brandDir) + '/logo.svg')} or ${chalk.cyan(rel(brandDir) + '/logo.png')}.`)
62
+ console.log(` The ${chalk.cyan(rel(brandDir) + '/')} folder already exists — just drop your logo into it:`)
63
+ console.log(` ${chalk.cyan('cp your-logo.svg ' + rel(brandDir) + '/logo.svg')}`)
64
+ console.log()
65
+ console.log(' Alternatives:')
66
+ console.log(` ${chalk.gray('•')} Pass the logo explicitly: ${chalk.cyan('purgetss brand path/to/logo.svg')}`)
67
+ console.log(` ${chalk.gray('•')} Point to it from config.cjs: ${chalk.gray('brand: { logo: \'./docs/logo.svg\' }')}`)
68
+ console.log()
69
+ }
@@ -60,7 +60,8 @@ function createProject(workspace, argsName, projectID, options) {
60
60
  const projectName = `"${argsName}"`
61
61
  const projectDirectory = `${workspace}/${projectName}`
62
62
 
63
- logger.info('Creating a new Titanium project')
63
+ logger.startSection()
64
+ logger.info('Creating Titanium project', chalk.yellow(`'${argsName}'`), 'in', chalk.yellow(workspace))
64
65
  execSync(`ti create -n ${projectName} -t app -p all --alloy --no-prompt --id ${projectID}`)
65
66
  execSync(`cd ${projectDirectory} && purgetss w && purgetss b`)
66
67
 
@@ -117,6 +118,7 @@ function createProject(workspace, argsName, projectID, options) {
117
118
  }
118
119
 
119
120
  finish(`The ${chalk.yellow(`'${argsName}'`)} project was created successfully in`)
121
+ logger.endSection()
120
122
 
121
123
  // Auto-open editor like original v6
122
124
  if (commandExistsSync.sync('code')) {
@@ -183,12 +185,14 @@ export function create(args, options) {
183
185
  }
184
186
  } else {
185
187
  console.log('')
186
- logger.error('::Can\'t create a Titanium project::')
187
- logger.info('You must have', chalk.green('`app.idprefix`'), 'and', chalk.green('`app.workspace`'), 'configured to create a project with', chalk.green('`PurgeTSS`'))
188
- console.log('')
189
- logger.info('Please, set them like this:')
190
- logger.info(chalk.green('ti config app.idprefix'), chalk.yellow(`'com.your.reverse.domain'`))
191
- logger.info(chalk.green('ti config app.workspace'), chalk.yellow(`'path/to/your/workspace'`))
188
+ logger.block(
189
+ chalk.red("Can't create a Titanium project"),
190
+ `You must have ${chalk.green('`app.idprefix`')} and ${chalk.green('`app.workspace`')} configured to create a project with ${chalk.green('`PurgeTSS`')}`,
191
+ '',
192
+ 'Please, set them like this:',
193
+ ` ${chalk.green('ti config app.idprefix')} ${chalk.yellow(`'com.your.reverse.domain'`)}`,
194
+ ` ${chalk.green('ti config app.workspace')} ${chalk.yellow(`'path/to/your/workspace'`)}`
195
+ )
192
196
  }
193
197
  })
194
198
  }