@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
@@ -1,5 +1,12 @@
1
1
  "use strict";
2
2
 
3
+ import { applyRowContainerStyle, resetRowContainerStyle } from "./templates/RowContainerStyle.js";
4
+ import { hasExplicitRowHeight, rowSizeModifier } from "./templates/RowElements.js";
5
+ import { applyTextStyleToSlot, applyValueSegments } from "./templates/RowText.js";
6
+ import { setMarketQuoteContent } from "./templates/MarketRowRenderer.js";
7
+ export { resolveWebMarketLayoutStyle } from "./templates/MarketRowRenderer.js";
8
+ import { measureRailWidth } from "./templates/RailRowRenderer.js";
9
+ import { rowRenderer, rowRendererKey, recycleRowBody } from "./templates/RowRendererRegistry.js";
3
10
  import { checkboxStateForKeys, checkboxStateForSection, isRowPressEnabled, isSelectableRow, reduceSelection, selectionStateFromSnapshot } from "../selection.js";
4
11
  import { calculateAlignedScrollOffset, resolveLocationIndex, scrollFailure, validateOffset } from "../scrolling.js";
5
12
  import { applyRowPatches, validateSnapshot } from "../validation.js";
@@ -99,25 +106,9 @@ export function webSectionIndexActiveKey(rows, layout, offset, viewportLength) {
99
106
  });
100
107
  return activeKey;
101
108
  }
109
+
102
110
  /** Resolves every reusable Market geometry field, including legacy defaults. */
103
- export function resolveWebMarketLayoutStyle(row) {
104
- const style = row.style;
105
- const imageWidth = style?.image?.width ?? (row.variant === 'stock' ? 40 : 32);
106
- const imageHeight = style?.image?.height ?? (row.variant === 'stock' ? 40 : 32);
107
- return {
108
- horizontalPadding: style?.horizontalPadding ?? (row.variant === 'perp' ? 16 : 20),
109
- verticalPadding: style?.verticalPadding ?? 12,
110
- leadingGap: style?.leadingGap ?? (row.variant === 'perp' ? 8 : 14),
111
- titleBadgeGap: style?.titleBadgeGap ?? 4,
112
- trailingGap: style?.trailingGap ?? 8,
113
- imageWidth,
114
- imageHeight,
115
- imageCornerRadius: style?.image?.cornerRadius ?? (style?.image?.shape === 'square' ? 0 : style?.image?.shape === 'rounded' ? 8 : Math.min(imageWidth, imageHeight) / 2),
116
- changeWidth: style?.changeWidth ?? 80,
117
- changeHeight: style?.changeHeight ?? 32,
118
- changeCornerRadius: style?.changeCornerRadius ?? 8
119
- };
120
- }
111
+
121
112
  export function webActionAnchorPayload(token, rect, source, generation, layoutDirection, slot) {
122
113
  return {
123
114
  token,
@@ -207,91 +198,19 @@ function isStructuralRow(row) {
207
198
  }
208
199
  function sizeModifier(row) {
209
200
  if (row.type === 'sectionHeader' && (row.variant === 'summary' || row.variant === 'gallery')) return 0;
210
- if (row.size === 'small') return -8;
211
- if (row.size === 'large') return 12;
212
- return 0;
213
- }
214
- function approximateMessageHeight(row, availableWidth) {
215
- if (row.type !== 'message') return 0;
216
- const leadingWidth = row.leading ? 52 : 0;
217
- const thumbnailWidth = row.thumbnail ? 88 : 0;
218
- const charactersPerLine = Math.max(18, Math.floor((availableWidth - leadingWidth - thumbnailWidth - 40) / 7));
219
- const titleLines = Math.min(2, Math.max(1, Math.ceil(row.title.length / charactersPerLine)));
220
- const bodyLines = Math.min(row.bodyLines ?? 3, Math.max(1, Math.ceil(row.body.length / charactersPerLine)));
221
- return 32 + titleLines * 20 + bodyLines * 20 + 22;
201
+ return rowSizeModifier(row.size);
222
202
  }
223
203
  export function estimateWebRowHeight(row, snapshot, availableWidth) {
224
- // OneKey patch: explicit selector height takes precedence over presets.
225
- // if (row.type === 'system' && row.variant === 'spacer') return row.height;
226
- if (row.height !== undefined) return row.height;
227
- if (row.type === 'system' && row.variant === 'warning') {
228
- const width = Math.max(1, availableWidth - 24);
229
- const lines = text => Math.max(1, Math.ceil(Array.from(text).reduce((length, char) => length + (char.charCodeAt(0) > 255 ? 14 : 7), 0) / width));
230
- return 32 + 20 * (lines(row.title) + lines(row.message));
231
- }
232
- if (row.type === 'walletGroup')
233
- // OneKey patch: wallet badges participate in the outer group height.
234
- // return (row.children.length + 1) * 68 + row.children.length * 12;
235
- return [row.parent, ...row.children].reduce((height, member) => height + estimateWebRowHeight(member, snapshot, availableWidth), 0) + row.children.length * 12 + (row.parent.height !== undefined ? 2 : 0);
236
- // OneKey patch: reserve the source badge line below wallet names.
237
- // if (row.type === 'identity' && row.presentation === 'walletSidebar')
238
- // return 68;
239
- if (row.type === 'identity' && row.presentation === 'walletSidebar') return 68 + (row.badges?.length ? 24 : 0);
240
- if (row.type === 'identity' && row.presentation === 'networkSelector') return 47;
241
- if (row.type === 'identity' && row.presentation === 'accountSelector') return 58;
242
- let base;
243
- switch (row.type) {
244
- case 'rail':
245
- base = 40;
246
- break;
247
- case 'activity':
248
- base = row.footerActions?.length ? 100 : 60;
249
- break;
250
- case 'message':
251
- base = approximateMessageHeight(row, availableWidth);
252
- break;
253
- case 'mediaTile':
254
- base = 244;
255
- break;
256
- case 'metricCard':
257
- base = row.variant === 'activity' ? 161 : row.variant === 'performance' ? 178 : 132;
258
- break;
259
- case 'sectionHeader':
260
- {
261
- const isHistory = row.variant === 'history' || row.key.startsWith('history-') || row.sectionKey.startsWith('history-');
262
- base = snapshot.layout.kind === 'table' ? 28 : row.presentation === 'networkSelector' ? 47 : isHistory ? 16 : row.variant === 'summary' ? 68 : row.variant === 'gallery' ? 32 : row.checkbox ? 56 : snapshot.layout.kind === 'linear' ? 30 : 36;
263
- break;
264
- }
265
- case 'system':
266
- base = row.variant === 'loading' && row.loadingStyle === 'skeleton' ? 56 : row.variant === 'loading' && row.loadingStyle === 'spinner' ? 52 : 'presentation' in row && row.presentation === 'market' ? row.variant === 'loading' ? 68 : 44 : row.variant === 'noMatch' || row.variant === 'end' ? 36 : row.variant === 'retry' ? 44 : 56;
267
- break;
268
- case 'action':
269
- base = row.presentation === 'accountSelector' ? 48 : row.icon ? 60 : 44;
270
- break;
271
- case 'dataRow':
272
- base = row.columns.some(column => column.secondaryText) ? 60 : 56;
273
- break;
274
- case 'market':
275
- {
276
- const marketStyle = resolveWebMarketLayoutStyle(row);
277
- base = Math.max(row.variant === 'stock' ? 72 : 68, marketStyle.imageHeight + marketStyle.verticalPadding * 2);
278
- break;
279
- }
280
- case 'identity':
281
- base = row.tertiary ? 72 : row.subtitle ? 60 : 56;
282
- break;
283
- default:
284
- base = 56;
285
- }
286
- const tableAdjustment = snapshot.layout.kind === 'table' && row.type === 'dataRow' && !row.columns.some(column => column.secondaryText) ? -8 : 0;
287
- return Math.max(0, base + sizeModifier(row) + tableAdjustment);
204
+ // Explicit container/model height takes precedence over template presets.
205
+ const explicitHeight = row.style?.container?.height ?? row.height;
206
+ if (explicitHeight !== undefined) return explicitHeight;
207
+ const registered = rowRenderer(row);
208
+ return Math.max(0, registered.measure(availableWidth, snapshot.layout.kind) + (registered.appliesSizePreset ? sizeModifier(row) : 0));
288
209
  }
289
210
  function estimateHorizontalWidth(row) {
290
211
  if (row.type === 'mediaTile') return 200;
291
212
  if (row.type !== 'rail') return 280;
292
- const badgeLength = row.badge?.text.length ?? 0;
293
- const statusLength = row.status && row.status !== 'none' ? row.status.length : 0;
294
- return Math.min(288, Math.max(72, 50 + (row.title.length + badgeLength + statusLength) * 7));
213
+ return measureRailWidth(row);
295
214
  }
296
215
  function sectionIndexEnabled(snapshot) {
297
216
  return Boolean(snapshot.capabilities?.sectionIndex?.enabled && snapshot.layout.kind === 'sectioned' && snapshot.layout.orientation !== 'horizontal' && snapshot.rows.some(row => row.type === 'sectionHeader' && row.indexTitle));
@@ -319,7 +238,7 @@ export function computeWebListLayout(snapshot, viewportWidth, viewportHeight, co
319
238
  x,
320
239
  y: padding.top,
321
240
  width: itemWidth,
322
- height: row.type === 'rail' ? Math.min(rowHeight, availableHeight) : availableHeight
241
+ height: (row.type === 'walletGroup' && row.key === compactRowKey ? WALLET_REORDER_COMPACT_HEIGHT : undefined) ?? row.style?.container?.height ?? row.height ?? (row.type === 'rail' ? Math.min(rowHeight, availableHeight) : availableHeight)
323
242
  });
324
243
  x += itemWidth + spacing;
325
244
  });
@@ -382,7 +301,7 @@ export function computeWebListLayout(snapshot, viewportWidth, viewportHeight, co
382
301
  bandStart = y;
383
302
  return;
384
303
  }
385
- const itemHeight = row.type === 'mediaTile' ? itemWidth + 48 : estimateWebRowHeight(row, snapshot, itemWidth);
304
+ const itemHeight = row.style?.container?.height ?? row.height ?? (row.type === 'mediaTile' ? itemWidth + 48 : estimateWebRowHeight(row, snapshot, itemWidth));
386
305
  items.push({
387
306
  index,
388
307
  key: row.key,
@@ -411,6 +330,8 @@ export function webWalletGroupReorderBadge(row) {
411
330
  export function canStartWebWalletGroupReorder(row, memberKey) {
412
331
  if (row.type !== 'walletGroup' || !memberKey) return true;
413
332
  const member = [row.parent, ...row.children].find(candidate => candidate.key === memberKey);
333
+ // Legacy: only `draggable: false` excludes a member; disabled members may
334
+ // still start the atomic group drag.
414
335
  return member?.draggable !== false;
415
336
  }
416
337
  function itemStart(item, horizontal) {
@@ -495,15 +416,15 @@ export function webMarketImageBindDeltaForPatches(patches) {
495
416
  // OneKey patch: selector names must shrink before the sidebar clips their contents.
496
417
  export const WEB_LIST_CSS = `
497
418
  [data-native-list-selector="walletSidebar"] .ok-native-list-title{max-width:100%;min-width:0}
498
- .ok-native-list-root{--nl-bg:#f7f7f7;--nl-row:#fff;--nl-selected:#eaf2ff;--nl-pressed:#e8e8e8;--nl-subdued:#f9f9f9;--nl-strong:#0000000f;--nl-primary:#111;--nl-secondary:#6b7280;--nl-disabled:#8d8d8d;--nl-icon:#111;--nl-icon-subdued:#8d8d8d;--nl-separator:#e5e7eb;--nl-accent:#2f6bff;--nl-positive:#15803d;--nl-negative:#dc2626;--nl-critical:#feecec;--nl-inverse:#202020;--nl-inverse-text:#fcfcfc;--nl-info:#0d74ce;position:absolute;inset:0;display:flex;min-width:0;min-height:0;overflow:hidden;background:var(--nl-bg);color:var(--nl-primary);font-family:Roobert,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-synthesis:none}
419
+ .ok-native-list-root{--nl-bg:#f7f7f7;--nl-row:#fff;--nl-selected:#eaf2ff;--nl-pressed:#e8e8e8;--nl-subdued:#f9f9f9;--nl-strong:#0000000f;--nl-primary:#111;--nl-secondary:#6b7280;--nl-disabled:#8d8d8d;--nl-icon:#111;--nl-icon-subdued:#8d8d8d;--nl-separator:#e5e7eb;--nl-accent:#2f6bff;--nl-positive:#15803d;--nl-negative:#dc2626;--nl-critical:#feecec;--nl-inverse:#202020;--nl-inverse-text:#fcfcfc;--nl-info:#0d74ce;position:absolute;inset:0;display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden;background:var(--nl-bg);color:var(--nl-primary);font-family:Roobert,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-synthesis:none}
499
420
  .ok-native-list-viewport-frame{position:relative;flex:1;min-width:0;min-height:0;overflow:hidden}
500
421
  .ok-native-list-viewport{position:absolute;inset:0;overflow:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;scrollbar-gutter:stable}
501
422
  .ok-native-list-content{position:relative;min-width:100%;min-height:100%}
502
- .ok-native-list-item{position:absolute;box-sizing:border-box;contain:layout paint style;outline:none}
423
+ .ok-native-list-item{position:absolute;left:0;top:0;box-sizing:border-box;contain:layout paint style;outline:none}
503
424
  .ok-native-list-row{width:100%;height:100%;box-sizing:border-box;display:flex;align-items:center;gap:12px;overflow:hidden;background:var(--nl-row);color:var(--nl-primary);cursor:default;user-select:none;-webkit-user-select:none}
504
425
  .ok-native-list-item[data-table-alternate="true"]>.ok-native-list-row{background:var(--nl-bg)}
505
426
  .ok-native-list-item[data-native-list-selected="true"]>.ok-native-list-row{background:var(--nl-selected)}
506
- .ok-native-list-item[data-native-list-disabled="true"]>.ok-native-list-row{opacity:.5;cursor:default}
427
+ .ok-native-list-item[data-native-list-disabled="true"]>.ok-native-list-row{cursor:default}
507
428
  .ok-native-list-item[data-native-list-row-press-enabled="true"]>.ok-native-list-row{cursor:pointer}
508
429
  .ok-native-list-item[data-native-list-row-press-enabled="true"]:hover>.ok-native-list-row{background:var(--nl-pressed)}
509
430
  .ok-native-list-item[data-native-list-row-press-enabled="true"]:active>.ok-native-list-row{background:var(--nl-pressed)}
@@ -528,10 +449,13 @@ export const WEB_LIST_CSS = `
528
449
  .ok-native-list-reorder-preview>.ok-native-list-row{background:var(--nl-pressed);cursor:grabbing}
529
450
  .ok-native-list-reorder-preview[data-native-list-selected="true"]>.ok-native-list-row{background:var(--nl-selected)}
530
451
  .ok-native-list-reorder-count{position:absolute;right:4px;bottom:4px;display:flex;align-items:center;justify-content:center;box-sizing:border-box;min-width:24px;height:24px;padding:0 6px;border:1px solid var(--nl-row);border-radius:12px;background:var(--nl-inverse);color:var(--nl-inverse-text);font-size:12px;line-height:22px;font-weight:600}
531
- .ok-native-list-item[data-separator="true"]>.ok-native-list-row{border-bottom:1px solid var(--nl-separator)}
532
- .ok-native-list-item[data-group-position="first"]>.ok-native-list-row{border-radius:12px 12px 0 0}
533
- .ok-native-list-item[data-group-position="last"]>.ok-native-list-row{border-radius:0 0 12px 12px}
534
- .ok-native-list-item[data-group-position="single"]>.ok-native-list-row{border-radius:12px}
452
+ .ok-native-list-item[data-separator="true"]>.ok-native-list-row{border-bottom:1px solid var(--nl-separator-color,var(--nl-separator))}
453
+ /* An inset separator keeps the transparent border so the row's height is unchanged, and paints the visible line with a logical-inset overlay so it follows RTL. */
454
+ .ok-native-list-root[data-separator-inset="true"] .ok-native-list-item[data-separator="true"]>.ok-native-list-row{position:relative;border-bottom-color:transparent}
455
+ .ok-native-list-root[data-separator-inset="true"] .ok-native-list-item[data-separator="true"]>.ok-native-list-row::after{content:"";position:absolute;inset-inline-start:var(--nl-separator-inset,0);inset-inline-end:0;bottom:-1px;height:1px;background:var(--nl-separator-color,var(--nl-separator))}
456
+ .ok-native-list-item[data-group-position="first"]>.ok-native-list-row{border-radius:var(--nl-group-radius,12px) var(--nl-group-radius,12px) 0 0}
457
+ .ok-native-list-item[data-group-position="last"]>.ok-native-list-row{border-radius:0 0 var(--nl-group-radius,12px) var(--nl-group-radius,12px)}
458
+ .ok-native-list-item[data-group-position="single"]>.ok-native-list-row{border-radius:var(--nl-group-radius,12px)}
535
459
  .ok-native-list-standard{padding:8px 12px}.ok-native-list-network-row{padding:0 12px}.ok-native-list-wallet-row{padding:4px 8px;flex-direction:column;justify-content:center;gap:4px}.ok-native-list-account-row{padding:4px 12px;gap:8px}
536
460
  .ok-native-list-account-row .ok-native-list-visual,.ok-native-list-account-action-row .ok-native-list-visual{width:32px;height:32px;flex-basis:32px}.ok-native-list-account-row .ok-native-list-visual>img,.ok-native-list-account-row .ok-native-list-visual-main,.ok-native-list-account-action-row .ok-native-list-visual>img,.ok-native-list-account-action-row .ok-native-list-visual-main{width:32px;height:32px}.ok-native-list-account-row .ok-native-list-visual>.ok-native-list-visual-corner{width:20px;height:20px;padding:2px}.ok-native-list-account-action-row .ok-native-list-visual{border-radius:8px!important}.ok-native-list-wallet-row .ok-native-list-title{color:var(--nl-secondary);font-size:12px;line-height:16px;font-weight:400}.ok-native-list-item[data-native-list-selected="true"]>.ok-native-list-wallet-row .ok-native-list-title{color:var(--nl-primary)}.ok-native-list-account-row .ok-native-list-title{font-size:16px;line-height:20px;font-weight:400}.ok-native-list-account-row .ok-native-list-secondary{font-size:14px;line-height:20px;font-weight:400}
537
461
  .ok-native-list-flex{display:flex;flex:1;min-width:0;flex-direction:column;justify-content:center}.ok-native-list-title{font-size:15px;line-height:20px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ok-native-list-secondary{font-size:13px;line-height:18px;color:var(--nl-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ok-native-list-tertiary{color:var(--nl-secondary)}.ok-native-list-info{color:var(--nl-info)}
@@ -551,18 +475,13 @@ export const WEB_LIST_CSS = `
551
475
  .ok-native-list-data{padding:6px 12px}.ok-native-list-index{flex:0 0 28px;color:var(--nl-secondary);font-size:13px}.ok-native-list-favorite{flex:0 0 24px;color:var(--nl-icon-subdued);font-size:22px}.ok-native-list-favorite[data-active="true"]{color:var(--nl-accent)}.ok-native-list-data-cell{display:flex;flex-direction:column;min-width:0}.ok-native-list-data-cell[data-align="center"]{align-items:center}.ok-native-list-data-cell[data-align="end"]{align-items:flex-end}.ok-native-list-data-primary{display:flex;align-items:center;gap:5px;max-width:100%;font-size:16px;font-weight:500;white-space:nowrap}.ok-native-list-unread{width:7px;height:7px;flex:0 0 7px;border-radius:50%;background:var(--nl-accent)}.ok-native-list-thumbnail{width:64px;height:64px;border-radius:10px;object-fit:cover}
552
476
  .ok-native-list-market{padding:12px 20px;gap:14px}.ok-native-list-market>.ok-native-list-visual{width:32px;height:32px;flex-basis:32px}.ok-native-list-market[data-variant="stock"]>.ok-native-list-visual{width:40px;height:40px;flex-basis:40px}.ok-native-list-market>.ok-native-list-visual>.ok-native-list-visual-main{width:100%;height:100%}.ok-native-list-market-main{display:flex;flex:1;min-width:0;flex-direction:column;justify-content:center}.ok-native-list-market-title-line{display:flex;align-items:center;min-width:0;gap:4px}.ok-native-list-market-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--nl-primary);font-size:16px;line-height:24px;font-weight:500}.ok-native-list-market-subtitle{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--nl-secondary);font-size:14px;line-height:20px;font-weight:400}.ok-native-list-market-badge{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;box-sizing:border-box;max-width:72px;height:18px;padding:0 5px;border:0;border-radius:4px;background:var(--nl-strong);color:var(--nl-secondary);font:500 11px/18px inherit;overflow:hidden;white-space:nowrap}.ok-native-list-market-badge img{width:14px;height:14px;object-fit:contain}.ok-native-list-market-trailing{display:flex;flex:0 0 auto;align-items:center;gap:8px}.ok-native-list-market-price{max-width:112px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:right;color:var(--nl-primary);font-size:16px;line-height:24px;font-weight:500;font-variant-numeric:tabular-nums}.ok-native-list-market-change{display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:80px;height:32px;border-radius:8px;color:#fff;background:#8d8d8d;font-size:14px;line-height:20px;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap}.ok-native-list-market-change[data-tone="positive"]{background:var(--nl-positive)}.ok-native-list-market-change[data-tone="negative"]{background:var(--nl-negative)}
553
477
  .ok-native-list-market-badge>svg,.ok-native-list-market-badge>.ok-native-list-market-badge-icon>svg{display:block;width:16px;height:16px;flex:0 0 16px}.ok-native-list-system[data-native-list-presentation="market"]{box-sizing:border-box;padding:12px 20px}.ok-native-list-system[data-native-list-presentation="market"]>.ok-native-list-spinner{width:32px;height:32px}
554
- .ok-native-list-footer{flex:0 0 auto;min-height:0}.ok-native-list-sticky{position:absolute;z-index:4;left:0;right:0;top:0;pointer-events:auto;box-shadow:0 1px 0 var(--nl-separator)}.ok-native-list-index-rail{position:absolute;z-index:6;top:0;right:0;bottom:0;width:${SECTION_INDEX_RAIL_WIDTH}px;touch-action:none;cursor:pointer}.ok-native-list-index-rail[hidden]{display:none}.ok-native-list-index-button{appearance:none;position:absolute;left:6px;display:flex;width:20px;height:16px;align-items:center;justify-content:center;padding:0;transform:translateY(-50%);border:0;border-radius:8px;background:transparent;color:var(--nl-secondary);font:600 10px/1 inherit;cursor:pointer}.ok-native-list-index-button[data-active="true"]{background:var(--nl-accent);color:var(--nl-inverse-text)}.ok-native-list-index-button:focus-visible{outline:2px solid var(--nl-accent);outline-offset:1px}.ok-native-list-index-preview{position:absolute;z-index:8;right:40px;top:50%;display:flex;width:48px;height:48px;align-items:center;justify-content:center;transform:translateY(-50%) scale(.92);border-radius:14px;background:var(--nl-inverse);color:var(--nl-inverse-text);font-size:22px;font-weight:600;opacity:0;pointer-events:none;transition:opacity .15s ease,transform .15s ease}.ok-native-list-index-preview[data-visible="true"]{opacity:1;transform:translateY(-50%) scale(1)}
555
- .ok-native-list-refresh{position:absolute;z-index:7;left:50%;top:8px;display:flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;background:var(--nl-inverse);color:var(--nl-inverse-text);font-size:12px;opacity:0;transform:translate(-50%,-16px);transition:opacity .15s ease,transform .15s ease;pointer-events:none}.ok-native-list-refresh[data-visible="true"]{opacity:1;transform:translate(-50%,0)}
556
- .ok-native-list-warning{height:auto;display:flex;flex-direction:column;align-items:stretch;gap:4px;padding:14px 12px;border-top:1px solid;border-bottom:1px solid;box-sizing:border-box;cursor:default}.ok-native-list-warning-title,.ok-native-list-warning-message{font-size:14px;line-height:20px;white-space:normal;overflow-wrap:anywhere}.ok-native-list-warning-title{font-weight:500;color:var(--nl-primary)}.ok-native-list-warning-message{font-weight:400;color:var(--nl-secondary)}
557
- .ok-native-list-subtitle-segments{display:flex;align-items:center;min-width:0;max-width:100%;height:20px}.ok-native-list-subtitle-segments>.ok-native-list-secondary{flex:0 1 auto;min-width:0}.ok-native-list-subtitle-dot{flex:0 0 4px;width:4px;height:4px;margin:0 6px;border-radius:50%;background:var(--nl-disabled)}.ok-native-list-wallet-row>.ok-native-list-flex{flex:0 1 auto;width:100%;align-items:center}.ok-native-list-wallet-badges{display:flex;gap:4px;justify-content:center;margin-top:4px;height:20px;max-width:100%}.ok-native-list-wallet-badges>.ok-native-list-badge{background:var(--nl-strong);color:var(--nl-secondary);font-size:12px;line-height:16px;height:20px;box-sizing:border-box;padding:2px 4px}.ok-native-list-visual-overlay{position:absolute;display:flex;align-items:center;justify-content:center;box-sizing:border-box;border-radius:50%;overflow:hidden;line-height:1;font-size:10px}.ok-native-list-visual-overlay img,.ok-native-list-visual-overlay svg{width:100%;height:100%;object-fit:contain}
478
+ .ok-native-list-index-preview{position:absolute;z-index:8;right:40px;top:50%;display:flex;width:48px;height:48px;align-items:center;justify-content:center;transform:translateY(-50%) scale(.92);border-radius:14px;background:var(--nl-inverse);color:var(--nl-inverse-text);font-size:22px;font-weight:600;opacity:0;pointer-events:none;transition:opacity .15s ease,transform .15s ease}.ok-native-list-index-preview[data-visible="true"]{opacity:1;transform:translateY(-50%) scale(1)}
558
479
  .ok-native-list-row.ok-native-list-market-skeleton{padding:12px 20px;gap:0}.ok-native-list-skeleton-left{display:flex;align-items:center;gap:12px;flex:1}.ok-native-list-skeleton-text{display:flex;flex-direction:column;gap:4px}.ok-native-list-skeleton-right{display:flex;align-items:center;gap:8px}.ok-native-list-skeleton-mark{display:block;flex-shrink:0;border-radius:8px;animation:ok-native-list-skeleton 1.5s linear infinite alternate}@keyframes ok-native-list-skeleton{from{background-color:var(--nl-skeleton-base)}to{background-color:var(--nl-skeleton-highlight)}}.ok-native-list-market-spinner{display:block;width:20px;height:20px;flex-shrink:0;color:var(--nl-icon);animation:ok-native-list-spin .75s linear infinite}
559
480
  @media (prefers-reduced-motion:reduce){.ok-native-list-index-preview,.ok-native-list-refresh{transition:none}.ok-native-list-spinner,.ok-native-list-market-spinner{animation:none}.ok-native-list-skeleton-mark{animation:none;background:var(--nl-skeleton-base)}}
560
481
  .ok-native-list-footer{flex:0 0 auto;min-height:0}.ok-native-list-sticky{position:absolute;z-index:4;left:0;right:0;top:0;pointer-events:auto;box-shadow:0 1px 0 var(--nl-separator)}.ok-native-list-viewport-frame:has(>.ok-native-list-index-rail:not([hidden]))>.ok-native-list-viewport,.ok-native-list-viewport-frame[data-section-index-visible="true"]>.ok-native-list-viewport{scrollbar-width:none}.ok-native-list-viewport-frame:has(>.ok-native-list-index-rail:not([hidden]))>.ok-native-list-viewport::-webkit-scrollbar,.ok-native-list-viewport-frame[data-section-index-visible="true"]>.ok-native-list-viewport::-webkit-scrollbar{display:none}.ok-native-list-index-rail{position:absolute;z-index:6;top:0;right:0;bottom:0;width:${SECTION_INDEX_RAIL_WIDTH}px;touch-action:none;cursor:pointer;font-family:Roobert,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.ok-native-list-index-rail[hidden]{display:none}.ok-native-list-index-button{appearance:none;position:absolute;left:15px;display:flex;width:14px;height:14px;align-items:center;justify-content:center;padding:0;transform:translateY(-50%);border:0;border-radius:7px;background:transparent;color:var(--nl-disabled);font-family:inherit;font-size:10px;font-weight:400;line-height:1;cursor:pointer;opacity:.5;transition:opacity .15s ease}.ok-native-list-index-rail:hover .ok-native-list-index-button,.ok-native-list-index-rail:focus-within .ok-native-list-index-button,.ok-native-list-index-button[data-active="true"]{opacity:1}.ok-native-list-index-button[data-active="true"]{background:var(--nl-positive);color:var(--nl-inverse-text);font-weight:500}.ok-native-list-index-button:focus-visible{outline:2px solid var(--nl-positive);outline-offset:1px}
561
482
  .ok-native-list-refresh{position:absolute;z-index:7;left:50%;top:8px;display:flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;background:var(--nl-inverse);color:var(--nl-inverse-text);font-size:12px;opacity:0;transform:translate(-50%,-16px);transition:opacity .15s ease,transform .15s ease;pointer-events:none}.ok-native-list-refresh[data-visible="true"]{opacity:1;transform:translate(-50%,0)}
562
483
  .ok-native-list-warning{height:auto;display:flex;flex-direction:column;align-items:stretch;gap:4px;padding:14px 12px;border-top:1px solid;border-bottom:1px solid;box-sizing:border-box;cursor:default}.ok-native-list-warning-title,.ok-native-list-warning-message{font-size:14px;line-height:20px;white-space:normal;overflow-wrap:anywhere}.ok-native-list-warning-title{font-weight:500;color:var(--nl-primary)}.ok-native-list-warning-message{font-weight:400;color:var(--nl-secondary)}
563
- .ok-native-list-subtitle-segments{display:flex;align-items:center;min-width:0;max-width:100%;height:20px}.ok-native-list-subtitle-segments>.ok-native-list-secondary{flex:0 1 auto;min-width:0}.ok-native-list-subtitle-dot{flex:0 0 4px;width:4px;height:4px;margin:0 6px;border-radius:50%;background:var(--nl-disabled)}.ok-native-list-wallet-row>.ok-native-list-flex{flex:0 1 auto;width:100%;align-items:center}.ok-native-list-wallet-badges{display:flex;gap:4px;justify-content:center;margin-top:4px;height:20px;max-width:100%}.ok-native-list-wallet-badges>.ok-native-list-badge{background:var(--nl-strong);color:var(--nl-secondary);font-size:12px;line-height:16px;height:20px;box-sizing:border-box;padding:2px 4px}.ok-native-list-visual-overlay{position:absolute;display:flex;align-items:center;justify-content:center;box-sizing:border-box;border-radius:50%;overflow:hidden;line-height:1;font-size:10px}.ok-native-list-visual-overlay img,.ok-native-list-visual-overlay svg{width:100%;height:100%;object-fit:contain}
564
- @media (prefers-reduced-motion:reduce){.ok-native-list-refresh{transition:none}.ok-native-list-spinner{animation:none}}
565
- /* OneKey patch: selector controls follow their original semantic colors and geometry. */
484
+ .ok-native-list-subtitle-segments{display:flex;align-items:center;min-width:0;max-width:100%;height:20px}.ok-native-list-subtitle-segments>.ok-native-list-secondary{flex:0 1 auto;min-width:0}.ok-native-list-subtitle-dot{flex:0 0 4px;width:4px;height:4px;margin:0 6px;border-radius:50%;background:var(--nl-disabled)}.ok-native-list-wallet-row>.ok-native-list-flex{flex:0 1 auto;width:100%;align-items:center}.ok-native-list-wallet-badges{display:flex;gap:4px;justify-content:center;margin-top:4px;height:20px;max-width:100%}.ok-native-list-wallet-badges>.ok-native-list-badge{background:var(--nl-strong);color:var(--nl-secondary);font-size:12px;line-height:16px;height:20px;box-sizing:border-box;padding:2px 4px}.ok-native-list-visual-overlay{position:absolute;display:flex;align-items:center;justify-content:center;box-sizing:border-box;border-radius:50%;overflow:hidden;line-height:1;font-size:10px}.ok-native-list-visual-overlay img,.ok-native-list-visual-overlay svg{width:100%;height:100%;object-fit:contain}/* OneKey patch: selector controls follow their original semantic colors and geometry. */
566
485
  .ok-native-list-checkbox[data-selector="networkSelector"]{padding:0;border-radius:4px;border-color:var(--nl-checkbox-border,var(--nl-separator));background:var(--nl-checkbox-icon,var(--nl-inverse-text))}
567
486
  .ok-native-list-checkbox[data-selector="networkSelector"]::after{display:none}
568
487
  .ok-native-list-checkbox[data-selector="networkSelector"]>svg{display:none;width:16px;height:16px;color:var(--nl-checkbox-icon,var(--nl-inverse-text));flex-shrink:0}
@@ -588,6 +507,11 @@ export const WEB_LIST_CSS = `
588
507
  .ok-native-list-root .ok-native-list-item>.ok-native-list-wallet-row,.ok-native-list-root .ok-native-list-wallet-member>.ok-native-list-wallet-row,.ok-native-list-root .ok-native-list-item>.ok-native-list-account-row,.ok-native-list-root .ok-native-list-item>.ok-native-list-account-action-row,.ok-native-list-wallet-member{cursor:default}
589
508
  /* OneKey patch: Add account hover and pressed backgrounds keep the account row corner radius. */
590
509
  .ok-native-list-account-action-row{border-radius:12px}
510
+
511
+ .ok-native-list-root .ok-native-list-item>[data-nl-container-background="true"],.ok-native-list-root .ok-native-list-wallet-member>[data-nl-container-background="true"]{background:var(--nl-container-background)}
512
+ .ok-native-list-root [data-nl-container-border="true"]{position:relative}
513
+ .ok-native-list-root [data-nl-container-border="true"]::before{content:"";position:absolute;inset:0;border-radius:inherit;box-shadow:inset 0 0 0 var(--nl-container-border-width) var(--nl-container-border-color);z-index:2;pointer-events:none}
514
+ .ok-native-list-root .ok-native-list-item[data-native-list-row-press-enabled="true"]:hover>[data-nl-container-background="true"],.ok-native-list-root .ok-native-list-item[data-native-list-row-press-enabled="true"]:active>[data-nl-container-background="true"],.ok-native-list-root .ok-native-list-wallet-member:hover>[data-nl-container-background="true"],.ok-native-list-root .ok-native-list-wallet-member:active>[data-nl-container-background="true"]{background:var(--nl-pressed)}
591
515
  `;
592
516
  function createElement(document, tag, className, text) {
593
517
  const element = document.createElement(tag);
@@ -1011,16 +935,6 @@ function iconGlyph(name) {
1011
935
  if (normalized.includes('trend') || normalized.includes('arrow')) return '↗';
1012
936
  return name.slice(0, 1).toLocaleUpperCase();
1013
937
  }
1014
- function visualFromRow(row) {
1015
- if (row.type === 'identity') return row.leading;
1016
- if (row.type === 'rail') return row.visual;
1017
- if (row.type === 'activity') return row.leading;
1018
- if (row.type === 'message') return row.leading;
1019
- if (row.type === 'dataRow') return row.leading;
1020
- if (row.type === 'market') return row.leading;
1021
- if (row.type === 'metricCard') return row.visual;
1022
- return undefined;
1023
- }
1024
938
  function createVisual(context, visual, selectorPresentation) {
1025
939
  if (!visual) return undefined;
1026
940
  if (visual.kind === 'stackedImages') {
@@ -1141,11 +1055,6 @@ function toneColor(tone, fallback) {
1141
1055
  if (tone === 'secondary') return 'var(--nl-secondary)';
1142
1056
  return fallback === 'secondary' ? 'var(--nl-secondary)' : 'var(--nl-primary)';
1143
1057
  }
1144
- function createBadge(context, badge) {
1145
- const element = createElement(context.document, 'span', 'ok-native-list-badge', badge.text);
1146
- setData(element, 'tone', badge.tone);
1147
- return element;
1148
- }
1149
1058
  function selectionKeysForTarget(target, rowKey, snapshot) {
1150
1059
  if (!target || target.scope === 'row') {
1151
1060
  return snapshot.rows.some(row => row.key === rowKey && isSelectableRow(row)) ? [rowKey] : [];
@@ -1211,21 +1120,7 @@ function markActionAnchorSource(element, source, slot) {
1211
1120
  }
1212
1121
 
1213
1122
  // OneKey patch: the compact zero-count digits share the amount baseline.
1214
- function applyValueSegments(element, segments, fontSize = 16, lineHeight = 24, weight = 500) {
1215
- if (!segments?.length) return;
1216
- element.textContent = '';
1217
- element.style.fontSize = String(fontSize) + 'px';
1218
- element.style.lineHeight = String(lineHeight) + 'px';
1219
- element.style.fontWeight = String(weight);
1220
- segments.forEach(segment => {
1221
- const span = createElement(element.ownerDocument, 'span', undefined, segment.text);
1222
- if (segment.style === 'subscript') {
1223
- span.style.fontSize = String(Math.ceil(fontSize * 0.6)) + 'px';
1224
- span.style.lineHeight = String(fontSize) + 'px';
1225
- }
1226
- element.appendChild(span);
1227
- });
1228
- }
1123
+
1229
1124
  function createAccessory(context, rowKey, accessory, slot) {
1230
1125
  if (accessory.kind === 'checkbox') {
1231
1126
  const element = createCheckbox(context, rowKey, accessory);
@@ -1242,7 +1137,7 @@ function createAccessory(context, rowKey, accessory, slot) {
1242
1137
  if (row && 'presentation' in row && row.presentation === 'accountSelector') {
1243
1138
  setData(element, 'nativeListAnchorInset', 7);
1244
1139
  // OneKey patch: the create-address button omits IconButton's one-point border.
1245
- if (row.height !== undefined && accessory.name === 'PlusSmallOutline') {
1140
+ if (hasExplicitRowHeight(row) && accessory.name === 'PlusSmallOutline') {
1246
1141
  setData(element, 'nativeListAccountControl', 'createAddress');
1247
1142
  if (!accessory.tintColor) element.style.color = 'var(--nl-icon-subdued)';
1248
1143
  }
@@ -1271,7 +1166,7 @@ function createAccessory(context, rowKey, accessory, slot) {
1271
1166
  primary.style.color = toneColor(accessory.primaryTone, 'primary');
1272
1167
  const secondary = createElement(context.document, 'span', 'ok-native-list-secondary', accessory.secondary);
1273
1168
  secondary.style.color = toneColor(accessory.secondaryTone, 'secondary');
1274
- element.replaceChildren(primary, secondary);
1169
+ element.replaceChildren(primary, '\n', secondary);
1275
1170
  element.classList.add('ok-native-list-amounts');
1276
1171
  break;
1277
1172
  }
@@ -1298,723 +1193,20 @@ function createAccessory(context, rowKey, accessory, slot) {
1298
1193
  if (actionKey) markActionAnchorSource(element, 'trailingAccessory', slot);
1299
1194
  return element;
1300
1195
  }
1301
- function appendAccessories(parent, context, rowKey, accessories) {
1302
- if (!accessories?.length) return;
1303
- const container = createElement(context.document, 'span', 'ok-native-list-accessories');
1304
- const row = context.snapshot.rows[context.itemIndex];
1305
- if (row && row.height !== undefined && 'presentation' in row && row.presentation === 'networkSelector') {
1306
- container.style.gap = accessories.some(accessory => accessory.kind === 'checkbox') ? '12px' : '20px';
1307
- }
1308
- if (row && row.height !== undefined && 'presentation' in row && row.presentation === 'accountSelector' && accessories.length === 1 && accessories[0]?.kind === 'icon' && accessories[0].name === 'PlusSmallOutline') {
1309
- setData(container, 'nativeListAccountControl', 'createAddress');
1310
- }
1311
- accessories.forEach((accessory, slot) => container.appendChild(createAccessory(context, rowKey, accessory, slot)));
1312
- parent.appendChild(container);
1313
- }
1314
- function createTextColumn(context, title, subtitle, tertiary, tertiaryTone, badges) {
1315
- const column = createElement(context.document, 'span', 'ok-native-list-flex');
1316
- const titleLine = createElement(context.document, 'span', 'ok-native-list-title', title);
1317
- if (badges?.length) {
1318
- const badgeLine = createElement(context.document, 'span', 'ok-native-list-badges');
1319
- badges.forEach(badge => badgeLine.appendChild(createBadge(context, badge)));
1320
- titleLine.appendChild(badgeLine);
1321
- }
1322
- column.appendChild(titleLine);
1323
- if (subtitle) column.appendChild(createElement(context.document, 'span', 'ok-native-list-secondary', subtitle));
1324
- if (tertiary) {
1325
- const element = createElement(context.document, 'span', tertiaryTone === 'info' ? 'ok-native-list-secondary ok-native-list-info' : 'ok-native-list-secondary ok-native-list-tertiary', tertiary);
1326
- column.appendChild(element);
1327
- }
1328
- return column;
1329
- }
1330
- function createSectionHeader(context, row) {
1331
- const body = createElement(context.document, 'div', 'ok-native-list-row ok-native-list-section');
1332
- setData(body, 'variant', row.variant);
1333
- if (row.titleIcon) {
1334
- const titleIcon = createIconAction(context, row.titleIcon.name, row.titleIcon.actionKey, row.titleIcon.disabled, row.titleIcon.tintColor);
1335
- markActionAnchorSource(titleIcon, 'leadingAction');
1336
- body.appendChild(titleIcon);
1337
- }
1338
- // OneKey patch: section title help has its own measurable action target.
1339
- // body.appendChild(createTextColumn(context, row.title, row.subtitle));
1340
- const column = createTextColumn(context, row.title, row.subtitle);
1341
- const title = column.firstElementChild;
1342
- title.classList.add('ok-native-list-section-title');
1343
- if (row.titleActionKey) {
1344
- setData(title, 'nativeListAction', row.titleActionKey);
1345
- markActionAnchorSource(title, 'leadingAction');
1346
- title.setAttribute('role', 'button');
1347
- title.tabIndex = 0;
1348
- title.style.alignSelf = 'flex-start';
1349
- title.style.maxWidth = '100%';
1350
- // OneKey patch: explicit network headers reserve a separate 3-point underline area.
1351
- if (row.presentation !== 'networkSelector' || row.height === undefined) {
1352
- title.style.textDecoration = 'underline dotted';
1353
- title.style.textUnderlineOffset = '6px';
1354
- }
1355
- if (row.titleActionOnHover) setData(title, 'nativeListHoverAction', true);
1356
- }
1357
- if (row.presentation === 'networkSelector' && row.height !== undefined) {
1358
- body.style.padding = row.variant === 'summary' ? '24px 12px 20px' : '0 12px';
1359
- body.style.backgroundColor = 'var(--nl-bg)';
1360
- body.style.gap = row.checkbox ? '12px' : '8px';
1361
- title.style.fontSize = row.variant === 'summary' ? '16px' : '14px';
1362
- title.style.lineHeight = row.variant === 'summary' ? '24px' : '20px';
1363
- title.style.fontWeight = row.variant === 'summary' || row.titleActionKey && !row.checkbox ? '500' : '600';
1364
- if (row.titleActionKey) {
1365
- const text = createElement(context.document, 'span', 'ok-native-list-section-title-text', row.title);
1366
- text.style.overflow = 'hidden';
1367
- text.style.textOverflow = 'ellipsis';
1368
- text.style.maxWidth = '100%';
1369
- const dotted = context.document.createElementNS('http://www.w3.org/2000/svg', 'svg');
1370
- dotted.setAttribute('height', '2');
1371
- dotted.style.cssText = 'display:block;position:absolute;left:0;bottom:0;width:100%;height:2px;color:var(--nl-secondary)';
1372
- const line = context.document.createElementNS('http://www.w3.org/2000/svg', 'line');
1373
- for (const [key, value] of Object.entries({
1374
- x1: '1',
1375
- y1: '1',
1376
- x2: '100%',
1377
- y2: '1',
1378
- stroke: 'currentColor',
1379
- 'stroke-width': '1.5',
1380
- 'stroke-dasharray': '0,4',
1381
- 'stroke-linecap': 'round'
1382
- })) line.setAttribute(key, value);
1383
- // OneKey patch: keep both round caps inside the original full-width viewport.
1384
- const lineViewport = context.document.createElementNS('http://www.w3.org/2000/svg', 'svg');
1385
- lineViewport.setAttribute('width', 'calc(100% - 1px)');
1386
- lineViewport.setAttribute('height', '2');
1387
- lineViewport.setAttribute('overflow', 'visible');
1388
- lineViewport.appendChild(line);
1389
- dotted.appendChild(lineViewport);
1390
- // OneKey patch: SVG intrinsic width must not expand the title action beyond its text.
1391
- title.style.display = 'block';
1392
- title.style.position = 'relative';
1393
- title.style.width = 'fit-content';
1394
- title.style.paddingBottom = '3px';
1395
- text.style.display = 'block';
1396
- title.replaceChildren(text, dotted);
1397
- }
1398
- }
1399
- body.appendChild(column);
1400
- if (row.value) {
1401
- const value = createElement(context.document, row.valueActionKey ? 'button' : 'span', row.valueActionKey ? 'ok-native-list-action-button ok-native-list-section-value' : 'ok-native-list-value ok-native-list-section-value', row.value);
1402
- applyValueSegments(value, row.valueSegments);
1403
- if (row.presentation === 'networkSelector' && row.height !== undefined) {
1404
- value.style.fontFamily = 'inherit';
1405
- value.style.fontSize = '16px';
1406
- value.style.lineHeight = '24px';
1407
- value.style.fontWeight = '500';
1408
- if (row.valueActionKey) {
1409
- value.style.color = 'var(--nl-secondary)';
1410
- value.style.padding = '0';
1411
- value.style.flexShrink = '0';
1412
- }
1413
- }
1414
- if (row.valueActionTestID) setData(value, 'testid', row.valueActionTestID);
1415
- if (row.valueActionKey) {
1416
- value.setAttribute('type', 'button');
1417
- setData(value, 'nativeListAction', row.valueActionKey);
1418
- markActionAnchorSource(value, 'trailingAccessory', 0);
1419
- }
1420
- body.appendChild(value);
1421
- }
1422
- if (row.valueIcon) {
1423
- const valueIcon = createIconAction(context, row.valueIcon.name, row.valueIcon.actionKey, row.valueIcon.disabled, row.valueIcon.tintColor);
1424
- markActionAnchorSource(valueIcon, 'trailingAccessory', 1);
1425
- body.appendChild(valueIcon);
1426
- }
1427
- if (row.checkbox) body.appendChild(createCheckbox(context, row.key, row.checkbox));
1428
- return body;
1429
- }
1430
- function createActionRow(context, row) {
1431
- const body = createElement(context.document, 'div', ['ok-native-list-row', 'ok-native-list-action-row', row.presentation === 'accountSelector' ? 'ok-native-list-account-action-row' : ''].filter(Boolean).join(' '));
1432
- if (row.icon) body.appendChild(createVisual(context, row.icon));
1433
- const title = createElement(context.document, 'span', 'ok-native-list-action-title', row.title);
1434
- setData(title, 'tone', row.tone);
1435
- if (row.presentation === 'accountSelector' && row.icon) title.style.fontWeight = '500';
1436
- body.appendChild(title);
1437
- if (row.checkbox) body.appendChild(createCheckbox(context, row.key, row.checkbox));
1438
- appendAccessories(body, context, row.key, row.trailing);
1439
- return body;
1440
- }
1441
- function createSystemRow(context, row) {
1442
- const body = createElement(context.document, 'div', 'ok-native-list-row ok-native-list-system');
1443
- setData(body, 'variant', row.variant);
1444
- if ('presentation' in row) setData(body, 'nativeListPresentation', row.presentation);
1445
- if (row.variant === 'loading' && row.loadingStyle === 'skeleton') {
1446
- body.classList.add('ok-native-list-market-skeleton');
1447
- const background = resolvedTheme(context.snapshot).background;
1448
- const rgb = Number.parseInt(background.slice(1, 7), 16);
1449
- const dark = (rgb >> 16 & 255) * 0.299 + (rgb >> 8 & 255) * 0.587 + (rgb & 255) * 0.114 < 128;
1450
- body.style.setProperty('--nl-skeleton-base', dark ? '#111111' : '#fafafa');
1451
- body.style.setProperty('--nl-skeleton-highlight', dark ? '#333333' : '#cdcdcd');
1452
- const left = createElement(context.document, 'div', 'ok-native-list-skeleton-left');
1453
- const mark = (width, height, circle = false) => {
1454
- const element = createElement(context.document, 'span', 'ok-native-list-skeleton-mark');
1455
- element.style.width = `${width}px`;
1456
- element.style.height = `${height}px`;
1457
- if (circle) element.style.borderRadius = '50%';
1458
- return element;
1459
- };
1460
- left.appendChild(mark(32, 32, true));
1461
- const text = createElement(context.document, 'div', 'ok-native-list-skeleton-text');
1462
- text.appendChild(mark(80, 16));
1463
- text.appendChild(mark(60, 12));
1464
- left.appendChild(text);
1465
- body.appendChild(left);
1466
- const right = createElement(context.document, 'div', 'ok-native-list-skeleton-right');
1467
- right.appendChild(mark(80, 18));
1468
- right.appendChild(mark(80, 18));
1469
- body.appendChild(right);
1470
- return body;
1471
- }
1472
- if (row.variant === 'loading' && row.loadingStyle === 'spinner') {
1473
- body.style.justifyContent = 'center';
1474
- body.style.padding = '16px';
1475
- const spinner = createElement(context.document, 'span', 'ok-native-list-market-spinner');
1476
- spinner.setAttribute('role', 'progressbar');
1477
- // Same 20px SVG and 750ms rotation as react-native-web ActivityIndicator.
1478
- spinner.innerHTML = '<svg viewBox="0 0 32 32" width="20" height="20"><circle cx="16" cy="16" r="14" fill="none" stroke="currentColor" stroke-width="4" opacity="0.2"/><circle cx="16" cy="16" r="14" fill="none" stroke="currentColor" stroke-width="4" stroke-dasharray="80" stroke-dashoffset="60"/></svg>';
1479
- body.appendChild(spinner);
1480
- return body;
1481
- }
1482
- if ('presentation' in row && row.presentation === 'market') {
1483
- if (row.variant === 'noMatch') {
1484
- body.style.justifyContent = 'center';
1485
- body.style.padding = '32px';
1486
- const message = createElement(context.document, 'span', '', row.message);
1487
- message.style.fontSize = '16px';
1488
- message.style.lineHeight = '24px';
1489
- body.appendChild(message);
1490
- return body;
1491
- }
1492
- if (row.variant === 'end') {
1493
- body.style.justifyContent = 'center';
1494
- body.style.padding = '16px';
1495
- body.style.gap = '8px';
1496
- for (const width of [80, 4, 80]) {
1497
- const mark = createElement(context.document, 'span', '');
1498
- mark.style.width = String(width) + 'px';
1499
- mark.style.height = width === 4 ? '4px' : '1px';
1500
- mark.style.borderRadius = width === 4 ? '2px' : '0';
1501
- mark.style.background = 'var(--nl-separator)';
1502
- body.appendChild(mark);
1503
- }
1504
- return body;
1505
- }
1506
- }
1507
- if (row.variant === 'warning') {
1508
- body.classList.add('ok-native-list-warning');
1509
- body.style.borderColor = row.borderColor ?? 'var(--nl-separator)';
1510
- body.appendChild(createElement(context.document, 'span', 'ok-native-list-warning-title', row.title));
1511
- body.appendChild(createElement(context.document, 'span', 'ok-native-list-warning-message', row.message));
1512
- return body;
1513
- }
1514
- if (row.variant === 'loading') body.appendChild(createElement(context.document, 'span', 'ok-native-list-spinner'));
1515
- const message = row.variant === 'spacer' ? '' : row.message ?? (row.variant === 'end' ? 'End' : '');
1516
- if (message) body.appendChild(createElement(context.document, 'span', 'ok-native-list-secondary', message));
1517
- return body;
1518
- }
1519
- function createRailRow(context, row) {
1520
- const body = createElement(context.document, 'div', 'ok-native-list-row ok-native-list-rail');
1521
- const visual = createVisual(context, row.visual);
1522
- if (visual) body.appendChild(visual);
1523
- body.appendChild(createElement(context.document, 'span', 'ok-native-list-rail-title', row.title));
1524
- if (row.status && row.status !== 'none') body.appendChild(createElement(context.document, 'span', 'ok-native-list-secondary', row.status));
1525
- if (row.badge) body.appendChild(createBadge(context, row.badge));
1526
- return body;
1527
- }
1528
- function createMediaRow(context, row) {
1529
- const body = createElement(context.document, 'div', 'ok-native-list-row ok-native-list-media');
1530
- if (row.image && !row.imageState) {
1531
- const image = createImage(context, row.image, 'ok-native-list-media-image');
1532
- if (image) body.appendChild(image);
1533
- } else {
1534
- const placeholder = createElement(context.document, 'span', 'ok-native-list-media-image', row.imageState === 'error' ? '▧' : '');
1535
- setData(placeholder, 'state', row.imageState ?? 'empty');
1536
- body.appendChild(placeholder);
1537
- }
1538
- const metadata = createElement(context.document, 'div', 'ok-native-list-media-meta');
1539
- const subtitleLine = createElement(context.document, 'div', 'ok-native-list-media-subtitle-row');
1540
- subtitleLine.appendChild(createElement(context.document, 'span', 'ok-native-list-media-subtitle', row.subtitle || '-'));
1541
- if (row.networkImage) {
1542
- const network = createImage(context, row.networkImage, 'ok-native-list-media-network');
1543
- if (network) subtitleLine.appendChild(network);
1544
- }
1545
- metadata.appendChild(subtitleLine);
1546
- metadata.appendChild(createElement(context.document, 'div', 'ok-native-list-media-title', row.title));
1547
- if (row.badge) metadata.appendChild(createBadge(context, row.badge));
1548
- body.appendChild(metadata);
1549
- if (row.closeActionKey) {
1550
- const close = createElement(context.document, 'button', 'ok-native-list-media-close', '×');
1551
- close.setAttribute('type', 'button');
1552
- close.setAttribute('aria-label', 'Close');
1553
- setData(close, 'nativeListAction', row.closeActionKey);
1554
- markActionAnchorSource(close, 'mediaClose');
1555
- body.appendChild(close);
1556
- }
1557
- return body;
1558
- }
1559
- function createMetricCell(context, metric, shaded) {
1560
- const cell = createElement(context.document, 'div', 'ok-native-list-composite-cell');
1561
- setData(cell, 'shaded', shaded);
1562
- cell.appendChild(createElement(context.document, 'div', 'ok-native-list-secondary', metric.label));
1563
- const valueLine = createElement(context.document, 'div');
1564
- valueLine.style.display = 'flex';
1565
- valueLine.style.alignItems = 'center';
1566
- valueLine.style.gap = '6px';
1567
- if (metric.visual) {
1568
- const visual = createVisual(context, metric.visual);
1569
- if (visual) {
1570
- visual.style.width = '16px';
1571
- visual.style.height = '16px';
1572
- visual.style.flexBasis = '16px';
1573
- const image = visual.querySelector('img');
1574
- if (image) {
1575
- image.style.width = '16px';
1576
- image.style.height = '16px';
1577
- }
1578
- valueLine.appendChild(visual);
1579
- }
1580
- }
1581
- const value = createElement(context.document, 'span', 'ok-native-list-composite-value', metric.value);
1582
- value.style.color = toneColor(metric.tone, 'primary');
1583
- valueLine.appendChild(value);
1584
- cell.appendChild(valueLine);
1585
- return cell;
1586
- }
1587
- function createMetricRow(context, row) {
1588
- if (row.variant === 'activity' || row.variant === 'performance') {
1589
- const body = createElement(context.document, 'div', 'ok-native-list-row ok-native-list-composite');
1590
- body.appendChild(createElement(context.document, 'div', 'ok-native-list-composite-heading', row.title));
1591
- const metrics = row.metrics ?? [];
1592
- const firstLine = createElement(context.document, 'div', 'ok-native-list-composite-row');
1593
- metrics.slice(0, 2).forEach(metric => firstLine.appendChild(createMetricCell(context, metric, false)));
1594
- body.appendChild(firstLine);
1595
- if (row.variant === 'activity') {
1596
- body.appendChild(createElement(context.document, 'div', 'ok-native-list-divider'));
1597
- } else {
1598
- const progress = createElement(context.document, 'div', 'ok-native-list-progress');
1599
- const fill = createElement(context.document, 'span');
1600
- fill.style.width = String(Math.round((row.progress ?? 0) * 100)) + '%';
1601
- progress.appendChild(fill);
1602
- body.appendChild(progress);
1603
- }
1604
- const secondLine = createElement(context.document, 'div', 'ok-native-list-composite-row');
1605
- metrics.slice(2).forEach(metric => secondLine.appendChild(createMetricCell(context, metric, row.variant === 'performance')));
1606
- body.appendChild(secondLine);
1607
- return body;
1608
- }
1609
- const body = createElement(context.document, 'div', 'ok-native-list-row ok-native-list-metric');
1610
- if (row.visual) {
1611
- const visual = createVisual(context, row.visual);
1612
- if (visual) body.appendChild(visual);
1613
- }
1614
- body.appendChild(createElement(context.document, 'div', 'ok-native-list-secondary', row.title));
1615
- body.appendChild(createElement(context.document, 'div', 'ok-native-list-metric-value', row.value));
1616
- if (row.trend) {
1617
- const trend = createElement(context.document, 'div', 'ok-native-list-secondary', row.trend);
1618
- trend.style.color = row.trendTone === 'positive' ? 'var(--nl-positive)' : row.trendTone === 'negative' ? 'var(--nl-negative)' : 'var(--nl-secondary)';
1619
- body.appendChild(trend);
1620
- }
1621
- if (row.subtitle) body.appendChild(createElement(context.document, 'div', 'ok-native-list-secondary', row.subtitle));
1622
- if (row.badge) body.appendChild(createBadge(context, row.badge));
1623
- return body;
1624
- }
1625
- function createDataRow(context, row) {
1626
- const body = createElement(context.document, 'div', 'ok-native-list-row ok-native-list-data');
1627
- if (row.checkbox) body.appendChild(createCheckbox(context, row.key, row.checkbox));
1628
- if (row.index !== undefined) body.appendChild(createElement(context.document, 'span', 'ok-native-list-index', String(row.index)));
1629
- if (row.favorite) {
1630
- const favorite = createElement(context.document, 'span', 'ok-native-list-favorite', row.favoriteActive ? '★' : '☆');
1631
- setData(favorite, 'active', row.favoriteActive);
1632
- body.appendChild(favorite);
1633
- }
1634
- if (row.leading) {
1635
- const visual = createVisual(context, row.leading);
1636
- if (visual) body.appendChild(visual);
1637
- }
1638
- row.columns.forEach(column => {
1639
- const cell = createElement(context.document, 'span', 'ok-native-list-data-cell');
1640
- cell.style.flex = String(column.weight ?? 1);
1641
- setData(cell, 'align', column.alignment ?? 'start');
1642
- const primary = createElement(context.document, 'span', 'ok-native-list-data-primary');
1643
- primary.style.color = toneColor(column.tone, 'primary');
1644
- if (column.secondaryLeadingText) primary.appendChild(createElement(context.document, 'span', 'ok-native-list-secondary', column.secondaryLeadingText));
1645
- primary.appendChild(createElement(context.document, 'span', undefined, column.text));
1646
- if (column.key === 'asset' && row.badges?.length) {
1647
- row.badges.forEach(badge => primary.appendChild(createBadge(context, badge)));
1648
- }
1649
- cell.appendChild(primary);
1650
- if (column.secondaryText) {
1651
- const secondary = createElement(context.document, 'span', 'ok-native-list-secondary', column.secondaryText);
1652
- secondary.style.color = toneColor(column.secondaryTone, 'secondary');
1653
- cell.appendChild(secondary);
1654
- }
1655
- body.appendChild(cell);
1656
- });
1657
- return body;
1658
- }
1659
- function createIdentityActivityOrMessageRow(context, row) {
1660
- const presentation = row.type === 'identity' ? row.presentation : undefined;
1661
- const body = createElement(context.document, 'div', ['ok-native-list-row', 'ok-native-list-standard', row.type === 'identity' && !presentation ? 'ok-native-list-identity-row' : '', presentation === 'networkSelector' ? 'ok-native-list-network-row' : '', presentation === 'walletSidebar' ? 'ok-native-list-wallet-row' : '', presentation === 'accountSelector' ? 'ok-native-list-account-row' : ''].filter(Boolean).join(' '));
1662
- setData(body, 'nativeListSelector', row.height !== undefined ? presentation : undefined);
1663
- if (row.type === 'identity' && row.titleActionKey && row.titleActionOnHover) {
1664
- setData(body, 'nativeListHoverAction', row.titleActionKey);
1665
- markActionAnchorSource(body, 'leadingAction');
1666
- }
1667
- if (row.type === 'identity' && row.leadingAction) {
1668
- const action = createIconAction(context, row.leadingAction.name, row.leadingAction.actionKey, row.leadingAction.disabled, row.leadingAction.tintColor);
1669
- markActionAnchorSource(action, 'leadingAction');
1670
- body.appendChild(action);
1671
- }
1672
- const visual = createVisual(context, visualFromRow(row), row.height !== undefined ? presentation : undefined);
1673
- if (visual && row.type === 'identity' && row.height !== undefined && row.presentation === 'walletSidebar' && 'fallbackIcon' in row.leading && row.leading.fallbackIcon?.name === 'LockSolid') {
1674
- // OneKey patch: hidden-wallet locks use WalletAvatar's full 40-point icon.
1675
- const icon = visual.querySelector('.ok-native-list-visual-fallback svg');
1676
- if (icon) {
1677
- icon.style.width = '40px';
1678
- icon.style.height = '40px';
1679
- }
1680
- const fallback = visual.querySelector('.ok-native-list-visual-fallback');
1681
- if (fallback) {
1682
- fallback.style.borderRadius = '0';
1683
- fallback.style.overflow = 'visible';
1684
- }
1685
- }
1686
- if (visual && row.type === 'identity' && row.height !== undefined && row.presentation === 'walletSidebar' && 'borderStyle' in row.leading && row.leading.borderStyle === 'dashed') visual.style.borderWidth = '1px';
1687
- if (visual) body.appendChild(visual);
1688
- if (row.type === 'activity' && row.secondaryLeading) {
1689
- const secondVisual = createVisual(context, row.secondaryLeading);
1690
- if (secondVisual) body.appendChild(secondVisual);
1691
- }
1692
- if (row.type === 'message' && row.unread) body.appendChild(createElement(context.document, 'span', 'ok-native-list-unread'));
1693
- const title = row.title;
1694
- const subtitle = row.type === 'identity' ? row.subtitle : row.type === 'activity' ? row.description : row.body;
1695
- const column = createTextColumn(context, title, subtitle, row.type === 'identity' ? row.tertiary : undefined, row.type === 'identity' ? row.tertiaryTone : undefined, row.type === 'identity' && presentation !== 'walletSidebar' ? row.badges : undefined);
1696
- // OneKey patch: match existing search, subtitle fragments, and sidebar badges.
1697
- if (row.type === 'identity') {
1698
- const titleElement = column.firstElementChild;
1699
- if (row.titleMatch?.length) {
1700
- const firstText = titleElement.firstChild;
1701
- if (firstText) firstText.remove();
1702
- const fragment = context.document.createDocumentFragment();
1703
- let offset = 0;
1704
- row.titleMatch.forEach(({
1705
- start,
1706
- end
1707
- }) => {
1708
- fragment.appendChild(context.document.createTextNode(row.title.slice(offset, start)));
1709
- const match = createElement(context.document, 'span', 'ok-native-list-info', row.title.slice(start, end));
1710
- fragment.appendChild(match);
1711
- offset = end;
1712
- });
1713
- fragment.appendChild(context.document.createTextNode(row.title.slice(offset)));
1714
- titleElement.prepend(fragment);
1715
- }
1716
- if (row.subtitleSegments?.length) {
1717
- column.querySelector('.ok-native-list-secondary')?.remove();
1718
- const segments = createElement(context.document, 'span', 'ok-native-list-subtitle-segments');
1719
- row.subtitleSegments.forEach(segment => {
1720
- if (segment.separatorBefore) segments.appendChild(createElement(context.document, 'span', 'ok-native-list-subtitle-dot'));
1721
- const text = createElement(context.document, 'span', 'ok-native-list-secondary', segment.text);
1722
- applyValueSegments(text, segment.textSegments, 14, 20, 400);
1723
- setData(text, 'tone', segment.tone);
1724
- text.style.color = segment.tone === 'disabled' ? 'var(--nl-disabled)' : segment.tone === 'caution' ? 'var(--nl-caution)' : toneColor(segment.tone, 'secondary');
1725
- segments.appendChild(text);
1726
- });
1727
- column.insertBefore(segments, titleElement.nextSibling);
1728
- }
1729
- if (presentation === 'walletSidebar' && row.badges?.length) {
1730
- const badges = createElement(context.document, 'span', 'ok-native-list-wallet-badges');
1731
- row.badges.forEach(badge => badges.appendChild(createBadge(context, badge)));
1732
- column.appendChild(badges);
1733
- }
1734
- }
1735
- if (row.type === 'activity' && row.status) column.appendChild(createElement(context.document, 'span', 'ok-native-list-secondary', row.status));
1736
- if (row.type === 'activity' && row.footerActions?.length) {
1737
- const actions = createElement(context.document, 'span', 'ok-native-list-actions');
1738
- row.footerActions.forEach((action, slot) => {
1739
- const button = createElement(context.document, 'button', 'ok-native-list-action-button', action.label);
1740
- button.setAttribute('type', 'button');
1741
- button.toggleAttribute('disabled', Boolean(action.disabled));
1742
- setData(button, 'tone', action.tone);
1743
- setData(button, 'nativeListAction', action.key);
1744
- markActionAnchorSource(button, 'footerAction', slot);
1745
- actions.appendChild(button);
1746
- });
1747
- column.appendChild(actions);
1748
- }
1749
- body.appendChild(column);
1750
- if (row.type === 'activity') {
1751
- const amounts = createElement(context.document, 'span', 'ok-native-list-amounts');
1752
- if (row.primaryAmount) amounts.appendChild(createElement(context.document, 'span', 'ok-native-list-value', row.primaryAmount));
1753
- if (row.secondaryAmount) amounts.appendChild(createElement(context.document, 'span', 'ok-native-list-secondary', row.secondaryAmount));
1754
- body.appendChild(amounts);
1755
- } else if (row.type === 'message') {
1756
- body.appendChild(createElement(context.document, 'span', 'ok-native-list-time', row.time));
1757
- if (row.thumbnail) {
1758
- const thumbnail = createImage(context, row.thumbnail, 'ok-native-list-thumbnail');
1759
- if (thumbnail) body.appendChild(thumbnail);
1760
- }
1761
- } else {
1762
- appendAccessories(body, context, row.key, row.trailing);
1763
- }
1764
- return body;
1765
- }
1766
-
1767
- // OneKey patch: SizableText enables tabular digits without replacing its font family.
1768
- function applySelectorTabularNumbers(body, row) {
1769
- if (!('presentation' in row) || !['accountSelector', 'networkSelector', 'walletSidebar'].includes(row.presentation ?? '')) return;
1770
- body.style.fontVariantNumeric = 'tabular-nums';
1771
- body.querySelectorAll('span,button').forEach(text => {
1772
- text.style.fontVariantNumeric = 'tabular-nums';
1773
- });
1774
- }
1775
- function createWalletGroupRow(context, row) {
1776
- const body = createElement(context.document, 'div', 'ok-native-list-wallet-group');
1777
- [row.parent, ...row.children].forEach((member, memberIndex) => {
1778
- const memberElement = createElement(context.document, 'div', 'ok-native-list-wallet-member');
1779
- setData(memberElement, 'nativeListGroupMemberKey', member.key);
1780
- setData(memberElement, 'testid', member.testID);
1781
- setData(memberElement, 'nativeListGroupParent', memberIndex === 0);
1782
- setData(memberElement, 'nativeListSelected', member.selected);
1783
- // OneKey patch: grouped members have the same selector typography as standalone wallets.
1784
- // memberElement.appendChild(createIdentityActivityOrMessageRow(context, member));
1785
- const memberBody = createIdentityActivityOrMessageRow(context, member);
1786
- applySelectorTabularNumbers(memberBody, member);
1787
- memberElement.appendChild(memberBody);
1788
- // OneKey patch: group children use their own measured badge height.
1789
- memberElement.style.flexBasis = String(member.height ?? 68 + (member.badges?.length ? 24 : 0)) + 'px';
1790
- memberElement.style.height = memberElement.style.flexBasis;
1791
- memberElement.style.opacity = String(member.opacity ?? 1);
1792
- body.appendChild(memberElement);
1793
- });
1794
- return body;
1795
- }
1796
- function marketFontWeight(value, fallback) {
1797
- if (value === 'bold') return 700;
1798
- if (value === 'semibold') return 600;
1799
- if (value === 'medium') return 500;
1800
- if (value === 'regular') return 400;
1801
- return fallback;
1802
- }
1803
- function applyMarketTextStyle(element, style, defaults) {
1804
- element.style.fontSize = String(style?.fontSize ?? defaults.fontSize) + 'px';
1805
- element.style.lineHeight = String(style?.lineHeight ?? defaults.lineHeight) + 'px';
1806
- element.style.fontWeight = String(marketFontWeight(style?.fontWeight, defaults.weight));
1807
- element.style.color = style?.color ?? '';
1808
- element.style.textAlign = style?.alignment ?? defaults.alignment;
1809
- element.style.whiteSpace = style?.lines === 2 ? 'normal' : 'nowrap';
1810
- element.style.display = '';
1811
- element.style.removeProperty('-webkit-line-clamp');
1812
- element.style.removeProperty('-webkit-box-orient');
1813
- if (style?.lines === 2) {
1814
- element.style.display = '-webkit-box';
1815
- element.style.setProperty('-webkit-line-clamp', '2');
1816
- element.style.setProperty('-webkit-box-orient', 'vertical');
1817
- }
1818
- }
1819
- function setMarketQuoteContent(element, row) {
1820
- const style = row.style;
1821
- const layoutStyle = resolveWebMarketLayoutStyle(row);
1822
- const price = element.querySelector('.ok-native-list-market-price');
1823
- if (price) {
1824
- price.textContent = row.price;
1825
- applyMarketTextStyle(price, style?.price, {
1826
- fontSize: 16,
1827
- lineHeight: 24,
1828
- weight: 500,
1829
- alignment: 'end'
1830
- });
1831
- applyValueSegments(price, row.priceSegments, style?.price?.fontSize ?? 16, style?.price?.lineHeight ?? 24, marketFontWeight(style?.price?.fontWeight, 500));
1832
- }
1833
- const change = element.querySelector('.ok-native-list-market-change');
1834
- if (change) {
1835
- change.textContent = row.change.text;
1836
- setData(change, 'tone', row.change.tone);
1837
- applyMarketTextStyle(change, style?.change, {
1838
- fontSize: 14,
1839
- lineHeight: 20,
1840
- weight: 500,
1841
- alignment: 'center'
1842
- });
1843
- applyValueSegments(change, row.change.textSegments, style?.change?.fontSize ?? 14, style?.change?.lineHeight ?? 20, marketFontWeight(style?.change?.fontWeight, 500));
1844
- change.style.width = String(layoutStyle.changeWidth) + 'px';
1845
- change.style.height = String(layoutStyle.changeHeight) + 'px';
1846
- change.style.borderRadius = String(layoutStyle.changeCornerRadius) + 'px';
1847
- change.style.color = row.change.textColor ?? style?.change?.color ?? '';
1848
- change.style.background = row.change.backgroundColor ?? '';
1849
- }
1850
- element.setAttribute('aria-label', row.accessibilityLabel ?? [row.title, row.subtitle, row.price, row.change.text].filter(Boolean).join(', '));
1851
- }
1852
- function createMarketRow(context, row) {
1853
- const body = createElement(context.document, 'div', 'ok-native-list-row ok-native-list-market');
1854
- setData(body, 'variant', row.variant);
1855
- const style = row.style;
1856
- const layoutStyle = resolveWebMarketLayoutStyle(row);
1857
- body.style.padding = String(layoutStyle.verticalPadding) + 'px ' + String(layoutStyle.horizontalPadding) + 'px';
1858
- body.style.gap = '0px';
1859
- if (row.leadingAction) {
1860
- const action = createIconAction(context, row.leadingAction.name, row.leadingAction.actionKey, row.leadingAction.disabled, row.leadingAction.tintColor);
1861
- action.style.flex = '0 0 36px';
1862
- action.style.width = '36px';
1863
- action.style.height = '36px';
1864
- action.style.marginRight = '5px';
1865
- setData(action, 'testid', row.leadingAction.testID);
1866
- if (row.leadingAction.accessibilityLabel) action.setAttribute('aria-label', row.leadingAction.accessibilityLabel);
1867
- markActionAnchorSource(action, 'leadingAction');
1868
- body.appendChild(action);
1869
- }
1870
- const visual = createVisual(context, row.leading);
1871
- if (visual) {
1872
- const width = layoutStyle.imageWidth;
1873
- const height = layoutStyle.imageHeight;
1874
- visual.style.width = String(width) + 'px';
1875
- visual.style.height = String(height) + 'px';
1876
- visual.style.flexBasis = String(width) + 'px';
1877
- visual.style.borderRadius = String(layoutStyle.imageCornerRadius) + 'px';
1878
- const borderColor = 'borderColor' in row.leading ? row.leading.borderColor : undefined;
1879
- if (borderColor) {
1880
- visual.style.border = '1px solid ' + borderColor;
1881
- visual.style.boxSizing = 'border-box';
1882
- }
1883
- visual.querySelectorAll('img').forEach(image => {
1884
- if (!image.classList.contains('ok-native-list-visual-corner')) {
1885
- image.style.width = '100%';
1886
- image.style.height = '100%';
1887
- if (borderColor) {
1888
- image.style.borderRadius = '0';
1889
- image.style.clipPath = 'inset(-1px round ' + String(layoutStyle.imageCornerRadius) + 'px)';
1890
- }
1891
- image.style.objectFit = style?.image?.contentFit ?? image.style.objectFit;
1892
- } else {
1893
- image.style.width = '20px';
1894
- image.style.height = '20px';
1895
- if (borderColor) {
1896
- image.style.right = '-5px';
1897
- image.style.bottom = '-5px';
1898
- }
1899
- }
1900
- });
1901
- visual.style.marginRight = String(layoutStyle.leadingGap) + 'px';
1902
- body.appendChild(visual);
1903
- }
1904
- const main = createElement(context.document, 'span', 'ok-native-list-market-main');
1905
- main.style.gap = String(style?.lineGap ?? 0) + 'px';
1906
- const titleLine = createElement(context.document, 'span', 'ok-native-list-market-title-line');
1907
- titleLine.style.gap = String(layoutStyle.titleBadgeGap) + 'px';
1908
- const title = createElement(context.document, 'span', 'ok-native-list-market-title', row.title);
1909
- applyMarketTextStyle(title, style?.title, {
1910
- fontSize: 16,
1911
- lineHeight: 24,
1912
- weight: 500,
1913
- alignment: 'start'
1914
- });
1915
- titleLine.appendChild(title);
1916
- row.badges?.forEach((badge, slot) => {
1917
- const element = createElement(context.document, badge.actionKey ? 'button' : 'span', 'ok-native-list-market-badge', badge.text);
1918
- if (badge.actionKey) {
1919
- element.setAttribute('type', 'button');
1920
- setData(element, 'nativeListAction', badge.actionKey);
1921
- markActionAnchorSource(element, 'marketBadge', slot);
1922
- }
1923
- setData(element, 'tone', badge.tone);
1924
- element.style.color = badge.textColor ?? (badge.tone === 'success' ? 'var(--nl-positive)' : badge.tone === 'danger' ? 'var(--nl-negative)' : badge.tone === 'info' ? 'var(--nl-info)' : badge.tone === 'warning' ? 'var(--nl-primary)' : '');
1925
- element.style.background = badge.backgroundColor ?? ((badge.icon || badge.iconName) && !badge.text ? 'transparent' : '');
1926
- if ((badge.icon || badge.iconName) && !badge.text) {
1927
- element.style.padding = '0';
1928
- }
1929
- if (badge.accessibilityLabel) element.setAttribute('aria-label', badge.accessibilityLabel);
1930
- if (badge.icon) {
1931
- const icon = createImage(context, badge.icon);
1932
- if (icon) {
1933
- icon.style.borderRadius = '50%';
1934
- element.prepend(icon);
1935
- }
1936
- }
1937
- if (badge.iconName === 'verified') {
1938
- const icon = createElement(context.document, 'span', 'ok-native-list-market-badge-icon');
1939
- applySelectorIcon(icon, 'BadgeVerifiedSolid');
1940
- element.prepend(icon);
1941
- }
1942
- titleLine.appendChild(element);
1943
- });
1944
- main.appendChild(titleLine);
1945
- if (row.subtitlePrefix || row.subtitle || row.subtitleSegments?.length) {
1946
- const subtitleLine = createElement(context.document, 'span', 'ok-native-list-market-subtitle-line');
1947
- subtitleLine.style.display = 'flex';
1948
- subtitleLine.style.alignItems = 'center';
1949
- subtitleLine.style.minWidth = '0';
1950
- subtitleLine.style.overflow = 'hidden';
1951
- subtitleLine.style.gap = String(row.subtitlePrefix ? row.subtitlePrefix.gap ?? 4 : 0) + 'px';
1952
- if (row.subtitlePrefix) {
1953
- const prefix = createElement(context.document, 'span', 'ok-native-list-market-subtitle-prefix', row.subtitlePrefix.text);
1954
- applyMarketTextStyle(prefix, row.subtitlePrefix.style, {
1955
- fontSize: 12,
1956
- lineHeight: 16,
1957
- weight: 400,
1958
- alignment: 'start'
1959
- });
1960
- prefix.style.display = 'block';
1961
- prefix.style.flex = '1 1 auto';
1962
- prefix.style.minWidth = '0';
1963
- prefix.style.overflow = 'hidden';
1964
- prefix.style.textOverflow = 'ellipsis';
1965
- prefix.style.color = row.subtitlePrefix.style?.color ?? 'var(--nl-secondary)';
1966
- if (row.subtitlePrefix.maxWidth !== undefined) {
1967
- prefix.style.maxWidth = String(row.subtitlePrefix.maxWidth) + 'px';
1968
- }
1969
- subtitleLine.appendChild(prefix);
1970
- }
1971
- if (row.subtitle || row.subtitleSegments?.length) {
1972
- const subtitle = createElement(context.document, 'span', 'ok-native-list-market-subtitle', row.subtitle);
1973
- applyMarketTextStyle(subtitle, style?.subtitle, {
1974
- fontSize: 14,
1975
- lineHeight: 20,
1976
- weight: 400,
1977
- alignment: 'start'
1978
- });
1979
- applyValueSegments(subtitle, row.subtitleSegments, style?.subtitle?.fontSize ?? 14, style?.subtitle?.lineHeight ?? 20, marketFontWeight(style?.subtitle?.fontWeight, 400));
1980
- subtitle.style.flex = row.subtitlePrefix ? '0 0 auto' : '1 1 auto';
1981
- subtitleLine.appendChild(subtitle);
1982
- }
1983
- main.appendChild(subtitleLine);
1984
- }
1985
- body.appendChild(main);
1986
- const trailing = createElement(context.document, 'span', 'ok-native-list-market-trailing');
1987
- trailing.style.gap = String(layoutStyle.trailingGap) + 'px';
1988
- trailing.append(createElement(context.document, 'span', 'ok-native-list-market-price'), createElement(context.document, 'span', 'ok-native-list-market-change'));
1989
- body.appendChild(trailing);
1990
- setMarketQuoteContent(body, row);
1991
- return body;
1196
+ function rendererPrimitives(context) {
1197
+ return {
1198
+ background: resolvedTheme(context.snapshot).background,
1199
+ accessory: (key, descriptor, slot) => createAccessory(context, key, descriptor, slot),
1200
+ visual: (source, presentation) => createVisual(context, source, presentation),
1201
+ thumbnail: source => createImage(context, source, 'ok-native-list-thumbnail'),
1202
+ textStyle: applyTextStyleToSlot,
1203
+ icon: applySelectorIcon,
1204
+ iconAction: (name, actionKey, disabled, tintColor) => createIconAction(context, name, actionKey, disabled, tintColor),
1205
+ dispose: disposeWebImageRetries
1206
+ };
1992
1207
  }
1993
- function createRowBody(context, row) {
1994
- switch (row.type) {
1995
- case 'walletGroup':
1996
- return createWalletGroupRow(context, row);
1997
- case 'sectionHeader':
1998
- return createSectionHeader(context, row);
1999
- case 'action':
2000
- return createActionRow(context, row);
2001
- case 'system':
2002
- return createSystemRow(context, row);
2003
- case 'rail':
2004
- return createRailRow(context, row);
2005
- case 'mediaTile':
2006
- return createMediaRow(context, row);
2007
- case 'metricCard':
2008
- return createMetricRow(context, row);
2009
- case 'dataRow':
2010
- return createDataRow(context, row);
2011
- case 'market':
2012
- return createMarketRow(context, row);
2013
- case 'identity':
2014
- case 'activity':
2015
- case 'message':
2016
- return createIdentityActivityOrMessageRow(context, row);
2017
- }
1208
+ export function createRowBody(context, row) {
1209
+ return rowRenderer(row).create(context.document, rendererPrimitives(context));
2018
1210
  }
2019
1211
  export class NativeListWebEngine {
2020
1212
  rows = [];
@@ -2026,7 +1218,21 @@ export class NativeListWebEngine {
2026
1218
  horizontal: false
2027
1219
  };
2028
1220
  mounted = new Map();
2029
- pool = [];
1221
+ pool = {
1222
+ walletGroup: [],
1223
+ message: [],
1224
+ rail: [],
1225
+ mediaTile: [],
1226
+ action: [],
1227
+ system: [],
1228
+ activity: [],
1229
+ dataRow: [],
1230
+ metricCard: [],
1231
+ market: [],
1232
+ identity: [],
1233
+ sectionHeader: []
1234
+ };
1235
+ rendererKeys = new WeakMap();
2030
1236
  sectionIndexTransformTrackingFrames = 0;
2031
1237
  sectionIndexTransformSettledFrames = 0;
2032
1238
  // OneKey patch: URI leases outlive DOM overscan only inside this bounded window.
@@ -2044,8 +1250,8 @@ export class NativeListWebEngine {
2044
1250
  lastViewportHeight = -1;
2045
1251
  destroyed = false;
2046
1252
  sectionIndexAlignStart = false;
2047
- // OneKey patch: warning banners are measured after normal browser text wrapping.
2048
- measuredWarningHeights = new Map();
1253
+ // Renderers report intrinsic height after browser text wrapping.
1254
+ measuredRendererHeights = new Map();
2049
1255
  constructor(host, snapshot, callbacks, virtualizationEnabled = true, sectionIndexContainer) {
2050
1256
  this.document = host.ownerDocument;
2051
1257
  this.snapshot = validateSnapshot(snapshot);
@@ -2323,20 +1529,22 @@ export class NativeListWebEngine {
2323
1529
  this.viewport.removeEventListener('pointercancel', this.handlePullEnd);
2324
1530
  const host = this.root.parentElement;
2325
1531
  disposeWebImageRetries(this.root);
2326
- this.pool.forEach(disposeWebImageRetries);
1532
+ Object.values(this.pool).forEach(elements => elements.forEach(disposeWebImageRetries));
2327
1533
  this.root.remove();
2328
1534
  this.indexRail.remove();
2329
1535
  this.hideReorderPreview();
2330
1536
  this.reorderPreview.remove();
2331
1537
  if (host) host.style.position = this.previousHostPosition;
2332
1538
  this.mounted.clear();
2333
- this.pool.length = 0;
1539
+ Object.values(this.pool).forEach(elements => {
1540
+ elements.length = 0;
1541
+ });
2334
1542
  this.avatarLeases.forEach(release => release());
2335
1543
  this.avatarLeases.clear();
2336
1544
  }
2337
1545
  setSnapshot(snapshot, selectedKeys) {
2338
1546
  this.cancelMarketPointer();
2339
- this.measuredWarningHeights.clear();
1547
+ this.measuredRendererHeights.clear();
2340
1548
  this.snapshot = snapshot;
2341
1549
  this.rows = effectiveRows(snapshot);
2342
1550
  this.selectedKeys = selectedKeys ?? selectionStateFromSnapshot(snapshot).selectedKeys;
@@ -2380,6 +1588,26 @@ export class NativeListWebEngine {
2380
1588
  this.indexRail.style.setProperty(name, value);
2381
1589
  this.reorderPreview.style.setProperty(name, value);
2382
1590
  });
1591
+ this.applyListStyle();
1592
+ }
1593
+
1594
+ /**
1595
+ * List chrome from `snapshot.listStyle`. Every value is absent by default and
1596
+ * the stylesheet carries today's number as the fallback, so an untouched list
1597
+ * renders exactly as before. See docs/STYLE_SPEC.md §5.
1598
+ */
1599
+ applyListStyle() {
1600
+ const listStyle = this.snapshot.listStyle;
1601
+ const inset = listStyle?.separator?.inset;
1602
+ setData(this.root, 'separatorInset', inset !== undefined && inset > 0);
1603
+ const chrome = {
1604
+ '--nl-separator-inset': inset === undefined ? undefined : String(inset) + 'px',
1605
+ '--nl-separator-color': listStyle?.separator?.color,
1606
+ '--nl-group-radius': listStyle?.groupCornerRadius === undefined ? undefined : String(listStyle.groupCornerRadius) + 'px'
1607
+ };
1608
+ Object.entries(chrome).forEach(([name, value]) => {
1609
+ if (value === undefined) this.root.style.removeProperty(name);else this.root.style.setProperty(name, value);
1610
+ });
2383
1611
  }
2384
1612
  recomputeLayout = () => {
2385
1613
  if (this.destroyed) return;
@@ -2395,16 +1623,16 @@ export class NativeListWebEngine {
2395
1623
  }
2396
1624
  if (this.lastViewportWidth >= 0 && (viewportWidth !== this.lastViewportWidth || viewportHeight !== this.lastViewportHeight)) {
2397
1625
  this.invalidateActionAnchor('layout');
2398
- this.measuredWarningHeights.clear();
1626
+ this.measuredRendererHeights.clear();
2399
1627
  }
2400
1628
  this.lastViewportWidth = viewportWidth;
2401
1629
  this.lastViewportHeight = viewportHeight;
2402
1630
  const previousHorizontal = this.layout.horizontal;
2403
1631
  const measuredSnapshot = {
2404
1632
  ...this.snapshot,
2405
- rows: this.snapshot.rows.map(row => row.type === 'system' && row.variant === 'warning' && row.height === undefined && this.measuredWarningHeights.has(row.key) ? {
1633
+ rows: this.snapshot.rows.map(row => row.style?.container?.height === undefined && row.height === undefined && this.measuredRendererHeights.get(row.key)?.signature === webRowRenderSignature(row) ? {
2406
1634
  ...row,
2407
- height: this.measuredWarningHeights.get(row.key)
1635
+ height: this.measuredRendererHeights.get(row.key).height
2408
1636
  } : row)
2409
1637
  };
2410
1638
  this.layout = computeWebListLayout(measuredSnapshot, viewportWidth, viewportHeight, this.reorderCompactKey);
@@ -2460,12 +1688,15 @@ export class NativeListWebEngine {
2460
1688
  const visible = webLayoutItemsForMount(this.layout, this.currentOffset(), viewportLength, this.virtualizationEnabled, viewportLength * OVERSCAN_VIEWPORTS);
2461
1689
  const desired = new Set(visible.map(item => item.index));
2462
1690
  this.mounted.forEach((element, index) => {
2463
- if (!desired.has(index)) {
1691
+ const nextRow = this.rows[index];
1692
+ const rendererKey = this.rendererKeys.get(element);
1693
+ if (!desired.has(index) || !nextRow || rendererKey !== rowRendererKey(nextRow)) {
2464
1694
  this.invalidateActionAnchorForElement(element);
2465
1695
  this.mounted.delete(index);
2466
- if (disposeWebImageRetries(element)) element.removeAttribute('data-render-signature');
1696
+ const recycled = element.firstElementChild ? recycleRowBody(element.firstElementChild) : false;
1697
+ if (disposeWebImageRetries(element) || recycled) element.removeAttribute('data-render-signature');
2467
1698
  element.remove();
2468
- this.pool.push(element);
1699
+ if (rendererKey) this.pool[rendererKey].push(element);
2469
1700
  }
2470
1701
  });
2471
1702
  visible.forEach(layoutItem => {
@@ -2473,7 +1704,9 @@ export class NativeListWebEngine {
2473
1704
  if (!row) return;
2474
1705
  let element = this.mounted.get(layoutItem.index);
2475
1706
  if (!element) {
2476
- element = this.pool.pop() ?? createElement(this.document, 'div', 'ok-native-list-item');
1707
+ const rendererKey = rowRendererKey(row);
1708
+ element = this.pool[rendererKey].pop() ?? createElement(this.document, 'div', 'ok-native-list-item');
1709
+ this.rendererKeys.set(element, rendererKey);
2477
1710
  setData(element, 'nativeListAnimateReorder', false);
2478
1711
  this.mounted.set(layoutItem.index, element);
2479
1712
  this.content.appendChild(element);
@@ -2485,17 +1718,23 @@ export class NativeListWebEngine {
2485
1718
  element.dataset.renderSignature = signature;
2486
1719
  }
2487
1720
  });
2488
- let measuredWarningChanged = false;
1721
+ let measuredHeightChanged = false;
2489
1722
  this.mounted.forEach((element, index) => {
2490
1723
  const row = this.rows[index];
2491
- if (row?.type !== 'system' || row.variant !== 'warning' || row.height !== undefined) return;
2492
- const height = element.querySelector('.ok-native-list-warning')?.offsetHeight ?? 0;
2493
- if (height > 0 && height !== this.measuredWarningHeights.get(row.key)) {
2494
- this.measuredWarningHeights.set(row.key, height);
2495
- measuredWarningChanged = true;
1724
+ const registered = row && rowRenderer(row);
1725
+ if (registered && element.firstElementChild) {
1726
+ const height = registered.measureRendered(element.firstElementChild);
1727
+ if (height !== undefined && (height !== this.measuredRendererHeights.get(row.key)?.height || webRowRenderSignature(row) !== this.measuredRendererHeights.get(row.key)?.signature)) {
1728
+ this.measuredRendererHeights.set(row.key, {
1729
+ signature: webRowRenderSignature(row),
1730
+ height
1731
+ });
1732
+ measuredHeightChanged = true;
1733
+ }
1734
+ return;
2496
1735
  }
2497
1736
  });
2498
- if (measuredWarningChanged) {
1737
+ if (measuredHeightChanged) {
2499
1738
  this.recomputeLayout();
2500
1739
  return;
2501
1740
  }
@@ -2509,7 +1748,10 @@ export class NativeListWebEngine {
2509
1748
  }
2510
1749
  renderElement(element, index, row, overlay = false) {
2511
1750
  this.invalidateActionAnchorForElement(element);
2512
- disposeWebImageRetries(element);
1751
+ const registered = rowRenderer(row);
1752
+ const existingBody = element.firstElementChild;
1753
+ const reusableBody = existingBody?.dataset.nlRenderer === registered.key ? existingBody : undefined;
1754
+ if (!reusableBody) disposeWebImageRetries(element);
2513
1755
  const bindingEpoch = String(++this.bindingEpochCounter);
2514
1756
  element.className = overlay ? 'ok-native-list-item ok-native-list-sticky' : 'ok-native-list-item';
2515
1757
  setData(element, 'nativeListRowKey', row.key);
@@ -2519,7 +1761,7 @@ export class NativeListWebEngine {
2519
1761
  setData(element, 'nativeListRowPressEnabled', isRowPressEnabled(row));
2520
1762
  setData(element, 'testid', row.testID);
2521
1763
  // OneKey patch: deprecation dims a row without disabling its actions.
2522
- element.style.opacity = String(row.opacity ?? 1);
1764
+ element.style.opacity = String((row.style?.container?.opacity ?? row.opacity ?? 1) * (row.disabled ? 0.5 : 1));
2523
1765
  setData(element, 'nativeListReorderable', this.isReorderable(row));
2524
1766
  setData(element, 'nativeListDragging', this.pointerReorder?.active && this.pointerReorder.sourceKey === row.key);
2525
1767
  setData(element, 'separator', row.separator);
@@ -2539,62 +1781,24 @@ export class NativeListWebEngine {
2539
1781
  selectedKeys: this.selectedKeys,
2540
1782
  itemIndex: index
2541
1783
  };
2542
- const body = createRowBody(context, row);
2543
- applySelectorTabularNumbers(body, row);
1784
+ const body = reusableBody ?? createRowBody(context, row);
1785
+ if (reusableBody) {
1786
+ resetRowContainerStyle(body);
1787
+ registered.bind(body, rendererPrimitives(context));
1788
+ }
2544
1789
  // OneKey patch: explicit selector fields preserve original page geometry.
2545
1790
  element.style.contain = row.backgroundFullWidth ? 'layout style' : '';
2546
1791
  if (row.backgroundColor) body.style.backgroundColor = row.backgroundColor;
2547
- if (row.backgroundFullWidth && row.backgroundColor) {
1792
+ const restingBackground = row.style?.container?.backgroundColor ?? row.backgroundColor;
1793
+ if (row.backgroundFullWidth && restingBackground) {
2548
1794
  const bleed = paddingValues(this.snapshot).horizontal;
2549
1795
  body.style.position = 'relative';
2550
1796
  body.style.overflow = 'visible';
2551
- body.style.boxShadow = String(-bleed) + 'px 0 ' + row.backgroundColor + ',' + String(bleed) + 'px 0 ' + row.backgroundColor;
2552
- }
2553
- if (row.type === 'identity' && row.height !== undefined) {
2554
- const title = body.querySelector('.ok-native-list-title');
2555
- if (row.presentation === 'accountSelector') {
2556
- body.style.gap = '12px';
2557
- body.style.borderRadius = '12px';
2558
- if ('shape' in row.leading && row.leading.shape === 'rounded') {
2559
- const visual = body.querySelector('.ok-native-list-visual');
2560
- if (visual) visual.style.borderRadius = '8px';
2561
- }
2562
- if (title) title.style.lineHeight = '24px';
2563
- }
2564
- if (row.presentation === 'networkSelector') {
2565
- body.style.borderRadius = '12px';
2566
- const visual = body.querySelector('.ok-native-list-visual');
2567
- if (visual) {
2568
- visual.style.width = '32px';
2569
- visual.style.height = '32px';
2570
- visual.style.flexBasis = '32px';
2571
- }
2572
- if (row.leading.kind === 'network' && !row.leading.image && !row.leading.fallbackIcon && row.leading.fallbackText) {
2573
- const fallback = visual?.querySelector('.ok-native-list-visual-fallback');
2574
- if (fallback) {
2575
- fallback.style.fontSize = '19px';
2576
- fallback.style.lineHeight = '27px';
2577
- fallback.style.fontWeight = '600';
2578
- fallback.style.color = 'var(--nl-inverse-text)';
2579
- }
2580
- }
2581
- visual?.querySelectorAll('.ok-native-list-visual-main').forEach(image => {
2582
- image.style.width = '32px';
2583
- image.style.height = '32px';
2584
- });
2585
- if (title) {
2586
- title.style.fontSize = '16px';
2587
- title.style.lineHeight = '24px';
2588
- title.style.fontWeight = '500';
2589
- }
2590
- body.querySelectorAll('.ok-native-list-accessory').forEach(value => {
2591
- value.style.fontSize = '16px';
2592
- value.style.lineHeight = '24px';
2593
- value.style.fontWeight = '500';
2594
- });
2595
- }
1797
+ body.style.boxShadow = String(-bleed) + 'px 0 ' + restingBackground + ',' + String(bleed) + 'px 0 ' + restingBackground;
2596
1798
  }
2597
- element.replaceChildren(body);
1799
+ // Template and presentation defaults must precede caller overrides.
1800
+ applyRowStyle(body, row);
1801
+ if (body.parentElement !== element) element.replaceChildren(body);
2598
1802
  if (row.type === 'market' && row.diagnostics?.imageBindActionKey && body.querySelector('img')) {
2599
1803
  this.callbacks.onRowAction?.({
2600
1804
  rowKey: row.key,
@@ -3128,6 +2332,8 @@ export class NativeListWebEngine {
3128
2332
  const memberKey = action.closest('[data-native-list-group-member-key]')?.dataset.nativeListGroupMemberKey;
3129
2333
  const sourceRow = row?.type === 'walletGroup' ? [row.parent, ...row.children].find(member => member.key === memberKey) ?? row : row;
3130
2334
  const actionKey = action.dataset.nativeListHoverAction === 'true' ? action.dataset.nativeListAction : action.dataset.nativeListHoverAction;
2335
+ // Legacy Web: hover checks only the resolved source (the member for a
2336
+ // WalletGroup), not the enclosing row's `disabled`.
3131
2337
  if (sourceRow && !sourceRow.disabled && actionKey) this.emitRowAction(sourceRow, actionKey, action, rowElement ?? undefined);
3132
2338
  };
3133
2339
  handleTitlePointerOut = event => {
@@ -3152,6 +2358,9 @@ export class NativeListWebEngine {
3152
2358
  const memberElement = target.closest('[data-native-list-group-member-key]');
3153
2359
  const memberKey = memberElement?.dataset.nativeListGroupMemberKey;
3154
2360
  const sourceRow = row.type === 'walletGroup' && memberKey ? [row.parent, ...row.children].find(member => member.key === memberKey) ?? row : row;
2361
+ // Legacy Web: a disabled row (or whole WalletGroup) blocks everything
2362
+ // above. A disabled WalletGroup member still emits its own actions, but its
2363
+ // member press is blocked by `isRowPressEnabled` in `handleRowPress`.
3155
2364
  const action = target.closest('[data-native-list-action]');
3156
2365
  if (action) {
3157
2366
  const scope = action.dataset.selectionScope;
@@ -3504,9 +2713,10 @@ export class NativeListWebEngine {
3504
2713
  const row = source?.firstElementChild;
3505
2714
  if (!source || !row) return;
3506
2715
  const rect = source.getBoundingClientRect();
3507
- const groupParent = row.querySelector('[data-native-list-group-parent="true"]>.ok-native-list-wallet-row');
3508
- const previewRow = groupParent ?? row;
3509
- const previewHeight = groupParent ? 68 : rect.height;
2716
+ const sourceRow = state.workingRows[state.currentIndex];
2717
+ const preview = sourceRow && rowRenderer(sourceRow).reorderPreview(row);
2718
+ const previewRow = preview?.element ?? row;
2719
+ const previewHeight = preview?.height ?? rect.height;
3510
2720
  state.previewOffsetX = state.startX - rect.left;
3511
2721
  state.previewOffsetY = Math.min(previewHeight, Math.max(0, state.startY - rect.top));
3512
2722
  this.reorderPreview.replaceChildren(previewRow.cloneNode(true));
@@ -3524,7 +2734,6 @@ export class NativeListWebEngine {
3524
2734
  }
3525
2735
  configureWebAvatar(image, avatar.source, avatar.uri);
3526
2736
  });
3527
- const sourceRow = state.workingRows[state.currentIndex];
3528
2737
  const badgeText = sourceRow ? webWalletGroupReorderBadge(sourceRow) : undefined;
3529
2738
  if (badgeText) {
3530
2739
  this.reorderPreview.appendChild(createElement(this.document, 'span', 'ok-native-list-reorder-count', badgeText));
@@ -3826,4 +3035,7 @@ export class NativeListWebEngine {
3826
3035
  });
3827
3036
  }
3828
3037
  }
3038
+ export function applyRowStyle(body, row) {
3039
+ applyRowContainerStyle(body, row);
3040
+ }
3829
3041
  //# sourceMappingURL=NativeListWebEngine.js.map