@maccesar/titools 4.1.0 → 4.4.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 (102) hide show
  1. package/README.md +30 -7
  2. package/bin/titools.js +4 -4
  3. package/commands/ti-audit.md +13 -0
  4. package/commands/ti-check.md +15 -0
  5. package/commands/ti-new-screen.md +18 -0
  6. package/lib/claude-plugin.js +105 -0
  7. package/lib/cleanup.js +18 -0
  8. package/lib/commands/doctor.js +75 -3
  9. package/lib/commands/skills.js +66 -6
  10. package/lib/commands/uninstall.js +32 -2
  11. package/lib/commands/update.js +15 -0
  12. package/lib/config.js +37 -0
  13. package/lib/installer.js +85 -1
  14. package/lib/symlink.js +40 -3
  15. package/package.json +2 -1
  16. package/skills/alloy-guides/references/MODELS.md +23 -0
  17. package/skills/alloy-guides/references/MODELS_ADVANCED.md +19 -0
  18. package/skills/alloy-guides/references/VIEWS_STYLES.md +12 -0
  19. package/skills/alloy-guides/references/VIEWS_XML.md +18 -0
  20. package/skills/purgetss/references/EXAMPLES.md +24 -0
  21. package/skills/purgetss/references/animation-advanced.md +12 -0
  22. package/skills/purgetss/references/animation-system.md +23 -0
  23. package/skills/purgetss/references/app-branding.md +23 -0
  24. package/skills/purgetss/references/apply-directive.md +17 -0
  25. package/skills/purgetss/references/arbitrary-values.md +13 -0
  26. package/skills/purgetss/references/class-index-properties.md +11 -0
  27. package/skills/purgetss/references/cli-commands.md +27 -0
  28. package/skills/purgetss/references/customization-deep-dive.md +15 -0
  29. package/skills/purgetss/references/dynamic-component-creation.md +16 -0
  30. package/skills/purgetss/references/migration-guide.md +20 -0
  31. package/skills/purgetss/references/multi-density-images.md +27 -0
  32. package/skills/purgetss/references/semantic-colors.md +15 -0
  33. package/skills/purgetss/references/tikit-components.md +17 -0
  34. package/skills/purgetss/references/titanium-resets.md +16 -0
  35. package/skills/purgetss/references/ui-ux-design.md +20 -0
  36. package/skills/ti-api/references/api-android.md +23 -0
  37. package/skills/ti-api/references/api-app-platform.md +24 -0
  38. package/skills/ti-api/references/api-core.md +24 -0
  39. package/skills/ti-api/references/api-data-network.md +21 -0
  40. package/skills/ti-api/references/api-media.md +15 -1
  41. package/skills/ti-api/references/api-modules-ble-bluetooth.md +26 -0
  42. package/skills/ti-api/references/api-modules-coremotion-urlsession.md +18 -0
  43. package/skills/ti-api/references/api-modules-map.md +21 -2
  44. package/skills/ti-api/references/api-modules-nfc.md +34 -0
  45. package/skills/ti-api/references/api-modules-social-misc.md +22 -0
  46. package/skills/ti-api/references/api-services.md +21 -1
  47. package/skills/ti-api/references/api-ui-android.md +18 -0
  48. package/skills/ti-api/references/api-ui-extras.md +19 -1
  49. package/skills/ti-api/references/api-ui-ios-animator.md +14 -0
  50. package/skills/ti-api/references/api-ui-ios.md +42 -0
  51. package/skills/ti-api/references/api-ui-lists.md +14 -2
  52. package/skills/ti-api/references/api-ui-text-input.md +15 -0
  53. package/skills/ti-api/references/api-ui-views.md +19 -0
  54. package/skills/ti-api/references/api-ui-windows-navigation.md +13 -0
  55. package/skills/ti-api/references/api-xml-global.md +31 -0
  56. package/skills/ti-expert/SKILL.md +38 -5
  57. package/skills/ti-expert/references/adaptive-layouts.md +14 -0
  58. package/skills/ti-expert/references/alloy-builtins.md +26 -22
  59. package/skills/ti-expert/references/alloy-structure.md +57 -18
  60. package/skills/ti-expert/references/anti-patterns.md +13 -5
  61. package/skills/ti-expert/references/code-conventions.md +28 -3
  62. package/skills/ti-expert/references/controller-patterns.md +51 -26
  63. package/skills/ti-expert/references/error-handling.md +16 -0
  64. package/skills/ti-expert/references/examples.md +65 -21
  65. package/skills/ti-expert/references/feedback-migration.md +165 -0
  66. package/skills/ti-expert/references/feedback-surfaces.md +140 -0
  67. package/skills/ti-expert/references/feedback-widget-contracts.md +213 -0
  68. package/skills/ti-expert/references/migration-patterns.md +61 -24
  69. package/skills/ti-expert/references/patterns.md +16 -0
  70. package/skills/ti-expert/references/performance-optimization.md +17 -0
  71. package/skills/ti-expert/references/security-device.md +71 -56
  72. package/skills/ti-expert/references/state-management.md +21 -5
  73. package/skills/ti-expert/references/testing-e2e-ci.md +17 -0
  74. package/skills/ti-expert/references/testing-unit.md +20 -0
  75. package/skills/ti-expert/references/theming.md +135 -373
  76. package/skills/ti-guides/references/app-distribution.md +34 -0
  77. package/skills/ti-guides/references/application-frameworks.md +13 -0
  78. package/skills/ti-guides/references/cli-reference.md +16 -0
  79. package/skills/ti-guides/references/hyperloop-native-access.md +21 -0
  80. package/skills/ti-guides/references/javascript-primer.md +12 -0
  81. package/skills/ti-guides/references/tiapp-config.md +15 -0
  82. package/skills/ti-howtos/references/android-platform-deep-dives.md +15 -0
  83. package/skills/ti-howtos/references/cross-platform-development.md +16 -0
  84. package/skills/ti-howtos/references/debugging-profiling.md +15 -0
  85. package/skills/ti-howtos/references/extending-titanium.md +14 -0
  86. package/skills/ti-howtos/references/ios-platform-deep-dives.md +16 -0
  87. package/skills/ti-howtos/references/local-data-sources.md +32 -0
  88. package/skills/ti-howtos/references/notification-services.md +12 -0
  89. package/skills/ti-howtos/references/remote-data-sources.md +15 -0
  90. package/skills/ti-howtos/references/tutorials.md +13 -0
  91. package/skills/ti-ui/references/animation-and-matrices.md +17 -0
  92. package/skills/ti-ui/references/application-structures.md +19 -0
  93. package/skills/ti-ui/references/custom-fonts-styling.md +18 -0
  94. package/skills/ti-ui/references/event-handling.md +16 -0
  95. package/skills/ti-ui/references/gestures.md +17 -0
  96. package/skills/ti-ui/references/icons-and-splash-screens.md +17 -0
  97. package/skills/ti-ui/references/layouts-and-positioning.md +16 -0
  98. package/skills/ti-ui/references/listviews-and-performance.md +21 -0
  99. package/skills/ti-ui/references/orientation.md +20 -0
  100. package/skills/ti-ui/references/platform-ui-android.md +20 -0
  101. package/skills/ti-ui/references/platform-ui-ios.md +17 -0
  102. package/skills/ti-ui/references/tableviews.md +24 -0
@@ -0,0 +1,213 @@
1
+ # Contracts for feedback Widgets
2
+
3
+ Use these contracts for app-owned Snackbar, Dialog, and Bottom Sheet surfaces in
4
+ Alloy. Namespaces and visual tokens belong to each app; behavior stays stable.
5
+
6
+ ## Contents
7
+
8
+ - [Boundary and registration](#boundary-and-registration)
9
+ - [Snackbar](#snackbar)
10
+ - [Dialog](#dialog)
11
+ - [Bottom Sheet](#bottom-sheet)
12
+ - [Shared lifecycle contract](#shared-lifecycle-contract)
13
+ - [PurgeTSS integration](#purgetss-integration)
14
+ - [Widget, Require, state, or native UI](#widget-require-state-or-native-ui)
15
+
16
+ ## Boundary and registration
17
+
18
+ Implement each surface as an independent Alloy Widget under `app/widgets/`.
19
+ Each Widget owns its XML, controller, optional styles, assets, state, listeners,
20
+ timers, and `widget.json`. Keep them independent from any third-party component
21
+ library — including TiKit, the Alloy/PurgeTSS component set. A feedback surface
22
+ must not require one to work, and must not be folded into one: its value comes
23
+ from being installable and testable on its own.
24
+
25
+ Register every Widget in `app/config.json`:
26
+
27
+ ```json
28
+ {
29
+ "dependencies": {
30
+ "com.example.ui.bottomsheet": "1.0.0",
31
+ "com.example.ui.dialog": "1.0.0",
32
+ "com.example.ui.snackbar": "1.0.0"
33
+ }
34
+ }
35
+ ```
36
+
37
+ Use the app's reverse-domain namespace. The dependency key must match the `id`
38
+ in that Widget's `widget.json`.
39
+
40
+ Declare Widgets in XML, normally as the last visual children of the owning
41
+ window so overlays render above screen content:
42
+
43
+ ```xml
44
+ <Widget id="snackbar" src="com.example.ui.snackbar" />
45
+ <Widget id="dialog" src="com.example.ui.dialog" />
46
+ <Widget id="bottomSheet" src="com.example.ui.bottomsheet" />
47
+ ```
48
+
49
+ Build the stable proxy tree in XML. JavaScript may update properties and supply
50
+ `ListView` data; it must not reconstruct the complete surface imperatively.
51
+
52
+ ## Snackbar
53
+
54
+ ```javascript
55
+ $.snackbar.show({
56
+ message,
57
+ tone: 'neutral', // neutral | success | warning | error
58
+ duration: 4500,
59
+ actionTitle,
60
+ onAction,
61
+ onDismiss
62
+ })
63
+
64
+ $.snackbar.dismiss()
65
+ $.snackbar.destroy()
66
+ ```
67
+
68
+ Contract:
69
+
70
+ - A new message replaces the visible message and restarts the timer.
71
+ - Default `duration` is 4500 ms; `duration: 0` remains visible until an action,
72
+ `dismiss()`, replacement, or destruction.
73
+ - Allow at most one text action, such as Undo.
74
+ - Invoke `onAction` once, then close the Snackbar.
75
+ - Invoke `onDismiss` once with the reason `timeout`, `action`, `replaced`,
76
+ `programmatic`, or `destroy`.
77
+ - Clear the old timer before replacement and on every close path.
78
+ - Position above bottom navigation and inside the current safe area.
79
+ - Queueing Snackbars is not the default. The newest relevant message wins.
80
+
81
+ ## Dialog
82
+
83
+ ```javascript
84
+ $.dialog.attach(window)
85
+
86
+ $.dialog.show({
87
+ title,
88
+ message,
89
+ tone: 'neutral', // neutral | warning | error
90
+ confirmTitle,
91
+ cancelTitle,
92
+ destructive: false,
93
+ onConfirm,
94
+ onCancel
95
+ })
96
+
97
+ $.dialog.dismiss('programmatic')
98
+ $.dialog.destroy()
99
+ ```
100
+
101
+ Contract:
102
+
103
+ - Without `cancelTitle`, present an acknowledgement. With it, present a
104
+ confirmation.
105
+ - Enqueue additional `show()` calls FIFO. Never stack two Dialogs visually.
106
+ - Disable both buttons on the first accepted tap.
107
+ - Invoke `onConfirm` or `onCancel` at most once; never both.
108
+ - Cancel button, Android Back, or allowed backdrop dismissal invokes `onCancel`.
109
+ Android Back closes an acknowledgement as its single dismissal action.
110
+ - A destructive confirmation must not close from a backdrop tap.
111
+ - `dismiss('programmatic')` closes without treating the operation as a user
112
+ confirmation or cancellation.
113
+ - `destroy()` clears the active request and queue without running business
114
+ callbacks.
115
+ - Use platform-familiar safe/cancel ordering and a distinct destructive role;
116
+ do not hard-code one platform's button order for both.
117
+
118
+ ## Bottom Sheet
119
+
120
+ ```javascript
121
+ $.bottomSheet.attach(window)
122
+
123
+ $.bottomSheet.show({
124
+ title,
125
+ message,
126
+ items: [{
127
+ id,
128
+ title,
129
+ subtitle,
130
+ icon,
131
+ tone: 'neutral',
132
+ enabled: true
133
+ }],
134
+ selectedId,
135
+ onSelect,
136
+ onCancel
137
+ })
138
+
139
+ $.bottomSheet.dismiss('cancel')
140
+ $.bottomSheet.destroy()
141
+ ```
142
+
143
+ Contract:
144
+
145
+ - Render items with a fixed `ListView` template and data objects. Support long
146
+ lists, disabled rows, optional subtitles/icons, and a selected marker.
147
+ - Enqueue additional `show()` calls FIFO. Never stack sheets.
148
+ - Accept the first enabled selection only, invoke `onSelect(item)` once, and
149
+ close.
150
+ - Backdrop, Android Back, Cancel, or `dismiss('cancel')` invokes `onCancel` once.
151
+ - Other programmatic dismissal and `destroy()` do not imply cancellation and do
152
+ not execute business callbacks.
153
+ - Present from the bottom on phones. On tablets and large screens, use a centered,
154
+ width-limited action surface. Reserve `anchor` for a future anchored variant;
155
+ do not expose behavior that is not implemented.
156
+
157
+ ## Shared lifecycle contract
158
+
159
+ - Prefer one instance of each needed Widget per window. Add a global coordinator
160
+ only after real cross-window requirements exist.
161
+ - Call `attach(window)` after the host window/controller exists. Call `destroy()`
162
+ from the host's close cleanup.
163
+ - Make `destroy()` idempotent. Remove listeners, clear timers, cancel pending
164
+ animation callbacks, clear queues, and release callback references.
165
+ - Guard every asynchronous completion with the current presentation generation
166
+ so a stale timer or animation cannot dismiss a newer request.
167
+ - Consume backdrop events and prevent them from bubbling to underlying controls.
168
+ - Save the host's previous accessibility state, exclude underlying content while
169
+ modal, move focus to the surface, and restore the saved state on close.
170
+ - Keep localized copy outside the Widget. Pass strings and business callbacks in
171
+ the public API.
172
+ - Keep business logic out of the Widget. It reports a choice; the host performs
173
+ domain work.
174
+ - Verify every callback path for exactly-once behavior: action, confirm, cancel,
175
+ Back, backdrop, timeout, replacement, programmatic dismissal, and destruction.
176
+
177
+ ## PurgeTSS integration
178
+
179
+ When `purgetss/` or `purgetss/config.cjs` exists, load the `purgetss` skill before
180
+ writing styles. `ti-expert` decides the component boundary; `purgetss` decides
181
+ the verified utilities and configuration.
182
+
183
+ Apply these architectural rules:
184
+
185
+ - Set PurgeTSS `options.widgets` to `true` so Widget XML is scanned.
186
+ - Express stable anatomy and utility classes in Widget XML.
187
+ - Use semantic color names for surfaces, text, borders, accent, overlay, success,
188
+ warning, and error. Let Titanium resolve light/dark variants.
189
+ - Do not edit generated `app/styles/app.tss`.
190
+ - Use `widget.tss` only for properties that have no verified utility or that are
191
+ inherently specific to the Widget.
192
+ - Declare animations as XML `Animation` proxies using `purgetss.ui`. Use explicit
193
+ `open()` and `close()` calls instead of toggle behavior.
194
+ - After generation, inspect the final `app.tss` section for unused or unsupported
195
+ classes and resolve every new entry.
196
+
197
+ Do not copy class inventories into this skill and do not guess Tailwind-like
198
+ names. The installed PurgeTSS skill remains the source of truth.
199
+
200
+ ## Widget, Require, state, or native UI
201
+
202
+ | Choose | When |
203
+ | --- | --- |
204
+ | Alloy Widget | Self-contained public API, state/lifecycle ownership, portable boundary, or reuse across apps/screens |
205
+ | `<Require>` controller | App-specific view/controller composition that shares the host's domain and lifecycle |
206
+ | Screen state in XML | Loading, empty, content, or error presentation belongs only to that screen |
207
+ | TSS or PurgeTSS utilities | Repeated visual treatment has no behavior, state, or lifecycle |
208
+ | Native/system UI | The operating system owns permissions, trust, files, sharing, identity, purchases, or another protected flow |
209
+
210
+ Usage count is evidence, not a threshold. A component can deserve a Widget on
211
+ its first use if it has a stable portable API and non-trivial lifecycle; three
212
+ similar labels still do not deserve one.
213
+
@@ -1,29 +1,57 @@
1
1
  # Migration patterns for legacy Titanium apps
2
2
 
3
+ <!-- TOC-START -->
4
+ ## Contents
5
+
6
+ - [Assessment checklist](#assessment-checklist)
7
+ - [Phase 1: styling-system organization](#phase-1-styling-system-organization)
8
+ - [Phase 1B: feedback surfaces](#phase-1b-feedback-surfaces)
9
+ - [Phase 2: service layer extraction](#phase-2-service-layer-extraction)
10
+ - [Phase 3: event system migration](#phase-3-event-system-migration)
11
+ - [Phase 4: navigation service](#phase-4-navigation-service)
12
+ - [Phase 5: state management](#phase-5-state-management)
13
+ - [Migration order](#migration-order)
14
+ - [Rollback strategy](#rollback-strategy)
15
+ - [Common migration pitfalls](#common-migration-pitfalls)
16
+
17
+ <!-- TOC-END -->
18
+
3
19
  ## Assessment checklist
4
20
 
5
21
  Before migrating, evaluate the current codebase:
6
22
 
7
23
  | Area | Signs of Legacy Code | Target State |
8
24
  | ----------- | -------------------------------------------------- | --------------------------------------------- |
9
- | Styling | Inline attributes, scattered styles | Organized TSS files (per-controller + global) |
25
+ | Styling | Inline attributes, scattered styles | PurgeTSS utilities when detected; otherwise organized Alloy TSS |
10
26
  | Controllers | 200+ lines, API calls, business logic | Thin orchestrators (<100 lines) |
11
27
  | Events | `Ti.App.fireEvent` everywhere | Backbone.Events or StateStore |
12
28
  | Navigation | Direct `Alloy.createController().getView().open()` | Navigation service |
13
29
  | Data | Scattered `Ti.App.Properties`, no collections | Centralized state + Collections |
14
30
 
15
- ## Phase 1: TSS organization
31
+ ## Phase 1: styling-system organization
32
+
33
+ **Goal**: Migrate from inline and scattered styling into the styling system the
34
+ project actually uses.
35
+
36
+ ### Step 0: detect PurgeTSS
37
+
38
+ Check for `purgetss/` or `purgetss/config.cjs` before editing styles.
16
39
 
17
- **Goal**: Migrate from inline styling and scattered styles to well-organized TSS files.
40
+ - **PurgeTSS detected:** invoke the `purgetss` skill, use verified utilities and
41
+ semantic colors, configure reusable rules in `config.cjs`, and never edit the
42
+ generated `app/styles/app.tss`.
43
+ - **No PurgeTSS:** use standard per-controller TSS and a manually maintained
44
+ `app/styles/app.tss` for genuinely global selectors.
45
+
46
+ Do not migrate a PurgeTSS application back into manual TSS by following a generic
47
+ Alloy example.
18
48
 
19
49
  ### Step 1: audit existing styles
20
50
 
21
- Find all inline attributes and consolidate into TSS files:
51
+ Find inline attributes and classify them for the detected styling system:
22
52
  ```bash
23
53
  # Find inline styling in XML views
24
- grep -r 'backgroundColor=' app/views/
25
- grep -r 'font=' app/views/
26
- grep -r 'color=' app/views/
54
+ rg -n 'backgroundColor=|font=|color=' app/views/
27
55
  ```
28
56
 
29
57
  ### Step 2: move inline attributes to TSS
@@ -36,7 +64,7 @@ grep -r 'color=' app/views/
36
64
  ```
37
65
 
38
66
  ```xml
39
- <!-- AFTER: Clean XML + TSS file -->
67
+ <!-- AFTER in standard Alloy: clean XML + TSS file -->
40
68
  <View id="header">
41
69
  <Label id="title" text="L('welcome')" />
42
70
  </View>
@@ -48,9 +76,11 @@ grep -r 'color=' app/views/
48
76
  "#title": { color: "#333", font: { fontSize: 18, fontWeight: "bold" } }
49
77
  ```
50
78
 
51
- ### Step 3: organize TSS structure
79
+ ### Step 3: organize standard Alloy TSS
80
+
81
+ This step applies only when PurgeTSS is not present.
52
82
 
53
- 1. Use `app.tss` for global styles (shared across all views)
83
+ 1. Use `app.tss` for genuinely global styles shared across views
54
84
  2. Use per-controller TSS files for view-specific styles
55
85
  3. Use class selectors for reusable style patterns:
56
86
  ```tss
@@ -59,6 +89,18 @@ grep -r 'color=' app/views/
59
89
  ".btn-primary": { backgroundColor: '#2563eb', color: '#fff', height: 44, borderRadius: 8 }
60
90
  ```
61
91
 
92
+ In either styling path, replace hard-coded visual colors with semantic roles as
93
+ part of the migration. See [Theming and Dark Mode](theming.md).
94
+
95
+ ## Phase 1B: feedback surfaces
96
+
97
+ Inventory native alerts, option dialogs, Toasts, and local overlays. Classify
98
+ each occurrence before replacing it, then migrate one window at a time. Preserve
99
+ system-owned pickers, permission prompts, sharing, and biometric UI.
100
+
101
+ Use [Feedback Migration](feedback-migration.md) for the sequence, deliberate
102
+ silence rules, and verification contract.
103
+
62
104
  ## Phase 2: service layer extraction
63
105
 
64
106
  **Goal**: Move business logic from controllers to services.
@@ -265,11 +307,12 @@ const { user } = appStore.getState()
265
307
 
266
308
  Recommended sequence to minimize risk:
267
309
 
268
- 1. **TSS Organization** - Visual only, no logic changes
269
- 2. **Service Layer** - Extract logic without changing behavior
270
- 3. **Navigation Service** - Centralize with cleanup
271
- 4. **Event System** - Replace Ti.App events
272
- 5. **State Management** - Last, as it touches everything
310
+ 1. **Styling-system organization** - Establish semantic tokens and the correct PurgeTSS/Alloy path
311
+ 2. **Feedback surfaces** - Migrate one window without changing domain outcomes
312
+ 3. **Service Layer** - Extract logic without changing behavior
313
+ 4. **Navigation Service** - Centralize with cleanup
314
+ 5. **Event System** - Replace Ti.App events
315
+ 6. **State Management** - Last, as it touches everything
273
316
 
274
317
  ## Rollback strategy
275
318
 
@@ -278,16 +321,10 @@ For each phase:
278
321
  1. **Create a branch** before starting
279
322
  2. **Migrate one feature** completely
280
323
  3. **Test thoroughly** before moving to next feature
281
- 4. **Keep legacy code** commented until stable
324
+ 4. **Delete the replaced implementation** after the vertical slice passes
282
325
 
283
- ```javascript
284
- // Temporary: Keep both during migration
285
- // Legacy:
286
- // Ti.App.fireEvent('user:updated', data)
287
-
288
- // New:
289
- Alloy.Events.trigger('user:updated', data)
290
- ```
326
+ Use version control for rollback. Do not keep commented legacy implementations:
327
+ they conceal duplicate listeners, stale callbacks, and incomplete migrations.
291
328
 
292
329
  ## Common migration pitfalls
293
330
 
@@ -1,5 +1,20 @@
1
1
  # Architectural patterns for Titanium + Alloy
2
2
 
3
+ <!-- TOC-START -->
4
+ ## Contents
5
+
6
+ - [1. Native module abstraction (Wrapper)](#1-native-module-abstraction-wrapper)
7
+ - [2. Repository pattern for data](#2-repository-pattern-for-data)
8
+ - [3. Service layer pattern](#3-service-layer-pattern)
9
+ - [4. Event bus for decoupling](#4-event-bus-for-decoupling)
10
+ - [5. Factory pattern for dynamic views](#5-factory-pattern-for-dynamic-views)
11
+ - [6. Navigation service pattern](#6-navigation-service-pattern)
12
+ - [7. Collection binding pattern](#7-collection-binding-pattern)
13
+ - [8. Cleanup pattern](#8-cleanup-pattern)
14
+ - [9. Singleton service pattern](#9-singleton-service-pattern)
15
+
16
+ <!-- TOC-END -->
17
+
3
18
  ## 1. Native module abstraction (Wrapper)
4
19
 
5
20
  **Use when:** Using native modules like Maps, Biometrics, or specialized Media players.
@@ -446,3 +461,4 @@ class AnalyticsService {
446
461
  }
447
462
 
448
463
  exports.Analytics = new AnalyticsService()
464
+ ```
@@ -1,5 +1,22 @@
1
1
  # Performance optimization: bridge, memory, animation & timing
2
2
 
3
+ <!-- TOC-START -->
4
+ ## Contents
5
+
6
+ - [Bridge optimization](#bridge-optimization)
7
+ - [Memory management](#memory-management)
8
+ - [Image memory management](#image-memory-management)
9
+ - [Lazy loading pattern](#lazy-loading-pattern)
10
+ - [Database performance](#database-performance)
11
+ - [Performance monitoring](#performance-monitoring)
12
+ - [Animation performance](#animation-performance)
13
+ - [Hardware-accelerated properties](#hardware-accelerated-properties)
14
+ - [Animation cleanup](#animation-cleanup)
15
+ - [Debouncing and throttling](#debouncing-and-throttling)
16
+ - [Performance checklist](#performance-checklist)
17
+
18
+ <!-- TOC-END -->
19
+
3
20
  ## Bridge optimization
4
21
 
5
22
  ### Minimize bridge crossings
@@ -1,5 +1,18 @@
1
1
  # Device security: biometrics, deep links & integrity
2
2
 
3
+ <!-- TOC-START -->
4
+ ## Contents
5
+
6
+ - [Biometric authentication](#biometric-authentication)
7
+ - [Biometric login flow](#biometric-login-flow)
8
+ - [Deep link security](#deep-link-security)
9
+ - [Registering deep link handler](#registering-deep-link-handler)
10
+ - [Jailbreak/Root detection](#jailbreakroot-detection)
11
+ - [Integrating security checks](#integrating-security-checks)
12
+ - [Security checklist](#security-checklist)
13
+
14
+ <!-- TOC-END -->
15
+
3
16
  ## Biometric authentication
4
17
 
5
18
  ### Using ti.identity module
@@ -92,6 +105,9 @@ const { TokenStorage } = require('services/tokenStorage')
92
105
  const { AuthService } = require('services/authService')
93
106
 
94
107
  function init() {
108
+ // The login view declares the app-owned Dialog Widget as its last child.
109
+ $.dialog.attach($.getView())
110
+
95
111
  // Check if biometric login is available and enabled
96
112
  const { available, type } = BiometricService.checkAvailability()
97
113
  const biometricEnabled = Ti.App.Properties.getBool('biometricEnabled', false)
@@ -130,22 +146,25 @@ async function onLoginSuccess(user) {
130
146
  if (available && !Ti.App.Properties.getBool('askedBiometric', false)) {
131
147
  Ti.App.Properties.setBool('askedBiometric', true)
132
148
 
133
- // Ask user if they want to enable biometric login
134
- const dialog = Ti.UI.createAlertDialog({
149
+ // The choice is app-owned; the later biometric authentication prompt remains native.
150
+ $.dialog.show({
135
151
  title: String.format(L('enable_biometric_title'), type),
136
152
  message: String.format(L('enable_biometric_msg'), type),
137
- buttonNames: [L('not_now'), L('enable')]
138
- })
139
-
140
- dialog.addEventListener('click', (e) => {
141
- if (e.index === 1) {
153
+ cancelTitle: L('not_now'),
154
+ confirmTitle: L('enable'),
155
+ onConfirm: () => {
142
156
  Ti.App.Properties.setBool('biometricEnabled', true)
143
157
  }
144
158
  })
145
-
146
- dialog.show()
147
159
  }
148
160
  }
161
+
162
+ function cleanup() {
163
+ $.dialog.destroy()
164
+ $.destroy()
165
+ }
166
+
167
+ $.cleanup = cleanup
149
168
  ```
150
169
 
151
170
  ## Deep link security
@@ -469,7 +488,9 @@ exports.SecurityService = {
469
488
  enforceSecurityPolicy() {
470
489
  const { compromised, reasons } = this.checkDeviceIntegrity()
471
490
 
472
- if (!compromised) return true
491
+ if (!compromised) {
492
+ return { outcome: 'allow', reasons: [], restricted: false }
493
+ }
473
494
 
474
495
  // Log security event
475
496
  logger.error('Security', 'Compromised device detected', {
@@ -483,50 +504,16 @@ exports.SecurityService = {
483
504
 
484
505
  switch (policy) {
485
506
  case 'block':
486
- // Prevent app usage
487
- this._showBlockedScreen()
488
- return false
507
+ return { outcome: 'block', reasons, restricted: true }
489
508
 
490
509
  case 'restrict':
491
- // Disable sensitive features
492
510
  Ti.App.Properties.setBool('restrictedMode', true)
493
- this._showWarning()
494
- return true
511
+ return { outcome: 'warn', reasons, restricted: true }
495
512
 
496
513
  case 'warn':
497
514
  default:
498
- // Just warn the user
499
- this._showWarning()
500
- return true
515
+ return { outcome: 'warn', reasons, restricted: false }
501
516
  }
502
- },
503
-
504
- _showBlockedScreen() {
505
- // Note: iOS guidelines prohibit programmatically terminating an app.
506
- // The best practice is to show a blocking dialog and prevent further interaction.
507
- // On Android you can call Ti.Android.currentActivity.finish() to close the activity.
508
- const dialog = Ti.UI.createAlertDialog({
509
- title: L('security_blocked_title'),
510
- message: L('security_blocked_msg'),
511
- buttonNames: [L('close')]
512
- })
513
-
514
- dialog.addEventListener('click', () => {
515
- if (OS_ANDROID) {
516
- Ti.Android.currentActivity.finish()
517
- }
518
- // On iOS: leave the dialog visible — Apple guidelines prohibit force-quitting apps.
519
- })
520
-
521
- dialog.show()
522
- },
523
-
524
- _showWarning() {
525
- Ti.UI.createAlertDialog({
526
- title: L('security_warning_title'),
527
- message: L('security_warning_msg'),
528
- buttonNames: [L('i_understand')]
529
- }).show()
530
517
  }
531
518
  }
532
519
  ```
@@ -537,18 +524,45 @@ exports.SecurityService = {
537
524
  // alloy.js
538
525
  const { SecurityService } = require('services/securityService')
539
526
 
540
- // Check device integrity at app start
541
- const securityCheck = SecurityService.enforceSecurityPolicy()
527
+ // Services return policy; they do not create UI before the root exists.
528
+ const securityDecision = SecurityService.enforceSecurityPolicy()
529
+ const root = Alloy.createController('index', { securityDecision })
530
+ root.getView().open()
531
+ ```
532
+
533
+ ```javascript
534
+ // controllers/index.js
535
+ const decision = $.args.securityDecision
536
+
537
+ function init() {
538
+ $.dialog.attach($.indexWindow)
539
+
540
+ if (decision.outcome === 'block') {
541
+ // Show a dedicated blocking state in the root XML and do not initialize
542
+ // protected application content. Do not force-quit the app.
543
+ $.blockingState.visible = true
544
+ $.appContent.visible = false
545
+ return
546
+ }
547
+
548
+ initializeAppContent({ restricted: decision.restricted })
549
+
550
+ if (decision.outcome === 'warn') {
551
+ $.dialog.show({
552
+ title: L('security_warning_title'),
553
+ message: L('security_warning_msg'),
554
+ tone: 'warning',
555
+ confirmTitle: L('i_understand')
556
+ })
557
+ }
558
+ }
542
559
 
543
- if (!securityCheck) {
544
- // Don't initialize the app
545
- return
560
+ function cleanup() {
561
+ $.dialog.destroy()
562
+ $.destroy()
546
563
  }
547
564
 
548
- // Check again periodically (in case of runtime jailbreak tools)
549
- setInterval(() => {
550
- SecurityService.checkDeviceIntegrity()
551
- }, 5 * 60 * 1000) // Every 5 minutes
565
+ $.cleanup = cleanup
552
566
  ```
553
567
 
554
568
  ## Security checklist
@@ -565,3 +579,4 @@ setInterval(() => {
565
579
  | **Integrity** | Check for jailbreak/root | checkDeviceIntegrity() |
566
580
  | **Integrity** | Define security policy | block/restrict/warn |
567
581
  | **Integrity** | Log security events | Always log compromises |
582
+ | **Integrity UI** | Keep policy out of services | Root controller presents blocking state or Dialog |
@@ -1,5 +1,21 @@
1
1
  # State management patterns
2
2
 
3
+ <!-- TOC-START -->
4
+ ## Contents
5
+
6
+ - [The problem](#the-problem)
7
+ - [Solution: centralized state store](#solution-centralized-state-store)
8
+ - [State store usage](#state-store-usage)
9
+ - [Service layer state caching](#service-layer-state-caching)
10
+ - [Collections vs state store](#collections-vs-state-store)
11
+ - [State synchronization pattern](#state-synchronization-pattern)
12
+ - [Anti-patterns](#anti-patterns)
13
+ - [Persistence strategies](#persistence-strategies)
14
+ - [State middleware](#state-middleware)
15
+ - [State debugging](#state-debugging)
16
+
17
+ <!-- TOC-END -->
18
+
3
19
  ## The problem
4
20
 
5
21
  As apps grow, state gets scattered:
@@ -730,6 +746,7 @@ appStore.use(debugMiddleware)
730
746
  // controllers/debug/stateViewer.js (Development only)
731
747
  const { StateDebug } = require('services/stateDebug')
732
748
  const { appStore } = require('services/stateStore')
749
+ const { Navigation } = require('services/navigation')
733
750
 
734
751
  function init() {
735
752
  if (!Alloy.CFG.debug) {
@@ -763,12 +780,11 @@ function onHistoryItemClick(e) {
763
780
  const index = e.itemIndex
764
781
  const entry = StateDebug.getHistory()[index]
765
782
 
766
- // Show diff
767
- Ti.UI.createAlertDialog({
783
+ // Structured, potentially long content belongs in a scrollable debug screen.
784
+ Navigation.open('debug/stateDiff', {
768
785
  title: entry.action,
769
- message: JSON.stringify(entry.diff, null, 2),
770
- buttonNames: ['OK']
771
- }).show()
786
+ content: JSON.stringify(entry.diff, null, 2)
787
+ })
772
788
  }
773
789
 
774
790
  function cleanup() {
@@ -1,5 +1,22 @@
1
1
  # End-to-end testing & CI/CD
2
2
 
3
+ <!-- TOC-START -->
4
+ ## Contents
5
+
6
+ - [Appium setup](#appium-setup)
7
+ - [Appium configuration](#appium-configuration)
8
+ - [Webdriverio integration](#webdriverio-integration)
9
+ - [E2E test example](#e2e-test-example)
10
+ - [Adding accessibility IDs for testing](#adding-accessibility-ids-for-testing)
11
+ - [Page object pattern](#page-object-pattern)
12
+ - [CI/CD integration](#cicd-integration)
13
+ - [Fastlane integration](#fastlane-integration)
14
+ - [Package.json scripts](#packagejson-scripts)
15
+ - [Branch protection rules](#branch-protection-rules)
16
+ - [Testing best practices summary](#testing-best-practices-summary)
17
+
18
+ <!-- TOC-END -->
19
+
3
20
  ## Appium setup
4
21
 
5
22
  ```bash