|
1 | 1 | <script> |
2 | 2 | import { |
3 | | - Apple, |
4 | | - Archive, |
5 | | - Download, |
6 | 3 | ExternalLink, |
7 | | - Package, |
8 | | - Store, |
9 | 4 | } from '@lucide/svelte' |
10 | 5 | import { spring } from 'svelte/motion' |
11 | 6 | import { onMount } from 'svelte' |
|
38 | 33 | const downloadGroups = [ |
39 | 34 | { |
40 | 35 | key: 'iosMacos', |
| 36 | + label: 'iOS / macOS', |
41 | 37 | sources: [ |
42 | | - { key: 'appStore', href: 'https://apps.apple.com/app/id1586449703', Icon: Apple }, |
43 | | - { key: 'homebrew', command: 'brew install --cask server-box', Icon: Package }, |
| 38 | + { label: 'App Store', href: 'https://apps.apple.com/app/id1586449703' }, |
| 39 | + { label: 'Homebrew Cask', command: 'brew install --cask server-box' }, |
44 | 40 | ], |
45 | 41 | }, |
46 | 42 | { |
47 | 43 | key: 'android', |
| 44 | + label: 'Android', |
48 | 45 | sources: [ |
49 | | - { key: 'github', href: 'https://github.com/lollipopkit/flutter_server_box/releases', Icon: Download }, |
50 | | - { key: 'cdn', href: 'https://cdn.lpkt.cn/serverbox/pkg/?sort=time&order=desc&layout=grid', Icon: Archive }, |
51 | | - { key: 'fdroid', href: 'https://f-droid.org/packages/tech.lolli.toolbox', Icon: Store }, |
52 | | - { key: 'openapk', href: 'https://www.openapk.net/serverbox/tech.lolli.toolbox/', Icon: Package }, |
| 46 | + { label: 'GitHub Releases', href: 'https://github.com/lollipopkit/flutter_server_box/releases' }, |
| 47 | + { label: 'CDN', href: 'https://cdn.lpkt.cn/serverbox/pkg/?sort=time&order=desc&layout=grid' }, |
| 48 | + { label: 'F-Droid', href: 'https://f-droid.org/packages/tech.lolli.toolbox' }, |
| 49 | + { label: 'OpenAPK', href: 'https://www.openapk.net/serverbox/tech.lolli.toolbox/' }, |
53 | 50 | ], |
54 | 51 | }, |
55 | 52 | { |
56 | 53 | key: 'linux', |
| 54 | + label: 'Linux', |
57 | 55 | sources: [ |
58 | | - { key: 'github', href: 'https://github.com/lollipopkit/flutter_server_box/releases', Icon: Download }, |
59 | | - { key: 'cdn', href: 'https://cdn.lpkt.cn/serverbox/pkg/?sort=time&order=desc&layout=grid', Icon: Archive }, |
| 56 | + { label: 'GitHub Releases', href: 'https://github.com/lollipopkit/flutter_server_box/releases' }, |
| 57 | + { label: 'CDN', href: 'https://cdn.lpkt.cn/serverbox/pkg/?sort=time&order=desc&layout=grid' }, |
60 | 58 | ], |
61 | 59 | }, |
62 | 60 | { |
63 | 61 | key: 'windows', |
| 62 | + label: 'Windows', |
64 | 63 | sources: [ |
65 | | - { key: 'github', href: 'https://github.com/lollipopkit/flutter_server_box/releases', Icon: Download }, |
66 | | - { key: 'cdn', href: 'https://cdn.lpkt.cn/serverbox/pkg/?sort=time&order=desc&layout=grid', Icon: Archive }, |
| 64 | + { label: 'GitHub Releases', href: 'https://github.com/lollipopkit/flutter_server_box/releases' }, |
| 65 | + { label: 'CDN', href: 'https://cdn.lpkt.cn/serverbox/pkg/?sort=time&order=desc&layout=grid' }, |
67 | 66 | ], |
68 | 67 | }, |
69 | 68 | ] |
|
136 | 135 | stackMotion.set({ x: 0, y: 0 }) |
137 | 136 | } |
138 | 137 |
|
| 138 | + function scrollToSection(event, id) { |
| 139 | + event.preventDefault() |
| 140 | + document.getElementById(id)?.scrollIntoView({ |
| 141 | + behavior: 'smooth', |
| 142 | + block: 'start', |
| 143 | + }) |
| 144 | + } |
| 145 | +
|
139 | 146 | async function copyCommand(command) { |
140 | 147 | try { |
141 | 148 | await navigator.clipboard.writeText(command) |
|
152 | 159 | {#if locale && isMounted} |
153 | 160 | <main class="site"> |
154 | 161 | <header class="site-nav" id="top"> |
155 | | - <a class="brand" href="#top">ServerBox</a> |
| 162 | + <a class="brand" href="#top" onclick={(event) => scrollToSection(event, 'top')}>ServerBox</a> |
156 | 163 | <nav> |
157 | | - <a href="#features">{$LL.nav.features()}</a> |
158 | | - <a href="#capabilities">{$LL.nav.capabilities()}</a> |
| 164 | + <a href="#features" onclick={(event) => scrollToSection(event, 'features')}>{$LL.nav.features()}</a> |
| 165 | + <a href="#capabilities" onclick={(event) => scrollToSection(event, 'capabilities')}>{$LL.nav.capabilities()}</a> |
| 166 | + <a href="#download" onclick={(event) => scrollToSection(event, 'download')}>{$LL.nav.download()}</a> |
159 | 167 | </nav> |
160 | 168 | <div class="nav-actions"> |
161 | 169 | <label class="language-switcher"> |
|
181 | 189 | {$LL.hero.subtitle()} |
182 | 190 | </p> |
183 | 191 | <div class="hero-actions"> |
184 | | - <a class="btn btn-primary" href="#download">{$LL.hero.primaryAction()}</a> |
185 | | - <a class="btn btn-secondary" href="#features">{$LL.hero.secondaryAction()}</a> |
| 192 | + <a class="btn btn-primary" href="#download" onclick={(event) => scrollToSection(event, 'download')}>{$LL.hero.primaryAction()}</a> |
| 193 | + <a class="btn btn-secondary" href="#features" onclick={(event) => scrollToSection(event, 'features')}>{$LL.hero.secondaryAction()}</a> |
186 | 194 | </div> |
187 | 195 |
|
188 | 196 | <div |
|
251 | 259 | {#each downloadGroups as group} |
252 | 260 | <article class="download-platform"> |
253 | 261 | <div class="download-platform-copy"> |
254 | | - <h3>{$LL.download.platforms[group.key].title()}</h3> |
255 | | - <p>{$LL.download.platforms[group.key].description()}</p> |
| 262 | + <h3>{group.label}</h3> |
256 | 263 | </div> |
257 | 264 | <div class="download-actions"> |
258 | 265 | {#each group.sources as source} |
259 | 266 | {#if source.command} |
260 | 267 | <button |
261 | 268 | class="download-icon-btn" |
262 | 269 | type="button" |
263 | | - aria-label={`${$LL.download.platforms[group.key].title()} ${$LL.download.sources[source.key].name()}`} |
| 270 | + aria-label={`${group.label} ${source.label}`} |
264 | 271 | onclick={() => copyCommand(source.command)} |
265 | 272 | > |
266 | | - <source.Icon size={17} strokeWidth={1.8} aria-hidden="true" /> |
267 | | - <span>{copiedCommand === source.command ? $LL.download.copied() : $LL.download.sources[source.key].name()}</span> |
| 273 | + <span>{copiedCommand === source.command ? $LL.download.copied() : source.label}</span> |
268 | 274 | </button> |
269 | 275 | {:else} |
270 | | - <a class="download-icon-btn" href={source.href} aria-label={`${$LL.download.platforms[group.key].title()} ${$LL.download.sources[source.key].name()}`}> |
271 | | - <source.Icon size={17} strokeWidth={1.8} aria-hidden="true" /> |
272 | | - <span>{$LL.download.sources[source.key].name()}</span> |
| 276 | + <a class="download-icon-btn" href={source.href} aria-label={`${group.label} ${source.label}`}> |
| 277 | + <span>{source.label}</span> |
273 | 278 | <ExternalLink size={14} strokeWidth={1.8} aria-hidden="true" /> |
274 | 279 | </a> |
275 | 280 | {/if} |
|
298 | 303 | <footer class="site-footer"> |
299 | 304 | <span>© 2026 ServerBox</span> |
300 | 305 | <div class="footer-links"> |
301 | | - <a href="#features">{$LL.footer.features()}</a> |
302 | | - <a href="#capabilities">{$LL.footer.capabilities()}</a> |
| 306 | + <a href="#features" onclick={(event) => scrollToSection(event, 'features')}>{$LL.footer.features()}</a> |
| 307 | + <a href="#capabilities" onclick={(event) => scrollToSection(event, 'capabilities')}>{$LL.footer.capabilities()}</a> |
303 | 308 | <a href="https://github.com/lollipopkit/flutter_server_box">GitHub</a> |
304 | 309 | <a href="https://github.com/lollipopkit/flutter_server_box/releases">{$LL.footer.releases()}</a> |
305 | 310 | </div> |
|
0 commit comments