@oxyhq/bloom 1.13.0 → 1.14.1

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 (114) hide show
  1. package/README.md +1 -1
  2. package/docs/avatar.mdx +46 -5
  3. package/docs/index.mdx +1 -1
  4. package/docs/media-flight.mdx +15 -6
  5. package/docs/tab-bar.mdx +35 -0
  6. package/lib/commonjs/avatar/Avatar.js +24 -8
  7. package/lib/commonjs/avatar/Avatar.js.map +1 -1
  8. package/lib/commonjs/avatar/AvatarRing.js +13 -12
  9. package/lib/commonjs/avatar/AvatarRing.js.map +1 -1
  10. package/lib/commonjs/avatar/index.js +19 -0
  11. package/lib/commonjs/avatar/index.js.map +1 -1
  12. package/lib/commonjs/avatar/resolve-shape.js +43 -0
  13. package/lib/commonjs/avatar/resolve-shape.js.map +1 -0
  14. package/lib/commonjs/avatar/shape-paths.js +174 -0
  15. package/lib/commonjs/avatar/shape-paths.js.map +1 -0
  16. package/lib/commonjs/media-flight/MediaSurface.js +51 -10
  17. package/lib/commonjs/media-flight/MediaSurface.js.map +1 -1
  18. package/lib/commonjs/media-flight/constants.js +32 -7
  19. package/lib/commonjs/media-flight/constants.js.map +1 -1
  20. package/lib/commonjs/tab-bar/TabBarBase.js +87 -13
  21. package/lib/commonjs/tab-bar/TabBarBase.js.map +1 -1
  22. package/lib/commonjs/tab-bar/expo-router/RouterTabBar.js +5 -0
  23. package/lib/commonjs/tab-bar/expo-router/RouterTabBar.js.map +1 -1
  24. package/lib/commonjs/theme/color-scope/ColorScope.js +42 -12
  25. package/lib/commonjs/theme/color-scope/ColorScope.js.map +1 -1
  26. package/lib/commonjs/theme/color-scope/ColorScope.web.js +2 -8
  27. package/lib/commonjs/theme/color-scope/ColorScope.web.js.map +1 -1
  28. package/lib/module/avatar/Avatar.js +24 -8
  29. package/lib/module/avatar/Avatar.js.map +1 -1
  30. package/lib/module/avatar/AvatarRing.js +13 -12
  31. package/lib/module/avatar/AvatarRing.js.map +1 -1
  32. package/lib/module/avatar/index.js +1 -0
  33. package/lib/module/avatar/index.js.map +1 -1
  34. package/lib/module/avatar/resolve-shape.js +40 -0
  35. package/lib/module/avatar/resolve-shape.js.map +1 -0
  36. package/lib/module/avatar/shape-paths.js +170 -0
  37. package/lib/module/avatar/shape-paths.js.map +1 -0
  38. package/lib/module/media-flight/MediaSurface.js +52 -11
  39. package/lib/module/media-flight/MediaSurface.js.map +1 -1
  40. package/lib/module/media-flight/constants.js +31 -6
  41. package/lib/module/media-flight/constants.js.map +1 -1
  42. package/lib/module/tab-bar/TabBarBase.js +89 -15
  43. package/lib/module/tab-bar/TabBarBase.js.map +1 -1
  44. package/lib/module/tab-bar/expo-router/RouterTabBar.js +5 -0
  45. package/lib/module/tab-bar/expo-router/RouterTabBar.js.map +1 -1
  46. package/lib/module/theme/color-scope/ColorScope.js +45 -13
  47. package/lib/module/theme/color-scope/ColorScope.js.map +1 -1
  48. package/lib/module/theme/color-scope/ColorScope.web.js +3 -9
  49. package/lib/module/theme/color-scope/ColorScope.web.js.map +1 -1
  50. package/lib/typescript/commonjs/avatar/Avatar.d.ts.map +1 -1
  51. package/lib/typescript/commonjs/avatar/AvatarRing.d.ts +4 -3
  52. package/lib/typescript/commonjs/avatar/AvatarRing.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/avatar/index.d.ts +3 -1
  54. package/lib/typescript/commonjs/avatar/index.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/avatar/resolve-shape.d.ts +17 -0
  56. package/lib/typescript/commonjs/avatar/resolve-shape.d.ts.map +1 -0
  57. package/lib/typescript/commonjs/avatar/shape-paths.d.ts +27 -0
  58. package/lib/typescript/commonjs/avatar/shape-paths.d.ts.map +1 -0
  59. package/lib/typescript/commonjs/avatar/types.d.ts +30 -3
  60. package/lib/typescript/commonjs/avatar/types.d.ts.map +1 -1
  61. package/lib/typescript/commonjs/media-flight/MediaSurface.d.ts.map +1 -1
  62. package/lib/typescript/commonjs/media-flight/constants.d.ts +30 -6
  63. package/lib/typescript/commonjs/media-flight/constants.d.ts.map +1 -1
  64. package/lib/typescript/commonjs/media-flight/types.d.ts +7 -2
  65. package/lib/typescript/commonjs/media-flight/types.d.ts.map +1 -1
  66. package/lib/typescript/commonjs/tab-bar/TabBarBase.d.ts.map +1 -1
  67. package/lib/typescript/commonjs/tab-bar/expo-router/RouterTabBar.d.ts +5 -0
  68. package/lib/typescript/commonjs/tab-bar/expo-router/RouterTabBar.d.ts.map +1 -1
  69. package/lib/typescript/commonjs/tab-bar/types.d.ts +36 -5
  70. package/lib/typescript/commonjs/tab-bar/types.d.ts.map +1 -1
  71. package/lib/typescript/commonjs/theme/color-scope/ColorScope.d.ts +36 -2
  72. package/lib/typescript/commonjs/theme/color-scope/ColorScope.d.ts.map +1 -1
  73. package/lib/typescript/commonjs/theme/color-scope/ColorScope.web.d.ts +6 -2
  74. package/lib/typescript/commonjs/theme/color-scope/ColorScope.web.d.ts.map +1 -1
  75. package/lib/typescript/module/avatar/Avatar.d.ts.map +1 -1
  76. package/lib/typescript/module/avatar/AvatarRing.d.ts +4 -3
  77. package/lib/typescript/module/avatar/AvatarRing.d.ts.map +1 -1
  78. package/lib/typescript/module/avatar/index.d.ts +3 -1
  79. package/lib/typescript/module/avatar/index.d.ts.map +1 -1
  80. package/lib/typescript/module/avatar/resolve-shape.d.ts +17 -0
  81. package/lib/typescript/module/avatar/resolve-shape.d.ts.map +1 -0
  82. package/lib/typescript/module/avatar/shape-paths.d.ts +27 -0
  83. package/lib/typescript/module/avatar/shape-paths.d.ts.map +1 -0
  84. package/lib/typescript/module/avatar/types.d.ts +30 -3
  85. package/lib/typescript/module/avatar/types.d.ts.map +1 -1
  86. package/lib/typescript/module/media-flight/MediaSurface.d.ts.map +1 -1
  87. package/lib/typescript/module/media-flight/constants.d.ts +30 -6
  88. package/lib/typescript/module/media-flight/constants.d.ts.map +1 -1
  89. package/lib/typescript/module/media-flight/types.d.ts +7 -2
  90. package/lib/typescript/module/media-flight/types.d.ts.map +1 -1
  91. package/lib/typescript/module/tab-bar/TabBarBase.d.ts.map +1 -1
  92. package/lib/typescript/module/tab-bar/expo-router/RouterTabBar.d.ts +5 -0
  93. package/lib/typescript/module/tab-bar/expo-router/RouterTabBar.d.ts.map +1 -1
  94. package/lib/typescript/module/tab-bar/types.d.ts +36 -5
  95. package/lib/typescript/module/tab-bar/types.d.ts.map +1 -1
  96. package/lib/typescript/module/theme/color-scope/ColorScope.d.ts +36 -2
  97. package/lib/typescript/module/theme/color-scope/ColorScope.d.ts.map +1 -1
  98. package/lib/typescript/module/theme/color-scope/ColorScope.web.d.ts +6 -2
  99. package/lib/typescript/module/theme/color-scope/ColorScope.web.d.ts.map +1 -1
  100. package/package.json +1 -1
  101. package/src/avatar/Avatar.tsx +27 -10
  102. package/src/avatar/AvatarRing.tsx +21 -14
  103. package/src/avatar/index.ts +7 -0
  104. package/src/avatar/resolve-shape.ts +33 -0
  105. package/src/avatar/shape-paths.ts +150 -0
  106. package/src/avatar/types.ts +32 -3
  107. package/src/media-flight/MediaSurface.tsx +60 -14
  108. package/src/media-flight/constants.ts +31 -6
  109. package/src/media-flight/types.ts +7 -2
  110. package/src/tab-bar/TabBarBase.tsx +110 -17
  111. package/src/tab-bar/expo-router/RouterTabBar.tsx +5 -0
  112. package/src/tab-bar/types.ts +36 -5
  113. package/src/theme/color-scope/ColorScope.tsx +53 -12
  114. package/src/theme/color-scope/ColorScope.web.tsx +10 -8
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.NAMED_SHAPE_VIEW_BOX = exports.AVATAR_SHAPE_PATHS = exports.AVATAR_SHAPE_LABELS = void 0;
7
+ /**
8
+ * Named avatar outlines.
9
+ *
10
+ * Each entry is an SVG path drawn in a 100×100 square coordinate space, scaled
11
+ * at render time to the avatar's pixel size. That space is declared once in
12
+ * {@link NAMED_SHAPE_VIEW_BOX} rather than baked into the numbers, so the paths
13
+ * can be read and edited at human-friendly magnitudes. The built-in squircle
14
+ * lives in `squircle-path.ts` and uses a 0–1 space; both are supported because
15
+ * {@link AvatarShapePath} carries its own `viewBox`.
16
+ *
17
+ * `circle` is deliberately absent: a circular avatar is rendered with
18
+ * `borderRadius` and never reaches the SVG renderer.
19
+ */
20
+
21
+ /** Side length of the coordinate space every path below is drawn in. */
22
+ const NAMED_SHAPE_VIEW_BOX = exports.NAMED_SHAPE_VIEW_BOX = 100;
23
+ const AVATAR_SHAPE_PATHS = exports.AVATAR_SHAPE_PATHS = {
24
+ // --- Basic ---
25
+ square: 'M12 0H88Q100 0 100 12V88Q100 100 88 100H12Q0 100 0 88V12Q0 0 12 0Z',
26
+ slanted: 'M22 0H88Q100 0 100 12V78Q100 100 78 100H12Q0 100 0 88V22Q0 0 22 0Z',
27
+ arch: 'M50 0Q100 0 100 50V88Q100 100 88 100H12Q0 100 0 88V50Q0 0 50 0Z',
28
+ semicircle: 'M0 50A50 50 0 0 1 100 50V88Q100 100 88 100H12Q0 100 0 88Z',
29
+ // --- Organic ---
30
+ oval: 'M50 2C78 2 96 22 96 50S78 98 50 98S4 78 4 50S22 2 50 2Z',
31
+ pill: 'M34 4H66Q96 4 96 34V66Q96 96 66 96H34Q4 96 4 66V34Q4 4 34 4Z',
32
+ triangle: 'M50 5L95 90Q97 95 92 95H8Q3 95 5 90Z',
33
+ arrow: 'M50 0L100 45Q100 52 94 52H68V92Q68 100 58 100H42Q32 100 32 92V52H6Q0 52 0 45Z',
34
+ fan: 'M50 8Q90 8 96 48Q100 78 70 96Q50 108 30 96Q0 78 4 48Q10 8 50 8Z',
35
+ // --- Geometric ---
36
+ diamond: 'M50 2L96 50L50 98L4 50Z',
37
+ clamshell: 'M10 20Q10 4 26 4H74Q90 4 90 20V60Q90 96 50 96Q10 96 10 60Z',
38
+ pentagon: 'M50 2L97 36L79 96H21L3 36Z',
39
+ gem: 'M30 4H70L98 36L50 98L2 36Z',
40
+ // --- Sunny/Cookie ---
41
+ 'very-sunny': 'M50 0L58 20L78 6L72 28L96 22L82 42L100 50L82 58L96 78L72 72L78 94L58 80L50 100L42 80L22 94L28 72L4 78L18 58L0 50L18 42L4 22L28 28L22 6L42 20Z',
42
+ sunny: 'M50 4L60 24L80 10L74 32L98 28L84 48L100 50L84 52L98 72L74 68L80 90L60 76L50 96L40 76L20 90L26 68L2 72L16 52L0 50L16 48L2 28L26 32L20 10L40 24Z',
43
+ '4-sided-cookie': 'M50 2Q76 22 76 50Q76 78 50 98Q24 78 24 50Q24 22 50 2Z',
44
+ '6-sided-cookie': 'M50 2Q72 10 82 30Q92 52 80 74Q66 96 44 96Q22 96 10 74Q-2 52 10 30Q22 10 50 2Z',
45
+ '7-sided-cookie': 'M50 2Q70 6 84 22Q96 40 94 62Q90 82 74 94Q56 102 38 96Q20 88 10 70Q2 50 8 32Q18 10 50 2Z',
46
+ '9-sided-cookie': 'M50 2Q64 4 76 14Q86 26 92 42Q94 58 88 72Q78 86 64 94Q50 98 36 94Q22 86 12 72Q6 58 8 42Q14 26 24 14Q36 4 50 2Z',
47
+ '12-sided-cookie': 'M50 2L62 6L74 14L84 26L90 40L92 54L88 68L80 80L68 88L54 92L40 90L28 84L18 72L10 58L8 44L12 30L20 18L32 10L44 4Z',
48
+ // --- Nature ---
49
+ '4-leaf-clover': 'M50 4Q70 4 70 26Q96 26 96 50Q96 74 70 74Q70 96 50 96Q30 96 30 74Q4 74 4 50Q4 26 30 26Q30 4 50 4Z',
50
+ '8-leaf-clover': 'M50 2Q60 16 72 8Q84 16 80 30Q96 32 92 46Q100 60 86 66Q92 80 78 84Q76 98 60 92Q52 100 42 92Q28 98 24 84Q10 80 16 66Q2 60 10 46Q6 32 22 30Q18 16 30 8Q42 16 50 2Z',
51
+ burst: 'M50 0L56 30L80 6L62 34L100 26L70 44L100 50L70 56L100 74L62 66L80 94L56 70L50 100L44 70L20 94L38 66L0 74L30 56L0 50L30 44L0 26L38 34L20 6L44 30Z',
52
+ 'soft-burst': 'M50 4L58 28L78 8L66 32L96 24L76 44L98 50L76 56L96 76L66 68L78 92L58 72L50 96L42 72L22 92L34 68L4 76L24 56L2 50L24 44L4 24L34 32L22 8L42 28Z',
53
+ boom: 'M50 0L54 18L62 2L60 20L76 6L68 24L88 14L76 30L98 24L82 38L100 38L86 46L100 50L86 54L100 62L82 62L98 76L76 70L88 86L68 76L76 94L60 80L62 98L54 82L50 100L46 82L38 98L40 80L24 94L32 76L12 86L24 70L2 76L18 62L0 62L14 54L0 50L14 46L0 38L18 38L2 24L24 30L12 14L32 24L24 6L40 20L38 2L46 18Z',
54
+ 'soft-boom': 'M50 4L56 22L68 6L64 26L82 14L74 32L94 24L82 40L100 36L86 48L100 50L86 52L100 64L82 60L94 76L74 68L82 86L64 74L68 94L56 78L50 96L44 78L32 94L36 74L18 86L26 68L6 76L18 60L0 64L14 52L0 50L14 48L0 36L18 40L6 24L26 32L18 14L36 26L32 6L44 22Z',
55
+ flower: 'M50 4Q62 18 72 10Q82 18 76 32Q90 30 88 44Q98 50 88 56Q90 70 76 68Q82 82 72 90Q62 82 50 96Q38 82 28 90Q18 82 24 68Q10 70 12 56Q2 50 12 44Q10 30 24 32Q18 18 28 10Q38 18 50 4Z',
56
+ puffy: 'M34 6Q50-4 66 6Q84 6 90 22Q104 34 96 50Q104 66 90 78Q84 94 66 94Q50 104 34 94Q16 94 10 78Q-4 66 4 50Q-4 34 10 22Q16 6 34 6Z',
57
+ 'puffy-diamond': 'M50 2Q66 10 72 4Q86 12 84 28Q98 32 94 48Q100 64 86 70Q88 86 72 88Q62 98 48 94Q34 100 26 86Q12 88 8 72Q-2 62 6 48Q0 32 16 28Q14 12 28 4Q38 10 50 2Z',
58
+ // --- Fun ---
59
+ 'ghost-ish': 'M20 96V40Q20 4 50 4Q80 4 80 40V96L70 86L60 96L50 86L40 96L30 86Z',
60
+ 'pixel-circle': 'M30 4H70V4H86V14H96V30H100V70H96V86H86V96H70V100H30V96H14V86H4V70H0V30H4V14H14V4H30Z',
61
+ 'pixel-triangle': 'M42 4H58V18H70V32H82V46H94V60H100V100H0V60H6V46H18V32H30V18H42Z',
62
+ bun: 'M12 54Q-4 24 20 8Q44-6 68 8Q92 22 94 50Q96 72 76 88Q56 100 36 96Q14 90 12 54Z',
63
+ heart: 'M50 90C25 70 2 55 2 32Q2 10 24 6Q40 2 50 18Q60 2 76 6Q98 10 98 32Q98 55 75 70Z'
64
+ };
65
+
66
+ /**
67
+ * Display labels for the named shapes, in the order a picker should show them.
68
+ * Exported so consumers building a shape picker do not re-derive the order (or
69
+ * the capitalisation) themselves.
70
+ */
71
+ const AVATAR_SHAPE_LABELS = exports.AVATAR_SHAPE_LABELS = [{
72
+ name: 'square',
73
+ label: 'Square'
74
+ }, {
75
+ name: 'slanted',
76
+ label: 'Slanted'
77
+ }, {
78
+ name: 'arch',
79
+ label: 'Arch'
80
+ }, {
81
+ name: 'semicircle',
82
+ label: 'Semicircle'
83
+ }, {
84
+ name: 'oval',
85
+ label: 'Oval'
86
+ }, {
87
+ name: 'pill',
88
+ label: 'Pill'
89
+ }, {
90
+ name: 'triangle',
91
+ label: 'Triangle'
92
+ }, {
93
+ name: 'arrow',
94
+ label: 'Arrow'
95
+ }, {
96
+ name: 'fan',
97
+ label: 'Fan'
98
+ }, {
99
+ name: 'diamond',
100
+ label: 'Diamond'
101
+ }, {
102
+ name: 'clamshell',
103
+ label: 'Clamshell'
104
+ }, {
105
+ name: 'pentagon',
106
+ label: 'Pentagon'
107
+ }, {
108
+ name: 'gem',
109
+ label: 'Gem'
110
+ }, {
111
+ name: 'very-sunny',
112
+ label: 'Very Sunny'
113
+ }, {
114
+ name: 'sunny',
115
+ label: 'Sunny'
116
+ }, {
117
+ name: '4-sided-cookie',
118
+ label: '4-Sided Cookie'
119
+ }, {
120
+ name: '6-sided-cookie',
121
+ label: '6-Sided Cookie'
122
+ }, {
123
+ name: '7-sided-cookie',
124
+ label: '7-Sided Cookie'
125
+ }, {
126
+ name: '9-sided-cookie',
127
+ label: '9-Sided Cookie'
128
+ }, {
129
+ name: '12-sided-cookie',
130
+ label: '12-Sided Cookie'
131
+ }, {
132
+ name: '4-leaf-clover',
133
+ label: '4-Leaf Clover'
134
+ }, {
135
+ name: '8-leaf-clover',
136
+ label: '8-Leaf Clover'
137
+ }, {
138
+ name: 'burst',
139
+ label: 'Burst'
140
+ }, {
141
+ name: 'soft-burst',
142
+ label: 'Soft Burst'
143
+ }, {
144
+ name: 'boom',
145
+ label: 'Boom'
146
+ }, {
147
+ name: 'soft-boom',
148
+ label: 'Soft Boom'
149
+ }, {
150
+ name: 'flower',
151
+ label: 'Flower'
152
+ }, {
153
+ name: 'puffy',
154
+ label: 'Puffy'
155
+ }, {
156
+ name: 'puffy-diamond',
157
+ label: 'Puffy Diamond'
158
+ }, {
159
+ name: 'ghost-ish',
160
+ label: 'Ghost-ish'
161
+ }, {
162
+ name: 'pixel-circle',
163
+ label: 'Pixel Circle'
164
+ }, {
165
+ name: 'pixel-triangle',
166
+ label: 'Pixel Triangle'
167
+ }, {
168
+ name: 'bun',
169
+ label: 'Bun'
170
+ }, {
171
+ name: 'heart',
172
+ label: 'Heart'
173
+ }];
174
+ //# sourceMappingURL=shape-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NAMED_SHAPE_VIEW_BOX","exports","AVATAR_SHAPE_PATHS","square","slanted","arch","semicircle","oval","pill","triangle","arrow","fan","diamond","clamshell","pentagon","gem","sunny","burst","boom","flower","puffy","bun","heart","AVATAR_SHAPE_LABELS","name","label"],"sourceRoot":"../../../src","sources":["avatar/shape-paths.ts"],"mappings":"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","ignoreList":[]}
@@ -77,17 +77,58 @@ const MediaSurface = exports.MediaSurface = /*#__PURE__*/(0, _react.memo)(functi
77
77
  // supports.
78
78
  const [mountedSurfaceType] = (0, _react.useState)(surfaceType);
79
79
 
80
- // A slot rebuilt on every render republishes this surface to the layer on
81
- // every render, which nothing at runtime reports the picture is correct and
82
- // the app is doing work in proportion to how often its rows re-render. The
83
- // counter lives in an effect rather than in render: a render-phase ref write
84
- // makes the React Compiler bail on the whole component.
85
- const slotChurn = (0, _react.useRef)(0);
80
+ // A slot rebuilt on every render re-renders this surface and on web
81
+ // republishes the shared node every time the row it lives in renders, which
82
+ // nothing at runtime reports: the picture is correct and the app is just
83
+ // doing work in proportion to its render count.
84
+ //
85
+ // What is counted is a RUN of consecutive commits that each carried a new
86
+ // slot, NOT how many times the slot has ever changed. The lifetime count
87
+ // cannot tell the defect from a correctly memoised slot: Bloom's own
88
+ // documented example lists `isWatched` in its deps, so its identity changes
89
+ // once per activation of a long-lived surface, and any such consumer reaches
90
+ // any fixed lifetime total eventually. It would then be told to do the thing
91
+ // it is already doing, with no way to make the warning stop — which is worse
92
+ // than not warning, because the advice is unsatisfiable. A slot rebuilt per
93
+ // render is different in kind: it is new on EVERY commit, including the ones
94
+ // where nothing it reads changed.
95
+ //
96
+ // The run must also be FAST, and that second condition is what covers the
97
+ // consumer who memoised everything else too. With `content` and the rest
98
+ // stable, `memo` skips the renders where only the parent changed, so this
99
+ // component commits ONLY when the slot changes — every commit carries a new
100
+ // slot and the run never breaks, even though there is nothing to fix and no
101
+ // cost to pay. Rate is what tells them apart, and it is what the warning was
102
+ // ever about: a slot rebuilt per render churns as fast as its row renders,
103
+ // while one rebuilt when its state changes moves at the speed of a finger.
104
+ //
105
+ // All three refs are written from effects rather than in render: a
106
+ // render-phase ref write makes the React Compiler bail on the whole
107
+ // component. The first effect fires only when the identity changed, and the
108
+ // second — declared after it, and with no dependency array, so it runs on
109
+ // every commit — is what reads that mark and either extends the run or
110
+ // clears it.
111
+ const slotChangedInCommit = (0, _react.useRef)(false);
112
+ const slotChurnRun = (0, _react.useRef)(0);
113
+ const slotChurnRunStartedAt = (0, _react.useRef)(0);
86
114
  (0, _react.useEffect)(() => {
87
- if (renderVideo === undefined) return;
88
- slotChurn.current += 1;
89
- if (slotChurn.current === _constants.SLOT_IDENTITY_CHURN_LIMIT) warnSlotNotMemoised();
115
+ slotChangedInCommit.current = true;
90
116
  }, [renderVideo]);
117
+ (0, _react.useEffect)(() => {
118
+ if (renderVideo === undefined || !slotChangedInCommit.current) {
119
+ slotChurnRun.current = 0;
120
+ return;
121
+ }
122
+ slotChangedInCommit.current = false;
123
+ const now = Date.now();
124
+ if (slotChurnRun.current === 0 || now - slotChurnRunStartedAt.current > _constants.SLOT_IDENTITY_CHURN_WINDOW_MS) {
125
+ slotChurnRun.current = 1;
126
+ slotChurnRunStartedAt.current = now;
127
+ return;
128
+ }
129
+ slotChurnRun.current += 1;
130
+ if (slotChurnRun.current === _constants.SLOT_IDENTITY_CHURN_LIMIT) warnSlotNotMemoised();
131
+ });
91
132
 
92
133
  // Both arms report the same fact — "there is a picture here now" — because the
93
134
  // destination of a flight can be either, and a caller should not have to know
@@ -172,7 +213,7 @@ function warnSlotNotMemoised() {
172
213
  if (process.env.NODE_ENV === 'production' || hasWarnedAboutSlotChurn) return;
173
214
  hasWarnedAboutSlotChurn = true;
174
215
  // eslint-disable-next-line no-console
175
- console.warn(`[Bloom] A \`renderVideo\` slot changed identity ${_constants.SLOT_IDENTITY_CHURN_LIMIT} times. ` + 'It is part of what a media host publishes to the flight layer and is compared by ' + 'identity, so a slot rebuilt on every render republishes the surface on every render. ' + 'Wrap it in `useCallback` with the props it actually reads.');
216
+ console.warn(`[Bloom] A \`renderVideo\` slot was a NEW function on ${_constants.SLOT_IDENTITY_CHURN_LIMIT} ` + 'consecutive renders, including ones where nothing it reads changed. The slot is ' + 'compared by identity — on web it is part of what a media host publishes to the ' + 'flight layer — so one rebuilt per render re-renders the surface per render. Wrap ' + 'it in `useCallback` with the props it actually reads. A slot that changes when ' + 'those props change is fine and does not reach this.');
176
217
  }
177
218
 
178
219
  /**
@@ -1 +1 @@
1
- {"version":3,"names":["_react","require","_reactNative","_expoImage","_reactNativeReanimated","_interopRequireDefault","_constants","_store","_expoVideoModule","_jsxRuntime","e","__esModule","default","webDraggableProps","Platform","OS","draggable","MediaSurface","exports","memo","content","style","contentFit","surfaceType","renderVideo","nativeControls","accessibilityLabel","pointerEvents","flightId","detached","mountedSurfaceType","useState","slotChurn","useRef","useEffect","undefined","current","SLOT_IDENTITY_CHURN_LIMIT","warnSlotNotMemoised","reportLive","useCallback","handOffFlight","still","kind","poster","uri","expoVideo","loadExpoVideo","warnExpoVideoUnavailable","jsxs","View","styles","box","children","jsx","Image","source","StyleSheet","absoluteFill","transition","onLoad","player","fillReplaced","VideoView","onFirstFrameRender","displayName","hasWarnedAboutSlotChurn","process","env","NODE_ENV","console","warn","MediaPoster","EmptyMediaSurface","empty","create","width","height","overflow","backgroundColor"],"sourceRoot":"../../../src","sources":["media-flight/MediaSurface.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","require","_reactNative","_expoImage","_reactNativeReanimated","_interopRequireDefault","_constants","_store","_expoVideoModule","_jsxRuntime","e","__esModule","default","webDraggableProps","Platform","OS","draggable","MediaSurface","exports","memo","content","style","contentFit","surfaceType","renderVideo","nativeControls","accessibilityLabel","pointerEvents","flightId","detached","mountedSurfaceType","useState","slotChangedInCommit","useRef","slotChurnRun","slotChurnRunStartedAt","useEffect","current","undefined","now","Date","SLOT_IDENTITY_CHURN_WINDOW_MS","SLOT_IDENTITY_CHURN_LIMIT","warnSlotNotMemoised","reportLive","useCallback","handOffFlight","still","kind","poster","uri","expoVideo","loadExpoVideo","warnExpoVideoUnavailable","jsxs","View","styles","box","children","jsx","Image","source","StyleSheet","absoluteFill","transition","onLoad","player","fillReplaced","VideoView","onFirstFrameRender","displayName","hasWarnedAboutSlotChurn","process","env","NODE_ENV","console","warn","MediaPoster","EmptyMediaSurface","empty","create","width","height","overflow","backgroundColor"],"sourceRoot":"../../../src","sources":["media-flight/MediaSurface.tsx"],"mappings":"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","ignoreList":[]}
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.SURFACE_MOUNT_TIMEOUT_MS = exports.SLOT_IDENTITY_CHURN_LIMIT = exports.SLOT_FILL_TOLERANCE_PX = void 0;
6
+ exports.SURFACE_MOUNT_TIMEOUT_MS = exports.SLOT_IDENTITY_CHURN_WINDOW_MS = exports.SLOT_IDENTITY_CHURN_LIMIT = exports.SLOT_FILL_TOLERANCE_PX = void 0;
7
7
  /**
8
8
  * How long `flyTo` will wait for `<MediaFlightLayer>` to commit the surface
9
9
  * before letting the caller carry on regardless (ms).
@@ -38,16 +38,41 @@ const SURFACE_MOUNT_TIMEOUT_MS = exports.SURFACE_MOUNT_TIMEOUT_MS = 250;
38
38
  */
39
39
 
40
40
  /**
41
- * How many times a video slot may change identity before Bloom says so.
41
+ * How many CONSECUTIVE renders a video slot may arrive new on before Bloom says
42
+ * so.
42
43
  *
43
- * `renderVideo` is part of what a host publishes to the layer and is compared
44
- * by identity, so an unmemoised slot republishes on every render of the row it
45
- * lives in silently, and only under load. Five is well past any legitimate
46
- * cause (a mute toggle, a fit change) and far below the churn of a slot rebuilt
47
- * per render in a scrolling feed.
44
+ * `renderVideo` is compared by identity and on web it is part of what a host
45
+ * publishes to the layer — so an unmemoised slot re-renders the surface on
46
+ * every render of the row it lives in, silently and only under load.
47
+ *
48
+ * Consecutive, never a lifetime total. A correctly memoised slot changes
49
+ * identity whenever the state it reads changes (`isWatched` on a reel slide,
50
+ * say), so ANY fixed lifetime total is reached eventually by a consumer with
51
+ * nothing to fix — and the warning's own advice, `useCallback` over the props
52
+ * the slot reads, is what such a consumer already wrote. Five in a row is
53
+ * unreachable that way: it takes five commits where nothing the slot reads
54
+ * changed and the slot was rebuilt anyway.
48
55
  */
49
56
  const SLOT_IDENTITY_CHURN_LIMIT = exports.SLOT_IDENTITY_CHURN_LIMIT = 5;
50
57
 
58
+ /**
59
+ * How long a run of {@link SLOT_IDENTITY_CHURN_LIMIT} new slots may take and
60
+ * still be reported (ms).
61
+ *
62
+ * "Consecutive" alone is not enough for the consumer who memoised the surface's
63
+ * other props as well: `memo` then skips every render where only the parent
64
+ * changed, so the surface commits ONLY when the slot changes and the run never
65
+ * breaks — with nothing to fix and, because the renders were skipped, no cost
66
+ * paid either.
67
+ *
68
+ * Rate is what separates them, and it is what the warning was always about. A
69
+ * slot rebuilt per render changes as fast as its row renders, which in a
70
+ * scrolling feed is five times in a fraction of a frame budget. A slot rebuilt
71
+ * when the state it reads changes moves at the speed of a finger, and half a
72
+ * second is far too short for five activations of ONE surface.
73
+ */
74
+ const SLOT_IDENTITY_CHURN_WINDOW_MS = exports.SLOT_IDENTITY_CHURN_WINDOW_MS = 500;
75
+
51
76
  /**
52
77
  * How far a media element may sit from the box it is supposed to fill, in px,
53
78
  * before Bloom says so.
@@ -1 +1 @@
1
- {"version":3,"names":["SURFACE_MOUNT_TIMEOUT_MS","exports","SLOT_IDENTITY_CHURN_LIMIT","SLOT_FILL_TOLERANCE_PX"],"sourceRoot":"../../../src","sources":["media-flight/constants.ts"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMA,wBAAwB,GAAAC,OAAA,CAAAD,wBAAA,GAAG,GAAG;;AAE3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAME,yBAAyB,GAAAD,OAAA,CAAAC,yBAAA,GAAG,CAAC;;AAE1C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,sBAAsB,GAAAF,OAAA,CAAAE,sBAAA,GAAG,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["SURFACE_MOUNT_TIMEOUT_MS","exports","SLOT_IDENTITY_CHURN_LIMIT","SLOT_IDENTITY_CHURN_WINDOW_MS","SLOT_FILL_TOLERANCE_PX"],"sourceRoot":"../../../src","sources":["media-flight/constants.ts"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMA,wBAAwB,GAAAC,OAAA,CAAAD,wBAAA,GAAG,GAAG;;AAE3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAME,yBAAyB,GAAAD,OAAA,CAAAC,yBAAA,GAAG,CAAC;;AAE1C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,6BAA6B,GAAAF,OAAA,CAAAE,6BAAA,GAAG,GAAG;;AAEhD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,sBAAsB,GAAAH,OAAA,CAAAG,sBAAA,GAAG,CAAC","ignoreList":[]}
@@ -49,6 +49,7 @@ function TabBarBody({
49
49
  Blur,
50
50
  children,
51
51
  activeIndex,
52
+ activeProgress,
52
53
  onIndexChange,
53
54
  onIndexLongPress,
54
55
  theme: themeOverrides,
@@ -128,6 +129,28 @@ function TabBarBody({
128
129
  // a legitimate effect (external-system sync), the same shape `Tabs` uses for
129
130
  // its underline. The focus-driven path is handled per-button instead — see
130
131
  // `TabBarButtonBody`.
132
+ // DRIVEN path: a pager owns the highlight's position and writes it every
133
+ // frame, so the position is COPIED rather than sprung — a spring chasing a
134
+ // per-frame value lags behind the finger, which is the whole thing this path
135
+ // exists to avoid.
136
+ //
137
+ // `useAnimatedReaction` cannot be called conditionally, so an unsupplied
138
+ // `activeProgress` is stood in for by a shared value nothing ever writes and
139
+ // the reaction returns early. `driven` changing flips the identity of `source`
140
+ // too, and both are in the deps array, so the reaction re-registers either
141
+ // way.
142
+ //
143
+ // Scrubbing still wins: a finger on the BAR is a direct manipulation of the
144
+ // bar and must not be fought by the pager's settle animation.
145
+ // (Deps: see the CRITICAL note below.)
146
+ const idleProgress = (0, _reactNativeReanimated.useSharedValue)(0);
147
+ const driven = activeProgress !== undefined;
148
+ const source = activeProgress ?? idleProgress;
149
+ (0, _reactNativeReanimated.useAnimatedReaction)(() => source.value, value => {
150
+ if (!driven) return;
151
+ if (isDragging.value) return;
152
+ slideIndex.value = value;
153
+ }, [source, driven, isDragging, slideIndex]);
131
154
  (0, _react.useEffect)(() => {
132
155
  if (activeIndex === undefined) return;
133
156
  // While scrubbing the finger owns the highlight; never fight it.
@@ -148,15 +171,28 @@ function TabBarBody({
148
171
  // stale index would animate out of a position the user never saw, and would
149
172
  // light up every tab in between on the way. Interrupted mid-fade it is
150
173
  // still on screen, so from there it slides as it always does.
151
- slideIndex.value = highlightOpacity.value === 0 ? activeIndex : (0, _reactNativeReanimated.withSpring)(activeIndex, _shared.SLIDE_SPRING);
174
+ //
175
+ // On the DRIVEN path this effect keeps owning visibility and gives up the
176
+ // position: the driver is already writing `slideIndex` every frame, and a
177
+ // spring started here would drag the capsule across the bar between two of
178
+ // its writes.
179
+ if (!driven) {
180
+ slideIndex.value = highlightOpacity.value === 0 ? activeIndex : (0, _reactNativeReanimated.withSpring)(activeIndex, _shared.SLIDE_SPRING);
181
+ }
152
182
  highlightOpacity.value = (0, _reactNativeReanimated.withTiming)(1, _shared.HIGHLIGHT_FADE);
153
- }, [activeIndex, hasSelection, slideIndex, highlightOpacity, isDragging]);
183
+ }, [activeIndex, hasSelection, driven, slideIndex, highlightOpacity, isDragging]);
154
184
 
155
185
  // Scrubbing: the highlight tracks the finger 1:1 while dragging (no spring —
156
186
  // it must feel attached), haptic ticks fire on boundary crossings, and
157
187
  // navigation happens only on release. Taps are handled by a Tap gesture
158
- // racing the pan — the detector consumes the bar's touches, so the inner
159
- // Pressables never receive them.
188
+ // racing the pan.
189
+ //
190
+ // THE DETECTOR DOES NOT CONSUME THE PRESS, which this file assumed for a long
191
+ // time. A `Tap` gesture activates on RELEASE, by which point the inner
192
+ // `Pressable` has already fired `onPress`, and RNGH's cancellation of the RN
193
+ // responder comes too late to stop it. Measured on a Pixel 10 Pro: ONE tap
194
+ // produced both, ~600ms apart on a busy JS thread, so every consumer got two
195
+ // selections per tap. `TabBarButtonBody` is where that is resolved.
160
196
  const gesture = (0, _react.useMemo)(() => {
161
197
  const indexAtX = (x, minimizedValue) => {
162
198
  'worklet';
@@ -216,7 +252,14 @@ function TabBarBody({
216
252
  const index = Math.round(indexAtX(event.x, progress.value));
217
253
  // Same rule as the controlled path above: appear at the tapped tab when
218
254
  // hidden, slide to it when already on screen.
219
- slideIndex.value = highlightOpacity.value === 0 ? index : (0, _reactNativeReanimated.withSpring)(index, _shared.SLIDE_SPRING);
255
+ //
256
+ // A tap is a discrete writer, so on the DRIVEN path it stands down like
257
+ // the others and the driver carries the highlight to the tapped tab.
258
+ // The scrub is the one gesture that does not: a finger dragging the
259
+ // pill is manipulating the bar itself, not asking for a page.
260
+ if (!driven) {
261
+ slideIndex.value = highlightOpacity.value === 0 ? index : (0, _reactNativeReanimated.withSpring)(index, _shared.SLIDE_SPRING);
262
+ }
220
263
  highlightOpacity.value = (0, _reactNativeReanimated.withTiming)(1, _shared.HIGHLIGHT_FADE);
221
264
  (0, _context.setMinimized)(minimized, 0);
222
265
  (0, _reactNativeReanimated.runOnJS)(selectIndex)(index);
@@ -236,7 +279,7 @@ function TabBarBody({
236
279
  (0, _reactNativeReanimated.runOnJS)(longPressIndex)(Math.round(indexAtX(event.x, progress.value)));
237
280
  });
238
281
  return _reactNativeGestureHandler.Gesture.Race(pan, tap, longPress);
239
- }, [barOuterWidth, tabCount, selectIndex, hasLongPress, longPressIndex, tick, isDragging, lastTicked, slideIndex, highlightOpacity, minimized, progress]);
282
+ }, [barOuterWidth, tabCount, selectIndex, hasLongPress, longPressIndex, tick, driven, isDragging, lastTicked, slideIndex, highlightOpacity, minimized, progress]);
240
283
 
241
284
  // CRITICAL — every shared value and every scalar a mapper READS must appear
242
285
  // in its dependency array. On web WITHOUT the react-native-worklets babel
@@ -340,8 +383,9 @@ function TabBarBody({
340
383
  isDragging,
341
384
  theme,
342
385
  activeIndex,
386
+ driven,
343
387
  selectIndex
344
- }), [slideIndex, highlightOpacity, isDragging, theme, activeIndex, selectIndex]);
388
+ }), [slideIndex, highlightOpacity, isDragging, theme, activeIndex, driven, selectIndex]);
345
389
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
346
390
  ...viewProps,
347
391
  pointerEvents: "box-none",
@@ -386,6 +430,10 @@ function TabBarBody({
386
430
  });
387
431
  }
388
432
  /** One tab trigger: icon + label that fades out when the bar minimizes. */
433
+ /** Stable identity: a new array each render would re-send the actions to the OS. */
434
+ const ACTIVATE_ACTIONS = [{
435
+ name: 'activate'
436
+ }];
389
437
  function TabBarButtonBody({
390
438
  Glyph,
391
439
  item,
@@ -415,6 +463,10 @@ function TabBarButtonBody({
415
463
  (0, _react.useEffect)(() => {
416
464
  if (isFocused === undefined || !isFocused || !bar) return;
417
465
  if (bar.isDragging.value) return;
466
+ // A driver owns the position — see `TabBarProps.activeProgress`. It is
467
+ // already tracking this same focus change; springing from here as well would
468
+ // be a second writer on one shared value.
469
+ if (bar.driven) return;
418
470
  bar.slideIndex.value = (0, _reactNativeReanimated.withSpring)(index, _shared.SLIDE_SPRING);
419
471
  }, [isFocused, index, bar]);
420
472
 
@@ -471,20 +523,42 @@ function TabBarButtonBody({
471
523
  "aria-selected": focused,
472
524
  accessibilityLabel: item.label,
473
525
  ...pressableProps,
526
+ // ASSISTIVE TECHNOLOGY, on native, is the one activation the bar's gesture
527
+ // cannot see: TalkBack and VoiceOver activate a view without ever
528
+ // producing a touch, so the `Tap` gesture never runs and the selection has
529
+ // to come from here.
530
+ accessibilityActions: ACTIVATE_ACTIONS,
531
+ onAccessibilityAction: event => {
532
+ if (event.nativeEvent.actionName !== 'activate') return;
533
+ if (isFocused === undefined) bar?.selectIndex(index);
534
+ },
474
535
  onPress: event => {
475
536
  // The bar's GestureDetector normally consumes touches; this still fires
476
537
  // for assistive-technology activation (VoiceOver) and keyboard focus.
477
538
  if (bar) {
478
539
  // Appear at the tab when hidden, slide to it when visible — the same
479
- // rule the tap gesture and the controlled path follow.
480
- bar.slideIndex.value = bar.highlightOpacity.value === 0 ? index : (0, _reactNativeReanimated.withSpring)(index, _shared.SLIDE_SPRING);
540
+ // rule the tap gesture and the controlled path follow. Skipped on the
541
+ // driven path, where the position belongs to the driver and this
542
+ // press will reach it as an ordinary selection.
543
+ if (!bar.driven) {
544
+ bar.slideIndex.value = bar.highlightOpacity.value === 0 ? index : (0, _reactNativeReanimated.withSpring)(index, _shared.SLIDE_SPRING);
545
+ }
481
546
  bar.highlightOpacity.value = (0, _reactNativeReanimated.withTiming)(1, _shared.HIGHLIGHT_FADE);
482
547
  }
483
548
  (0, _context.setMinimized)(minimized, 0);
484
- // Controlled path only. On the focus-driven path the trigger's own
485
- // `onPress` below performs the navigation, so reporting the selection
486
- // here as well would navigate twice.
487
- if (isFocused === undefined) bar?.selectIndex(index);
549
+ // Controlled path only, and NOT on Android. On the focus-driven path the
550
+ // trigger's own `onPress` below performs the navigation, so reporting
551
+ // the selection here as well would navigate twice.
552
+ //
553
+ // ANDROID IS THE ONE PLATFORM THAT DOUBLES. RNGH sets
554
+ // `cancelsTouchesInView` on iOS, so a recognised tap cancels this press
555
+ // and the gesture above is the only reporter there; react-native-web
556
+ // likewise routes a keyboard Enter or Space through this press and
557
+ // nothing else. Android cancels nothing — measured on a Pixel 10 Pro,
558
+ // one tap produced `PRESSABLE selectIndex(1)` AND `GESTURE tap
559
+ // selectIndex(1)` ~600ms apart on a busy JS thread — so there, and only
560
+ // there, this press stands down.
561
+ if (isFocused === undefined && _reactNative.Platform.OS !== 'android') bar?.selectIndex(index);
488
562
  onPress?.(event);
489
563
  }
490
564
  // `Pressable`'s `style` also accepts a function of the press state; both