@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.
- package/AGENTS-VERCEL-RESEARCH.md +172 -0
- package/README.md +408 -389
- package/agents/{ti-researcher.md → ti-pro.md} +14 -13
- package/bin/titools.js +16 -12
- package/lib/cleanup.js +95 -0
- package/lib/commands/agents.js +146 -70
- package/lib/commands/skills.js +349 -0
- package/lib/commands/uninstall.js +189 -154
- package/lib/commands/update.js +104 -21
- package/lib/config.js +26 -20
- package/lib/downloader.js +1 -1
- package/lib/installer.js +37 -97
- package/lib/platform.js +9 -5
- package/lib/prompts/checkboxCancel.js +264 -0
- package/lib/prompts/selectCancel.js +204 -0
- package/lib/symlink.js +19 -7
- package/lib/utils.js +17 -17
- package/package.json +10 -10
- package/skills/alloy-guides/SKILL.md +8 -30
- package/skills/alloy-guides/references/CLI_TASKS.md +14 -24
- package/skills/alloy-guides/references/CONCEPTS.md +5 -25
- package/skills/alloy-guides/references/CONTROLLERS.md +2 -19
- package/skills/alloy-guides/references/MODELS.md +40 -10
- package/skills/alloy-guides/references/PURGETSS.md +1 -11
- package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +8 -14
- package/skills/alloy-guides/references/VIEWS_STYLES.md +14 -25
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +2 -8
- package/skills/alloy-guides/references/VIEWS_XML.md +29 -40
- package/skills/alloy-guides/references/WIDGETS.md +1 -17
- package/skills/alloy-howtos/SKILL.md +1 -22
- package/skills/alloy-howtos/references/best_practices.md +0 -17
- package/skills/alloy-howtos/references/cli_reference.md +1 -23
- package/skills/alloy-howtos/references/config_files.md +86 -15
- package/skills/alloy-howtos/references/custom_tags.md +14 -13
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +3 -22
- package/skills/alloy-howtos/references/samples.md +4 -14
- package/skills/purgetss/SKILL.md +11 -37
- package/skills/purgetss/references/EXAMPLES.md +106 -17
- package/skills/purgetss/references/animation-system.md +0 -67
- package/skills/purgetss/references/apply-directive.md +0 -18
- package/skills/purgetss/references/arbitrary-values.md +0 -25
- package/skills/purgetss/references/class-index.md +0 -46
- package/skills/purgetss/references/cli-commands.md +0 -41
- package/skills/purgetss/references/configurable-properties.md +0 -55
- package/skills/purgetss/references/custom-rules.md +0 -14
- package/skills/purgetss/references/customization-deep-dive.md +0 -49
- package/skills/purgetss/references/dynamic-component-creation.md +56 -37
- package/skills/purgetss/references/grid-layout.md +0 -32
- package/skills/purgetss/references/icon-fonts.md +0 -50
- package/skills/purgetss/references/installation-setup.md +61 -32
- package/skills/purgetss/references/migration-guide.md +127 -0
- package/skills/purgetss/references/opacity-modifier.md +0 -34
- package/skills/purgetss/references/performance-tips.md +261 -0
- package/skills/purgetss/references/platform-modifiers.md +0 -46
- package/skills/purgetss/references/smart-mappings.md +0 -12
- package/skills/purgetss/references/tikit-components.md +393 -0
- package/skills/purgetss/references/titanium-resets.md +1 -27
- package/skills/purgetss/references/ui-ux-design.md +467 -117
- package/skills/{alloy-expert → ti-expert}/SKILL.md +45 -54
- package/skills/ti-expert/references/alloy-builtins.md +387 -0
- package/skills/{alloy-expert → ti-expert}/references/alloy-structure.md +147 -40
- package/skills/ti-expert/references/anti-patterns.md +90 -0
- package/skills/ti-expert/references/cli-expert.md +109 -0
- package/skills/{alloy-expert → ti-expert}/references/code-conventions.md +182 -49
- package/skills/{alloy-expert → ti-expert}/references/controller-patterns.md +103 -93
- package/skills/{alloy-expert → ti-expert}/references/error-handling.md +12 -12
- package/skills/{alloy-expert → ti-expert}/references/examples.md +144 -85
- package/skills/{alloy-expert → ti-expert}/references/migration-patterns.md +38 -36
- package/skills/{alloy-expert → ti-expert}/references/patterns.md +8 -8
- package/skills/ti-expert/references/performance-listview.md +251 -0
- package/skills/{alloy-expert/references/performance-patterns.md → ti-expert/references/performance-optimization.md} +67 -283
- package/skills/{alloy-expert/references/security-patterns.md → ti-expert/references/security-device.md} +12 -295
- package/skills/ti-expert/references/security-fundamentals.md +284 -0
- package/skills/{alloy-expert → ti-expert}/references/state-management.md +10 -10
- package/skills/ti-expert/references/testing-e2e-ci.md +432 -0
- package/skills/ti-expert/references/testing-unit.md +433 -0
- package/skills/ti-expert/references/theming.md +394 -0
- package/skills/ti-guides/SKILL.md +1 -14
- package/skills/ti-guides/references/advanced-data-and-images.md +126 -10
- package/skills/ti-guides/references/app-distribution.md +48 -10
- package/skills/ti-guides/references/application-frameworks.md +9 -2
- package/skills/ti-guides/references/cli-reference.md +338 -82
- package/skills/ti-guides/references/coding-best-practices.md +14 -4
- package/skills/ti-guides/references/commonjs-advanced.md +103 -6
- package/skills/ti-guides/references/hello-world.md +5 -2
- package/skills/ti-guides/references/hyperloop-native-access.md +439 -46
- package/skills/ti-guides/references/javascript-primer.md +4 -3
- package/skills/ti-guides/references/resources.md +0 -1
- package/skills/ti-guides/references/style-and-conventions.md +57 -9
- package/skills/ti-guides/references/tiapp-config.md +48 -6
- package/skills/ti-howtos/SKILL.md +2 -27
- package/skills/ti-howtos/references/android-platform-deep-dives.md +41 -90
- package/skills/ti-howtos/references/automation-fastlane-appium.md +15 -14
- package/skills/ti-howtos/references/buffer-codec-streams.md +22 -22
- package/skills/ti-howtos/references/cross-platform-development.md +68 -37
- package/skills/ti-howtos/references/debugging-profiling.md +8 -53
- package/skills/ti-howtos/references/extending-titanium.md +12 -51
- package/skills/ti-howtos/references/google-maps-v2.md +30 -29
- package/skills/ti-howtos/references/ios-map-kit.md +19 -25
- package/skills/ti-howtos/references/ios-platform-deep-dives.md +95 -283
- package/skills/ti-howtos/references/local-data-sources.md +45 -37
- package/skills/ti-howtos/references/location-and-maps.md +47 -28
- package/skills/ti-howtos/references/media-apis.md +84 -27
- package/skills/ti-howtos/references/notification-services.md +40 -89
- package/skills/ti-howtos/references/remote-data-sources.md +21 -36
- package/skills/ti-howtos/references/tutorials.md +79 -39
- package/skills/ti-howtos/references/using-modules.md +4 -30
- package/skills/ti-howtos/references/web-content-integration.md +28 -45
- package/skills/ti-howtos/references/webpack-build-pipeline.md +54 -18
- package/skills/ti-ui/SKILL.md +1 -21
- package/skills/ti-ui/references/accessibility-deep-dive.md +71 -25
- package/skills/ti-ui/references/animation-and-matrices.md +0 -47
- package/skills/ti-ui/references/application-structures.md +0 -46
- package/skills/ti-ui/references/custom-fonts-styling.md +9 -57
- package/skills/ti-ui/references/event-handling.md +32 -40
- package/skills/ti-ui/references/gestures.md +0 -40
- package/skills/ti-ui/references/icons-and-splash-screens.md +21 -44
- package/skills/ti-ui/references/layouts-and-positioning.md +47 -39
- package/skills/ti-ui/references/listviews-and-performance.md +187 -73
- package/skills/ti-ui/references/orientation.md +57 -71
- package/skills/ti-ui/references/platform-ui-android.md +114 -48
- package/skills/ti-ui/references/platform-ui-ios.md +63 -38
- package/skills/ti-ui/references/scrolling-views.md +14 -23
- package/skills/ti-ui/references/tableviews.md +6 -56
- package/AGENTS-TEMPLATE.md +0 -174
- package/lib/commands/install.js +0 -188
- package/skills/alloy-expert/references/anti-patterns.md +0 -133
- package/skills/alloy-expert/references/testing.md +0 -872
- package/skills/ti-guides/references/alloy-cli-advanced.md +0 -84
- package/skills/ti-guides/references/alloy-data-mastery.md +0 -29
- package/skills/ti-guides/references/alloy-widgets-and-themes.md +0 -19
- /package/skills/{alloy-expert → ti-expert}/assets/ControllerAutoCleanup.js +0 -0
- /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 | `
|
|
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
|
-
|
|
5
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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)`
|
|
17
|
-
-
|
|
18
|
-
- **Resizing**: Always resize images to their display dimensions via `imageAsResized` before setting the `image` property.
|
|
19
|
-
- **
|
|
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`:
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|