@onekeyfe/react-native-native-list 6.7.0-alpha.414 → 6.7.0-alpha.418
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/android/src/main/java/com/onekey/nativelist/NativeListAccessoryStack.kt +5 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListGeneratedIcons.kt +47 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListLeadingVisual.kt +5 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt +120 -338
- package/android/src/main/java/com/onekey/nativelist/NativeListRendererRowView.kt +5 -0
- package/docs/SPEC.md +122 -7
- package/ios/NativeListAccessoryStack.swift +6 -16
- package/ios/NativeListDataRowRenderer.swift +15 -6
- package/ios/NativeListDesignAssets.swift +13 -84
- package/ios/NativeListGeneratedIcons.swift +55 -0
- package/ios/NativeListIconBinding.swift +171 -0
- package/ios/NativeListIdentityRenderer.swift +12 -10
- package/ios/NativeListLeadingVisual.swift +11 -9
- package/ios/NativeListMarketRenderer.swift +7 -13
- package/ios/NativeListMediaTileRenderer.swift +6 -2
- package/ios/NativeListMetricCardRenderer.swift +4 -2
- package/ios/NativeListSectionHeaderRenderer.swift +12 -4
- package/ios/tests/NativeListAccessoryStackTests.swift +12 -0
- package/ios/tests/NativeListSharedIconTests.swift +236 -0
- package/lib/module/web/NativeListGeneratedIcons.js +31 -0
- package/lib/module/web/NativeListWebEngine.js +21 -323
- package/lib/module/web/templates/ActivityRowRenderer.js +4 -24
- package/lib/module/web/templates/MarketTableRowRenderer.js +11 -15
- package/lib/module/web/templates/MediaTileRowRenderer.js +2 -12
- package/lib/typescript/src/web/NativeListGeneratedIcons.d.ts +2 -0
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +0 -1
- package/package.json +10 -4
- package/react-native-native-list.podspec +0 -5
- package/scripts/generate-icons.mjs +150 -0
- package/scripts/generate-icons.test.mjs +288 -0
- package/scripts/native-list-icons.json +260 -0
- package/src/web/NativeListGeneratedIcons.ts +28 -0
- package/src/web/NativeListWebEngine.ts +25 -405
- package/src/web/templates/ActivityRowRenderer.ts +15 -53
- package/src/web/templates/MarketTableRowRenderer.ts +17 -29
- package/src/web/templates/MediaTileRowRenderer.ts +2 -15
- package/ios/Resources/NativeListIcons.xcassets/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_bottom.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_bottom.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_top.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_top.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_badge_verified_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_badge_verified_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chart_trending_up.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chart_trending_up.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_checked.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_checked.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_indeterminate.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_indeterminate.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_bottom.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_bottom.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_grabber_ver.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_grabber_ver.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_right_small.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_right_small.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_top.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_top.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_crypto_coin_outline.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_crypto_coin_outline.imageset/icon.svg +0 -4
- package/ios/Resources/NativeListIcons.xcassets/onekey_document2.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_document2.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_dot_hor.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_dot_hor.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_dot_ver_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_dot_ver_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_drag.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_drag.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_error_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_error_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_gas_solid.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_gas_solid.imageset/icon.svg +0 -3
- package/ios/Resources/NativeListIcons.xcassets/onekey_image_square_waves.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_image_square_waves.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_info_circle.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_info_circle.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_pencil.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_pencil.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_circle.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_circle.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_large.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_large.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_small.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_small.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_questionmark_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_questionmark_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_refresh_ccw_outline.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_refresh_ccw_outline.imageset/icon.svg +0 -3
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_account_error_custom.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_account_error_custom.imageset/icon.svg +0 -14
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/icon.svg +0 -12
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_apple_brand.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_apple_brand.imageset/icon.svg +0 -6
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_bot_illus.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_bot_illus.imageset/icon.svg +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_crossed_small_solid.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_crossed_small_solid.imageset/icon.svg +0 -3
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_globus_outline.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_globus_outline.imageset/icon.svg +0 -7
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_google_illus.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_google_illus.imageset/icon.svg +0 -18
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_lock_solid.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_lock_solid.imageset/icon.svg +0 -7
- package/ios/Resources/NativeListIcons.xcassets/onekey_shield_exclamation.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_shield_exclamation.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_speaker_promote.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_speaker_promote.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_star.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_star.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_star_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_star_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_swap_hor.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_swap_hor.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_ticket.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_ticket.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_wallet.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_wallet.imageset/icon.svg +0 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const prettier = require('prettier');
|
|
9
|
+
const format = JSON.parse(
|
|
10
|
+
fs.readFileSync(path.join(root, 'package.json'), 'utf8')
|
|
11
|
+
).prettier;
|
|
12
|
+
const header =
|
|
13
|
+
'// Generated by scripts/generate-icons.mjs. Coverage and legacy names only; hosts provide SVG resources.\n';
|
|
14
|
+
export function generateOutputs(
|
|
15
|
+
fixtureRoot = path.resolve(
|
|
16
|
+
root,
|
|
17
|
+
'../../example/react-native/icon-fixtures/native/icons'
|
|
18
|
+
),
|
|
19
|
+
outputRoot = root
|
|
20
|
+
) {
|
|
21
|
+
const entries = JSON.parse(
|
|
22
|
+
fs.readFileSync(path.join(root, 'scripts/native-list-icons.json'), 'utf8')
|
|
23
|
+
);
|
|
24
|
+
const catalog = JSON.parse(
|
|
25
|
+
fs.readFileSync(path.join(fixtureRoot, 'manifest.json'), 'utf8')
|
|
26
|
+
);
|
|
27
|
+
const records = resolveRecords(entries, catalog, fixtureRoot);
|
|
28
|
+
return renderOutputs(records, outputRoot);
|
|
29
|
+
}
|
|
30
|
+
export function resolveRecords(entries, catalog, fixtureRoot) {
|
|
31
|
+
const names = new Set();
|
|
32
|
+
if (!Array.isArray(entries)) throw new Error('Invalid icon manifest');
|
|
33
|
+
return entries
|
|
34
|
+
.map((entry) => {
|
|
35
|
+
if (
|
|
36
|
+
!entry ||
|
|
37
|
+
typeof entry.name !== 'string' ||
|
|
38
|
+
!/^[A-Z][A-Za-z0-9]*$/.test(entry.name) ||
|
|
39
|
+
names.has(entry.name) ||
|
|
40
|
+
!Array.isArray(entry.platforms) ||
|
|
41
|
+
!entry.platforms.length ||
|
|
42
|
+
new Set(entry.platforms).size !== entry.platforms.length ||
|
|
43
|
+
entry.platforms.some((p) => !['ios', 'android', 'web'].includes(p)) ||
|
|
44
|
+
!['template', 'original', 'primitive'].includes(entry.rendering)
|
|
45
|
+
)
|
|
46
|
+
throw new Error('Invalid icon manifest entry');
|
|
47
|
+
names.add(entry.name);
|
|
48
|
+
if (entry.name === 'Circle')
|
|
49
|
+
return { ...entry, canonical: 'Circle', width: 24, height: 24 };
|
|
50
|
+
if (
|
|
51
|
+
typeof entry.source !== 'string' ||
|
|
52
|
+
entry.source.includes('..') ||
|
|
53
|
+
path.isAbsolute(entry.source)
|
|
54
|
+
)
|
|
55
|
+
throw new Error('Invalid icon manifest entry');
|
|
56
|
+
const matches = Object.entries(catalog).filter(
|
|
57
|
+
([, value]) => value.source === entry.source
|
|
58
|
+
);
|
|
59
|
+
if (matches.length > 1)
|
|
60
|
+
throw new Error(`Ambiguous shared SVG source: ${entry.source}`);
|
|
61
|
+
const match = matches[0];
|
|
62
|
+
if (!match) throw new Error(`Missing shared SVG source: ${entry.source}`);
|
|
63
|
+
const [canonical, metadata] = match;
|
|
64
|
+
const source = fs.readFileSync(
|
|
65
|
+
path.join(fixtureRoot, `${canonical}.svg`),
|
|
66
|
+
'utf8'
|
|
67
|
+
);
|
|
68
|
+
const raw = source.endsWith('\n') ? source : source + '\n';
|
|
69
|
+
if (
|
|
70
|
+
crypto.createHash('sha256').update(raw).digest('hex') !==
|
|
71
|
+
metadata.digest
|
|
72
|
+
)
|
|
73
|
+
throw new Error(`Shared SVG resources stale: ${entry.source}`);
|
|
74
|
+
return {
|
|
75
|
+
...entry,
|
|
76
|
+
canonical,
|
|
77
|
+
width: metadata.viewBox[2],
|
|
78
|
+
height: metadata.viewBox[3],
|
|
79
|
+
};
|
|
80
|
+
})
|
|
81
|
+
.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
82
|
+
}
|
|
83
|
+
function renderOutputs(records, outputRoot) {
|
|
84
|
+
const swift =
|
|
85
|
+
header +
|
|
86
|
+
'import UIKit\n\nstruct NativeListIconMetadata {\n let name: String\n let size: CGSize\n let original: Bool\n}\n\nenum NativeListGeneratedIcons {\n static let icons: [String: NativeListIconMetadata] = [\n' +
|
|
87
|
+
records
|
|
88
|
+
.filter((x) => x.platforms.includes('ios'))
|
|
89
|
+
.map(
|
|
90
|
+
(x) =>
|
|
91
|
+
` "${x.name}": NativeListIconMetadata(name: "${
|
|
92
|
+
x.canonical
|
|
93
|
+
}", size: CGSize(width: ${x.width}, height: ${x.height}), original: ${
|
|
94
|
+
x.rendering === 'original'
|
|
95
|
+
}),`
|
|
96
|
+
)
|
|
97
|
+
.join('\n') +
|
|
98
|
+
'\n ]\n}\n';
|
|
99
|
+
const kotlin =
|
|
100
|
+
header +
|
|
101
|
+
'package com.margelo.nitro.nativelist\n\ninternal data class NativeListIconMetadata(val name: String, val width: Float, val height: Float)\ninternal val nativeListGeneratedIcons: Map<String, NativeListIconMetadata> = mapOf(\n' +
|
|
102
|
+
records
|
|
103
|
+
.filter((x) => x.platforms.includes('android'))
|
|
104
|
+
.map(
|
|
105
|
+
(x) =>
|
|
106
|
+
` "${x.name}" to NativeListIconMetadata("${x.canonical}", ${x.width}f, ${x.height}f),`
|
|
107
|
+
)
|
|
108
|
+
.join('\n') +
|
|
109
|
+
'\n)\n';
|
|
110
|
+
const web =
|
|
111
|
+
header +
|
|
112
|
+
'export const nativeListGeneratedIcons: Readonly<Record<string, string>> = ' +
|
|
113
|
+
JSON.stringify(
|
|
114
|
+
Object.fromEntries(
|
|
115
|
+
records
|
|
116
|
+
.filter((x) => x.platforms.includes('web'))
|
|
117
|
+
.map((x) => [x.name, x.canonical])
|
|
118
|
+
)
|
|
119
|
+
) +
|
|
120
|
+
';\n';
|
|
121
|
+
return new Map([
|
|
122
|
+
[path.join(outputRoot, 'ios/NativeListGeneratedIcons.swift'), swift],
|
|
123
|
+
[
|
|
124
|
+
path.join(
|
|
125
|
+
outputRoot,
|
|
126
|
+
'android/src/main/java/com/onekey/nativelist/NativeListGeneratedIcons.kt'
|
|
127
|
+
),
|
|
128
|
+
kotlin,
|
|
129
|
+
],
|
|
130
|
+
[
|
|
131
|
+
path.join(outputRoot, 'src/web/NativeListGeneratedIcons.ts'),
|
|
132
|
+
prettier.format(web, { ...format, parser: 'typescript' }),
|
|
133
|
+
],
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
137
|
+
const outputs = generateOutputs();
|
|
138
|
+
const stale = [...outputs].filter(
|
|
139
|
+
([file, text]) =>
|
|
140
|
+
!fs.existsSync(file) || fs.readFileSync(file, 'utf8') !== text
|
|
141
|
+
);
|
|
142
|
+
if (process.argv.includes('--check') && stale.length)
|
|
143
|
+
throw new Error('Stale NativeList icon metadata');
|
|
144
|
+
if (!process.argv.includes('--check'))
|
|
145
|
+
for (const [file, text] of stale) {
|
|
146
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
147
|
+
fs.writeFileSync(file, text);
|
|
148
|
+
}
|
|
149
|
+
console.log(JSON.stringify({ outputs: outputs.size, changed: stale.length }));
|
|
150
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import test from 'node:test';
|
|
7
|
+
import { generateOutputs, resolveRecords } from './generate-icons.mjs';
|
|
8
|
+
const scripts = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const packageRoot = path.dirname(scripts);
|
|
10
|
+
const fixtureRoot = path.resolve(
|
|
11
|
+
packageRoot,
|
|
12
|
+
'../../example/react-native/icon-fixtures/native/icons'
|
|
13
|
+
);
|
|
14
|
+
const manifest = JSON.parse(
|
|
15
|
+
fs.readFileSync(path.join(scripts, 'native-list-icons.json'), 'utf8')
|
|
16
|
+
);
|
|
17
|
+
const catalogFile = path.join(fixtureRoot, 'manifest.json');
|
|
18
|
+
const catalog = JSON.parse(fs.readFileSync(catalogFile, 'utf8'));
|
|
19
|
+
// Frozen migration coverage, independent from the production manifest.
|
|
20
|
+
const migrationCoverage = {
|
|
21
|
+
ios: [
|
|
22
|
+
'AccountErrorCustom',
|
|
23
|
+
'AllNetworksSolid',
|
|
24
|
+
'AppleBrand',
|
|
25
|
+
'ArrowBottomOutline',
|
|
26
|
+
'ArrowTopOutline',
|
|
27
|
+
'BadgeVerifiedSolid',
|
|
28
|
+
'BotIllus',
|
|
29
|
+
'ChartTrendingUpOutline',
|
|
30
|
+
'CheckboxCheckedCustom',
|
|
31
|
+
'CheckboxIndeterminateCustom',
|
|
32
|
+
'ChevronBottomOutline',
|
|
33
|
+
'ChevronGrabberVerOutline',
|
|
34
|
+
'ChevronRightSmallOutline',
|
|
35
|
+
'ChevronTopOutline',
|
|
36
|
+
'CrossedSmallSolid',
|
|
37
|
+
'CryptoCoinOutline',
|
|
38
|
+
'Document2Outline',
|
|
39
|
+
'DotHorOutline',
|
|
40
|
+
'DotVerSolid',
|
|
41
|
+
'DragOutline',
|
|
42
|
+
'ErrorSolid',
|
|
43
|
+
'GasSolid',
|
|
44
|
+
'GlobusOutline',
|
|
45
|
+
'GoogleIllus',
|
|
46
|
+
'ImageSquareWavesOutline',
|
|
47
|
+
'InfoCircleOutline',
|
|
48
|
+
'LockSolid',
|
|
49
|
+
'MinusCircleOutline',
|
|
50
|
+
'MinusCircleSolid',
|
|
51
|
+
'PencilOutline',
|
|
52
|
+
'PlusCircleOutline',
|
|
53
|
+
'PlusLargeOutline',
|
|
54
|
+
'PlusSmallOutline',
|
|
55
|
+
'QuestionmarkSolid',
|
|
56
|
+
'RefreshCcwOutline',
|
|
57
|
+
'ShieldExclamationOutline',
|
|
58
|
+
'SpeakerPromoteOutline',
|
|
59
|
+
'StarOutline',
|
|
60
|
+
'StarSolid',
|
|
61
|
+
'SwapHorOutline',
|
|
62
|
+
'TicketOutline',
|
|
63
|
+
'WalletOutline',
|
|
64
|
+
],
|
|
65
|
+
android: [
|
|
66
|
+
'AccountErrorCustom',
|
|
67
|
+
'AllNetworksSolid',
|
|
68
|
+
'AppleBrand',
|
|
69
|
+
'ArrowBottomOutline',
|
|
70
|
+
'ArrowTopOutline',
|
|
71
|
+
'BadgeVerifiedSolid',
|
|
72
|
+
'BotIllus',
|
|
73
|
+
'ChartTrendingUpOutline',
|
|
74
|
+
'ChevronBottomOutline',
|
|
75
|
+
'ChevronGrabberVerOutline',
|
|
76
|
+
'ChevronRightSmallOutline',
|
|
77
|
+
'ChevronTopOutline',
|
|
78
|
+
'Circle',
|
|
79
|
+
'CrossedSmallSolid',
|
|
80
|
+
'CryptoCoinOutline',
|
|
81
|
+
'Document2Outline',
|
|
82
|
+
'DotHorOutline',
|
|
83
|
+
'DotVerSolid',
|
|
84
|
+
'DragOutline',
|
|
85
|
+
'ErrorSolid',
|
|
86
|
+
'GasSolid',
|
|
87
|
+
'GlobusOutline',
|
|
88
|
+
'GoogleIllus',
|
|
89
|
+
'ImageSquareWavesOutline',
|
|
90
|
+
'InfoCircleOutline',
|
|
91
|
+
'LockSolid',
|
|
92
|
+
'MinusCircleOutline',
|
|
93
|
+
'MinusCircleSolid',
|
|
94
|
+
'PencilOutline',
|
|
95
|
+
'PlusCircleOutline',
|
|
96
|
+
'PlusLargeOutline',
|
|
97
|
+
'PlusSmallOutline',
|
|
98
|
+
'QuestionmarkSolid',
|
|
99
|
+
'RefreshCcwOutline',
|
|
100
|
+
'ShieldExclamationOutline',
|
|
101
|
+
'SpeakerPromoteOutline',
|
|
102
|
+
'StarOutline',
|
|
103
|
+
'StarSolid',
|
|
104
|
+
'SwapHorOutline',
|
|
105
|
+
'TicketOutline',
|
|
106
|
+
'WalletOutline',
|
|
107
|
+
],
|
|
108
|
+
web: [
|
|
109
|
+
'AccountErrorCustom',
|
|
110
|
+
'AllNetworksSolid',
|
|
111
|
+
'AppleBrand',
|
|
112
|
+
'BadgeVerifiedSolid',
|
|
113
|
+
'BotIllus',
|
|
114
|
+
'CheckboxCheckedCustom',
|
|
115
|
+
'CheckboxIndeterminateCustom',
|
|
116
|
+
'ChevronRightSmallOutline',
|
|
117
|
+
'Circle',
|
|
118
|
+
'CrossedSmallSolid',
|
|
119
|
+
'CryptoCoinOutline',
|
|
120
|
+
'Document2Outline',
|
|
121
|
+
'DotHorOutline',
|
|
122
|
+
'DotVerSolid',
|
|
123
|
+
'DragOutline',
|
|
124
|
+
'GasSolid',
|
|
125
|
+
'GlobusOutline',
|
|
126
|
+
'GoogleIllus',
|
|
127
|
+
'LockSolid',
|
|
128
|
+
'PencilOutline',
|
|
129
|
+
'PlusLargeOutline',
|
|
130
|
+
'PlusSmallOutline',
|
|
131
|
+
'RefreshCcwOutline',
|
|
132
|
+
'TicketOutline',
|
|
133
|
+
'WalletOutline',
|
|
134
|
+
],
|
|
135
|
+
};
|
|
136
|
+
function assertRegistryCoverage(entries) {
|
|
137
|
+
for (const platform of Object.keys(migrationCoverage))
|
|
138
|
+
assert.deepEqual(
|
|
139
|
+
entries
|
|
140
|
+
.filter((entry) => entry.platforms.includes(platform))
|
|
141
|
+
.map((entry) => entry.name)
|
|
142
|
+
.sort(),
|
|
143
|
+
migrationCoverage[platform]
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
test('preserves frozen coverage, canonical aliases and intrinsic metadata without path copies', () => {
|
|
148
|
+
assertRegistryCoverage(manifest);
|
|
149
|
+
assert.throws(() =>
|
|
150
|
+
assertRegistryCoverage(
|
|
151
|
+
manifest.map((e) =>
|
|
152
|
+
e.name === 'DotVerSolid' ? { ...e, name: 'RenamedDotVerSolid' } : e
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
);
|
|
156
|
+
const records = resolveRecords(manifest, catalog, fixtureRoot);
|
|
157
|
+
assert.equal(
|
|
158
|
+
records.find((e) => e.name === 'BadgeVerifiedSolid').canonical,
|
|
159
|
+
'BadgeRecognizedSolid'
|
|
160
|
+
);
|
|
161
|
+
for (const [name, w, h] of [
|
|
162
|
+
['AppleBrand', 16, 20],
|
|
163
|
+
['CheckboxCheckedCustom', 16, 16],
|
|
164
|
+
['AccountErrorCustom', 18, 18],
|
|
165
|
+
]) {
|
|
166
|
+
const record = records.find((e) => e.name === name);
|
|
167
|
+
assert.deepEqual([record.width, record.height], [w, h]);
|
|
168
|
+
}
|
|
169
|
+
const outputs = generateOutputs(fixtureRoot);
|
|
170
|
+
assert.equal(outputs.size, 3);
|
|
171
|
+
assert.deepEqual([...outputs], [...generateOutputs(fixtureRoot)]);
|
|
172
|
+
const contents = [...outputs.values()];
|
|
173
|
+
assert.ok(
|
|
174
|
+
contents.every(
|
|
175
|
+
(text) => !text.includes(fixtureRoot) && !text.includes('<path')
|
|
176
|
+
)
|
|
177
|
+
);
|
|
178
|
+
const keys = [
|
|
179
|
+
[...contents[0].matchAll(/^ "([^"]+)":/gm)].map((m) => m[1]),
|
|
180
|
+
[...contents[1].matchAll(/^ "([^"]+)" to /gm)].map((m) => m[1]),
|
|
181
|
+
[...contents[2].matchAll(/^ ([A-Za-z0-9]+):/gm)].map((m) => m[1]),
|
|
182
|
+
];
|
|
183
|
+
assert.deepEqual(keys, [
|
|
184
|
+
migrationCoverage.ios,
|
|
185
|
+
migrationCoverage.android,
|
|
186
|
+
migrationCoverage.web,
|
|
187
|
+
]);
|
|
188
|
+
});
|
|
189
|
+
test('rejects invalid coverage metadata and missing, ambiguous or stale canonical sources', () => {
|
|
190
|
+
for (const invalid of [
|
|
191
|
+
{ ...manifest[0], name: undefined },
|
|
192
|
+
{ ...manifest[0], platforms: ['other'] },
|
|
193
|
+
{ ...manifest[0], platforms: ['ios', 'ios'] },
|
|
194
|
+
])
|
|
195
|
+
assert.throws(
|
|
196
|
+
() => resolveRecords([invalid], catalog, fixtureRoot),
|
|
197
|
+
/Invalid icon manifest/
|
|
198
|
+
);
|
|
199
|
+
assert.throws(
|
|
200
|
+
() => resolveRecords([manifest[0], manifest[0]], catalog, fixtureRoot),
|
|
201
|
+
/Invalid icon manifest/
|
|
202
|
+
);
|
|
203
|
+
assert.throws(
|
|
204
|
+
() => resolveRecords(manifest, {}, fixtureRoot),
|
|
205
|
+
/Missing shared SVG/
|
|
206
|
+
);
|
|
207
|
+
assert.throws(
|
|
208
|
+
() =>
|
|
209
|
+
resolveRecords(
|
|
210
|
+
manifest,
|
|
211
|
+
{
|
|
212
|
+
...catalog,
|
|
213
|
+
Duplicate:
|
|
214
|
+
catalog[
|
|
215
|
+
Object.keys(catalog).find(
|
|
216
|
+
(k) => catalog[k].source === manifest[0].source
|
|
217
|
+
)
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
fixtureRoot
|
|
221
|
+
),
|
|
222
|
+
/Ambiguous shared SVG/
|
|
223
|
+
);
|
|
224
|
+
const changed = structuredClone(catalog);
|
|
225
|
+
changed[
|
|
226
|
+
Object.keys(changed).find((k) => changed[k].source === manifest[0].source)
|
|
227
|
+
].digest = '0';
|
|
228
|
+
assert.throws(() => resolveRecords(manifest, changed, fixtureRoot), /stale/);
|
|
229
|
+
});
|
|
230
|
+
test('generation is idempotent, check is read-only and invalid late input cannot partially write', () => {
|
|
231
|
+
const tempRoot = path.join(packageRoot, '.tmp');
|
|
232
|
+
fs.mkdirSync(tempRoot, { recursive: true });
|
|
233
|
+
const temp = fs.mkdtempSync(path.join(tempRoot, 'metadata-'));
|
|
234
|
+
try {
|
|
235
|
+
const sdk = path.join(temp, 'native-views/react-native-native-list');
|
|
236
|
+
fs.mkdirSync(path.join(sdk, 'scripts'), { recursive: true });
|
|
237
|
+
for (const name of ['generate-icons.mjs', 'native-list-icons.json'])
|
|
238
|
+
fs.copyFileSync(
|
|
239
|
+
path.join(scripts, name),
|
|
240
|
+
path.join(sdk, 'scripts', name)
|
|
241
|
+
);
|
|
242
|
+
fs.copyFileSync(
|
|
243
|
+
path.join(packageRoot, 'package.json'),
|
|
244
|
+
path.join(sdk, 'package.json')
|
|
245
|
+
);
|
|
246
|
+
fs.symlinkSync(
|
|
247
|
+
path.resolve(packageRoot, '../../node_modules'),
|
|
248
|
+
path.join(temp, 'node_modules'),
|
|
249
|
+
'dir'
|
|
250
|
+
);
|
|
251
|
+
const targetCatalog = path.join(
|
|
252
|
+
temp,
|
|
253
|
+
'example/react-native/icon-fixtures/native/icons/manifest.json'
|
|
254
|
+
);
|
|
255
|
+
fs.mkdirSync(path.dirname(targetCatalog), { recursive: true });
|
|
256
|
+
fs.cpSync(fixtureRoot, path.dirname(targetCatalog), { recursive: true });
|
|
257
|
+
const run = (check = false) =>
|
|
258
|
+
spawnSync(
|
|
259
|
+
process.execPath,
|
|
260
|
+
[
|
|
261
|
+
path.join(sdk, 'scripts/generate-icons.mjs'),
|
|
262
|
+
...(check ? ['--check'] : []),
|
|
263
|
+
],
|
|
264
|
+
{ encoding: 'utf8' }
|
|
265
|
+
);
|
|
266
|
+
assert.equal(run().status, 0);
|
|
267
|
+
assert.equal(JSON.parse(run().stdout).changed, 0);
|
|
268
|
+
assert.equal(run(true).status, 0);
|
|
269
|
+
const files = [...generateOutputs(fixtureRoot, sdk).keys()];
|
|
270
|
+
const snapshot = () => files.map((file) => fs.readFileSync(file, 'utf8'));
|
|
271
|
+
fs.appendFileSync(files[0], '// stale\n');
|
|
272
|
+
const stale = snapshot();
|
|
273
|
+
assert.notEqual(run(true).status, 0);
|
|
274
|
+
assert.deepEqual(snapshot(), stale);
|
|
275
|
+
assert.equal(run().status, 0);
|
|
276
|
+
const before = snapshot();
|
|
277
|
+
const changed = structuredClone(catalog);
|
|
278
|
+
changed[
|
|
279
|
+
Object.keys(changed).find((k) => changed[k].source === manifest[0].source)
|
|
280
|
+
].viewBox = [0, 0, 12, 12];
|
|
281
|
+
delete changed.DotVerSolid;
|
|
282
|
+
fs.writeFileSync(targetCatalog, JSON.stringify(changed));
|
|
283
|
+
assert.notEqual(run().status, 0);
|
|
284
|
+
assert.deepEqual(snapshot(), before);
|
|
285
|
+
} finally {
|
|
286
|
+
fs.rmSync(temp, { recursive: true, force: true });
|
|
287
|
+
}
|
|
288
|
+
});
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "AccountErrorCustom",
|
|
4
|
+
"source": "custom/account-error.svg",
|
|
5
|
+
"platforms": ["ios", "android", "web"],
|
|
6
|
+
"rendering": "original"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "AllNetworksSolid",
|
|
10
|
+
"source": "solid/all-networks.svg",
|
|
11
|
+
"platforms": ["ios", "android", "web"],
|
|
12
|
+
"rendering": "template"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "AppleBrand",
|
|
16
|
+
"source": "brand/apple.svg",
|
|
17
|
+
"platforms": ["ios", "android", "web"],
|
|
18
|
+
"rendering": "template"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "ArrowBottomOutline",
|
|
22
|
+
"source": "outline/arrow-bottom.svg",
|
|
23
|
+
"platforms": ["ios", "android"],
|
|
24
|
+
"rendering": "template"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "ArrowTopOutline",
|
|
28
|
+
"source": "outline/arrow-top.svg",
|
|
29
|
+
"platforms": ["ios", "android"],
|
|
30
|
+
"rendering": "template"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "BadgeVerifiedSolid",
|
|
34
|
+
"source": "solid/badge-recognized.svg",
|
|
35
|
+
"platforms": ["ios", "android", "web"],
|
|
36
|
+
"rendering": "template"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "BotIllus",
|
|
40
|
+
"source": "illus/bot.svg",
|
|
41
|
+
"platforms": ["ios", "android", "web"],
|
|
42
|
+
"rendering": "original"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "ChartTrendingUpOutline",
|
|
46
|
+
"source": "outline/chart-trending-up.svg",
|
|
47
|
+
"platforms": ["ios", "android"],
|
|
48
|
+
"rendering": "template"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "CheckboxCheckedCustom",
|
|
52
|
+
"source": "custom/checkbox-checked.svg",
|
|
53
|
+
"platforms": ["ios", "web"],
|
|
54
|
+
"rendering": "template"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "CheckboxIndeterminateCustom",
|
|
58
|
+
"source": "custom/checkbox-indeterminate.svg",
|
|
59
|
+
"platforms": ["ios", "web"],
|
|
60
|
+
"rendering": "template"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "ChevronBottomOutline",
|
|
64
|
+
"source": "outline/chevron-bottom.svg",
|
|
65
|
+
"platforms": ["ios", "android"],
|
|
66
|
+
"rendering": "template"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "ChevronGrabberVerOutline",
|
|
70
|
+
"source": "outline/chevron-grabber-ver.svg",
|
|
71
|
+
"platforms": ["ios", "android"],
|
|
72
|
+
"rendering": "template"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "ChevronRightSmallOutline",
|
|
76
|
+
"source": "outline/chevron-right-small.svg",
|
|
77
|
+
"platforms": ["ios", "android", "web"],
|
|
78
|
+
"rendering": "template"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "ChevronTopOutline",
|
|
82
|
+
"source": "outline/chevron-top.svg",
|
|
83
|
+
"platforms": ["ios", "android"],
|
|
84
|
+
"rendering": "template"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "Circle",
|
|
88
|
+
"source": null,
|
|
89
|
+
"platforms": ["android", "web"],
|
|
90
|
+
"rendering": "template"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "CrossedSmallSolid",
|
|
94
|
+
"source": "solid/crossed-small.svg",
|
|
95
|
+
"platforms": ["ios", "android", "web"],
|
|
96
|
+
"rendering": "template"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "CryptoCoinOutline",
|
|
100
|
+
"source": "outline/crypto-coin.svg",
|
|
101
|
+
"platforms": ["ios", "android", "web"],
|
|
102
|
+
"rendering": "template"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "Document2Outline",
|
|
106
|
+
"source": "outline/document-2.svg",
|
|
107
|
+
"platforms": ["ios", "android", "web"],
|
|
108
|
+
"rendering": "template"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "DotHorOutline",
|
|
112
|
+
"source": "outline/dot-hor.svg",
|
|
113
|
+
"platforms": ["ios", "android", "web"],
|
|
114
|
+
"rendering": "template"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"name": "DotVerSolid",
|
|
118
|
+
"source": "solid/dot-ver.svg",
|
|
119
|
+
"platforms": ["ios", "android", "web"],
|
|
120
|
+
"rendering": "template"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "DragOutline",
|
|
124
|
+
"source": "outline/drag.svg",
|
|
125
|
+
"platforms": ["ios", "android", "web"],
|
|
126
|
+
"rendering": "template"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"name": "ErrorSolid",
|
|
130
|
+
"source": "solid/error.svg",
|
|
131
|
+
"platforms": ["ios", "android"],
|
|
132
|
+
"rendering": "template"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"name": "GasSolid",
|
|
136
|
+
"source": "solid/gas.svg",
|
|
137
|
+
"platforms": ["ios", "android", "web"],
|
|
138
|
+
"rendering": "template"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "GlobusOutline",
|
|
142
|
+
"source": "outline/globus.svg",
|
|
143
|
+
"platforms": ["ios", "android", "web"],
|
|
144
|
+
"rendering": "template"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"name": "GoogleIllus",
|
|
148
|
+
"source": "illus/google.svg",
|
|
149
|
+
"platforms": ["ios", "android", "web"],
|
|
150
|
+
"rendering": "original"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"name": "ImageSquareWavesOutline",
|
|
154
|
+
"source": "outline/image-square-waves.svg",
|
|
155
|
+
"platforms": ["ios", "android"],
|
|
156
|
+
"rendering": "template"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "InfoCircleOutline",
|
|
160
|
+
"source": "outline/info-circle.svg",
|
|
161
|
+
"platforms": ["ios", "android"],
|
|
162
|
+
"rendering": "template"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"name": "LockSolid",
|
|
166
|
+
"source": "solid/lock.svg",
|
|
167
|
+
"platforms": ["ios", "android", "web"],
|
|
168
|
+
"rendering": "template"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "MinusCircleOutline",
|
|
172
|
+
"source": "outline/minus-circle.svg",
|
|
173
|
+
"platforms": ["ios", "android"],
|
|
174
|
+
"rendering": "template"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"name": "MinusCircleSolid",
|
|
178
|
+
"source": "solid/minus-circle.svg",
|
|
179
|
+
"platforms": ["ios", "android"],
|
|
180
|
+
"rendering": "template"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"name": "PencilOutline",
|
|
184
|
+
"source": "outline/pencil.svg",
|
|
185
|
+
"platforms": ["ios", "android", "web"],
|
|
186
|
+
"rendering": "template"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"name": "PlusCircleOutline",
|
|
190
|
+
"source": "outline/plus-circle.svg",
|
|
191
|
+
"platforms": ["ios", "android"],
|
|
192
|
+
"rendering": "template"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"name": "PlusLargeOutline",
|
|
196
|
+
"source": "outline/plus-large.svg",
|
|
197
|
+
"platforms": ["ios", "android", "web"],
|
|
198
|
+
"rendering": "template"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"name": "PlusSmallOutline",
|
|
202
|
+
"source": "outline/plus-small.svg",
|
|
203
|
+
"platforms": ["ios", "android", "web"],
|
|
204
|
+
"rendering": "template"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"name": "QuestionmarkSolid",
|
|
208
|
+
"source": "solid/questionmark.svg",
|
|
209
|
+
"platforms": ["ios", "android"],
|
|
210
|
+
"rendering": "template"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"name": "RefreshCcwOutline",
|
|
214
|
+
"source": "outline/refresh-ccw.svg",
|
|
215
|
+
"platforms": ["ios", "android", "web"],
|
|
216
|
+
"rendering": "template"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"name": "ShieldExclamationOutline",
|
|
220
|
+
"source": "outline/shield-exclamation.svg",
|
|
221
|
+
"platforms": ["ios", "android"],
|
|
222
|
+
"rendering": "template"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"name": "SpeakerPromoteOutline",
|
|
226
|
+
"source": "outline/speaker-promote.svg",
|
|
227
|
+
"platforms": ["ios", "android"],
|
|
228
|
+
"rendering": "template"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"name": "StarOutline",
|
|
232
|
+
"source": "outline/star.svg",
|
|
233
|
+
"platforms": ["ios", "android"],
|
|
234
|
+
"rendering": "template"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"name": "StarSolid",
|
|
238
|
+
"source": "solid/star.svg",
|
|
239
|
+
"platforms": ["ios", "android"],
|
|
240
|
+
"rendering": "template"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"name": "SwapHorOutline",
|
|
244
|
+
"source": "outline/swap-hor.svg",
|
|
245
|
+
"platforms": ["ios", "android"],
|
|
246
|
+
"rendering": "template"
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"name": "TicketOutline",
|
|
250
|
+
"source": "outline/ticket.svg",
|
|
251
|
+
"platforms": ["ios", "android", "web"],
|
|
252
|
+
"rendering": "template"
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
"name": "WalletOutline",
|
|
256
|
+
"source": "outline/wallet.svg",
|
|
257
|
+
"platforms": ["ios", "android", "web"],
|
|
258
|
+
"rendering": "template"
|
|
259
|
+
}
|
|
260
|
+
]
|