finish homepage

This commit is contained in:
vcoppe
2026-03-29 20:04:38 +02:00
parent 553f73f992
commit d5f1fe1c7b
6 changed files with 85 additions and 163 deletions

View File

@@ -90,14 +90,12 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
{elevationFill} {elevationFill}
/> />
</div> </div>
<div class="flex flex-col items-center -mt-6"> <div class="flex flex-col items-center w-full">
<div class="h-10 w-fit"> <GPXStatistics
<GPXStatistics {gpxStatistics}
{gpxStatistics} {slicedGPXStatistics}
{slicedGPXStatistics} orientation={'horizontal'}
orientation={'horizontal'} />
/>
</div>
</div> </div>
### Additional data ### Additional data

View File

@@ -1,13 +0,0 @@
<script>
import { HeartHandshake } from '@lucide/svelte';
</script>
## <HeartHandshake size="18" class="inline-block align-baseline" /> Help keep the website free (and ad-free)
Each time you add or move GPS points, our servers calculate the best route on the road network.
We also use APIs from <a href="https://maptiler.com" target="_blank">MapTiler</a> to display beautiful maps, retrieve elevation data and allow you to search for places.
Unfortunately, this is expensive.
If you enjoy using this tool and find it valuable, please consider making a small donation to help keep the website free and ad-free.
Thank you very much for your support! ❤️

View File

@@ -1,12 +0,0 @@
<script>
import { Languages } from '@lucide/svelte';
</script>
## <Languages size="18" class="inline-block align-baseline" /> Translation
The website is translated by volunteers using a collaborative translation platform.
You can contribute by adding or improving translations on our <a href="https://crowdin.com/project/gpxstudio" target="_blank">Crowdin project</a>.
If you would like to start translating into a new language, please <a href="#contact">get in touch</a>.
Any help is greatly appreciated!

View File

@@ -493,7 +493,6 @@
"email": "Email", "email": "Email",
"contribute": "Contribute", "contribute": "Contribute",
"supported_by": "supported by", "supported_by": "supported by",
"support_button": "Support gpx.studio on Open Collective",
"features": "Features", "features": "Features",
"route_planning": "Route planning", "route_planning": "Route planning",
"route_planning_description": "An intuitive interface to create itineraries tailored to each sport, based on OpenStreetMap data.", "route_planning_description": "An intuitive interface to create itineraries tailored to each sport, based on OpenStreetMap data.",
@@ -504,8 +503,14 @@
"data_visualization": "Data visualization", "data_visualization": "Data visualization",
"data_visualization_description": "An interactive elevation profile with detailed statistics to analyze recorded activities and future objectives.", "data_visualization_description": "An interactive elevation profile with detailed statistics to analyze recorded activities and future objectives.",
"philosophy": "Philosophy", "philosophy": "Philosophy",
"identity": "Free, ad-free and open source", "foss": "Free, ad-free and open source",
"identity_description": "The website is free to use, without ads, and the source code is publicly available on GitHub. This is only possible thanks to the incredible support of the community." "foss_description": "The website is free to use, without ads, and the source code is publicly available on GitHub.",
"privacy": "Privacy-friendly",
"privacy_description": "Your GPX files never leave your browser. No tracking, no analytics.",
"community": "Made possible by the community",
"community_description": "gpx.studio is funded by user donations, which have sustainably covered its costs for years. The community also helps shape the project through feature suggestions, bug reports, and collaborative translations into many languages.",
"support_button": "Support gpx.studio on Open Collective",
"translate_button": "Help translate the website on Crowdin"
}, },
"docs": { "docs": {
"translate": "Improve the translation on Crowdin", "translate": "Improve the translation on Crowdin",

View File

@@ -1,19 +1,20 @@
<script lang="ts"> <script lang="ts">
import { Button } from '$lib/components/ui/button'; 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/elevation-profile/ElevationProfile.svelte'; import ElevationProfile from '$lib/components/elevation-profile/ElevationProfile.svelte';
import GPXStatistics from '$lib/components/GPXStatistics.svelte'; import GPXStatistics from '$lib/components/GPXStatistics.svelte';
import Routing from '$lib/components/toolbar/tools/routing/Routing.svelte'; import Routing from '$lib/components/toolbar/tools/routing/Routing.svelte';
import { import {
BookOpenText, BookOpenText,
Heart, Heart,
HeartHandshake,
ChartArea, ChartArea,
Map, Map,
PencilRuler, PencilRuler,
PenLine,
Route, Route,
Scale, Scale,
HatGlasses,
Languages,
ExternalLink,
} from '@lucide/svelte'; } from '@lucide/svelte';
import { i18n } from '$lib/i18n.svelte'; import { i18n } from '$lib/i18n.svelte';
import { getURLForLanguage } from '$lib/utils'; import { getURLForLanguage } from '$lib/utils';
@@ -23,15 +24,6 @@
import { currentTool, Tool } from '$lib/components/toolbar/tools'; import { currentTool, Tool } from '$lib/components/toolbar/tools';
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
let {
data,
}: {
data: {
fundingModule: Promise<any>;
translationModule: Promise<any>;
};
} = $props();
let gpxStatistics = writable(exampleGPXFile.getStatistics()); let gpxStatistics = writable(exampleGPXFile.getStatistics());
let slicedGPXStatistics = writable(undefined); let slicedGPXStatistics = writable(undefined);
let hoveredPoint = writable(null); let hoveredPoint = writable(null);
@@ -88,16 +80,16 @@
class="rounded-xl shadow-2xl w-full" class="rounded-xl shadow-2xl w-full"
/> />
</div> </div>
<div class="text-center text-4xl font-extrabold mt-24 mb-6"> <h2>
{i18n._('homepage.features')} {i18n._('homepage.features')}
</div> </h2>
<div class="grid md:grid-cols-2 gap-12 border-t pt-6"> <div class="grid md:grid-cols-2 gap-12 border-t pt-6">
<div class="grid md:grid-rows-subgrid md:row-start-1 md:row-end-3 gap-4"> <div class="grid md:grid-rows-subgrid md:row-start-1 md:row-end-3 gap-4">
<div class="markdown homepage"> <div>
<h1> <h3>
<Route size="20" class="inline-block align-baseline" /> <Route size="20" class="inline-block align-baseline" />
{i18n._('homepage.route_planning')} {i18n._('homepage.route_planning')}
</h1> </h3>
<p> <p>
{i18n._('homepage.route_planning_description')} {i18n._('homepage.route_planning_description')}
</p> </p>
@@ -116,11 +108,11 @@
</div> </div>
</div> </div>
<div class="grid md:grid-rows-subgrid md:row-start-1 md:row-end-3 gap-4"> <div class="grid md:grid-rows-subgrid md:row-start-1 md:row-end-3 gap-4">
<div class="markdown homepage"> <div>
<h1> <h3>
<Map size="20" class="inline-block align-baseline" /> <Map size="20" class="inline-block align-baseline" />
{i18n._('homepage.maps')} {i18n._('homepage.maps')}
</h1> </h3>
<p>{i18n._('homepage.maps_description')}</p> <p>{i18n._('homepage.maps_description')}</p>
</div> </div>
<enhanced:img <enhanced:img
@@ -129,18 +121,18 @@
class="h-full object-cover rounded-xl shadow-lg" class="h-full object-cover rounded-xl shadow-lg"
/> />
</div> </div>
<div class="grid md:grid-rows-subgrid md:row-start-3 md:row-end-6 gap-4"> <div class="grid md:grid-rows-subgrid md:row-start-3 md:row-end-5 gap-4">
<div class="markdown homepage"> <div>
<h1> <h3>
<ChartArea size="20" class="inline-block align-baseline" /> <ChartArea size="20" class="inline-block align-baseline" />
{i18n._('homepage.data_visualization')} {i18n._('homepage.data_visualization')}
</h1> </h3>
<p> <p>
{i18n._('homepage.data_visualization_description')} {i18n._('homepage.data_visualization_description')}
</p> </p>
</div> </div>
<div <div
class="h-full w-full aspect-3/2 overflow-hidden flex flex-col gap-2 rounded-xl pt-6 pb-4 px-6 bg-secondary/50 border shadow-lg" class="w-full aspect-3/2 overflow-hidden flex flex-col gap-2 rounded-xl pt-6 pb-4 px-6 bg-secondary/50 border shadow-lg"
> >
<div class="grow"> <div class="grow">
<ElevationProfile <ElevationProfile
@@ -158,12 +150,12 @@
/> />
</div> </div>
</div> </div>
<div class="grid md:grid-rows-subgrid md:row-start-3 md:row-end-6 gap-4"> <div class="grid md:grid-rows-subgrid md:row-start-3 md:row-end-5 gap-4">
<div class="markdown homepage"> <div>
<h1> <h3>
<PencilRuler size="20" class="inline-block align-baseline" /> <PencilRuler size="20" class="inline-block align-baseline" />
{i18n._('homepage.file_processing')} {i18n._('homepage.file_processing')}
</h1> </h3>
<p> <p>
{i18n._('homepage.file_processing_description')} {i18n._('homepage.file_processing_description')}
</p> </p>
@@ -182,103 +174,61 @@
</div> </div>
</div> </div>
</div> </div>
<div class="text-center text-4xl font-extrabold mt-24 mb-6"> <h2>
{i18n._('homepage.philosophy')} {i18n._('homepage.philosophy')}
</div> </h2>
<div class="grid md:grid-cols-2 gap-12 border-t pt-6"> <div class="w-full grid md:grid-cols-2 gap-12 border-t pt-6">
<div class="grid md:grid-rows-subgrid md:row-start-1 md:row-end-3 gap-4"> <div class="w-full">
<div class="markdown homepage"> <h3>
<h1> <Scale size="20" class="inline-block align-baseline" />
<PencilRuler size="20" class="inline-block align-baseline" /> {i18n._('homepage.foss')}
Free and ad-free </h3>
</h1> <p>
<p>explanation</p> {i18n._('homepage.foss_description')}
</div> <Button
<p>image?</p> variant="link"
href="https://github.com/gpxstudio/gpx.studio"
target="_blank"
class="p-0 has-[>svg]:p-0 h-fit"
>
<ExternalLink size="20" class="inline-block align-baseline" />
</Button>
</p>
</div> </div>
<div class="grid md:grid-rows-subgrid md:row-start-1 md:row-end-3 gap-4"> <div>
<div class="markdown homepage"> <h3>
<h1> <HatGlasses size="20" class="inline-block align-baseline" />
<PencilRuler size="20" class="inline-block align-baseline" /> {i18n._('homepage.privacy')}
Privacy first </h3>
</h1> <p>{i18n._('homepage.privacy_description')}</p>
<p>explanation</p>
</div>
<p>image?</p>
</div>
<div class="grid md:grid-rows-subgrid md:row-start-3 md:row-end-6 gap-4">
<div class="markdown homepage">
<h1>
<PencilRuler size="20" class="inline-block align-baseline" />
Free and ad-free
</h1>
<p>explanation</p>
</div>
<p>image?</p>
</div>
<div class="grid md:grid-rows-subgrid md:row-start-3 md:row-end-6 gap-4">
<div class="markdown homepage">
<h1>
<PencilRuler size="20" class="inline-block align-baseline" />
Free and ad-free
</h1>
<p>explanation</p>
</div>
<p>image?</p>
</div> </div>
</div> </div>
<div class="px-12 sm:px-24 w-full flex flex-col items-center"> <div
<div class="md:text-center flex flex-col md:items-center mt-12 mb-24 p-6 border bg-secondary/50 rounded-xl"
class="flex flex-col md:flex-row gap-x-12 gap-y-6 items-center justify-between max-w-5xl" >
> <h3>
<div class="markdown text-center md:hidden"> {i18n._('homepage.community')}
<h1> </h3>
<Scale size="20" class="inline-block align-baseline" /> <p class="md:max-w-2/3">{i18n._('homepage.community_description')}</p>
{i18n._('homepage.identity')} <HeartHandshake size="80" class="mt-6 self-center" />
</h1> <div class="flex flex-row flex-wrap gap-4 justify-center mt-6">
<p class="text-muted-foreground">{i18n._('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="20" class="inline-block align-baseline" />
{i18n._('homepage.identity')}
</h1>
<p class="text-muted-foreground">{i18n._('homepage.identity_description')}</p>
</div>
</div>
</div>
<div class="flex flex-row flex-wrap lg:flex-nowrap items-center justify-center">
<div
class="grow max-w-xl flex flex-col items-center gap-6 p-8 border rounded-2xl shadow-xl"
>
{#await data.fundingModule then fundingModule}
<DocsContainer module={fundingModule.default} />
{/await}
<Button <Button
variant="outline"
href="https://opencollective.com/gpxstudio" href="https://opencollective.com/gpxstudio"
target="_blank" target="_blank"
class="text-base" class="text-support text-base"
> >
<Heart size="16" fill="var(--support)" color="var(--support)" />
<span>{i18n._('homepage.support_button')}</span> <span>{i18n._('homepage.support_button')}</span>
<Heart size="16" fill="var(--support)" />
</Button> </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"
>
{#await data.translationModule then translationModule}
<DocsContainer module={translationModule.default} />
{/await}
<Button <Button
variant="outline"
href="https://crowdin.com/project/gpxstudio" href="https://crowdin.com/project/gpxstudio"
target="_blank" target="_blank"
class="text-base" class="text-base"
> >
<PenLine size="16" /> <Languages size="16" />
<span>{i18n._('homepage.contribute')}</span> <span>{i18n._('homepage.translate_button')}</span>
</Button> </Button>
</div> </div>
</div> </div>
@@ -288,8 +238,15 @@
<style lang="postcss"> <style lang="postcss">
@reference "../../app.css"; @reference "../../app.css";
:global(.markdown.homepage > h1) { div :global(h2) {
@apply text-2xl; @apply text-center text-4xl font-extrabold mt-24 mb-6;
@apply pt-0; }
div :global(h3) {
@apply text-2xl pt-0 font-semibold mb-3;
}
div :global(p) {
@apply text-muted-foreground;
} }
</style> </style>

View File

@@ -1,13 +0,0 @@
function getModule(language: string | undefined, guide: string) {
language = language ?? 'en';
return import(`./../../lib/docs/${language}/home/${guide}.mdx`);
}
export async function load({ params }) {
const { language } = params;
return {
fundingModule: getModule(language, 'funding'),
translationModule: getModule(language, 'translation'),
};
}