@maccesar/titools 2.0.7 → 2.2.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 (133) hide show
  1. package/AGENTS-VERCEL-RESEARCH.md +172 -0
  2. package/README.md +408 -389
  3. package/agents/{ti-researcher.md → ti-pro.md} +14 -13
  4. package/bin/titools.js +16 -12
  5. package/lib/cleanup.js +95 -0
  6. package/lib/commands/agents.js +146 -70
  7. package/lib/commands/skills.js +349 -0
  8. package/lib/commands/uninstall.js +189 -154
  9. package/lib/commands/update.js +104 -21
  10. package/lib/config.js +26 -20
  11. package/lib/downloader.js +1 -1
  12. package/lib/installer.js +37 -97
  13. package/lib/platform.js +9 -5
  14. package/lib/prompts/checkboxCancel.js +264 -0
  15. package/lib/prompts/selectCancel.js +204 -0
  16. package/lib/symlink.js +19 -7
  17. package/lib/utils.js +17 -17
  18. package/package.json +10 -10
  19. package/skills/alloy-guides/SKILL.md +8 -30
  20. package/skills/alloy-guides/references/CLI_TASKS.md +14 -24
  21. package/skills/alloy-guides/references/CONCEPTS.md +5 -25
  22. package/skills/alloy-guides/references/CONTROLLERS.md +2 -19
  23. package/skills/alloy-guides/references/MODELS.md +40 -10
  24. package/skills/alloy-guides/references/PURGETSS.md +1 -11
  25. package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +8 -14
  26. package/skills/alloy-guides/references/VIEWS_STYLES.md +14 -25
  27. package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +2 -8
  28. package/skills/alloy-guides/references/VIEWS_XML.md +29 -40
  29. package/skills/alloy-guides/references/WIDGETS.md +1 -17
  30. package/skills/alloy-howtos/SKILL.md +1 -22
  31. package/skills/alloy-howtos/references/best_practices.md +0 -17
  32. package/skills/alloy-howtos/references/cli_reference.md +1 -23
  33. package/skills/alloy-howtos/references/config_files.md +86 -15
  34. package/skills/alloy-howtos/references/custom_tags.md +14 -13
  35. package/skills/alloy-howtos/references/debugging_troubleshooting.md +3 -22
  36. package/skills/alloy-howtos/references/samples.md +4 -14
  37. package/skills/purgetss/SKILL.md +11 -37
  38. package/skills/purgetss/references/EXAMPLES.md +106 -17
  39. package/skills/purgetss/references/animation-system.md +0 -67
  40. package/skills/purgetss/references/apply-directive.md +0 -18
  41. package/skills/purgetss/references/arbitrary-values.md +0 -25
  42. package/skills/purgetss/references/class-index.md +0 -46
  43. package/skills/purgetss/references/cli-commands.md +0 -41
  44. package/skills/purgetss/references/configurable-properties.md +0 -55
  45. package/skills/purgetss/references/custom-rules.md +0 -14
  46. package/skills/purgetss/references/customization-deep-dive.md +0 -49
  47. package/skills/purgetss/references/dynamic-component-creation.md +56 -37
  48. package/skills/purgetss/references/grid-layout.md +0 -32
  49. package/skills/purgetss/references/icon-fonts.md +0 -50
  50. package/skills/purgetss/references/installation-setup.md +61 -32
  51. package/skills/purgetss/references/migration-guide.md +127 -0
  52. package/skills/purgetss/references/opacity-modifier.md +0 -34
  53. package/skills/purgetss/references/performance-tips.md +261 -0
  54. package/skills/purgetss/references/platform-modifiers.md +0 -46
  55. package/skills/purgetss/references/smart-mappings.md +0 -12
  56. package/skills/purgetss/references/tikit-components.md +393 -0
  57. package/skills/purgetss/references/titanium-resets.md +1 -27
  58. package/skills/purgetss/references/ui-ux-design.md +467 -117
  59. package/skills/{alloy-expert → ti-expert}/SKILL.md +45 -54
  60. package/skills/ti-expert/references/alloy-builtins.md +387 -0
  61. package/skills/{alloy-expert → ti-expert}/references/alloy-structure.md +147 -40
  62. package/skills/ti-expert/references/anti-patterns.md +90 -0
  63. package/skills/ti-expert/references/cli-expert.md +109 -0
  64. package/skills/{alloy-expert → ti-expert}/references/code-conventions.md +182 -49
  65. package/skills/{alloy-expert → ti-expert}/references/controller-patterns.md +103 -93
  66. package/skills/{alloy-expert → ti-expert}/references/error-handling.md +12 -12
  67. package/skills/{alloy-expert → ti-expert}/references/examples.md +144 -85
  68. package/skills/{alloy-expert → ti-expert}/references/migration-patterns.md +38 -36
  69. package/skills/{alloy-expert → ti-expert}/references/patterns.md +8 -8
  70. package/skills/ti-expert/references/performance-listview.md +251 -0
  71. package/skills/{alloy-expert/references/performance-patterns.md → ti-expert/references/performance-optimization.md} +67 -283
  72. package/skills/{alloy-expert/references/security-patterns.md → ti-expert/references/security-device.md} +12 -295
  73. package/skills/ti-expert/references/security-fundamentals.md +284 -0
  74. package/skills/{alloy-expert → ti-expert}/references/state-management.md +10 -10
  75. package/skills/ti-expert/references/testing-e2e-ci.md +432 -0
  76. package/skills/ti-expert/references/testing-unit.md +433 -0
  77. package/skills/ti-expert/references/theming.md +394 -0
  78. package/skills/ti-guides/SKILL.md +1 -14
  79. package/skills/ti-guides/references/advanced-data-and-images.md +126 -10
  80. package/skills/ti-guides/references/app-distribution.md +48 -10
  81. package/skills/ti-guides/references/application-frameworks.md +9 -2
  82. package/skills/ti-guides/references/cli-reference.md +338 -82
  83. package/skills/ti-guides/references/coding-best-practices.md +14 -4
  84. package/skills/ti-guides/references/commonjs-advanced.md +103 -6
  85. package/skills/ti-guides/references/hello-world.md +5 -2
  86. package/skills/ti-guides/references/hyperloop-native-access.md +439 -46
  87. package/skills/ti-guides/references/javascript-primer.md +4 -3
  88. package/skills/ti-guides/references/resources.md +0 -1
  89. package/skills/ti-guides/references/style-and-conventions.md +57 -9
  90. package/skills/ti-guides/references/tiapp-config.md +48 -6
  91. package/skills/ti-howtos/SKILL.md +2 -27
  92. package/skills/ti-howtos/references/android-platform-deep-dives.md +41 -90
  93. package/skills/ti-howtos/references/automation-fastlane-appium.md +15 -14
  94. package/skills/ti-howtos/references/buffer-codec-streams.md +22 -22
  95. package/skills/ti-howtos/references/cross-platform-development.md +68 -37
  96. package/skills/ti-howtos/references/debugging-profiling.md +8 -53
  97. package/skills/ti-howtos/references/extending-titanium.md +12 -51
  98. package/skills/ti-howtos/references/google-maps-v2.md +30 -29
  99. package/skills/ti-howtos/references/ios-map-kit.md +19 -25
  100. package/skills/ti-howtos/references/ios-platform-deep-dives.md +95 -283
  101. package/skills/ti-howtos/references/local-data-sources.md +45 -37
  102. package/skills/ti-howtos/references/location-and-maps.md +47 -28
  103. package/skills/ti-howtos/references/media-apis.md +84 -27
  104. package/skills/ti-howtos/references/notification-services.md +40 -89
  105. package/skills/ti-howtos/references/remote-data-sources.md +21 -36
  106. package/skills/ti-howtos/references/tutorials.md +79 -39
  107. package/skills/ti-howtos/references/using-modules.md +4 -30
  108. package/skills/ti-howtos/references/web-content-integration.md +28 -45
  109. package/skills/ti-howtos/references/webpack-build-pipeline.md +54 -18
  110. package/skills/ti-ui/SKILL.md +1 -21
  111. package/skills/ti-ui/references/accessibility-deep-dive.md +71 -25
  112. package/skills/ti-ui/references/animation-and-matrices.md +0 -47
  113. package/skills/ti-ui/references/application-structures.md +0 -46
  114. package/skills/ti-ui/references/custom-fonts-styling.md +9 -57
  115. package/skills/ti-ui/references/event-handling.md +32 -40
  116. package/skills/ti-ui/references/gestures.md +0 -40
  117. package/skills/ti-ui/references/icons-and-splash-screens.md +21 -44
  118. package/skills/ti-ui/references/layouts-and-positioning.md +47 -39
  119. package/skills/ti-ui/references/listviews-and-performance.md +187 -73
  120. package/skills/ti-ui/references/orientation.md +57 -71
  121. package/skills/ti-ui/references/platform-ui-android.md +114 -48
  122. package/skills/ti-ui/references/platform-ui-ios.md +63 -38
  123. package/skills/ti-ui/references/scrolling-views.md +14 -23
  124. package/skills/ti-ui/references/tableviews.md +6 -56
  125. package/AGENTS-TEMPLATE.md +0 -174
  126. package/lib/commands/install.js +0 -188
  127. package/skills/alloy-expert/references/anti-patterns.md +0 -133
  128. package/skills/alloy-expert/references/testing.md +0 -872
  129. package/skills/ti-guides/references/alloy-cli-advanced.md +0 -84
  130. package/skills/ti-guides/references/alloy-data-mastery.md +0 -29
  131. package/skills/ti-guides/references/alloy-widgets-and-themes.md +0 -19
  132. /package/skills/{alloy-expert → ti-expert}/assets/ControllerAutoCleanup.js +0 -0
  133. /package/skills/{alloy-expert → ti-expert}/references/contracts.md +0 -0
@@ -0,0 +1,394 @@
1
+ # Theming & Dark Mode
2
+
3
+ ## Alloy Theme System
4
+
5
+ Alloy has a built-in theme system via the `app/themes/` folder. Themes override views, styles, and assets without modifying originals.
6
+
7
+ ### Theme Folder Structure
8
+
9
+ ```
10
+ app/
11
+ ├── themes/
12
+ │ ├── light/ # Light theme (or default)
13
+ │ │ ├── styles/
14
+ │ │ │ └── app.tss # Overrides app/styles/app.tss
15
+ │ │ └── assets/
16
+ │ │ └── images/
17
+ │ │ └── logo.png # Overrides app/assets/images/logo.png
18
+ │ └── dark/ # Dark theme
19
+ │ ├── styles/
20
+ │ │ └── app.tss # Dark overrides
21
+ │ └── assets/
22
+ │ └── images/
23
+ │ └── logo.png # Dark version of logo
24
+ ├── styles/
25
+ │ └── app.tss # Base styles (used when no theme active)
26
+ ├── config.json
27
+ ```
28
+
29
+ ### Activating a Theme
30
+
31
+ Set the theme in `config.json`:
32
+
33
+ ```json
34
+ {
35
+ "global": {
36
+ "theme": "light"
37
+ },
38
+ "env:development": {
39
+ "theme": "light"
40
+ }
41
+ }
42
+ ```
43
+
44
+ :::warning Theme is compile-time only
45
+ Alloy themes are applied at **build time**. You cannot switch themes at runtime using this system alone. For runtime switching, use the Dynamic Theming approach below.
46
+ :::
47
+
48
+ ### Theme Cascade
49
+
50
+ When a theme is active, Alloy merges files in this order:
51
+ 1. `app/styles/app.tss` (base)
52
+ 2. `app/themes/<theme>/styles/app.tss` (theme override)
53
+ 3. `app/styles/<controller>.tss` (controller-specific)
54
+ 4. `app/themes/<theme>/styles/<controller>.tss` (theme + controller override)
55
+
56
+ Theme files **merge** with base files — you only need to include properties you want to override.
57
+
58
+ ---
59
+
60
+ ## Dynamic Theming with Alloy.Globals
61
+
62
+ For runtime theme switching (including Dark Mode), use `Alloy.Globals` as a centralized color palette.
63
+
64
+ ### Step 1: Define Color Palette
65
+
66
+ ```javascript
67
+ // alloy.js
68
+ const themes = {
69
+ light: {
70
+ bg: '#ffffff',
71
+ bgSecondary: '#f3f4f6',
72
+ text: '#111827',
73
+ textSecondary: '#6b7280',
74
+ primary: '#2563eb',
75
+ primaryText: '#ffffff',
76
+ border: '#e5e7eb',
77
+ card: '#ffffff',
78
+ danger: '#ef4444',
79
+ success: '#22c55e'
80
+ },
81
+ dark: {
82
+ bg: '#111827',
83
+ bgSecondary: '#1f2937',
84
+ text: '#f9fafb',
85
+ textSecondary: '#9ca3af',
86
+ primary: '#3b82f6',
87
+ primaryText: '#ffffff',
88
+ border: '#374151',
89
+ card: '#1f2937',
90
+ danger: '#f87171',
91
+ success: '#4ade80'
92
+ }
93
+ }
94
+
95
+ // Load saved preference or default to light
96
+ const savedTheme = Ti.App.Properties.getString('app:theme', 'light')
97
+ Alloy.Globals.theme = themes[savedTheme]
98
+ Alloy.Globals.themeName = savedTheme
99
+ ```
100
+
101
+ ### Step 2: Use in TSS with Alloy.Globals
102
+
103
+ ```tss
104
+ /* app.tss - Global base styles using theme colors */
105
+ "Window": {
106
+ backgroundColor: Alloy.Globals.theme.bg
107
+ }
108
+
109
+ "Label": {
110
+ color: Alloy.Globals.theme.text
111
+ }
112
+
113
+ ".card": {
114
+ backgroundColor: Alloy.Globals.theme.card,
115
+ borderWidth: 1,
116
+ borderColor: Alloy.Globals.theme.border,
117
+ borderRadius: 12
118
+ }
119
+
120
+ ".btn-primary": {
121
+ backgroundColor: Alloy.Globals.theme.primary,
122
+ color: Alloy.Globals.theme.primaryText,
123
+ height: 48,
124
+ borderRadius: 8,
125
+ font: { fontWeight: 'bold' }
126
+ }
127
+
128
+ ".text-secondary": {
129
+ color: Alloy.Globals.theme.textSecondary
130
+ }
131
+
132
+ ".bg-secondary": {
133
+ backgroundColor: Alloy.Globals.theme.bgSecondary
134
+ }
135
+ ```
136
+
137
+ ### Step 3: Theme Switching Service
138
+
139
+ ```javascript
140
+ // lib/services/themeService.js
141
+ const EventBus = require('services/eventBus')
142
+
143
+ const themes = {
144
+ light: { /* ... same as alloy.js ... */ },
145
+ dark: { /* ... same as alloy.js ... */ }
146
+ }
147
+
148
+ exports.ThemeService = {
149
+ getCurrentTheme() {
150
+ return Alloy.Globals.themeName
151
+ },
152
+
153
+ setTheme(name) {
154
+ if (!themes[name]) return
155
+
156
+ Alloy.Globals.theme = themes[name]
157
+ Alloy.Globals.themeName = name
158
+ Ti.App.Properties.setString('app:theme', name)
159
+
160
+ // Notify all listeners
161
+ EventBus.trigger('theme:changed', { theme: name })
162
+ },
163
+
164
+ toggle() {
165
+ const next = Alloy.Globals.themeName === 'light' ? 'dark' : 'light'
166
+ this.setTheme(next)
167
+ },
168
+
169
+ // Follow system Dark Mode (iOS 13+, Android 10+)
170
+ followSystem() {
171
+ if (Ti.UI.semanticColorType !== undefined) {
172
+ const isDark = Ti.UI.semanticColorType === Ti.UI.SEMANTIC_COLOR_TYPE_DARK
173
+ this.setTheme(isDark ? 'dark' : 'light')
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ ### Step 4: Respond to Theme Changes in Controllers
180
+
181
+ :::warning Theme change requires UI rebuild
182
+ Since TSS is applied at controller creation time, changing `Alloy.Globals.theme` does NOT retroactively update already-rendered views. You must manually update visible elements or restart the root controller.
183
+ :::
184
+
185
+ **Approach A: Rebuild root controller (simplest)**
186
+
187
+ ```javascript
188
+ // controllers/settings.js
189
+ const { ThemeService } = require('services/themeService')
190
+
191
+ function onToggleTheme() {
192
+ ThemeService.toggle()
193
+
194
+ // Restart app from root controller
195
+ const root = Alloy.createController('index')
196
+ root.getView().open()
197
+
198
+ // Close current window tree
199
+ $.getView().close()
200
+ }
201
+ ```
202
+
203
+ **Approach B: Update visible elements in-place (smoother)**
204
+
205
+ ```javascript
206
+ // controllers/settings.js
207
+ const EventBus = require('services/eventBus')
208
+ const { ThemeService } = require('services/themeService')
209
+
210
+ function init() {
211
+ EventBus.on('theme:changed', applyTheme)
212
+ }
213
+
214
+ function applyTheme() {
215
+ const t = Alloy.Globals.theme
216
+
217
+ $.settingsWindow.applyProperties({ backgroundColor: t.bg })
218
+ $.titleLabel.applyProperties({ color: t.text })
219
+ $.themeToggle.applyProperties({ backgroundColor: t.bgSecondary })
220
+ }
221
+
222
+ function onToggleTheme() {
223
+ ThemeService.toggle()
224
+ }
225
+
226
+ function cleanup() {
227
+ EventBus.off('theme:changed', applyTheme)
228
+ $.destroy()
229
+ }
230
+
231
+ $.cleanup = cleanup
232
+ ```
233
+
234
+ ---
235
+
236
+ ## System Dark Mode Detection
237
+
238
+ ### iOS 13+ / Android 10+
239
+
240
+ ```javascript
241
+ // lib/services/themeService.js (add to existing service)
242
+
243
+ exports.ThemeService = {
244
+ // ... existing methods ...
245
+
246
+ // Initialize system theme listener
247
+ initSystemListener() {
248
+ // Detect current system theme
249
+ this.followSystem()
250
+
251
+ // Listen for system theme changes
252
+ Ti.App.addEventListener('significantTimeChange', () => {
253
+ this.followSystem()
254
+ })
255
+
256
+ // iOS: userInterfaceStyle change
257
+ if (OS_IOS) {
258
+ Ti.App.addEventListener('traitCollectionChange', () => {
259
+ this.followSystem()
260
+ })
261
+ }
262
+ }
263
+ }
264
+ ```
265
+
266
+ ### Semantic Colors (iOS)
267
+
268
+ Titanium supports iOS semantic colors that automatically adapt to Dark Mode:
269
+
270
+ ```tss
271
+ /* These colors automatically switch in Dark Mode */
272
+ "#label": {
273
+ color: Ti.UI.fetchSemanticColor('label')
274
+ }
275
+
276
+ "#bg": {
277
+ backgroundColor: Ti.UI.fetchSemanticColor('systemBackground')
278
+ }
279
+ ```
280
+
281
+ Available semantic colors: `label`, `secondaryLabel`, `systemBackground`, `secondarySystemBackground`, `separator`, `systemFill`, etc.
282
+
283
+ :::tip When to use Semantic Colors vs Alloy.Globals
284
+ - **Semantic Colors**: Quick adaptation for iOS only. No Android support.
285
+ - **Alloy.Globals palette**: Cross-platform, full control, consistent behavior on both platforms. **Recommended.**
286
+ :::
287
+
288
+ ---
289
+
290
+ ## Reusable Style Classes Pattern
291
+
292
+ Define a library of reusable TSS classes in `app.tss` for consistency across the app:
293
+
294
+ ```tss
295
+ /* app.tss - Design System */
296
+
297
+ /* === Layout === */
298
+ ".row": { layout: 'horizontal', width: Ti.UI.FILL, height: Ti.UI.SIZE }
299
+ ".col": { layout: 'vertical', width: Ti.UI.FILL, height: Ti.UI.SIZE }
300
+ ".centered": { width: Ti.UI.SIZE, height: Ti.UI.SIZE }
301
+
302
+ /* === Cards === */
303
+ ".card": {
304
+ backgroundColor: Alloy.Globals.theme.card,
305
+ borderRadius: 12,
306
+ borderWidth: 1,
307
+ borderColor: Alloy.Globals.theme.border,
308
+ width: Ti.UI.FILL,
309
+ height: Ti.UI.SIZE
310
+ }
311
+
312
+ /* === Buttons === */
313
+ ".btn-primary": {
314
+ backgroundColor: Alloy.Globals.theme.primary,
315
+ color: Alloy.Globals.theme.primaryText,
316
+ height: 48,
317
+ width: Ti.UI.FILL,
318
+ borderRadius: 8,
319
+ font: { fontSize: 16, fontWeight: 'bold' }
320
+ }
321
+
322
+ ".btn-secondary": {
323
+ backgroundColor: 'transparent',
324
+ color: Alloy.Globals.theme.primary,
325
+ height: 48,
326
+ width: Ti.UI.FILL,
327
+ borderRadius: 8,
328
+ borderWidth: 1,
329
+ borderColor: Alloy.Globals.theme.primary,
330
+ font: { fontSize: 16, fontWeight: 'bold' }
331
+ }
332
+
333
+ ".btn-danger": {
334
+ backgroundColor: Alloy.Globals.theme.danger,
335
+ color: '#ffffff',
336
+ height: 48,
337
+ width: Ti.UI.FILL,
338
+ borderRadius: 8,
339
+ font: { fontSize: 16, fontWeight: 'bold' }
340
+ }
341
+
342
+ /* === Typography === */
343
+ ".text-title": { font: { fontSize: 24, fontWeight: 'bold' }, color: Alloy.Globals.theme.text }
344
+ ".text-subtitle": { font: { fontSize: 18, fontWeight: 'semibold' }, color: Alloy.Globals.theme.text }
345
+ ".text-body": { font: { fontSize: 16 }, color: Alloy.Globals.theme.text }
346
+ ".text-caption": { font: { fontSize: 14 }, color: Alloy.Globals.theme.textSecondary }
347
+ ".text-small": { font: { fontSize: 12 }, color: Alloy.Globals.theme.textSecondary }
348
+
349
+ /* === Inputs === */
350
+ ".input": {
351
+ height: 48,
352
+ width: Ti.UI.FILL,
353
+ borderRadius: 8,
354
+ borderWidth: 1,
355
+ borderColor: Alloy.Globals.theme.border,
356
+ backgroundColor: Alloy.Globals.theme.bg,
357
+ color: Alloy.Globals.theme.text,
358
+ paddingLeft: 12,
359
+ font: { fontSize: 16 }
360
+ }
361
+
362
+ /* === Spacing (applied via class in XML) === */
363
+ ".mt-sm": { top: 8 }
364
+ ".mt-md": { top: 16 }
365
+ ".mt-lg": { top: 24 }
366
+ ".mt-xl": { top: 32 }
367
+ ".mx-md": { left: 16, right: 16 }
368
+ ".mx-lg": { left: 24, right: 24 }
369
+ ```
370
+
371
+ ### Using Style Classes in Views
372
+
373
+ ```xml
374
+ <Alloy>
375
+ <Window>
376
+ <View class="col mx-md">
377
+ <Label class="text-title mt-lg" text="L('settings')" />
378
+
379
+ <View class="card mt-md">
380
+ <Label class="text-body mx-md mt-md" text="L('appearance')" />
381
+ <Button class="btn-secondary mx-md mt-md" title="L('toggle_theme')" onClick="onToggleTheme" />
382
+ </View>
383
+
384
+ <Button class="btn-danger mt-xl mx-md" title="L('logout')" onClick="onLogout" />
385
+ </View>
386
+ </Window>
387
+ </Alloy>
388
+ ```
389
+
390
+ This approach gives you:
391
+ - Consistent spacing and typography across the entire app
392
+ - Theme-aware colors that change with `Alloy.Globals.theme`
393
+ - Clean XML views without inline styles
394
+ - A single place to update design decisions (`app.tss`)
@@ -28,19 +28,6 @@ This skill automatically detects Titanium projects when invoked and provides SDK
28
28
  - **Not detected** → Indicates this is for Titanium projects only
29
29
  :::
30
30
 
31
- ## Table of Contents
32
-
33
- - [Titanium SDK Guide Expert](#titanium-sdk-guide-expert)
34
- - [Project Detection](#project-detection)
35
- - [Table of Contents](#table-of-contents)
36
- - [Core Workflow](#core-workflow)
37
- - [Procedural Rules (Low Freedom)](#procedural-rules-low-freedom)
38
- - [Reference Guides (Progressive Disclosure)](#reference-guides-progressive-disclosure)
39
- - [Related Skills](#related-skills)
40
- - [Response Format](#response-format)
41
-
42
- ---
43
-
44
31
  ## Core Workflow
45
32
 
46
33
  1. **Architecture Check**: Validate that the project follows a modular pattern (CommonJS or Alloy).
@@ -82,7 +69,7 @@ For tasks beyond SDK fundamentals, use these complementary skills:
82
69
 
83
70
  | Task | Use This Skill |
84
71
  | ---------------------------------------- | -------------- |
85
- | Project architecture, services, patterns | `alloy-expert` |
72
+ | Project architecture, services, patterns | `ti-expert` |
86
73
  | Native features (location, push, media) | `ti-howtos` |
87
74
  | Alloy CLI, configuration, debugging | `alloy-howtos` |
88
75
  | UI layouts, ListViews, gestures | `ti-ui` |
@@ -1,19 +1,135 @@
1
1
  # Advanced Database and Image Best Practices
2
2
 
3
3
  ## 1. SQLite Optimization
4
- - **Close Everything**: Always `resultSet.close()` and `db.close()` after each operation to avoid locks and memory bloat.
5
- - **Transactions**: Use `BEGIN` and `COMMIT` for batch inserts. It is significantly faster.
4
+
5
+ - **Close Everything**: Always `resultSet.close()` and `db.close()` after each operation to avoid locks and memory bloat. SQLite enforces sequential write-access — if you don't close after `INSERT` or `UPDATE`, you may receive **DatabaseObjectNotClosed** exceptions on the next write attempt.
6
+ - **Transactions**: Use `BEGIN` and `COMMIT` for batch inserts. It is significantly faster. Note: if any update within the transaction fails, the **entire batch is rolled back** (atomicity).
6
7
  ```javascript
8
+ const db = Ti.Database.open('myDatabase');
7
9
  db.execute('BEGIN');
8
- // loop inserts
10
+ for (const item of playlist) {
11
+ db.execute('INSERT INTO albums (disc, artist, rating) VALUES (?, ?, ?)', item.disc, item.artist, item.rating);
12
+ }
9
13
  db.execute('COMMIT');
14
+ db.close();
10
15
  ```
11
- - **Database Skeleton**: Do not ship large pre-populated databases. Ship a "skeleton" and download data on first boot to reduce IPA/APK size.
16
+ - **Database Skeleton**: Do not ship large pre-populated databases. Ship a "skeleton" and download data on first boot to reduce IPA/APK size. The `Resources` directory is read-only, so installing a database copies it to `applicationDataDirectory`, resulting in two copies on the device.
17
+ - **Android note**: In Android 2.2 and earlier, the installer could not uncompress assets over 1 MB. Workaround: rename the file with a `.mp3` extension to prevent `aapt` compression.
18
+
19
+ ### Database Version Management
20
+
21
+ Store a version number in your database to detect which version is installed and take action (create tables, alter columns) on upgrades. If you lack a version table, use `PRAGMA TABLE_INFO` to detect column presence:
22
+
23
+ ```javascript
24
+ const addColumn = (dbname, tblName, newFieldName, colSpec) => {
25
+ const db = Ti.Database.open(dbname);
26
+ let fieldExists = false;
27
+ const resultSet = db.execute(`PRAGMA TABLE_INFO(${tblName})`);
28
+ while (resultSet.isValidRow()) {
29
+ if (resultSet.field(1) === newFieldName) {
30
+ fieldExists = true;
31
+ }
32
+ resultSet.next();
33
+ }
34
+ resultSet.close();
35
+ if (!fieldExists) {
36
+ db.execute(`ALTER TABLE ${tblName} ADD COLUMN ${newFieldName} ${colSpec}`);
37
+ }
38
+ db.close();
39
+ };
40
+ ```
41
+
42
+ ---
43
+
44
+ ## 2. Image Format Selection
45
+
46
+ Choose the right format for each use case:
47
+
48
+ | Format | Compression | Best For | Avoid For |
49
+ | ------ | ----------- | -------- | --------- |
50
+ | **PNG** | Lossless | Icons, text, line-art, buttons | Photos (file size) |
51
+ | **JPG** | Lossy | Photographs | Text, icons, line drawings (artifacts) |
52
+ | **GIF** | Lossless (256 colors) | Rarely appropriate | Almost everything — limited colors, proprietary, animated GIF not supported on all platforms |
12
53
 
13
- ## 2. Image Memory Management
14
- - **Memory Footprint**: A JPG is tiny on disk but consumes `width * height * 3 bytes` in RAM.
54
+ **For flip-book animations**: Use `ImageView.images` property with an array of PNG or optimized JPG files instead of animated GIFs.
55
+
56
+ ---
57
+
58
+ ## 3. Image Memory Management
59
+
60
+ - **Memory Footprint**: A JPG is small on disk but consumes `width * height * 3 bytes` in RAM when decompressed. Each pixel uses 24 bits (8 bits per R/G/B channel).
61
+ - Example: `(640 × 480 × 3) / 1024 = 900 KB` in memory for a single image
62
+ - Device RAM available to your app can be as low as 12 MB, shared with your code and the Titanium framework
15
63
  - **Unloading**:
16
- - `view.remove(imageView)` to help the OS free memory.
17
- - Set `imageView = null` to release the native proxy.
18
- - **Resizing**: Always resize images to their display dimensions via `imageAsResized` before setting the `image` property.
19
- - **Caching**: iOS caches remote images automatically, but you should manually cache critical assets in `applicationDataDirectory` for predictability.
64
+ - `view.remove(imageView)` removes from view hierarchy, helps OS free memory
65
+ - `imageView = null` releases the native proxy object
66
+ - **Resizing**: Always resize images to their display dimensions via `imageAsResized` or `imageAsCropped` (both on `Ti.Blob`) before setting the `image` property. Don't display a 1024×768 image on a 640×480 screen.
67
+ - **Placeholder**: Set the `defaultImage` property on ImageView to show a local placeholder while a remote image downloads:
68
+ ```javascript
69
+ const imageView = Ti.UI.createImageView({
70
+ image: 'https://example.com/photo.jpg',
71
+ defaultImage: '/images/placeholder.png',
72
+ width: 300,
73
+ height: 200
74
+ });
75
+ ```
76
+
77
+ ---
78
+
79
+ ## 4. Image Optimization Tools
80
+
81
+ Resize and compress images before including them in your app to minimize IPA/APK size and network usage.
82
+
83
+ | Platform | File Types | Tool |
84
+ | -------- | ---------- | ---- |
85
+ | Mac | PNG, JPG, GIF | ImageOptim |
86
+ | Mac, Windows, Linux | PNG, JPG, GIF | ImageMagick |
87
+ | Windows/DOS | PNG | PNGCrush |
88
+ | Windows | JPG | Nikkho |
89
+
90
+ ---
91
+
92
+ ## 5. Caching Remote Images
93
+
94
+ Remote images are cached automatically on iOS, Android, and Windows.
95
+
96
+ **Platform differences:**
97
+ - **Android**: Cache limited to **25 MB**, persists for the lifetime of the app (while installed)
98
+ - **iOS**: Cache size not predetermined (no guaranteed size), cleared by iOS when device needs storage
99
+
100
+ To manually clean the cache, delete files in `applicationCacheDirectory`.
101
+
102
+ **Android 6+ requirement**: Call `Ti.Filesystem.requestStoragePermissions()` before loading remote images (runtime permissions).
103
+
104
+ ### Manual Caching Utility
105
+
106
+ For critical assets, cache manually in `applicationDataDirectory` for predictability:
107
+
108
+ ```javascript
109
+ const CacheUtils = {
110
+ getExtension: (fn) => {
111
+ const match = /(?:\.([^.]+))?$/.exec(fn);
112
+ return match[1] || '';
113
+ },
114
+
115
+ createCachedImageView: (options) => {
116
+ const md5 = Ti.Utils.md5HexDigest(options.image) + CacheUtils.getExtension(options.image);
117
+ const savedFile = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, md5);
118
+
119
+ if (savedFile.exists()) {
120
+ options.image = savedFile;
121
+ return Ti.UI.createImageView(options);
122
+ }
123
+
124
+ const imageView = Ti.UI.createImageView(options);
125
+ const saveImage = () => {
126
+ imageView.removeEventListener('load', saveImage);
127
+ savedFile.write(
128
+ Ti.UI.createImageView({ image: imageView.image, width: Ti.UI.FILL, height: Ti.UI.FILL }).toImage()
129
+ );
130
+ };
131
+ imageView.addEventListener('load', saveImage);
132
+ return imageView;
133
+ }
134
+ };
135
+ ```
@@ -39,7 +39,7 @@ ti build -p android -T dist-playstore -K ~/android.keystore -P secret -L foo -O
39
39
 
40
40
  **Output files:**
41
41
  - `.apk` - Legacy format (still supported)
42
- - `.aab` - Android App Bundle (preferred, smaller downloads)
42
+ - `.aab` - Android App Bundle (preferred, smaller downloads). Requires Titanium 9.0.0+. An AAB file cannot be installed directly on a device; it is a publishing format for Google Play only. Once uploaded, Google Play generates multiple device-specific APKs split by CPU architecture and image density, allowing smaller downloads.
43
43
 
44
44
  ### 4. Verify APK Signing
45
45
 
@@ -81,7 +81,7 @@ adb uninstall com.your.appid
81
81
  </android>
82
82
  ```
83
83
 
84
- - `versionCode`: Integer, must increment for each update
84
+ - `versionCode`: Must be a 32-bit integer (whole number), must increment for each update. Cannot be a floating point number.
85
85
  - `versionName`: String, any format you prefer
86
86
 
87
87
  ### 7. SD Card Installation
@@ -100,13 +100,18 @@ Values: `preferExternal`, `auto`, `internalOnly`
100
100
 
101
101
  ### 1. Distribution Types
102
102
 
103
- - **App Store**: Public distribution via iTunes Store
104
- - **Ad Hoc**: Limited testing (max 100 devices)
103
+ - **App Store**: Public distribution via App Store (formerly known as iTunes Connect, now App Store Connect)
104
+ - **Ad Hoc**: Limited testing (max 100 devices per year; devices cannot be removed once registered, so use care when registering)
105
105
  - **In House**: Enterprise distribution for employees (Enterprise program only)
106
106
 
107
107
  ### 2. Create Distribution Certificate
108
108
 
109
- 1. Log in to [Apple Developer Member Center](https://developer.apple.com/membercenter/)
109
+ **Certificate types:**
110
+ - **Development certificate**: Each developer on the team can have their own, used for building test versions.
111
+ - **Distribution certificate**: A single certificate for the entire team. Only the Team Agent (the account owner who oversees final distribution) can create it. This ensures developers cannot publish the final app without authorization.
112
+
113
+ **Steps:**
114
+ 1. Log in to [Apple Developer Member Center](https://developer.apple.com/membercenter/) as Team Agent or Admin
110
115
  2. Go to **Certificates, Identifiers & Profiles** > **Certificates**
111
116
  3. Click **+** > **App Store and Ad Hoc**
112
117
  4. Create a CSR (Certificate Signing Request) in Keychain Access
@@ -124,9 +129,11 @@ Values: `preferExternal`, `auto`, `internalOnly`
124
129
  6. Name profile (include "distribution" or "ad hoc")
125
130
  7. Download `.mobileprovision` and install (drag to Xcode icon)
126
131
 
127
- ### 4. Create App ID on iTunes Connect
132
+ ### 4. Create App ID on App Store Connect
133
+
134
+ App Store Connect (formerly iTunes Connect) is the app distribution management portal.
128
135
 
129
- 1. Log in to [iTunes Connect](https://itunesconnect.apple.com)
136
+ 1. Log in to App Store Connect
130
137
  2. **Manage Your Apps** > **Add New App**
131
138
  3. Provide:
132
139
  - App name
@@ -172,11 +179,11 @@ ti build -p ios -T dist-appstore -R "Pseudo, Inc." -P "AAAAAAAA-0000-9999-8888-7
172
179
 
173
180
  The CLI installs the package to Xcode's Organizer.
174
181
 
175
- ### 6. Upload to iTunes Connect
182
+ ### 6. Upload to App Store Connect
176
183
 
177
184
  1. Open Xcode > **Window** > **Organizer**
178
185
  2. Select your app archive
179
- 3. Click **Verify** (validates against iTunes Connect app definition)
186
+ 3. Click **Verify** (validates against App Store Connect app definition)
180
187
  4. Click **Submit** (uploads app)
181
188
  5. Status changes to **Waiting for Review**
182
189
 
@@ -192,7 +199,38 @@ Testers need:
192
199
  3. Select device > **Installed Apps** > **+**
193
200
  4. Select IPA file
194
201
 
195
- ### 8. App Store Requirements
202
+ ### 8. App Thinning (iOS)
203
+
204
+ App Thinning optimizes your application by reducing its installed size on devices.
205
+
206
+ **Slicing (Asset Catalog):**
207
+ - Installs only the assets needed for the specific device (e.g., @2x images for non-Plus iPhones, @3x for Plus/Pro models)
208
+ - When enabled, Titanium automatically adds all PNG and JPEG images to an Asset Catalog
209
+ - Images with matching suffixes (@2x, @3x, etc.) are grouped into one imageset
210
+ - **Important limitation**: When slicing is enabled, you cannot access images from the filesystem using `Ti.Filesystem` or path/URL-based access. Only use image names (without path) in ImageView and similar APIs.
211
+ - Icons and launch images are always added to the Asset Catalog
212
+
213
+ **Enable slicing in tiapp.xml:**
214
+ ```xml
215
+ <ti:app>
216
+ <ios>
217
+ <use-app-thinning>true</use-app-thinning>
218
+ </ios>
219
+ </ti:app>
220
+ ```
221
+
222
+ By default, slicing is disabled.
223
+
224
+ **Bitcode:**
225
+ - Submits partially compiled code to App Store Connect, which then optimizes and compiles for specific architectures
226
+ - Currently disabled in Titanium because all frameworks (including third-party modules) must have bitcode enabled
227
+
228
+ **On-Demand Resources:**
229
+ - Resources tagged into groups, stored on Apple servers, downloaded when needed by the app
230
+ - iOS purges on-demand resources when device disk space is low
231
+ - Titanium SDK does not currently provide a way to tag on-demand resources
232
+
233
+ ### 9. App Store Requirements
196
234
 
197
235
  Apple's guidelines include:
198
236
  - Apps must be useful, well-designed, and error-free