@buoy-gg/assets 7.0.41 → 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 (204) hide show
  1. package/README.md +12 -73
  2. package/bundlers/vite.mjs +1 -53
  3. package/lib/commonjs/autoCapture.js +1 -5
  4. package/lib/commonjs/autoCapture.web.js +1 -6
  5. package/lib/commonjs/capture/assetLifecycle.js +1 -11
  6. package/lib/commonjs/capture/assetLifecycle.web.js +1 -20
  7. package/lib/commonjs/capture/browserAssets.js +1 -83
  8. package/lib/commonjs/capture/expo.js +1 -46
  9. package/lib/commonjs/capture/expo.web.js +1 -19
  10. package/lib/commonjs/capture/install.js +1 -91
  11. package/lib/commonjs/capture/install.web.js +1 -75
  12. package/lib/commonjs/capture/isBuoyAsset.js +1 -23
  13. package/lib/commonjs/capture/measure.js +1 -187
  14. package/lib/commonjs/capture/metroGraph.js +1 -175
  15. package/lib/commonjs/capture/metroGraph.web.js +1 -27
  16. package/lib/commonjs/capture/registry.js +1 -127
  17. package/lib/commonjs/components/AssetDetail.js +1 -211
  18. package/lib/commonjs/components/AssetImage.js +1 -66
  19. package/lib/commonjs/components/AssetRow.js +1 -232
  20. package/lib/commonjs/components/AssetsFilterBar.js +1 -105
  21. package/lib/commonjs/components/AssetsHeader.js +1 -73
  22. package/lib/commonjs/components/AssetsIcon.js +1 -19
  23. package/lib/commonjs/components/AssetsListControls.js +1 -81
  24. package/lib/commonjs/components/AssetsModal.js +1 -378
  25. package/lib/commonjs/components/ScanNotice.js +1 -86
  26. package/lib/commonjs/components/optionalExpoImage.js +1 -16
  27. package/lib/commonjs/components/optionalExpoImage.web.js +1 -10
  28. package/lib/commonjs/export.js +1 -62
  29. package/lib/commonjs/format.js +1 -59
  30. package/lib/commonjs/hooks/useAssetRecords.js +1 -22
  31. package/lib/commonjs/hooks/useAssetScanStatus.js +1 -28
  32. package/lib/commonjs/index.js +1 -294
  33. package/lib/commonjs/preset.js +1 -39
  34. package/lib/commonjs/remote.js +1 -68
  35. package/lib/commonjs/state.js +1 -51
  36. package/lib/commonjs/store/assetStore.js +1 -256
  37. package/lib/commonjs/store/baseline.js +1 -175
  38. package/lib/commonjs/store/insights.js +1 -120
  39. package/lib/commonjs/sync/assetsSyncAdapter.js +1 -157
  40. package/lib/commonjs/types.js +0 -1
  41. package/lib/commonjs/utils/deviceScale.js +1 -50
  42. package/lib/commonjs/utils/testIDs.js +1 -110
  43. package/lib/module/autoCapture.js +1 -5
  44. package/lib/module/autoCapture.web.js +1 -5
  45. package/lib/module/capture/assetLifecycle.js +1 -7
  46. package/lib/module/capture/assetLifecycle.web.js +1 -16
  47. package/lib/module/capture/browserAssets.js +1 -75
  48. package/lib/module/capture/expo.js +1 -41
  49. package/lib/module/capture/expo.web.js +1 -14
  50. package/lib/module/capture/install.js +1 -85
  51. package/lib/module/capture/install.web.js +1 -68
  52. package/lib/module/capture/isBuoyAsset.js +1 -18
  53. package/lib/module/capture/measure.js +1 -180
  54. package/lib/module/capture/metroGraph.js +1 -168
  55. package/lib/module/capture/metroGraph.web.js +1 -21
  56. package/lib/module/capture/registry.js +1 -120
  57. package/lib/module/components/AssetDetail.js +1 -207
  58. package/lib/module/components/AssetImage.js +1 -62
  59. package/lib/module/components/AssetRow.js +1 -228
  60. package/lib/module/components/AssetsFilterBar.js +1 -101
  61. package/lib/module/components/AssetsHeader.js +1 -69
  62. package/lib/module/components/AssetsIcon.js +1 -12
  63. package/lib/module/components/AssetsListControls.js +1 -77
  64. package/lib/module/components/AssetsModal.js +1 -373
  65. package/lib/module/components/ScanNotice.js +1 -82
  66. package/lib/module/components/optionalExpoImage.js +1 -12
  67. package/lib/module/components/optionalExpoImage.web.js +1 -6
  68. package/lib/module/export.js +1 -57
  69. package/lib/module/format.js +1 -51
  70. package/lib/module/hooks/useAssetRecords.js +1 -18
  71. package/lib/module/hooks/useAssetScanStatus.js +1 -23
  72. package/lib/module/index.js +1 -64
  73. package/lib/module/preset.js +1 -35
  74. package/lib/module/remote.js +1 -61
  75. package/lib/module/state.js +1 -47
  76. package/lib/module/store/assetStore.js +1 -240
  77. package/lib/module/store/baseline.js +1 -167
  78. package/lib/module/store/insights.js +1 -116
  79. package/lib/module/sync/assetsSyncAdapter.js +1 -153
  80. package/lib/module/types.js +0 -1
  81. package/lib/module/utils/deviceScale.js +1 -46
  82. package/lib/module/utils/testIDs.js +1 -99
  83. package/lib/web/index.mjs +3 -4
  84. package/package.json +2 -2
  85. package/lib/commonjs/autoCapture.js.map +0 -1
  86. package/lib/commonjs/autoCapture.web.js.map +0 -1
  87. package/lib/commonjs/capture/assetLifecycle.js.map +0 -1
  88. package/lib/commonjs/capture/assetLifecycle.web.js.map +0 -1
  89. package/lib/commonjs/capture/browserAssets.js.map +0 -1
  90. package/lib/commonjs/capture/expo.js.map +0 -1
  91. package/lib/commonjs/capture/expo.web.js.map +0 -1
  92. package/lib/commonjs/capture/install.js.map +0 -1
  93. package/lib/commonjs/capture/install.web.js.map +0 -1
  94. package/lib/commonjs/capture/isBuoyAsset.js.map +0 -1
  95. package/lib/commonjs/capture/measure.js.map +0 -1
  96. package/lib/commonjs/capture/metroGraph.js.map +0 -1
  97. package/lib/commonjs/capture/metroGraph.web.js.map +0 -1
  98. package/lib/commonjs/capture/registry.js.map +0 -1
  99. package/lib/commonjs/components/AssetDetail.js.map +0 -1
  100. package/lib/commonjs/components/AssetImage.js.map +0 -1
  101. package/lib/commonjs/components/AssetRow.js.map +0 -1
  102. package/lib/commonjs/components/AssetsFilterBar.js.map +0 -1
  103. package/lib/commonjs/components/AssetsHeader.js.map +0 -1
  104. package/lib/commonjs/components/AssetsIcon.js.map +0 -1
  105. package/lib/commonjs/components/AssetsListControls.js.map +0 -1
  106. package/lib/commonjs/components/AssetsModal.js.map +0 -1
  107. package/lib/commonjs/components/ScanNotice.js.map +0 -1
  108. package/lib/commonjs/components/optionalExpoImage.js.map +0 -1
  109. package/lib/commonjs/components/optionalExpoImage.web.js.map +0 -1
  110. package/lib/commonjs/export.js.map +0 -1
  111. package/lib/commonjs/format.js.map +0 -1
  112. package/lib/commonjs/hooks/useAssetRecords.js.map +0 -1
  113. package/lib/commonjs/hooks/useAssetScanStatus.js.map +0 -1
  114. package/lib/commonjs/index.js.map +0 -1
  115. package/lib/commonjs/preset.js.map +0 -1
  116. package/lib/commonjs/remote.js.map +0 -1
  117. package/lib/commonjs/state.js.map +0 -1
  118. package/lib/commonjs/store/assetStore.js.map +0 -1
  119. package/lib/commonjs/store/baseline.js.map +0 -1
  120. package/lib/commonjs/store/insights.js.map +0 -1
  121. package/lib/commonjs/sync/assetsSyncAdapter.js.map +0 -1
  122. package/lib/commonjs/types.js.map +0 -1
  123. package/lib/commonjs/utils/deviceScale.js.map +0 -1
  124. package/lib/commonjs/utils/testIDs.js.map +0 -1
  125. package/lib/module/autoCapture.js.map +0 -1
  126. package/lib/module/autoCapture.web.js.map +0 -1
  127. package/lib/module/capture/assetLifecycle.js.map +0 -1
  128. package/lib/module/capture/assetLifecycle.web.js.map +0 -1
  129. package/lib/module/capture/browserAssets.js.map +0 -1
  130. package/lib/module/capture/expo.js.map +0 -1
  131. package/lib/module/capture/expo.web.js.map +0 -1
  132. package/lib/module/capture/install.js.map +0 -1
  133. package/lib/module/capture/install.web.js.map +0 -1
  134. package/lib/module/capture/isBuoyAsset.js.map +0 -1
  135. package/lib/module/capture/measure.js.map +0 -1
  136. package/lib/module/capture/metroGraph.js.map +0 -1
  137. package/lib/module/capture/metroGraph.web.js.map +0 -1
  138. package/lib/module/capture/registry.js.map +0 -1
  139. package/lib/module/components/AssetDetail.js.map +0 -1
  140. package/lib/module/components/AssetImage.js.map +0 -1
  141. package/lib/module/components/AssetRow.js.map +0 -1
  142. package/lib/module/components/AssetsFilterBar.js.map +0 -1
  143. package/lib/module/components/AssetsHeader.js.map +0 -1
  144. package/lib/module/components/AssetsIcon.js.map +0 -1
  145. package/lib/module/components/AssetsListControls.js.map +0 -1
  146. package/lib/module/components/AssetsModal.js.map +0 -1
  147. package/lib/module/components/ScanNotice.js.map +0 -1
  148. package/lib/module/components/optionalExpoImage.js.map +0 -1
  149. package/lib/module/components/optionalExpoImage.web.js.map +0 -1
  150. package/lib/module/export.js.map +0 -1
  151. package/lib/module/format.js.map +0 -1
  152. package/lib/module/hooks/useAssetRecords.js.map +0 -1
  153. package/lib/module/hooks/useAssetScanStatus.js.map +0 -1
  154. package/lib/module/index.js.map +0 -1
  155. package/lib/module/preset.js.map +0 -1
  156. package/lib/module/remote.js.map +0 -1
  157. package/lib/module/state.js.map +0 -1
  158. package/lib/module/store/assetStore.js.map +0 -1
  159. package/lib/module/store/baseline.js.map +0 -1
  160. package/lib/module/store/insights.js.map +0 -1
  161. package/lib/module/sync/assetsSyncAdapter.js.map +0 -1
  162. package/lib/module/types.js.map +0 -1
  163. package/lib/module/utils/deviceScale.js.map +0 -1
  164. package/lib/module/utils/testIDs.js.map +0 -1
  165. package/lib/typescript/autoCapture.d.ts.map +0 -1
  166. package/lib/typescript/autoCapture.web.d.ts.map +0 -1
  167. package/lib/typescript/capture/assetLifecycle.d.ts.map +0 -1
  168. package/lib/typescript/capture/assetLifecycle.web.d.ts.map +0 -1
  169. package/lib/typescript/capture/browserAssets.d.ts.map +0 -1
  170. package/lib/typescript/capture/expo.d.ts.map +0 -1
  171. package/lib/typescript/capture/expo.web.d.ts.map +0 -1
  172. package/lib/typescript/capture/install.d.ts.map +0 -1
  173. package/lib/typescript/capture/install.web.d.ts.map +0 -1
  174. package/lib/typescript/capture/isBuoyAsset.d.ts.map +0 -1
  175. package/lib/typescript/capture/measure.d.ts.map +0 -1
  176. package/lib/typescript/capture/metroGraph.d.ts.map +0 -1
  177. package/lib/typescript/capture/metroGraph.web.d.ts.map +0 -1
  178. package/lib/typescript/capture/registry.d.ts.map +0 -1
  179. package/lib/typescript/components/AssetDetail.d.ts.map +0 -1
  180. package/lib/typescript/components/AssetImage.d.ts.map +0 -1
  181. package/lib/typescript/components/AssetRow.d.ts.map +0 -1
  182. package/lib/typescript/components/AssetsFilterBar.d.ts.map +0 -1
  183. package/lib/typescript/components/AssetsHeader.d.ts.map +0 -1
  184. package/lib/typescript/components/AssetsIcon.d.ts.map +0 -1
  185. package/lib/typescript/components/AssetsListControls.d.ts.map +0 -1
  186. package/lib/typescript/components/AssetsModal.d.ts.map +0 -1
  187. package/lib/typescript/components/ScanNotice.d.ts.map +0 -1
  188. package/lib/typescript/components/optionalExpoImage.d.ts.map +0 -1
  189. package/lib/typescript/components/optionalExpoImage.web.d.ts.map +0 -1
  190. package/lib/typescript/export.d.ts.map +0 -1
  191. package/lib/typescript/format.d.ts.map +0 -1
  192. package/lib/typescript/hooks/useAssetRecords.d.ts.map +0 -1
  193. package/lib/typescript/hooks/useAssetScanStatus.d.ts.map +0 -1
  194. package/lib/typescript/index.d.ts.map +0 -1
  195. package/lib/typescript/preset.d.ts.map +0 -1
  196. package/lib/typescript/remote.d.ts.map +0 -1
  197. package/lib/typescript/state.d.ts.map +0 -1
  198. package/lib/typescript/store/assetStore.d.ts.map +0 -1
  199. package/lib/typescript/store/baseline.d.ts.map +0 -1
  200. package/lib/typescript/store/insights.d.ts.map +0 -1
  201. package/lib/typescript/sync/assetsSyncAdapter.d.ts.map +0 -1
  202. package/lib/typescript/types.d.ts.map +0 -1
  203. package/lib/typescript/utils/deviceScale.d.ts.map +0 -1
  204. package/lib/typescript/utils/testIDs.d.ts.map +0 -1
@@ -1,12 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * AssetsIcon — re-exported from the Buoy Icon Format.
5
- *
6
- * The artwork lives in shared/icons/assets.json and is generated into
7
- * @buoy-gg/floating-tools-core, so React Native, the desktop dashboard and
8
- * Flutter all render the same drawing. Edit the JSON, then `pnpm icons`.
9
- */
10
-
11
- export { AssetsIcon, ASSETS_ICON_COLOR } from "@buoy-gg/shared-ui";
12
- //# sourceMappingURL=AssetsIcon.js.map
1
+ "use strict";export{AssetsIcon,ASSETS_ICON_COLOR}from"@buoy-gg/shared-ui";
@@ -1,77 +1 @@
1
- "use strict";
2
-
3
- /** Asset list search and sort controls. */
4
-
5
- import { StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native";
6
- import { night } from "@buoy-gg/shared-ui";
7
- import { ASSETS_TEST_IDS } from "../utils/testIDs";
8
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
- const NEXT_SORT = {
10
- size: "name",
11
- name: "type",
12
- type: "size"
13
- };
14
- export function AssetsListControls({
15
- query,
16
- onQueryChange,
17
- sort,
18
- onSortChange
19
- }) {
20
- return /*#__PURE__*/_jsxs(View, {
21
- style: styles.row,
22
- children: [/*#__PURE__*/_jsx(TextInput, {
23
- style: styles.input,
24
- value: query,
25
- onChangeText: onQueryChange,
26
- placeholder: "Search assets\u2026",
27
- placeholderTextColor: night.color.placeholder,
28
- autoCapitalize: "none",
29
- autoCorrect: false,
30
- testID: ASSETS_TEST_IDS.search
31
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
32
- style: styles.button,
33
- onPress: () => onSortChange(NEXT_SORT[sort]),
34
- testID: ASSETS_TEST_IDS.sort,
35
- children: /*#__PURE__*/_jsxs(Text, {
36
- style: styles.buttonText,
37
- testID: `${ASSETS_TEST_IDS.sort}-label`,
38
- children: ["\u2193 ", sort.toUpperCase()]
39
- })
40
- })]
41
- });
42
- }
43
- const styles = StyleSheet.create({
44
- row: {
45
- flexDirection: "row",
46
- alignItems: "center",
47
- gap: 8,
48
- paddingHorizontal: 10,
49
- paddingBottom: 8
50
- },
51
- input: {
52
- flex: 1,
53
- backgroundColor: night.color.surfaceElevated,
54
- borderWidth: 1,
55
- borderColor: night.color.border,
56
- borderRadius: 8,
57
- paddingHorizontal: 10,
58
- paddingVertical: 6,
59
- color: night.color.text,
60
- fontSize: 12
61
- },
62
- button: {
63
- backgroundColor: night.color.surface,
64
- borderWidth: 1,
65
- borderColor: night.color.border,
66
- borderRadius: 8,
67
- paddingHorizontal: 10,
68
- paddingVertical: 7
69
- },
70
- buttonText: {
71
- color: night.color.textSecondary,
72
- fontSize: 10,
73
- fontWeight: "700",
74
- letterSpacing: 0.5
75
- }
76
- });
77
- //# sourceMappingURL=AssetsListControls.js.map
1
+ "use strict";import{StyleSheet as c,Text as d,TextInput as p,TouchableOpacity as u,View as b}from"react-native";import{night as o}from"@buoy-gg/shared-ui";import{ASSETS_TEST_IDS as t}from"../utils/testIDs";import{jsx as l,jsxs as a}from"react/jsx-runtime";const h={size:"name",name:"type",type:"size"};export function AssetsListControls({query:n,onQueryChange:s,sort:r,onSortChange:i}){return a(b,{style:e.row,children:[l(p,{style:e.input,value:n,onChangeText:s,placeholder:"Search assets\u2026",placeholderTextColor:o.color.placeholder,autoCapitalize:"none",autoCorrect:false,testID:t.search}),l(u,{style:e.button,onPress:()=>i(h[r]),testID:t.sort,children:a(d,{style:e.buttonText,testID:`${t.sort}-label`,children:["\u2193 ",r.toUpperCase()]})})]})}const e=c.create({row:{flexDirection:"row",alignItems:"center",gap:8,paddingHorizontal:10,paddingBottom:8},input:{flex:1,backgroundColor:o.color.surfaceElevated,borderWidth:1,borderColor:o.color.border,borderRadius:8,paddingHorizontal:10,paddingVertical:6,color:o.color.text,fontSize:12},button:{backgroundColor:o.color.surface,borderWidth:1,borderColor:o.color.border,borderRadius:8,paddingHorizontal:10,paddingVertical:7},buttonText:{color:o.color.textSecondary,fontSize:10,fontWeight:"700",letterSpacing:.5}});
@@ -1,373 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * AssetsModal — bottom-sheet wrapper rendered by FloatingDevTools when the
5
- * Assets tool is opened. Inventory list (largest first) → tap for detail.
6
- * Opening triggers a full rescan (registry walk + Metro graph + size
7
- * measurement); merely installing the package never hits the network.
8
- */
9
-
10
- import { useEffect, useState } from "react";
11
- import { FlatList, StyleSheet, Text, TouchableOpacity, View } from "react-native";
12
- import { EventStepperFooter, JsModal, macOSColors, night, windowDragProps, ToolEmptyState } from "@buoy-gg/shared-ui";
13
- import { getScanStatus, installAssetCapture, rescan } from "../capture/install";
14
- import { formatAssetMarkdown, formatInventoryMarkdown } from "../export";
15
- import { useAssetRecords } from "../hooks/useAssetRecords";
16
- import { useAssetScanStatus } from "../hooks/useAssetScanStatus";
17
- import { remoteAssetsDispatch } from "../remote";
18
- import { clearRecords, estDecodedBytes, getRecord } from "../store/assetStore";
19
- import { getDiff, loadBaseline } from "../store/baseline";
20
- import { AssetDetail } from "./AssetDetail";
21
- import { AssetRow } from "./AssetRow";
22
- import { AssetsFilterBar } from "./AssetsFilterBar";
23
- import { AssetsHeader } from "./AssetsHeader";
24
- import { AssetsListControls } from "./AssetsListControls";
25
- import { ScanNotice } from "./ScanNotice";
26
- import { ASSETS_TEST_IDS, assetFontChipTestID } from "../utils/testIDs";
27
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
28
- function FontFamilies({
29
- names
30
- }) {
31
- const families = [...new Set(names)];
32
- if (!families.length) return null;
33
- return /*#__PURE__*/_jsxs(View, {
34
- style: styles.fontsBox,
35
- testID: ASSETS_TEST_IDS.fonts,
36
- children: [/*#__PURE__*/_jsxs(Text, {
37
- style: styles.fontsTitle,
38
- testID: ASSETS_TEST_IDS.fontsTitle,
39
- children: ["LOADED FONT FAMILIES (", families.length, ")"]
40
- }), /*#__PURE__*/_jsx(View, {
41
- style: styles.fontsWrap,
42
- children: families.map(family => /*#__PURE__*/_jsx(Text, {
43
- style: styles.fontChip,
44
- testID: assetFontChipTestID(family),
45
- children: family
46
- }, family))
47
- })]
48
- });
49
- }
50
- function EmptyState({
51
- duplicates = false
52
- }) {
53
- return /*#__PURE__*/_jsx(ToolEmptyState, {
54
- testID: ASSETS_TEST_IDS.empty
55
- // scanning: a scan ran and found nothing — the copy says "try Rescan".
56
- ,
57
- otto: "scanning",
58
- title: duplicates ? "No duplicates found" : "No assets found yet",
59
- message: duplicates ? "No assets in this view share a content hash." : "Every bundled asset — images, fonts, video, audio — appears here with sizes, scale coverage, and runtime registration status. Try Rescan after navigating around the app."
60
- });
61
- }
62
-
63
- /** Largest first — measured bytes, falling back to decoded estimate. */
64
- function bySizeDesc(a, b) {
65
- return (b.sizeBytes ?? estDecodedBytes(b)) - (a.sizeBytes ?? estDecodedBytes(a));
66
- }
67
- const COMPARATORS = {
68
- size: bySizeDesc,
69
- name: (a, b) => a.name.localeCompare(b.name) || a.type.localeCompare(b.type),
70
- type: (a, b) => a.type.localeCompare(b.type) || bySizeDesc(a, b)
71
- };
72
- export function AssetsModal({
73
- visible,
74
- onClose,
75
- onBack,
76
- onMinimize,
77
- persistenceKey,
78
- inline = false
79
- }) {
80
- // Safety net: the index import normally installed this already.
81
- installAssetCapture();
82
- const {
83
- records,
84
- stats,
85
- insights
86
- } = useAssetRecords();
87
- const status = useAssetScanStatus();
88
- const [remoteError, setRemoteError] = useState(false);
89
- useEffect(() => {
90
- setRemoteError(false);
91
- }, [status.graphFetchInFlight, status.lastScanAt]);
92
- const retry = () => {
93
- setRemoteError(false);
94
- const remote = remoteAssetsDispatch();
95
- if (remote) void remote("rescan").catch(() => setRemoteError(true));else void rescan();
96
- };
97
- const [selectedId, setSelectedId] = useState(null);
98
- const [filter, setFilter] = useState("all");
99
- const [query, setQuery] = useState("");
100
- const [sort, setSort] = useState("size");
101
- const selected = selectedId != null ? getRecord(selectedId) : undefined;
102
-
103
- // Full scan on open — locally on device, forwarded on dashboard mirrors.
104
- // The baseline load is async and re-renders through the store emit.
105
- useEffect(() => {
106
- if (!visible) return;
107
- void loadBaseline();
108
- setRemoteError(false);
109
- const remote = remoteAssetsDispatch();
110
- if (remote) {
111
- remote("rescan").catch(() => setRemoteError(true));
112
- } else {
113
- void rescan();
114
- }
115
- }, [visible]);
116
- const duplicateIds = new Set(insights.duplicates.flatMap(group => group.ids));
117
- const sorted = [...records].sort(COMPARATORS[sort]);
118
- const trimmedQuery = query.trim().toLowerCase();
119
- const filtered = sorted.filter(r => {
120
- if (filter === "unused" && r.loaded) return false;
121
- if (filter === "duplicates" && !duplicateIds.has(r.id)) return false;
122
- if (filter !== "all" && filter !== "unused" && filter !== "duplicates" && r.kind !== filter) return false;
123
- if (trimmedQuery && !`${r.name}.${r.type}`.toLowerCase().includes(trimmedQuery)) {
124
- return false;
125
- }
126
- return true;
127
- });
128
- const selectedIndex = selected ? filtered.findIndex(r => r.id === selected.id) : -1;
129
- const stepTo = index => {
130
- const target = filtered[index];
131
- if (target) setSelectedId(target.id);
132
- };
133
-
134
- // Set lookups so each row's badge checks are O(1).
135
- const flagSets = {
136
- scaleGaps: new Set(insights.scaleGaps),
137
- scaleAnomalies: new Set(insights.scaleAnomalies),
138
- duplicates: duplicateIds,
139
- webpCandidates: new Set(insights.webpCandidates),
140
- overDecode: new Set(insights.overDecode),
141
- heavy: new Set([...insights.heavyGifs, ...insights.heavyFonts])
142
- };
143
-
144
- // The proportional size bar is relative to the largest asset in view.
145
- let maxBytes = 0;
146
- for (const r of filtered) {
147
- const bytes = r.sizeBytes ?? estDecodedBytes(r);
148
- if (bytes > maxBytes) maxBytes = bytes;
149
- }
150
- const getReport = () => selected ? formatAssetMarkdown(selected) : formatInventoryMarkdown(sorted, stats, insights, getScanStatus(), getDiff());
151
- const headerContent = /*#__PURE__*/_jsx(AssetsHeader, {
152
- stats: stats,
153
- measuring: status.measuring,
154
- discovering: status.graphFetchInFlight,
155
- detailOpen: selected != null,
156
- detailIndex: selectedIndex,
157
- detailTotal: filtered.length,
158
- onCloseDetail: () => setSelectedId(null),
159
- onClear: clearRecords,
160
- getReport: getReport,
161
- onBack: onBack
162
- });
163
- const body = selected ? /*#__PURE__*/_jsxs(View, {
164
- style: styles.detailContainer,
165
- children: [/*#__PURE__*/_jsx(AssetDetail, {
166
- record: selected,
167
- insights: insights
168
- }), selectedIndex >= 0 ? /*#__PURE__*/_jsx(EventStepperFooter, {
169
- currentIndex: selectedIndex,
170
- totalItems: filtered.length,
171
- itemLabel: "Asset",
172
- onPrevious: () => stepTo(selectedIndex - 1),
173
- onNext: () => stepTo(selectedIndex + 1)
174
- }) : null]
175
- }) : /*#__PURE__*/_jsxs(View, {
176
- style: styles.listContainer,
177
- children: [/*#__PURE__*/_jsx(AssetsFilterBar, {
178
- stats: stats,
179
- duplicateCount: duplicateIds.size,
180
- filter: filter,
181
- onChange: next => {
182
- setFilter(next);
183
- if (next === "duplicates") setQuery("");
184
- }
185
- }), /*#__PURE__*/_jsx(AssetsListControls, {
186
- query: query,
187
- onQueryChange: setQuery,
188
- sort: sort,
189
- onSortChange: setSort
190
- }), /*#__PURE__*/_jsx(ScanNotice, {
191
- status: status,
192
- total: stats.total,
193
- onRetry: retry
194
- }), remoteError && !status.graphFetchInFlight ? /*#__PURE__*/_jsxs(View, {
195
- style: styles.notice,
196
- children: [/*#__PURE__*/_jsx(Text, {
197
- style: styles.noticeText,
198
- children: "Couldn't request an asset scan from the device."
199
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
200
- accessibilityRole: "button",
201
- onPress: retry,
202
- children: /*#__PURE__*/_jsx(Text, {
203
- style: styles.retryText,
204
- children: "Retry"
205
- })
206
- })]
207
- }) : null, filter === "unused" ? /*#__PURE__*/_jsx(View, {
208
- style: styles.notice,
209
- children: /*#__PURE__*/_jsx(Text, {
210
- style: styles.noticeText,
211
- children: "Not seen in React Native's asset registry this app run. Assets loaded through other paths may appear here; this does not prove they are unused."
212
- })
213
- }) : null, filter === "font" ? /*#__PURE__*/_jsx(FontFamilies, {
214
- names: status.fontFamilies
215
- }) : null, /*#__PURE__*/_jsx(FlatList, {
216
- testID: ASSETS_TEST_IDS.list,
217
- data: filtered,
218
- keyExtractor: item => String(item.id),
219
- contentContainerStyle: styles.listContent,
220
- renderItem: ({
221
- item
222
- }) => /*#__PURE__*/_jsx(AssetRow, {
223
- record: item,
224
- flags: {
225
- scaleGap: flagSets.scaleGaps.has(item.id),
226
- scaleAnomaly: flagSets.scaleAnomalies.has(item.id),
227
- duplicate: flagSets.duplicates.has(item.id),
228
- webp: flagSets.webpCandidates.has(item.id),
229
- oversize: flagSets.overDecode.has(item.id),
230
- heavy: flagSets.heavy.has(item.id)
231
- },
232
- sizeFraction: maxBytes > 0 ? (item.sizeBytes ?? estDecodedBytes(item)) / maxBytes : 0
233
- // Guarded: programmatic invocations (automation) can call this
234
- // with an event object instead of an id.
235
- ,
236
- onPress: id => setSelectedId(typeof id === "number" ? id : null)
237
- }),
238
- ListEmptyComponent: status.graphFetchInFlight ? null : /*#__PURE__*/_jsx(EmptyState, {
239
- duplicates: filter === "duplicates"
240
- })
241
- })]
242
- });
243
-
244
- // The ONLY difference between phone and desktop is which shell the header
245
- // and body go in (same pattern as ImagesModal).
246
- if (inline) {
247
- return /*#__PURE__*/_jsxs(View, {
248
- style: styles.inlineRoot,
249
- children: [/*#__PURE__*/_jsx(View, {
250
- style: styles.inlineHeader,
251
- ...windowDragProps,
252
- children: headerContent
253
- }), body]
254
- });
255
- }
256
- return /*#__PURE__*/_jsx(JsModal, {
257
- visible: visible,
258
- onClose: onClose,
259
- onMinimize: onMinimize,
260
- persistenceKey: persistenceKey,
261
- header: {
262
- showToggleButton: true,
263
- customContent: headerContent
264
- },
265
- enablePersistence: true,
266
- initialMode: "bottomSheet",
267
- disableScrollWrapper: true,
268
- children: body
269
- });
270
- }
271
-
272
- /**
273
- * The same Assets tool rendered INLINE for hosts that place tools in a panel
274
- * rather than a modal (the desktop dashboard). One implementation only.
275
- */
276
- export function AssetsToolView(props = {}) {
277
- return /*#__PURE__*/_jsx(AssetsModal, {
278
- ...props,
279
- inline: true,
280
- visible: true,
281
- onClose: () => {}
282
- });
283
- }
284
- const styles = StyleSheet.create({
285
- inlineRoot: {
286
- flex: 1,
287
- minHeight: 0,
288
- // Transparent so the desktop workbench's hoisted sky (one field spanning
289
- // sidebar + panel) shows through — the shell paints its base beneath it.
290
- backgroundColor: "transparent"
291
- },
292
- inlineHeader: {
293
- paddingHorizontal: 12,
294
- paddingVertical: 8,
295
- borderBottomWidth: 1,
296
- borderBottomColor: macOSColors.border.default,
297
- // Clear, like Bench — the host's hoisted sky shows through the header.
298
- backgroundColor: "transparent"
299
- },
300
- listContainer: {
301
- flex: 1
302
- },
303
- listContent: {
304
- paddingBottom: 16
305
- },
306
- detailContainer: {
307
- flex: 1
308
- },
309
- notice: {
310
- marginHorizontal: 10,
311
- marginBottom: 8,
312
- padding: 10,
313
- borderRadius: 8,
314
- borderWidth: 1,
315
- borderColor: night.color.border,
316
- backgroundColor: night.color.surface,
317
- gap: 4
318
- },
319
- noticeText: {
320
- color: night.color.textSecondary,
321
- fontSize: 11
322
- },
323
- retryText: {
324
- color: night.color.text,
325
- fontSize: 12,
326
- paddingVertical: 6
327
- },
328
- fontsBox: {
329
- marginHorizontal: 10,
330
- marginTop: 8,
331
- padding: 10,
332
- borderRadius: 8,
333
- backgroundColor: night.color.surface,
334
- gap: 6
335
- },
336
- fontsTitle: {
337
- color: night.color.textTertiary,
338
- fontSize: 9,
339
- fontWeight: "700",
340
- letterSpacing: 0.5
341
- },
342
- fontsWrap: {
343
- flexDirection: "row",
344
- flexWrap: "wrap",
345
- gap: 6
346
- },
347
- fontChip: {
348
- color: night.color.textSecondary,
349
- fontSize: 10,
350
- backgroundColor: night.color.bg,
351
- borderRadius: 999,
352
- paddingHorizontal: 8,
353
- paddingVertical: 3,
354
- overflow: "hidden"
355
- },
356
- empty: {
357
- alignItems: "center",
358
- padding: 32,
359
- gap: 8
360
- },
361
- emptyTitle: {
362
- color: night.color.text,
363
- fontSize: 13,
364
- fontWeight: "600"
365
- },
366
- emptyText: {
367
- color: night.color.textSecondary,
368
- fontSize: 11,
369
- textAlign: "center",
370
- lineHeight: 16
371
- }
372
- });
373
- //# sourceMappingURL=AssetsModal.js.map
1
+ "use strict";import{useEffect as L,useState as S}from"react";import{FlatList as J,StyleSheet as K,Text as C,TouchableOpacity as U,View as d}from"react-native";import{EventStepperFooter as X,JsModal as Y,macOSColors as Z,night as i,windowDragProps as ee,ToolEmptyState as te}from"@buoy-gg/shared-ui";import{getScanStatus as oe,installAssetCapture as se,rescan as N}from"../capture/install";import{formatAssetMarkdown as re,formatInventoryMarkdown as ne}from"../export";import{useAssetRecords as ie}from"../hooks/useAssetRecords";import{useAssetScanStatus as ae}from"../hooks/useAssetScanStatus";import{remoteAssetsDispatch as W}from"../remote";import{clearRecords as le,estDecodedBytes as v,getRecord as ce}from"../store/assetStore";import{getDiff as de,loadBaseline as pe}from"../store/baseline";import{AssetDetail as ue}from"./AssetDetail";import{AssetRow as fe}from"./AssetRow";import{AssetsFilterBar as me}from"./AssetsFilterBar";import{AssetsHeader as he}from"./AssetsHeader";import{AssetsListControls as ge}from"./AssetsListControls";import{ScanNotice as ye}from"./ScanNotice";import{ASSETS_TEST_IDS as w,assetFontChipTestID as Se}from"../utils/testIDs";import{jsxs as m,jsx as t}from"react/jsx-runtime";function Ce({names:o}){const s=[...new Set(o)];if(!s.length)return null;return m(d,{style:r.fontsBox,testID:w.fonts,children:[m(C,{style:r.fontsTitle,testID:w.fontsTitle,children:["LOADED FONT FAMILIES (",s.length,")"]}),t(d,{style:r.fontsWrap,children:s.map(h=>t(C,{style:r.fontChip,testID:Se(h),children:h},h))})]})}function xe({duplicates:o=false}){return t(te,{testID:w.empty,otto:"scanning",title:o?"No duplicates found":"No assets found yet",message:o?"No assets in this view share a content hash.":"Every bundled asset \u2014 images, fonts, video, audio \u2014 appears here with sizes, scale coverage, and runtime registration status. Try Rescan after navigating around the app."})}function O(o,s){return(s.sizeBytes??v(s))-(o.sizeBytes??v(o))}const be={size:O,name:(o,s)=>o.name.localeCompare(s.name)||o.type.localeCompare(s.type),type:(o,s)=>o.type.localeCompare(s.type)||O(o,s)};export function AssetsModal({visible:o,onClose:s,onBack:h,onMinimize:P,persistenceKey:G,inline:V=false}){se();const{records:j,stats:x,insights:a}=ie();const l=ae();const[_,g]=S(false);L(()=>{g(false)},[l.graphFetchInFlight,l.lastScanAt]);const I=()=>{g(false);const e=W();if(e)void e("rescan").catch(()=>g(true));else void N()};const[z,T]=S(null);const[n,q]=S("all");const[D,F]=S("");const[R,Q]=S("size");const p=z!=null?ce(z):void 0;L(()=>{if(!o)return;void pe();g(false);const e=W();if(e){e("rescan").catch(()=>g(true))}else{void N()}},[o]);const A=new Set(a.duplicates.flatMap(e=>e.ids));const B=[...j].sort(be[R]);const E=D.trim().toLowerCase();const u=B.filter(e=>{if(n==="unused"&&e.loaded)return false;if(n==="duplicates"&&!A.has(e.id))return false;if(n!=="all"&&n!=="unused"&&n!=="duplicates"&&e.kind!==n)return false;if(E&&!`${e.name}.${e.type}`.toLowerCase().includes(E)){return false}return true});const y=p?u.findIndex(e=>e.id===p.id):-1;const k=e=>{const c=u[e];if(c)T(c.id)};const f={scaleGaps:new Set(a.scaleGaps),scaleAnomalies:new Set(a.scaleAnomalies),duplicates:A,webpCandidates:new Set(a.webpCandidates),overDecode:new Set(a.overDecode),heavy:new Set([...a.heavyGifs,...a.heavyFonts])};let b=0;for(const e of u){const c=e.sizeBytes??v(e);if(c>b)b=c}const $=()=>p?re(p):ne(B,x,a,oe(),de());const H=t(he,{stats:x,measuring:l.measuring,discovering:l.graphFetchInFlight,detailOpen:p!=null,detailIndex:y,detailTotal:u.length,onCloseDetail:()=>T(null),onClear:le,getReport:$,onBack:h});const M=p?m(d,{style:r.detailContainer,children:[t(ue,{record:p,insights:a}),y>=0?t(X,{currentIndex:y,totalItems:u.length,itemLabel:"Asset",onPrevious:()=>k(y-1),onNext:()=>k(y+1)}):null]}):m(d,{style:r.listContainer,children:[t(me,{stats:x,duplicateCount:A.size,filter:n,onChange:e=>{q(e);if(e==="duplicates")F("")}}),t(ge,{query:D,onQueryChange:F,sort:R,onSortChange:Q}),t(ye,{status:l,total:x.total,onRetry:I}),_&&!l.graphFetchInFlight?m(d,{style:r.notice,children:[t(C,{style:r.noticeText,children:"Couldn't request an asset scan from the device."}),t(U,{accessibilityRole:"button",onPress:I,children:t(C,{style:r.retryText,children:"Retry"})})]}):null,n==="unused"?t(d,{style:r.notice,children:t(C,{style:r.noticeText,children:"Not seen in React Native's asset registry this app run. Assets loaded through other paths may appear here; this does not prove they are unused."})}):null,n==="font"?t(Ce,{names:l.fontFamilies}):null,t(J,{testID:w.list,data:u,keyExtractor:e=>String(e.id),contentContainerStyle:r.listContent,renderItem:({item:e})=>t(fe,{record:e,flags:{scaleGap:f.scaleGaps.has(e.id),scaleAnomaly:f.scaleAnomalies.has(e.id),duplicate:f.duplicates.has(e.id),webp:f.webpCandidates.has(e.id),oversize:f.overDecode.has(e.id),heavy:f.heavy.has(e.id)},sizeFraction:b>0?(e.sizeBytes??v(e))/b:0,onPress:c=>T(typeof c==="number"?c:null)}),ListEmptyComponent:l.graphFetchInFlight?null:t(xe,{duplicates:n==="duplicates"})})]});if(V){return m(d,{style:r.inlineRoot,children:[t(d,{style:r.inlineHeader,...ee,children:H}),M]})}return t(Y,{visible:o,onClose:s,onMinimize:P,persistenceKey:G,header:{showToggleButton:true,customContent:H},enablePersistence:true,initialMode:"bottomSheet",disableScrollWrapper:true,children:M})}export function AssetsToolView(o={}){return t(AssetsModal,{...o,inline:true,visible:true,onClose:()=>{}})}const r=K.create({inlineRoot:{flex:1,minHeight:0,backgroundColor:"transparent"},inlineHeader:{paddingHorizontal:12,paddingVertical:8,borderBottomWidth:1,borderBottomColor:Z.border.default,backgroundColor:"transparent"},listContainer:{flex:1},listContent:{paddingBottom:16},detailContainer:{flex:1},notice:{marginHorizontal:10,marginBottom:8,padding:10,borderRadius:8,borderWidth:1,borderColor:i.color.border,backgroundColor:i.color.surface,gap:4},noticeText:{color:i.color.textSecondary,fontSize:11},retryText:{color:i.color.text,fontSize:12,paddingVertical:6},fontsBox:{marginHorizontal:10,marginTop:8,padding:10,borderRadius:8,backgroundColor:i.color.surface,gap:6},fontsTitle:{color:i.color.textTertiary,fontSize:9,fontWeight:"700",letterSpacing:.5},fontsWrap:{flexDirection:"row",flexWrap:"wrap",gap:6},fontChip:{color:i.color.textSecondary,fontSize:10,backgroundColor:i.color.bg,borderRadius:999,paddingHorizontal:8,paddingVertical:3,overflow:"hidden"},empty:{alignItems:"center",padding:32,gap:8},emptyTitle:{color:i.color.text,fontSize:13,fontWeight:"600"},emptyText:{color:i.color.textSecondary,fontSize:11,textAlign:"center",lineHeight:16}});
@@ -1,82 +1 @@
1
- "use strict";
2
-
3
- import { useEffect, useState } from "react";
4
- import { ActivityIndicator, StyleSheet, Text, TouchableOpacity, View } from "react-native";
5
- import { night } from "@buoy-gg/shared-ui";
6
- import { ASSETS_TEST_IDS } from "../utils/testIDs";
7
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
- export function ScanNotice({
9
- status,
10
- total,
11
- onRetry
12
- }) {
13
- const [slow, setSlow] = useState(false);
14
- useEffect(() => {
15
- setSlow(false);
16
- if (!status.graphFetchInFlight) return;
17
- // Start locally for older snapshots that omit the device timestamp.
18
- const elapsed = status.graphFetchStartedAt == null ? 0 : Math.max(0, Date.now() - status.graphFetchStartedAt);
19
- const timer = setTimeout(() => setSlow(true), Math.max(0, 8_000 - elapsed));
20
- return () => clearTimeout(timer);
21
- }, [status.graphFetchInFlight, status.graphFetchStartedAt]);
22
- const notices = [];
23
- if (status.browser && !status.graphSupported) notices.push('Showing resources this page has loaded. Add an asset manifest to include files the page has not requested.');
24
- if (status.graphFetchInFlight) {
25
- notices.push(`Finding assets… ${total} found so far.`);
26
- if (slow) notices.push("Still finding assets. This is taking longer than usual.");
27
- }
28
- if (!status.registryAvailable) {
29
- notices.push("The asset registry is unavailable in this environment.");
30
- }
31
- if (!status.graphFetchInFlight && !status.graphSupported && status.graphError) {
32
- notices.push("Showing runtime-registered assets only. A development server is needed to find more assets and measure their sizes.");
33
- }
34
- const failed = !status.graphFetchInFlight && status.graphSupported && !!status.graphError;
35
- if (failed) notices.push(`Couldn't finish finding assets. Showing ${total} assets found so far.`);
36
- if (status.measuring && !status.graphFetchInFlight) notices.push("Checking asset sizes…");
37
- if (notices.length === 0) return null;
38
- return /*#__PURE__*/_jsxs(View, {
39
- style: styles.notice,
40
- testID: ASSETS_TEST_IDS.notice,
41
- children: [status.graphFetchInFlight ? /*#__PURE__*/_jsx(ActivityIndicator, {
42
- size: "small",
43
- color: night.color.textSecondary,
44
- accessibilityLabel: "Finding assets",
45
- testID: ASSETS_TEST_IDS.discoverySpinner
46
- }) : null, notices.map(n => /*#__PURE__*/_jsx(Text, {
47
- style: styles.noticeText,
48
- testID: ASSETS_TEST_IDS.noticeText,
49
- children: n
50
- }, n)), failed ? /*#__PURE__*/_jsx(TouchableOpacity, {
51
- accessibilityRole: "button",
52
- onPress: onRetry,
53
- testID: ASSETS_TEST_IDS.retry,
54
- children: /*#__PURE__*/_jsx(Text, {
55
- style: styles.retryText,
56
- children: "Retry"
57
- })
58
- }) : null]
59
- });
60
- }
61
- const styles = StyleSheet.create({
62
- notice: {
63
- marginHorizontal: 10,
64
- marginBottom: 8,
65
- padding: 10,
66
- borderRadius: 8,
67
- borderWidth: 1,
68
- borderColor: night.color.border,
69
- backgroundColor: night.color.surface,
70
- gap: 4
71
- },
72
- noticeText: {
73
- color: night.color.textSecondary,
74
- fontSize: 11
75
- },
76
- retryText: {
77
- color: night.color.text,
78
- fontSize: 12,
79
- paddingVertical: 6
80
- }
81
- });
82
- //# sourceMappingURL=ScanNotice.js.map
1
+ "use strict";import{useEffect as f,useState as m}from"react";import{ActivityIndicator as u,StyleSheet as S,Text as c,TouchableOpacity as y,View as F}from"react-native";import{night as t}from"@buoy-gg/shared-ui";import{ASSETS_TEST_IDS as o}from"../utils/testIDs";import{jsx as n,jsxs as x}from"react/jsx-runtime";export function ScanNotice({status:e,total:a,onRetry:d}){const[g,h]=m(false);f(()=>{h(false);if(!e.graphFetchInFlight)return;const i=e.graphFetchStartedAt==null?0:Math.max(0,Date.now()-e.graphFetchStartedAt);const p=setTimeout(()=>h(true),Math.max(0,8e3-i));return()=>clearTimeout(p)},[e.graphFetchInFlight,e.graphFetchStartedAt]);const r=[];if(e.browser&&!e.graphSupported)r.push("Showing resources this page has loaded. Add an asset manifest to include files the page has not requested.");if(e.graphFetchInFlight){r.push(`Finding assets\u2026 ${a} found so far.`);if(g)r.push("Still finding assets. This is taking longer than usual.")}if(!e.registryAvailable){r.push("The asset registry is unavailable in this environment.")}if(!e.graphFetchInFlight&&!e.graphSupported&&e.graphError){r.push("Showing runtime-registered assets only. A development server is needed to find more assets and measure their sizes.")}const l=!e.graphFetchInFlight&&e.graphSupported&&!!e.graphError;if(l)r.push(`Couldn't finish finding assets. Showing ${a} assets found so far.`);if(e.measuring&&!e.graphFetchInFlight)r.push("Checking asset sizes\u2026");if(r.length===0)return null;return x(F,{style:s.notice,testID:o.notice,children:[e.graphFetchInFlight?n(u,{size:"small",color:t.color.textSecondary,accessibilityLabel:"Finding assets",testID:o.discoverySpinner}):null,r.map(i=>n(c,{style:s.noticeText,testID:o.noticeText,children:i},i)),l?n(y,{accessibilityRole:"button",onPress:d,testID:o.retry,children:n(c,{style:s.retryText,children:"Retry"})}):null]})}const s=S.create({notice:{marginHorizontal:10,marginBottom:8,padding:10,borderRadius:8,borderWidth:1,borderColor:t.color.border,backgroundColor:t.color.surface,gap:4},noticeText:{color:t.color.textSecondary,fontSize:11},retryText:{color:t.color.text,fontSize:12,paddingVertical:6}});
@@ -1,12 +1 @@
1
- "use strict";
2
-
3
- let ExpoImage = null;
4
- try {
5
- ExpoImage = require("expo-image").Image ?? null;
6
- } catch {
7
- // Optional peer: the package or its native module may be unavailable.
8
- }
9
- export function getExpoPreviewImage() {
10
- return ExpoImage;
11
- }
12
- //# sourceMappingURL=optionalExpoImage.js.map
1
+ "use strict";let e=null;try{e=require("expo-image").Image??null}catch{}export function getExpoPreviewImage(){return e}
@@ -1,6 +1 @@
1
- "use strict";
2
-
3
- export function getExpoPreviewImage() {
4
- return null;
5
- }
6
- //# sourceMappingURL=optionalExpoImage.web.js.map
1
+ "use strict";export function getExpoPreviewImage(){return null}
@@ -1,57 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Markdown reports for the shared ToolbarCopyButton — the paste-into-your-
5
- * issue-tracker view of the inventory.
6
- */
7
-
8
- import { formatBytes, formatDimensions, formatScales } from "./format";
9
- import { estDecodedBytes } from "./store/assetStore";
10
- export function formatAssetMarkdown(record) {
11
- const lines = [`# Asset: ${record.name}.${record.type}`, "", `- Kind: ${record.kind}`, `- Status: ${record.loaded ? "loaded at runtime" : "not observed in the runtime registry"}`];
12
- if (record.kind === "image") lines.push(`- Dimensions: ${formatDimensions(record)} (logical)`);
13
- if (record.scales.length) lines.push(`- Scales: ${formatScales(record.scales)}`);
14
- if (record.sizeBytes != null && record.sizeSource === "measured") {
15
- lines.push(`- Size (all variants): ${formatBytes(record.sizeBytes)}`);
16
- }
17
- const estimate = estDecodedBytes(record);
18
- if (estimate > 0) lines.push(`- Decoded memory: ~${formatBytes(estimate)}`);
19
- if (record.hash) lines.push(`- Hash: ${record.hash}`);
20
- if (record.httpServerLocation) lines.push(`- Location: ${record.httpServerLocation}`);
21
- if (record.fileSystemLocation) lines.push(`- Source dir: ${record.fileSystemLocation}`);
22
- if (record.resolvedUri) lines.push(`- Resolved URI: ${record.resolvedUri}`);
23
- return lines.join("\n");
24
- }
25
- export function formatInventoryMarkdown(records, stats, insights, scanStatus, diff) {
26
- const lines = ["# Asset inventory", "", `${stats.total} assets · ${stats.loaded} loaded · ${stats.neverLoaded} not observed` + (stats.totalBytes > 0 ? ` · ${formatBytes(stats.totalBytes)} measured (${stats.measuredCount}/${stats.total})` : "")];
27
- if (scanStatus.fontFamilies.length) {
28
- lines.push(`Fonts loaded: ${scanStatus.fontFamilies.join(", ")}`);
29
- }
30
- if (!scanStatus.graphSupported) {
31
- lines.push(scanStatus.graphError ? `_Metro bundle graph unavailable (${scanStatus.graphError}) — runtime-registered assets only._` : "_No Metro bundle graph — runtime-registered assets only (release build)._");
32
- }
33
- if (diff) {
34
- const net = diff.netBytes != null ? `${diff.netBytes >= 0 ? "+" : "−"}${formatBytes(Math.abs(diff.netBytes))} net` : "net size unknown";
35
- lines.push("", `## Since baseline (${new Date(diff.baselineSavedAt).toISOString()})`, `- ${diff.added.length} added · ${diff.removed.length} removed · ${diff.grown.length} grew · ${net}`);
36
- for (const gone of diff.removed.slice(0, 20)) {
37
- lines.push(`- removed: ${gone.name}.${gone.type}`);
38
- }
39
- }
40
- const findings = [];
41
- if (insights.neverLoaded.length) findings.push(`- ${insights.neverLoaded.length} shipped but not observed`);
42
- if (insights.duplicates.length) findings.push(`- ${insights.duplicates.length} duplicate content groups`);
43
- if (insights.webpCandidates.length) findings.push(`- ${insights.webpCandidates.length} WebP conversion candidates`);
44
- if (insights.scaleGaps.length) findings.push(`- ${insights.scaleGaps.length} scale-coverage gaps`);
45
- if (insights.overDecode.length) findings.push(`- ${insights.overDecode.length} images decoding larger than 2× the screen`);
46
- if (insights.heavyGifs.length) findings.push(`- ${insights.heavyGifs.length} heavy GIFs (convert to video/Lottie)`);
47
- if (insights.heavyFonts.length) findings.push(`- ${insights.heavyFonts.length} heavy fonts (subset candidates)`);
48
- if (insights.scaleAnomalies.length) findings.push(`- ${insights.scaleAnomalies.length} scale-variant anomalies (check exports)`);
49
- if (findings.length) lines.push("", "## Findings", ...findings);
50
- lines.push("", "## Assets (largest first)", "", "| Asset | Kind | Dimensions | Scales | Size | Loaded |", "| --- | --- | --- | --- | --- | --- |");
51
- for (const r of records) {
52
- const size = r.sizeBytes != null && r.sizeSource === "measured" ? formatBytes(r.sizeBytes) : estDecodedBytes(r) > 0 ? `~${formatBytes(estDecodedBytes(r))}` : "—";
53
- lines.push(`| ${r.name}.${r.type} | ${r.kind} | ${r.kind === "image" ? formatDimensions(r) : "—"} | ${formatScales(r.scales)} | ${size} | ${r.loaded ? "yes" : "NO"} |`);
54
- }
55
- return lines.join("\n");
56
- }
57
- //# sourceMappingURL=export.js.map
1
+ "use strict";import{formatBytes as i,formatDimensions as u,formatScales as p}from"./format";import{estDecodedBytes as d}from"./store/assetStore";export function formatAssetMarkdown(e){const t=[`# Asset: ${e.name}.${e.type}`,"",`- Kind: ${e.kind}`,`- Status: ${e.loaded?"loaded at runtime":"not observed in the runtime registry"}`];if(e.kind==="image")t.push(`- Dimensions: ${u(e)} (logical)`);if(e.scales.length)t.push(`- Scales: ${p(e.scales)}`);if(e.sizeBytes!=null&&e.sizeSource==="measured"){t.push(`- Size (all variants): ${i(e.sizeBytes)}`)}const n=d(e);if(n>0)t.push(`- Decoded memory: ~${i(n)}`);if(e.hash)t.push(`- Hash: ${e.hash}`);if(e.httpServerLocation)t.push(`- Location: ${e.httpServerLocation}`);if(e.fileSystemLocation)t.push(`- Source dir: ${e.fileSystemLocation}`);if(e.resolvedUri)t.push(`- Resolved URI: ${e.resolvedUri}`);return t.join("\n")}export function formatInventoryMarkdown(e,t,n,h,a){const l=["# Asset inventory","",`${t.total} assets \xB7 ${t.loaded} loaded \xB7 ${t.neverLoaded} not observed`+(t.totalBytes>0?` \xB7 ${i(t.totalBytes)} measured (${t.measuredCount}/${t.total})`:"")];if(h.fontFamilies.length){l.push(`Fonts loaded: ${h.fontFamilies.join(", ")}`)}if(!h.graphSupported){l.push(h.graphError?`_Metro bundle graph unavailable (${h.graphError}) \u2014 runtime-registered assets only._`:"_No Metro bundle graph \u2014 runtime-registered assets only (release build)._")}if(a){const s=a.netBytes!=null?`${a.netBytes>=0?"+":"\u2212"}${i(Math.abs(a.netBytes))} net`:"net size unknown";l.push("",`## Since baseline (${new Date(a.baselineSavedAt).toISOString()})`,`- ${a.added.length} added \xB7 ${a.removed.length} removed \xB7 ${a.grown.length} grew \xB7 ${s}`);for(const r of a.removed.slice(0,20)){l.push(`- removed: ${r.name}.${r.type}`)}}const o=[];if(n.neverLoaded.length)o.push(`- ${n.neverLoaded.length} shipped but not observed`);if(n.duplicates.length)o.push(`- ${n.duplicates.length} duplicate content groups`);if(n.webpCandidates.length)o.push(`- ${n.webpCandidates.length} WebP conversion candidates`);if(n.scaleGaps.length)o.push(`- ${n.scaleGaps.length} scale-coverage gaps`);if(n.overDecode.length)o.push(`- ${n.overDecode.length} images decoding larger than 2\xD7 the screen`);if(n.heavyGifs.length)o.push(`- ${n.heavyGifs.length} heavy GIFs (convert to video/Lottie)`);if(n.heavyFonts.length)o.push(`- ${n.heavyFonts.length} heavy fonts (subset candidates)`);if(n.scaleAnomalies.length)o.push(`- ${n.scaleAnomalies.length} scale-variant anomalies (check exports)`);if(o.length)l.push("","## Findings",...o);l.push("","## Assets (largest first)","","| Asset | Kind | Dimensions | Scales | Size | Loaded |","| --- | --- | --- | --- | --- | --- |");for(const s of e){const r=s.sizeBytes!=null&&s.sizeSource==="measured"?i(s.sizeBytes):d(s)>0?`~${i(d(s))}`:"\u2014";l.push(`| ${s.name}.${s.type} | ${s.kind} | ${s.kind==="image"?u(s):"\u2014"} | ${p(s.scales)} | ${r} | ${s.loaded?"yes":"NO"} |`)}return l.join("\n")}