Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/foss-serverless-app/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ description: >-

# Build a FOSS, serverless remake — end to end

This is the method used to build `MAX-Finder` (a MAX JEUNE / SENIOR train-seat
This is the method used to build `Railmap` (a MAX JEUNE / SENIOR train-seat
finder on SNCF open data). It generalizes to any "remake X as a FOSS static app" task.

## Core principles (decide these first)
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-apk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ jobs:
set -euo pipefail
tag="${{ github.event.release.tag_name || github.ref_name }}"
src=$(ls android/app/build/outputs/apk/release/*-release.apk | grep -v -- -unsigned | head -n1)
name="max-finder-${tag}.apk"
name="railmap-${tag}.apk"
cp "$src" "$name"
echo "name=$name" >> "$GITHUB_OUTPUT"

Expand Down
192 changes: 95 additions & 97 deletions README.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions VISION.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Vision — MAX Finder
# Vision — Railmap

Where MAX Finder is today, and where it's headed. For the day-to-day of *how it
Where Railmap is today, and where it's headed. For the day-to-day of *how it
works now*, see [`docs/how-it-works.md`](docs/how-it-works.md) and
[`docs/algorithms.md`](docs/algorithms.md). The non-negotiable principles live in
[`specs/constitution.md`](specs/constitution.md).
Expand All @@ -9,7 +9,7 @@ works now*, see [`docs/how-it-works.md`](docs/how-it-works.md) and

## Today (V1): SNCF, done well

MAX Finder finds SNCF trains where a free **MAX JEUNE / MAX SENIOR** seat is
Railmap finds SNCF trains where a free **MAX JEUNE / MAX SENIOR** seat is
reservable, from SNCF open data — Where to?, Where from?, Exact trip, Tour, Ideas,
round trips and night trains, all serverless and account-free. **This is and stays
the heart of the app**, and the branding stays SNCF / MAX.
Expand Down
4 changes: 2 additions & 2 deletions android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_name">MAX Finder</string>
<string name="title_activity_main">MAX Finder</string>
<string name="app_name">Railmap</string>
<string name="title_activity_main">Railmap</string>
<string name="package_name">org.maxfinder.app</string>
<string name="custom_url_scheme">org.maxfinder.app</string>
</resources>
2 changes: 1 addition & 1 deletion capacitor.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { CapacitorConfig } from "@capacitor/cli";
// running `npx cap sync`.
const config: CapacitorConfig = {
appId: "org.maxfinder.app",
appName: "MAX Finder",
appName: "Railmap",
webDir: "dist",
backgroundColor: "#0f7a52",
android: {
Expand Down
14 changes: 7 additions & 7 deletions docs/FDROID.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Publishing MAX Finder (GitHub Releases + F-Droid)
# Publishing Railmap (GitHub Releases + F-Droid)

There are two independent Android distribution channels. They are **signed by
different keys**, so a user must pick one and stick with it (see
Expand All @@ -23,7 +23,7 @@ be injected by CI.
- `versionCode` → an integer that **increases by at least 1 every release** (Android refuses to upgrade otherwise). e.g. `1` → `2`.
2. Commit, then tag and push: `git tag v0.2.0 && git push origin v0.2.0`.
3. **Publish a GitHub Release** for that tag. The workflow builds, signs (if
secrets are set), and attaches `max-finder-v0.2.0.apk`.
secrets are set), and attaches `railmap-v0.2.0.apk`.
4. **For F-Droid**, add a new build entry with the same `versionCode`/`versionName`
to the metadata in `fdroiddata` (see below). With `UpdateCheckMode: Tags` the
F-Droid bot will also propose this for you.
Expand Down Expand Up @@ -77,14 +77,14 @@ Categories:
- Internet
- Travel
License: AGPL-3.0-only
AuthorName: MAX Finder contributors
SourceCode: https://github.com/offware-apps/MAX-Finder
IssueTracker: https://github.com/offware-apps/MAX-Finder/issues
AuthorName: Railmap contributors
SourceCode: https://github.com/offware-apps/Railmap
IssueTracker: https://github.com/offware-apps/Railmap/issues

AutoName: MAX Finder
AutoName: Railmap

RepoType: git
Repo: https://github.com/offware-apps/MAX-Finder.git
Repo: https://github.com/offware-apps/Railmap.git

Builds:
- versionName: 0.1.0
Expand Down
4 changes: 2 additions & 2 deletions docs/algorithms.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# How MAX Finder finds your trains
# How Railmap finds your trains

MAX Finder helps people with an SNCF "MAX JEUNE" or "MAX SENIOR" pass find trains where a reserved seat costs nothing extra. This page explains, in plain words, how the app decides which trains to show you and how it strings them into trips. No maths, just everyday pictures. Each section has a one-line intro, a real-world analogy, the actual steps, and a small diagram.
Railmap helps people with an SNCF "MAX JEUNE" or "MAX SENIOR" pass find trains where a reserved seat costs nothing extra. This page explains, in plain words, how the app decides which trains to show you and how it strings them into trips. No maths, just everyday pictures. Each section has a one-line intro, a real-world analogy, the actual steps, and a small diagram.

---

Expand Down
14 changes: 7 additions & 7 deletions docs/how-it-works.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# How MAX Finder Works
# How Railmap Works

A friendly tour of what this little app does, where its train data comes from, and why it can run for free forever without a server or an account.

Expand All @@ -8,7 +8,7 @@ A friendly tour of what this little app does, where its train data comes from, a

If you hold an SNCF **MAX JEUNE** or **MAX SENIOR** pass, you can ride certain high-speed trains for free — but only if there's a special "MAX" seat still open on that train. The catch: normally you'd have to poke at the SNCF Connect booking site one station pair at a time ("Paris to Lyon? Paris to Bordeaux? Paris to Nantes?…") just to discover *where* those free seats even exist.

MAX Finder flips that around. Instead of you guessing routes, it already knows every train with a free reservable seat and lets you ask big, open questions like:
Railmap flips that around. Instead of you guessing routes, it already knows every train with a free reservable seat and lets you ask big, open questions like:

> "From my city, where can I go for free this weekend?"

Expand Down Expand Up @@ -43,12 +43,12 @@ flowchart LR

## How it runs for free, forever — no server, no account

Here's the part that keeps it truly free: **there is no back-end and no login.** MAX Finder is just a bundle of ordinary files — a web page, some code, and that daily data file — parked on **GitHub Pages**, GitHub's free static-file hosting.
Here's the part that keeps it truly free: **there is no back-end and no login.** Railmap is just a bundle of ordinary files — a web page, some code, and that daily data file — parked on **GitHub Pages**, GitHub's free static-file hosting.

When you open the site, *your own browser* downloads the data file and does **all** the searching right there on your device. Nothing you type is sent off to some computer in a data centre. Compare the two shapes:

- **A traditional app:** your browser → a company's server → a database → back to you. Someone pays for that server every month.
- **MAX Finder:** your browser → a static file on free hosting → and the searching happens on your own device.
- **Railmap:** your browser → a static file on free hosting → and the searching happens on your own device.

Because there's no server to keep running, there's nothing to pay for and nothing that can quietly shut down when the bill isn't paid.

Expand Down Expand Up @@ -78,11 +78,11 @@ flowchart TD

## The five things you can do

MAX Finder gives you five ways to explore the same pool of free seats. Pick one from the tabs and fill in a station or two.
Railmap gives you five ways to explore the same pool of free seats. Pick one from the tabs and fill in a station or two.

```mermaid
flowchart TD
H["MAX Finder"] --> A["Where to?<br/>Pick a start,<br/>see everywhere you can go free"]
H["Railmap"] --> A["Where to?<br/>Pick a start,<br/>see everywhere you can go free"]
H --> B["Where from?<br/>Pick a destination,<br/>see every free way to reach it"]
H --> C["Exact trip<br/>One route in detail:<br/>connections plus a 30-day calendar"]
H --> D["Tour<br/>Chain several cities<br/>into one free itinerary"]
Expand Down Expand Up @@ -113,7 +113,7 @@ For when you have **no fixed plan**. Pick just a starting city and it lists the

## Your privacy

MAX Finder is private by design, because there's simply nowhere for your information to go.
Railmap is private by design, because there's simply nowhere for your information to go.

- Your **pass type, language, theme, favourite routes, saved trips, and watched routes** are stored only in your browser (in local storage on your own device). They never leave it. If you clear your browser data, they're gone — that's the only way they leave.
- Because there's no server and no account, there's nothing to log in to and no profile being built about you.
Expand Down
2 changes: 1 addition & 1 deletion docs/user-flows.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# User flows

The canonical map of what a user can do in MAX Finder and how each flow behaves. Keep
The canonical map of what a user can do in Railmap and how each flow behaves. Keep
this in sync with the app — see the rule in `AGENTS.md` ("Living documentation"): any
change to a user-facing flow must update this file in the same PR.

Expand Down
2 changes: 1 addition & 1 deletion fastlane/metadata/android/en-US/full_description.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ SENIOR (ex-TGVmax) seat is currently reservable — so you don't have to probe
station-by-station in SNCF Connect.

The hard part — which trains actually have a free MAX seat — is published by SNCF
as open data. MAX Finder is a frontend over that dataset: no backend, no account,
as open data. Railmap is a frontend over that dataset: no backend, no account,
no cost, no tracking. Your favorites, settings and searches live only on your
device.

Expand Down
2 changes: 1 addition & 1 deletion fastlane/metadata/android/en-US/title.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
MAX Finder
Railmap
54 changes: 27 additions & 27 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,68 +7,68 @@
http-equiv="Content-Security-Policy"
content="default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src 'self' https://ressources.data.sncf.com; worker-src 'self'; base-uri 'self'"
/>
<title>Trains MAX JEUNE / MAX SENIOR (ex-TGVmax) réservables — MAX Finder</title>
<title>Trains MAX JEUNE / MAX SENIOR (ex-TGVmax) réservables — Railmap</title>
<meta
name="description"
content="Trouvez les trains où une place TGV Max (MAX JEUNE ou MAX SENIOR) est réservable : trajet précis, carte des destinations, calendrier 30 jours, allers-retours. Open data SNCF, gratuit, open source, non affilié à la SNCF."
/>
<link rel="canonical" href="https://offware-apps.github.io/MAX-Finder/" />
<link rel="canonical" href="https://offware-apps.github.io/Railmap/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="MAX Finder" />
<meta property="og:title" content="MAX Finder — trouver un train TGVmax avec une place réservable" />
<meta property="og:site_name" content="Railmap" />
<meta property="og:title" content="Railmap — trouver un train TGVmax avec une place réservable" />
<meta
property="og:description"
content="Toutes les places MAX JEUNE / MAX SENIOR ouvertes à la réservation, mises à jour chaque jour depuis l'open data SNCF. Gratuit, sans compte."
/>
<meta property="og:url" content="https://offware-apps.github.io/MAX-Finder/" />
<meta property="og:image" content="https://offware-apps.github.io/MAX-Finder/icons/og.png" />
<meta property="og:url" content="https://offware-apps.github.io/Railmap/" />
<meta property="og:image" content="https://offware-apps.github.io/Railmap/icons/og.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:locale" content="fr_FR" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="MAX Finder — trouver un train TGVmax avec une place réservable" />
<meta name="twitter:title" content="Railmap — trouver un train TGVmax avec une place réservable" />
<meta
name="twitter:description"
content="Toutes les places MAX JEUNE / MAX SENIOR ouvertes à la réservation, mises à jour chaque jour depuis l'open data SNCF."
/>
<meta name="twitter:image" content="https://offware-apps.github.io/MAX-Finder/icons/og.png" />
<meta name="twitter:image" content="https://offware-apps.github.io/Railmap/icons/og.png" />
<link rel="manifest" href="manifest.webmanifest" />
<meta name="theme-color" content="#0f7a52" />
<link rel="icon" type="image/svg+xml" href="icons/icon.svg" />
<link rel="apple-touch-icon" href="icons/apple-touch-icon.png" />
<link rel="alternate" type="text/plain" href="llms.txt" title="LLM instructions" />
<link rel="alternate" type="application/json" href="api.json" title="MAX Finder machine API" />
<link rel="alternate" type="application/json" href="api.json" title="Railmap machine API" />
<link rel="preload" href="%BASE_URL%data/tgvmax.json" as="fetch" crossorigin />
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "WebSite",
"@id": "https://offware-apps.github.io/MAX-Finder/#website",
"url": "https://offware-apps.github.io/MAX-Finder/",
"name": "MAX Finder",
"alternateName": "MAX Finder TGVmax",
"@id": "https://offware-apps.github.io/Railmap/#website",
"url": "https://offware-apps.github.io/Railmap/",
"name": "Railmap",
"alternateName": ["MAX Finder", "Railmap TGVmax"],
"description": "Recherche de places TGV Max (MAX JEUNE / MAX SENIOR) ouvertes à la réservation, à partir de l'open data SNCF.",
"inLanguage": ["fr", "en", "es", "de", "it", "pt", "nl", "ko", "ja", "zh", "ar"]
},
{
"@type": "WebApplication",
"@id": "https://offware-apps.github.io/MAX-Finder/#app",
"isPartOf": { "@id": "https://offware-apps.github.io/MAX-Finder/#website" },
"name": "MAX Finder",
"url": "https://offware-apps.github.io/MAX-Finder/",
"@id": "https://offware-apps.github.io/Railmap/#app",
"isPartOf": { "@id": "https://offware-apps.github.io/Railmap/#website" },
"name": "Railmap",
"url": "https://offware-apps.github.io/Railmap/",
"applicationCategory": "TravelApplication",
"operatingSystem": "Any",
"browserRequirements": "Requires JavaScript",
"isAccessibleForFree": true,
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "EUR" },
"license": "https://github.com/offware-apps/MAX-Finder/blob/main/LICENSE",
"sameAs": ["https://github.com/offware-apps/MAX-Finder"],
"license": "https://github.com/offware-apps/Railmap/blob/main/LICENSE",
"sameAs": ["https://github.com/offware-apps/Railmap"],
"author": {
"@type": "Organization",
"name": "MAX Finder contributors",
"url": "https://github.com/offware-apps/MAX-Finder"
"name": "Railmap contributors",
"url": "https://github.com/offware-apps/Railmap"
},
"description": "Application gratuite et open source qui affiche les trains avec une place MAX JEUNE ou MAX SENIOR (ex-TGVmax) ouverte à la réservation. Projet indépendant, non affilié à la SNCF ; la réservation se fait sur SNCF Connect.",
"featureList": [
Expand All @@ -79,7 +79,7 @@
"Itinéraires multi-villes (tour)",
"Carte des gares, 11 langues, PWA hors-ligne"
],
"screenshot": "https://offware-apps.github.io/MAX-Finder/icons/og.png"
"screenshot": "https://offware-apps.github.io/Railmap/icons/og.png"
}
]
}
Expand All @@ -90,7 +90,7 @@
"@type": "Dataset",
"name": "Places MAX JEUNE / MAX SENIOR ouvertes à la réservation — snapshot quotidien",
"description": "Snapshot quotidien des places TGV Max (MAX JEUNE / MAX SENIOR) ouvertes à la réservation sur environ 30 jours glissants : date, gare d'origine, gare de destination, heures de départ et d'arrivée, numéro de train, disponibilité. Environ 32 000 enregistrements dérivés de l'open data SNCF (jeu de données tgvmax).",
"url": "https://offware-apps.github.io/MAX-Finder/",
"url": "https://offware-apps.github.io/Railmap/",
"isAccessibleForFree": true,
"license": "https://www.etalab.gouv.fr/licence-ouverte-open-licence",
"creator": { "@type": "Organization", "name": "SNCF" },
Expand All @@ -100,17 +100,17 @@
"distribution": [
{
"@type": "DataDownload",
"contentUrl": "https://offware-apps.github.io/MAX-Finder/data/tgvmax.json",
"contentUrl": "https://offware-apps.github.io/Railmap/data/tgvmax.json",
"encodingFormat": "application/json"
},
{
"@type": "DataDownload",
"contentUrl": "https://offware-apps.github.io/MAX-Finder/data/stations.json",
"contentUrl": "https://offware-apps.github.io/Railmap/data/stations.json",
"encodingFormat": "application/json"
},
{
"@type": "DataDownload",
"contentUrl": "https://offware-apps.github.io/MAX-Finder/data/meta.json",
"contentUrl": "https://offware-apps.github.io/Railmap/data/meta.json",
"encodingFormat": "application/json"
}
]
Expand All @@ -122,7 +122,7 @@
<body>
<noscript>
<p>
MAX Finder est une application de recherche de places TGV Max (MAX JEUNE / MAX SENIOR) ouvertes à la
Railmap est une application de recherche de places TGV Max (MAX JEUNE / MAX SENIOR) ouvertes à la
réservation, basée sur l'open data SNCF. La recherche interactive nécessite JavaScript ; la présentation
complète de l'outil se trouve ci-dessous.
</p>
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "max-finder",
"name": "railmap",
"version": "0.2.0",
"private": true,
"description": "FOSS, serverless finder for free MAX JEUNE / MAX SENIOR (ex-TGVmax) seats, built on SNCF Open Data.",
Expand Down
10 changes: 5 additions & 5 deletions public/404.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
http-equiv="Content-Security-Policy"
content="default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self'; base-uri 'self'"
/>
<title>Page introuvable — MAX Finder</title>
<title>Page introuvable — Railmap</title>
<meta name="robots" content="noindex" />
<!-- Static routes (e.g. /trajets/…) are regenerated from the daily dataset, so a
previously-indexed or mistyped URL can 404. Send visitors back to the app
instead of a dead end. Meta-refresh (no JS) keeps this CSP-safe. -->
<meta http-equiv="refresh" content="3; url=/MAX-Finder/" />
<link rel="icon" type="image/svg+xml" href="/MAX-Finder/icons/icon.svg" />
<meta http-equiv="refresh" content="3; url=/Railmap/" />
<link rel="icon" type="image/svg+xml" href="/Railmap/icons/icon.svg" />
<meta name="theme-color" content="#0f7a52" />
<style>
:root {
Expand Down Expand Up @@ -87,8 +87,8 @@ <h1>Page introuvable</h1>
Cette page n'existe pas ou a été mise à jour. Les trajets sont régénérés chaque jour à partir de l'open
data SNCF, donc un lien ancien peut ne plus être valide.
</p>
<p>Redirection vers MAX Finder…</p>
<a class="cta" href="/MAX-Finder/">Ouvrir MAX Finder</a>
<p>Redirection vers Railmap…</p>
<a class="cta" href="/Railmap/">Ouvrir Railmap</a>
</main>
</body>
</html>
Loading
Loading