prettier config + format all, closes #175

This commit is contained in:
vcoppe
2025-02-02 11:17:22 +01:00
parent 01cfd448f0
commit 0b457f9a1e
157 changed files with 17194 additions and 29365 deletions

View File

@@ -10,4 +10,4 @@ Object.keys(languages).forEach((lang) => {
init({
fallbackLocale: 'en',
initialLocale: 'en',
});
});

View File

@@ -1,28 +1,30 @@
import { guides } from '$lib/components/docs/docs.js';
function getModule(language: string | undefined, guide: string) {
language = language ?? 'en';
let subguide = undefined;
if (guide.includes('/')) {
[guide, subguide] = guide.split('/');
}
return subguide
? import(`./../lib/docs/${language}/${guide}/${subguide}.mdx`)
: import(`./../lib/docs/${language}/${guide}.mdx`);
language = language ?? 'en';
let subguide = undefined;
if (guide.includes('/')) {
[guide, subguide] = guide.split('/');
}
return subguide
? import(`./../lib/docs/${language}/${guide}/${subguide}.mdx`)
: import(`./../lib/docs/${language}/${guide}.mdx`);
}
export async function load({ params }) {
const { language } = params;
const { language } = params;
const guideTitles: Record<string, string> = {};
for (let guide of Object.keys(guides)) {
guideTitles[guide] = (await getModule(language, guide)).metadata.title;
for (let subguide of guides[guide]) {
guideTitles[`${guide}/${subguide}`] = (await getModule(language, `${guide}/${subguide}`)).metadata.title;
}
}
const guideTitles: Record<string, string> = {};
for (let guide of Object.keys(guides)) {
guideTitles[guide] = (await getModule(language, guide)).metadata.title;
for (let subguide of guides[guide]) {
guideTitles[`${guide}/${subguide}`] = (
await getModule(language, `${guide}/${subguide}`)
).metadata.title;
}
}
return {
guideTitles
};
}
return {
guideTitles
};
}

View File

@@ -1,71 +1,72 @@
<script lang="ts">
import '../app.pcss';
import { ModeWatcher } from 'mode-watcher';
import { isLoading, _, locale } from 'svelte-i18n';
import { page } from '$app/stores';
import Nav from '$lib/components/Nav.svelte';
import Footer from '$lib/components/Footer.svelte';
import { onMount } from 'svelte';
import { convertOldEmbeddingOptions } from '$lib/components/embedding/Embedding';
import { base } from '$app/paths';
import { languages } from '$lib/languages';
import { browser } from '$app/environment';
import { goto } from '$app/navigation';
import { getURLForLanguage } from '$lib/utils';
import '../app.pcss';
import { ModeWatcher } from 'mode-watcher';
import { isLoading, _, locale } from 'svelte-i18n';
import { page } from '$app/stores';
import Nav from '$lib/components/Nav.svelte';
import Footer from '$lib/components/Footer.svelte';
import { onMount } from 'svelte';
import { convertOldEmbeddingOptions } from '$lib/components/embedding/Embedding';
import { base } from '$app/paths';
import { languages } from '$lib/languages';
import { browser } from '$app/environment';
import { goto } from '$app/navigation';
import { getURLForLanguage } from '$lib/utils';
export let data: {
guideTitles: Record<string, string>;
};
export let data: {
guideTitles: Record<string, string>;
};
const appRoutes = ['/[[language]]/app', '/[[language]]/embed'];
const appRoutes = ['/[[language]]/app', '/[[language]]/embed'];
onMount(() => {
if ($page.url.searchParams.has('embed')) {
// convert old embedding options to new format and redirect to new embed page
let folders = $page.url.pathname.split('/');
let locale = folders.indexOf('l') >= 0 ? folders[folders.indexOf('l') + 1] ?? 'en' : 'en';
window.location.href = `${getURLForLanguage(locale, '/embed')}?options=${encodeURIComponent(JSON.stringify(convertOldEmbeddingOptions($page.url.searchParams)))}`;
}
});
onMount(() => {
if ($page.url.searchParams.has('embed')) {
// convert old embedding options to new format and redirect to new embed page
let folders = $page.url.pathname.split('/');
let locale =
folders.indexOf('l') >= 0 ? (folders[folders.indexOf('l') + 1] ?? 'en') : 'en';
window.location.href = `${getURLForLanguage(locale, '/embed')}?options=${encodeURIComponent(JSON.stringify(convertOldEmbeddingOptions($page.url.searchParams)))}`;
}
});
$: if ($page.route.id?.includes('[[language]]')) {
if ($page.params.language) {
let lang = $page.params.language.replace('/', '');
if ($locale !== lang) {
if (languages.hasOwnProperty(lang)) {
$locale = lang;
} else if (browser) {
goto(`${base}/404`);
}
}
} else if ($locale !== 'en') {
$locale = 'en';
}
}
$: if ($page.route.id?.includes('[[language]]')) {
if ($page.params.language) {
let lang = $page.params.language.replace('/', '');
if ($locale !== lang) {
if (languages.hasOwnProperty(lang)) {
$locale = lang;
} else if (browser) {
goto(`${base}/404`);
}
}
} else if ($locale !== 'en') {
$locale = 'en';
}
}
$: if (browser && !$isLoading && $locale) {
let title = `gpx.studio — ${$_(`metadata.${$page.route.id?.replace('/[[language]]', '').split('/')[1] ?? 'home'}_title`)}`;
if ($page.params.guide) {
document.title = `${title} | ${data.guideTitles[$page.params.guide]}`;
} else {
document.title = title;
}
}
$: showNavAndFooter = $page.route.id === null || !appRoutes.includes($page.route.id);
$: if (browser && !$isLoading && $locale) {
let title = `gpx.studio — ${$_(`metadata.${$page.route.id?.replace('/[[language]]', '').split('/')[1] ?? 'home'}_title`)}`;
if ($page.params.guide) {
document.title = `${title} | ${data.guideTitles[$page.params.guide]}`;
} else {
document.title = title;
}
}
$: showNavAndFooter = $page.route.id === null || !appRoutes.includes($page.route.id);
</script>
<ModeWatcher />
<div class="flex flex-col min-h-screen">
{#if !$isLoading}
{#if showNavAndFooter}
<Nav />
{/if}
<main class="grow flex flex-col">
<slot />
</main>
{#if showNavAndFooter}
<Footer />
{/if}
{/if}
{#if !$isLoading}
{#if showNavAndFooter}
<Nav />
{/if}
<main class="grow flex flex-col">
<slot />
</main>
{#if showNavAndFooter}
<Footer />
{/if}
{/if}
</div>

View File

@@ -1,33 +1,36 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button';
import Logo from '$lib/components/Logo.svelte';
import { getURLForLanguage } from '$lib/utils';
import { Home, Map, BookOpenText } from 'lucide-svelte';
import { _, locale } from 'svelte-i18n';
import { Button } from '$lib/components/ui/button';
import Logo from '$lib/components/Logo.svelte';
import { getURLForLanguage } from '$lib/utils';
import { Home, Map, BookOpenText } from 'lucide-svelte';
import { _, locale } from 'svelte-i18n';
</script>
<div class="grow px-12 my-12 flex flex-col items-center justify-center gap-6">
<p class="text-7xl font-black">404</p>
<p class="text-xl -mt-6">{$_('page_not_found')}</p>
<Logo class="h-40 my-3 animate-spin" style="animation-duration: 20000ms" iconOnly={true} />
<div class="w-full flex flex-row flex-wrap gap-3 justify-center">
<Button href={getURLForLanguage($locale, '/')} class="text-base w-1/4 min-w-fit rounded-full">
<Home size="18" class="mr-1.5" />
{$_('homepage.home')}
</Button>
<Button
href={getURLForLanguage($locale, '/app')}
class="text-base w-1/4 min-w-fit rounded-full"
>
<Map size="18" class="mr-1.5" />
{$_('homepage.app')}
</Button>
<Button
href={getURLForLanguage($locale, '/help')}
class="text-base w-1/4 min-w-fit rounded-full"
>
<BookOpenText size="18" class="mr-1.5" />
<span>{$_('menu.help')}</span>
</Button>
</div>
<p class="text-7xl font-black">404</p>
<p class="text-xl -mt-6">{$_('page_not_found')}</p>
<Logo class="h-40 my-3 animate-spin" style="animation-duration: 20000ms" iconOnly={true} />
<div class="w-full flex flex-row flex-wrap gap-3 justify-center">
<Button
href={getURLForLanguage($locale, '/')}
class="text-base w-1/4 min-w-fit rounded-full"
>
<Home size="18" class="mr-1.5" />
{$_('homepage.home')}
</Button>
<Button
href={getURLForLanguage($locale, '/app')}
class="text-base w-1/4 min-w-fit rounded-full"
>
<Map size="18" class="mr-1.5" />
{$_('homepage.app')}
</Button>
<Button
href={getURLForLanguage($locale, '/help')}
class="text-base w-1/4 min-w-fit rounded-full"
>
<BookOpenText size="18" class="mr-1.5" />
<span>{$_('menu.help')}</span>
</Button>
</div>
</div>

View File

@@ -1,264 +1,281 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button';
import DocsContainer from '$lib/components/docs/DocsContainer.svelte';
import Logo from '$lib/components/Logo.svelte';
import ElevationProfile from '$lib/components/ElevationProfile.svelte';
import GPXStatistics from '$lib/components/GPXStatistics.svelte';
import Routing from '$lib/components/toolbar/tools/routing/Routing.svelte';
import {
BookOpenText,
Heart,
ChartArea,
Map,
PencilRuler,
PenLine,
Route,
Scale
} from 'lucide-svelte';
import { _, locale } from 'svelte-i18n';
import { getURLForLanguage } from '$lib/utils';
import { exampleGPXFile } from '$lib/assets/example';
import { writable } from 'svelte/store';
import Toolbar from '$lib/components/toolbar/Toolbar.svelte';
import { currentTool, Tool } from '$lib/stores';
import { onDestroy, onMount } from 'svelte';
import { Button } from '$lib/components/ui/button';
import DocsContainer from '$lib/components/docs/DocsContainer.svelte';
import Logo from '$lib/components/Logo.svelte';
import ElevationProfile from '$lib/components/ElevationProfile.svelte';
import GPXStatistics from '$lib/components/GPXStatistics.svelte';
import Routing from '$lib/components/toolbar/tools/routing/Routing.svelte';
import {
BookOpenText,
Heart,
ChartArea,
Map,
PencilRuler,
PenLine,
Route,
Scale,
} from 'lucide-svelte';
import { _, locale } from 'svelte-i18n';
import { getURLForLanguage } from '$lib/utils';
import { exampleGPXFile } from '$lib/assets/example';
import { writable } from 'svelte/store';
import Toolbar from '$lib/components/toolbar/Toolbar.svelte';
import { currentTool, Tool } from '$lib/stores';
import { onDestroy, onMount } from 'svelte';
export let data: {
fundingModule: any;
translationModule: any;
mapboxModule: any;
};
export let data: {
fundingModule: any;
translationModule: any;
mapboxModule: any;
};
let gpxStatistics = writable(exampleGPXFile.getStatistics());
let slicedGPXStatistics = writable(undefined);
let additionalDatasets = writable(['speed', 'atemp']);
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
let gpxStatistics = writable(exampleGPXFile.getStatistics());
let slicedGPXStatistics = writable(undefined);
let additionalDatasets = writable(['speed', 'atemp']);
let elevationFill = writable<'slope' | 'surface' | undefined>(undefined);
onMount(() => {
$currentTool = Tool.SCISSORS;
});
onMount(() => {
$currentTool = Tool.SCISSORS;
});
$: $currentTool, ($currentTool = Tool.SCISSORS);
$: $currentTool, ($currentTool = Tool.SCISSORS);
onDestroy(() => {
$currentTool = null;
});
onDestroy(() => {
$currentTool = null;
});
</script>
<div class="space-y-24 my-24">
<div class="-mt-12 sm:mt-0">
<div class="px-12 w-full flex flex-col items-center">
<div class="flex flex-col gap-6 items-center max-w-3xl">
<h1 class="text-4xl sm:text-6xl font-black text-center">{$_('metadata.home_title')}</h1>
<div class="text-lg sm:text-xl text-muted-foreground text-center">
{$_('metadata.description')}
</div>
<div class="w-full flex flex-row justify-center gap-3">
<Button href={getURLForLanguage($locale, '/app')} class="w-1/3 min-w-fit">
<Map size="18" class="mr-1.5" />
{$_('homepage.app')}
</Button>
<Button
variant="secondary"
href={getURLForLanguage($locale, '/help')}
class="w-1/3 min-w-fit"
>
<BookOpenText size="18" class="mr-1.5" />
<span>{$_('menu.help')}</span>
</Button>
</div>
</div>
</div>
<div class="relative overflow-hidden">
<enhanced:img
src="/src/lib/assets/img/home/routing.png"
alt="Screenshot of the gpx.studio map in 3D."
class="w-full min-w-[1200px] ml-[20%] -translate-x-[20%]"
/>
<div
class="absolute top-0 left-0 w-full h-full bg-gradient-to-b from-background via-transparent to-background"
/>
</div>
</div>
<div class="px-12 sm:px-24 w-full flex flex-col items-center">
<div class="flex flex-col md:flex-row gap-x-12 gap-y-6 items-center justify-between max-w-5xl">
<div class="markdown text-center">
<h1>
<Route size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.route_planning')}
</h1>
<p class="text-muted-foreground">{$_('homepage.route_planning_description')}</p>
</div>
<div class="p-3 w-fit rounded-md border shadow-xl md:shrink-0">
<Routing minimizable={false} />
</div>
</div>
</div>
<div class="px-12 sm:px-24 w-full flex flex-col items-center">
<div class="flex flex-col md:flex-row gap-x-12 gap-y-6 items-center justify-between max-w-5xl">
<div class="markdown text-center md:hidden">
<h1>
<PencilRuler size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.file_processing')}
</h1>
<p class="text-muted-foreground">{$_('homepage.file_processing_description')}</p>
</div>
<div class="relative md:shrink-0 max-w-[400px]">
<Toolbar />
</div>
<div class="markdown text-center hidden md:block">
<h1>
<PencilRuler size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.file_processing')}
</h1>
<p class="text-muted-foreground">{$_('homepage.file_processing_description')}</p>
</div>
</div>
</div>
<div class="px-12 sm:px-24 w-full flex flex-col items-center">
<div
class="markdown flex flex-col md:flex-row gap-x-12 gap-y-6 items-center justify-between max-w-5xl"
>
<div class="markdown text-center">
<h1>
<Map size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.maps')}
</h1>
<p class="text-muted-foreground">{$_('homepage.maps_description')}</p>
</div>
<div
class="relative h-60 xs:h-80 aspect-square rounded-2xl shadow-xl overflow-hidden overflow-clip"
>
<enhanced:img
src="/src/lib/assets/img/home/mapbox-outdoors.png"
alt="Mapbox Outdoors map screenshot."
class="absolute"
style="clip-path: inset(0 50% 50% 0);"
/>
<enhanced:img
src="/src/lib/assets/img/home/mapbox-satellite.png"
alt="Mapbox Satellite map screenshot."
class="absolute"
style="clip-path: inset(0 0 50% 50%);"
/>
<enhanced:img
src="/src/lib/assets/img/home/ign.png"
alt="IGN map screenshot."
class="absolute"
style="clip-path: inset(50% 50% 0 0);"
/>
<enhanced:img
src="/src/lib/assets/img/home/cyclosm.png"
alt="CyclOSM map screenshot."
class="absolute"
style="clip-path: inset(50% 0 0 50%);"
/>
<enhanced:img
src="/src/lib/assets/img/home/waymarked.png"
alt="Waymarked Trails map screenshot."
class="absolute"
/>
</div>
</div>
</div>
<div class="px-8 md:px-12">
<div class="markdown text-center px-4 md:px-12">
<h1>
<ChartArea size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.data_visualization')}
</h1>
<p class="text-muted-foreground mb-6">{$_('homepage.data_visualization_description')}</p>
</div>
<div class="h-48 w-full">
<ElevationProfile
{gpxStatistics}
{slicedGPXStatistics}
additionalDatasets={$additionalDatasets}
elevationFill={$elevationFill}
/>
</div>
<div class="flex flex-col items-center">
<div class="h-10 w-fit">
<GPXStatistics
{gpxStatistics}
{slicedGPXStatistics}
panelSize={192}
orientation={'horizontal'}
/>
</div>
</div>
</div>
<div class="px-12 sm:px-24 w-full flex flex-col items-center">
<div class="flex flex-col md:flex-row gap-x-12 gap-y-6 items-center justify-between max-w-5xl">
<div class="markdown text-center md:hidden">
<h1>
<Scale size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.identity')}
</h1>
<p class="text-muted-foreground">{$_('homepage.identity_description')}</p>
</div>
<a href="https://github.com/gpxstudio/gpx.studio" target="_blank">
<Logo class="h-32" company="github" />
</a>
<div class="markdown text-center hidden md:block">
<h1>
<Scale size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.identity')}
</h1>
<p class="text-muted-foreground">{$_('homepage.identity_description')}</p>
</div>
</div>
</div>
<div class="px-12 w-full">
<div class="w-full max-w-7xl mx-auto rounded-2xl shadow-xl overflow-hidden overflow-clip">
<enhanced:img
src="/src/lib/assets/img/home/map.png"
alt="Screenshot of the gpx.studio map in 3D."
class="min-w-[800px] ml-[15%] -translate-x-[15%]"
/>
</div>
</div>
<div
class="px-12 md:px-24 flex flex-row flex-wrap lg:flex-nowrap items-center justify-center -space-y-0.5 lg:-space-x-0.5"
>
<div
class="grow max-w-xl flex flex-col items-center gap-6 p-8 border rounded-2xl shadow-xl -rotate-1 lg:rotate-1"
>
{#await data.fundingModule then fundingModule}
<DocsContainer module={fundingModule.default} />
{/await}
<Button href="https://ko-fi.com/gpxstudio" target="_blank" class="text-base">
<Heart size="16" class="mr-1" fill="rgb(var(--support))" color="rgb(var(--support))" />
<span>{$_('homepage.support_button')}</span>
</Button>
</div>
<div
class="grow max-w-lg mx-6 h-fit bg-background flex flex-col items-center gap-6 p-8 border rounded-2xl shadow-xl rotate-1 lg:-rotate-1"
>
{#await data.translationModule then translationModule}
<DocsContainer module={translationModule.default} />
{/await}
<Button href="https://crowdin.com/project/gpxstudio" target="_blank" class="text-base">
<PenLine size="16" class="mr-1" />
<span>{$_('homepage.contribute')}</span>
</Button>
</div>
</div>
<div class="px-12 md:px-24 flex flex-col items-center">
<div
class="max-w-4xl flex flex-col lg:flex-row items-center justify-center gap-x-12 gap-y-6 p-6 border rounded-2xl shadow-xl bg-secondary"
>
<div class="shrink-0 flex flex-col sm:flex-row lg:flex-col items-center gap-x-4 gap-y-2">
<div class="text-lg font-semibold text-muted-foreground">
❤️ {$_('homepage.supported_by')}
</div>
<a href="https://www.mapbox.com/" target="_blank">
<Logo company="mapbox" class="w-60" />
</a>
</div>
{#await data.mapboxModule then mapboxModule}
<DocsContainer module={mapboxModule.default} />
{/await}
</div>
</div>
<div class="-mt-12 sm:mt-0">
<div class="px-12 w-full flex flex-col items-center">
<div class="flex flex-col gap-6 items-center max-w-3xl">
<h1 class="text-4xl sm:text-6xl font-black text-center">
{$_('metadata.home_title')}
</h1>
<div class="text-lg sm:text-xl text-muted-foreground text-center">
{$_('metadata.description')}
</div>
<div class="w-full flex flex-row justify-center gap-3">
<Button href={getURLForLanguage($locale, '/app')} class="w-1/3 min-w-fit">
<Map size="18" class="mr-1.5" />
{$_('homepage.app')}
</Button>
<Button
variant="secondary"
href={getURLForLanguage($locale, '/help')}
class="w-1/3 min-w-fit"
>
<BookOpenText size="18" class="mr-1.5" />
<span>{$_('menu.help')}</span>
</Button>
</div>
</div>
</div>
<div class="relative overflow-hidden">
<enhanced:img
src="/src/lib/assets/img/home/routing.png"
alt="Screenshot of the gpx.studio map in 3D."
class="w-full min-w-[1200px] ml-[20%] -translate-x-[20%]"
/>
<div
class="absolute top-0 left-0 w-full h-full bg-gradient-to-b from-background via-transparent to-background"
/>
</div>
</div>
<div class="px-12 sm:px-24 w-full flex flex-col items-center">
<div
class="flex flex-col md:flex-row gap-x-12 gap-y-6 items-center justify-between max-w-5xl"
>
<div class="markdown text-center">
<h1>
<Route size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.route_planning')}
</h1>
<p class="text-muted-foreground">{$_('homepage.route_planning_description')}</p>
</div>
<div class="p-3 w-fit rounded-md border shadow-xl md:shrink-0">
<Routing minimizable={false} />
</div>
</div>
</div>
<div class="px-12 sm:px-24 w-full flex flex-col items-center">
<div
class="flex flex-col md:flex-row gap-x-12 gap-y-6 items-center justify-between max-w-5xl"
>
<div class="markdown text-center md:hidden">
<h1>
<PencilRuler size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.file_processing')}
</h1>
<p class="text-muted-foreground">{$_('homepage.file_processing_description')}</p>
</div>
<div class="relative md:shrink-0 max-w-[400px]">
<Toolbar />
</div>
<div class="markdown text-center hidden md:block">
<h1>
<PencilRuler size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.file_processing')}
</h1>
<p class="text-muted-foreground">{$_('homepage.file_processing_description')}</p>
</div>
</div>
</div>
<div class="px-12 sm:px-24 w-full flex flex-col items-center">
<div
class="markdown flex flex-col md:flex-row gap-x-12 gap-y-6 items-center justify-between max-w-5xl"
>
<div class="markdown text-center">
<h1>
<Map size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.maps')}
</h1>
<p class="text-muted-foreground">{$_('homepage.maps_description')}</p>
</div>
<div
class="relative h-60 xs:h-80 aspect-square rounded-2xl shadow-xl overflow-hidden overflow-clip"
>
<enhanced:img
src="/src/lib/assets/img/home/mapbox-outdoors.png"
alt="Mapbox Outdoors map screenshot."
class="absolute"
style="clip-path: inset(0 50% 50% 0);"
/>
<enhanced:img
src="/src/lib/assets/img/home/mapbox-satellite.png"
alt="Mapbox Satellite map screenshot."
class="absolute"
style="clip-path: inset(0 0 50% 50%);"
/>
<enhanced:img
src="/src/lib/assets/img/home/ign.png"
alt="IGN map screenshot."
class="absolute"
style="clip-path: inset(50% 50% 0 0);"
/>
<enhanced:img
src="/src/lib/assets/img/home/cyclosm.png"
alt="CyclOSM map screenshot."
class="absolute"
style="clip-path: inset(50% 0 0 50%);"
/>
<enhanced:img
src="/src/lib/assets/img/home/waymarked.png"
alt="Waymarked Trails map screenshot."
class="absolute"
/>
</div>
</div>
</div>
<div class="px-8 md:px-12">
<div class="markdown text-center px-4 md:px-12">
<h1>
<ChartArea size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.data_visualization')}
</h1>
<p class="text-muted-foreground mb-6">
{$_('homepage.data_visualization_description')}
</p>
</div>
<div class="h-48 w-full">
<ElevationProfile
{gpxStatistics}
{slicedGPXStatistics}
additionalDatasets={$additionalDatasets}
elevationFill={$elevationFill}
/>
</div>
<div class="flex flex-col items-center">
<div class="h-10 w-fit">
<GPXStatistics
{gpxStatistics}
{slicedGPXStatistics}
panelSize={192}
orientation={'horizontal'}
/>
</div>
</div>
</div>
<div class="px-12 sm:px-24 w-full flex flex-col items-center">
<div
class="flex flex-col md:flex-row gap-x-12 gap-y-6 items-center justify-between max-w-5xl"
>
<div class="markdown text-center md:hidden">
<h1>
<Scale size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.identity')}
</h1>
<p class="text-muted-foreground">{$_('homepage.identity_description')}</p>
</div>
<a href="https://github.com/gpxstudio/gpx.studio" target="_blank">
<Logo class="h-32" company="github" />
</a>
<div class="markdown text-center hidden md:block">
<h1>
<Scale size="24" class="mr-1 inline-block align-baseline" />
{$_('homepage.identity')}
</h1>
<p class="text-muted-foreground">{$_('homepage.identity_description')}</p>
</div>
</div>
</div>
<div class="px-12 w-full">
<div class="w-full max-w-7xl mx-auto rounded-2xl shadow-xl overflow-hidden overflow-clip">
<enhanced:img
src="/src/lib/assets/img/home/map.png"
alt="Screenshot of the gpx.studio map in 3D."
class="min-w-[800px] ml-[15%] -translate-x-[15%]"
/>
</div>
</div>
<div
class="px-12 md:px-24 flex flex-row flex-wrap lg:flex-nowrap items-center justify-center -space-y-0.5 lg:-space-x-0.5"
>
<div
class="grow max-w-xl flex flex-col items-center gap-6 p-8 border rounded-2xl shadow-xl -rotate-1 lg:rotate-1"
>
{#await data.fundingModule then fundingModule}
<DocsContainer module={fundingModule.default} />
{/await}
<Button href="https://ko-fi.com/gpxstudio" target="_blank" class="text-base">
<Heart
size="16"
class="mr-1"
fill="rgb(var(--support))"
color="rgb(var(--support))"
/>
<span>{$_('homepage.support_button')}</span>
</Button>
</div>
<div
class="grow max-w-lg mx-6 h-fit bg-background flex flex-col items-center gap-6 p-8 border rounded-2xl shadow-xl rotate-1 lg:-rotate-1"
>
{#await data.translationModule then translationModule}
<DocsContainer module={translationModule.default} />
{/await}
<Button href="https://crowdin.com/project/gpxstudio" target="_blank" class="text-base">
<PenLine size="16" class="mr-1" />
<span>{$_('homepage.contribute')}</span>
</Button>
</div>
</div>
<div class="px-12 md:px-24 flex flex-col items-center">
<div
class="max-w-4xl flex flex-col lg:flex-row items-center justify-center gap-x-12 gap-y-6 p-6 border rounded-2xl shadow-xl bg-secondary"
>
<div
class="shrink-0 flex flex-col sm:flex-row lg:flex-col items-center gap-x-4 gap-y-2"
>
<div class="text-lg font-semibold text-muted-foreground">
❤️ {$_('homepage.supported_by')}
</div>
<a href="https://www.mapbox.com/" target="_blank">
<Logo company="mapbox" class="w-60" />
</a>
</div>
{#await data.mapboxModule then mapboxModule}
<DocsContainer module={mapboxModule.default} />
{/await}
</div>
</div>
</div>

View File

@@ -11,4 +11,4 @@ export async function load({ params }) {
translationModule: getModule(language, 'translation'),
mapboxModule: getModule(language, 'mapbox'),
};
}
}

View File

@@ -1,155 +1,160 @@
<script lang="ts">
import GPXLayers from '$lib/components/gpx-layer/GPXLayers.svelte';
import ElevationProfile from '$lib/components/ElevationProfile.svelte';
import FileList from '$lib/components/file-list/FileList.svelte';
import GPXStatistics from '$lib/components/GPXStatistics.svelte';
import Map from '$lib/components/Map.svelte';
import Menu from '$lib/components/Menu.svelte';
import Toolbar from '$lib/components/toolbar/Toolbar.svelte';
import StreetViewControl from '$lib/components/street-view-control/StreetViewControl.svelte';
import LayerControl from '$lib/components/layer-control/LayerControl.svelte';
import CoordinatesPopup from '$lib/components/CoordinatesPopup.svelte';
import Resizer from '$lib/components/Resizer.svelte';
import { Toaster } from '$lib/components/ui/sonner';
import { observeFilesFromDatabase, settings } from '$lib/db';
import { gpxStatistics, loadFiles, slicedGPXStatistics } from '$lib/stores';
import { onMount } from 'svelte';
import { page } from '$app/stores';
import { languages } from '$lib/languages';
import { getURLForLanguage } from '$lib/utils';
import { getURLForGoogleDriveFile } from '$lib/components/embedding/Embedding';
import { _ } from 'svelte-i18n';
import GPXLayers from '$lib/components/gpx-layer/GPXLayers.svelte';
import ElevationProfile from '$lib/components/ElevationProfile.svelte';
import FileList from '$lib/components/file-list/FileList.svelte';
import GPXStatistics from '$lib/components/GPXStatistics.svelte';
import Map from '$lib/components/Map.svelte';
import Menu from '$lib/components/Menu.svelte';
import Toolbar from '$lib/components/toolbar/Toolbar.svelte';
import StreetViewControl from '$lib/components/street-view-control/StreetViewControl.svelte';
import LayerControl from '$lib/components/layer-control/LayerControl.svelte';
import CoordinatesPopup from '$lib/components/CoordinatesPopup.svelte';
import Resizer from '$lib/components/Resizer.svelte';
import { Toaster } from '$lib/components/ui/sonner';
import { observeFilesFromDatabase, settings } from '$lib/db';
import { gpxStatistics, loadFiles, slicedGPXStatistics } from '$lib/stores';
import { onMount } from 'svelte';
import { page } from '$app/stores';
import { languages } from '$lib/languages';
import { getURLForLanguage } from '$lib/utils';
import { getURLForGoogleDriveFile } from '$lib/components/embedding/Embedding';
import { _ } from 'svelte-i18n';
const {
treeFileView,
elevationProfile,
bottomPanelSize,
rightPanelSize,
additionalDatasets,
elevationFill
} = settings;
const {
treeFileView,
elevationProfile,
bottomPanelSize,
rightPanelSize,
additionalDatasets,
elevationFill,
} = settings;
onMount(() => {
let files: string[] = JSON.parse($page.url.searchParams.get('files') || '[]');
let ids: string[] = JSON.parse($page.url.searchParams.get('ids') || '[]');
let urls: string[] = files.concat(ids.map(getURLForGoogleDriveFile));
onMount(() => {
let files: string[] = JSON.parse($page.url.searchParams.get('files') || '[]');
let ids: string[] = JSON.parse($page.url.searchParams.get('ids') || '[]');
let urls: string[] = files.concat(ids.map(getURLForGoogleDriveFile));
observeFilesFromDatabase(urls.length === 0);
observeFilesFromDatabase(urls.length === 0);
if (urls.length > 0) {
let downloads: Promise<File | null>[] = [];
urls.forEach((url) => {
downloads.push(
fetch(url)
.then((response) => response.blob())
.then((blob) => new File([blob], url.split('/').pop()))
);
});
if (urls.length > 0) {
let downloads: Promise<File | null>[] = [];
urls.forEach((url) => {
downloads.push(
fetch(url)
.then((response) => response.blob())
.then((blob) => new File([blob], url.split('/').pop()))
);
});
Promise.all(downloads).then((files) => {
files = files.filter((file) => file !== null);
loadFiles(files);
});
}
});
Promise.all(downloads).then((files) => {
files = files.filter((file) => file !== null);
loadFiles(files);
});
}
});
</script>
<div class="fixed -z-10 text-transparent">
<h1>{$_('metadata.home_title')}{$_('metadata.app_title')}</h1>
<p>{$_('metadata.description')}</p>
<h2>{$_('toolbar.routing.tooltip')}</h2>
<p>{$_('toolbar.routing.help_no_file')}</p>
<p>{$_('toolbar.routing.help')}</p>
<h3>{$_('toolbar.routing.reverse.button')}</h3>
<p>{$_('toolbar.routing.reverse.tooltip')}</p>
<h3>{$_('toolbar.routing.route_back_to_start.button')}</h3>
<p>{$_('toolbar.routing.route_back_to_start.tooltip')}</p>
<h3>{$_('toolbar.routing.round_trip.button')}</h3>
<p>{$_('toolbar.routing.round_trip.tooltip')}</p>
<h3>{$_('toolbar.routing.start_loop_here')}</h3>
<h2>{$_('toolbar.scissors.tooltip')}</h2>
<p>{$_('toolbar.scissors.help')}</p>
<h2>{$_('toolbar.time.tooltip')}</h2>
<p>{$_('toolbar.time.help')}</p>
<h2>{$_('toolbar.merge.tooltip')}</h2>
<h3>{$_('toolbar.merge.merge_traces')}</h3>
<p>{$_('toolbar.merge.help_merge_traces')}</p>
<h3>{$_('toolbar.merge.merge_contents')}</h3>
<p>{$_('toolbar.merge.help_merge_contents')}</p>
<h2>{$_('toolbar.elevation.button')}</h2>
<p>{$_('toolbar.elevation.help')}</p>
<h2>{$_('toolbar.waypoint.tooltip')}</h2>
<p>{$_('toolbar.waypoint.help')}</p>
<h2>{$_('toolbar.reduce.tooltip')}</h2>
<p>{$_('toolbar.reduce.help')}</p>
<h2>{$_('toolbar.clean.tooltip')}</h2>
<p>{$_('toolbar.clean.help')}</p>
<h2>{$_('gpx.files')}, {$_('gpx.tracks')}, {$_('gpx.segments')}, {$_('gpx.waypoints')}</h2>
<h1>{$_('metadata.home_title')}{$_('metadata.app_title')}</h1>
<p>{$_('metadata.description')}</p>
<h2>{$_('toolbar.routing.tooltip')}</h2>
<p>{$_('toolbar.routing.help_no_file')}</p>
<p>{$_('toolbar.routing.help')}</p>
<h3>{$_('toolbar.routing.reverse.button')}</h3>
<p>{$_('toolbar.routing.reverse.tooltip')}</p>
<h3>{$_('toolbar.routing.route_back_to_start.button')}</h3>
<p>{$_('toolbar.routing.route_back_to_start.tooltip')}</p>
<h3>{$_('toolbar.routing.round_trip.button')}</h3>
<p>{$_('toolbar.routing.round_trip.tooltip')}</p>
<h3>{$_('toolbar.routing.start_loop_here')}</h3>
<h2>{$_('toolbar.scissors.tooltip')}</h2>
<p>{$_('toolbar.scissors.help')}</p>
<h2>{$_('toolbar.time.tooltip')}</h2>
<p>{$_('toolbar.time.help')}</p>
<h2>{$_('toolbar.merge.tooltip')}</h2>
<h3>{$_('toolbar.merge.merge_traces')}</h3>
<p>{$_('toolbar.merge.help_merge_traces')}</p>
<h3>{$_('toolbar.merge.merge_contents')}</h3>
<p>{$_('toolbar.merge.help_merge_contents')}</p>
<h2>{$_('toolbar.elevation.button')}</h2>
<p>{$_('toolbar.elevation.help')}</p>
<h2>{$_('toolbar.waypoint.tooltip')}</h2>
<p>{$_('toolbar.waypoint.help')}</p>
<h2>{$_('toolbar.reduce.tooltip')}</h2>
<p>{$_('toolbar.reduce.help')}</p>
<h2>{$_('toolbar.clean.tooltip')}</h2>
<p>{$_('toolbar.clean.help')}</p>
<h2>{$_('gpx.files')}, {$_('gpx.tracks')}, {$_('gpx.segments')}, {$_('gpx.waypoints')}</h2>
</div>
<div class="fixed flex flex-row w-screen h-screen supports-dvh:h-dvh">
<div class="flex flex-col grow h-full min-w-0">
<div class="grow relative">
<Menu />
<div
class="absolute top-0 bottom-0 left-0 z-20 flex flex-col justify-center pointer-events-none"
>
<Toolbar />
</div>
<Map class="h-full {$treeFileView ? '' : 'horizontal'}" />
<StreetViewControl />
<LayerControl />
<GPXLayers />
<CoordinatesPopup />
<Toaster richColors />
{#if !$treeFileView}
<div class="h-10 -translate-y-10 w-full pointer-events-none absolute z-30">
<FileList orientation="horizontal" />
</div>
{/if}
</div>
{#if $elevationProfile}
<Resizer orientation="row" bind:after={$bottomPanelSize} minAfter={100} maxAfter={300} />
{/if}
<div
class="{$elevationProfile ? '' : 'h-10'} flex flex-row gap-2 px-2 sm:px-4"
style={$elevationProfile ? `height: ${$bottomPanelSize}px` : ''}
>
<GPXStatistics
{gpxStatistics}
{slicedGPXStatistics}
panelSize={$bottomPanelSize}
orientation={$elevationProfile ? 'vertical' : 'horizontal'}
/>
{#if $elevationProfile}
<ElevationProfile
{gpxStatistics}
{slicedGPXStatistics}
bind:additionalDatasets={$additionalDatasets}
bind:elevationFill={$elevationFill}
/>
{/if}
</div>
</div>
{#if $treeFileView}
<Resizer orientation="col" bind:after={$rightPanelSize} minAfter={100} maxAfter={400} />
<FileList orientation="vertical" recursive={true} style="width: {$rightPanelSize}px" />
{/if}
<div class="flex flex-col grow h-full min-w-0">
<div class="grow relative">
<Menu />
<div
class="absolute top-0 bottom-0 left-0 z-20 flex flex-col justify-center pointer-events-none"
>
<Toolbar />
</div>
<Map class="h-full {$treeFileView ? '' : 'horizontal'}" />
<StreetViewControl />
<LayerControl />
<GPXLayers />
<CoordinatesPopup />
<Toaster richColors />
{#if !$treeFileView}
<div class="h-10 -translate-y-10 w-full pointer-events-none absolute z-30">
<FileList orientation="horizontal" />
</div>
{/if}
</div>
{#if $elevationProfile}
<Resizer
orientation="row"
bind:after={$bottomPanelSize}
minAfter={100}
maxAfter={300}
/>
{/if}
<div
class="{$elevationProfile ? '' : 'h-10'} flex flex-row gap-2 px-2 sm:px-4"
style={$elevationProfile ? `height: ${$bottomPanelSize}px` : ''}
>
<GPXStatistics
{gpxStatistics}
{slicedGPXStatistics}
panelSize={$bottomPanelSize}
orientation={$elevationProfile ? 'vertical' : 'horizontal'}
/>
{#if $elevationProfile}
<ElevationProfile
{gpxStatistics}
{slicedGPXStatistics}
bind:additionalDatasets={$additionalDatasets}
bind:elevationFill={$elevationFill}
/>
{/if}
</div>
</div>
{#if $treeFileView}
<Resizer orientation="col" bind:after={$rightPanelSize} minAfter={100} maxAfter={400} />
<FileList orientation="vertical" recursive={true} style="width: {$rightPanelSize}px" />
{/if}
</div>
<!-- hidden links for svelte crawling -->
<div class="hidden">
{#each Object.entries(languages) as [lang, label]}
<a href={getURLForLanguage(lang, '/embed')}>
{label}
</a>
{/each}
{#each Object.entries(languages) as [lang, label]}
<a href={getURLForLanguage(lang, '/embed')}>
{label}
</a>
{/each}
</div>
<style lang="postcss">
div :global(.toaster.group) {
@apply absolute;
@apply right-2;
--offset: 50px !important;
}
div :global(.toaster.group) {
@apply absolute;
@apply right-2;
--offset: 50px !important;
}
</style>

View File

@@ -1,27 +1,27 @@
<script lang="ts">
import { page } from '$app/stores';
import { onMount } from 'svelte';
import Embedding from '$lib/components/embedding/Embedding.svelte';
import {
getMergedEmbeddingOptions,
type EmbeddingOptions
} from '$lib/components/embedding/Embedding';
import { page } from '$app/stores';
import { onMount } from 'svelte';
import Embedding from '$lib/components/embedding/Embedding.svelte';
import {
getMergedEmbeddingOptions,
type EmbeddingOptions,
} from '$lib/components/embedding/Embedding';
let embeddingOptions: EmbeddingOptions | undefined = undefined;
let embeddingOptions: EmbeddingOptions | undefined = undefined;
onMount(() => {
let options = $page.url.searchParams.get('options');
if (options === null) {
return;
}
options = JSON.parse(options);
if (options === null) {
return;
}
embeddingOptions = getMergedEmbeddingOptions(options);
});
onMount(() => {
let options = $page.url.searchParams.get('options');
if (options === null) {
return;
}
options = JSON.parse(options);
if (options === null) {
return;
}
embeddingOptions = getMergedEmbeddingOptions(options);
});
</script>
{#if embeddingOptions}
<Embedding options={embeddingOptions} hash={$page.url.hash} />
<Embedding options={embeddingOptions} hash={$page.url.hash} />
{/if}

View File

@@ -1,44 +1,46 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button';
import { getURLForLanguage } from '$lib/utils';
import { locale } from 'svelte-i18n';
import { page } from '$app/stores';
import { guides } from '$lib/components/docs/docs';
import { Button } from '$lib/components/ui/button';
import { getURLForLanguage } from '$lib/utils';
import { locale } from 'svelte-i18n';
import { page } from '$app/stores';
import { guides } from '$lib/components/docs/docs';
export let data: {
guideTitles: Record<string, string>;
};
export let data: {
guideTitles: Record<string, string>;
};
</script>
<div class="grow px-12 pt-6 pb-12 flex flex-row gap-24">
<div class="hidden md:flex flex-col gap-2 w-40 sticky mt-[27px] top-[108px] self-start shrink-0">
{#each Object.keys(guides) as guide}
<Button
variant="link"
href={getURLForLanguage($locale, `/help/${guide}`)}
class="min-h-5 h-fit p-0 w-fit text-muted-foreground hover:text-foreground hover:no-underline font-normal hover:font-semibold items-start whitespace-normal {$page
.params.guide === guide
? 'font-semibold text-foreground'
: ''}"
>
{data.guideTitles[guide]}
</Button>
{#each guides[guide] as subGuide}
<Button
variant="link"
href={getURLForLanguage($locale, `/help/${guide}/${subGuide}`)}
class="min-h-5 h-fit p-0 w-fit text-muted-foreground hover:text-foreground hover:no-underline font-normal hover:font-semibold items-start whitespace-normal ml-3 {$page
.params.guide ===
guide + '/' + subGuide
? 'font-semibold text-foreground'
: ''}"
>
{data.guideTitles[`${guide}/${subGuide}`]}
</Button>
{/each}
{/each}
</div>
<div class="grow">
<slot />
</div>
<div
class="hidden md:flex flex-col gap-2 w-40 sticky mt-[27px] top-[108px] self-start shrink-0"
>
{#each Object.keys(guides) as guide}
<Button
variant="link"
href={getURLForLanguage($locale, `/help/${guide}`)}
class="min-h-5 h-fit p-0 w-fit text-muted-foreground hover:text-foreground hover:no-underline font-normal hover:font-semibold items-start whitespace-normal {$page
.params.guide === guide
? 'font-semibold text-foreground'
: ''}"
>
{data.guideTitles[guide]}
</Button>
{#each guides[guide] as subGuide}
<Button
variant="link"
href={getURLForLanguage($locale, `/help/${guide}/${subGuide}`)}
class="min-h-5 h-fit p-0 w-fit text-muted-foreground hover:text-foreground hover:no-underline font-normal hover:font-semibold items-start whitespace-normal ml-3 {$page
.params.guide ===
guide + '/' + subGuide
? 'font-semibold text-foreground'
: ''}"
>
{data.guideTitles[`${guide}/${subGuide}`]}
</Button>
{/each}
{/each}
</div>
<div class="grow">
<slot />
</div>
</div>

View File

@@ -1,41 +1,45 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button';
import { getURLForLanguage } from '$lib/utils';
import { locale } from 'svelte-i18n';
import { guides, guideIcons } from '$lib/components/docs/docs';
import { Button } from '$lib/components/ui/button';
import { getURLForLanguage } from '$lib/utils';
import { locale } from 'svelte-i18n';
import { guides, guideIcons } from '$lib/components/docs/docs';
export let data: {
guideTitles: Record<string, string>;
};
export let data: {
guideTitles: Record<string, string>;
};
</script>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
{#each Object.keys(guides) as guide}
<Button
variant="outline"
href={getURLForLanguage($locale, `/help/${guide}`)}
class="min-h-36 h-full pt-6 pb-3 px-0"
>
<div class="flex flex-col w-full">
<div class="h-12 text-center text-5xl">
{guideIcons[guide]}
</div>
<div class="min-h-8 text-2xl text-center my-3 w-full whitespace-normal px-6">
{data.guideTitles[guide]}
</div>
<div class="flex flex-row justify-center flex-wrap gap-x-6 px-6">
{#each guides[guide] as subGuide}
<Button
variant="link"
href={getURLForLanguage($locale, `/help/${guide}/${subGuide}`)}
class="min-h-8 h-fit min-w-24 px-0 py-1 text-muted-foreground text-base text-center whitespace-normal"
>
<svelte:component this={guideIcons[subGuide]} size="16" class="mr-1 shrink-0" />
{data.guideTitles[`${guide}/${subGuide}`]}
</Button>
{/each}
</div>
</div>
</Button>
{/each}
{#each Object.keys(guides) as guide}
<Button
variant="outline"
href={getURLForLanguage($locale, `/help/${guide}`)}
class="min-h-36 h-full pt-6 pb-3 px-0"
>
<div class="flex flex-col w-full">
<div class="h-12 text-center text-5xl">
{guideIcons[guide]}
</div>
<div class="min-h-8 text-2xl text-center my-3 w-full whitespace-normal px-6">
{data.guideTitles[guide]}
</div>
<div class="flex flex-row justify-center flex-wrap gap-x-6 px-6">
{#each guides[guide] as subGuide}
<Button
variant="link"
href={getURLForLanguage($locale, `/help/${guide}/${subGuide}`)}
class="min-h-8 h-fit min-w-24 px-0 py-1 text-muted-foreground text-base text-center whitespace-normal"
>
<svelte:component
this={guideIcons[subGuide]}
size="16"
class="mr-1 shrink-0"
/>
{data.guideTitles[`${guide}/${subGuide}`]}
</Button>
{/each}
</div>
</div>
</Button>
{/each}
</div>

View File

@@ -1,81 +1,81 @@
<script lang="ts">
import { page } from '$app/stores';
import { getNextGuide, getPreviousGuide } from '$lib/components/docs/docs';
import DocsContainer from '$lib/components/docs/DocsContainer.svelte';
import { Button } from '$lib/components/ui/button';
import { getURLForLanguage } from '$lib/utils';
import { ChevronLeft, ChevronRight, PenLine, CornerDownRight } from 'lucide-svelte';
import { _, locale } from 'svelte-i18n';
import { page } from '$app/stores';
import { getNextGuide, getPreviousGuide } from '$lib/components/docs/docs';
import DocsContainer from '$lib/components/docs/DocsContainer.svelte';
import { Button } from '$lib/components/ui/button';
import { getURLForLanguage } from '$lib/utils';
import { ChevronLeft, ChevronRight, PenLine, CornerDownRight } from 'lucide-svelte';
import { _, locale } from 'svelte-i18n';
export let data: {
guideModule: any;
guideTitles: Record<string, string>;
};
export let data: {
guideModule: any;
guideTitles: Record<string, string>;
};
$: previousGuide = getPreviousGuide($page.params.guide);
$: nextGuide = getNextGuide($page.params.guide);
$: previousGuide = getPreviousGuide($page.params.guide);
$: nextGuide = getNextGuide($page.params.guide);
</script>
<div class="markdown flex flex-col gap-3">
<DocsContainer module={data.guideModule.default} />
<DocsContainer module={data.guideModule.default} />
</div>
<div class="flex flex-row flex-wrap gap-3 pt-6">
{#if previousGuide}
<Button
variant="outline"
class="mr-auto"
href={getURLForLanguage($locale, `/help/${previousGuide}`)}
>
<ChevronLeft size="14" class="mr-1 mt-0.5" />
{data.guideTitles[previousGuide]}
</Button>
{/if}
{#if nextGuide}
<Button
variant="outline"
class="ml-auto"
href={getURLForLanguage($locale, `/help/${nextGuide}`)}
>
{data.guideTitles[nextGuide]}
<ChevronRight size="14" class="ml-1 mt-0.5" />
</Button>
{/if}
{#if previousGuide}
<Button
variant="outline"
class="mr-auto"
href={getURLForLanguage($locale, `/help/${previousGuide}`)}
>
<ChevronLeft size="14" class="mr-1 mt-0.5" />
{data.guideTitles[previousGuide]}
</Button>
{/if}
{#if nextGuide}
<Button
variant="outline"
class="ml-auto"
href={getURLForLanguage($locale, `/help/${nextGuide}`)}
>
{data.guideTitles[nextGuide]}
<ChevronRight size="14" class="ml-1 mt-0.5" />
</Button>
{/if}
</div>
<div class="flex flex-row flex-wrap justify-between items-start mt-10 gap-3">
<div class="flex flex-col items-start">
<p class="text-sm text-muted-foreground">{$_('docs.answer_not_found')}</p>
<Button
variant="link"
href="https://www.reddit.com/r/gpxstudio/"
target="_blank"
class="p-0 h-6 text-link"
>
<CornerDownRight size="16" class="mr-1" />
{$_('docs.ask_on_reddit')}
</Button>
</div>
{#if $locale === 'en'}
<Button
variant="link"
href="https://github.com/gpxstudio/gpx.studio/edit/dev/website/src/lib/docs/en/{$page.params
.guide}.mdx"
target="_blank"
class="p-0 h-6 ml-auto text-link"
>
<PenLine size="16" class="mr-1" />
Edit this page on GitHub
</Button>
{:else}
<Button
variant="link"
href="https://crowdin.com/project/gpxstudio/{$locale}"
target="_blank"
class="p-0 h-6 ml-auto text-link"
>
<PenLine size="16" class="mr-1" />
{$_('docs.translate')}
</Button>
{/if}
<div class="flex flex-col items-start">
<p class="text-sm text-muted-foreground">{$_('docs.answer_not_found')}</p>
<Button
variant="link"
href="https://www.reddit.com/r/gpxstudio/"
target="_blank"
class="p-0 h-6 text-link"
>
<CornerDownRight size="16" class="mr-1" />
{$_('docs.ask_on_reddit')}
</Button>
</div>
{#if $locale === 'en'}
<Button
variant="link"
href="https://github.com/gpxstudio/gpx.studio/edit/dev/website/src/lib/docs/en/{$page
.params.guide}.mdx"
target="_blank"
class="p-0 h-6 ml-auto text-link"
>
<PenLine size="16" class="mr-1" />
Edit this page on GitHub
</Button>
{:else}
<Button
variant="link"
href="https://crowdin.com/project/gpxstudio/{$locale}"
target="_blank"
class="p-0 h-6 ml-auto text-link"
>
<PenLine size="16" class="mr-1" />
{$_('docs.translate')}
</Button>
{/if}
</div>

View File

@@ -17,4 +17,4 @@ export async function load({ params }) {
return {
guideModule,
};
}
}