@buoy-gg/assets 7.0.29 → 7.0.31
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.
- package/lib/commonjs/components/AssetDetail.js +10 -1
- package/lib/commonjs/components/AssetDetail.js.map +1 -1
- package/lib/commonjs/components/AssetRow.js +13 -4
- package/lib/commonjs/components/AssetRow.js.map +1 -1
- package/lib/commonjs/components/AssetsFilterBar.js +4 -0
- package/lib/commonjs/components/AssetsFilterBar.js.map +1 -1
- package/lib/commonjs/components/AssetsHeader.js +11 -5
- package/lib/commonjs/components/AssetsHeader.js.map +1 -1
- package/lib/commonjs/components/AssetsListControls.js +5 -3
- package/lib/commonjs/components/AssetsListControls.js.map +1 -1
- package/lib/commonjs/components/AssetsModal.js +10 -0
- package/lib/commonjs/components/AssetsModal.js.map +1 -1
- package/lib/commonjs/components/BaselineBar.js +5 -0
- package/lib/commonjs/components/BaselineBar.js.map +1 -1
- package/lib/commonjs/index.js +53 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/store/assetStore.js +8 -1
- package/lib/commonjs/store/assetStore.js.map +1 -1
- package/lib/commonjs/store/baseline.js +9 -1
- package/lib/commonjs/store/baseline.js.map +1 -1
- package/lib/commonjs/store/insights.js +2 -11
- package/lib/commonjs/store/insights.js.map +1 -1
- package/lib/commonjs/utils/deviceScale.js +50 -0
- package/lib/commonjs/utils/deviceScale.js.map +1 -0
- package/lib/commonjs/utils/testIDs.js +114 -0
- package/lib/commonjs/utils/testIDs.js.map +1 -0
- package/lib/module/components/AssetDetail.js +10 -1
- package/lib/module/components/AssetDetail.js.map +1 -1
- package/lib/module/components/AssetRow.js +13 -4
- package/lib/module/components/AssetRow.js.map +1 -1
- package/lib/module/components/AssetsFilterBar.js +4 -0
- package/lib/module/components/AssetsFilterBar.js.map +1 -1
- package/lib/module/components/AssetsHeader.js +11 -5
- package/lib/module/components/AssetsHeader.js.map +1 -1
- package/lib/module/components/AssetsListControls.js +5 -3
- package/lib/module/components/AssetsListControls.js.map +1 -1
- package/lib/module/components/AssetsModal.js +10 -0
- package/lib/module/components/AssetsModal.js.map +1 -1
- package/lib/module/components/BaselineBar.js +5 -0
- package/lib/module/components/BaselineBar.js.map +1 -1
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/store/assetStore.js +8 -1
- package/lib/module/store/assetStore.js.map +1 -1
- package/lib/module/store/baseline.js +9 -1
- package/lib/module/store/baseline.js.map +1 -1
- package/lib/module/store/insights.js +1 -10
- package/lib/module/store/insights.js.map +1 -1
- package/lib/module/utils/deviceScale.js +46 -0
- package/lib/module/utils/deviceScale.js.map +1 -0
- package/lib/module/utils/testIDs.js +103 -0
- package/lib/module/utils/testIDs.js.map +1 -0
- package/lib/typescript/components/AssetDetail.d.ts.map +1 -1
- package/lib/typescript/components/AssetRow.d.ts.map +1 -1
- package/lib/typescript/components/AssetsFilterBar.d.ts.map +1 -1
- package/lib/typescript/components/AssetsHeader.d.ts.map +1 -1
- package/lib/typescript/components/AssetsListControls.d.ts.map +1 -1
- package/lib/typescript/components/AssetsModal.d.ts.map +1 -1
- package/lib/typescript/components/BaselineBar.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/store/assetStore.d.ts +6 -0
- package/lib/typescript/store/assetStore.d.ts.map +1 -1
- package/lib/typescript/store/baseline.d.ts.map +1 -1
- package/lib/typescript/store/insights.d.ts.map +1 -1
- package/lib/typescript/utils/deviceScale.d.ts +25 -0
- package/lib/typescript/utils/deviceScale.d.ts.map +1 -0
- package/lib/typescript/utils/testIDs.d.ts +75 -0
- package/lib/typescript/utils/testIDs.d.ts.map +1 -0
- package/package.json +5 -6
- package/src/capture/expo.ts +0 -45
- package/src/capture/install.ts +0 -73
- package/src/capture/measure.ts +0 -167
- package/src/capture/metroGraph.ts +0 -146
- package/src/capture/registry.ts +0 -137
- package/src/components/AssetDetail.tsx +0 -207
- package/src/components/AssetRow.tsx +0 -243
- package/src/components/AssetsFilterBar.tsx +0 -99
- package/src/components/AssetsHeader.tsx +0 -75
- package/src/components/AssetsIcon.tsx +0 -9
- package/src/components/AssetsListControls.tsx +0 -101
- package/src/components/AssetsModal.tsx +0 -371
- package/src/components/BaselineBar.tsx +0 -98
- package/src/export.ts +0 -110
- package/src/format.ts +0 -53
- package/src/hooks/useAssetRecords.ts +0 -26
- package/src/index.tsx +0 -87
- package/src/preset.tsx +0 -35
- package/src/remote.ts +0 -65
- package/src/state.ts +0 -84
- package/src/store/assetStore.ts +0 -209
- package/src/store/baseline.ts +0 -152
- package/src/store/insights.ts +0 -145
- package/src/sync/assetsSyncAdapter.ts +0 -151
- package/src/types.ts +0 -160
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AssetRow — one bundled asset as a family-style card (matching the network
|
|
3
|
-
* tool's rows): rounded card, colored accent edge per kind, thumbnail, name,
|
|
4
|
-
* quiet metadata, bold right-aligned size, and finding badges ONLY when a
|
|
5
|
-
* finding exists. A thin proportional bar along the card bottom makes the
|
|
6
|
-
* heavy assets feel heavy at a glance.
|
|
7
|
-
*
|
|
8
|
-
* Thumbnails render through the NUMERIC registry id when available — that
|
|
9
|
-
* routes through resolveAssetSource natively, so it works in dev, iOS
|
|
10
|
-
* release (file://) and Android release (bare resource id) alike. They carry
|
|
11
|
-
* the buoy-internal testID so the images tool never records our own UI.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { Image, StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|
15
|
-
import { macOSColors, night } from "@buoy-gg/shared-ui";
|
|
16
|
-
import { displaySource } from "../capture/measure";
|
|
17
|
-
import {
|
|
18
|
-
formatDimensions,
|
|
19
|
-
formatScales,
|
|
20
|
-
formatSize,
|
|
21
|
-
KIND_COLORS,
|
|
22
|
-
KIND_LABELS,
|
|
23
|
-
} from "../format";
|
|
24
|
-
import { estDecodedBytes } from "../store/assetStore";
|
|
25
|
-
import type { AssetRecord } from "../types";
|
|
26
|
-
|
|
27
|
-
/** Which audit findings apply to this record (drives the row badges). */
|
|
28
|
-
export interface AssetRowFlags {
|
|
29
|
-
scaleGap: boolean;
|
|
30
|
-
scaleAnomaly: boolean;
|
|
31
|
-
duplicate: boolean;
|
|
32
|
-
webp: boolean;
|
|
33
|
-
oversize: boolean;
|
|
34
|
-
heavy: boolean;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
interface AssetRowProps {
|
|
38
|
-
record: AssetRecord;
|
|
39
|
-
flags: AssetRowFlags;
|
|
40
|
-
/** This record's size relative to the largest in the list (0–1). */
|
|
41
|
-
sizeFraction: number;
|
|
42
|
-
onPress: (id: number) => void;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function Badge({ label, color }: { label: string; color?: string }) {
|
|
46
|
-
// Quiet chip: legibility from light-gray text on a neutral fill. Only the
|
|
47
|
-
// serious tiers (red) and WEBP (blue) carry a small dot; the rest are plain.
|
|
48
|
-
return (
|
|
49
|
-
<View style={styles.badgePill}>
|
|
50
|
-
{color ? <View style={[styles.badgeDot, { backgroundColor: color }]} /> : null}
|
|
51
|
-
<Text style={styles.badgePillText}>{label}</Text>
|
|
52
|
-
</View>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function AssetRow({ record, flags, sizeFraction, onPress }: AssetRowProps) {
|
|
57
|
-
const accent = KIND_COLORS[record.kind];
|
|
58
|
-
const thumbSource = record.kind === "image" ? displaySource(record) : null;
|
|
59
|
-
|
|
60
|
-
const badges: Array<{ label: string; color?: string }> = [];
|
|
61
|
-
if (!record.loaded) badges.push({ label: "UNUSED" });
|
|
62
|
-
if (flags.oversize) badges.push({ label: "OVERSIZE", color: macOSColors.semantic.error });
|
|
63
|
-
if (flags.duplicate) badges.push({ label: "DUP" });
|
|
64
|
-
if (flags.heavy) badges.push({ label: "HEAVY" });
|
|
65
|
-
if (flags.scaleAnomaly) badges.push({ label: "BAD SCALE", color: macOSColors.semantic.error });
|
|
66
|
-
else if (flags.scaleGap) badges.push({ label: "LOW-RES" });
|
|
67
|
-
if (flags.webp) badges.push({ label: "WEBP", color: macOSColors.semantic.info });
|
|
68
|
-
|
|
69
|
-
return (
|
|
70
|
-
<TouchableOpacity
|
|
71
|
-
style={styles.card}
|
|
72
|
-
testID={`buoy-asset-row-${record.id}`}
|
|
73
|
-
onPress={() => onPress(record.id)}
|
|
74
|
-
activeOpacity={0.7}
|
|
75
|
-
>
|
|
76
|
-
<View style={styles.body}>
|
|
77
|
-
<View style={styles.thumbBox}>
|
|
78
|
-
{thumbSource != null ? (
|
|
79
|
-
<Image
|
|
80
|
-
testID="buoy-internal-assets-thumb"
|
|
81
|
-
source={thumbSource}
|
|
82
|
-
style={styles.thumb}
|
|
83
|
-
resizeMode="contain"
|
|
84
|
-
/>
|
|
85
|
-
) : (
|
|
86
|
-
<Text style={[styles.thumbFallback, { color: accent }]}>
|
|
87
|
-
{KIND_LABELS[record.kind]}
|
|
88
|
-
</Text>
|
|
89
|
-
)}
|
|
90
|
-
</View>
|
|
91
|
-
|
|
92
|
-
<View style={styles.main}>
|
|
93
|
-
<Text style={styles.name} numberOfLines={1}>
|
|
94
|
-
{record.name}
|
|
95
|
-
<Text style={styles.ext}>.{record.type}</Text>
|
|
96
|
-
</Text>
|
|
97
|
-
<View style={styles.metaRow}>
|
|
98
|
-
{record.kind === "image" && record.width != null ? (
|
|
99
|
-
<Text style={styles.meta}>{formatDimensions(record)}</Text>
|
|
100
|
-
) : null}
|
|
101
|
-
{/* Scale variants only mean something for images. */}
|
|
102
|
-
{record.kind === "image" && record.scales.length ? (
|
|
103
|
-
<Text style={styles.meta}>{formatScales(record.scales)}</Text>
|
|
104
|
-
) : null}
|
|
105
|
-
</View>
|
|
106
|
-
</View>
|
|
107
|
-
|
|
108
|
-
<View style={styles.right}>
|
|
109
|
-
<Text style={styles.size}>
|
|
110
|
-
{formatSize(record, estDecodedBytes(record))}
|
|
111
|
-
</Text>
|
|
112
|
-
{badges.length ? (
|
|
113
|
-
<View style={styles.badgeRow}>
|
|
114
|
-
{badges.slice(0, 3).map((b) => (
|
|
115
|
-
<Badge key={b.label} label={b.label} color={b.color} />
|
|
116
|
-
))}
|
|
117
|
-
</View>
|
|
118
|
-
) : null}
|
|
119
|
-
</View>
|
|
120
|
-
</View>
|
|
121
|
-
|
|
122
|
-
{sizeFraction > 0 ? (
|
|
123
|
-
<View style={styles.sizeTrack}>
|
|
124
|
-
<View
|
|
125
|
-
style={[
|
|
126
|
-
styles.sizeFill,
|
|
127
|
-
{
|
|
128
|
-
backgroundColor: accent,
|
|
129
|
-
width: `${Math.max(2, Math.round(sizeFraction * 100))}%`,
|
|
130
|
-
},
|
|
131
|
-
]}
|
|
132
|
-
/>
|
|
133
|
-
</View>
|
|
134
|
-
) : null}
|
|
135
|
-
</TouchableOpacity>
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const styles = StyleSheet.create({
|
|
140
|
-
card: {
|
|
141
|
-
marginHorizontal: 10,
|
|
142
|
-
marginTop: 6,
|
|
143
|
-
borderRadius: 10,
|
|
144
|
-
backgroundColor: night.color.surface,
|
|
145
|
-
borderWidth: 1,
|
|
146
|
-
borderColor: night.color.border,
|
|
147
|
-
overflow: "hidden",
|
|
148
|
-
},
|
|
149
|
-
body: {
|
|
150
|
-
flexDirection: "row",
|
|
151
|
-
alignItems: "center",
|
|
152
|
-
paddingVertical: 8,
|
|
153
|
-
paddingHorizontal: 12,
|
|
154
|
-
gap: 10,
|
|
155
|
-
},
|
|
156
|
-
thumbBox: {
|
|
157
|
-
width: 40,
|
|
158
|
-
height: 40,
|
|
159
|
-
borderRadius: 8,
|
|
160
|
-
// Light plate so dark glyph assets stay visible on the dark card.
|
|
161
|
-
backgroundColor: "rgba(255,255,255,0.10)",
|
|
162
|
-
borderWidth: 1,
|
|
163
|
-
borderColor: night.color.border,
|
|
164
|
-
alignItems: "center",
|
|
165
|
-
justifyContent: "center",
|
|
166
|
-
overflow: "hidden",
|
|
167
|
-
},
|
|
168
|
-
thumb: {
|
|
169
|
-
width: 34,
|
|
170
|
-
height: 34,
|
|
171
|
-
},
|
|
172
|
-
thumbFallback: {
|
|
173
|
-
fontSize: 9,
|
|
174
|
-
fontWeight: "700",
|
|
175
|
-
letterSpacing: 0.5,
|
|
176
|
-
},
|
|
177
|
-
main: {
|
|
178
|
-
flex: 1,
|
|
179
|
-
gap: 3,
|
|
180
|
-
},
|
|
181
|
-
name: {
|
|
182
|
-
color: night.color.text,
|
|
183
|
-
fontSize: 12,
|
|
184
|
-
fontWeight: "600",
|
|
185
|
-
},
|
|
186
|
-
ext: {
|
|
187
|
-
color: night.color.textTertiary,
|
|
188
|
-
fontWeight: "400",
|
|
189
|
-
},
|
|
190
|
-
metaRow: {
|
|
191
|
-
flexDirection: "row",
|
|
192
|
-
alignItems: "center",
|
|
193
|
-
gap: 8,
|
|
194
|
-
},
|
|
195
|
-
meta: {
|
|
196
|
-
color: night.color.textSecondary,
|
|
197
|
-
fontSize: 10,
|
|
198
|
-
fontVariant: ["tabular-nums"],
|
|
199
|
-
},
|
|
200
|
-
right: {
|
|
201
|
-
alignItems: "flex-end",
|
|
202
|
-
gap: 4,
|
|
203
|
-
},
|
|
204
|
-
size: {
|
|
205
|
-
color: night.color.text,
|
|
206
|
-
fontSize: 12,
|
|
207
|
-
fontWeight: "700",
|
|
208
|
-
fontVariant: ["tabular-nums"],
|
|
209
|
-
},
|
|
210
|
-
badgeRow: {
|
|
211
|
-
flexDirection: "row",
|
|
212
|
-
gap: 4,
|
|
213
|
-
},
|
|
214
|
-
badgePill: {
|
|
215
|
-
flexDirection: "row",
|
|
216
|
-
alignItems: "center",
|
|
217
|
-
gap: 4,
|
|
218
|
-
backgroundColor: "rgba(255,255,255,0.06)",
|
|
219
|
-
borderRadius: 4,
|
|
220
|
-
paddingHorizontal: 6,
|
|
221
|
-
paddingVertical: 2.5,
|
|
222
|
-
},
|
|
223
|
-
badgeDot: {
|
|
224
|
-
width: 5,
|
|
225
|
-
height: 5,
|
|
226
|
-
borderRadius: 2.5,
|
|
227
|
-
},
|
|
228
|
-
badgePillText: {
|
|
229
|
-
color: night.color.textSecondary,
|
|
230
|
-
fontSize: 9,
|
|
231
|
-
fontWeight: "600",
|
|
232
|
-
letterSpacing: 0.3,
|
|
233
|
-
},
|
|
234
|
-
sizeTrack: {
|
|
235
|
-
height: 2,
|
|
236
|
-
backgroundColor: "transparent",
|
|
237
|
-
},
|
|
238
|
-
sizeFill: {
|
|
239
|
-
height: 2,
|
|
240
|
-
opacity: 0.45,
|
|
241
|
-
borderTopRightRadius: 2,
|
|
242
|
-
},
|
|
243
|
-
});
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AssetsFilterBar — kind filter pills with counts, styled like the network
|
|
3
|
-
* tool's bordered pills: outlined, kind-accent-colored when active. Wrapping
|
|
4
|
-
* row, not a horizontal ScrollView (same height-measurement pitfall the
|
|
5
|
-
* images tool documents).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|
9
|
-
import { night } from "@buoy-gg/shared-ui";
|
|
10
|
-
import { KIND_COLORS, KIND_LABELS } from "../format";
|
|
11
|
-
import type { AssetKind, AssetsStats } from "../types";
|
|
12
|
-
|
|
13
|
-
export type AssetFilter = "all" | "unused" | AssetKind;
|
|
14
|
-
|
|
15
|
-
interface AssetsFilterBarProps {
|
|
16
|
-
stats: AssetsStats;
|
|
17
|
-
filter: AssetFilter;
|
|
18
|
-
onChange: (filter: AssetFilter) => void;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function AssetsFilterBar({ stats, filter, onChange }: AssetsFilterBarProps) {
|
|
22
|
-
const chips: Array<{ key: AssetFilter; label: string; accent: string }> = [
|
|
23
|
-
{ key: "all", label: `ALL ${stats.total}`, accent: night.color.textSecondary },
|
|
24
|
-
];
|
|
25
|
-
for (const kind of Object.keys(stats.byKind) as AssetKind[]) {
|
|
26
|
-
const entry = stats.byKind[kind];
|
|
27
|
-
if (entry.count > 0) {
|
|
28
|
-
chips.push({
|
|
29
|
-
key: kind,
|
|
30
|
-
label: `${KIND_LABELS[kind]} ${entry.count}`,
|
|
31
|
-
accent: KIND_COLORS[kind],
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
if (stats.neverLoaded > 0) {
|
|
36
|
-
// Amber, not semantic.warning — the bright yellow reads as an alarm.
|
|
37
|
-
chips.push({
|
|
38
|
-
key: "unused",
|
|
39
|
-
label: `UNUSED ${stats.neverLoaded}`,
|
|
40
|
-
accent: "#FF9F1C",
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
if (chips.length <= 1) return null;
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<View style={styles.bar}>
|
|
47
|
-
{chips.map((chip) => {
|
|
48
|
-
const active = chip.key === filter;
|
|
49
|
-
return (
|
|
50
|
-
<TouchableOpacity
|
|
51
|
-
key={chip.key}
|
|
52
|
-
style={[
|
|
53
|
-
styles.chip,
|
|
54
|
-
active && {
|
|
55
|
-
borderColor: chip.accent,
|
|
56
|
-
backgroundColor: `${chip.accent}1A`,
|
|
57
|
-
},
|
|
58
|
-
]}
|
|
59
|
-
// Tapping the active chip again untoggles back to ALL.
|
|
60
|
-
onPress={() => onChange(active ? "all" : chip.key)}
|
|
61
|
-
>
|
|
62
|
-
<Text
|
|
63
|
-
style={[
|
|
64
|
-
styles.chipText,
|
|
65
|
-
{ color: active ? chip.accent : night.color.textSecondary },
|
|
66
|
-
]}
|
|
67
|
-
>
|
|
68
|
-
{chip.label}
|
|
69
|
-
</Text>
|
|
70
|
-
</TouchableOpacity>
|
|
71
|
-
);
|
|
72
|
-
})}
|
|
73
|
-
</View>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const styles = StyleSheet.create({
|
|
78
|
-
bar: {
|
|
79
|
-
flexDirection: "row",
|
|
80
|
-
flexWrap: "wrap",
|
|
81
|
-
gap: 6,
|
|
82
|
-
paddingHorizontal: 10,
|
|
83
|
-
paddingVertical: 8,
|
|
84
|
-
},
|
|
85
|
-
chip: {
|
|
86
|
-
borderWidth: 1,
|
|
87
|
-
borderColor: night.color.border,
|
|
88
|
-
backgroundColor: night.color.surface,
|
|
89
|
-
borderRadius: 999,
|
|
90
|
-
paddingHorizontal: 10,
|
|
91
|
-
paddingVertical: 5,
|
|
92
|
-
},
|
|
93
|
-
chipText: {
|
|
94
|
-
fontSize: 10,
|
|
95
|
-
fontWeight: "700",
|
|
96
|
-
letterSpacing: 0.4,
|
|
97
|
-
lineHeight: 13,
|
|
98
|
-
},
|
|
99
|
-
});
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AssetsHeader — shared ModalHeader wiring, matching the Images/Console
|
|
3
|
-
* header pattern. List mode shows inventory totals plus the shared
|
|
4
|
-
* ToolbarCopyButton (full markdown report); detail mode shows back
|
|
5
|
-
* navigation and the single-asset report.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
ModalHeader,
|
|
10
|
-
ToolbarClearButton,
|
|
11
|
-
ToolbarCopyButton,
|
|
12
|
-
} from "@buoy-gg/shared-ui";
|
|
13
|
-
import { formatBytes } from "../format";
|
|
14
|
-
import type { AssetsStats } from "../types";
|
|
15
|
-
|
|
16
|
-
interface AssetsHeaderProps {
|
|
17
|
-
stats: AssetsStats;
|
|
18
|
-
/** Size measurement in progress — suppress the (partial) byte total. */
|
|
19
|
-
measuring: boolean;
|
|
20
|
-
detailOpen: boolean;
|
|
21
|
-
detailIndex?: number;
|
|
22
|
-
detailTotal?: number;
|
|
23
|
-
onCloseDetail: () => void;
|
|
24
|
-
onClear: () => void;
|
|
25
|
-
getReport: () => string;
|
|
26
|
-
onBack?: () => void;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function AssetsHeader({
|
|
30
|
-
stats,
|
|
31
|
-
measuring,
|
|
32
|
-
detailOpen,
|
|
33
|
-
detailIndex = -1,
|
|
34
|
-
detailTotal = 0,
|
|
35
|
-
onCloseDetail,
|
|
36
|
-
onClear,
|
|
37
|
-
getReport,
|
|
38
|
-
onBack,
|
|
39
|
-
}: AssetsHeaderProps) {
|
|
40
|
-
if (detailOpen) {
|
|
41
|
-
const counter =
|
|
42
|
-
detailIndex >= 0 && detailTotal > 0
|
|
43
|
-
? `${detailIndex + 1} of ${detailTotal}`
|
|
44
|
-
: undefined;
|
|
45
|
-
return (
|
|
46
|
-
<ModalHeader>
|
|
47
|
-
<ModalHeader.Navigation onBack={onCloseDetail} />
|
|
48
|
-
<ModalHeader.Content title="Asset detail" subtitle={counter} />
|
|
49
|
-
<ModalHeader.Actions>
|
|
50
|
-
<ToolbarCopyButton value={getReport} />
|
|
51
|
-
</ModalHeader.Actions>
|
|
52
|
-
</ModalHeader>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const subtitleParts = [`${stats.total} assets`];
|
|
57
|
-
// A mid-measure byte total reads as nonsense ("653 B+") — wait it out.
|
|
58
|
-
if (stats.totalBytes > 0 && !measuring) {
|
|
59
|
-
const partial = stats.measuredCount < stats.total ? "+" : "";
|
|
60
|
-
subtitleParts.push(`${formatBytes(stats.totalBytes)}${partial}`);
|
|
61
|
-
}
|
|
62
|
-
if (stats.neverLoaded > 0) subtitleParts.push(`${stats.neverLoaded} never loaded`);
|
|
63
|
-
if (stats.fontFamilies > 0) subtitleParts.push(`${stats.fontFamilies} fonts`);
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<ModalHeader>
|
|
67
|
-
{onBack ? <ModalHeader.Navigation onBack={onBack} /> : null}
|
|
68
|
-
<ModalHeader.Content title="Assets" subtitle={subtitleParts.join(" · ")} />
|
|
69
|
-
<ModalHeader.Actions>
|
|
70
|
-
<ToolbarCopyButton value={getReport} />
|
|
71
|
-
<ToolbarClearButton onPress={onClear} />
|
|
72
|
-
</ModalHeader.Actions>
|
|
73
|
-
</ModalHeader>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AssetsIcon — re-exported from the Buoy Icon Format.
|
|
3
|
-
*
|
|
4
|
-
* The artwork lives in shared/icons/assets.json and is generated into
|
|
5
|
-
* @buoy-gg/floating-tools-core, so React Native, the desktop dashboard and
|
|
6
|
-
* Flutter all render the same drawing. Edit the JSON, then `pnpm icons`.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export { AssetsIcon, ASSETS_ICON_COLOR } from "@buoy-gg/shared-ui";
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AssetsListControls — one compact row: name search, sort cycle (SIZE →
|
|
3
|
-
* NAME → TYPE), and — when no baseline exists yet — the BASE button that
|
|
4
|
-
* saves one. Once a baseline exists the BaselineBar takes over the diff UI,
|
|
5
|
-
* so this row never wastes space on a hint sentence.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native";
|
|
9
|
-
import { night } from "@buoy-gg/shared-ui";
|
|
10
|
-
import { saveBaseline } from "../store/baseline";
|
|
11
|
-
|
|
12
|
-
export type AssetSort = "size" | "name" | "type";
|
|
13
|
-
|
|
14
|
-
const NEXT_SORT: Record<AssetSort, AssetSort> = {
|
|
15
|
-
size: "name",
|
|
16
|
-
name: "type",
|
|
17
|
-
type: "size",
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
interface AssetsListControlsProps {
|
|
21
|
-
query: string;
|
|
22
|
-
onQueryChange: (query: string) => void;
|
|
23
|
-
sort: AssetSort;
|
|
24
|
-
onSortChange: (sort: AssetSort) => void;
|
|
25
|
-
/** Show the save-baseline button (no baseline saved yet). */
|
|
26
|
-
showBaselineButton: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function AssetsListControls({
|
|
30
|
-
query,
|
|
31
|
-
onQueryChange,
|
|
32
|
-
sort,
|
|
33
|
-
onSortChange,
|
|
34
|
-
showBaselineButton,
|
|
35
|
-
}: AssetsListControlsProps) {
|
|
36
|
-
return (
|
|
37
|
-
<View style={styles.row}>
|
|
38
|
-
<TextInput
|
|
39
|
-
style={styles.input}
|
|
40
|
-
value={query}
|
|
41
|
-
onChangeText={onQueryChange}
|
|
42
|
-
placeholder="Search assets…"
|
|
43
|
-
placeholderTextColor={night.color.placeholder}
|
|
44
|
-
autoCapitalize="none"
|
|
45
|
-
autoCorrect={false}
|
|
46
|
-
testID="buoy-assets-search"
|
|
47
|
-
/>
|
|
48
|
-
<TouchableOpacity
|
|
49
|
-
style={styles.button}
|
|
50
|
-
onPress={() => onSortChange(NEXT_SORT[sort])}
|
|
51
|
-
testID="buoy-assets-sort"
|
|
52
|
-
>
|
|
53
|
-
<Text style={styles.buttonText}>↓ {sort.toUpperCase()}</Text>
|
|
54
|
-
</TouchableOpacity>
|
|
55
|
-
{showBaselineButton ? (
|
|
56
|
-
<TouchableOpacity
|
|
57
|
-
style={styles.button}
|
|
58
|
-
onPress={() => void saveBaseline()}
|
|
59
|
-
testID="buoy-assets-save-baseline"
|
|
60
|
-
>
|
|
61
|
-
<Text style={styles.buttonText}>BASE</Text>
|
|
62
|
-
</TouchableOpacity>
|
|
63
|
-
) : null}
|
|
64
|
-
</View>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const styles = StyleSheet.create({
|
|
69
|
-
row: {
|
|
70
|
-
flexDirection: "row",
|
|
71
|
-
alignItems: "center",
|
|
72
|
-
gap: 8,
|
|
73
|
-
paddingHorizontal: 10,
|
|
74
|
-
paddingBottom: 8,
|
|
75
|
-
},
|
|
76
|
-
input: {
|
|
77
|
-
flex: 1,
|
|
78
|
-
backgroundColor: night.color.surfaceElevated,
|
|
79
|
-
borderWidth: 1,
|
|
80
|
-
borderColor: night.color.border,
|
|
81
|
-
borderRadius: 8,
|
|
82
|
-
paddingHorizontal: 10,
|
|
83
|
-
paddingVertical: 6,
|
|
84
|
-
color: night.color.text,
|
|
85
|
-
fontSize: 12,
|
|
86
|
-
},
|
|
87
|
-
button: {
|
|
88
|
-
backgroundColor: night.color.surface,
|
|
89
|
-
borderWidth: 1,
|
|
90
|
-
borderColor: night.color.border,
|
|
91
|
-
borderRadius: 8,
|
|
92
|
-
paddingHorizontal: 10,
|
|
93
|
-
paddingVertical: 7,
|
|
94
|
-
},
|
|
95
|
-
buttonText: {
|
|
96
|
-
color: night.color.textSecondary,
|
|
97
|
-
fontSize: 10,
|
|
98
|
-
fontWeight: "700",
|
|
99
|
-
letterSpacing: 0.5,
|
|
100
|
-
},
|
|
101
|
-
});
|