19 Commits

Author SHA1 Message Date
vcoppe
57afaedf83 rephrasing 2026-03-29 23:22:36 +02:00
vcoppe
48063b9066 allow line breaks in buttons 2026-03-29 23:06:59 +02:00
vcoppe
452d356599 rephrase 2026-03-29 22:56:48 +02:00
vcoppe
25eda8041e slight rephrasing 2026-03-29 22:38:31 +02:00
vcoppe
ae4d5356eb fix color 2026-03-29 22:31:57 +02:00
vcoppe
3343bb906e format for crowdin 2026-03-29 20:34:52 +02:00
vcoppe
7b17900160 simplify styling 2026-03-29 20:21:52 +02:00
vcoppe
d5f1fe1c7b finish homepage 2026-03-29 20:04:38 +02:00
vcoppe
553f73f992 change break point for centering 2026-03-29 15:14:21 +02:00
vcoppe
c8cedf2e2c simplify illustration 2026-03-29 14:06:59 +02:00
vcoppe
a751817847 max size for docs 2026-03-28 19:41:44 +01:00
vcoppe
d1ef12db8d work in progress 2026-03-28 19:31:52 +01:00
vcoppe
43d73edf29 small fix 2026-03-28 17:12:43 +01:00
vcoppe
5a0b8c376c small ui changes 2026-03-28 13:03:25 +01:00
vcoppe
9743fd460e update embedding instructions 2026-03-28 12:09:31 +01:00
vcoppe
f70f92a176 change note type 2026-03-28 11:42:11 +01:00
vcoppe
1a4175446c add missing instructions 2026-03-28 11:40:27 +01:00
vcoppe
ed6dfab4c1 fix embedding spacing 2026-03-28 11:32:25 +01:00
vcoppe
6a6e1105c0 update images 2026-03-28 11:32:05 +01:00
37 changed files with 408 additions and 444 deletions

View File

@@ -5,7 +5,7 @@
[**gpx.studio**](https://gpx.studio) is an online tool for creating and editing GPX files. [**gpx.studio**](https://gpx.studio) is an online tool for creating and editing GPX files.
![gpx.studio screenshot](website/src/lib/assets/img/docs/getting-started/interface.png) ![gpx.studio screenshot](website/src/lib/assets/img/docs/getting-started/interface.webp)
This repository contains the source code of the website. This repository contains the source code of the website.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 KiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 KiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 KiB

After

Width:  |  Height:  |  Size: 348 KiB

View File

@@ -26,7 +26,7 @@
<Tooltip.Root> <Tooltip.Root>
<Tooltip.Trigger> <Tooltip.Trigger>
{#snippet child({ props })} {#snippet child({ props })}
<Button {...props} {variant} class={className} {onclick}> <Button {...props} {variant} class="bg-inherit {className}" {onclick}>
{@render children()} {@render children()}
</Button> </Button>
{/snippet} {/snippet}

View File

@@ -8,9 +8,8 @@
import { getURLForLanguage } from '$lib/utils'; import { getURLForLanguage } from '$lib/utils';
</script> </script>
<footer class="w-full"> <footer class="w-full px-12 py-10 border-t flex flex-col items-center">
<div class="mx-6 border-t"> <div class="w-full max-w-5xl flex flex-row flex-wrap justify-between gap-x-10 gap-y-6">
<div class="mx-12 py-10 flex flex-row flex-wrap justify-between gap-x-10 gap-y-6">
<div class="grow flex flex-col items-start"> <div class="grow flex flex-col items-start">
<Logo class="h-8" width="153" /> <Logo class="h-8" width="153" />
<Button <Button
@@ -117,5 +116,4 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</footer> </footer>

View File

@@ -12,16 +12,17 @@
const { velocityUnits } = settings; const { velocityUnits } = settings;
let panelHeight: number = $state(0);
let panelWidth: number = $state(0);
let { let {
gpxStatistics, gpxStatistics,
slicedGPXStatistics, slicedGPXStatistics,
orientation, orientation,
panelSize,
}: { }: {
gpxStatistics: Readable<GPXStatisticsGroup>; gpxStatistics: Readable<GPXStatisticsGroup>;
slicedGPXStatistics: Readable<[GPXGlobalStatistics, number, number] | undefined>; slicedGPXStatistics: Readable<[GPXGlobalStatistics, number, number] | undefined>;
orientation: 'horizontal' | 'vertical'; orientation: 'horizontal' | 'vertical';
panelSize: number;
} = $props(); } = $props();
let statistics = $derived( let statistics = $derived(
@@ -32,12 +33,15 @@
<Card.Root <Card.Root
class="h-full {orientation === 'vertical' class="h-full {orientation === 'vertical'
? 'min-w-40 sm:min-w-44' ? 'min-w-40 sm:min-w-44'
: 'w-full h-10'} border-none shadow-none p-0 text-sm sm:text-base" : 'w-full h-fit my-1'} border-none shadow-none p-0 text-sm sm:text-base bg-transparent"
> >
<Card.Content <Card.Content class="h-full p-0">
class="h-full flex {orientation === 'vertical' <div
? 'flex-col justify-center' bind:clientHeight={panelHeight}
: 'flex-row w-full justify-evenly'} gap-4 p-0" bind:clientWidth={panelWidth}
class="flex {orientation === 'vertical'
? 'flex-col h-full justify-center'
: 'flex-row w-full justify-evenly'} gap-4"
> >
<Tooltip label={i18n._('quantities.distance')}> <Tooltip label={i18n._('quantities.distance')}>
<span class="flex flex-row items-center"> <span class="flex flex-row items-center">
@@ -53,9 +57,8 @@
<WithUnits value={statistics.elevation.loss} type="elevation" /> <WithUnits value={statistics.elevation.loss} type="elevation" />
</span> </span>
</Tooltip> </Tooltip>
{#if panelSize > 120 || orientation === 'horizontal'} {#if panelHeight > 120 || (orientation === 'horizontal' && panelWidth > 450)}
<Tooltip <Tooltip
class={orientation === 'horizontal' ? 'hidden xs:block' : ''}
label="{$velocityUnits === 'speed' label="{$velocityUnits === 'speed'
? i18n._('quantities.speed') ? i18n._('quantities.speed')
: i18n._('quantities.pace')} ({i18n._('quantities.moving')} / {i18n._( : i18n._('quantities.pace')} ({i18n._('quantities.moving')} / {i18n._(
@@ -70,9 +73,8 @@
</span> </span>
</Tooltip> </Tooltip>
{/if} {/if}
{#if panelSize > 160 || orientation === 'horizontal'} {#if panelHeight > 160 || (orientation === 'horizontal' && panelWidth > 620)}
<Tooltip <Tooltip
class={orientation === 'horizontal' ? 'hidden md:block' : ''}
label="{i18n._('quantities.time')} ({i18n._('quantities.moving')} / {i18n._( label="{i18n._('quantities.time')} ({i18n._('quantities.moving')} / {i18n._(
'quantities.total' 'quantities.total'
)})" )})"
@@ -85,5 +87,6 @@
</span> </span>
</Tooltip> </Tooltip>
{/if} {/if}
</div>
</Card.Content> </Card.Content>
</Card.Root> </Card.Root>

View File

@@ -6,11 +6,11 @@
import { getURLForLanguage } from '$lib/utils'; import { getURLForLanguage } from '$lib/utils';
</script> </script>
<nav class="w-full sticky top-0 bg-background z-50"> <nav class="sticky top-0 w-full px-12 py-2 bg-background z-50 flex flex-col items-center border-b">
<div class="mx-6 py-2 flex flex-row items-center border-b gap-4 sm:gap-8"> <div class="w-full max-w-5xl flex flex-row items-center gap-4 sm:gap-8">
<a <a
href={getURLForLanguage(i18n.lang, '/')} href={getURLForLanguage(i18n.lang, '/')}
class="shrink-0 translate-y-0.5 justify-self-start" class="shrink-0 translate-y-0.25 justify-self-start"
> >
<Logo class="h-8 xs:hidden" iconOnly={true} width="26" /> <Logo class="h-8 xs:hidden" iconOnly={true} width="26" />
<Logo class="h-8 hidden xs:block" width="153" /> <Logo class="h-8 hidden xs:block" width="153" />

View File

@@ -19,7 +19,7 @@
@apply text-foreground; @apply text-foreground;
@apply text-3xl; @apply text-3xl;
@apply font-semibold; @apply font-semibold;
@apply mb-3 pt-6; @apply mb-3;
} }
:global(.markdown h2) { :global(.markdown h2) {

View File

@@ -12,7 +12,7 @@
<div class="rounded-md overflow-hidden overflow-clip shadow-xl mx-auto"> <div class="rounded-md overflow-hidden overflow-clip shadow-xl mx-auto">
{#if src === 'getting-started/interface'} {#if src === 'getting-started/interface'}
<enhanced:img <enhanced:img
src="/src/lib/assets/img/docs/getting-started/interface.png" src="/src/lib/assets/img/docs/getting-started/interface.webp"
{alt} {alt}
class="w-full max-w-3xl" class="w-full max-w-3xl"
/> />
@@ -20,13 +20,13 @@
<enhanced:img <enhanced:img
src="/src/lib/assets/img/docs/tools/routing.png" src="/src/lib/assets/img/docs/tools/routing.png"
{alt} {alt}
class="w-full max-w-3xl" class="w-full max-w-lg"
/> />
{:else if src === 'tools/split'} {:else if src === 'tools/split'}
<enhanced:img <enhanced:img
src="/src/lib/assets/img/docs/tools/split.png" src="/src/lib/assets/img/docs/tools/split.png"
{alt} {alt}
class="w-full max-w-3xl" class="w-full max-w-lg"
/> />
{/if} {/if}
</div> </div>

View File

@@ -75,7 +75,7 @@
label={i18n._('chart.settings')} label={i18n._('chart.settings')}
variant="outline" variant="outline"
side="left" side="left"
class="w-7 h-7 p-0 flex justify-center opacity-70 hover:opacity-100 transition-opacity duration-300 hover:bg-background" class="w-7 h-7 p-0 flex justify-center opacity-70 hover:opacity-100 transition-opacity duration-300 bg-background"
> >
<ChartNoAxesColumn size="18" /> <ChartNoAxesColumn size="18" />
</ButtonWithTooltip> </ButtonWithTooltip>

View File

@@ -117,13 +117,12 @@
{/if} {/if}
</div> </div>
<div <div
class="{options.elevation.show ? '' : 'h-10'} flex flex-row gap-2 px-2 sm:px-4" class="{options.elevation.show ? '' : 'h-10'} flex flex-row gap-2 p-2 sm:px-4"
style={options.elevation.show ? `height: ${options.elevation.height}px` : ''} style={options.elevation.show ? `height: ${options.elevation.height}px` : ''}
> >
<GPXStatistics <GPXStatistics
{gpxStatistics} {gpxStatistics}
{slicedGPXStatistics} {slicedGPXStatistics}
panelSize={options.elevation.height}
orientation={options.elevation.show ? 'vertical' : 'horizontal'} orientation={options.elevation.show ? 'vertical' : 'horizontal'}
/> />
{#if options.elevation.show} {#if options.elevation.show}

View File

@@ -90,6 +90,9 @@ export function getCleanedEmbeddingOptions(
delete cleanedOptions[key]; delete cleanedOptions[key];
} }
} }
if (cleanedOptions['key'] && cleanedOptions['key'] === PUBLIC_MAPTILER_KEY) {
delete cleanedOptions['key'];
}
return cleanedOptions; return cleanedOptions;
} }

View File

@@ -54,7 +54,7 @@ export class MapLibreGLMap {
zoom: 0, zoom: 0,
hash: hash, hash: hash,
boxZoom: false, boxZoom: false,
maxPitch: 85, maxPitch: 90,
}); });
this.layerEventManager = new MapLayerEventManager(map); this.layerEventManager = new MapLayerEventManager(map);
map.addControl( map.addControl(

View File

@@ -85,9 +85,11 @@ export class StyleManager {
this.merge(style, basemapStyle); this.merge(style, basemapStyle);
if (this._maptilerKey !== '') {
const terrain = this.getCurrentTerrain(); const terrain = this.getCurrentTerrain();
style.sources[terrain.source] = terrainSources[terrain.source]; style.sources[terrain.source] = terrainSources[terrain.source];
style.terrain = terrain.exaggeration > 0 ? terrain : undefined; style.terrain = terrain.exaggeration > 0 ? terrain : undefined;
}
style.layers.push(...anchorLayers); style.layers.push(...anchorLayers);
@@ -152,6 +154,7 @@ export class StyleManager {
} }
updateTerrain() { updateTerrain() {
if (this._maptilerKey === '') return;
const map_ = get(this._map); const map_ = get(this._map);
if (!map_) return; if (!map_) return;

View File

@@ -90,16 +90,13 @@ 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}
panelSize={120}
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

@@ -12,8 +12,8 @@ title: Integration
You can use **gpx.studio** to create maps showing your GPX files and embed them in your website. You can use **gpx.studio** to create maps showing your GPX files and embed them in your website.
All you need is: All you need is:
1. A <a href="https://cloud.maptiler.com/auth/widget?next=https://cloud.maptiler.com/maps/" target="_blank">MapTiler key</a> to load the map, and 1. GPX files hosted on your server or on Google Drive, or accessible via a public URL;
1. GPX files hosted on your server or on Google Drive, or accessible via a public URL. 1. *Optional:* a <a href="https://cloud.maptiler.com/auth/widget?next=https://cloud.maptiler.com/maps/" target="_blank">MapTiler key</a> to load MapTiler maps.
You can then play with the configurator below to customize your map and generate the corresponding HTML code. You can then play with the configurator below to customize your map and generate the corresponding HTML code.

View File

@@ -58,7 +58,9 @@ Only one basemap can be displayed at a time.
<div class="flex flex-col items-center"> <div class="flex flex-col items-center">
<DocsLayers /> <DocsLayers />
<span class="text-sm text-center mt-2"> <span class="text-sm text-center mt-2">
Hover over the map to show the <a href="https://hiking.waymarkedtrails.org" target="_blank">Waymarked Trails hiking</a> overlay on top of the <a href="https://www.maptiler.com/maps/outdoor-topo/" target="_blank">MapTiler Topo</a> basemap. Hover over the map to show the <a href="https://hiking.waymarkedtrails.org" target="_blank">Waymarked Trails hiking</a> overlay on top of the <a href="https://www.maptiler.com/maps/outdoor-topo/" target="_blank">MapTiler Topo</a> basemap.
</span> </span>
</div> </div>

View File

@@ -5,6 +5,7 @@ title: Merge
<script> <script>
import { Group } from '@lucide/svelte'; import { Group } from '@lucide/svelte';
import Merge from '$lib/components/toolbar/tools/Merge.svelte'; import Merge from '$lib/components/toolbar/tools/Merge.svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
# <Group size="24" class="inline-block" style="margin-bottom: 5px" /> { title } # <Group size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
@@ -15,6 +16,13 @@ To use this tool, you need to [select](../files-and-stats) multiple files, [trac
- The second option can be used to create or manage files with multiple [tracks or segments](../gpx). - The second option can be used to create or manage files with multiple [tracks or segments](../gpx).
Merging files (or tracks) will result in a single file (or track) containing all tracks (or segments) from the selection. Merging files (or tracks) will result in a single file (or track) containing all tracks (or segments) from the selection.
<DocsNote>
Selected items are merged in the order they appear in the files list.
Reorder items by drag-and-drop if needed.
</DocsNote>
<div class="flex flex-row justify-center"> <div class="flex flex-row justify-center">
<Merge class="text-foreground p-3 border rounded-md shadow-lg" /> <Merge class="text-foreground p-3 border rounded-md shadow-lg" />
</div> </div>

View File

@@ -493,7 +493,7 @@
"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",
"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.",
"file_processing": "Advanced file processing", "file_processing": "Advanced file processing",
@@ -502,8 +502,15 @@
"maps_description": "A large collection of basemaps, overlays and points of interest to help you craft your next outdoor adventure, or visualize your latest achievement.", "maps_description": "A large collection of basemaps, overlays and points of interest to help you craft your next outdoor adventure, or visualize your latest achievement.",
"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.",
"identity": "Free, ad-free and open source", "philosophy": "Philosophy",
"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": "Free, ad-free and open source",
"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 data collection.",
"community": "Made possible by the community",
"community_description": "gpx.studio has an amazing community that has covered its costs through donations for years, while shaping the project through feature suggestions, bug reports, and 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",
@@ -528,7 +535,7 @@
}, },
"embedding": { "embedding": {
"title": "Create your own map", "title": "Create your own map",
"maptiler_key": "MapTiler key", "maptiler_key": "MapTiler key (optional, only required for MapTiler maps)",
"file_urls": "File URLs (separated by commas)", "file_urls": "File URLs (separated by commas)",
"drive_ids": "Google Drive file IDs (separated by commas)", "drive_ids": "Google Drive file IDs (separated by commas)",
"basemap": "Basemap", "basemap": "Basemap",

View File

@@ -1,37 +1,29 @@
<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';
import { exampleGPXFile } from '$lib/assets/example'; import { exampleGPXFile } from '$lib/assets/example';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import Toolbar from '$lib/components/toolbar/Toolbar.svelte'; import Scissors from '$lib/components/toolbar/tools/scissors/Scissors.svelte';
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);
@@ -53,17 +45,17 @@
}); });
</script> </script>
<div class="space-y-24 my-24"> <div class="w-full px-12 flex flex-col items-center">
<div class="-mt-12 sm:mt-0"> <div class="w-full max-w-5xl flex flex-col items-center">
<div class="px-12 w-full flex flex-col items-center"> <div class="mt-12 flex flex-col xs:items-center gap-12">
<div class="flex flex-col gap-6 items-center max-w-3xl"> <div class="flex flex-col xs:items-center gap-6 max-w-3xl">
<h1 class="text-4xl sm:text-6xl font-black text-center"> <h1 class="text-4xl xs:text-5xl sm:text-6xl xs:text-center font-black">
{i18n._('metadata.home_title')} {i18n._('metadata.home_title')}
</h1> </h1>
<div class="text-lg sm:text-xl text-muted-foreground text-center"> <div class="text-lg sm:text-xl text-muted-foreground xs:text-center">
{i18n._('metadata.description')} {i18n._('metadata.description')}
</div> </div>
<div class="w-full flex flex-row justify-center gap-3"> <div class="w-full flex flex-row xs:justify-center gap-3">
<Button <Button
data-sveltekit-reload data-sveltekit-reload
href={getURLForLanguage(i18n.lang, '/app')} href={getURLForLanguage(i18n.lang, '/app')}
@@ -82,118 +74,67 @@
</Button> </Button>
</div> </div>
</div> </div>
</div>
<div class="relative overflow-hidden">
<enhanced:img <enhanced:img
src="/src/lib/assets/img/home/routing.png" src="/src/lib/assets/img/docs/getting-started/interface.webp"
alt="Screenshot of the gpx.studio map in 3D." alt="The gpx.studio interface."
class="w-full min-w-[1200px] ml-[20%] -translate-x-[20%]" class="rounded-xl shadow-2xl w-full"
/> />
<div
class="absolute top-0 left-0 w-full h-full bg-gradient-to-b from-background via-transparent to-background"
></div>
</div> </div>
</div> <h2>
<div class="px-12 sm:px-24 w-full flex flex-col items-center"> {i18n._('homepage.features')}
<div </h2>
class="flex flex-col md:flex-row gap-x-12 gap-y-6 items-center justify-between max-w-5xl" <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="markdown text-center"> <div>
<h1> <h3>
<Route size="24" class="inline-block align-baseline" /> <Route size="20" class="inline-block align-baseline" />
{i18n._('homepage.route_planning')} {i18n._('homepage.route_planning')}
</h1> </h3>
<p class="text-muted-foreground">{i18n._('homepage.route_planning_description')}</p> <p>
{i18n._('homepage.route_planning_description')}
</p>
</div> </div>
<div class="p-3 w-fit rounded-md border shadow-xl md:shrink-0"> <div class="relative">
<div
class="p-3 border rounded-xl shadow-xl origin-top-left scale-45 xs:scale-75 md:scale-45 lg:scale-70 absolute top-1.5 left-1.5 bg-background"
>
<Routing minimizable={false} /> <Routing minimizable={false} />
</div> </div>
<enhanced:img
src="/src/lib/assets/img/docs/tools/routing.png"
alt="Route planning illustration."
class="h-full object-cover rounded-xl shadow-lg"
/>
</div> </div>
</div> </div>
<div class="px-12 sm:px-24 w-full flex flex-col items-center"> <div class="grid md:grid-rows-subgrid md:row-start-1 md:row-end-3 gap-4">
<div <div>
class="flex flex-col md:flex-row gap-x-12 gap-y-6 items-center justify-between max-w-5xl" <h3>
> <Map size="20" class="inline-block align-baseline" />
<div class="markdown text-center md:hidden">
<h1>
<PencilRuler size="24" class="inline-block align-baseline" />
{i18n._('homepage.file_processing')}
</h1>
<p class="text-muted-foreground">
{i18n._('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="inline-block align-baseline" />
{i18n._('homepage.file_processing')}
</h1>
<p class="text-muted-foreground">
{i18n._('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="inline-block align-baseline" />
{i18n._('homepage.maps')} {i18n._('homepage.maps')}
</h1> </h3>
<p class="text-muted-foreground">{i18n._('homepage.maps_description')}</p> <p>{i18n._('homepage.maps_description')}</p>
</div> </div>
<div
class="relative w-full max-w-[320px] aspect-square rounded-2xl shadow-xl overflow-clip"
>
<enhanced:img <enhanced:img
src="/src/lib/assets/img/home/maptiler-topo.png" src="/src/lib/assets/img/home/map-overlay.png"
alt="MapTiler Topo map screenshot." alt="3D map with multiple layers and points of interest."
class="absolute" class="h-full object-cover rounded-xl shadow-lg"
style="clip-path: inset(0 50% 50% 0);"
/>
<enhanced:img
src="/src/lib/assets/img/home/maptiler-satellite.png"
alt="MapTiler 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="grid md:grid-rows-subgrid md:row-start-3 md:row-end-5 gap-4">
</div> <div>
<div class="px-8 md:px-12"> <h3>
<div class="markdown text-center px-4 md:px-12"> <ChartArea size="20" class="inline-block align-baseline" />
<h1>
<ChartArea size="24" class="inline-block align-baseline" />
{i18n._('homepage.data_visualization')} {i18n._('homepage.data_visualization')}
</h1> </h3>
<p class="text-muted-foreground mb-6"> <p>
{i18n._('homepage.data_visualization_description')} {i18n._('homepage.data_visualization_description')}
</p> </p>
</div> </div>
<div class="h-48 w-full"> <div
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">
<ElevationProfile <ElevationProfile
{gpxStatistics} {gpxStatistics}
{slicedGPXStatistics} {slicedGPXStatistics}
@@ -202,73 +143,110 @@
{elevationFill} {elevationFill}
/> />
</div> </div>
<div class="flex flex-col items-center">
<div class="h-10 w-fit">
<GPXStatistics <GPXStatistics
{gpxStatistics} {gpxStatistics}
{slicedGPXStatistics} {slicedGPXStatistics}
panelSize={192}
orientation={'horizontal'} orientation={'horizontal'}
/> />
</div> </div>
</div> </div>
<div class="grid md:grid-rows-subgrid md:row-start-3 md:row-end-5 gap-4">
<div>
<h3>
<PencilRuler size="20" class="inline-block align-baseline" />
{i18n._('homepage.file_processing')}
</h3>
<p>
{i18n._('homepage.file_processing_description')}
</p>
</div> </div>
<div class="px-12 sm:px-24 w-full flex flex-col items-center"> <div class="relative">
<div <div
class="flex flex-col md:flex-row gap-x-12 gap-y-6 items-center justify-between max-w-5xl" class="p-3 border rounded-xl shadow-xl origin-top-right scale-45 xs:scale-75 md:scale-45 lg:scale-70 absolute top-1.5 right-1.5 bg-background"
> >
<div class="markdown text-center md:hidden"> <Scissors />
<h1>
<Scale size="24" class="inline-block align-baseline" />
{i18n._('homepage.identity')}
</h1>
<p class="text-muted-foreground">{i18n._('homepage.identity_description')}</p>
</div> </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="inline-block align-baseline" />
{i18n._('homepage.identity')}
</h1>
<p class="text-muted-foreground">{i18n._('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 <enhanced:img
src="/src/lib/assets/img/home/map.png" src="/src/lib/assets/img/docs/tools/split.png"
alt="Screenshot of the gpx.studio map in 3D." alt="Splitting illustration."
class="min-w-[800px] ml-[15%] -translate-x-[15%]" class="h-full object-cover rounded-xl shadow-lg"
/> />
</div> </div>
</div> </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-6" <h2>
{i18n._('homepage.philosophy')}
</h2>
<div class="w-full grid md:grid-cols-2 gap-12 border-t pt-6">
<div class="w-full">
<h3>
<Scale size="20" class="inline-block align-baseline" />
{i18n._('homepage.foss')}
</h3>
<p>
{i18n._('homepage.foss_description')}
<Button
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>
<h3>
<HatGlasses size="20" class="inline-block align-baseline" />
{i18n._('homepage.privacy')}
</h3>
<p>{i18n._('homepage.privacy_description')}</p>
</div>
</div>
<div <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" class="md:text-center flex flex-col md:items-center mt-12 mb-24 p-6 border bg-secondary/50 rounded-xl"
>
<h3>
{i18n._('homepage.community')}
</h3>
<p class="md:max-w-3/4">{i18n._('homepage.community_description')}</p>
<HeartHandshake size="80" class="mt-6 self-center" />
<div class="flex flex-row flex-wrap gap-4 justify-center mt-6">
<Button
variant="outline"
href="https://opencollective.com/gpxstudio"
target="_blank"
class="text-support text-base max-w-full h-auto whitespace-normal"
> >
{#await data.fundingModule then fundingModule}
<DocsContainer module={fundingModule.default} />
{/await}
<Button href="https://opencollective.com/gpxstudio" target="_blank" class="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)" color="var(--support)" />
</Button> </Button>
</div> <Button
<div variant="outline"
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" href="https://crowdin.com/project/gpxstudio"
target="_blank"
class="text-base max-w-full h-auto whitespace-normal"
> >
{#await data.translationModule then translationModule} <Languages size="16" />
<DocsContainer module={translationModule.default} /> <span>{i18n._('homepage.translate_button')}</span>
{/await}
<Button href="https://crowdin.com/project/gpxstudio" target="_blank" class="text-base">
<PenLine size="16" />
<span>{i18n._('homepage.contribute')}</span>
</Button> </Button>
</div> </div>
</div> </div>
</div> </div>
</div>
<style lang="postcss">
@reference "../../app.css";
div :global(h2) {
@apply text-center text-4xl font-extrabold mt-24 mb-6;
}
div :global(h3) {
@apply text-2xl pt-0 font-semibold mb-3;
}
div :global(p) {
@apply text-muted-foreground;
}
</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'),
};
}

View File

@@ -141,7 +141,6 @@
<GPXStatistics <GPXStatistics
{gpxStatistics} {gpxStatistics}
{slicedGPXStatistics} {slicedGPXStatistics}
panelSize={$bottomPanelSize}
orientation={bottomPanelOrientation == 'horizontal' ? 'vertical' : 'horizontal'} orientation={bottomPanelOrientation == 'horizontal' ? 'vertical' : 'horizontal'}
/> />
{#if $elevationProfile} {#if $elevationProfile}

View File

@@ -19,6 +19,9 @@
return; return;
} }
embeddingOptions = getMergedEmbeddingOptions(options); embeddingOptions = getMergedEmbeddingOptions(options);
if (embeddingOptions.key === '' && embeddingOptions.basemap.startsWith('maptiler')) {
embeddingOptions.basemap = 'openStreetMap';
}
}); });
</script> </script>

View File

@@ -18,8 +18,9 @@
} = $props(); } = $props();
</script> </script>
<div class="grow px-12 pt-6 pb-12 flex flex-row gap-24"> <div class="grow flex flex-col items-center p-12">
<div class="hidden md:flex flex-col gap-2 w-40 sticky top-[108px] self-start shrink-0"> <div class="max-w-5xl flex flex-row gap-24">
<div class="hidden md:flex flex-col gap-2 w-40 sticky top-26 self-start shrink-0">
<div class="mb-2"> <div class="mb-2">
<AlgoliaDocSearch /> <AlgoliaDocSearch />
</div> </div>
@@ -53,3 +54,4 @@
{@render children()} {@render children()}
</div> </div>
</div> </div>
</div>

View File

@@ -59,7 +59,7 @@
href="https://github.com/gpxstudio/gpx.studio/edit/dev/website/src/lib/docs/en/{page href="https://github.com/gpxstudio/gpx.studio/edit/dev/website/src/lib/docs/en/{page
.params.guide}.mdx" .params.guide}.mdx"
target="_blank" target="_blank"
class="p-0 h-6 ml-auto text-link" class="p-0 has-[>svg]:px-0 h-6 ml-auto text-link"
> >
<PenLine size="16" /> <PenLine size="16" />
Edit this page on GitHub Edit this page on GitHub