@lukso/transaction-view-headless 0.2.2-dev.a8c9315

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 (76) hide show
  1. package/.turbo/turbo-build.log +51 -0
  2. package/CHANGELOG.md +88 -0
  3. package/COMPONENT_ARCHITECTURE.md +190 -0
  4. package/COMPONENT_CONVERSION_ANALYSIS.md +371 -0
  5. package/CONTEXT_VARIANT_GUIDE.md +425 -0
  6. package/LAZY_LOADING_GUIDE.md +534 -0
  7. package/LICENSE +201 -0
  8. package/PLAYBACK_EXAMPLES.md +658 -0
  9. package/PLAYBACK_GUIDE.md +402 -0
  10. package/README.md +43 -0
  11. package/REGISTRY_SUMMARY.md +638 -0
  12. package/SUFFERING_ECONOMICS.md +346 -0
  13. package/SUFFERING_ECONOMICS.zip +0 -0
  14. package/VIEW_REGISTRATION_GUIDE.md +795 -0
  15. package/dist/composables/index.cjs +1321 -0
  16. package/dist/composables/index.cjs.map +1 -0
  17. package/dist/composables/index.d.cts +6 -0
  18. package/dist/composables/index.d.ts +6 -0
  19. package/dist/composables/index.js +1276 -0
  20. package/dist/composables/index.js.map +1 -0
  21. package/dist/index-CMLU1hKL.d.ts +374 -0
  22. package/dist/index-CSDjhiKV.d.cts +374 -0
  23. package/dist/index.cjs +1534 -0
  24. package/dist/index.cjs.map +1 -0
  25. package/dist/index.d.cts +93 -0
  26. package/dist/index.d.ts +93 -0
  27. package/dist/index.js +1463 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/registry/index.cjs +522 -0
  30. package/dist/registry/index.cjs.map +1 -0
  31. package/dist/registry/index.d.cts +46 -0
  32. package/dist/registry/index.d.ts +46 -0
  33. package/dist/registry/index.js +487 -0
  34. package/dist/registry/index.js.map +1 -0
  35. package/dist/types/index.cjs +19 -0
  36. package/dist/types/index.cjs.map +1 -0
  37. package/dist/types/index.d.cts +203 -0
  38. package/dist/types/index.d.ts +203 -0
  39. package/dist/types/index.js +1 -0
  40. package/dist/types/index.js.map +1 -0
  41. package/dist/utils/index.cjs +217 -0
  42. package/dist/utils/index.cjs.map +1 -0
  43. package/dist/utils/index.d.cts +47 -0
  44. package/dist/utils/index.d.ts +47 -0
  45. package/dist/utils/index.js +188 -0
  46. package/dist/utils/index.js.map +1 -0
  47. package/dist/view-loader-dD86QAlQ.d.cts +121 -0
  48. package/dist/view-loader-ii8AxiQR.d.ts +121 -0
  49. package/package.json +64 -0
  50. package/publish.log +0 -0
  51. package/src/composables/index.ts +59 -0
  52. package/src/composables/use-image-loader.ts +315 -0
  53. package/src/composables/use-transaction-playback.ts +396 -0
  54. package/src/composables/use-transaction-view.ts +309 -0
  55. package/src/config/lukso-config.ts +171 -0
  56. package/src/examples/context-aware-views.ts +300 -0
  57. package/src/examples/example-views.ts +197 -0
  58. package/src/examples/lit-plus-rn-strategy.ts +448 -0
  59. package/src/examples/lsp7-custom-matcher-examples.ts +174 -0
  60. package/src/examples/opt-in-context-examples.ts +372 -0
  61. package/src/examples/optimized-view-definitions.ts +408 -0
  62. package/src/examples/vue-to-lit-compilation.ts +358 -0
  63. package/src/hooks/useAddressQuery.ts +135 -0
  64. package/src/hooks/useAddressResolution.ts +437 -0
  65. package/src/index.ts +36 -0
  66. package/src/registry/index.ts +13 -0
  67. package/src/registry/view-loader.ts +305 -0
  68. package/src/registry/view-registry.ts +135 -0
  69. package/src/types/index.ts +16 -0
  70. package/src/types/view-contexts.ts +72 -0
  71. package/src/types/view-matching.ts +173 -0
  72. package/src/utils/context-matcher.ts +165 -0
  73. package/src/utils/index.ts +5 -0
  74. package/src/utils/view-matcher.ts +338 -0
  75. package/tsconfig.json +20 -0
  76. package/tsup.config.ts +34 -0
@@ -0,0 +1,638 @@
1
+ # Transaction View Registry - Complete System
2
+
3
+ ## Overview
4
+
5
+ The view registry system provides a flexible, framework-agnostic way to register and select transaction views with automatic priority for native implementations.
6
+
7
+ ## Key Features
8
+
9
+ ### 1. **Framework Priority System**
10
+ ```
11
+ Vue view: Base score + 0.15 bonus = Preferred in Vue apps
12
+ RN view: Base score + 0.15 bonus = Preferred in RN apps
13
+ Lit view: Base score + 0.00 bonus = Universal fallback
14
+ ```
15
+
16
+ ### 2. **Index-Based Playback**
17
+ ```
18
+ Single Transaction:
19
+ Index 0 → transaction
20
+ Index 1 → transaction (same)
21
+
22
+ Batch Transaction:
23
+ Index 0 → summary/expandable
24
+ Index 1 → children[0]
25
+ Index 2 → children[1]
26
+ ```
27
+
28
+ ### 3. **Automatic View Selection**
29
+ - Match by `standard`, `recordType`, `functionName`, `contractAddress`, `chainId`
30
+ - Context-aware matching (approval, feed, list, detail)
31
+ - Custom matcher functions
32
+ - Priority-based sorting
33
+
34
+ ## Complete Architecture
35
+
36
+ ```
37
+ ┌─────────────────────────────────────────────────────────┐
38
+ │ Your Application │
39
+ ├─────────────────────────────────────────────────────────┤
40
+ │ │
41
+ │ 1. Register enhancers (decoder plugins) │
42
+ │ 2. Register views (view definitions) │
43
+ │ 3. Use playback component │
44
+ │ │
45
+ └─────────────────┬───────────────────────────────────────┘
46
+
47
+ ┌─────────────────▼───────────────────────────────────────┐
48
+ │ @lukso/transaction-view-headless │
49
+ │ │
50
+ │ ┌────────────────┐ ┌────────────────┐ │
51
+ │ │ View Registry │ │ View Matcher │ │
52
+ │ │ - register() │ │ - scoring │ │
53
+ │ │ - findMatch() │ │ - framework │ │
54
+ │ │ │ │ priority │ │
55
+ │ └────────────────┘ └────────────────┘ │
56
+ │ │
57
+ │ ┌────────────────────────────────────┐ │
58
+ │ │ Playback System │ │
59
+ │ │ - useTransactionPlayback() │ │
60
+ │ │ - createTransactionPlayback() │ │
61
+ │ │ - Index-based selection │ │
62
+ │ └────────────────────────────────────┘ │
63
+ └─────────────────┬───────────────────────────────────────┘
64
+
65
+ ┌─────────┼─────────┐
66
+ │ │ │
67
+ ┌───────▼───┐ ┌──▼─────┐ ┌─▼─────────┐
68
+ │ Lit │ │ Vue │ │React Native│
69
+ │ Component │ │Component│ │ Component │
70
+ └───────────┘ └────────┘ └────────────┘
71
+ ```
72
+
73
+ ## Quick Start
74
+
75
+ ### 1. Register Views at Startup
76
+
77
+ ```typescript
78
+ // main.ts
79
+ import { getGlobalRegistry } from '@lukso/transaction-view-headless'
80
+ import type { ViewDefinition } from '@lukso/transaction-view-headless'
81
+
82
+ const registry = getGlobalRegistry()
83
+
84
+ const myViews: ViewDefinition[] = [
85
+ {
86
+ id: 'lsp7-transfer-custom',
87
+ name: 'Custom LSP7 Transfer',
88
+ priority: 120,
89
+ criteria: {
90
+ standard: 'LSP7',
91
+ recordType: 'lsp7-transfer'
92
+ },
93
+ frameworks: {
94
+ lit: {
95
+ loader: { type: 'custom-element', tagName: 'custom-lsp7-view' },
96
+ component: 'custom-lsp7-view'
97
+ },
98
+ vue: {
99
+ loader: { type: 'dynamic-import', path: './CustomLSP7.vue' },
100
+ component: 'CustomLSP7'
101
+ }
102
+ }
103
+ }
104
+ ]
105
+
106
+ registry.registerMany(myViews)
107
+ ```
108
+
109
+ ### 2. Use Playback Component
110
+
111
+ ```vue
112
+ <template>
113
+ <TransactionPlayback
114
+ :transaction="transaction"
115
+ :index="1"
116
+ framework="vue"
117
+ />
118
+ </template>
119
+
120
+ <script setup lang="ts">
121
+ import { TransactionPlayback } from '@lukso/transaction-view-core-vue'
122
+ import type { DecoderResult } from '@lukso/transaction-decoder'
123
+
124
+ const props = defineProps<{
125
+ transaction: DecoderResult
126
+ }>()
127
+ </script>
128
+ ```
129
+
130
+ ## Framework Selection Logic
131
+
132
+ ```typescript
133
+ // Example: LSP7 transfer with both Vue and Lit views registered
134
+
135
+ // Vue app
136
+ const result = useTransactionPlayback(transaction, 1, {
137
+ framework: 'vue' // Selects Vue view (score + 0.15 bonus)
138
+ })
139
+
140
+ // Angular app
141
+ const result = useTransactionPlayback(transaction, 1, {
142
+ framework: 'lit' // Selects Lit view (universal fallback)
143
+ })
144
+
145
+ // React Native app
146
+ const result = useTransactionPlayback(transaction, 1, {
147
+ framework: 'rn' // Would select RN view if registered (score + 0.15)
148
+ // Falls back to Lit if no RN view
149
+ })
150
+ ```
151
+
152
+ ## Custom View Packages
153
+
154
+ Package structure for bundling views with enhancers:
155
+
156
+ ```
157
+ @myorg/custom-lukso-views/
158
+ ├── src/
159
+ │ ├── enhancers/ # Decoder plugins
160
+ │ ├── views/
161
+ │ │ ├── lit/ # Lit components (required)
162
+ │ │ ├── vue/ # Vue components (optional)
163
+ │ │ └── react-native/ # RN components (optional)
164
+ │ ├── registry/
165
+ │ │ ├── view-definitions.ts
166
+ │ │ └── register.ts
167
+ │ └── index.ts
168
+ └── package.json
169
+ ```
170
+
171
+ Usage:
172
+ ```typescript
173
+ import {
174
+ registerCustomViews,
175
+ getCustomEnhancers
176
+ } from '@myorg/custom-lukso-views'
177
+
178
+ // Register decoder enhancers
179
+ const decoder = initializeDecoder({
180
+ enhancers: getCustomEnhancers()
181
+ })
182
+
183
+ // Register views
184
+ registerCustomViews({
185
+ loadVue: true, // Load native Vue views
186
+ loadLit: true // Load Lit fallbacks
187
+ })
188
+ ```
189
+
190
+ ## View Selection Examples
191
+
192
+ ### Single Transaction
193
+
194
+ ```typescript
195
+ const playback = createTransactionPlayback(transaction, {
196
+ framework: 'vue'
197
+ })
198
+
199
+ // For single transaction:
200
+ console.log(playback.isBatch) // false
201
+ console.log(playback.children.length) // 0
202
+ console.log(playback.indexCount) // 2
203
+
204
+ const view0 = playback.getViewAtIndex(0) // Returns transaction
205
+ const view1 = playback.getViewAtIndex(1) // Returns transaction (same)
206
+ const view2 = playback.getViewAtIndex(2) // Returns null (invalid)
207
+ ```
208
+
209
+ ### Batch Transaction
210
+
211
+ ```typescript
212
+ const playback = createTransactionPlayback(batchTransaction, {
213
+ framework: 'vue'
214
+ })
215
+
216
+ // For batch with 3 children:
217
+ console.log(playback.isBatch) // true
218
+ console.log(playback.children.length) // 3
219
+ console.log(playback.indexCount) // 4 (0 + 3 children)
220
+
221
+ const view0 = playback.getViewAtIndex(0) // Returns batch summary
222
+ const view1 = playback.getViewAtIndex(1) // Returns children[0]
223
+ const view2 = playback.getViewAtIndex(2) // Returns children[1]
224
+ const view3 = playback.getViewAtIndex(3) // Returns children[2]
225
+ const view4 = playback.getViewAtIndex(4) // Returns null (invalid)
226
+ ```
227
+
228
+ ## Scoring System
229
+
230
+ ### Base Score (0-1)
231
+ Calculated from criteria matches:
232
+ - `recordType`: 0.25 weight
233
+ - `functionName`: 0.20 weight
234
+ - `context`: 0.20 weight
235
+ - `standard`: 0.15 weight
236
+ - `contractAddress`: 0.10 weight
237
+ - `customMatcher`: 0.05 weight (numeric scoring - see below)
238
+ - `contextRequirements`: 0.03 weight
239
+ - `chainId`: 0.02 weight
240
+
241
+ ### Custom Matcher Scoring
242
+ Custom matchers return a **match count** (0-N) which is normalized:
243
+ - `normalizedScore = Math.min(matchCount / 10, 1)`
244
+ - `finalScore = weight * normalizedScore`
245
+
246
+ **Example:**
247
+ ```typescript
248
+ customMatcher: (tx) => {
249
+ let matches = 0
250
+ if (tx.custom?.tokenType === 0) matches++ // +1
251
+ if (tx.custom?.isToken === true) matches++ // +1
252
+ if (BigInt(tx.value || 0) > 0) matches++ // +1
253
+ return matches // 0-3
254
+ }
255
+
256
+ // If matches = 2:
257
+ // normalizedScore = Math.min(2 / 10, 1) = 0.2
258
+ // contribution = 0.05 * 0.2 = 0.01 (added to total score)
259
+ ```
260
+
261
+ ### Framework Bonus
262
+ - Vue: +0.15
263
+ - React Native: +0.15
264
+ - Lit: +0.00
265
+
266
+ ### Priority Tiebreaker
267
+ When scores are equal, higher `priority` value wins.
268
+
269
+ ### Example Scoring
270
+
271
+ ```typescript
272
+ // View definitions
273
+ const vueView = {
274
+ id: 'lsp7-vue',
275
+ priority: 100,
276
+ criteria: { standard: 'LSP7', recordType: 'lsp7-transfer' },
277
+ frameworks: { lit: {...}, vue: {...} }
278
+ }
279
+
280
+ const litView = {
281
+ id: 'lsp7-lit',
282
+ priority: 100,
283
+ criteria: { standard: 'LSP7', recordType: 'lsp7-transfer' },
284
+ frameworks: { lit: {...} }
285
+ }
286
+
287
+ // Vue app selection
288
+ framework: 'vue'
289
+ vueView score: 0.40 (base) + 0.15 (bonus) = 0.55 ✅ SELECTED
290
+ litView score: 0.40 (base) + 0.00 (no bonus) = 0.40
291
+
292
+ // Angular app selection
293
+ framework: 'lit'
294
+ vueView: Not available (no 'lit' in Vue app's framework list)
295
+ litView score: 0.40 (base) + 0.00 (lit never gets bonus) = 0.40 ✅ SELECTED
296
+ ```
297
+
298
+ ## Transaction Metadata System
299
+
300
+ ### Overview
301
+ The `transaction.custom` field allows enhancers to populate metadata for view matching without requiring async operations during view selection.
302
+
303
+ ### Architecture Flow
304
+ ```
305
+ 1. Enhancer Phase (Async)
306
+ └─> Fetch contract metadata (LSP4TokenType, etc.)
307
+ └─> Cache by contract address
308
+ └─> Populate transaction.custom
309
+
310
+ 2. View Matching Phase (Sync)
311
+ └─> customMatcher reads transaction.custom
312
+ └─> Returns match count
313
+ └─> Score calculated and view selected
314
+ ```
315
+
316
+ ### Contract-Level Caching
317
+ When processing batches with multiple transactions to the same contract:
318
+
319
+ ```typescript
320
+ // 5 transactions to contract 0xABC...
321
+ Transaction 1 → cache.getOrSet() → RPC call → cache SET
322
+ Transaction 2 → cache.getOrSet() → cache HIT ✓
323
+ Transaction 3 → cache.getOrSet() → cache HIT ✓
324
+ Transaction 4 → cache.getOrSet() → cache HIT ✓
325
+ Transaction 5 → cache.getOrSet() → cache HIT ✓
326
+
327
+ Total RPC calls: 1 (not 5!)
328
+ ```
329
+
330
+ ### Custom Field Structure
331
+
332
+ ```typescript
333
+ // Populated by enhancer
334
+ result.custom = {
335
+ // Token metadata
336
+ tokenType: 0, // 0=Token, 1=NFT, 2=Collection
337
+ isToken: true,
338
+ isNFT: false,
339
+ isCollection: false,
340
+
341
+ // Operation metadata
342
+ operationType: 'mint', // 'mint' | 'burn' | 'transfer'
343
+
344
+ // Any custom data
345
+ myCustomFlag: true,
346
+ metadata: { /* ... */ }
347
+ }
348
+
349
+ // Accessed in view matcher
350
+ customMatcher: (tx) => {
351
+ let matches = 0
352
+ if (tx.custom?.tokenType === 0) matches++
353
+ if (tx.custom?.isToken === true) matches++
354
+ if (tx.custom?.myCustomFlag === true) matches++
355
+ return matches
356
+ }
357
+ ```
358
+
359
+ ### Real-World Example
360
+
361
+ ```typescript
362
+ // packages/decoder/src/decoder/plugins/enhanceLSP7DigitalAsset.ts
363
+ import { makeCacheKey, CacheKeyPrefix } from '../cache'
364
+ import { getPublicClient } from '../utils'
365
+
366
+ async function enhanceLSP7Metadata(
367
+ result: DecoderResult,
368
+ options: DecoderOptions
369
+ ): Promise<void> {
370
+ if (!options.cache || !result.to) return
371
+
372
+ const cacheKey = makeCacheKey(
373
+ CacheKeyPrefix.CONTRACT_METADATA,
374
+ result.to,
375
+ 'lsp4-token-type'
376
+ )
377
+
378
+ try {
379
+ const metadata = await options.cache.getOrSet(
380
+ cacheKey,
381
+ async () => {
382
+ const client = getPublicClient(options.chain)
383
+ const tokenTypeData = await client.readContract({
384
+ address: result.to,
385
+ abi: LSP7_ABI,
386
+ functionName: 'getData',
387
+ args: ['0xe0261fa95db2eb3b5439bd033cda66d56b96f92f243a8228fd87550ed7bdfdb3']
388
+ })
389
+
390
+ const typeValue = tokenTypeData ? parseInt(tokenTypeData as string, 16) : 0
391
+
392
+ return {
393
+ tokenType: typeValue,
394
+ isToken: typeValue === 0,
395
+ isNFT: typeValue === 1,
396
+ isCollection: typeValue === 2
397
+ }
398
+ },
399
+ { ttl: 1000 * 60 * 60 * 24 } // 24 hour cache
400
+ )
401
+
402
+ result.custom = result.custom || {}
403
+ Object.assign(result.custom, metadata)
404
+ } catch (error) {
405
+ // Set defaults on error
406
+ result.custom = result.custom || {}
407
+ result.custom.tokenType = 0
408
+ result.custom.isToken = true
409
+ }
410
+ }
411
+ ```
412
+
413
+ ### View Registration with Metadata
414
+
415
+ ```typescript
416
+ import type { ViewDefinition } from '@lukso/transaction-view-headless'
417
+
418
+ // Token-specific view
419
+ export const LSP7TokenTransferView: ViewDefinition = {
420
+ id: 'lsp7-token-transfer',
421
+ name: 'LSP7 Token Transfer',
422
+ priority: 100,
423
+ criteria: {
424
+ standard: 'LSP7',
425
+ functionName: 'transfer',
426
+ customMatcher: (tx) => {
427
+ let matches = 0
428
+ if (tx.custom?.tokenType === 0) matches++
429
+ if (tx.custom?.isToken === true) matches++
430
+ return matches
431
+ }
432
+ },
433
+ frameworks: {
434
+ lit: {
435
+ loader: { type: 'custom-element', tagName: 'lsp7-token-transfer-view' },
436
+ component: 'lsp7-token-transfer-view'
437
+ }
438
+ }
439
+ }
440
+
441
+ // NFT-specific view
442
+ export const LSP7NFTTransferView: ViewDefinition = {
443
+ id: 'lsp7-nft-transfer',
444
+ name: 'LSP7 NFT Transfer',
445
+ priority: 100,
446
+ criteria: {
447
+ standard: 'LSP7',
448
+ functionName: 'transfer',
449
+ customMatcher: (tx) => {
450
+ let matches = 0
451
+ if (tx.custom?.tokenType === 1) matches++
452
+ if (tx.custom?.isNFT === true) matches++
453
+ return matches
454
+ }
455
+ },
456
+ frameworks: {
457
+ lit: {
458
+ loader: { type: 'custom-element', tagName: 'lsp7-nft-transfer-view' },
459
+ component: 'lsp7-nft-transfer-view'
460
+ }
461
+ }
462
+ }
463
+
464
+ // Generic fallback (no customMatcher)
465
+ export const LSP7GenericTransferView: ViewDefinition = {
466
+ id: 'lsp7-generic-transfer',
467
+ name: 'LSP7 Transfer',
468
+ priority: 50, // Lower priority
469
+ criteria: {
470
+ standard: 'LSP7',
471
+ functionName: 'transfer'
472
+ // No customMatcher - matches all LSP7 transfers
473
+ },
474
+ frameworks: {
475
+ lit: {
476
+ loader: { type: 'custom-element', tagName: 'lsp7-transfer-view' },
477
+ component: 'lsp7-transfer-view'
478
+ }
479
+ }
480
+ }
481
+ ```
482
+
483
+ ### Matching Behavior
484
+
485
+ When a transaction is matched against these views:
486
+
487
+ ```typescript
488
+ // Transaction with metadata
489
+ const tx = {
490
+ standard: 'LSP7',
491
+ functionName: 'transfer',
492
+ custom: { tokenType: 0, isToken: true }
493
+ }
494
+
495
+ // Token view: customMatcher returns 2 → normalized 0.2 → score boosted
496
+ // NFT view: customMatcher returns 0 → no match
497
+ // Generic view: no customMatcher → base score only
498
+
499
+ // Winner: Token view (highest score)
500
+ ```
501
+
502
+ ```typescript
503
+ // Transaction without metadata
504
+ const tx = {
505
+ standard: 'LSP7',
506
+ functionName: 'transfer'
507
+ // custom field missing
508
+ }
509
+
510
+ // Token view: customMatcher returns 0 → no match
511
+ // NFT view: customMatcher returns 0 → no match
512
+ // Generic view: no customMatcher → matches with base criteria
513
+
514
+ // Winner: Generic view (fallback)
515
+ ```
516
+
517
+ ### Best Practices
518
+
519
+ 1. **Always provide fallback views** without customMatcher for graceful degradation
520
+ 2. **Cache by contract address** to avoid duplicate RPC calls in batches
521
+ 3. **Use descriptive metadata keys** (tokenType, isToken, etc.)
522
+ 4. **Set defaults on error** to ensure views still match
523
+ 5. **Keep customMatcher synchronous** - all async work in enhancers
524
+
525
+ ## API Reference
526
+
527
+ ### Registry API
528
+ ```typescript
529
+ const registry = getGlobalRegistry()
530
+
531
+ // Register views
532
+ registry.register(view)
533
+ registry.registerMany(views)
534
+
535
+ // Query views
536
+ registry.getView(id)
537
+ registry.getAllViews()
538
+ registry.findMatches(transaction, options)
539
+ registry.getBestMatch(transaction, options)
540
+
541
+ // Manage registry
542
+ registry.unregister(id)
543
+ registry.clear()
544
+ registry.getStats()
545
+ ```
546
+
547
+ ### Playback API
548
+ ```typescript
549
+ // Composable (single index)
550
+ const result = useTransactionPlayback(transaction, index, options)
551
+
552
+ // State (multiple indices)
553
+ const playback = createTransactionPlayback(transaction, options)
554
+ playback.getViewAtIndex(index)
555
+ playback.getAllViews()
556
+
557
+ // Helpers
558
+ isValidPlaybackIndex(transaction, index)
559
+ getValidPlaybackIndices(transaction)
560
+ ```
561
+
562
+ ## Documentation Files
563
+
564
+ - **[VIEW_REGISTRATION_GUIDE.md](./VIEW_REGISTRATION_GUIDE.md)** - Complete registration guide with custom package patterns
565
+ - **[PLAYBACK_GUIDE.md](./PLAYBACK_GUIDE.md)** - Playback system overview and API reference
566
+ - **[PLAYBACK_EXAMPLES.md](./PLAYBACK_EXAMPLES.md)** - Real-world code examples for Lit and Vue
567
+ - **[COMPONENT_ARCHITECTURE.md](../../docs/COMPONENT_ARCHITECTURE.md)** - System architecture overview
568
+
569
+ ## Benefits
570
+
571
+ 1. **Framework Flexibility**: Write once in Lit, use everywhere. Add native implementations when needed.
572
+ 2. **Priority System**: Native views automatically preferred over fallbacks.
573
+ 3. **Index-Based UI**: Maximum flexibility for batch display (summary, pagination, expansion).
574
+ 4. **Extensibility**: Easy to add custom views and enhancers in separate packages.
575
+ 5. **Type Safety**: Full TypeScript support throughout.
576
+ 6. **Performance**: Lazy loading, tree-shaking, and efficient scoring.
577
+
578
+ ## Migration Path
579
+
580
+ ### From Manual Component Selection
581
+ ```vue
582
+ <!-- Before -->
583
+ <LSP7Transfer v-if="isLSP7" />
584
+ <LSP8Transfer v-else-if="isLSP8" />
585
+ <GenericTransaction v-else />
586
+
587
+ <!-- After -->
588
+ <TransactionPlayback :transaction="transaction" :index="1" />
589
+ ```
590
+
591
+ ### From TransactionIframe
592
+ ```vue
593
+ <!-- Before -->
594
+ <TransactionIframe :transaction="transaction" />
595
+
596
+ <!-- After -->
597
+ <TransactionPlayback
598
+ :transaction="transaction"
599
+ :index="1"
600
+ framework="lit"
601
+ />
602
+ ```
603
+
604
+ ## Common Patterns
605
+
606
+ ### 1. Always Show Main Transaction
607
+ ```vue
608
+ <TransactionPlayback :index="1" />
609
+ ```
610
+
611
+ ### 2. Expandable Batch List
612
+ ```vue
613
+ <TransactionPlayback :index="1" />
614
+ <div v-if="expanded">
615
+ <TransactionPlayback
616
+ v-for="i in childIndices"
617
+ :index="i"
618
+ />
619
+ </div>
620
+ ```
621
+
622
+ ### 3. Context-Aware Selection
623
+ ```typescript
624
+ const index = computed(() => {
625
+ if (context === 'approval') return 1 // Skip summaries
626
+ if (context === 'detail' && isBatch) return 0 // Show summary
627
+ return 1 // Default: main transaction
628
+ })
629
+ ```
630
+
631
+ ## Next Steps
632
+
633
+ 1. **Register your views** using the patterns in VIEW_REGISTRATION_GUIDE.md
634
+ 2. **Use playback components** in your UI (see PLAYBACK_EXAMPLES.md)
635
+ 3. **Create custom packages** for reusable views + enhancers
636
+ 4. **Test with debug mode** to verify view selection
637
+
638
+ The system is designed to grow with your needs - start with Lit fallbacks, add native implementations when performance matters!