@buoy-gg/images 7.0.41 → 7.0.43

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 (194) hide show
  1. package/README.md +11 -70
  2. package/lib/commonjs/autoCapture.js +1 -4
  3. package/lib/commonjs/autoCapture.web.js +1 -6
  4. package/lib/commonjs/capture/actions.js +1 -243
  5. package/lib/commonjs/capture/install.js +1 -45
  6. package/lib/commonjs/capture/install.web.js +1 -250
  7. package/lib/commonjs/capture/installExpoImage.js +1 -99
  8. package/lib/commonjs/capture/installRnImage.js +1 -132
  9. package/lib/commonjs/capture/nativeImageModules.js +1 -30
  10. package/lib/commonjs/capture/nativeImageModules.web.js +1 -18
  11. package/lib/commonjs/capture/overrides.js +1 -346
  12. package/lib/commonjs/capture/rnDecoratorEarly.js +1 -55
  13. package/lib/commonjs/capture/savings.js +1 -209
  14. package/lib/commonjs/capture/savings.web.js +1 -71
  15. package/lib/commonjs/capture/shared.js +1 -378
  16. package/lib/commonjs/components/ImageDetail.js +1 -455
  17. package/lib/commonjs/components/ImageDetailFooter.js +1 -158
  18. package/lib/commonjs/components/ImageRow.js +1 -156
  19. package/lib/commonjs/components/ImagesHeader.js +1 -63
  20. package/lib/commonjs/components/ImagesIcon.js +1 -19
  21. package/lib/commonjs/components/ImagesListFooter.js +1 -126
  22. package/lib/commonjs/components/ImagesModal.js +1 -240
  23. package/lib/commonjs/components/ImagesRoot.js +1 -24
  24. package/lib/commonjs/components/ImagesRoot.web.js +1 -13
  25. package/lib/commonjs/components/InsightsBar.js +1 -59
  26. package/lib/commonjs/export.js +1 -81
  27. package/lib/commonjs/format.js +1 -117
  28. package/lib/commonjs/hooks/useImageRecords.js +1 -20
  29. package/lib/commonjs/index.js +1 -261
  30. package/lib/commonjs/preset.js +1 -39
  31. package/lib/commonjs/register.js +1 -19
  32. package/lib/commonjs/registerEarly.js +1 -14
  33. package/lib/commonjs/remote.js +1 -42
  34. package/lib/commonjs/state.js +1 -58
  35. package/lib/commonjs/store/imageEventStore.js +1 -238
  36. package/lib/commonjs/store/insights.js +1 -76
  37. package/lib/commonjs/sync/imagesSyncAdapter.js +1 -361
  38. package/lib/commonjs/types.js +0 -1
  39. package/lib/module/autoCapture.js +1 -4
  40. package/lib/module/autoCapture.web.js +1 -5
  41. package/lib/module/capture/actions.js +1 -230
  42. package/lib/module/capture/install.js +1 -40
  43. package/lib/module/capture/install.web.js +1 -243
  44. package/lib/module/capture/installExpoImage.js +1 -94
  45. package/lib/module/capture/installRnImage.js +1 -125
  46. package/lib/module/capture/nativeImageModules.js +1 -24
  47. package/lib/module/capture/nativeImageModules.web.js +1 -12
  48. package/lib/module/capture/overrides.js +1 -329
  49. package/lib/module/capture/rnDecoratorEarly.js +1 -50
  50. package/lib/module/capture/savings.js +1 -205
  51. package/lib/module/capture/savings.web.js +1 -67
  52. package/lib/module/capture/shared.js +1 -369
  53. package/lib/module/components/ImageDetail.js +1 -451
  54. package/lib/module/components/ImageDetailFooter.js +1 -154
  55. package/lib/module/components/ImageRow.js +1 -152
  56. package/lib/module/components/ImagesHeader.js +1 -59
  57. package/lib/module/components/ImagesIcon.js +1 -12
  58. package/lib/module/components/ImagesListFooter.js +1 -122
  59. package/lib/module/components/ImagesModal.js +1 -235
  60. package/lib/module/components/ImagesRoot.js +1 -20
  61. package/lib/module/components/ImagesRoot.web.js +1 -9
  62. package/lib/module/components/InsightsBar.js +1 -55
  63. package/lib/module/export.js +1 -76
  64. package/lib/module/format.js +1 -108
  65. package/lib/module/hooks/useImageRecords.js +1 -16
  66. package/lib/module/index.js +1 -79
  67. package/lib/module/preset.js +1 -35
  68. package/lib/module/register.js +1 -19
  69. package/lib/module/registerEarly.js +1 -14
  70. package/lib/module/remote.js +1 -36
  71. package/lib/module/state.js +1 -54
  72. package/lib/module/store/imageEventStore.js +1 -221
  73. package/lib/module/store/insights.js +1 -71
  74. package/lib/module/sync/imagesSyncAdapter.js +1 -356
  75. package/lib/module/types.js +0 -1
  76. package/lib/web/chunks/chunk-TTS5EH4R.mjs +1 -2
  77. package/lib/web/index.mjs +3 -4
  78. package/lib/web/register.mjs +1 -2
  79. package/package.json +3 -7
  80. package/register.js +1 -15
  81. package/lib/commonjs/autoCapture.js.map +0 -1
  82. package/lib/commonjs/autoCapture.web.js.map +0 -1
  83. package/lib/commonjs/capture/actions.js.map +0 -1
  84. package/lib/commonjs/capture/install.js.map +0 -1
  85. package/lib/commonjs/capture/install.web.js.map +0 -1
  86. package/lib/commonjs/capture/installExpoImage.js.map +0 -1
  87. package/lib/commonjs/capture/installRnImage.js.map +0 -1
  88. package/lib/commonjs/capture/nativeImageModules.js.map +0 -1
  89. package/lib/commonjs/capture/nativeImageModules.web.js.map +0 -1
  90. package/lib/commonjs/capture/overrides.js.map +0 -1
  91. package/lib/commonjs/capture/rnDecoratorEarly.js.map +0 -1
  92. package/lib/commonjs/capture/savings.js.map +0 -1
  93. package/lib/commonjs/capture/savings.web.js.map +0 -1
  94. package/lib/commonjs/capture/shared.js.map +0 -1
  95. package/lib/commonjs/components/ImageDetail.js.map +0 -1
  96. package/lib/commonjs/components/ImageDetailFooter.js.map +0 -1
  97. package/lib/commonjs/components/ImageRow.js.map +0 -1
  98. package/lib/commonjs/components/ImagesHeader.js.map +0 -1
  99. package/lib/commonjs/components/ImagesIcon.js.map +0 -1
  100. package/lib/commonjs/components/ImagesListFooter.js.map +0 -1
  101. package/lib/commonjs/components/ImagesModal.js.map +0 -1
  102. package/lib/commonjs/components/ImagesRoot.js.map +0 -1
  103. package/lib/commonjs/components/ImagesRoot.web.js.map +0 -1
  104. package/lib/commonjs/components/InsightsBar.js.map +0 -1
  105. package/lib/commonjs/export.js.map +0 -1
  106. package/lib/commonjs/format.js.map +0 -1
  107. package/lib/commonjs/hooks/useImageRecords.js.map +0 -1
  108. package/lib/commonjs/index.js.map +0 -1
  109. package/lib/commonjs/preset.js.map +0 -1
  110. package/lib/commonjs/register.js.map +0 -1
  111. package/lib/commonjs/registerEarly.js.map +0 -1
  112. package/lib/commonjs/remote.js.map +0 -1
  113. package/lib/commonjs/state.js.map +0 -1
  114. package/lib/commonjs/store/imageEventStore.js.map +0 -1
  115. package/lib/commonjs/store/insights.js.map +0 -1
  116. package/lib/commonjs/sync/imagesSyncAdapter.js.map +0 -1
  117. package/lib/commonjs/types.js.map +0 -1
  118. package/lib/module/autoCapture.js.map +0 -1
  119. package/lib/module/autoCapture.web.js.map +0 -1
  120. package/lib/module/capture/actions.js.map +0 -1
  121. package/lib/module/capture/install.js.map +0 -1
  122. package/lib/module/capture/install.web.js.map +0 -1
  123. package/lib/module/capture/installExpoImage.js.map +0 -1
  124. package/lib/module/capture/installRnImage.js.map +0 -1
  125. package/lib/module/capture/nativeImageModules.js.map +0 -1
  126. package/lib/module/capture/nativeImageModules.web.js.map +0 -1
  127. package/lib/module/capture/overrides.js.map +0 -1
  128. package/lib/module/capture/rnDecoratorEarly.js.map +0 -1
  129. package/lib/module/capture/savings.js.map +0 -1
  130. package/lib/module/capture/savings.web.js.map +0 -1
  131. package/lib/module/capture/shared.js.map +0 -1
  132. package/lib/module/components/ImageDetail.js.map +0 -1
  133. package/lib/module/components/ImageDetailFooter.js.map +0 -1
  134. package/lib/module/components/ImageRow.js.map +0 -1
  135. package/lib/module/components/ImagesHeader.js.map +0 -1
  136. package/lib/module/components/ImagesIcon.js.map +0 -1
  137. package/lib/module/components/ImagesListFooter.js.map +0 -1
  138. package/lib/module/components/ImagesModal.js.map +0 -1
  139. package/lib/module/components/ImagesRoot.js.map +0 -1
  140. package/lib/module/components/ImagesRoot.web.js.map +0 -1
  141. package/lib/module/components/InsightsBar.js.map +0 -1
  142. package/lib/module/export.js.map +0 -1
  143. package/lib/module/format.js.map +0 -1
  144. package/lib/module/hooks/useImageRecords.js.map +0 -1
  145. package/lib/module/index.js.map +0 -1
  146. package/lib/module/preset.js.map +0 -1
  147. package/lib/module/register.js.map +0 -1
  148. package/lib/module/registerEarly.js.map +0 -1
  149. package/lib/module/remote.js.map +0 -1
  150. package/lib/module/state.js.map +0 -1
  151. package/lib/module/store/imageEventStore.js.map +0 -1
  152. package/lib/module/store/insights.js.map +0 -1
  153. package/lib/module/sync/imagesSyncAdapter.js.map +0 -1
  154. package/lib/module/types.js.map +0 -1
  155. package/lib/typescript/autoCapture.d.ts.map +0 -1
  156. package/lib/typescript/autoCapture.web.d.ts.map +0 -1
  157. package/lib/typescript/capture/actions.d.ts.map +0 -1
  158. package/lib/typescript/capture/install.d.ts.map +0 -1
  159. package/lib/typescript/capture/install.web.d.ts.map +0 -1
  160. package/lib/typescript/capture/installExpoImage.d.ts.map +0 -1
  161. package/lib/typescript/capture/installRnImage.d.ts.map +0 -1
  162. package/lib/typescript/capture/nativeImageModules.d.ts.map +0 -1
  163. package/lib/typescript/capture/nativeImageModules.web.d.ts.map +0 -1
  164. package/lib/typescript/capture/overrides.d.ts.map +0 -1
  165. package/lib/typescript/capture/rnDecoratorEarly.d.ts.map +0 -1
  166. package/lib/typescript/capture/savings.d.ts.map +0 -1
  167. package/lib/typescript/capture/savings.web.d.ts.map +0 -1
  168. package/lib/typescript/capture/shared.d.ts.map +0 -1
  169. package/lib/typescript/components/ImageDetail.d.ts.map +0 -1
  170. package/lib/typescript/components/ImageDetailFooter.d.ts.map +0 -1
  171. package/lib/typescript/components/ImageRow.d.ts.map +0 -1
  172. package/lib/typescript/components/ImagesHeader.d.ts.map +0 -1
  173. package/lib/typescript/components/ImagesIcon.d.ts.map +0 -1
  174. package/lib/typescript/components/ImagesListFooter.d.ts.map +0 -1
  175. package/lib/typescript/components/ImagesModal.d.ts.map +0 -1
  176. package/lib/typescript/components/ImagesRoot.d.ts.map +0 -1
  177. package/lib/typescript/components/ImagesRoot.web.d.ts.map +0 -1
  178. package/lib/typescript/components/InsightsBar.d.ts.map +0 -1
  179. package/lib/typescript/export.d.ts.map +0 -1
  180. package/lib/typescript/format.d.ts.map +0 -1
  181. package/lib/typescript/hooks/useImageRecords.d.ts.map +0 -1
  182. package/lib/typescript/index.d.ts.map +0 -1
  183. package/lib/typescript/preset.d.ts.map +0 -1
  184. package/lib/typescript/register.d.ts.map +0 -1
  185. package/lib/typescript/registerEarly.d.ts.map +0 -1
  186. package/lib/typescript/remote.d.ts.map +0 -1
  187. package/lib/typescript/state.d.ts.map +0 -1
  188. package/lib/typescript/store/imageEventStore.d.ts.map +0 -1
  189. package/lib/typescript/store/insights.d.ts.map +0 -1
  190. package/lib/typescript/sync/imagesSyncAdapter.d.ts.map +0 -1
  191. package/lib/typescript/types.d.ts.map +0 -1
  192. package/lib/web/chunks/chunk-7CWLRQ2L.mjs +0 -2
  193. package/lib/web/chunks/chunk-B5A6CYSS.mjs +0 -2
  194. package/lib/web/chunks/chunk-LKTR6X2V.mjs +0 -2
@@ -1,42 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.remoteActionResult = remoteActionResult;
7
- exports.remoteImagesDispatch = remoteImagesDispatch;
8
- exports.setRemoteImagesHandler = setRemoteImagesHandler;
9
- var _state = require("./state");
10
- /**
11
- * Remote-dispatch seam for dashboard mirrors (Buoy Desktop).
12
- *
13
- * On device this stays null and every action in capture/ runs locally. A
14
- * dashboard sets a handler that forwards the action name + params over the
15
- * external-sync protocol to the SAME adapter actions the MCP uses
16
- * (sync/imagesSyncAdapter.ts), so one action vocabulary serves phone UI,
17
- * desktop UI, and LLM automation.
18
- */
19
-
20
- function setRemoteImagesHandler(handler) {
21
- (0, _state.imagesState)().remoteDispatch = handler;
22
- }
23
- function remoteImagesDispatch() {
24
- return (0, _state.imagesState)().remoteDispatch;
25
- }
26
-
27
- /** Coerce an adapter action's `{ ok, message }` reply into an ActionResult. */
28
- async function remoteActionResult(call) {
29
- try {
30
- const result = await call;
31
- return {
32
- ok: !!result?.ok,
33
- message: String(result?.message ?? "")
34
- };
35
- } catch (error) {
36
- return {
37
- ok: false,
38
- message: String(error?.message ?? error)
39
- };
40
- }
41
- }
42
- //# sourceMappingURL=remote.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.remoteActionResult=remoteActionResult;exports.remoteImagesDispatch=remoteImagesDispatch;exports.setRemoteImagesHandler=setRemoteImagesHandler;var _state=require("./state");function setRemoteImagesHandler(t){(0,_state.imagesState)().remoteDispatch=t}function remoteImagesDispatch(){return(0,_state.imagesState)().remoteDispatch}async function remoteActionResult(t){try{const e=await t;return{ok:!!e?.ok,message:String(e?.message??"")}}catch(e){return{ok:false,message:String(e?.message??e)}}}
@@ -1,58 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.imagesState = imagesState;
7
- /**
8
- * Global singleton state for the Images tool.
9
- *
10
- * The package can get evaluated twice as two separate module instances (e.g.
11
- * the `/register` entry shim resolving to one copy while the package root
12
- * import resolves to another — lib vs src). If capture state lived in module
13
- * scope, the early copy would install the RN decorator and collect records
14
- * while the UI copy read an empty store and a false "decorator never ran"
15
- * flag. Keying all mutable state on globalThis under a well-known symbol makes
16
- * every copy operate on the same data.
17
- */
18
-
19
- const KEY = Symbol.for("buoy.images.globalState.v1");
20
- function imagesState() {
21
- const g = globalThis;
22
- if (!g[KEY]) {
23
- g[KEY] = {
24
- records: [],
25
- byId: new Map(),
26
- nextId: 1,
27
- listeners: new Set(),
28
- snapshot: [],
29
- emitScheduled: false,
30
- captureInstalled: false,
31
- rnInstalled: false,
32
- expoInstalled: false,
33
- decoratorRegistered: false,
34
- decoratorInvoked: false,
35
- attachSeen: 0,
36
- expoAvailable: false,
37
- expoPatched: false,
38
- reloadRequests: new Map(),
39
- actionListeners: new Set(),
40
- expoInstances: new Map(),
41
- overrides: new Map(),
42
- flashUntil: new Map(),
43
- recordVersions: new Map(),
44
- globalVersion: 0,
45
- versionCounter: 0,
46
- globalModes: {
47
- network: "normal",
48
- blank: false
49
- },
50
- captureDisabled: false,
51
- remoteDispatch: null,
52
- mirrorPixelRatio: null,
53
- mirrorCaptureStatus: null
54
- };
55
- }
56
- return g[KEY];
57
- }
58
- //# sourceMappingURL=state.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.imagesState=imagesState;const KEY=Symbol.for("buoy.images.globalState.v1");function imagesState(){const e=globalThis;if(!e[KEY]){e[KEY]={records:[],byId:new Map,nextId:1,listeners:new Set,snapshot:[],emitScheduled:false,captureInstalled:false,rnInstalled:false,expoInstalled:false,decoratorRegistered:false,decoratorInvoked:false,attachSeen:0,expoAvailable:false,expoPatched:false,reloadRequests:new Map,actionListeners:new Set,expoInstances:new Map,overrides:new Map,flashUntil:new Map,recordVersions:new Map,globalVersion:0,versionCounter:0,globalModes:{network:"normal",blank:false},captureDisabled:false,remoteDispatch:null,mirrorPixelRatio:null,mirrorCaptureStatus:null}}return e[KEY]}
@@ -1,238 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.clearRecords = clearRecords;
7
- exports.computeStats = computeStats;
8
- exports.createRecord = createRecord;
9
- exports.disableImagesCapture = disableImagesCapture;
10
- exports.effectivePixelRatio = effectivePixelRatio;
11
- exports.estDecodedBytes = estDecodedBytes;
12
- exports.estWastedBytes = estWastedBytes;
13
- exports.getRecord = getRecord;
14
- exports.getSnapshot = getSnapshot;
15
- exports.neededPixels = neededPixels;
16
- exports.oversizeFactor = oversizeFactor;
17
- exports.replaceImagesFromSnapshot = replaceImagesFromSnapshot;
18
- exports.subscribe = subscribe;
19
- exports.updateRecord = updateRecord;
20
- var _reactNative = require("react-native");
21
- var _remote = require("../remote");
22
- var _state = require("../state");
23
- /**
24
- * imageEventStore
25
- *
26
- * Bounded buffer of ImageRecords fed by the capture layer (installRnImage /
27
- * installExpoImage). Follows the console tool's store shape — Set of
28
- * listeners, frame-coalesced emit so bursty image grids don't turn every
29
- * event into its own React commit — but ALL mutable state lives on the
30
- * globalThis singleton (see ../state.ts) so a dual module instance of this
31
- * package still reads/writes one shared store.
32
- */
33
-
34
- /** Max records retained before the oldest are dropped. */
35
- const MAX_RECORDS = 500;
36
- function emit() {
37
- const s = (0, _state.imagesState)();
38
- if (s.emitScheduled) return;
39
- s.emitScheduled = true;
40
- requestAnimationFrame(() => {
41
- s.emitScheduled = false;
42
- for (const listener of s.listeners) listener();
43
- });
44
- }
45
-
46
- /** Snapshot identity only changes on emit, so useSyncExternalStore stays cheap. */
47
- function invalidate() {
48
- const s = (0, _state.imagesState)();
49
- s.snapshot = [...s.records];
50
- emit();
51
- }
52
- function createRecord(partial) {
53
- const s = (0, _state.imagesState)();
54
- const record = {
55
- ...partial,
56
- id: s.nextId++,
57
- createdAt: Date.now(),
58
- status: "pending",
59
- mounted: true,
60
- loadCount: 0
61
- };
62
- s.records.push(record);
63
- s.byId.set(record.id, record);
64
- if (s.records.length > MAX_RECORDS) {
65
- const dropped = s.records.splice(0, s.records.length - MAX_RECORDS);
66
- for (const d of dropped) s.byId.delete(d.id);
67
- }
68
- invalidate();
69
- return record;
70
- }
71
-
72
- /** Mutate a record in place (records are owned by the store) and notify. */
73
- function updateRecord(id, patch) {
74
- const s = (0, _state.imagesState)();
75
- const record = s.byId.get(id);
76
- if (!record) return undefined;
77
- Object.assign(record, patch);
78
- invalidate();
79
- return record;
80
- }
81
- function getRecord(id) {
82
- return (0, _state.imagesState)().byId.get(id);
83
- }
84
- function getSnapshot() {
85
- return (0, _state.imagesState)().snapshot;
86
- }
87
- function subscribe(listener) {
88
- const s = (0, _state.imagesState)();
89
- s.listeners.add(listener);
90
- return () => (0, _state.imagesState)().listeners.delete(listener);
91
- }
92
- function clearRecords() {
93
- // Mirror mode: clear the device's registry too, or the next snapshot would
94
- // refill this store straight away.
95
- void (0, _remote.remoteImagesDispatch)()?.("clearRecords");
96
- const s = (0, _state.imagesState)();
97
- // Keep records still actively loading so in-flight events don't orphan.
98
- s.records = s.records.filter(r => r.mounted && r.status !== "loaded" && r.status !== "error");
99
- s.byId = new Map(s.records.map(r => [r.id, r]));
100
- invalidate();
101
- }
102
-
103
- // ---------------------------------------------------------------------------
104
- // Mirror mode (desktop dashboard)
105
- // ---------------------------------------------------------------------------
106
-
107
- /**
108
- * Dashboards never instrument their own images — the store is purely a mirror
109
- * of the synced device. Makes installImageCapture() a permanent no-op.
110
- */
111
- function disableImagesCapture() {
112
- (0, _state.imagesState)().captureDisabled = true;
113
- }
114
-
115
- /**
116
- * Replace the whole store from a synced adapter snapshot (wire records are
117
- * newest-first; the store keeps oldest-first and useImageRecords reverses).
118
- * Also adopts the device's global modes / capture status and derives the
119
- * DEVICE's PixelRatio so oversize math doesn't use the dashboard display's.
120
- */
121
- function replaceImagesFromSnapshot(payload) {
122
- const s = (0, _state.imagesState)();
123
- const wire = payload.records ?? [];
124
-
125
- // neededPx was computed on device as round(layout * PixelRatio.get()), so
126
- // the widest record recovers the device scale with the least rounding error.
127
- let bestWidth = 0;
128
- for (const w of wire) {
129
- if (w.layout && w.neededPx && w.layout.width > bestWidth) {
130
- bestWidth = w.layout.width;
131
- s.mirrorPixelRatio = w.neededPx.width / w.layout.width;
132
- }
133
- }
134
- const now = Date.now();
135
- s.records = [...wire].reverse().map(w => ({
136
- id: w.id,
137
- lib: w.lib,
138
- uri: w.uri,
139
- sourceKind: w.kind,
140
- status: w.status,
141
- mounted: w.mounted,
142
- createdAt: now - w.ageMs,
143
- durationMs: w.ms ?? undefined,
144
- intrinsic: w.intrinsic ?? undefined,
145
- layout: w.layout ?? undefined,
146
- cacheVerdict: w.cache ?? undefined,
147
- cacheVerdictSource: w.cacheSource ?? undefined,
148
- progressSeen: w.progressSeen,
149
- // The wire carries only the total; a settled download loaded all of it.
150
- bytesLoaded: w.status === "loaded" ? w.bytesTotal ?? undefined : undefined,
151
- bytesTotal: w.bytesTotal ?? undefined,
152
- error: w.error ?? undefined,
153
- errorCode: w.errorCode ?? undefined,
154
- loadCount: w.loadCount,
155
- overrideLabel: w.overrideLabel ?? undefined,
156
- hasAltText: w.hasAltText ?? undefined,
157
- layoutShifts: w.layoutShifts
158
- }));
159
- s.byId = new Map(s.records.map(r => [r.id, r]));
160
- if (payload.globalModes) s.globalModes = {
161
- ...payload.globalModes
162
- };
163
- if (payload.captureStatus) s.mirrorCaptureStatus = payload.captureStatus;
164
- invalidate();
165
- // Mode toggles render through the actions bus, not the record store.
166
- for (const l of s.actionListeners) l();
167
- }
168
-
169
- // ---------------------------------------------------------------------------
170
- // Derived math (shared by rows, detail view, and stats)
171
- // ---------------------------------------------------------------------------
172
-
173
- /** Device PixelRatio on device; the SYNCED device's ratio on a mirror. */
174
- function effectivePixelRatio() {
175
- return (0, _state.imagesState)().mirrorPixelRatio ?? _reactNative.PixelRatio.get();
176
- }
177
-
178
- /** Physical pixels actually needed to fill the laid-out view 1:1. */
179
- function neededPixels(record) {
180
- if (!record.layout || record.layout.width <= 0 || record.layout.height <= 0) return null;
181
- const scale = effectivePixelRatio();
182
- return {
183
- width: Math.round(record.layout.width * scale),
184
- height: Math.round(record.layout.height * scale)
185
- };
186
- }
187
-
188
- /**
189
- * Linear oversize factor: how many times larger (per axis, area-normalized)
190
- * the decoded bitmap is vs what the view needs. 1 = perfect, 2 = 4× the pixels.
191
- */
192
- function oversizeFactor(record) {
193
- const needed = neededPixels(record);
194
- if (!needed || !record.intrinsic) return null;
195
- const neededArea = needed.width * needed.height;
196
- const intrinsicArea = record.intrinsic.width * record.intrinsic.height;
197
- if (neededArea <= 0 || intrinsicArea <= 0) return null;
198
- return Math.sqrt(intrinsicArea / neededArea);
199
- }
200
-
201
- /** Estimated decoded-bitmap memory for a loaded record (RGBA8888). */
202
- function estDecodedBytes(record) {
203
- if (!record.intrinsic) return 0;
204
- return record.intrinsic.width * record.intrinsic.height * 4;
205
- }
206
-
207
- /** Decoded bytes beyond what the laid-out size needs (0 if not oversized). */
208
- function estWastedBytes(record) {
209
- const needed = neededPixels(record);
210
- if (!needed || !record.intrinsic) return 0;
211
- const wasted = (record.intrinsic.width * record.intrinsic.height - needed.width * needed.height) * 4;
212
- return Math.max(0, wasted);
213
- }
214
- function computeStats(list) {
215
- const stats = {
216
- total: list.length,
217
- loading: 0,
218
- loaded: 0,
219
- errors: 0,
220
- networkLoads: 0,
221
- estDecodedBytes: 0,
222
- estWastedBytes: 0
223
- };
224
- for (const r of list) {
225
- // Unmounted in-flight records are stale (the instance died mid-load) —
226
- // they must not count as live in-flight work.
227
- if ((r.status === "loading" || r.status === "pending") && r.mounted) stats.loading++;
228
- if (r.status === "loaded") stats.loaded++;
229
- if (r.status === "error") stats.errors++;
230
- if (r.progressSeen || r.cacheVerdict === "none") stats.networkLoads++;
231
- if (r.mounted && r.status === "loaded") {
232
- stats.estDecodedBytes += estDecodedBytes(r);
233
- stats.estWastedBytes += estWastedBytes(r);
234
- }
235
- }
236
- return stats;
237
- }
238
- //# sourceMappingURL=imageEventStore.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.clearRecords=clearRecords;exports.computeStats=computeStats;exports.createRecord=createRecord;exports.disableImagesCapture=disableImagesCapture;exports.effectivePixelRatio=effectivePixelRatio;exports.estDecodedBytes=estDecodedBytes;exports.estWastedBytes=estWastedBytes;exports.getRecord=getRecord;exports.getSnapshot=getSnapshot;exports.neededPixels=neededPixels;exports.oversizeFactor=oversizeFactor;exports.replaceImagesFromSnapshot=replaceImagesFromSnapshot;exports.subscribe=subscribe;exports.updateRecord=updateRecord;var _reactNative=require("react-native");var _remote=require("../remote");var _state=require("../state");const MAX_RECORDS=500;function emit(){const e=(0,_state.imagesState)();if(e.emitScheduled)return;e.emitScheduled=true;requestAnimationFrame(()=>{e.emitScheduled=false;for(const t of e.listeners)t()})}function invalidate(){const e=(0,_state.imagesState)();e.snapshot=[...e.records];emit()}function createRecord(e){const t=(0,_state.imagesState)();const r={...e,id:t.nextId++,createdAt:Date.now(),status:"pending",mounted:true,loadCount:0};t.records.push(r);t.byId.set(r.id,r);if(t.records.length>MAX_RECORDS){const i=t.records.splice(0,t.records.length-MAX_RECORDS);for(const n of i)t.byId.delete(n.id)}invalidate();return r}function updateRecord(e,t){const r=(0,_state.imagesState)();const i=r.byId.get(e);if(!i)return void 0;Object.assign(i,t);invalidate();return i}function getRecord(e){return(0,_state.imagesState)().byId.get(e)}function getSnapshot(){return(0,_state.imagesState)().snapshot}function subscribe(e){const t=(0,_state.imagesState)();t.listeners.add(e);return()=>(0,_state.imagesState)().listeners.delete(e)}function clearRecords(){void(0,_remote.remoteImagesDispatch)()?.("clearRecords");const e=(0,_state.imagesState)();e.records=e.records.filter(t=>t.mounted&&t.status!=="loaded"&&t.status!=="error");e.byId=new Map(e.records.map(t=>[t.id,t]));invalidate()}function disableImagesCapture(){(0,_state.imagesState)().captureDisabled=true}function replaceImagesFromSnapshot(e){const t=(0,_state.imagesState)();const r=e.records??[];let i=0;for(const s of r){if(s.layout&&s.neededPx&&s.layout.width>i){i=s.layout.width;t.mirrorPixelRatio=s.neededPx.width/s.layout.width}}const n=Date.now();t.records=[...r].reverse().map(s=>({id:s.id,lib:s.lib,uri:s.uri,sourceKind:s.kind,status:s.status,mounted:s.mounted,createdAt:n-s.ageMs,durationMs:s.ms??void 0,intrinsic:s.intrinsic??void 0,layout:s.layout??void 0,cacheVerdict:s.cache??void 0,cacheVerdictSource:s.cacheSource??void 0,progressSeen:s.progressSeen,bytesLoaded:s.status==="loaded"?s.bytesTotal??void 0:void 0,bytesTotal:s.bytesTotal??void 0,error:s.error??void 0,errorCode:s.errorCode??void 0,loadCount:s.loadCount,overrideLabel:s.overrideLabel??void 0,hasAltText:s.hasAltText??void 0,layoutShifts:s.layoutShifts}));t.byId=new Map(t.records.map(s=>[s.id,s]));if(e.globalModes)t.globalModes={...e.globalModes};if(e.captureStatus)t.mirrorCaptureStatus=e.captureStatus;invalidate();for(const s of t.actionListeners)s()}function effectivePixelRatio(){return(0,_state.imagesState)().mirrorPixelRatio??_reactNative.PixelRatio.get()}function neededPixels(e){if(!e.layout||e.layout.width<=0||e.layout.height<=0)return null;const t=effectivePixelRatio();return{width:Math.round(e.layout.width*t),height:Math.round(e.layout.height*t)}}function oversizeFactor(e){const t=neededPixels(e);if(!t||!e.intrinsic)return null;const r=t.width*t.height;const i=e.intrinsic.width*e.intrinsic.height;if(r<=0||i<=0)return null;return Math.sqrt(i/r)}function estDecodedBytes(e){if(!e.intrinsic)return 0;return e.intrinsic.width*e.intrinsic.height*4}function estWastedBytes(e){const t=neededPixels(e);if(!t||!e.intrinsic)return 0;const r=(e.intrinsic.width*e.intrinsic.height-t.width*t.height)*4;return Math.max(0,r)}function computeStats(e){const t={total:e.length,loading:0,loaded:0,errors:0,networkLoads:0,estDecodedBytes:0,estWastedBytes:0};for(const r of e){if((r.status==="loading"||r.status==="pending")&&r.mounted)t.loading++;if(r.status==="loaded")t.loaded++;if(r.status==="error")t.errors++;if(r.progressSeen||r.cacheVerdict==="none")t.networkLoads++;if(r.mounted&&r.status==="loaded"){t.estDecodedBytes+=estDecodedBytes(r);t.estWastedBytes+=estWastedBytes(r)}}return t}
@@ -1,76 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.computeInsights = computeInsights;
7
- exports.insightChips = insightChips;
8
- /**
9
- * Cross-record insights — findings computed purely from data the registry
10
- * already holds (no extra instrumentation): duplicate simultaneous loads,
11
- * retry storms, iOS image-queue saturation, missing alt text, and
12
- * layout-shifting images.
13
- */
14
-
15
- /** loadCount at/above this flags render-thrash reload storms. */
16
- const RETRY_STORM_THRESHOLD = 5;
17
- /** RCTImageLoader allows 4 concurrent loading tasks on iOS. */
18
- const IOS_CONCURRENT_LOAD_LIMIT = 4;
19
- function computeInsights(records) {
20
- const byUri = new Map();
21
- const retryStorms = [];
22
- const layoutShifters = [];
23
- let missingAlt = 0;
24
- let rnLoading = 0;
25
- for (const r of records) {
26
- if (r.mounted && r.uri) {
27
- byUri.set(r.uri, (byUri.get(r.uri) ?? 0) + 1);
28
- }
29
- if (r.loadCount >= RETRY_STORM_THRESHOLD) {
30
- retryStorms.push({
31
- id: r.id,
32
- uri: r.uri,
33
- loadCount: r.loadCount
34
- });
35
- }
36
- if ((r.layoutShifts ?? 0) > 0) {
37
- layoutShifters.push({
38
- id: r.id,
39
- uri: r.uri,
40
- shifts: r.layoutShifts ?? 0
41
- });
42
- }
43
- if (r.mounted && r.hasAltText === false) missingAlt++;
44
- if (r.lib === "rn" && r.mounted && r.status === "loading") rnLoading++;
45
- }
46
- const duplicates = [...byUri.entries()].filter(([, count]) => count > 1).map(([uri, count]) => ({
47
- uri,
48
- count
49
- })).sort((a, b) => b.count - a.count);
50
- return {
51
- duplicates,
52
- retryStorms: retryStorms.sort((a, b) => b.loadCount - a.loadCount),
53
- queueSaturated: rnLoading > IOS_CONCURRENT_LOAD_LIMIT,
54
- missingAlt,
55
- layoutShifters
56
- };
57
- }
58
-
59
- /** Compact one-line chips for UI/MCP surfacing. Empty array = all clear. */
60
- function insightChips(insights) {
61
- const chips = [];
62
- if (insights.duplicates.length > 0) {
63
- const extra = insights.duplicates.reduce((n, d) => n + d.count - 1, 0);
64
- chips.push(`${insights.duplicates.length} duplicate URL${insights.duplicates.length > 1 ? "s" : ""} (${extra} redundant loads)`);
65
- }
66
- if (insights.retryStorms.length > 0) {
67
- chips.push(`${insights.retryStorms.length} retry storm${insights.retryStorms.length > 1 ? "s" : ""}`);
68
- }
69
- if (insights.queueSaturated) chips.push("iOS image queue saturated (>4 loading)");
70
- if (insights.missingAlt > 0) chips.push(`${insights.missingAlt} missing alt text`);
71
- if (insights.layoutShifters.length > 0) {
72
- chips.push(`${insights.layoutShifters.length} layout-shifting image${insights.layoutShifters.length > 1 ? "s" : ""}`);
73
- }
74
- return chips;
75
- }
76
- //# sourceMappingURL=insights.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.computeInsights=computeInsights;exports.insightChips=insightChips;const RETRY_STORM_THRESHOLD=5;const IOS_CONCURRENT_LOAD_LIMIT=4;function computeInsights(e){const u=new Map;const r=[];const s=[];let i=0;let l=0;for(const t of e){if(t.mounted&&t.uri){u.set(t.uri,(u.get(t.uri)??0)+1)}if(t.loadCount>=RETRY_STORM_THRESHOLD){r.push({id:t.id,uri:t.uri,loadCount:t.loadCount})}if((t.layoutShifts??0)>0){s.push({id:t.id,uri:t.uri,shifts:t.layoutShifts??0})}if(t.mounted&&t.hasAltText===false)i++;if(t.lib==="rn"&&t.mounted&&t.status==="loading")l++}const n=[...u.entries()].filter(([,t])=>t>1).map(([t,o])=>({uri:t,count:o})).sort((t,o)=>o.count-t.count);return{duplicates:n,retryStorms:r.sort((t,o)=>o.loadCount-t.loadCount),queueSaturated:l>IOS_CONCURRENT_LOAD_LIMIT,missingAlt:i,layoutShifters:s}}function insightChips(e){const u=[];if(e.duplicates.length>0){const r=e.duplicates.reduce((s,i)=>s+i.count-1,0);u.push(`${e.duplicates.length} duplicate URL${e.duplicates.length>1?"s":""} (${r} redundant loads)`)}if(e.retryStorms.length>0){u.push(`${e.retryStorms.length} retry storm${e.retryStorms.length>1?"s":""}`)}if(e.queueSaturated)u.push("iOS image queue saturated (>4 loading)");if(e.missingAlt>0)u.push(`${e.missingAlt} missing alt text`);if(e.layoutShifters.length>0){u.push(`${e.layoutShifters.length} layout-shifting image${e.layoutShifters.length>1?"s":""}`)}return u}