@maccesar/titools 2.0.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.
Files changed (120) hide show
  1. package/AGENTS-TEMPLATE.md +173 -0
  2. package/README.md +867 -0
  3. package/agents/ti-researcher.md +108 -0
  4. package/bin/titools.js +53 -0
  5. package/lib/commands/agents.js +126 -0
  6. package/lib/commands/install.js +188 -0
  7. package/lib/commands/uninstall.js +215 -0
  8. package/lib/commands/update.js +159 -0
  9. package/lib/config.js +119 -0
  10. package/lib/downloader.js +153 -0
  11. package/lib/installer.js +253 -0
  12. package/lib/platform.js +108 -0
  13. package/lib/symlink.js +142 -0
  14. package/lib/utils.js +270 -0
  15. package/package.json +67 -0
  16. package/skills/alloy-expert/SKILL.md +247 -0
  17. package/skills/alloy-expert/assets/ControllerAutoCleanup.js +182 -0
  18. package/skills/alloy-expert/references/alloy-structure.md +381 -0
  19. package/skills/alloy-expert/references/anti-patterns.md +133 -0
  20. package/skills/alloy-expert/references/code-conventions.md +469 -0
  21. package/skills/alloy-expert/references/contracts.md +280 -0
  22. package/skills/alloy-expert/references/controller-patterns.md +520 -0
  23. package/skills/alloy-expert/references/error-handling.md +484 -0
  24. package/skills/alloy-expert/references/examples.md +735 -0
  25. package/skills/alloy-expert/references/migration-patterns.md +298 -0
  26. package/skills/alloy-expert/references/patterns.md +448 -0
  27. package/skills/alloy-expert/references/performance-patterns.md +855 -0
  28. package/skills/alloy-expert/references/security-patterns.md +847 -0
  29. package/skills/alloy-expert/references/state-management.md +779 -0
  30. package/skills/alloy-expert/references/testing.md +872 -0
  31. package/skills/alloy-guides/SKILL.md +214 -0
  32. package/skills/alloy-guides/references/CLI_TASKS.md +243 -0
  33. package/skills/alloy-guides/references/CONCEPTS.md +191 -0
  34. package/skills/alloy-guides/references/CONTROLLERS.md +298 -0
  35. package/skills/alloy-guides/references/MODELS.md +1028 -0
  36. package/skills/alloy-guides/references/PURGETSS.md +56 -0
  37. package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +242 -0
  38. package/skills/alloy-guides/references/VIEWS_STYLES.md +388 -0
  39. package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +109 -0
  40. package/skills/alloy-guides/references/VIEWS_XML.md +558 -0
  41. package/skills/alloy-guides/references/WIDGETS.md +176 -0
  42. package/skills/alloy-howtos/SKILL.md +203 -0
  43. package/skills/alloy-howtos/references/best_practices.md +138 -0
  44. package/skills/alloy-howtos/references/cli_reference.md +253 -0
  45. package/skills/alloy-howtos/references/config_files.md +87 -0
  46. package/skills/alloy-howtos/references/custom_tags.md +147 -0
  47. package/skills/alloy-howtos/references/debugging_troubleshooting.md +101 -0
  48. package/skills/alloy-howtos/references/samples.md +167 -0
  49. package/skills/purgetss/SKILL.md +442 -0
  50. package/skills/purgetss/assets/purgetss.config.cjs +17 -0
  51. package/skills/purgetss/references/EXAMPLES.md +247 -0
  52. package/skills/purgetss/references/animation-system.md +1294 -0
  53. package/skills/purgetss/references/apply-directive.md +375 -0
  54. package/skills/purgetss/references/arbitrary-values.md +612 -0
  55. package/skills/purgetss/references/class-index.md +1350 -0
  56. package/skills/purgetss/references/cli-commands.md +948 -0
  57. package/skills/purgetss/references/configurable-properties.md +654 -0
  58. package/skills/purgetss/references/custom-rules.md +161 -0
  59. package/skills/purgetss/references/customization-deep-dive.md +722 -0
  60. package/skills/purgetss/references/dynamic-component-creation.md +489 -0
  61. package/skills/purgetss/references/grid-layout.md +455 -0
  62. package/skills/purgetss/references/icon-fonts.md +609 -0
  63. package/skills/purgetss/references/installation-setup.md +366 -0
  64. package/skills/purgetss/references/opacity-modifier.md +291 -0
  65. package/skills/purgetss/references/platform-modifiers.md +479 -0
  66. package/skills/purgetss/references/smart-mappings.md +42 -0
  67. package/skills/purgetss/references/titanium-resets.md +359 -0
  68. package/skills/purgetss/references/ui-ux-design.md +1526 -0
  69. package/skills/ti-guides/SKILL.md +94 -0
  70. package/skills/ti-guides/references/advanced-data-and-images.md +19 -0
  71. package/skills/ti-guides/references/alloy-cli-advanced.md +84 -0
  72. package/skills/ti-guides/references/alloy-data-mastery.md +29 -0
  73. package/skills/ti-guides/references/alloy-widgets-and-themes.md +19 -0
  74. package/skills/ti-guides/references/android-manifest.md +97 -0
  75. package/skills/ti-guides/references/app-distribution.md +258 -0
  76. package/skills/ti-guides/references/application-frameworks.md +377 -0
  77. package/skills/ti-guides/references/cli-reference.md +402 -0
  78. package/skills/ti-guides/references/coding-best-practices.md +102 -0
  79. package/skills/ti-guides/references/commonjs-advanced.md +134 -0
  80. package/skills/ti-guides/references/hello-world.md +100 -0
  81. package/skills/ti-guides/references/hyperloop-native-access.md +62 -0
  82. package/skills/ti-guides/references/javascript-primer.md +411 -0
  83. package/skills/ti-guides/references/reserved-words.md +36 -0
  84. package/skills/ti-guides/references/resources.md +183 -0
  85. package/skills/ti-guides/references/style-and-conventions.md +48 -0
  86. package/skills/ti-guides/references/tiapp-config.md +609 -0
  87. package/skills/ti-howtos/SKILL.md +174 -0
  88. package/skills/ti-howtos/references/android-platform-deep-dives.md +658 -0
  89. package/skills/ti-howtos/references/automation-fastlane-appium.md +95 -0
  90. package/skills/ti-howtos/references/buffer-codec-streams.md +140 -0
  91. package/skills/ti-howtos/references/cross-platform-development.md +348 -0
  92. package/skills/ti-howtos/references/debugging-profiling.md +543 -0
  93. package/skills/ti-howtos/references/extending-titanium.md +723 -0
  94. package/skills/ti-howtos/references/google-maps-v2.md +169 -0
  95. package/skills/ti-howtos/references/ios-map-kit.md +143 -0
  96. package/skills/ti-howtos/references/ios-platform-deep-dives.md +783 -0
  97. package/skills/ti-howtos/references/local-data-sources.md +301 -0
  98. package/skills/ti-howtos/references/location-and-maps.md +252 -0
  99. package/skills/ti-howtos/references/media-apis.md +210 -0
  100. package/skills/ti-howtos/references/notification-services.md +599 -0
  101. package/skills/ti-howtos/references/remote-data-sources.md +349 -0
  102. package/skills/ti-howtos/references/tutorials.md +502 -0
  103. package/skills/ti-howtos/references/using-modules.md +237 -0
  104. package/skills/ti-howtos/references/web-content-integration.md +307 -0
  105. package/skills/ti-howtos/references/webpack-build-pipeline.md +78 -0
  106. package/skills/ti-ui/SKILL.md +179 -0
  107. package/skills/ti-ui/references/accessibility-deep-dive.md +242 -0
  108. package/skills/ti-ui/references/animation-and-matrices.md +599 -0
  109. package/skills/ti-ui/references/application-structures.md +655 -0
  110. package/skills/ti-ui/references/custom-fonts-styling.md +579 -0
  111. package/skills/ti-ui/references/event-handling.md +393 -0
  112. package/skills/ti-ui/references/gestures.md +473 -0
  113. package/skills/ti-ui/references/icons-and-splash-screens.md +409 -0
  114. package/skills/ti-ui/references/layouts-and-positioning.md +462 -0
  115. package/skills/ti-ui/references/listviews-and-performance.md +619 -0
  116. package/skills/ti-ui/references/orientation.md +362 -0
  117. package/skills/ti-ui/references/platform-ui-android.md +635 -0
  118. package/skills/ti-ui/references/platform-ui-ios.md +469 -0
  119. package/skills/ti-ui/references/scrolling-views.md +252 -0
  120. package/skills/ti-ui/references/tableviews.md +568 -0
@@ -0,0 +1,473 @@
1
+ # Gestures
2
+
3
+ ## Table of Contents
4
+
5
+ - [Gestures](#gestures)
6
+ - [Table of Contents](#table-of-contents)
7
+ - [1. Overview](#1-overview)
8
+ - [2. Touch Events](#2-touch-events)
9
+ - [Touch Lifecycle](#touch-lifecycle)
10
+ - [Touch Event Properties](#touch-event-properties)
11
+ - [Android Note](#android-note)
12
+ - [3. Swipe Gesture](#3-swipe-gesture)
13
+ - [Basic Swipe](#basic-swipe)
14
+ - [Swipe Direction Detection](#swipe-direction-detection)
15
+ - [Swipe vs Scroll](#swipe-vs-scroll)
16
+ - [4. Pinch Gesture (iOS Only)](#4-pinch-gesture-ios-only)
17
+ - [Pinch to Zoom](#pinch-to-zoom)
18
+ - [5. Long Press Gesture](#5-long-press-gesture)
19
+ - [Basic Long Press](#basic-long-press)
20
+ - [Long Press Duration](#long-press-duration)
21
+ - [Android Long Press Convention](#android-long-press-convention)
22
+ - [6. Shake Gesture](#6-shake-gesture)
23
+ - [Detecting Shake](#detecting-shake)
24
+ - [Shake Example](#shake-example)
25
+ - [Testing Shake](#testing-shake)
26
+ - [7. Accelerometer as Input](#7-accelerometer-as-input)
27
+ - [Basic Accelerometer](#basic-accelerometer)
28
+ - [Accelerometer Properties](#accelerometer-properties)
29
+ - [Using Accelerometer for Control](#using-accelerometer-for-control)
30
+ - [Smoothing Accelerometer Data](#smoothing-accelerometer-data)
31
+ - [8. Gesture Lifecycle Management](#8-gesture-lifecycle-management)
32
+ - [Battery Considerations](#battery-considerations)
33
+ - [9. Platform-Specific Considerations](#9-platform-specific-considerations)
34
+ - [iOS](#ios)
35
+ - [Android](#android)
36
+ - [10. Best Practices](#10-best-practices)
37
+ - [11. Combining Gestures](#11-combining-gestures)
38
+ - [Multiple Gesture Types](#multiple-gesture-types)
39
+ - [Preventing Gesture Conflicts](#preventing-gesture-conflicts)
40
+
41
+ ---
42
+
43
+ ## 1. Overview
44
+
45
+ Titanium supports various gestures beyond simple taps:
46
+ - **Touch Events** - Low-level touch tracking
47
+ - **Swipe** - Left/right drag gestures
48
+ - **Pinch** - Zoom gestures (iOS only)
49
+ - **Long Press** - Extended press gesture
50
+ - **Shake** - Device shake detection
51
+ - **Accelerometer** - Device orientation/movement
52
+
53
+ ## 2. Touch Events
54
+
55
+ ### Touch Lifecycle
56
+
57
+ ```javascript
58
+ const view = Ti.UI.createView({
59
+ backgroundColor: 'blue',
60
+ width: 200, height: 200
61
+ });
62
+
63
+ view.addEventListener('touchstart', (e) => {
64
+ Ti.API.info(`Touch started at: ${e.x}, ${e.y}`);
65
+ });
66
+
67
+ view.addEventListener('touchmove', (e) => {
68
+ Ti.API.info(`Moving to: ${e.x}, ${e.y}`);
69
+ });
70
+
71
+ view.addEventListener('touchend', (e) => {
72
+ Ti.API.info(`Touch ended at: ${e.x}, ${e.y}`);
73
+ });
74
+
75
+ view.addEventListener('touchcancel', (e) => {
76
+ Ti.API.info('Touch cancelled (incoming call, etc.)');
77
+ });
78
+ ```
79
+
80
+ ### Touch Event Properties
81
+
82
+ | Property | Description |
83
+ | ------------- | ---------------------------------------- |
84
+ | `x` | X coordinate in view's coordinate system |
85
+ | `y` | Y coordinate in view's coordinate system |
86
+ | `globalPoint` | Screen coordinates (iOS only) |
87
+
88
+ ### Android Note
89
+
90
+ On Android, `longpress` and `swipe` cancel touch events - `touchend` may not fire after `touchstart`.
91
+
92
+ ## 3. Swipe Gesture
93
+
94
+ ### Basic Swipe
95
+
96
+ ```javascript
97
+ const view = Ti.UI.createView({
98
+ backgroundColor: 'yellow',
99
+ width: Ti.UI.FILL,
100
+ height: 100
101
+ });
102
+
103
+ view.addEventListener('swipe', (e) => {
104
+ Ti.API.info(`Swiped direction: ${e.direction}`);
105
+ // e.direction can be: 'left', 'right', 'up', 'down'
106
+ });
107
+ ```
108
+
109
+ ### Swipe Direction Detection
110
+
111
+ ```javascript
112
+ view.addEventListener('swipe', (e) => {
113
+ switch(e.direction) {
114
+ case 'left':
115
+ Ti.API.info('Swiped left');
116
+ showPreviousPage();
117
+ break;
118
+ case 'right':
119
+ Ti.API.info('Swiped right');
120
+ showNextPage();
121
+ break;
122
+ case 'up':
123
+ Ti.API.info('Swiped up');
124
+ break;
125
+ case 'down':
126
+ Ti.API.info('Swiped down');
127
+ break;
128
+ }
129
+ });
130
+ ```
131
+
132
+ ### Swipe vs Scroll
133
+
134
+ - **Swipe**: Quick flick gesture (left/right mainly)
135
+ - **Scroll**: Sustained drag gesture (up/down mainly)
136
+
137
+ ## 4. Pinch Gesture (iOS Only)
138
+
139
+ ```javascript
140
+ const view = Ti.UI.createView({
141
+ backgroundColor: 'green',
142
+ width: 300, height: 300
143
+ });
144
+
145
+ view.addEventListener('pinch', (e) => {
146
+ Ti.API.info(`Pinch scale: ${e.scale}`);
147
+ // e.scale < 1.0 = pinch together
148
+ // e.scale > 1.0 = pinch apart
149
+ });
150
+ ```
151
+
152
+ ### Pinch to Zoom
153
+
154
+ ```javascript
155
+ const imageView = Ti.UI.createImageView({
156
+ image: 'photo.jpg',
157
+ width: 300, height: 300
158
+ });
159
+
160
+ let currentScale = 1.0;
161
+
162
+ imageView.addEventListener('pinch', (e) => {
163
+ currentScale = e.scale;
164
+ imageView.transform = Ti.UI.create2DMatrix().scale(currentScale);
165
+ });
166
+ ```
167
+
168
+ ## 5. Long Press Gesture
169
+
170
+ ### Basic Long Press
171
+
172
+ ```javascript
173
+ const view = Ti.UI.createView({
174
+ backgroundColor: 'orange',
175
+ width: 200, height: 200
176
+ });
177
+
178
+ view.addEventListener('longpress', (e) => {
179
+ Ti.API.info(`Long press at: ${e.x}, ${e.y}`);
180
+ showContextMenu(e);
181
+ });
182
+ ```
183
+
184
+ ### Long Press Duration
185
+
186
+ Default long press duration varies by platform. Custom handling:
187
+
188
+ ```javascript
189
+ let pressTimer = null;
190
+ const PRESS_DURATION = 1000; // 1 second
191
+
192
+ view.addEventListener('touchstart', (e) => {
193
+ pressTimer = setTimeout(() => {
194
+ showContextMenu(e);
195
+ }, PRESS_DURATION);
196
+ });
197
+
198
+ view.addEventListener('touchend', () => {
199
+ if (pressTimer) {
200
+ clearTimeout(pressTimer);
201
+ pressTimer = null;
202
+ }
203
+ });
204
+
205
+ view.addEventListener('touchmove', () => {
206
+ // Cancel long press if moved significantly
207
+ if (pressTimer) {
208
+ clearTimeout(pressTimer);
209
+ pressTimer = null;
210
+ }
211
+ });
212
+ ```
213
+
214
+ ### Android Long Press Convention
215
+
216
+ On Android, long press typically shows context menu:
217
+
218
+ ```javascript
219
+ view.addEventListener('longpress', (e) => {
220
+ const dialog = Ti.UI.createAlertDialog({
221
+ title: 'Options',
222
+ message: 'What would you like to do?',
223
+ buttonNames: ['Edit', 'Delete', 'Share', 'Cancel']
224
+ });
225
+ dialog.addEventListener('click', (e) => {
226
+ switch(e.index) {
227
+ case 0: editItem(); break;
228
+ case 1: deleteItem(); break;
229
+ case 2: shareItem(); break;
230
+ }
231
+ });
232
+ dialog.show();
233
+ });
234
+ ```
235
+
236
+ ## 6. Shake Gesture
237
+
238
+ ### Detecting Shake
239
+
240
+ ```javascript
241
+ Ti.Gesture.addEventListener('shake', (e) => {
242
+ Ti.API.info(`Device shaken at ${e.timestamp}`);
243
+
244
+ // Refresh content, undo action, etc.
245
+ refreshData();
246
+ });
247
+ ```
248
+
249
+ ### Shake Example
250
+
251
+ ```javascript
252
+ // Use shake to refresh data
253
+ const scrollView = Ti.UI.createScrollView({
254
+ contentHeight: Ti.UI.SIZE
255
+ });
256
+
257
+ Ti.Gesture.addEventListener('shake', () => {
258
+ // Refresh data
259
+ loadDataFromServer();
260
+ });
261
+
262
+ function loadDataFromServer() {
263
+ // ...
264
+ Ti.API.info('Data refreshed due to shake');
265
+ }
266
+ ```
267
+
268
+ ### Testing Shake
269
+
270
+ - **iOS Simulator**: Hardware > Shake Device
271
+ - **Android Emulator**: Not supported - test on physical device
272
+ - **Physical Device**: Just shake the device
273
+
274
+ ## 7. Accelerometer as Input
275
+
276
+ ### Basic Accelerometer
277
+
278
+ ```javascript
279
+ const labelX = Ti.UI.createLabel({ text: 'X: 0' });
280
+ const labelY = Ti.UI.createLabel({ text: 'Y: 0', top: 30 });
281
+ const labelZ = Ti.UI.createLabel({ text: 'Z: 0', top: 60 });
282
+
283
+ Ti.Accelerometer.addEventListener('update', (e) => {
284
+ labelX.text = `X: ${e.x.toFixed(2)}`;
285
+ labelY.text = `Y: ${e.y.toFixed(2)}`;
286
+ labelZ.text = `Z: ${e.z.toFixed(2)}`;
287
+ });
288
+ ```
289
+
290
+ ### Accelerometer Properties
291
+
292
+ | Property | Description | Range |
293
+ | ----------- | ------------------- | -------------------- |
294
+ | `x` | X-axis acceleration | G-force (±9.81 m/s²) |
295
+ | `y` | Y-axis acceleration | G-force |
296
+ | `z` | Z-axis acceleration | G-force |
297
+ | `timestamp` | When event occurred | Timestamp |
298
+
299
+ ### Using Accelerometer for Control
300
+
301
+ ```javascript
302
+ const sensitivity = 2.0;
303
+ let lastX = 0, lastY = 0;
304
+
305
+ Ti.Accelerometer.addEventListener('update', (e) => {
306
+ const deltaX = e.x - lastX;
307
+ const deltaY = e.y - lastY;
308
+
309
+ if (Math.abs(deltaX) > sensitivity) {
310
+ if (deltaX > 0) {
311
+ moveRight();
312
+ } else {
313
+ moveLeft();
314
+ }
315
+ }
316
+
317
+ lastX = e.x;
318
+ lastY = e.y;
319
+ });
320
+ ```
321
+
322
+ ### Smoothing Accelerometer Data
323
+
324
+ Accelerometer data is very sensitive. Apply smoothing:
325
+
326
+ ```javascript
327
+ let samples = [];
328
+ const SAMPLE_SIZE = 10;
329
+
330
+ Ti.Accelerometer.addEventListener('update', (e) => {
331
+ samples.push({ x: e.x, y: e.y, z: e.z });
332
+
333
+ if (samples.length >= SAMPLE_SIZE) {
334
+ // Calculate average
335
+ const avgX = samples.reduce((sum, s) => sum + s.x, 0) / samples.length;
336
+ const avgY = samples.reduce((sum, s) => sum + s.y, 0) / samples.length;
337
+ const avgZ = samples.reduce((sum, s) => sum + s.z, 0) / samples.length;
338
+
339
+ // Use averaged values
340
+ updatePosition(avgX, avgY, avgZ);
341
+
342
+ // Clear samples
343
+ samples = [];
344
+ }
345
+ });
346
+ ```
347
+
348
+ ## 8. Gesture Lifecycle Management
349
+
350
+ ### Battery Considerations
351
+
352
+ Global gesture events (`Ti.Gesture`, `Ti.Accelerometer`) keep hardware powered and drain battery.
353
+
354
+ **Always remove listeners when not needed:**
355
+
356
+ ```javascript
357
+ let accelerometerAdded = false;
358
+ const accelerometerCallback = (e) => {
359
+ // Process accelerometer data
360
+ };
361
+
362
+ function startTracking() {
363
+ if (!accelerometerAdded) {
364
+ Ti.Accelerometer.addEventListener('update', accelerometerCallback);
365
+ accelerometerAdded = true;
366
+ }
367
+ }
368
+
369
+ function stopTracking() {
370
+ if (accelerometerAdded) {
371
+ Ti.Accelerometer.removeEventListener('update', accelerometerCallback);
372
+ accelerometerAdded = false;
373
+ }
374
+ }
375
+
376
+ // Android: Manage with app lifecycle
377
+ if (Ti.Platform.osname === 'android') {
378
+ Ti.Android.currentActivity.addEventListener('pause', () => {
379
+ stopTracking();
380
+ });
381
+
382
+ Ti.Android.currentActivity.addEventListener('resume', () => {
383
+ startTracking();
384
+ });
385
+ }
386
+ ```
387
+
388
+ ## 9. Platform-Specific Considerations
389
+
390
+ ### iOS
391
+
392
+ - **Pinch gesture** fully supported
393
+ - **Simulator shake**: Hardware > Shake Device
394
+ - **More touch events** supported
395
+ - **Smoother gesture recognition**
396
+
397
+ ### Android
398
+
399
+ - **Pinch** limited/experimental support
400
+ - **No simulator shake** - must test on device
401
+ - **Long press** for context menus (standard pattern)
402
+ - **Hardware button events** available
403
+
404
+ ## 10. Best Practices
405
+
406
+ 1. **Test on physical devices** - Simulators don't support all gestures
407
+ 2. **Remove global gesture listeners** when not needed to save battery
408
+ 3. **Smooth accelerometer data** - Use averaging/rounding
409
+ 4. **Use appropriate gesture for context** - Long press for context menus, swipe for navigation
410
+ 5. **Consider accessibility** - Ensure gestures don't conflict with screen readers
411
+ 6. **Handle edge cases** - Touch cancel, gesture interruptions
412
+ 7. **Provide alternatives** - Not all users can perform all gestures
413
+
414
+ ## 11. Combining Gestures
415
+
416
+ ### Multiple Gesture Types
417
+
418
+ ```javascript
419
+ const view = Ti.UI.createView({
420
+ width: 300, height: 300,
421
+ backgroundColor: 'cyan'
422
+ });
423
+
424
+ // Swipe for navigation
425
+ view.addEventListener('swipe', (e) => {
426
+ if (e.direction === 'left') {
427
+ showPrevious();
428
+ } else if (e.direction === 'right') {
429
+ showNext();
430
+ }
431
+ });
432
+
433
+ // Long press for options
434
+ view.addEventListener('longpress', (e) => {
435
+ showOptions();
436
+ });
437
+
438
+ // Double tap for like
439
+ view.addEventListener('doubletap', (e) => {
440
+ likeContent();
441
+ });
442
+
443
+ // Pinch to zoom (iOS)
444
+ view.addEventListener('pinch', (e) => {
445
+ if (e.scale > 1.0) {
446
+ zoomIn();
447
+ } else {
448
+ zoomOut();
449
+ }
450
+ });
451
+ ```
452
+
453
+ ### Preventing Gesture Conflicts
454
+
455
+ ```javascript
456
+ let touchStartTime = 0;
457
+
458
+ view.addEventListener('touchstart', (e) => {
459
+ touchStartTime = Date.now();
460
+ });
461
+
462
+ view.addEventListener('touchend', (e) => {
463
+ const touchDuration = Date.now() - touchStartTime;
464
+
465
+ if (touchDuration < 200) {
466
+ // Short tap - treat as click
467
+ handleClick();
468
+ } else if (touchDuration > 500) {
469
+ // Long touch - already handled by longpress
470
+ // Ignore
471
+ }
472
+ });
473
+ ```