@buoy-gg/images 7.0.40 → 7.0.42

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 (179) hide show
  1. package/README.md +12 -65
  2. package/lib/commonjs/autoCapture.js +1 -0
  3. package/lib/commonjs/autoCapture.web.js +1 -0
  4. package/lib/commonjs/capture/actions.js +1 -242
  5. package/lib/commonjs/capture/install.js +1 -45
  6. package/lib/commonjs/capture/install.web.js +1 -0
  7. package/lib/commonjs/capture/installExpoImage.js +1 -99
  8. package/lib/commonjs/capture/installRnImage.js +1 -128
  9. package/lib/commonjs/capture/nativeImageModules.js +1 -0
  10. package/lib/commonjs/capture/nativeImageModules.web.js +1 -0
  11. package/lib/commonjs/capture/overrides.js +1 -346
  12. package/lib/commonjs/capture/rnDecoratorEarly.js +1 -0
  13. package/lib/commonjs/capture/savings.js +1 -209
  14. package/lib/commonjs/capture/savings.web.js +1 -0
  15. package/lib/commonjs/capture/shared.js +1 -378
  16. package/lib/commonjs/components/ImageDetail.js +1 -454
  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 -0
  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 -0
  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 -360
  38. package/lib/commonjs/types.js +0 -1
  39. package/lib/module/autoCapture.js +1 -0
  40. package/lib/module/autoCapture.web.js +1 -0
  41. package/lib/module/capture/actions.js +1 -229
  42. package/lib/module/capture/install.js +1 -40
  43. package/lib/module/capture/install.web.js +1 -0
  44. package/lib/module/capture/installExpoImage.js +1 -94
  45. package/lib/module/capture/installRnImage.js +1 -122
  46. package/lib/module/capture/nativeImageModules.js +1 -0
  47. package/lib/module/capture/nativeImageModules.web.js +1 -0
  48. package/lib/module/capture/overrides.js +1 -329
  49. package/lib/module/capture/rnDecoratorEarly.js +1 -0
  50. package/lib/module/capture/savings.js +1 -205
  51. package/lib/module/capture/savings.web.js +1 -0
  52. package/lib/module/capture/shared.js +1 -369
  53. package/lib/module/components/ImageDetail.js +1 -450
  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 -0
  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 -0
  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 -355
  75. package/lib/module/types.js +0 -1
  76. package/lib/typescript/autoCapture.d.ts +2 -0
  77. package/lib/typescript/autoCapture.web.d.ts +2 -0
  78. package/lib/typescript/capture/actions.d.ts +0 -17
  79. package/lib/typescript/capture/install.web.d.ts +6 -0
  80. package/lib/typescript/capture/installRnImage.d.ts +5 -0
  81. package/lib/typescript/capture/nativeImageModules.d.ts +4 -0
  82. package/lib/typescript/capture/nativeImageModules.web.d.ts +4 -0
  83. package/lib/typescript/capture/rnDecoratorEarly.d.ts +18 -0
  84. package/lib/typescript/capture/savings.web.d.ts +5 -0
  85. package/lib/typescript/components/ImageDetail.d.ts +0 -6
  86. package/lib/typescript/components/ImagesRoot.web.d.ts +2 -0
  87. package/lib/typescript/index.d.ts +1 -1
  88. package/lib/typescript/registerEarly.d.ts +10 -0
  89. package/lib/typescript/sync/imagesSyncAdapter.d.ts +0 -9
  90. package/lib/typescript/types.d.ts +2 -1
  91. package/lib/web/chunks/chunk-TTS5EH4R.mjs +1 -0
  92. package/lib/web/index.mjs +3 -0
  93. package/lib/web/register.mjs +1 -0
  94. package/package.json +52 -6
  95. package/register.js +1 -15
  96. package/lib/commonjs/capture/actions.js.map +0 -1
  97. package/lib/commonjs/capture/install.js.map +0 -1
  98. package/lib/commonjs/capture/installExpoImage.js.map +0 -1
  99. package/lib/commonjs/capture/installRnImage.js.map +0 -1
  100. package/lib/commonjs/capture/overrides.js.map +0 -1
  101. package/lib/commonjs/capture/savings.js.map +0 -1
  102. package/lib/commonjs/capture/shared.js.map +0 -1
  103. package/lib/commonjs/components/ImageDetail.js.map +0 -1
  104. package/lib/commonjs/components/ImageDetailFooter.js.map +0 -1
  105. package/lib/commonjs/components/ImageRow.js.map +0 -1
  106. package/lib/commonjs/components/ImagesHeader.js.map +0 -1
  107. package/lib/commonjs/components/ImagesIcon.js.map +0 -1
  108. package/lib/commonjs/components/ImagesListFooter.js.map +0 -1
  109. package/lib/commonjs/components/ImagesModal.js.map +0 -1
  110. package/lib/commonjs/components/ImagesRoot.js.map +0 -1
  111. package/lib/commonjs/components/InsightsBar.js.map +0 -1
  112. package/lib/commonjs/export.js.map +0 -1
  113. package/lib/commonjs/format.js.map +0 -1
  114. package/lib/commonjs/hooks/useImageRecords.js.map +0 -1
  115. package/lib/commonjs/index.js.map +0 -1
  116. package/lib/commonjs/preset.js.map +0 -1
  117. package/lib/commonjs/register.js.map +0 -1
  118. package/lib/commonjs/remote.js.map +0 -1
  119. package/lib/commonjs/state.js.map +0 -1
  120. package/lib/commonjs/store/imageEventStore.js.map +0 -1
  121. package/lib/commonjs/store/insights.js.map +0 -1
  122. package/lib/commonjs/sync/imagesSyncAdapter.js.map +0 -1
  123. package/lib/commonjs/types.js.map +0 -1
  124. package/lib/module/capture/actions.js.map +0 -1
  125. package/lib/module/capture/install.js.map +0 -1
  126. package/lib/module/capture/installExpoImage.js.map +0 -1
  127. package/lib/module/capture/installRnImage.js.map +0 -1
  128. package/lib/module/capture/overrides.js.map +0 -1
  129. package/lib/module/capture/savings.js.map +0 -1
  130. package/lib/module/capture/shared.js.map +0 -1
  131. package/lib/module/components/ImageDetail.js.map +0 -1
  132. package/lib/module/components/ImageDetailFooter.js.map +0 -1
  133. package/lib/module/components/ImageRow.js.map +0 -1
  134. package/lib/module/components/ImagesHeader.js.map +0 -1
  135. package/lib/module/components/ImagesIcon.js.map +0 -1
  136. package/lib/module/components/ImagesListFooter.js.map +0 -1
  137. package/lib/module/components/ImagesModal.js.map +0 -1
  138. package/lib/module/components/ImagesRoot.js.map +0 -1
  139. package/lib/module/components/InsightsBar.js.map +0 -1
  140. package/lib/module/export.js.map +0 -1
  141. package/lib/module/format.js.map +0 -1
  142. package/lib/module/hooks/useImageRecords.js.map +0 -1
  143. package/lib/module/index.js.map +0 -1
  144. package/lib/module/preset.js.map +0 -1
  145. package/lib/module/register.js.map +0 -1
  146. package/lib/module/remote.js.map +0 -1
  147. package/lib/module/state.js.map +0 -1
  148. package/lib/module/store/imageEventStore.js.map +0 -1
  149. package/lib/module/store/insights.js.map +0 -1
  150. package/lib/module/sync/imagesSyncAdapter.js.map +0 -1
  151. package/lib/module/types.js.map +0 -1
  152. package/lib/typescript/capture/actions.d.ts.map +0 -1
  153. package/lib/typescript/capture/install.d.ts.map +0 -1
  154. package/lib/typescript/capture/installExpoImage.d.ts.map +0 -1
  155. package/lib/typescript/capture/installRnImage.d.ts.map +0 -1
  156. package/lib/typescript/capture/overrides.d.ts.map +0 -1
  157. package/lib/typescript/capture/savings.d.ts.map +0 -1
  158. package/lib/typescript/capture/shared.d.ts.map +0 -1
  159. package/lib/typescript/components/ImageDetail.d.ts.map +0 -1
  160. package/lib/typescript/components/ImageDetailFooter.d.ts.map +0 -1
  161. package/lib/typescript/components/ImageRow.d.ts.map +0 -1
  162. package/lib/typescript/components/ImagesHeader.d.ts.map +0 -1
  163. package/lib/typescript/components/ImagesIcon.d.ts.map +0 -1
  164. package/lib/typescript/components/ImagesListFooter.d.ts.map +0 -1
  165. package/lib/typescript/components/ImagesModal.d.ts.map +0 -1
  166. package/lib/typescript/components/ImagesRoot.d.ts.map +0 -1
  167. package/lib/typescript/components/InsightsBar.d.ts.map +0 -1
  168. package/lib/typescript/export.d.ts.map +0 -1
  169. package/lib/typescript/format.d.ts.map +0 -1
  170. package/lib/typescript/hooks/useImageRecords.d.ts.map +0 -1
  171. package/lib/typescript/index.d.ts.map +0 -1
  172. package/lib/typescript/preset.d.ts.map +0 -1
  173. package/lib/typescript/register.d.ts.map +0 -1
  174. package/lib/typescript/remote.d.ts.map +0 -1
  175. package/lib/typescript/state.d.ts.map +0 -1
  176. package/lib/typescript/store/imageEventStore.d.ts.map +0 -1
  177. package/lib/typescript/store/insights.d.ts.map +0 -1
  178. package/lib/typescript/sync/imagesSyncAdapter.d.ts.map +0 -1
  179. package/lib/typescript/types.d.ts.map +0 -1
@@ -1,450 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * ImageDetail — full record view: preview, source, load timeline, dimensions
5
- * with the oversize verdict + estimated decoded/wasted memory, cache verdict,
6
- * and error details (including HTTP status/headers when the platform provides
7
- * them). Cache actions live here too (expo-image global clears).
8
- */
9
-
10
- import { useState } from "react";
11
- import { Image, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native";
12
- import { macOSColors, night, monoFont } from "@buoy-gg/shared-ui";
13
- import { clearOverride, setOverride } from "../capture/overrides";
14
- import { proveSavings } from "../capture/savings";
15
- import { remoteImagesDispatch } from "../remote";
16
- import { effectivePixelRatio, estDecodedBytes, estWastedBytes, neededPixels } from "../store/imageEventStore";
17
- import { cacheBadge, formatBytes, formatMs, sizeVerdict } from "../format";
18
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
19
- function Section({
20
- title,
21
- children
22
- }) {
23
- return /*#__PURE__*/_jsxs(View, {
24
- style: styles.section,
25
- children: [/*#__PURE__*/_jsx(Text, {
26
- style: styles.sectionTitle,
27
- children: title
28
- }), /*#__PURE__*/_jsx(View, {
29
- style: styles.sectionBody,
30
- children: children
31
- })]
32
- });
33
- }
34
- function Row({
35
- label,
36
- value,
37
- color
38
- }) {
39
- return /*#__PURE__*/_jsxs(View, {
40
- style: styles.kvRow,
41
- children: [/*#__PURE__*/_jsx(Text, {
42
- style: styles.kvLabel,
43
- children: label
44
- }), /*#__PURE__*/_jsx(Text, {
45
- style: [styles.kvValue, color ? {
46
- color
47
- } : null],
48
- selectable: true,
49
- children: value
50
- })]
51
- });
52
- }
53
-
54
- /** expo-image global cache clears (no-ops if expo-image is absent). */
55
- function clearExpoCache(kind) {
56
- // Mirror mode: the caches live on the synced device.
57
- const remote = remoteImagesDispatch();
58
- if (remote) {
59
- void remote("clearExpoCaches", {
60
- memory: kind === "memory",
61
- disk: kind === "disk"
62
- });
63
- return;
64
- }
65
- try {
66
- const expo = require("expo-image");
67
- if (kind === "memory") void expo.Image.clearMemoryCache();else void expo.Image.clearDiskCache();
68
- } catch {
69
- // expo-image not installed
70
- }
71
- }
72
-
73
- /** Free-text source swap: test another URL/format/CDN param on the live view. */
74
- function SwapUrlSection({
75
- record
76
- }) {
77
- const [url, setUrl] = useState(record.uri);
78
- const trimmed = url.trim();
79
- const canApply = record.mounted && trimmed.length > 0 && trimmed !== record.uri;
80
- return /*#__PURE__*/_jsxs(Section, {
81
- title: "SWAP SOURCE URL",
82
- children: [/*#__PURE__*/_jsx(TextInput, {
83
- style: styles.urlInput,
84
- value: url,
85
- onChangeText: setUrl,
86
- placeholder: "https://\u2026",
87
- placeholderTextColor: night.color.placeholder,
88
- autoCapitalize: "none",
89
- autoCorrect: false,
90
- multiline: true
91
- }), /*#__PURE__*/_jsxs(View, {
92
- style: styles.actionsRow,
93
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
94
- style: [styles.actionButton, !canApply && styles.actionDisabled],
95
- disabled: !canApply,
96
- onPress: () => setOverride(record.id, {
97
- source: {
98
- type: "url",
99
- uri: trimmed
100
- },
101
- label: "URL swapped"
102
- }),
103
- children: /*#__PURE__*/_jsx(Text, {
104
- style: styles.actionText,
105
- children: "Apply"
106
- })
107
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
108
- style: styles.actionButton,
109
- onPress: () => setUrl(record.uri),
110
- children: /*#__PURE__*/_jsx(Text, {
111
- style: styles.actionText,
112
- children: "Reset field"
113
- })
114
- })]
115
- })]
116
- });
117
- }
118
-
119
- /**
120
- * Real, on-device savings proof: re-encode at displayed size as WebP via
121
- * expo-image-manipulator, report actual bytes, offer an in-place preview.
122
- */
123
- function SavingsSection({
124
- record
125
- }) {
126
- const [busy, setBusy] = useState(false);
127
- const [result, setResult] = useState(null);
128
- const run = () => {
129
- setBusy(true);
130
- proveSavings(record).then(setResult).catch(error => setResult({
131
- ok: false,
132
- message: String(error?.message ?? error)
133
- })).finally(() => setBusy(false));
134
- };
135
- return /*#__PURE__*/_jsx(Section, {
136
- title: "PROVE SAVINGS (re-encode on device)",
137
- children: result == null ? /*#__PURE__*/_jsx(View, {
138
- style: styles.actionsRow,
139
- children: /*#__PURE__*/_jsx(TouchableOpacity, {
140
- style: styles.actionButton,
141
- onPress: run,
142
- disabled: busy,
143
- children: /*#__PURE__*/_jsx(Text, {
144
- style: styles.actionText,
145
- children: busy ? "Re-encoding…" : "Re-encode at displayed size (WebP)"
146
- })
147
- })
148
- }) : /*#__PURE__*/_jsxs(_Fragment, {
149
- children: [/*#__PURE__*/_jsx(Row, {
150
- label: "Result",
151
- value: result.message || (result.ok ? "done" : "failed"),
152
- color: result.ok ? macOSColors.semantic.success : macOSColors.semantic.error
153
- }), result.originalBytes != null ? /*#__PURE__*/_jsx(Row, {
154
- label: "Original",
155
- value: `${formatBytes(result.originalBytes)} (${result.originalBytesSource})`
156
- }) : null, result.optimizedBytes != null ? /*#__PURE__*/_jsx(Row, {
157
- label: "Optimized",
158
- value: `${formatBytes(result.optimizedBytes)} — ${result.optimizedDims?.width}×${result.optimizedDims?.height} WebP`,
159
- color: macOSColors.semantic.success
160
- }) : null, result.savedBytes != null ? /*#__PURE__*/_jsx(Row, {
161
- label: "Saved",
162
- value: `${formatBytes(result.savedBytes)} (${result.savedPct}%)`,
163
- color: macOSColors.semantic.success
164
- }) : null, /*#__PURE__*/_jsxs(View, {
165
- style: styles.actionsRow,
166
- children: [result.ok && result.optimizedUri && record.mounted ? /*#__PURE__*/_jsx(TouchableOpacity, {
167
- style: styles.actionButton,
168
- onPress: () => setOverride(record.id, {
169
- source: {
170
- type: "url",
171
- uri: result.optimizedUri
172
- },
173
- label: "Optimized preview"
174
- }),
175
- children: /*#__PURE__*/_jsx(Text, {
176
- style: styles.actionText,
177
- children: "Preview in place"
178
- })
179
- }) : null, /*#__PURE__*/_jsx(TouchableOpacity, {
180
- style: styles.actionButton,
181
- onPress: run,
182
- disabled: busy,
183
- children: /*#__PURE__*/_jsx(Text, {
184
- style: styles.actionText,
185
- children: busy ? "Re-encoding…" : "Re-run"
186
- })
187
- })]
188
- })]
189
- })
190
- });
191
- }
192
- export function ImageDetail({
193
- record
194
- }) {
195
- const verdict = sizeVerdict(record);
196
- const cache = cacheBadge(record);
197
- const needed = neededPixels(record);
198
- const decoded = estDecodedBytes(record);
199
- const wasted = estWastedBytes(record);
200
- return /*#__PURE__*/_jsxs(ScrollView, {
201
- style: styles.container,
202
- contentContainerStyle: styles.content,
203
- children: [record.overrideLabel ? /*#__PURE__*/_jsxs(View, {
204
- style: styles.overrideBanner,
205
- children: [/*#__PURE__*/_jsxs(Text, {
206
- style: styles.overrideText,
207
- children: ["Simulation active: ", record.overrideLabel, " \u2014 stats below describe the override, not the original source."]
208
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
209
- onPress: () => clearOverride(record.id),
210
- children: /*#__PURE__*/_jsx(Text, {
211
- style: styles.overrideRestore,
212
- children: "RESTORE"
213
- })
214
- })]
215
- }) : null, record.uri && record.sourceKind !== "data" ? /*#__PURE__*/_jsx(View, {
216
- style: styles.previewBox,
217
- children: /*#__PURE__*/_jsx(Image, {
218
- testID: "buoy-internal-images-preview",
219
- source: {
220
- uri: record.uri
221
- },
222
- style: styles.preview,
223
- resizeMode: "contain"
224
- })
225
- }) : null, /*#__PURE__*/_jsxs(Section, {
226
- title: "SOURCE",
227
- children: [/*#__PURE__*/_jsx(Row, {
228
- label: "URI",
229
- value: record.uri || "(none)"
230
- }), /*#__PURE__*/_jsx(Row, {
231
- label: "Kind",
232
- value: record.sourceKind
233
- }), /*#__PURE__*/_jsx(Row, {
234
- label: "Library",
235
- value: record.lib === "expo" ? "expo-image" : "RN <Image>"
236
- }), record.hasAltText === false ? /*#__PURE__*/_jsx(Row, {
237
- label: "Alt text",
238
- value: "missing \u2014 add alt/accessibilityLabel",
239
- color: macOSColors.semantic.warning
240
- }) : null, record.mediaType != null ? /*#__PURE__*/_jsx(Row, {
241
- label: "Media type",
242
- value: String(record.mediaType)
243
- }) : null, record.isAnimated != null ? /*#__PURE__*/_jsx(Row, {
244
- label: "Animated",
245
- value: record.isAnimated ? "yes" : "no"
246
- }) : null]
247
- }), /*#__PURE__*/_jsxs(Section, {
248
- title: "LOAD",
249
- children: [/*#__PURE__*/_jsx(Row, {
250
- label: "Status",
251
- value: record.status,
252
- color: record.status === "error" ? macOSColors.semantic.error : record.status === "loaded" ? macOSColors.semantic.success : macOSColors.semantic.warning
253
- }), /*#__PURE__*/_jsx(Row, {
254
- label: "Duration",
255
- value: formatMs(record.durationMs)
256
- }), record.displayedAt && record.loadedAt ? /*#__PURE__*/_jsx(Row, {
257
- label: "Decode \u2192 display",
258
- value: formatMs(Math.max(0, record.displayedAt - record.loadedAt))
259
- }) : null, /*#__PURE__*/_jsx(Row, {
260
- label: "Cache",
261
- value: `${cache.label}`,
262
- color: cache.color
263
- }), record.cacheVerdictSource ? /*#__PURE__*/_jsx(Row, {
264
- label: "Cache source",
265
- value: record.cacheVerdictSource === "event" ? "reported by expo-image onLoad" : "Image.queryCache after load"
266
- }) : null, record.progressSeen ? /*#__PURE__*/_jsx(Row, {
267
- label: "Downloaded",
268
- value: record.bytesTotal ? `${formatBytes(record.bytesLoaded ?? 0)} / ${formatBytes(record.bytesTotal)}` : "network transfer observed"
269
- }) : null, /*#__PURE__*/_jsx(Row, {
270
- label: "Load cycles",
271
- value: String(record.loadCount)
272
- }), /*#__PURE__*/_jsx(Row, {
273
- label: "Still mounted",
274
- value: record.mounted ? "yes" : "no"
275
- })]
276
- }), /*#__PURE__*/_jsxs(Section, {
277
- title: "DIMENSIONS",
278
- children: [record.intrinsic ? /*#__PURE__*/_jsx(Row, {
279
- label: "Decoded",
280
- value: `${record.intrinsic.width}×${record.intrinsic.height} px`
281
- }) : /*#__PURE__*/_jsx(Row, {
282
- label: "Decoded",
283
- value: "unknown"
284
- }), record.layout ? /*#__PURE__*/_jsx(Row, {
285
- label: "Displayed",
286
- value: `${Math.round(record.layout.width)}×${Math.round(record.layout.height)} dp (needs ${needed ? `${needed.width}×${needed.height}` : "?"} px @${Math.round(effectivePixelRatio() * 100) / 100}x)`
287
- }) : /*#__PURE__*/_jsx(Row, {
288
- label: "Displayed",
289
- value: "no layout yet"
290
- }), verdict.label ? /*#__PURE__*/_jsx(Row, {
291
- label: "Verdict",
292
- value: verdict.label,
293
- color: verdict.color
294
- }) : null, decoded > 0 ? /*#__PURE__*/_jsx(Row, {
295
- label: "Est. decoded memory",
296
- value: formatBytes(decoded)
297
- }) : null, wasted > 0 ? /*#__PURE__*/_jsx(Row, {
298
- label: "Est. wasted",
299
- value: `${formatBytes(wasted)} — resize the source to ${needed ? `${needed.width}×${needed.height}` : "the displayed size"}`,
300
- color: macOSColors.semantic.warning
301
- }) : null, (record.layoutShifts ?? 0) > 0 ? /*#__PURE__*/_jsx(Row, {
302
- label: "Layout shifts",
303
- value: `${record.layoutShifts} — box resized after load; reserve explicit width/height`,
304
- color: macOSColors.semantic.warning
305
- }) : null]
306
- }), record.sourceKind === "network" || record.sourceKind === "asset" ? /*#__PURE__*/_jsx(SavingsSection, {
307
- record: record
308
- }, `savings-${record.id}`) : null, record.status === "error" ? /*#__PURE__*/_jsxs(Section, {
309
- title: "ERROR",
310
- children: [/*#__PURE__*/_jsx(Row, {
311
- label: "Message",
312
- value: record.error ?? "unknown",
313
- color: macOSColors.semantic.error
314
- }), record.errorCode != null ? /*#__PURE__*/_jsx(Row, {
315
- label: "HTTP status",
316
- value: String(record.errorCode)
317
- }) : null, record.errorHeaders ? Object.entries(record.errorHeaders).map(([k, v]) => /*#__PURE__*/_jsx(Row, {
318
- label: k,
319
- value: String(v)
320
- }, k)) : null]
321
- }) : null, record.sourceKind === "network" && record.mounted ? /*#__PURE__*/_jsx(SwapUrlSection, {
322
- record: record
323
- }, record.id) : null, record.lib === "expo" ? /*#__PURE__*/_jsx(Section, {
324
- title: "CACHE ACTIONS (expo-image, global)",
325
- children: /*#__PURE__*/_jsxs(View, {
326
- style: styles.actionsRow,
327
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
328
- style: styles.actionButton,
329
- onPress: () => clearExpoCache("memory"),
330
- children: /*#__PURE__*/_jsx(Text, {
331
- style: styles.actionText,
332
- children: "Clear memory cache"
333
- })
334
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
335
- style: styles.actionButton,
336
- onPress: () => clearExpoCache("disk"),
337
- children: /*#__PURE__*/_jsx(Text, {
338
- style: styles.actionText,
339
- children: "Clear disk cache"
340
- })
341
- })]
342
- })
343
- }) : null]
344
- });
345
- }
346
- const styles = StyleSheet.create({
347
- container: {
348
- flex: 1
349
- },
350
- content: {
351
- padding: 12,
352
- gap: 12
353
- },
354
- previewBox: {
355
- height: 140,
356
- borderRadius: 8,
357
- backgroundColor: night.color.bg,
358
- overflow: "hidden"
359
- },
360
- preview: {
361
- width: "100%",
362
- height: "100%"
363
- },
364
- section: {
365
- gap: 6
366
- },
367
- sectionTitle: {
368
- color: night.color.textTertiary,
369
- fontSize: 10,
370
- fontWeight: "700",
371
- letterSpacing: 0.8
372
- },
373
- sectionBody: {
374
- backgroundColor: night.color.surface,
375
- borderRadius: 8,
376
- borderWidth: StyleSheet.hairlineWidth,
377
- borderColor: night.color.border,
378
- paddingHorizontal: 10,
379
- paddingVertical: 4
380
- },
381
- kvRow: {
382
- flexDirection: "row",
383
- paddingVertical: 5,
384
- gap: 12
385
- },
386
- kvLabel: {
387
- color: night.color.textSecondary,
388
- fontSize: 11,
389
- width: 110
390
- },
391
- kvValue: {
392
- color: night.color.text,
393
- fontSize: 11,
394
- flex: 1
395
- },
396
- actionsRow: {
397
- flexDirection: "row",
398
- gap: 8,
399
- paddingVertical: 8
400
- },
401
- actionButton: {
402
- backgroundColor: night.color.surfaceElevated,
403
- borderRadius: 6,
404
- paddingHorizontal: 10,
405
- paddingVertical: 6,
406
- borderWidth: StyleSheet.hairlineWidth,
407
- borderColor: night.color.border
408
- },
409
- actionText: {
410
- color: night.color.text,
411
- fontSize: 11,
412
- fontWeight: "600"
413
- },
414
- actionDisabled: {
415
- opacity: 0.4
416
- },
417
- urlInput: {
418
- color: night.color.text,
419
- fontSize: 11,
420
- fontFamily: monoFont,
421
- backgroundColor: night.color.surfaceElevated,
422
- borderRadius: 6,
423
- borderWidth: StyleSheet.hairlineWidth,
424
- borderColor: night.color.border,
425
- paddingHorizontal: 8,
426
- paddingVertical: 6,
427
- marginTop: 8
428
- },
429
- overrideBanner: {
430
- flexDirection: "row",
431
- alignItems: "center",
432
- gap: 10,
433
- padding: 10,
434
- borderRadius: 8,
435
- backgroundColor: night.color.warningSoft
436
- },
437
- overrideText: {
438
- flex: 1,
439
- color: night.color.warning,
440
- fontSize: 11,
441
- lineHeight: 15
442
- },
443
- overrideRestore: {
444
- color: night.color.warning,
445
- fontSize: 11,
446
- fontWeight: "700",
447
- letterSpacing: 0.5
448
- }
449
- });
450
- //# sourceMappingURL=ImageDetail.js.map
1
+ "use strict";import{loadExpoImage as C}from"../capture/nativeImageModules";import{useState as f}from"react";import{Image as T,ScrollView as B,StyleSheet as v,Text as c,TextInput as z,TouchableOpacity as m,View as u}from"react-native";import{macOSColors as d,night as a,monoFont as $}from"@buoy-gg/shared-ui";import{clearOverride as k,setOverride as x}from"../capture/overrides";import{proveSavings as D}from"../capture/savings";import{remoteImagesDispatch as I}from"../remote";import{effectivePixelRatio as P,estDecodedBytes as A,estWastedBytes as O,neededPixels as E}from"../store/imageEventStore";import{cacheBadge as V,formatBytes as y,formatMs as w,sizeVerdict as L}from"../format";import{jsx as l,jsxs as r,Fragment as W}from"react/jsx-runtime";function p({title:e,children:n}){return r(u,{style:t.section,children:[l(c,{style:t.sectionTitle,children:e}),l(u,{style:t.sectionBody,children:n})]})}function i({label:e,value:n,color:s}){return r(u,{style:t.kvRow,children:[l(c,{style:t.kvLabel,children:e}),l(c,{style:[t.kvValue,s?{color:s}:null],selectable:true,children:n})]})}function S(e){const n=I();if(n){void n("clearExpoCaches",{memory:e==="memory",disk:e==="disk"});return}try{const s=C();if(e==="memory")void s.Image.clearMemoryCache();else void s.Image.clearDiskCache()}catch{}}function M({record:e}){const[n,s]=f(e.uri);const o=n.trim();const h=e.mounted&&o.length>0&&o!==e.uri;return r(p,{title:"SWAP SOURCE URL",children:[l(z,{style:t.urlInput,value:n,onChangeText:s,placeholder:"https://\u2026",placeholderTextColor:a.color.placeholder,autoCapitalize:"none",autoCorrect:false,multiline:true}),r(u,{style:t.actionsRow,children:[l(m,{style:[t.actionButton,!h&&t.actionDisabled],disabled:!h,onPress:()=>x(e.id,{source:{type:"url",uri:o},label:"URL swapped"}),children:l(c,{style:t.actionText,children:"Apply"})}),l(m,{style:t.actionButton,onPress:()=>s(e.uri),children:l(c,{style:t.actionText,children:"Reset field"})})]})]})}function U({record:e}){const[n,s]=f(false);const[o,h]=f(null);const b=()=>{s(true);D(e).then(h).catch(g=>h({ok:false,message:String(g?.message??g)})).finally(()=>s(false))};return l(p,{title:"PROVE SAVINGS (re-encode on device)",children:o==null?l(u,{style:t.actionsRow,children:l(m,{style:t.actionButton,onPress:b,disabled:n,children:l(c,{style:t.actionText,children:n?"Re-encoding\u2026":"Re-encode at displayed size (WebP)"})})}):r(W,{children:[l(i,{label:"Result",value:o.message||(o.ok?"done":"failed"),color:o.ok?d.semantic.success:d.semantic.error}),o.originalBytes!=null?l(i,{label:"Original",value:`${y(o.originalBytes)} (${o.originalBytesSource})`}):null,o.optimizedBytes!=null?l(i,{label:"Optimized",value:`${y(o.optimizedBytes)} \u2014 ${o.optimizedDims?.width}\xD7${o.optimizedDims?.height} WebP`,color:d.semantic.success}):null,o.savedBytes!=null?l(i,{label:"Saved",value:`${y(o.savedBytes)} (${o.savedPct}%)`,color:d.semantic.success}):null,r(u,{style:t.actionsRow,children:[o.ok&&o.optimizedUri&&e.mounted?l(m,{style:t.actionButton,onPress:()=>x(e.id,{source:{type:"url",uri:o.optimizedUri},label:"Optimized preview"}),children:l(c,{style:t.actionText,children:"Preview in place"})}):null,l(m,{style:t.actionButton,onPress:b,disabled:n,children:l(c,{style:t.actionText,children:n?"Re-encoding\u2026":"Re-run"})})]})]})})}export function ImageDetail({record:e}){const n=L(e);const s=V(e);const o=E(e);const h=A(e);const b=O(e);return r(B,{style:t.container,contentContainerStyle:t.content,children:[e.overrideLabel?r(u,{style:t.overrideBanner,children:[r(c,{style:t.overrideText,children:["Simulation active: ",e.overrideLabel," \u2014 stats below describe the override, not the original source."]}),l(m,{onPress:()=>k(e.id),children:l(c,{style:t.overrideRestore,children:"RESTORE"})})]}):null,e.uri&&e.sourceKind!=="data"?l(u,{style:t.previewBox,children:l(T,{testID:"buoy-internal-images-preview",source:{uri:e.uri},style:t.preview,resizeMode:"contain"})}):null,r(p,{title:"SOURCE",children:[l(i,{label:"URI",value:e.uri||"(none)"}),l(i,{label:"Kind",value:e.sourceKind}),l(i,{label:"Library",value:e.lib==="web"?"Browser <img>":e.lib==="expo"?"expo-image":"RN <Image>"}),e.hasAltText===false?l(i,{label:"Alt text",value:"missing \u2014 add alt/accessibilityLabel",color:d.semantic.warning}):null,e.mediaType!=null?l(i,{label:"Media type",value:String(e.mediaType)}):null,e.isAnimated!=null?l(i,{label:"Animated",value:e.isAnimated?"yes":"no"}):null]}),r(p,{title:"LOAD",children:[l(i,{label:"Status",value:e.status,color:e.status==="error"?d.semantic.error:e.status==="loaded"?d.semantic.success:d.semantic.warning}),l(i,{label:"Duration",value:w(e.durationMs)}),e.displayedAt&&e.loadedAt?l(i,{label:"Decode \u2192 display",value:w(Math.max(0,e.displayedAt-e.loadedAt))}):null,l(i,{label:"Cache",value:`${s.label}`,color:s.color}),e.cacheVerdictSource?l(i,{label:"Cache source",value:e.cacheVerdictSource==="event"?"reported by expo-image onLoad":"Image.queryCache after load"}):null,e.progressSeen?l(i,{label:"Downloaded",value:e.bytesTotal?`${y(e.bytesLoaded??0)} / ${y(e.bytesTotal)}`:"network transfer observed"}):null,l(i,{label:"Load cycles",value:String(e.loadCount)}),l(i,{label:"Still mounted",value:e.mounted?"yes":"no"})]}),r(p,{title:"DIMENSIONS",children:[e.intrinsic?l(i,{label:"Decoded",value:`${e.intrinsic.width}\xD7${e.intrinsic.height} px`}):l(i,{label:"Decoded",value:"unknown"}),e.layout?l(i,{label:"Displayed",value:`${Math.round(e.layout.width)}\xD7${Math.round(e.layout.height)} dp (needs ${o?`${o.width}\xD7${o.height}`:"?"} px @${Math.round(P()*100)/100}x)`}):l(i,{label:"Displayed",value:"no layout yet"}),n.label?l(i,{label:"Verdict",value:n.label,color:n.color}):null,h>0?l(i,{label:"Est. decoded memory",value:y(h)}):null,b>0?l(i,{label:"Est. wasted",value:`${y(b)} \u2014 resize the source to ${o?`${o.width}\xD7${o.height}`:"the displayed size"}`,color:d.semantic.warning}):null,(e.layoutShifts??0)>0?l(i,{label:"Layout shifts",value:`${e.layoutShifts} \u2014 box resized after load; reserve explicit width/height`,color:d.semantic.warning}):null]}),e.sourceKind==="network"||e.sourceKind==="asset"?l(U,{record:e},`savings-${e.id}`):null,e.status==="error"?r(p,{title:"ERROR",children:[l(i,{label:"Message",value:e.error??"unknown",color:d.semantic.error}),e.errorCode!=null?l(i,{label:"HTTP status",value:String(e.errorCode)}):null,e.errorHeaders?Object.entries(e.errorHeaders).map(([g,R])=>l(i,{label:g,value:String(R)},g)):null]}):null,e.sourceKind==="network"&&e.mounted?l(M,{record:e},e.id):null,e.lib==="expo"?l(p,{title:"CACHE ACTIONS (expo-image, global)",children:r(u,{style:t.actionsRow,children:[l(m,{style:t.actionButton,onPress:()=>S("memory"),children:l(c,{style:t.actionText,children:"Clear memory cache"})}),l(m,{style:t.actionButton,onPress:()=>S("disk"),children:l(c,{style:t.actionText,children:"Clear disk cache"})})]})}):null]})}const t=v.create({container:{flex:1},content:{padding:12,gap:12},previewBox:{height:140,borderRadius:8,backgroundColor:a.color.bg,overflow:"hidden"},preview:{width:"100%",height:"100%"},section:{gap:6},sectionTitle:{color:a.color.textTertiary,fontSize:10,fontWeight:"700",letterSpacing:.8},sectionBody:{backgroundColor:a.color.surface,borderRadius:8,borderWidth:v.hairlineWidth,borderColor:a.color.border,paddingHorizontal:10,paddingVertical:4},kvRow:{flexDirection:"row",paddingVertical:5,gap:12},kvLabel:{color:a.color.textSecondary,fontSize:11,width:110},kvValue:{color:a.color.text,fontSize:11,flex:1},actionsRow:{flexDirection:"row",gap:8,paddingVertical:8},actionButton:{backgroundColor:a.color.surfaceElevated,borderRadius:6,paddingHorizontal:10,paddingVertical:6,borderWidth:v.hairlineWidth,borderColor:a.color.border},actionText:{color:a.color.text,fontSize:11,fontWeight:"600"},actionDisabled:{opacity:.4},urlInput:{color:a.color.text,fontSize:11,fontFamily:$,backgroundColor:a.color.surfaceElevated,borderRadius:6,borderWidth:v.hairlineWidth,borderColor:a.color.border,paddingHorizontal:8,paddingVertical:6,marginTop:8},overrideBanner:{flexDirection:"row",alignItems:"center",gap:10,padding:10,borderRadius:8,backgroundColor:a.color.warningSoft},overrideText:{flex:1,color:a.color.warning,fontSize:11,lineHeight:15},overrideRestore:{color:a.color.warning,fontSize:11,fontWeight:"700",letterSpacing:.5}});
@@ -1,154 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * ImageDetailFooter — bottom navbar for the detail view: previous/next
5
- * stepping through the captured list at the edges, per-image actions in the
6
- * middle (hard reload / retry), with inline action feedback. Styling matches
7
- * shared-ui's EventStepperFooter so it reads like the rest of Buoy.
8
- *
9
- * Mount with key={record.id} so action feedback resets when stepping.
10
- */
11
-
12
- import { useState } from "react";
13
- import { StyleSheet, TouchableOpacity } from "react-native";
14
- import { ChevronLeft, ChevronRight, FooterButton, FooterMessage, FooterRow, RefreshCw, ToolFooter, night } from "@buoy-gg/shared-ui";
15
- import { hardReloadRecord, retryRecord } from "../capture/actions";
16
- import { clearOverride, flashRecord, setOverride } from "../capture/overrides";
17
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
- export function ImageDetailFooter({
19
- record,
20
- hasPrevious,
21
- hasNext,
22
- onPrevious,
23
- onNext
24
- }) {
25
- const [message, setMessage] = useState(null);
26
- const runAction = action => {
27
- setMessage("…");
28
- action().then(result => setMessage(result.message)).catch(error => setMessage(String(error?.message ?? error)));
29
- };
30
- const actionsDisabled = !record.mounted;
31
- const overrideActive = record.overrideLabel != null;
32
- const simulate = (source, label) => {
33
- setOverride(record.id, {
34
- source,
35
- label
36
- });
37
- setMessage(`${label} — Restore to undo`);
38
- };
39
- return /*#__PURE__*/_jsxs(ToolFooter, {
40
- children: [message ? /*#__PURE__*/_jsx(FooterMessage, {
41
- children: message
42
- }) : actionsDisabled ? /*#__PURE__*/_jsx(FooterMessage, {
43
- tone: "muted",
44
- children: "Instance unmounted \u2014 reload actions need the image on screen."
45
- }) : null, /*#__PURE__*/_jsxs(FooterRow, {
46
- style: {
47
- gap: 8
48
- },
49
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
50
- onPress: onPrevious,
51
- disabled: !hasPrevious,
52
- style: [styles.navButton, !hasPrevious && styles.disabled],
53
- activeOpacity: 0.7,
54
- accessibilityLabel: "Previous image",
55
- children: /*#__PURE__*/_jsx(ChevronLeft, {
56
- size: 20,
57
- color: hasPrevious ? night.color.accent : night.color.textTertiary
58
- })
59
- }), /*#__PURE__*/_jsx(FooterButton, {
60
- label: "Hard reload",
61
- size: "md",
62
- disabled: actionsDisabled,
63
- icon: /*#__PURE__*/_jsx(RefreshCw, {
64
- size: 13,
65
- color: actionsDisabled ? night.color.textTertiary : night.color.info
66
- }),
67
- onPress: () => runAction(() => hardReloadRecord(record))
68
- }), /*#__PURE__*/_jsx(FooterButton, {
69
- label: record.status === "error" ? "Retry" : "Reload",
70
- size: "md",
71
- disabled: actionsDisabled,
72
- onPress: () => runAction(() => retryRecord(record))
73
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
74
- onPress: onNext,
75
- disabled: !hasNext,
76
- style: [styles.navButton, !hasNext && styles.disabled],
77
- activeOpacity: 0.7,
78
- accessibilityLabel: "Next image",
79
- children: /*#__PURE__*/_jsx(ChevronRight, {
80
- size: 20,
81
- color: hasNext ? night.color.accent : night.color.textTertiary
82
- })
83
- })]
84
- }), /*#__PURE__*/_jsxs(FooterRow, {
85
- style: {
86
- gap: 8
87
- },
88
- children: [/*#__PURE__*/_jsx(SimButton, {
89
- label: "Error",
90
- disabled: actionsDisabled,
91
- onPress: () => simulate({
92
- type: "error"
93
- }, "Forced error")
94
- }), /*#__PURE__*/_jsx(SimButton, {
95
- label: "Loading",
96
- disabled: actionsDisabled,
97
- onPress: () => simulate({
98
- type: "hang"
99
- }, "Forced loading")
100
- }), /*#__PURE__*/_jsx(SimButton, {
101
- label: "Blank",
102
- disabled: actionsDisabled,
103
- onPress: () => simulate({
104
- type: "blank"
105
- }, "Blanked")
106
- }), /*#__PURE__*/_jsx(SimButton, {
107
- label: "Find",
108
- disabled: actionsDisabled,
109
- onPress: () => {
110
- flashRecord(record.id);
111
- setMessage("Flashing a red border on the image (2.5s)");
112
- }
113
- }), /*#__PURE__*/_jsx(SimButton, {
114
- label: "Restore",
115
- accent: true,
116
- disabled: !overrideActive,
117
- onPress: () => {
118
- clearOverride(record.id);
119
- setMessage("Override removed — original source restored");
120
- }
121
- })]
122
- })]
123
- });
124
- }
125
- function SimButton({
126
- label,
127
- onPress,
128
- disabled,
129
- accent
130
- }) {
131
- return /*#__PURE__*/_jsx(FooterButton, {
132
- label: label,
133
- onPress: onPress,
134
- disabled: disabled,
135
- accent: accent,
136
- style: {
137
- paddingVertical: 7
138
- }
139
- });
140
- }
141
- const styles = StyleSheet.create({
142
- navButton: {
143
- paddingHorizontal: 10,
144
- paddingVertical: 8,
145
- borderRadius: 6,
146
- backgroundColor: night.color.button,
147
- alignItems: "center",
148
- justifyContent: "center"
149
- },
150
- disabled: {
151
- opacity: 0.4
152
- }
153
- });
154
- //# sourceMappingURL=ImageDetailFooter.js.map
1
+ "use strict";import{useState as P}from"react";import{StyleSheet as x,TouchableOpacity as h}from"react-native";import{ChevronLeft as B,ChevronRight as z,FooterButton as u,FooterMessage as f,FooterRow as v,RefreshCw as O,ToolFooter as j,night as s}from"@buoy-gg/shared-ui";import{hardReloadRecord as C,retryRecord as L}from"../capture/actions";import{clearOverride as S,flashRecord as T,setOverride as k}from"../capture/overrides";import{jsx as e,jsxs as g}from"react/jsx-runtime";export function ImageDetailFooter({record:t,hasPrevious:i,hasNext:n,onPrevious:c,onNext:R}){const[y,a]=P(null);const p=m=>{a("\u2026");m().then(r=>a(r.message)).catch(r=>a(String(r?.message??r)))};const o=!t.mounted;const F=t.overrideLabel!=null;const b=(m,r)=>{k(t.id,{source:m,label:r});a(`${r} \u2014 Restore to undo`)};return g(j,{children:[y?e(f,{children:y}):o?e(f,{tone:"muted",children:"Instance unmounted \u2014 reload actions need the image on screen."}):null,g(v,{style:{gap:8},children:[e(h,{onPress:c,disabled:!i,style:[d.navButton,!i&&d.disabled],activeOpacity:.7,accessibilityLabel:"Previous image",children:e(B,{size:20,color:i?s.color.accent:s.color.textTertiary})}),e(u,{label:"Hard reload",size:"md",disabled:o,icon:e(O,{size:13,color:o?s.color.textTertiary:s.color.info}),onPress:()=>p(()=>C(t))}),e(u,{label:t.status==="error"?"Retry":"Reload",size:"md",disabled:o,onPress:()=>p(()=>L(t))}),e(h,{onPress:R,disabled:!n,style:[d.navButton,!n&&d.disabled],activeOpacity:.7,accessibilityLabel:"Next image",children:e(z,{size:20,color:n?s.color.accent:s.color.textTertiary})})]}),g(v,{style:{gap:8},children:[e(l,{label:"Error",disabled:o,onPress:()=>b({type:"error"},"Forced error")}),e(l,{label:"Loading",disabled:o,onPress:()=>b({type:"hang"},"Forced loading")}),e(l,{label:"Blank",disabled:o,onPress:()=>b({type:"blank"},"Blanked")}),e(l,{label:"Find",disabled:o,onPress:()=>{T(t.id);a("Flashing a red border on the image (2.5s)")}}),e(l,{label:"Restore",accent:true,disabled:!F,onPress:()=>{S(t.id);a("Override removed \u2014 original source restored")}})]})]})}function l({label:t,onPress:i,disabled:n,accent:c}){return e(u,{label:t,onPress:i,disabled:n,accent:c,style:{paddingVertical:7}})}const d=x.create({navButton:{paddingHorizontal:10,paddingVertical:8,borderRadius:6,backgroundColor:s.color.button,alignItems:"center",justifyContent:"center"},disabled:{opacity:.4}});