@onekeyfe/react-native-native-list 3.0.157-alpha.248 → 3.0.157-alpha.250

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 (130) hide show
  1. package/README.md +21 -5
  2. package/android/src/main/java/com/onekey/nativelist/NativeListAccessoryStack.kt +711 -0
  3. package/android/src/main/java/com/onekey/nativelist/NativeListActionRenderer.kt +169 -0
  4. package/android/src/main/java/com/onekey/nativelist/NativeListActivityRenderer.kt +243 -0
  5. package/android/src/main/java/com/onekey/nativelist/NativeListAdapter.kt +20 -6
  6. package/android/src/main/java/com/onekey/nativelist/NativeListDataRowRenderer.kt +487 -0
  7. package/android/src/main/java/com/onekey/nativelist/NativeListIdentityRenderer.kt +820 -0
  8. package/android/src/main/java/com/onekey/nativelist/NativeListImageSlot.kt +147 -0
  9. package/android/src/main/java/com/onekey/nativelist/NativeListLeadingVisual.kt +453 -0
  10. package/android/src/main/java/com/onekey/nativelist/NativeListMarketRenderer.kt +997 -0
  11. package/android/src/main/java/com/onekey/nativelist/NativeListMediaTileRenderer.kt +234 -0
  12. package/android/src/main/java/com/onekey/nativelist/NativeListMessageRenderer.kt +232 -0
  13. package/android/src/main/java/com/onekey/nativelist/NativeListMetricCardRenderer.kt +551 -0
  14. package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +24 -0
  15. package/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt +475 -0
  16. package/android/src/main/java/com/onekey/nativelist/NativeListRailRenderer.kt +143 -0
  17. package/android/src/main/java/com/onekey/nativelist/NativeListRendererRegistry.kt +42 -0
  18. package/android/src/main/java/com/onekey/nativelist/NativeListRendererRowView.kt +415 -0
  19. package/android/src/main/java/com/onekey/nativelist/NativeListResolvedText.kt +163 -0
  20. package/android/src/main/java/com/onekey/nativelist/NativeListRowHost.kt +70 -0
  21. package/android/src/main/java/com/onekey/nativelist/NativeListSectionHeaderRenderer.kt +748 -0
  22. package/android/src/main/java/com/onekey/nativelist/NativeListSystemRenderer.kt +464 -0
  23. package/android/src/main/java/com/onekey/nativelist/NativeListTextView.kt +25 -0
  24. package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +174 -104
  25. package/android/src/main/java/com/onekey/nativelist/NativeListWalletGroupRenderer.kt +407 -0
  26. package/android/src/test/java/com/margelo/nitro/nativelist/NativeListSelectorBackgroundColorTest.kt +34 -0
  27. package/docs/DESIGN.md +518 -9
  28. package/docs/MIGRATION_BASELINE.md +117 -0
  29. package/docs/ROW_TEMPLATES.md +359 -0
  30. package/docs/SPEC.md +358 -0
  31. package/docs/STYLE_SPEC.md +978 -0
  32. package/docs/images/list-capabilities.svg +39 -0
  33. package/docs/images/row-action.svg +17 -0
  34. package/docs/images/row-activity.svg +24 -0
  35. package/docs/images/row-data.svg +24 -0
  36. package/docs/images/row-identity.svg +25 -0
  37. package/docs/images/row-market.svg +23 -0
  38. package/docs/images/row-media-tile.svg +26 -0
  39. package/docs/images/row-message.svg +20 -0
  40. package/docs/images/row-metric-card.svg +33 -0
  41. package/docs/images/row-rail.svg +18 -0
  42. package/docs/images/row-section-header.svg +19 -0
  43. package/docs/images/row-style-regions.svg +30 -0
  44. package/docs/images/row-system.svg +30 -0
  45. package/docs/images/row-wallet-group.svg +26 -0
  46. package/ios/NativeListAccessoryStack.swift +680 -0
  47. package/ios/NativeListActionRenderer.swift +108 -0
  48. package/ios/NativeListActivityRenderer.swift +179 -0
  49. package/ios/NativeListDataRowRenderer.swift +444 -0
  50. package/ios/NativeListIdentityRenderer.swift +685 -0
  51. package/ios/NativeListImageSlot.swift +140 -0
  52. package/ios/NativeListLeadingVisual.swift +372 -0
  53. package/ios/NativeListMarketRenderer.swift +698 -0
  54. package/ios/NativeListMediaTileRenderer.swift +184 -0
  55. package/ios/NativeListMessageRenderer.swift +168 -0
  56. package/ios/NativeListMetricCardRenderer.swift +420 -0
  57. package/ios/NativeListModels.swift +32 -0
  58. package/ios/NativeListRailRenderer.swift +152 -0
  59. package/ios/NativeListRendererCell.swift +311 -0
  60. package/ios/NativeListRendererRegistry.swift +50 -0
  61. package/ios/NativeListResolvedText.swift +122 -0
  62. package/ios/NativeListRowHost.swift +63 -0
  63. package/ios/NativeListSectionHeaderRenderer.swift +708 -0
  64. package/ios/NativeListSystemRenderer.swift +568 -0
  65. package/ios/NativeListTextLabel.swift +42 -0
  66. package/ios/NativeListTextStyles.swift +236 -0
  67. package/ios/NativeListWalletGroupRenderer.swift +255 -0
  68. package/ios/RNCNativeListView.swift +111 -180
  69. package/lib/module/models.js +54 -0
  70. package/lib/module/validation.js +324 -26
  71. package/lib/module/web/NativeListWebEngine.js +148 -936
  72. package/lib/module/web/templates/ActionRowRenderer.js +97 -0
  73. package/lib/module/web/templates/ActivityRowRenderer.js +95 -0
  74. package/lib/module/web/templates/DataRowRenderer.js +145 -0
  75. package/lib/module/web/templates/IdentityRowRenderer.js +289 -0
  76. package/lib/module/web/templates/MarketRowRenderer.js +310 -0
  77. package/lib/module/web/templates/MediaTileRowRenderer.js +132 -0
  78. package/lib/module/web/templates/MessageRowRenderer.js +165 -0
  79. package/lib/module/web/templates/MetricCardRowRenderer.js +172 -0
  80. package/lib/module/web/templates/RailRowRenderer.js +98 -0
  81. package/lib/module/web/templates/RowContainerStyle.js +100 -0
  82. package/lib/module/web/templates/RowElements.js +93 -0
  83. package/lib/module/web/templates/RowRendererRegistry.js +70 -0
  84. package/lib/module/web/templates/RowText.js +121 -0
  85. package/lib/module/web/templates/RowVisual.js +43 -0
  86. package/lib/module/web/templates/SectionHeaderRowRenderer.js +146 -0
  87. package/lib/module/web/templates/SystemRowRenderer.js +161 -0
  88. package/lib/module/web/templates/WalletGroupRowRenderer.js +96 -0
  89. package/lib/typescript/src/models.d.ts +166 -13
  90. package/lib/typescript/src/web/NativeListWebEngine.d.ts +18 -16
  91. package/lib/typescript/src/web/templates/ActionRowRenderer.d.ts +15 -0
  92. package/lib/typescript/src/web/templates/ActivityRowRenderer.d.ts +15 -0
  93. package/lib/typescript/src/web/templates/DataRowRenderer.d.ts +15 -0
  94. package/lib/typescript/src/web/templates/IdentityRowRenderer.d.ts +16 -0
  95. package/lib/typescript/src/web/templates/MarketRowRenderer.d.ts +30 -0
  96. package/lib/typescript/src/web/templates/MediaTileRowRenderer.d.ts +15 -0
  97. package/lib/typescript/src/web/templates/MessageRowRenderer.d.ts +44 -0
  98. package/lib/typescript/src/web/templates/MetricCardRowRenderer.d.ts +15 -0
  99. package/lib/typescript/src/web/templates/RailRowRenderer.d.ts +28 -0
  100. package/lib/typescript/src/web/templates/RowContainerStyle.d.ts +9 -0
  101. package/lib/typescript/src/web/templates/RowElements.d.ts +38 -0
  102. package/lib/typescript/src/web/templates/RowRendererRegistry.d.ts +83 -0
  103. package/lib/typescript/src/web/templates/RowText.d.ts +9 -0
  104. package/lib/typescript/src/web/templates/RowVisual.d.ts +20 -0
  105. package/lib/typescript/src/web/templates/SectionHeaderRowRenderer.d.ts +15 -0
  106. package/lib/typescript/src/web/templates/SystemRowRenderer.d.ts +16 -0
  107. package/lib/typescript/src/web/templates/WalletGroupRowRenderer.d.ts +20 -0
  108. package/package.json +3 -3
  109. package/src/models.ts +281 -19
  110. package/src/validation.ts +501 -46
  111. package/src/web/NativeListWebEngine.ts +361 -1932
  112. package/src/web/templates/ActionRowRenderer.ts +123 -0
  113. package/src/web/templates/ActivityRowRenderer.ts +159 -0
  114. package/src/web/templates/DataRowRenderer.ts +212 -0
  115. package/src/web/templates/IdentityRowRenderer.ts +527 -0
  116. package/src/web/templates/MarketRowRenderer.ts +467 -0
  117. package/src/web/templates/MediaTileRowRenderer.ts +182 -0
  118. package/src/web/templates/MessageRowRenderer.ts +306 -0
  119. package/src/web/templates/MetricCardRowRenderer.ts +337 -0
  120. package/src/web/templates/RailRowRenderer.ts +136 -0
  121. package/src/web/templates/RowContainerStyle.ts +131 -0
  122. package/src/web/templates/RowElements.ts +160 -0
  123. package/src/web/templates/RowRendererRegistry.ts +111 -0
  124. package/src/web/templates/RowText.ts +181 -0
  125. package/src/web/templates/RowVisual.ts +115 -0
  126. package/src/web/templates/SectionHeaderRowRenderer.ts +243 -0
  127. package/src/web/templates/SystemRowRenderer.ts +282 -0
  128. package/src/web/templates/WalletGroupRowRenderer.ts +131 -0
  129. package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +0 -4538
  130. package/ios/NativeListCell.swift +0 -4300
package/docs/DESIGN.md CHANGED
@@ -8,8 +8,9 @@ selection feedback, image request cancellation, and scrolling. JavaScript sends
8
8
  normalized plain data; rows never contain a `ReactNode`, function, or arbitrary
9
9
  style object.
10
10
 
11
- The component supports `identity`, `rail`, `activity`, `message`, `dataRow`,
12
- `mediaTile`, `metricCard`, `sectionHeader`, `action`, and `system` rows.
11
+ The component supports 12 row types: `identity`, `walletGroup`, `rail`, `activity`,
12
+ `message`, `dataRow`, `market`, `mediaTile`, `metricCard`, `sectionHeader`, `action`,
13
+ and `system`. [ROW_TEMPLATES.md](ROW_TEMPLATES.md) defines each with an illustration.
13
14
  `metricCard` supports a compact KPI template and the fixed `activity` and
14
15
  `performance` Portfolio & PnL composites. Their bounded metric arrays and
15
16
  progress value have dedicated native binders on both mobile platforms.
@@ -22,16 +23,30 @@ Business concepts such as asset, account, network, wallet, and settings map to
22
23
  the same presentation templates instead of introducing business-specific row
23
24
  types.
24
25
 
26
+ Each row also accepts a bounded `style`, whose keys name model fields rather than
27
+ views. [STYLE_SPEC.md](STYLE_SPEC.md) is the shared vocabulary: the design tokens,
28
+ the per-template style surface with each platform's current values, the template
29
+ isolation rules, and the cross-platform divergences that are registered rather
30
+ than fixed. Its container capability contract keeps header/footer placement,
31
+ sections, scrolling and the indexed bar independent of content templates. A local
32
+ style must preserve the template skeleton and allocated row frame. Developers
33
+ integrating the list own overflow prevention and choose fitting content, style
34
+ values and row heights; the component does not implement combination-fit checks
35
+ or automatic size correction.
36
+
25
37
  The wrapper validates and normalizes data before serializing it. A structural
26
38
  change is one snapshot payload. Frequently changing fields use one batch patch
27
39
  payload keyed by row key. Neither API performs one native call per row. The
28
40
  native side rejects malformed or duplicate-key payloads without partially
29
41
  applying them.
30
42
 
31
- Section headers, footers, separators, empty rows, and fixed-footer descriptors
32
- are represented as rows in one flattened native data source. `groupId` and
33
- `groupPosition` preserve continuous card geometry without nesting lists inside
34
- cells.
43
+ Section headers and scrolling footer/status rows use the flattened row stream.
44
+ `fixedFooter` is a separate descriptor outside that stream, and `emptyState` is
45
+ used when content is empty; both reuse row binders and currently accept only
46
+ `action` or `system` through the public API. A generic public list-header slot is
47
+ not yet exposed. `groupId` and `groupPosition` preserve continuous card geometry
48
+ without nesting lists inside cells. These placement/grouping rules belong to the
49
+ container even when the displayed content uses an existing row template.
35
50
 
36
51
  ## Native architecture
37
52
 
@@ -47,6 +62,113 @@ cells.
47
62
  selection reconciliation, and scrolling. Nitro callback props carry bounded
48
63
  JSON payloads which the wrapper decodes into strict public event types.
49
64
 
65
+ ### Incremental renderer migration
66
+
67
+ Template dispatch is a closed-set lookup, not a state machine. The target
68
+ ownership is container → template registry → renderer → shared primitives:
69
+
70
+ - The container owns data, placement, sections, header/footer, scrolling,
71
+ indexed bar, selection and reorder.
72
+ - A renderer owns its view structure, template defaults, semantic text/image
73
+ targets, measurement, binding and supported partial updates.
74
+ - A row host owns shared container appearance, events and binding invalidation.
75
+ Image/text primitives do not branch on business keys or template names.
76
+ - `row.key` identifies data. A structural reuse key identifies a compatible
77
+ renderer/view tree; style values and item keys must not enter that key.
78
+ - Resolved style must come from template defaults, theme, model and explicit
79
+ style, never from the previous bound view. Rendering and measurement consume
80
+ the same resolved inputs. Height precedence remains `style.container.height`
81
+ → `row.height` → template measurement/default. There is no automatic fit pass.
82
+ - Full binding must be correct without a preceding recycle callback. Image
83
+ descriptors that did not change should retain their bindings. Image requests have their own
84
+ slot epoch; source changes/recycle invalidate them. Row rebind/recycle still
85
+ invalidates action anchors through the host's binding epoch.
86
+
87
+ Message completes the first renderer pilot, with one platform per commit:
88
+
89
+ | Platform | Renderer and host ownership | Container ownership |
90
+ | --- | --- | --- |
91
+ | Web | `MessageRowRenderer`: persistent body/text/image nodes, resolved styles, update classification and intrinsic estimate/DOM measurement | Registered family pooling, placement, shared row appearance/events and layout correction |
92
+ | Android | `NativeListMessageRowView`: lightweight host; `NativeListMessageRenderer`: owned column, resolved text/spacing/images and update classification; native `onMeasure` consumes the bound views | Registered holder factories, DiffUtil, selection/reorder routing and viewport allocation |
93
+ | iOS | `NativeListMessageCell`: lightweight host; `NativeListMessageRenderer`: owned column, resolved text/spacing/images and width-aware measurement | Registered cell factories, data-source reload/reconfigure, selection/reorder routing and viewport allocation |
94
+
95
+ `NativeListResolvedText`, `NativeListLeadingVisual` and `NativeListImageSlot`
96
+ are internal primitives. The image slot owns request identity, bounded retries,
97
+ completion-once guards and stale-write invalidation. A text/style-only update
98
+ retains the existing image slot and, when its effective image request is
99
+ unchanged, the request. Geometry/content-fit changes may require the image
100
+ module to decode/request again. The existing bounded source-fallback cache is
101
+ shared by image primitives. Image slots are lazy and bounded to the template's
102
+ visible assets; Message never allocates the unused legacy Market/Wallet/table
103
+ subtrees. This is source-level ownership evidence, not a performance benchmark.
104
+
105
+ The renderer resolves defaults from current data/theme/style rather than
106
+ capturing a previous row's view state. Global cross-template slot maps and legacy hosts are removed after all twelve
107
+ renderer migrations. No public plugin API or Nitro schema is added. See
108
+ [SPEC.md](SPEC.md#conformance-and-six-stage-migration) for all six stages.
109
+
110
+ Structural reuse now has twelve dedicated families on all platforms; see the
111
+ current registry inventory in SPEC.md. WalletGroup composes keyed Identity
112
+ hosts. The old native classes and fallback registry entries are deleted in stage 6.
113
+ Neither `row.key`, content, style, height nor placement changes the family.
114
+ Web selects a compatible wrapper pool even when the row at a mounted index
115
+ changes family. iOS registers separate reuse identifiers and reloads retained
116
+ keys whose family changed; compatible updates still reconfigure, and newly
117
+ inserted keys are not marked for reload/reconfigure. Android uses the family as
118
+ `getItemViewType`; DiffUtil identifies data by `row.key`, allowing RecyclerView
119
+ to replace an incompatible holder. Market quote and selection payloads retain
120
+ their existing full/partial update decisions. Footer and nested-row hosts remain
121
+ outside these scrolling pools.
122
+
123
+ The native reuse families allocate different classes through the closed
124
+ registry. The list only uses `NativeListRowHost` lifecycle operations; Message
125
+ binds and measures its own resolved model. Market quote, selection echo,
126
+ WalletGroup compact/expanded reorder and fixed-footer behavior remain on their
127
+ existing paths. Stage 3 migrates simple templates one at a time.
128
+
129
+ The structural reuse follow-up on 2026-09-22 passed 150 package tests, typecheck,
130
+ lint (zero errors), the Android build/seven unit tests and a full iOS build.
131
+ Chrome, Android API 36 and iOS 26.5 ran 41 alternating Message/Identity rows,
132
+ swapped every template while retaining keys, emptied/repopulated the list and
133
+ scrolled end/back. Style reset, image add/remove and fixed-footer placement were
134
+ checked again. Web additionally verifies wrapper identity in regression tests;
135
+ runtime checks included ten Web and five iOS mixed-template switching cycles.
136
+ No browser page errors, Android crashes or iOS runtime exceptions were observed.
137
+
138
+ The Message text ownership follow-up on 2026-09-22 merged main `52849b86f`,
139
+ regenerated the ignored Image/List bindings and rebuilt both real native apps.
140
+ The 150 package tests, typecheck, lint (zero errors), Android build/seven unit
141
+ tests and full iOS build passed. Both native runtimes preserved the sampled
142
+ pre-extraction title/body/time geometry. They passed style and height clearing,
143
+ same-key template switching, content replacement, image add/remove, scrolling
144
+ end/back, mixed-family replacement and empty/repopulation with the fixed footer.
145
+ Additional cases set font size/weight, lineGap, horizontal/vertical alignment,
146
+ optical offset and line limits, then cleared them or rebound empty body/time
147
+ without recycling; no stale text remained and default geometry was restored.
148
+ Chrome desktop and 390px RTL passed the same applicable Message cases. Runtime
149
+ screenshots, accessibility trees and an iOS recording were retained outside the
150
+ repository. These checks cover this text-subtree increment, not completion of
151
+ stage 2 or full native acceptance of every template.
152
+
153
+ The Message host completion on 2026-09-22 adds the implicit-key inventory and
154
+ finishes stage 2. Package regression tests cover persistent text/image identity,
155
+ style clearing, removed-image cleanup and incompatible-family reuse. Both native
156
+ apps were rebuilt and installed on the dedicated external-drive iOS 26.5/API 36
157
+ simulators. Runtime cases cover style/height set-clear, intrinsic height with
158
+ missing fields, narrow grid columns, local/HTTP images, text-only image updates,
159
+ failed-image fallback/recovery, delayed-source removal, mixed-family swaps,
160
+ empty/repopulation and scrolling with the common fixed footer. Chrome also runs
161
+ these flows at desktop width and 390px RTL. The baseline explicit row heights
162
+ remain iOS 136 → 192 → 136pt and Android 122 → 192 → 122px on the narrow device;
163
+ Web remains 136 → 192 → 136px. Automatic measurement now follows actual resolved
164
+ metrics (the title/body/time intrinsic example is iOS 114pt and Web 74px),
165
+ including the allocated iOS grid column width. A grid text-only patch shrinks
166
+ the Message from 114 to 94pt; the same full-width text would occupy one line,
167
+ so patch invalidation is checked against the actual column. The Web primary
168
+ image style leaves network/corner decorations at their original dimensions. Runtime evidence is retained in
169
+ the task's external validation directory. The dated earlier paragraphs describe
170
+ intermediate commits, not the current stage-2 boundary.
171
+
50
172
  `linear`, `sectioned`, `grid`, and `table` are observable native container
51
173
  semantics. Linear is a full-width stream. Sectioned adds a visual break before
52
174
  section-header rows and can pin those rows. Grid allocates native spans and
@@ -55,6 +177,39 @@ weighted/aligned columns. Orientation, refresh, load-more, visibility events,
55
177
  empty state, fixed footer, and native reordering are capabilities, not row
56
178
  types.
57
179
 
180
+
181
+ ### Stage 3: Rail increment (2026-09-22)
182
+
183
+ Rail joins the closed registry on Web, Android and iOS. Its title, badge, status
184
+ and leading visual belong to the Rail renderer; it no longer allocates the
185
+ legacy composite tree. Message and Rail share `NativeListRendererCell` /
186
+ `NativeListRendererRowView` for appearance, binding classification, action-anchor
187
+ invalidation and recycling. Each declares its own asset fields. Web uses a
188
+ shared primitive contract and visual-style restoration without touching async
189
+ image visibility. No public API or generated bridge change is involved.
190
+
191
+ Unstyled typography, gaps, platform height defaults and horizontal width
192
+ allowances are preserved. Explicit row styles participate in horizontal sizing;
193
+ this repairs the previous fixed-metric estimator. The Android resolved-text
194
+ primitive also fixes an omitted `offsetY` being read as NaN when another text
195
+ style was provided; it now resolves to zero. Explicit line heights without an
196
+ offset visibly render in both Rail and Message.
197
+
198
+ The increment passes 153 package tests, TypeScript, lint (zero errors), Android
199
+ build/seven unit tests, and a full iOS build. Chrome, Android API 36 and iOS 26.5
200
+ ran style set/clear, local images and corner badges, text/image replacement,
201
+ badge/status removal, horizontal sizing, same-key Rail/Message/Identity swaps,
202
+ end/top scrolling, empty/repopulate and the shared fixed footer. iOS default and
203
+ styled text bounds match the captured legacy Rail baseline. Chrome additionally
204
+ ran a 390px RTL viewport. All three platforms verified row clicks, disabled clicks and drag reorder.
205
+ iOS reorder used XCTest long-press-and-drag with a 600ms hold; a synthesized
206
+ swipe alone did not exercise the long-press recognizer.
207
+
208
+ This is stage 3's first template, not completion of the seven-template stage.
209
+ mediaTile, action, system, activity, dataRow and metricCard remain in the legacy
210
+ family. Their variants, embedded actions and table semantics require their own
211
+ increments and runtime acceptance.
212
+
58
213
  ## Section index
59
214
 
60
215
  The mobile section index is an opt-in capability for vertical `sectioned`
@@ -109,7 +264,11 @@ pool.
109
264
  Snapshot application uses stable keys and platform diffing. A patch updates only
110
265
  changed models and rebinds/reconfigures only the corresponding visible cells.
111
266
  Selection-only changes use native payload updates and never require rebuilding
112
- all JavaScript rows.
267
+ all JavaScript rows. On iOS a cell also rebinds when its list-level inputs
268
+ change: layout, layout direction, or `theme`/`listStyle` under type-aware
269
+ structural equality (`true` differs from `1`, `1` equals `1.0`, an explicit
270
+ `null` inside an object differs from a missing key, and a missing
271
+ `theme`/`listStyle` equals `{}`).
113
272
 
114
273
  `visibleRangeChanged` is coalesced to at most once per animation frame and only
115
274
  emitted when the first/last visible keys change. `endReached` is edge-triggered
@@ -118,8 +277,11 @@ and `reorder`.
118
277
 
119
278
  ## Height, layout, and performance constraints
120
279
 
121
- Template sizes are fixed to a small set of variants. `message` body text is
122
- bounded to four lines and uses a deterministic template height. System spacers
280
+ `style.container.height` defines an explicit row height before legacy `row.height`.
281
+ Without either, templates use their existing preset or measured sizes. Styling
282
+ can change measured sizes; explicit heights remain exact, and clearing the style
283
+ restores the model/template height. `message` body text is bounded to three lines
284
+ and uses a deterministic measurement. System spacers
123
285
  are explicit and bounded. Images have explicit sizes, and grid columns are fixed
124
286
  per snapshot. This keeps layout work predictable for 1,000- and 5,000-row
125
287
  workloads without an unbounded self-sizing pass.
@@ -127,3 +289,350 @@ workloads without an unbounded self-sizing pass.
127
289
  The native list runs in the main UI runtime. OneKey's background and main
128
290
  JavaScript heaps are not shared, so this API accepts normalized serializable data
129
291
  and never depends on background-runtime object identity.
292
+
293
+ ### Template versus style
294
+
295
+ A row template (`type` and declared variant/presentation) defines structure,
296
+ semantic fields and interactions. `row.style` only changes allowlisted local
297
+ properties of those fields; it does not add children or rearrange the template.
298
+ The [shared style matrix](STYLE_SPEC.md#41-shared-configurable-properties) defines
299
+ the same configurable properties and semantics for Web, iOS and Android.
300
+ Header/footer placement, sections, scrolling and indexed bar belong to the
301
+ container. The integrating developer owns content fitting and row-height choices.
302
+
303
+ ### Stage 3: MediaTile acceptance (2026-09-23)
304
+
305
+ Dedicated Swift/Kotlin hosts and a persistent Web body now own MediaTile. Old
306
+ MediaTile binders, native-only view allocations and semantic style-map branches
307
+ were removed. Shared hosts provide vertical container alignment and epoch-owned
308
+ action dispatch without allocating other templates.
309
+
310
+ Validation used the external-drive runtime and its dedicated iOS 26 simulator
311
+ and Android API 36 emulator, plus headed Chrome. Native before/after captures
312
+ confirmed the grid geometry; styled height 260 restores to the iOS 233-point
313
+ grid default on clear. iOS retains its legacy title compression with a close
314
+ control at the default grid height. Style set/clear, empty/error images, close
315
+ action counter, end/top scroll, empty/repopulate and fixed footer were exercised
316
+ on all three platforms. Web also verifies same-key incompatible-family reuse,
317
+ retained image elements and source contentFit restoration in regression tests.
318
+ Evidence: external validation runtime `ios-media-*`, `android-media-*` and
319
+ `web-media-*`; build logs retained there. iOS and Android Debug builds and
320
+ Android unit tests pass; package typecheck and all 154 tests pass. Focused ESLint
321
+ has no errors (six pre-existing engine warnings). Remaining stage 3 templates:
322
+ Action, System, Activity, DataRow and MetricCard.
323
+
324
+ ### Stage 3: Action acceptance (2026-09-23)
325
+
326
+ Action uses a dedicated renderer on all three platforms. Native accessories
327
+ are a bounded primitive (two accessory slots plus checkbox/spinner), shared
328
+ through the host action epoch and a selection-only presentation hook. Legacy
329
+ Action dispatch, typography, style-slot and default-height branches are removed.
330
+ Fixed-footer creation was migrated to the same registry after runtime QA found
331
+ that the old direct legacy allocation no longer rendered Action. Footer family
332
+ replacement invalidates the previous host and keeps list-owned gestures/events.
333
+
334
+ On the task's iOS and Android simulators, default and value-pair/menu bounds
335
+ match the pre-migration captures. Style set/clear, same-key accessory variants,
336
+ menu and select-all checkbox actions, end/top, empty/repopulate, Media/Action
337
+ family replacement and fixed-footer rendering/clicks were exercised. The iOS
338
+ event counter reached 3 after menu, checkbox and footer; Web checked select-all
339
+ state and footer dispatch in headed Chrome. Evidence is `ios-action-*`,
340
+ `android-action-*` and `web-action-*` in the external validation runtime.
341
+ Build/type/unit checks pass; 155 package tests include style restoration, stable
342
+ Action title/icon nodes, selection echoes and incompatible-family reuse.
343
+ An Android input timeout also affected Launcher; its captured main stack was in the display event loop. After restarting the task emulator, menu, checkbox, scroll, repopulate and footer checks passed (action counter 3). This does not establish a library-level ANR cause.
344
+ Remaining stage 3 templates: System, Activity, DataRow and MetricCard.
345
+
346
+ ### Stage 3: System acceptance (2026-09-23)
347
+
348
+ System owns loading, retry, warning, noMatch, end and spacer rendering and
349
+ warning measurement on all three platforms. Its skeleton views and legacy
350
+ binders are removed from the universal host. Native source pixel rounding,
351
+ warning wrapping and existing per-platform default geometries remain intact.
352
+ The shared footer factory can reuse System and Action through the same registry.
353
+
354
+ The dedicated iOS and Android runtimes exercised default retry, style set/clear,
355
+ warning, skeleton, spinner, Market retry/noMatch/end, spacer, retry action,
356
+ empty/repopulate and end/top scrolling. Default retry captures match the old
357
+ geometry. Headed Chrome exercised the same variant cycle and retry action;
358
+ regression tests cover style clearing, retained warning nodes and variant reuse.
359
+ Evidence: external runtime `ios-system-*`, `android-system-*`, `web-system-*`.
360
+ iOS/Android Debug builds, Android unit tests and 156 package tests pass.
361
+ Remaining stage 3 templates: Activity, DataRow and MetricCard.
362
+
363
+ ### Stage 3: Activity acceptance (2026-09-23)
364
+
365
+ Activity owns its leading visuals, text, amounts and three bounded footer-action
366
+ buttons. Its legacy dispatch and exclusive native allocations are removed; Web
367
+ Identity no longer dispatches Activity internally. Secondary image descriptors
368
+ use the shared bounded image primitive. Action's Web visual now also applies
369
+ explicit image width/height and clears them back to its defaults.
370
+
371
+ The dedicated native runtimes and headed Chrome exercised default captures,
372
+ style set/clear, secondary images, row footer actions, end/top scrolling and
373
+ empty/repopulate with incompatible-family reuse. iOS preserves its pre-existing
374
+ text compression for content exceeding the default 60-point height. Native
375
+ builds and Android unit tests pass; 157 package tests cover image retention,
376
+ amount/style clearing, action replacement and Action image dimensions.
377
+ Evidence is retained as `ios-activity-*`, `android-activity-*`, `web-activity-*`
378
+ in the external runtime. Remaining stage 3 templates: DataRow and MetricCard.
379
+
380
+ ### Stage 3: DataRow acceptance (2026-09-23)
381
+
382
+ DataRow owns four bounded column views, leading visual, favorite, index and
383
+ checkbox controls. Its table striping retains the effective row index and
384
+ platform defaults. Dedicated native hosts and the persistent Web body replace
385
+ the legacy table allocations, dispatch and style paths.
386
+
387
+ The dedicated iOS/Android simulators and headed Chrome exercised selection
388
+ (callback counter increment), linear/table switching, style set/clear,
389
+ end/top scrolling and empty/repopulate with family replacement. Native default
390
+ captures preserve column geometry and existing truncation for the **table**
391
+ layout only; the 2026-09-24 audit found that native linear DataRows had been
392
+ moved to the table column structure (pill badges, separate secondary lines) instead of
393
+ the legacy single label with inline badges. The same audit restored the legacy
394
+ Web linear structure (secondary leading text inline before the primary text,
395
+ secondary text directly below) and the 48-unit Web table height for rows
396
+ without secondary text. The Web regression
397
+ changes three columns to two and back, clearing secondary labels and styles
398
+ while retaining the unchanged image. Native builds and Android unit tests
399
+ pass; all 158 package tests and typecheck pass. Evidence: external runtime
400
+ `ios-data-*`, `android-data-*`; MetricCard is the last stage 3 template.
401
+
402
+ ### Stage 3: MetricCard acceptance and closeout (2026-09-23)
403
+
404
+ MetricCard completes the seven-template migration. Standard, activity and
405
+ performance layouts use dedicated native hosts and a persistent Web body.
406
+ Legacy metric allocations, composite builders, slot remapping and height
407
+ dispatch are removed. Shared iOS asset classification also accepts arrays;
408
+ metric visuals use independent slot identities even when metric keys repeat.
409
+ Text changes retain unchanged image requests. Removing an image invalidates
410
+ its pending work before a different visual is bound.
411
+
412
+ The dedicated iOS/Android runtimes exercised all three variants, style
413
+ set/clear, explicit height restoration, end/top scroll, empty/repopulate,
414
+ incompatible-family replacement and fixed-footer display. Their default
415
+ geometry was compared with pre-migration captures; standard badge color and
416
+ Android subtitle sizing retain their original defaults. Final DataRow audit
417
+ also restored native table padding to 20 horizontal / 10 vertical.
418
+ Headed Chrome exercised the same transitions. The regression changes content
419
+ while styled, clears styles, cycles the three variants, and verifies stable
420
+ image elements and restored 40px standard / 16px composite visual dimensions.
421
+
422
+ Validation: iOS Debug build/link, Android Debug APK and seven Android unit tests
423
+ pass; package typecheck and all 159 tests in five suites pass. Focused lint has
424
+ zero errors. Evidence remains in the external-drive validation runtime as
425
+ `ios-metric-*`, `android-metric-*`, `web-metric-*`, `ios-data-*`,
426
+ `android-data-*` and native build logs. These are functional/visual checks,
427
+ not scrolling-performance measurements.
428
+
429
+ Stage 3 is complete: Rail, MediaTile, Action, System, Activity, DataRow and
430
+ MetricCard (7/7). With the earlier Message pilot, eight template types now use
431
+ the closed renderer registry at that checkpoint. Stage 4 completion is recorded
432
+ below; stage 5 owns WalletGroup, and stage 6 removes remaining legacy policies.
433
+ Historical “remaining stage 3” lists above describe each incremental checkpoint.
434
+
435
+ The final iOS ownership pass moves measurement and size-preset policy into each
436
+ registered renderer host. The registry now only forwards through its type table.
437
+ After rebuilding and reinstalling, default → styled → cleared heights were
438
+ verified as MediaTile 233 → 260 → 233, Action 60 → 100 → 60, System retry
439
+ 44 → 160 → 44, Activity 60 → 160 → 60, DataRow 60 → 120 → 60 and MetricCard
440
+ 132 → 240 → 132 points. The runtime stores `ios-renderer-measurement-final.json`.
441
+
442
+ ## Stage 4: Market, Identity and SectionHeader (2026-09-23)
443
+
444
+ The closed registries now dispatch eleven templates to dedicated native hosts
445
+ and Web bodies. Market owns its leading visual, bounded badge slots, quote
446
+ controls and quote-only binding. Identity owns title/subtitle/tertiary, selector
447
+ presentation and trailing controls. SectionHeader owns title/value/checkbox,
448
+ summary updates and the platform-specific header measurement. iOS container
449
+ measurement no longer contains branches for these three templates. Android's
450
+ sticky header uses the same registered SectionHeader host as ordinary rows.
451
+ WalletGroup's nested native Identity implementation remains scoped to stage 5;
452
+ the unused legacy Market/Header branches and global style maps are stage 6
453
+ cleanup, not an alternative dispatch path for these migrated rows.
454
+
455
+ Shared primitives accept only the geometry/typography needed by these renderers.
456
+ They retain unchanged image requests and reset style defaults before each full
457
+ bind. Web restores image geometry without restoring stale async opacity.
458
+ Accessory orientation/spacing starts from the renderer's defaults on every bind,
459
+ including style removal without recycling. Selector rounding, native idle corner
460
+ radii and explicit-height precedence remain intact. Header partial updates
461
+ retain resolved font metrics, so repeated updates do not rescale Android text.
462
+ The template-local iOS helpers no longer branch on other template types.
463
+
464
+ The baseline comparison included token/stock/perp Market, standard/account/
465
+ network/sidebar Identity, and standard/summary/gallery/history/network headers.
466
+ All were exercised with style set/clear and same-key family changes. Fresh legacy
467
+ iOS Identity/Header hosts matched the extracted text layouts; the original
468
+ mixed-family baseline also exposed stale text attributes inherited from Market.
469
+ Dedicated reuse families prevent that cross-template state from being reused.
470
+
471
+ Interaction acceptance covers quote patches with preserved style/image state,
472
+ badge actions, press-in and long-press anchors with a window point, checkbox
473
+ selection, summary title/value patches and subsequent value actions, end/top
474
+ scroll, empty/repopulation and the common fixed footer. The real section fixture
475
+ contains three indexed headers and ten Identity members per section. It exposed
476
+ two container problems: iOS selected pinned headers from the previous visible-cell
477
+ set during imperative scrolling; Android's pre-draw overlay could remain at zero
478
+ size beneath a React Native parent. The iOS container now uses original layout
479
+ positions for pinning/index highlighting, and Android lays out its measured
480
+ header overlay when needed. These policies remain outside row renderers.
481
+
482
+ The repository caller audit is recorded in MIGRATION_BASELINE.md. History
483
+ examples already specify their variant; both Token Manager example headers now
484
+ specify rowStyle height/typography/spacing. External-consumer compatibility
485
+ fallbacks remain until the stage 6 audit.
486
+
487
+ Validation artifacts are in the task's external-drive validation runtime:
488
+ `ios-stage4-*` and `android-stage4-*` screenshots/accessibility trees, with Web
489
+ captures under the stage 4 harness. These are native simulator and headed Chrome
490
+ functional/visual checks, not a consumer-app release acceptance or a scrolling
491
+ performance measurement. Web also exercised narrow/RTL sections, index clicks
492
+ and image-node retention after a quote patch.
493
+
494
+ Stage 4 completion: package typecheck and 161 tests across five suites pass,
495
+ with zero focused lint errors (two existing shadowing warnings). iOS Debug
496
+ build/link passes; Android Debug APK and seven unit tests across three suites
497
+ pass. Both native runtimes passed the interaction sequence after the container
498
+ fixes. Stage 5 WalletGroup/member dragging and stage 6 legacy/key cleanup remain.
499
+
500
+ ## Stage 5: WalletGroup (2026-09-23)
501
+
502
+ All twelve templates now enter through the closed registry. The new native
503
+ WalletGroup hosts and Web renderer reconcile member views by member key, bind
504
+ each through the migrated Identity renderer, and recycle removed members
505
+ immediately. Retention is bounded by the current member count; native holds one
506
+ additional compact parent. There is no spare-member high-water pool. Recycle
507
+ releases member images, action bindings and the compact parent. Group appearance
508
+ and padding stay on the composite; member text/image/container styles stay local.
509
+
510
+ iOS and Web expanded measurement moved from the list container into WalletGroup.
511
+ Android uses its registered composite host to resolve expanded, compact and
512
+ animated heights. Explicit group/member style heights retain precedence, and
513
+ member defaults/badges keep the previous measurement. The list continues to own
514
+ reorder activation, destination selection, scroll and final order events. Native
515
+ compact rendering uses a separate Identity parent and `+N` badge; Web uses the
516
+ existing leased preview. Only children whose draggable is not false count toward
517
+ `+N`; non-draggable members cannot initiate a drag. (Correction, 2026-09-24:
518
+ this stage also blocked disabled members; the audit restored the legacy rule on
519
+ all platforms, so only `draggable: false` excludes a member.) Drop restoration
520
+ uses the current configured height/appearance rather than default chrome.
521
+ The compact allocation is 68 on iOS and Web; Android keeps its legacy
522
+ `max(68, parent member height + 2 × group vertical inset)`.
523
+
524
+ Member event origins retain the nested Identity host and its binding epoch.
525
+ Removing/rebinding a member invalidates its previous action anchor. The iOS
526
+ member tap recognizer ignores UIControls so menu actions do not also emit a row
527
+ press. Native WalletSidebar now binds optional trailing controls, matching Web;
528
+ callers allocate sufficient height for these controls. Member selection visuals
529
+ continue to follow each Identity's selected field. This migration does not add
530
+ nested members to the list's existing top-level selection domain. Group-level
531
+ disabled state gates member actions on Web. (Correction, 2026-09-24: this stage
532
+ also gated disabled members on Web; the audit removed that non-legacy gate, so a
533
+ disabled member in an enabled group keeps its actions.) Android keeps its
534
+ legacy rule that a disabled group does not block member actions; the resulting
535
+ per-platform differences are listed in SPEC.md.
536
+
537
+ Acceptance ran on the task's independent external-drive iOS 26.5 simulator,
538
+ Android API 36 emulator and headed Chrome. Member press/menu and updated action
539
+ keys/anchors, selected/disabled state, member removal/swap, style set/reset,
540
+ same-key Identity/composite replacement, empty/refill, recycle and the shared
541
+ footer passed. Child-initiated drags reorder the whole group; excluded members
542
+ do not reorder. Captures show the 68-point preview and +1 badge (one draggable
543
+ child and one excluded add-wallet row). The fixture restores 274-point default
544
+ and 340-point styled heights. iOS no-op drag, Android touch cancel and Web Escape
545
+ cancel also restore the expanded appearance. A fast iOS QA scroll initially
546
+ moved the group outside the viewport; returning to Top verified the full settled
547
+ 340-point group, without a product-code change.
548
+
549
+ Validation: TypeScript and 163 tests in five suites; focused lint zero errors
550
+ with two pre-existing warnings; iOS Debug build/link; Android Debug APK plus
551
+ seven tests in three suites. Web tests additionally assert keyed member/image
552
+ retention, removed-member disposal, style clearing and disabled menu dispatch.
553
+ The validation runtime's `stage5` directory contains fixture, scripts, screenshots,
554
+ accessibility trees and iOS drag recordings. This is functional/visual simulator
555
+ acceptance, not a performance benchmark. Stage 6 completion and retained compatibility boundaries are recorded below.
556
+
557
+ ### Stage 6: remove migration scaffolding (2026-09-23)
558
+
559
+ Deleted `NativeListCell.swift` and `NativeListRowView.kt`, their global style-slot
560
+ maps, and the obsolete slot-map tests. Still-used fonts, icons, controls and
561
+ layout helpers keep their existing behavior in shared primitive/host files.
562
+ The registry has only the twelve supported families; native parsing rejects
563
+ unknown types. Footer initialization starts with the lightweight Action host.
564
+
565
+ Web removes the legacy pool and warning-specific container measurement cache;
566
+ the System renderer reports a warning's rendered height through the registry.
567
+ Selector tabular typography belongs to Identity, Header and Action. WalletGroup
568
+ provides its compact parent preview through the internal renderer registration;
569
+ the container no longer selects a parent DOM node from the composite internals.
570
+ Common event hit-testing, selection primitives and cloned-image leases remain
571
+ container responsibilities.
572
+
573
+ The source audit at the app-monorepo refs in MIGRATION_BASELINE.md found no
574
+ callers dependent on history/token key styling or separator suppression.
575
+ Removed those three inference rules. Explicit variant/style/separator fields
576
+ now determine the behavior. The audited Market pagination-tail optimization and
577
+ Web DataRow `asset` badge placement remain documented compatibility boundaries;
578
+ this cleanup does not alter pagination or introduce a new column-badge API.
579
+
580
+ Acceptance ran on the same task-owned external-drive iOS 26.5 and Android API 36
581
+ simulators and headed Chrome. The same row key cycles through all twelve families;
582
+ each survives an explicit styled height/background and clearing back to its
583
+ original geometry. Ordinary/history-named headers match; explicit history uses
584
+ 16 units, and explicit token-header styles use 30. Action → System retry → Action
585
+ footers render and emit their current action. Scrolling/reuse and member/menu
586
+ routing pass. WalletGroup drag previews remain 68 high with +1 for one draggable
587
+ child, excluded add-wallet members do not reorder, and dropping restores default
588
+ 274 or explicit 340 heights. Web also checks a 390px viewport and changing family
589
+ while scrolled. Screenshots show controls/footer within the viewport; list content
590
+ scrolls normally. No stale prior-template content or cleared-style leakage was
591
+ observed in these scenarios.
592
+
593
+ Validation: TypeScript, 164 tests in five suites, focused ESLint (zero errors,
594
+ two pre-existing shadowing warnings), iOS Debug build/link and Android Debug APK
595
+ plus five unit tests in two suites. The two deleted tests only exercised the
596
+ removed global slot mapper. The source audit finds no legacy host/registry/global
597
+ slot references in production code and no header/separator business-key styling.
598
+
599
+ Fixtures, scripts, build/test logs, screenshots, accessibility trees, event/geometry
600
+ results and iOS drag recordings are saved under the validation runtime's `stage6`
601
+ directory. This completes all six architecture stages, with the two explicit
602
+ compatibility boundaries above. It is functional/visual simulator acceptance,
603
+ not a scrolling-performance benchmark or a full app-monorepo release signoff.
604
+
605
+ ### Legacy-default audit (2026-09-24)
606
+
607
+ A review of the completed migration compared every renderer with the
608
+ pre-migration engines and restored legacy unstyled rendering inside the
609
+ renderer structure; implicit key-based styling stays removed. Notable outcomes:
610
+
611
+ - Native DataRow linear rows use the legacy single-label column again (see
612
+ ROW_TEMPLATES.md); Web restored its own legacy linear structure.
613
+ - Pressed feedback, WalletGroup appearance, separator insets, disabled-row
614
+ action rules and the System retry label are documented as retained platform
615
+ differences in SPEC.md.
616
+ - Android's pinned-header pre-draw listener is registered only while the list
617
+ is attached, on the captured view-tree observer, and removed on detach, so
618
+ detached lists no longer leak it.
619
+ - iOS caches measured Message text heights per resolved text/width input in a
620
+ process-wide `NSCache` bounded to 4,096 entries (system eviction), which
621
+ avoids repeating text layout for unchanged rows during scrolling. This is a
622
+ cost reduction, not a measured scrolling benchmark.
623
+ - Web: MetricCard keeps loaded images visible across rebinding, WalletGroup
624
+ `verticalPadding` replaces the legacy border inset in both measurement and
625
+ rendering, container writes are undone before each renderer rebind (no leaked
626
+ alignment, no reverted renderer values), every System retry is message-only
627
+ again, and selector geometry follows either explicit height field.
628
+ - Review round 3: Web restores the legacy Market subscript ratio under an
629
+ explicit `fontSize` (iOS/Android too), System warning DOM-height layout, a
630
+ text-style rebuild for System/MetricCard so cleared `lines`/`offsetY` leave
631
+ no wrapper or rewritten text, and `margin-bottom` restoration on reused
632
+ visuals. iOS/Android selector geometry now follows either explicit height
633
+ field (Android source scale still keys on `row.height`), Android rebinds the
634
+ fixed footer on a `listStyle`-only change, and iOS WalletGroup ignores the
635
+ row-level `backgroundColor` in its full-width fill too.
636
+
637
+ Web validation is jsdom/unit level (package typecheck and tests); native
638
+ validation for this round is reported with the native changes.