@maccesar/titools 2.0.6 → 2.2.0
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-TEMPLATE.md → AGENTS-VERCEL-RESEARCH.md} +16 -18
- package/README.md +250 -168
- package/agents/{ti-researcher.md → ti-pro.md} +10 -10
- 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 +21 -31
- 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/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,251 @@
|
|
|
1
|
+
# ListView & ScrollView Performance
|
|
2
|
+
|
|
3
|
+
## Critical Rules
|
|
4
|
+
|
|
5
|
+
1. **NEVER use `Ti.UI.SIZE` in ListView items** - Causes layout recalculation on every scroll
|
|
6
|
+
2. **ALWAYS use fixed heights** - Pre-calculated heights enable fast scrolling
|
|
7
|
+
3. **USE templates** - Reuse view instances instead of creating new ones
|
|
8
|
+
|
|
9
|
+
## Optimized ListView Template
|
|
10
|
+
|
|
11
|
+
```xml
|
|
12
|
+
<!-- views/user/list.xml -->
|
|
13
|
+
<Alloy>
|
|
14
|
+
<ListView id="listView">
|
|
15
|
+
<Templates>
|
|
16
|
+
<!-- FIXED HEIGHT is critical for performance -->
|
|
17
|
+
<ItemTemplate name="userTemplate" height="64">
|
|
18
|
+
<View id="rowContainer">
|
|
19
|
+
<ImageView bindId="avatar" id="avatarImg" />
|
|
20
|
+
<View id="userInfo">
|
|
21
|
+
<Label bindId="name" id="nameLabel" />
|
|
22
|
+
<Label bindId="email" id="emailLabel" />
|
|
23
|
+
</View>
|
|
24
|
+
</View>
|
|
25
|
+
</ItemTemplate>
|
|
26
|
+
</Templates>
|
|
27
|
+
|
|
28
|
+
<ListSection id="section" dataCollection="users">
|
|
29
|
+
<ListItem template="userTemplate" avatar:image="{avatar}" name:text="{name}" email:text="{email}" />
|
|
30
|
+
</ListSection>
|
|
31
|
+
</ListView>
|
|
32
|
+
</Alloy>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```tss
|
|
36
|
+
/* styles/user/list.tss */
|
|
37
|
+
"#listView": { width: Ti.UI.FILL, height: Ti.UI.FILL }
|
|
38
|
+
"#rowContainer": { layout: 'horizontal', height: 64, width: Ti.UI.FILL }
|
|
39
|
+
"#avatarImg": { left: 16, width: 48, height: 48, borderRadius: 24 }
|
|
40
|
+
"#userInfo": { layout: 'vertical', left: 12 }
|
|
41
|
+
"#nameLabel": { font: { fontSize: 16, fontWeight: 'bold' } }
|
|
42
|
+
"#emailLabel": { font: { fontSize: 14 }, color: '#6b7280' }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Titanium Layout Rules:**
|
|
46
|
+
- Use `layout: 'horizontal'`/`layout: 'vertical'` for directional layouts
|
|
47
|
+
- Use margins on children for spacing (NOT padding on parent)
|
|
48
|
+
- Use `width: Ti.UI.FILL, height: Ti.UI.FILL` for full width + height
|
|
49
|
+
|
|
50
|
+
## Efficient Data Binding
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
// controllers/feed/list.js
|
|
54
|
+
function renderItems(items) {
|
|
55
|
+
// Pre-format data to avoid calculation in render
|
|
56
|
+
const listItems = items.map(item => ({
|
|
57
|
+
template: 'feedTemplate',
|
|
58
|
+
properties: {
|
|
59
|
+
itemId: item.id,
|
|
60
|
+
searchableText: `${item.title} ${item.description}`
|
|
61
|
+
},
|
|
62
|
+
title: { text: item.title },
|
|
63
|
+
description: { text: item.description },
|
|
64
|
+
timestamp: { text: formatTimestamp(item.created_at) }
|
|
65
|
+
}))
|
|
66
|
+
|
|
67
|
+
// Single update
|
|
68
|
+
$.section.items = listItems
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Timestamp formatter (cache results)
|
|
72
|
+
const timestampCache = new Map()
|
|
73
|
+
|
|
74
|
+
function formatTimestamp(timestamp) {
|
|
75
|
+
if (timestampCache.has(timestamp)) {
|
|
76
|
+
return timestampCache.get(timestamp)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const formatted = new Date(timestamp).toLocaleString()
|
|
80
|
+
timestampCache.set(timestamp, formatted)
|
|
81
|
+
|
|
82
|
+
return formatted
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Image Loading & Caching
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
// lib/services/imageCache.js
|
|
90
|
+
exports.ImageCache = {
|
|
91
|
+
_cache: new Map(),
|
|
92
|
+
_loading: new Map(),
|
|
93
|
+
|
|
94
|
+
// Get image at appropriate size for list item
|
|
95
|
+
getListThumbnail(url) {
|
|
96
|
+
const cacheKey = `thumb_${url}`
|
|
97
|
+
|
|
98
|
+
// Return cached if available
|
|
99
|
+
if (this._cache.has(cacheKey)) {
|
|
100
|
+
return this._cache.get(cacheKey)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check if already loading
|
|
104
|
+
if (this._loading.has(cacheKey)) {
|
|
105
|
+
return this._loading.get(cacheKey)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Load and resize
|
|
109
|
+
const promise = this._loadAndResize(url, { width: 80, height: 80 })
|
|
110
|
+
.then(resized => {
|
|
111
|
+
this._cache.set(cacheKey, resized)
|
|
112
|
+
this._loading.delete(cacheKey)
|
|
113
|
+
return resized
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
this._loading.set(cacheKey, promise)
|
|
117
|
+
|
|
118
|
+
return promise
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
async _loadAndResize(url, size) {
|
|
122
|
+
return new Promise((resolve, reject) => {
|
|
123
|
+
const imageView = Ti.UI.createImageView({
|
|
124
|
+
image: url,
|
|
125
|
+
width: size.width,
|
|
126
|
+
height: size.height,
|
|
127
|
+
preventsDefaultAnimation: true
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
imageView.addEventListener('load', () => {
|
|
131
|
+
const blob = imageView.toImage()
|
|
132
|
+
const resized = blob.imageAsResized(size.width, size.height)
|
|
133
|
+
imageView.image = null
|
|
134
|
+
resolve(resized)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
imageView.addEventListener('error', (e) => {
|
|
138
|
+
reject(e)
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
clear() {
|
|
144
|
+
this._cache.clear()
|
|
145
|
+
this._loading.clear()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## ScrollView Performance
|
|
151
|
+
|
|
152
|
+
### Optimizing Large ScrollViews
|
|
153
|
+
|
|
154
|
+
```xml
|
|
155
|
+
<!-- Avoid: Creating many views at once -->
|
|
156
|
+
<ScrollView id="scrollView" layout="vertical">
|
|
157
|
+
<!-- DON'T: 100+ views created immediately -->
|
|
158
|
+
</ScrollView>
|
|
159
|
+
|
|
160
|
+
<!-- Better: Use ListView for list-like content -->
|
|
161
|
+
<ListView id="listView">
|
|
162
|
+
<!-- Views created on-demand as user scrolls -->
|
|
163
|
+
</ListView>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### When You Must Use ScrollView
|
|
167
|
+
|
|
168
|
+
```javascript
|
|
169
|
+
// Lazy load content sections
|
|
170
|
+
const sections = [
|
|
171
|
+
{ id: 'header', height: 200 },
|
|
172
|
+
{ id: 'featured', height: 300 },
|
|
173
|
+
{ id: 'products', height: 400 },
|
|
174
|
+
{ id: 'reviews', height: 500 }
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
let loadedSections = new Set()
|
|
178
|
+
|
|
179
|
+
function init() {
|
|
180
|
+
// Load only visible sections initially
|
|
181
|
+
loadSection('header')
|
|
182
|
+
|
|
183
|
+
$.scrollView.addEventListener('scroll', onScroll)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function onScroll(e) {
|
|
187
|
+
const scrollY = e.y
|
|
188
|
+
const viewportHeight = $.scrollView.rect.height
|
|
189
|
+
|
|
190
|
+
sections.forEach(section => {
|
|
191
|
+
if (loadedSections.has(section.id)) return
|
|
192
|
+
|
|
193
|
+
// Check if section is about to be visible
|
|
194
|
+
const sectionTop = getSectionTop(section.id)
|
|
195
|
+
|
|
196
|
+
if (sectionTop < scrollY + viewportHeight + 100) {
|
|
197
|
+
loadSection(section.id)
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function loadSection(sectionId) {
|
|
203
|
+
if (loadedSections.has(sectionId)) return
|
|
204
|
+
|
|
205
|
+
loadedSections.add(sectionId)
|
|
206
|
+
|
|
207
|
+
// Load content for this section
|
|
208
|
+
const container = $[sectionId + 'Container']
|
|
209
|
+
const content = createSectionContent(sectionId)
|
|
210
|
+
container.add(content)
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## ScrollView Memory Management
|
|
215
|
+
|
|
216
|
+
```javascript
|
|
217
|
+
// Release images when scrolled far away
|
|
218
|
+
function onScroll(e) {
|
|
219
|
+
const scrollY = e.y
|
|
220
|
+
const viewportHeight = $.scrollView.rect.height
|
|
221
|
+
|
|
222
|
+
// Release images more than 2 screens away
|
|
223
|
+
const releaseThreshold = viewportHeight * 2
|
|
224
|
+
|
|
225
|
+
imageViews.forEach((img, index) => {
|
|
226
|
+
const imgTop = img.rect.y
|
|
227
|
+
const distance = Math.abs(imgTop - scrollY)
|
|
228
|
+
|
|
229
|
+
if (distance > releaseThreshold && img.image) {
|
|
230
|
+
// Store URL for later reload
|
|
231
|
+
img._originalUrl = img.image
|
|
232
|
+
img.image = null
|
|
233
|
+
} else if (distance < viewportHeight && img._originalUrl) {
|
|
234
|
+
// Reload when close to viewport
|
|
235
|
+
img.image = img._originalUrl
|
|
236
|
+
delete img._originalUrl
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Performance Checklist (UI)
|
|
243
|
+
|
|
244
|
+
| Area | Check |
|
|
245
|
+
| ------------ | ---------------------------------- |
|
|
246
|
+
| **ListView** | Fixed heights on all templates |
|
|
247
|
+
| **ListView** | Using templates, not dynamic views |
|
|
248
|
+
| **ListView** | Image pre-sizing and caching |
|
|
249
|
+
| **Scroll** | Lazy loading content sections |
|
|
250
|
+
| **Scroll** | Release off-screen images |
|
|
251
|
+
| **Images** | Resized appropriately for display |
|
|
@@ -1,151 +1,10 @@
|
|
|
1
|
-
# Performance Optimization
|
|
2
|
-
|
|
3
|
-
## Critical Rules
|
|
4
|
-
|
|
5
|
-
1. **NEVER use `Ti.UI.SIZE` in ListView items** - Causes layout recalculation on every scroll
|
|
6
|
-
2. **ALWAYS use fixed heights** - Pre-calculated heights enable fast scrolling
|
|
7
|
-
3. **USE templates** - Reuse view instances instead of creating new ones
|
|
8
|
-
4. **CACHE Ti.Platform properties** - Avoid repeated bridge crossings
|
|
9
|
-
5. **USE applyProperties()** - Batch UI updates in a single call
|
|
10
|
-
|
|
11
|
-
## ListView Performance
|
|
12
|
-
|
|
13
|
-
### Optimized ListView Template
|
|
14
|
-
|
|
15
|
-
```xml
|
|
16
|
-
<!-- views/user/list.xml -->
|
|
17
|
-
<Alloy>
|
|
18
|
-
<ListView class="wh-screen">
|
|
19
|
-
<Templates>
|
|
20
|
-
<!-- FIXED HEIGHT is critical for performance -->
|
|
21
|
-
<ItemTemplate name="userTemplate" height="64">
|
|
22
|
-
<View class="horizontal h-16 w-screen">
|
|
23
|
-
<ImageView bindId="avatar" class="rounded-full-12 ml-4" />
|
|
24
|
-
<View class="vertical ml-3">
|
|
25
|
-
<Label bindId="name" class="text-base font-bold" />
|
|
26
|
-
<Label bindId="email" class="text-sm text-gray-500" />
|
|
27
|
-
</View>
|
|
28
|
-
</View>
|
|
29
|
-
</ItemTemplate>
|
|
30
|
-
</Templates>
|
|
31
|
-
|
|
32
|
-
<ListSection id="section" dataCollection="users">
|
|
33
|
-
<ListItem template="userTemplate" avatar:image="{avatar}" name:text="{name}" email:text="{email}" />
|
|
34
|
-
</ListSection>
|
|
35
|
-
</ListView>
|
|
36
|
-
</Alloy>
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
**PurgeTSS Layout Rules:**
|
|
40
|
-
- Use `horizontal`/`vertical` for layout (NOT flexbox)
|
|
41
|
-
- Use `m-*` on children for spacing (NOT `p-*` on parent)
|
|
42
|
-
- Use `wh-screen` for full width + height
|
|
43
|
-
|
|
44
|
-
### Efficient Data Binding
|
|
45
|
-
|
|
46
|
-
```javascript
|
|
47
|
-
// controllers/feed/list.js
|
|
48
|
-
function renderItems(items) {
|
|
49
|
-
// Pre-format data to avoid calculation in render
|
|
50
|
-
const listItems = items.map(item => ({
|
|
51
|
-
template: 'feedTemplate',
|
|
52
|
-
properties: {
|
|
53
|
-
itemId: item.id,
|
|
54
|
-
searchableText: `${item.title} ${item.description}`
|
|
55
|
-
},
|
|
56
|
-
title: { text: item.title },
|
|
57
|
-
description: { text: item.description },
|
|
58
|
-
timestamp: { text: formatTimestamp(item.created_at) }
|
|
59
|
-
}))
|
|
60
|
-
|
|
61
|
-
// Single update
|
|
62
|
-
$.section.items = listItems
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Timestamp formatter (cache results)
|
|
66
|
-
const timestampCache = new Map()
|
|
67
|
-
|
|
68
|
-
function formatTimestamp(timestamp) {
|
|
69
|
-
if (timestampCache.has(timestamp)) {
|
|
70
|
-
return timestampCache.get(timestamp)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const formatted = new Date(timestamp).toLocaleString()
|
|
74
|
-
timestampCache.set(timestamp, formatted)
|
|
75
|
-
|
|
76
|
-
return formatted
|
|
77
|
-
}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Image Loading & Caching
|
|
81
|
-
|
|
82
|
-
```javascript
|
|
83
|
-
// lib/services/imageCache.js
|
|
84
|
-
exports.ImageCache = {
|
|
85
|
-
_cache: new Map(),
|
|
86
|
-
_loading: new Map(),
|
|
87
|
-
|
|
88
|
-
// Get image at appropriate size for list item
|
|
89
|
-
getListThumbnail(url) {
|
|
90
|
-
const cacheKey = `thumb_${url}`
|
|
91
|
-
|
|
92
|
-
// Return cached if available
|
|
93
|
-
if (this._cache.has(cacheKey)) {
|
|
94
|
-
return this._cache.get(cacheKey)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Check if already loading
|
|
98
|
-
if (this._loading.has(cacheKey)) {
|
|
99
|
-
return this._loading.get(cacheKey)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Load and resize
|
|
103
|
-
const promise = this._loadAndResize(url, { width: 80, height: 80 })
|
|
104
|
-
.then(resized => {
|
|
105
|
-
this._cache.set(cacheKey, resized)
|
|
106
|
-
this._loading.delete(cacheKey)
|
|
107
|
-
return resized
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
this._loading.set(cacheKey, promise)
|
|
111
|
-
|
|
112
|
-
return promise
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
async _loadAndResize(url, size) {
|
|
116
|
-
return new Promise((resolve, reject) => {
|
|
117
|
-
const imageView = Ti.UI.createImageView({
|
|
118
|
-
image: url,
|
|
119
|
-
width: size.width,
|
|
120
|
-
height: size.height,
|
|
121
|
-
preventsDefaultAnimation: true
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
imageView.addEventListener('load', () => {
|
|
125
|
-
const blob = imageView.toImage()
|
|
126
|
-
const resized = blob.imageAsResized(size.width, size.height)
|
|
127
|
-
imageView.image = null
|
|
128
|
-
resolve(resized)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
imageView.addEventListener('error', (e) => {
|
|
132
|
-
reject(e)
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
},
|
|
136
|
-
|
|
137
|
-
clear() {
|
|
138
|
-
this._cache.clear()
|
|
139
|
-
this._loading.clear()
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
```
|
|
1
|
+
# Performance Optimization: Bridge, Memory, Animation & Timing
|
|
143
2
|
|
|
144
3
|
## Bridge Optimization
|
|
145
4
|
|
|
146
5
|
### Minimize Bridge Crossings
|
|
147
6
|
|
|
148
|
-
Every JavaScript
|
|
7
|
+
Every JavaScript -> Native call crosses a bridge. Minimize these:
|
|
149
8
|
|
|
150
9
|
```javascript
|
|
151
10
|
// BAD: Multiple bridge crossings
|
|
@@ -176,14 +35,19 @@ $.nameLabel.applyProperties({
|
|
|
176
35
|
})
|
|
177
36
|
```
|
|
178
37
|
|
|
179
|
-
### Use
|
|
38
|
+
### Use TSS Files Instead of Inline Attributes
|
|
180
39
|
|
|
181
40
|
```xml
|
|
182
|
-
<!-- BAD: Inline styling =
|
|
41
|
+
<!-- BAD: Inline styling = scattered and hard to maintain -->
|
|
183
42
|
<Label text="Hello" width="200" height="40" color="#000" font="{fontSize:16}" />
|
|
184
43
|
|
|
185
|
-
<!-- GOOD:
|
|
186
|
-
<Label
|
|
44
|
+
<!-- GOOD: Style defined in TSS file -->
|
|
45
|
+
<Label id="helloLabel" text="Hello" />
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```tss
|
|
49
|
+
/* In the corresponding .tss file */
|
|
50
|
+
"#helloLabel": { width: 200, height: 40, color: '#000', font: { fontSize: 16 } }
|
|
187
51
|
```
|
|
188
52
|
|
|
189
53
|
## Memory Management
|
|
@@ -242,7 +106,7 @@ function cleanup() {
|
|
|
242
106
|
$.cleanup = cleanup
|
|
243
107
|
```
|
|
244
108
|
|
|
245
|
-
|
|
109
|
+
## Image Memory Management
|
|
246
110
|
|
|
247
111
|
```javascript
|
|
248
112
|
// lib/services/imageManager.js
|
|
@@ -479,115 +343,6 @@ renderUsers(users)
|
|
|
479
343
|
measure.end()
|
|
480
344
|
```
|
|
481
345
|
|
|
482
|
-
## Performance Checklist
|
|
483
|
-
|
|
484
|
-
| Area | Check |
|
|
485
|
-
| ------------ | ----------------------------------------- |
|
|
486
|
-
| **ListView** | Fixed heights on all templates |
|
|
487
|
-
| **ListView** | Using templates, not dynamic views |
|
|
488
|
-
| **ListView** | Image pre-sizing and caching |
|
|
489
|
-
| **Bridge** | Cached Ti.Platform properties |
|
|
490
|
-
| **Bridge** | Using applyProperties for updates |
|
|
491
|
-
| **Bridge** | PurgeTSS classes instead of inline styles |
|
|
492
|
-
| **Memory** | All global listeners cleaned up |
|
|
493
|
-
| **Memory** | Heavy objects nulled in cleanup |
|
|
494
|
-
| **Memory** | Images resized appropriately |
|
|
495
|
-
| **Database** | Using transactions for batch ops |
|
|
496
|
-
| **Database** | Indexes on frequently queried columns |
|
|
497
|
-
| **Database** | ResultSets and DB handles closed |
|
|
498
|
-
|
|
499
|
-
## ScrollView Performance
|
|
500
|
-
|
|
501
|
-
### Optimizing Large ScrollViews
|
|
502
|
-
|
|
503
|
-
```xml
|
|
504
|
-
<!-- Avoid: Creating many views at once -->
|
|
505
|
-
<ScrollView class="wh-screen vertical">
|
|
506
|
-
<!-- DON'T: 100+ views created immediately -->
|
|
507
|
-
</ScrollView>
|
|
508
|
-
|
|
509
|
-
<!-- Better: Use ListView for list-like content -->
|
|
510
|
-
<ListView class="wh-screen">
|
|
511
|
-
<!-- Views created on-demand as user scrolls -->
|
|
512
|
-
</ListView>
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
### When You Must Use ScrollView
|
|
516
|
-
|
|
517
|
-
```javascript
|
|
518
|
-
// Lazy load content sections
|
|
519
|
-
const sections = [
|
|
520
|
-
{ id: 'header', height: 200 },
|
|
521
|
-
{ id: 'featured', height: 300 },
|
|
522
|
-
{ id: 'products', height: 400 },
|
|
523
|
-
{ id: 'reviews', height: 500 }
|
|
524
|
-
]
|
|
525
|
-
|
|
526
|
-
let loadedSections = new Set()
|
|
527
|
-
|
|
528
|
-
function init() {
|
|
529
|
-
// Load only visible sections initially
|
|
530
|
-
loadSection('header')
|
|
531
|
-
|
|
532
|
-
$.scrollView.addEventListener('scroll', onScroll)
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
function onScroll(e) {
|
|
536
|
-
const scrollY = e.y
|
|
537
|
-
const viewportHeight = $.scrollView.rect.height
|
|
538
|
-
|
|
539
|
-
sections.forEach(section => {
|
|
540
|
-
if (loadedSections.has(section.id)) return
|
|
541
|
-
|
|
542
|
-
// Check if section is about to be visible
|
|
543
|
-
const sectionTop = getSectionTop(section.id)
|
|
544
|
-
|
|
545
|
-
if (sectionTop < scrollY + viewportHeight + 100) {
|
|
546
|
-
loadSection(section.id)
|
|
547
|
-
}
|
|
548
|
-
})
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
function loadSection(sectionId) {
|
|
552
|
-
if (loadedSections.has(sectionId)) return
|
|
553
|
-
|
|
554
|
-
loadedSections.add(sectionId)
|
|
555
|
-
|
|
556
|
-
// Load content for this section
|
|
557
|
-
const container = $[sectionId + 'Container']
|
|
558
|
-
const content = createSectionContent(sectionId)
|
|
559
|
-
container.add(content)
|
|
560
|
-
}
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
### ScrollView Memory Management
|
|
564
|
-
|
|
565
|
-
```javascript
|
|
566
|
-
// Release images when scrolled far away
|
|
567
|
-
function onScroll(e) {
|
|
568
|
-
const scrollY = e.y
|
|
569
|
-
const viewportHeight = $.scrollView.rect.height
|
|
570
|
-
|
|
571
|
-
// Release images more than 2 screens away
|
|
572
|
-
const releaseThreshold = viewportHeight * 2
|
|
573
|
-
|
|
574
|
-
imageViews.forEach((img, index) => {
|
|
575
|
-
const imgTop = img.rect.y
|
|
576
|
-
const distance = Math.abs(imgTop - scrollY)
|
|
577
|
-
|
|
578
|
-
if (distance > releaseThreshold && img.image) {
|
|
579
|
-
// Store URL for later reload
|
|
580
|
-
img._originalUrl = img.image
|
|
581
|
-
img.image = null
|
|
582
|
-
} else if (distance < viewportHeight && img._originalUrl) {
|
|
583
|
-
// Reload when close to viewport
|
|
584
|
-
img.image = img._originalUrl
|
|
585
|
-
delete img._originalUrl
|
|
586
|
-
}
|
|
587
|
-
})
|
|
588
|
-
}
|
|
589
|
-
```
|
|
590
|
-
|
|
591
346
|
## Animation Performance
|
|
592
347
|
|
|
593
348
|
### 60fps Animation Rules
|
|
@@ -610,37 +365,48 @@ const animation = Ti.UI.createAnimation({
|
|
|
610
365
|
$.view.animate(animation)
|
|
611
366
|
```
|
|
612
367
|
|
|
613
|
-
###
|
|
614
|
-
|
|
615
|
-
```xml
|
|
616
|
-
<!-- Define animations with state modifiers -->
|
|
617
|
-
<Animation id="fadeIn" module="purgetss.ui" class="close:opacity-0 duration-300 open:opacity-100" />
|
|
618
|
-
<Animation id="fadeOut" module="purgetss.ui" class="close:opacity-100 duration-300 open:opacity-0" />
|
|
619
|
-
<Animation id="slideInRight" module="purgetss.ui" class="close:translate-x-full duration-300 open:translate-x-0" />
|
|
620
|
-
<Animation id="scalePress" module="purgetss.ui" class="close:scale-100 duration-150 open:scale-95" />
|
|
621
|
-
```
|
|
368
|
+
### Ti.UI.createAnimation (Recommended)
|
|
622
369
|
|
|
623
370
|
```javascript
|
|
624
|
-
// Fade in
|
|
625
|
-
|
|
626
|
-
|
|
371
|
+
// Fade in
|
|
372
|
+
function fadeIn(view, callback) {
|
|
373
|
+
const animation = Ti.UI.createAnimation({
|
|
374
|
+
opacity: 1,
|
|
375
|
+
duration: 300,
|
|
376
|
+
curve: Ti.UI.ANIMATION_CURVE_EASE_OUT
|
|
377
|
+
})
|
|
378
|
+
if (callback) animation.addEventListener('complete', callback)
|
|
379
|
+
view.animate(animation)
|
|
380
|
+
}
|
|
627
381
|
|
|
628
|
-
//
|
|
629
|
-
|
|
630
|
-
|
|
382
|
+
// Fade out
|
|
383
|
+
function fadeOut(view, callback) {
|
|
384
|
+
const animation = Ti.UI.createAnimation({
|
|
385
|
+
opacity: 0,
|
|
386
|
+
duration: 300,
|
|
387
|
+
curve: Ti.UI.ANIMATION_CURVE_EASE_OUT
|
|
388
|
+
})
|
|
389
|
+
if (callback) animation.addEventListener('complete', callback)
|
|
390
|
+
view.animate(animation)
|
|
391
|
+
}
|
|
631
392
|
|
|
632
|
-
//
|
|
633
|
-
|
|
634
|
-
|
|
393
|
+
// Slide in from right
|
|
394
|
+
function slideInRight(view) {
|
|
395
|
+
view.transform = Ti.UI.createMatrix2D().translate(Ti.Platform.displayCaps.platformWidth, 0)
|
|
396
|
+
view.animate(Ti.UI.createAnimation({
|
|
397
|
+
transform: Ti.UI.createMatrix2D(),
|
|
398
|
+
duration: 300,
|
|
399
|
+
curve: Ti.UI.ANIMATION_CURVE_EASE_OUT
|
|
400
|
+
}))
|
|
401
|
+
}
|
|
635
402
|
|
|
636
403
|
// Chained animations with callback
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
$.slideInRight.open($.content)
|
|
404
|
+
fadeIn($.modal, () => {
|
|
405
|
+
slideInRight($.content)
|
|
640
406
|
})
|
|
641
407
|
```
|
|
642
408
|
|
|
643
|
-
|
|
409
|
+
## Hardware-Accelerated Properties
|
|
644
410
|
|
|
645
411
|
```javascript
|
|
646
412
|
// These properties are GPU-accelerated (fast):
|
|
@@ -672,7 +438,7 @@ const badAnimation = Ti.UI.createAnimation({
|
|
|
672
438
|
})
|
|
673
439
|
```
|
|
674
440
|
|
|
675
|
-
|
|
441
|
+
## Animation Cleanup
|
|
676
442
|
|
|
677
443
|
```javascript
|
|
678
444
|
// Always remove animation listeners
|
|
@@ -734,7 +500,7 @@ exports.debounce = function debounce(fn, delay = 300) {
|
|
|
734
500
|
|
|
735
501
|
```javascript
|
|
736
502
|
// Usage: Search input
|
|
737
|
-
const { debounce } = require('
|
|
503
|
+
const { debounce } = require('helpers/timing')
|
|
738
504
|
|
|
739
505
|
const debouncedSearch = debounce(async (query) => {
|
|
740
506
|
const results = await searchService.search(query)
|
|
@@ -788,7 +554,7 @@ exports.throttle = function throttle(fn, limit = 100) {
|
|
|
788
554
|
|
|
789
555
|
```javascript
|
|
790
556
|
// Usage: Scroll handler
|
|
791
|
-
const { throttle } = require('
|
|
557
|
+
const { throttle } = require('helpers/timing')
|
|
792
558
|
|
|
793
559
|
const throttledScroll = throttle((scrollY) => {
|
|
794
560
|
updateHeaderOpacity(scrollY)
|
|
@@ -821,7 +587,7 @@ function cleanup() {
|
|
|
821
587
|
|
|
822
588
|
```javascript
|
|
823
589
|
// Show immediate feedback while typing, but only search when done
|
|
824
|
-
const { debounce, throttle } = require('
|
|
590
|
+
const { debounce, throttle } = require('helpers/timing')
|
|
825
591
|
|
|
826
592
|
// Update UI immediately (throttled)
|
|
827
593
|
const updateSuggestions = throttle((query) => {
|
|
@@ -853,3 +619,21 @@ function cleanup() {
|
|
|
853
619
|
$.destroy()
|
|
854
620
|
}
|
|
855
621
|
```
|
|
622
|
+
|
|
623
|
+
## Performance Checklist
|
|
624
|
+
|
|
625
|
+
| Area | Check |
|
|
626
|
+
| ------------- | --------------------------------------- |
|
|
627
|
+
| **Bridge** | Cached Ti.Platform properties |
|
|
628
|
+
| **Bridge** | Using applyProperties for updates |
|
|
629
|
+
| **Bridge** | TSS styles instead of inline attributes |
|
|
630
|
+
| **Memory** | All global listeners cleaned up |
|
|
631
|
+
| **Memory** | Heavy objects nulled in cleanup |
|
|
632
|
+
| **Memory** | Images resized appropriately |
|
|
633
|
+
| **Database** | Using transactions for batch ops |
|
|
634
|
+
| **Database** | Indexes on frequently queried columns |
|
|
635
|
+
| **Database** | ResultSets and DB handles closed |
|
|
636
|
+
| **Animation** | Using native animations, not intervals |
|
|
637
|
+
| **Animation** | GPU-accelerated properties preferred |
|
|
638
|
+
| **Timing** | Debounce on search/input |
|
|
639
|
+
| **Timing** | Throttle on scroll/touch events |
|