···
10
10
arch: string
11
11
ending: string
12
12
}
13
13
-
const macOsArm: Version = {
14
14
-
os: 'macOS',
15
15
-
arch: 'arm64',
16
16
-
ending: '-mac-arm64.dmg',
17
17
-
}
18
18
-
const macOsX64: Version = {
19
19
-
os: 'macOS',
20
20
-
arch: 'x64',
21
21
-
ending: '-mac-x64.dmg',
22
22
-
}
23
23
-
const windowsX64: Version = {
24
24
-
os: 'Windows',
25
25
-
arch: 'x64',
26
26
-
ending: '-win-x64.exe',
27
27
-
}
28
28
-
const windowsArm: Version = {
29
29
-
os: 'Windows',
30
30
-
arch: 'arm64',
31
31
-
ending: '-win-arm64.exe',
32
32
-
}
33
33
-
const linuxDeb: Version = {
34
34
-
os: 'Linux .deb',
35
35
-
arch: 'x64',
36
36
-
ending: '-linux-amd64.deb',
37
37
-
}
38
38
-
const linuxlinuxRpm: Version = {
39
39
-
os: 'Linux .rpm',
40
40
-
arch: 'x64',
41
41
-
ending: 'linux-x86_64.rpm',
42
42
-
}
43
43
-
const versionList: Version[] = [
44
44
-
macOsArm,
45
45
-
macOsX64,
46
46
-
windowsX64,
47
47
-
windowsArm,
48
48
-
linuxDeb,
49
49
-
linuxlinuxRpm,
13
13
+
const macos_arm: Version = { os: 'macOS', arch: 'arm64', ending: '-mac-arm64.dmg' }
14
14
+
const macos_x64: Version = { os: 'macOS', arch: 'x64', ending: '-mac-x64.dmg' }
15
15
+
const windows_x64: Version = { os: 'Windows', arch: 'x64', ending: '-win-x64.exe' }
16
16
+
const windows_arm: Version = { os: 'Windows', arch: 'arm64', ending: '-win-arm64.exe' }
17
17
+
const linux_deb: Version = { os: 'Linux', arch: '.deb', ending: '-linux-amd64.deb' }
18
18
+
const linux_rpm: Version = { os: 'Linux', arch: '.rpm', ending: 'linux-x86_64.rpm' }
19
19
+
const android_obtainium: Version = { os: 'Obtainium', arch: '', ending: '' }
20
20
+
const android_arm64: Version = { os: 'Android', arch: 'arm64', ending: '-android-arm64.apk' }
21
21
+
const android_x86_64: Version = { os: 'Android', arch: 'x86_64', ending: '-android-x86_64.apk' }
22
22
+
23
23
+
const desktop_versions: Version[] = [
24
24
+
macos_arm,
25
25
+
macos_x64,
26
26
+
windows_x64,
27
27
+
windows_arm,
28
28
+
linux_deb,
29
29
+
linux_rpm,
50
30
]
51
51
-
let suggestedVersion = windowsX64
31
31
+
const android_versions: Version[] = [android_obtainium, android_arm64, android_x86_64]
32
32
+
33
33
+
const obtainium_url =
34
34
+
'https://apps.obtainium.imranr.dev/redirect?r=obtainium://app/%7B%22id%22%3A%22space.kasper.ferrum%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fprobablykasper%2Fferrum%22%2C%22author%22%3A%22probablykasper%22%2C%22name%22%3A%22Ferrum%22%2C%22preferredApkIndex%22%3A0%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Afalse%2C%5C%22fallbackToOlderReleases%5C%22%3Atrue%2C%5C%22filterReleaseTitlesByRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22filterReleaseNotesByRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22verifyLatestTag%5C%22%3Afalse%2C%5C%22sortMethodChoice%5C%22%3A%5C%22date%5C%22%2C%5C%22useLatestAssetDateAsReleaseDate%5C%22%3Afalse%2C%5C%22releaseTitleAsVersion%5C%22%3Afalse%2C%5C%22trackOnly%5C%22%3Afalse%2C%5C%22versionExtractionRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22matchGroupToUse%5C%22%3A%5C%22%5C%22%2C%5C%22versionDetection%5C%22%3Atrue%2C%5C%22releaseDateAsVersion%5C%22%3Afalse%2C%5C%22useVersionCodeAsOSVersion%5C%22%3Afalse%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22invertAPKFilter%5C%22%3Afalse%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22%5C%22%2C%5C%22appAuthor%5C%22%3A%5C%22%5C%22%2C%5C%22shizukuPretendToBeGooglePlay%5C%22%3Afalse%2C%5C%22allowInsecure%5C%22%3Afalse%2C%5C%22exemptFromBackgroundUpdates%5C%22%3Afalse%2C%5C%22skipUpdateNotifications%5C%22%3Afalse%2C%5C%22about%5C%22%3A%5C%22%5C%22%2C%5C%22refreshBeforeDownload%5C%22%3Afalse%2C%5C%22includeZips%5C%22%3Afalse%2C%5C%22zippedApkFilterRegEx%5C%22%3A%5C%22%5C%22%7D%22%2C%22overrideSource%22%3A%22GitHub%22%7D'
35
35
+
36
36
+
let suggested_desktop = windows_x64
37
37
+
52
38
onMount(() => {
53
39
const browser = Bowser.getParser(window.navigator.userAgent)
54
54
-
const osName = browser.getOSName()
55
55
-
if (osName === 'macOS' || osName === 'iOS') {
56
56
-
suggestedVersion = macOsArm
57
57
-
} else if (osName === 'Windows') {
58
58
-
suggestedVersion = windowsX64
59
59
-
} else if (osName === 'Linux' || osName === 'Chrome OS') {
60
60
-
suggestedVersion = linuxDeb
40
40
+
const os_name = browser.getOSName()
41
41
+
if (os_name === 'macOS' || os_name === 'iOS') {
42
42
+
suggested_desktop = macos_arm
43
43
+
} else if (os_name === 'Windows') {
44
44
+
suggested_desktop = windows_x64
45
45
+
} else if (os_name === 'Linux' || os_name === 'Chrome OS') {
46
46
+
suggested_desktop = linux_deb
61
47
}
62
48
})
63
49
64
64
-
let downloadError = ''
50
50
+
let download_error = ''
65
51
let loading = false
66
52
type LatestRelease = RestEndpointMethodTypes['repos']['getLatestRelease']['response']['data']
67
53
68
68
-
function getAsset(assets: LatestRelease['assets'], version: Version) {
54
54
+
function get_asset(assets: LatestRelease['assets'], version: Version) {
69
55
for (const asset of assets) {
70
70
-
if (asset.name.toLowerCase().endsWith(version.ending)) {
71
71
-
return asset
72
72
-
}
56
56
+
if (asset.name.toLowerCase().endsWith(version.ending)) return asset
73
57
}
74
58
return null
75
59
}
76
76
-
async function innerDownload(version: Version) {
77
77
-
downloadError = ''
60
60
+
61
61
+
async function inner_download(version: Version) {
62
62
+
download_error = ''
63
63
+
if (version.os === 'Obtainium') {
64
64
+
window.open(obtainium_url, '_blank', 'noopener,noreferrer')
65
65
+
return
66
66
+
}
78
67
try {
79
68
const response = await fetch(
80
80
-
`https://api.github.com/repos/probablykasper/ferrum/releases/latest`,
69
69
+
'https://api.github.com/repos/probablykasper/ferrum/releases/latest',
81
70
)
82
82
-
console.log(response)
83
83
-
84
71
const json: LatestRelease = await response.json()
85
85
-
console.log(json)
86
86
-
87
72
if (response.status !== 200) {
88
88
-
downloadError = 'Could not download'
73
73
+
download_error = 'Could not download'
89
74
console.error('Error response from GitHub', json)
90
75
return
91
76
}
92
92
-
93
93
-
const asset = getAsset(json.assets, version)
77
77
+
const asset = get_asset(json.assets, version)
94
78
if (asset === null) {
95
95
-
downloadError = 'No file found'
79
79
+
download_error = 'No file found'
96
80
console.error('No file found')
97
81
return
98
82
}
99
99
-
console.log(asset)
100
83
location.href = asset.browser_download_url
101
84
} catch (error) {
102
102
-
downloadError = 'Network error'
85
85
+
download_error = 'Network error'
103
86
console.error(error)
104
87
}
105
88
}
89
89
+
106
90
async function download(version: Version) {
107
91
loading = true
108
108
-
await innerDownload(version)
92
92
+
await inner_download(version)
109
93
loading = false
110
94
}
111
95
</script>
112
96
113
113
-
<div class="mb-2 h-6">
114
114
-
{#if downloadError}
115
115
-
<div
116
116
-
class="error block text-center text-sm font-semibold text-red-500"
117
117
-
transition:fly={{ y: 3, duration: 200 }}
97
97
+
<!-- Hidden SVG sprite sheet for all logos -->
98
98
+
<svg style="display:none" aria-hidden="true">
99
99
+
<!-- Tabler filled Apple -->
100
100
+
<symbol id="icon-apple" viewBox="0 0 24 24" fill="currentColor">
101
101
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
102
102
+
<path
103
103
+
d="M15.079 5.999l.239 .012c1.43 .097 3.434 1.013 4.508 2.586a1 1 0 0 1 -.344 1.44c-.05 .028 -.372 .158 -.497 .217a4.15 4.15 0 0 0 -.722 .431c-.614 .461 -.948 1.009 -.942 1.694c.01 .885 .339 1.454 .907 1.846c.208 .143 .436 .253 .666 .33c.126 .043 .426 .116 .444 .122a1 1 0 0 1 .662 .942c0 2.621 -3.04 6.381 -5.286 6.381c-.79 0 -1.272 -.091 -1.983 -.315l-.098 -.031c-.463 -.146 -.702 -.192 -1.133 -.192c-.52 0 -.863 .06 -1.518 .237l-.197 .053c-.575 .153 -.964 .226 -1.5 .248c-2.749 0 -5.285 -5.093 -5.285 -9.072c0 -3.87 1.786 -6.92 5.286 -6.92c.297 0 .598 .045 .909 .128c.403 .107 .774 .26 1.296 .508c.787 .374 .948 .44 1.009 .44h.016c.03 -.003 .128 -.047 1.056 -.457c1.061 -.467 1.864 -.685 2.746 -.616l-.24 -.012z"
104
104
+
/>
105
105
+
<path d="M14 1a1 1 0 0 1 1 1a3 3 0 0 1 -3 3a1 1 0 0 1 -1 -1a3 3 0 0 1 3 -3z" />
106
106
+
</symbol>
107
107
+
<!-- Tabler filled Windows -->
108
108
+
<symbol id="icon-windows" viewBox="0 0 24 24" fill="currentColor">
109
109
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
110
110
+
<path
111
111
+
d="M21 13v5c0 1.57 -1.248 2.832 -2.715 2.923l-.113 .003l-.042 .018a1 1 0 0 1 -.336 .056l-.118 -.008l-4.676 -.585v-7.407zm-10 0v7.157l-5.3 -.662c-1.514 -.151 -2.7 -1.383 -2.7 -2.895v-3.6zm0 -9.158v7.158h-8v-3.6c0 -1.454 1.096 -2.648 2.505 -2.87zm10 2.058v5.1h-8v-7.409l4.717 -.589c1.759 -.145 3.283 1.189 3.283 2.898"
112
112
+
/>
113
113
+
</symbol>
114
114
+
<!-- Custom Tux -->
115
115
+
<symbol id="icon-linux" viewBox="3 2 18 20" fill="currentColor">
116
116
+
<path
117
117
+
d="M6.09159 20.5316C7.0409 20.645 8.10753 21.2599 9.00016 21.3685C9.89759 21.4818 10.1753 20.7574 10.1753 20.7574C10.1753 20.7574 11.1852 20.5316 12.2469 20.5056C13.3096 20.4758 14.3156 20.7266 14.3156 20.7266C14.3156 20.7266 14.5107 21.1734 14.8749 21.3685C15.2391 21.5674 16.0231 21.5943 16.5256 21.0648C17.0291 20.5316 18.3724 19.8599 19.1267 19.44C19.8858 19.0191 19.7465 18.3773 19.2699 18.1822C18.7934 17.9872 18.4032 17.6797 18.433 17.0897C18.459 16.5046 18.0122 16.1145 18.0122 16.1145C18.0122 16.1145 18.4032 14.8269 18.0391 13.7603C17.6749 12.6986 16.4738 10.9911 15.5504 9.70743C14.6271 8.41988 15.4111 6.93343 14.5704 5.03377C13.7296 3.13126 11.5494 3.24371 10.3743 4.05463C9.19913 4.8656 9.55942 6.87668 9.6161 7.83085C9.67279 8.78017 9.64204 9.45948 9.53347 9.7036C9.4249 9.95148 8.66679 10.8518 8.1633 11.6061C7.66079 12.3642 7.29656 13.9295 6.92856 14.5752C6.56822 15.2171 6.81999 15.8022 6.81999 15.8022C6.81999 15.8022 6.56827 15.8887 6.36936 16.3096C6.17427 16.7256 5.78416 16.9245 5.08176 17.0601C4.38416 17.2032 4.38416 17.6529 4.55233 18.1564C4.72142 18.6589 4.55233 18.9404 4.35725 19.5823C4.16222 20.2241 5.13845 20.4191 6.09159 20.5316ZM15.9597 17.6038C16.4584 17.8219 17.1752 17.5182 17.3933 17.3001C17.6105 17.083 17.7642 16.7601 17.7642 16.7601C17.7642 16.7601 17.9823 16.8687 17.9602 17.2136C17.9371 17.5634 18.1101 18.062 18.4368 18.235C18.7635 18.407 19.2622 18.6472 19.0037 18.8874C18.7404 19.1276 17.2838 19.7138 16.8485 20.1711C16.4171 20.6256 15.8502 20.9975 15.5052 20.8879C15.1564 20.7794 14.8518 20.3028 15.0018 19.6052C15.1564 18.9105 15.2871 18.1485 15.265 17.7132C15.242 17.278 15.1564 16.6919 15.265 16.6054C15.3736 16.5198 15.5466 16.5611 15.5466 16.5611C15.5466 16.5611 15.46 17.3867 15.9597 17.6038ZM12.7235 5.84474C13.2039 5.84474 13.5912 6.32131 13.5912 6.90743C13.5912 7.32348 13.3961 7.68383 13.1107 7.8568C13.0387 7.82703 12.9637 7.79337 12.8811 7.75874C13.054 7.67325 13.1741 7.45514 13.1741 7.20337C13.1741 6.87383 12.9714 6.60286 12.7167 6.60286C12.4688 6.60286 12.2622 6.87377 12.2622 7.20337C12.2622 7.32354 12.292 7.4436 12.341 7.53777C12.1911 7.4772 12.0556 7.42537 11.9471 7.38405C11.8904 7.24085 11.8567 7.07943 11.8567 6.90748C11.8568 6.32137 12.243 5.84474 12.7235 5.84474ZM11.5339 7.67617C11.7703 7.71748 12.4198 7.99903 12.6601 8.08548C12.9003 8.16811 13.1664 8.32183 13.1405 8.4756C13.1107 8.63411 12.9868 8.63411 12.6601 8.83303C12.3372 9.02811 11.632 9.46337 11.4062 9.49314C11.1813 9.52291 11.0535 9.39605 10.8132 9.24137C10.573 9.08377 10.1224 8.71577 10.2358 8.52074C10.2358 8.52074 10.5884 8.25074 10.7422 8.1124C10.8959 7.9692 11.2937 7.63194 11.5339 7.67617ZM10.4981 6.01388C10.8767 6.01388 11.1851 6.46451 11.1851 7.01988C11.1851 7.1208 11.1736 7.21491 11.1554 7.30908C11.0612 7.33886 10.9671 7.38788 10.8767 7.46668C10.8325 7.50417 10.7911 7.53777 10.7537 7.57526C10.8132 7.46286 10.8363 7.30143 10.8094 7.13228C10.7575 6.83251 10.5548 6.61057 10.3559 6.64034C10.156 6.674 10.0368 6.9488 10.0849 7.25246C10.1378 7.55994 10.3366 7.78183 10.5394 7.74823C10.5509 7.7444 10.5615 7.74057 10.573 7.73668C10.476 7.83085 10.3856 7.91348 10.2915 7.98074C10.0176 7.85291 9.8149 7.47051 9.8149 7.01988C9.81496 6.46068 10.1186 6.01388 10.4981 6.01388ZM8.38902 13.2838C8.77913 12.6689 9.03084 11.3246 9.42096 10.8778C9.81496 10.4319 10.1186 9.48165 9.98021 9.06177C9.98021 9.06177 10.821 10.0678 11.4062 9.90251C11.9923 9.73337 13.3096 8.75428 13.5047 8.92245C13.6998 9.09154 15.3774 12.7813 15.5465 13.9564C15.7156 15.1306 15.4341 16.0281 15.4341 16.0281C15.4341 16.0281 14.7923 15.8589 14.7096 16.249C14.627 16.643 14.627 18.0699 14.627 18.0699C14.627 18.0699 13.7593 19.271 12.416 19.4698C11.0727 19.6649 10.4 19.5227 10.4 19.5227L9.64576 18.6588C9.64576 18.6588 10.2319 18.5724 10.1492 17.9834C10.0666 17.3982 8.35816 16.5872 8.05067 15.8589C7.7433 15.1305 7.9941 13.8997 8.38902 13.2838ZM5.0673 17.6903C5.13456 17.402 6.00507 17.4021 6.33947 17.1993C6.67387 16.9966 6.74107 16.4143 7.01113 16.2606C7.27724 16.103 7.76924 16.6622 7.97199 16.9773C8.1709 17.2848 8.9329 18.6291 9.24422 18.9634C9.55936 19.3007 9.84862 19.7475 9.75827 20.1491C9.67279 20.5507 9.19907 20.8438 9.19907 20.8438C8.77536 20.9745 7.59347 20.4643 7.05639 20.2394C6.5193 20.0136 5.1529 19.9463 4.97707 19.7475C4.79645 19.5447 5.06353 19.0979 5.13462 18.6742C5.19799 18.2456 4.99913 17.9795 5.0673 17.6903Z"
118
118
+
/>
119
119
+
</symbol>
120
120
+
<!-- Tabler outline desktop -->
121
121
+
<symbol
122
122
+
id="icon-desktop"
123
123
+
viewBox="0 0 24 24"
124
124
+
fill="none"
125
125
+
stroke="currentColor"
126
126
+
stroke-width="2"
127
127
+
stroke-linecap="round"
128
128
+
stroke-linejoin="round"
129
129
+
>
130
130
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
131
131
+
<path d="M3 5a1 1 0 0 1 1 -1h16a1 1 0 0 1 1 1v10a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1v-10" />
132
132
+
<path d="M7 20h10" /><path d="M9 16v4" /><path d="M15 16v4" />
133
133
+
</symbol>
134
134
+
<!-- Tabler outline Android -->
135
135
+
<symbol
136
136
+
id="icon-android"
137
137
+
viewBox="0 0 24 24"
138
138
+
fill="none"
139
139
+
stroke="currentColor"
140
140
+
stroke-width="2"
141
141
+
stroke-linecap="round"
142
142
+
stroke-linejoin="round"
143
143
+
>
144
144
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
145
145
+
<path d="M4 10l0 6" /><path d="M20 10l0 6" />
146
146
+
<path d="M7 9h10v8a1 1 0 0 1 -1 1h-8a1 1 0 0 1 -1 -1v-8a5 5 0 0 1 10 0" />
147
147
+
<path d="M8 3l1 2" /><path d="M16 3l-1 2" />
148
148
+
<path d="M9 18l0 3" /><path d="M15 18l0 3" />
149
149
+
</symbol>
150
150
+
<!-- Obtainium real logo -->
151
151
+
<symbol id="icon-obtainium" viewBox="30.204 75 512.59 471.09">
152
152
+
<path
153
153
+
fill="currentColor"
154
154
+
d="M380.1 542.6c-6.5-3.4-44.3-24.4-221.4-122.7-49.3-27.4-96.8-53.7-105.5-58.5-19.8-10.9-22.8-13.6-22.8-20.1 0-3.4 7.9-25.4 26.4-73.5 14.5-37.7 27.4-70.8 28.5-73.5 2.7-6.1 7-11.3 11.2-13.5 3.9-2 102.4-38 107.5-39.2 5.4-1.3 10.1 1.2 12.9 7.1 1.3 2.8 6.8 17.2 12.2 32.1s13.4 36.8 17.9 48.8c10.1 27.5 10.5 31.4 3.5 35.5-1.9 1.1-14.1 5.9-27.1 10.6s-25.4 9.6-27.6 10.9c-5 2.9-5.7 7.4-1.8 11 1.6 1.4 26.6 15.7 55.6 31.7 81.2 44.8 76.7 42.7 82.1 36.9 2.7-2.9 51.1-126.2 53.9-137.2 1.2-4.7 1.1-5.3-1.5-7.4-3.5-2.8-3-2.9-34.8 8.3-31.6 11.2-34.6 11.4-38.8 3.1-2.2-4.4-36.2-97.8-38.3-105.3-1.4-4.9.3-9.5 4.4-12.2 1.7-1.1 26.5-10.6 55-21.1 42.4-15.6 53.1-19.1 58.7-19.2 7.1-.1 8 .4 138.4 72.5 9 5 13.9 10.3 13.9 15.1 0 1.9-32.3 87.7-71.8 190.5-55.7 145.1-72.5 187.6-75 189.8-4.3 3.9-7.7 3.7-15.7-.5z"
155
155
+
/>
156
156
+
</symbol>
157
157
+
<!-- Tabler outline package -->
158
158
+
<symbol
159
159
+
id="icon-apk"
160
160
+
viewBox="0 0 24 24"
161
161
+
fill="none"
162
162
+
stroke="currentColor"
163
163
+
stroke-width="2"
164
164
+
stroke-linecap="round"
165
165
+
stroke-linejoin="round"
166
166
+
>
167
167
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
168
168
+
<path d="M12 3l8 4.5l0 9l-8 4.5l-8 -4.5l0 -9l8 -4.5" />
169
169
+
<path d="M12 12l8 -4.5" /><path d="M12 12l0 9" /><path d="M12 12l-8 -4.5" />
170
170
+
<path d="M16 5.25l-8 4.5" />
171
171
+
</symbol>
172
172
+
</svg>
173
173
+
174
174
+
<div class="flex w-full flex-col items-center gap-3">
175
175
+
{#if download_error}
176
176
+
<p
177
177
+
class="error-glow text-sm font-semibold text-red-400"
178
178
+
transition:fly={{ y: -4, duration: 200 }}
118
179
>
119
119
-
{downloadError}
120
120
-
</div>
180
180
+
{download_error}
181
181
+
</p>
121
182
{/if}
122
122
-
</div>
123
183
124
124
-
<ButtonPopup let:toggle let:isOpen let:close>
125
184
<div
126
126
-
class="relative mx-auto flex h-9 items-center border border-white/10 bg-white/5 text-base text-[15px] font-medium transition-all duration-200 ease-in-out hover:border-white/20"
127
127
-
class:rounded-2xl={!isOpen}
128
128
-
class:rounded-lg={isOpen}
129
129
-
class:pointer-events-none={loading}
130
130
-
class:opacity-75={loading}
185
185
+
class="flex w-full max-w-xl items-stretch rounded-2xl border border-white/10 bg-white/[0.04]"
131
186
>
132
132
-
<button
133
133
-
class="group relative flex h-full cursor-pointer items-center pr-4 pl-5 text-white/70 outline-none transition-all duration-200 hover:text-white/100"
134
134
-
on:click={() => download(suggestedVersion)}
135
135
-
class:opacity-50={loading}
136
136
-
>
187
187
+
<!-- ── Desktop ── -->
188
188
+
<div class="relative z-10 flex flex-1 flex-col gap-2.5 p-5">
137
189
<div
138
138
-
class="ease-out-cubic opacity-0 transition-all duration-700 group-hover:opacity-40 group-focus:opacity-40"
190
190
+
class="flex items-center gap-1.5 text-[10px] font-semibold tracking-widest text-white/35 uppercase"
139
191
>
192
192
+
<svg
193
193
+
width="15"
194
194
+
height="15"
195
195
+
fill="none"
196
196
+
stroke="currentColor"
197
197
+
stroke-width="2"
198
198
+
stroke-linecap="round"
199
199
+
stroke-linejoin="round"
200
200
+
aria-hidden="true"><use href="#icon-desktop" /></svg
201
201
+
>
202
202
+
Desktop
203
203
+
</div>
204
204
+
<p class="-mt-1 text-xs leading-snug text-white/0 select-none" aria-hidden="true">.</p>
205
205
+
206
206
+
<ButtonPopup let:toggle let:isOpen let:close>
140
207
<div
141
141
-
class="gradient gradient-3 ease-out-cubic absolute inset-0 -z-10 scale-80 transition-all duration-700 group-hover:scale-100 group-hover:blur-md group-focus:scale-100 group-focus:blur-md"
142
142
-
></div>
143
143
-
</div>
144
144
-
Download for {suggestedVersion.os}
145
145
-
{suggestedVersion.arch}
146
146
-
</button>
147
147
-
<div class="h-5 border-l border-white/30"></div>
148
148
-
<button
149
149
-
class="group relative h-full cursor-pointer pr-5 pl-4 text-white/70 outline-none transition-all duration-200 hover:text-white/100"
150
150
-
on:click={toggle}
151
151
-
aria-label="Download for other platforms"
152
152
-
>
208
208
+
class="relative flex h-[38px] items-center border bg-white/[0.06] text-sm font-medium transition-[border-color,border-radius] duration-200 {isOpen
209
209
+
? 'rounded-t-[10px] border-white/20'
210
210
+
: 'rounded-xl border-white/[0.12]'} {loading ? 'pointer-events-none opacity-60' : ''}"
211
211
+
>
212
212
+
<button
213
213
+
type="button"
214
214
+
class="flex h-full cursor-pointer items-center gap-1.5 pr-3.5 pl-3 whitespace-nowrap text-white/70 transition-colors duration-150 outline-none hover:text-white"
215
215
+
on:click={() => download(suggested_desktop)}
216
216
+
>
217
217
+
<svg
218
218
+
width="15"
219
219
+
height="15"
220
220
+
fill="currentColor"
221
221
+
class="shrink-0 opacity-80"
222
222
+
aria-hidden="true"
223
223
+
>
224
224
+
<use
225
225
+
href={suggested_desktop.os === 'macOS'
226
226
+
? '#icon-apple'
227
227
+
: suggested_desktop.os === 'Windows'
228
228
+
? '#icon-windows'
229
229
+
: '#icon-linux'}
230
230
+
/>
231
231
+
</svg>
232
232
+
Download for {suggested_desktop.os}
233
233
+
<span class="text-xs text-white/40">{suggested_desktop.arch}</span>
234
234
+
</button>
235
235
+
<div class="h-5 w-px shrink-0 bg-white/15"></div>
236
236
+
<button
237
237
+
type="button"
238
238
+
class="flex h-full cursor-pointer items-center justify-center px-3 text-white/50 transition-colors duration-150 outline-none hover:text-white/90"
239
239
+
on:click={toggle}
240
240
+
aria-label="Other desktop platforms"
241
241
+
>
242
242
+
<svg
243
243
+
fill="currentColor"
244
244
+
viewBox="0 0 24 24"
245
245
+
width="11"
246
246
+
height="11"
247
247
+
class="chevron"
248
248
+
class:rotated={isOpen}
249
249
+
aria-hidden="true"
250
250
+
>
251
251
+
<path d="M0 7.33l2.829-2.83 9.175 9.339 9.167-9.339 2.829 2.83-11.996 12.17z" />
252
252
+
</svg>
253
253
+
</button>
254
254
+
</div>
255
255
+
256
256
+
<div
257
257
+
slot="popup"
258
258
+
class="dropdown absolute top-full right-0 left-0 overflow-hidden rounded-b-[10px] border border-t-0 border-white/20 bg-white/5 backdrop-blur-xl"
259
259
+
style="z-index: 9999;"
260
260
+
transition:scale={{ start: 0.95, opacity: 0, duration: 160 }}
261
261
+
>
262
262
+
{#each desktop_versions as version}
263
263
+
<button
264
264
+
type="button"
265
265
+
class="dropdown-item flex h-9 w-full cursor-pointer items-center gap-2 bg-transparent px-3.5 text-left text-[13.5px] text-white/80 transition-colors duration-100 outline-none hover:bg-white/[0.06] hover:text-white"
266
266
+
on:click={() => {
267
267
+
close()
268
268
+
download(version)
269
269
+
}}
270
270
+
>
271
271
+
<svg
272
272
+
width="15"
273
273
+
height="15"
274
274
+
fill="currentColor"
275
275
+
class="shrink-0 opacity-70"
276
276
+
aria-hidden="true"
277
277
+
>
278
278
+
<use
279
279
+
href={version.os === 'macOS'
280
280
+
? '#icon-apple'
281
281
+
: version.os === 'Windows'
282
282
+
? '#icon-windows'
283
283
+
: '#icon-linux'}
284
284
+
/>
285
285
+
</svg>
286
286
+
<span class="flex-1">{version.os}</span>
287
287
+
<span class="text-xs text-white/35">{version.arch}</span>
288
288
+
</button>
289
289
+
{/each}
290
290
+
</div>
291
291
+
</ButtonPopup>
292
292
+
</div>
293
293
+
294
294
+
<!-- vertical divider -->
295
295
+
<div class="my-3.5 w-px shrink-0 bg-white/[0.08]"></div>
296
296
+
297
297
+
<!-- ── Android ── -->
298
298
+
<div class="relative z-[5] flex flex-1 flex-col gap-2.5 p-5">
153
299
<div
154
154
-
class="ease-out-cubic opacity-0 transition-all duration-700 group-hover:opacity-40 group-focus:opacity-40"
300
300
+
class="flex items-center gap-1.5 text-[10px] font-semibold tracking-widest text-white/35 uppercase"
155
301
>
302
302
+
<svg width="15" height="15" fill="currentColor" aria-hidden="true"
303
303
+
><use href="#icon-android" /></svg
304
304
+
>
305
305
+
Android
306
306
+
</div>
307
307
+
<p class="-mt-1 text-xs leading-snug text-white/30">Currently library browser only</p>
308
308
+
309
309
+
<ButtonPopup let:toggle let:isOpen let:close>
156
310
<div
157
157
-
class="gradient gradient-3 ease-out-cubic absolute inset-0 -z-10 scale-80 transition-all duration-700 group-hover:scale-100 group-hover:blur-md group-focus:scale-100 group-focus:blur-md"
158
158
-
></div>
159
159
-
</div>
160
160
-
<svg
161
161
-
fill="currentColor"
162
162
-
xmlns="http://www.w3.org/2000/svg"
163
163
-
width="12"
164
164
-
height="12"
165
165
-
viewBox="0 0 24 24"
166
166
-
><path d="M0 7.33l2.829-2.83 9.175 9.339 9.167-9.339 2.829 2.83-11.996 12.17z" /></svg
167
167
-
>
168
168
-
</button>
169
169
-
</div>
170
170
-
<div
171
171
-
slot="popup"
172
172
-
class="mt-0.5 w-full divide-y divide-white/10 rounded-lg border border-white/20 bg-white/5 text-[15px] text-white backdrop-blur-md will-change-contents"
173
173
-
transition:scale={{ start: 0.9, opacity: 0, duration: 200 }}
174
174
-
>
175
175
-
{#each versionList as version}
176
176
-
<button
177
177
-
class="focus:bg-opacity-5 h-9 w-full cursor-pointer bg-white/0 px-5 text-left outline-none hover:bg-white/5"
178
178
-
on:click={() => {
179
179
-
close()
180
180
-
download(version)
181
181
-
}}
182
182
-
>
183
183
-
{version.os} <span class="opacity-70">{version.arch}</span>
184
184
-
</button>
185
185
-
{/each}
311
311
+
class="relative flex h-[38px] items-center border bg-white/[0.06] text-sm font-medium transition-[border-color,border-radius] duration-200 {isOpen
312
312
+
? 'rounded-t-[10px] border-white/20'
313
313
+
: 'rounded-xl border-white/[0.12]'} {loading ? 'pointer-events-none opacity-60' : ''}"
314
314
+
>
315
315
+
<a
316
316
+
href={obtainium_url}
317
317
+
target="_blank"
318
318
+
rel="noopener noreferrer"
319
319
+
class="flex h-full cursor-pointer items-center gap-1.5 pr-3.5 pl-3 whitespace-nowrap text-white/70 transition-colors duration-150 outline-none hover:text-white"
320
320
+
>
321
321
+
<svg
322
322
+
width="14"
323
323
+
height="14"
324
324
+
fill="none"
325
325
+
stroke="currentColor"
326
326
+
stroke-width="2"
327
327
+
stroke-linecap="round"
328
328
+
stroke-linejoin="round"
329
329
+
class="shrink-0 opacity-75"
330
330
+
aria-hidden="true"
331
331
+
>
332
332
+
<use href="#icon-obtainium" />
333
333
+
</svg>
334
334
+
Get it on Obtainium
335
335
+
</a>
336
336
+
<div class="h-5 w-px shrink-0 bg-white/15"></div>
337
337
+
<button
338
338
+
type="button"
339
339
+
class="flex h-full cursor-pointer items-center justify-center px-3 text-white/50 transition-colors duration-150 outline-none hover:text-white/90"
340
340
+
on:click={toggle}
341
341
+
aria-label="Other Android options"
342
342
+
>
343
343
+
<svg
344
344
+
fill="currentColor"
345
345
+
viewBox="0 0 24 24"
346
346
+
width="11"
347
347
+
height="11"
348
348
+
class="chevron"
349
349
+
class:rotated={isOpen}
350
350
+
aria-hidden="true"
351
351
+
>
352
352
+
<path d="M0 7.33l2.829-2.83 9.175 9.339 9.167-9.339 2.829 2.83-11.996 12.17z" />
353
353
+
</svg>
354
354
+
</button>
355
355
+
</div>
356
356
+
357
357
+
<div
358
358
+
slot="popup"
359
359
+
class="dropdown absolute top-full right-0 left-0 overflow-hidden rounded-b-[10px] border border-t-0 border-white/20 bg-white/5 backdrop-blur-xl"
360
360
+
style="z-index: 9999;"
361
361
+
transition:scale={{ start: 0.95, opacity: 0, duration: 160 }}
362
362
+
>
363
363
+
{#each android_versions as version}
364
364
+
{#if version.os === 'Obtainium'}
365
365
+
<a
366
366
+
href={obtainium_url}
367
367
+
target="_blank"
368
368
+
rel="noopener noreferrer"
369
369
+
class="dropdown-item flex h-9 w-full cursor-pointer items-center gap-2 bg-transparent px-3.5 text-left text-[13.5px] text-white/80 no-underline transition-colors duration-100 outline-none hover:bg-white/[0.06] hover:text-white"
370
370
+
on:click={close}
371
371
+
>
372
372
+
<svg
373
373
+
width="15"
374
374
+
height="15"
375
375
+
fill="currentColor"
376
376
+
class="shrink-0 opacity-70"
377
377
+
aria-hidden="true"
378
378
+
>
379
379
+
<use href="#icon-obtainium" />
380
380
+
</svg>
381
381
+
<span class="flex-1">Obtainium</span>
382
382
+
<span class="text-xs text-white/35">recommended</span>
383
383
+
</a>
384
384
+
{:else}
385
385
+
<button
386
386
+
type="button"
387
387
+
class="dropdown-item flex h-9 w-full cursor-pointer items-center gap-2 bg-transparent px-3.5 text-left text-[13.5px] text-white/80 transition-colors duration-100 outline-none hover:bg-white/[0.06] hover:text-white"
388
388
+
on:click={() => {
389
389
+
close()
390
390
+
download(version)
391
391
+
}}
392
392
+
>
393
393
+
<svg
394
394
+
width="15"
395
395
+
height="15"
396
396
+
fill="currentColor"
397
397
+
class="shrink-0 opacity-70"
398
398
+
aria-hidden="true"
399
399
+
>
400
400
+
<use href="#icon-apk" />
401
401
+
</svg>
402
402
+
<span class="flex-1">APK</span>
403
403
+
<span class="text-xs text-white/35">{version.arch}</span>
404
404
+
</button>
405
405
+
{/if}
406
406
+
{/each}
407
407
+
</div>
408
408
+
</ButtonPopup>
409
409
+
</div>
186
410
</div>
187
187
-
</ButtonPopup>
411
411
+
</div>
188
412
189
189
-
<style lang="sass">
190
190
-
.gradient
191
191
-
will-change: contents
192
192
-
.gradient-3
193
193
-
background: linear-gradient(130deg,#3EFF8A 10%,#03B5FF 90%)
194
194
-
.error
195
195
-
text-shadow: 0px 0px 7px hsl(0deg 100% 50% / 60%)
413
413
+
<style>
414
414
+
.error-glow {
415
415
+
text-shadow: 0 0 8px hsl(0 100% 60% / 50%);
416
416
+
}
417
417
+
.chevron {
418
418
+
transition: transform 0.2s ease;
419
419
+
}
420
420
+
.chevron.rotated {
421
421
+
transform: rotate(180deg);
422
422
+
}
423
423
+
.dropdown-item + .dropdown-item {
424
424
+
border-top: 1px solid rgba(255, 255, 255, 0.05);
425
425
+
}
196
426
</style>