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 .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copy to ".env" at the repository root (Docker Compose and local tooling load it from here).
# Django also loads this file via backend/setrsoft/settings.py (repo root).

#Docker
#Docker (for production)
DOCKER_NETWORK_NAME=default

# --- PORTS & IP ---
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

SetRsoft is an open source community driven software. Test it live on the website http://www.setrsoft.com/

For better understanding the purpose of this project you can have a look at [one of those videos](https://www.youtube.com/results?search_query=routesetting+in+climbing+gym).

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spelling/grammar: "puropose" is misspelled, and the sentence reads ungrammatically ("have a look to"). Consider updating to "For a better understanding of the purpose of this project, you can have a look at one of these videos ...".

Suggested change
For better understanding the purpose of this project you can have a look at [one of those videos](https://www.youtube.com/results?search_query=routesetting+in+climbing+gym).
For a better understanding of the purpose of this project, you can have a look at [one of those videos](https://www.youtube.com/results?search_query=routesetting+in+climbing+gym).

Copilot uses AI. Check for mistakes.

Feel free to create new Github Issues for new features or signal bugs.


Expand Down
10 changes: 9 additions & 1 deletion frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
<title>setrsoft</title>
<title>SetRsoft – Éditeur 3D de blocs d'escalade bouldering</title>
<meta name="description" content="SetRsoft est un éditeur 3D en ligne pour créer des blocs et voies d'escalade bouldering. Routesetting virtuel, simulation 3D, partage de séquences." />
<meta name="keywords" content="éditeur 3D escalade, bouldering editor, routesetting en ligne, créer voie escalade, virtual climbing wall, bloc escalade 3D" />
<meta property="og:title" content="SetRsoft – Éditeur 3D escalade bouldering" />
<meta property="og:description" content="Créez et partagez vos blocs d'escalade en 3D. Éditeur virtuel bouldering en ligne." />
Comment on lines +11 to +15

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These default SEO tags in index.html are hard-coded in French, but the app is configured for multiple locales (i18next with fallbackLng=en). If you expect non-FR users/SEO, consider making the default metadata language-neutral/English, or add a prerender/SSR strategy so metadata can be emitted per-locale rather than relying on a single static index.html.

Suggested change
<title>SetRsoft – Éditeur 3D de blocs d'escalade bouldering</title>
<meta name="description" content="SetRsoft est un éditeur 3D en ligne pour créer des blocs et voies d'escalade bouldering. Routesetting virtuel, simulation 3D, partage de séquences." />
<meta name="keywords" content="éditeur 3D escalade, bouldering editor, routesetting en ligne, créer voie escalade, virtual climbing wall, bloc escalade 3D" />
<meta property="og:title" content="SetRsoft – Éditeur 3D escalade bouldering" />
<meta property="og:description" content="Créez et partagez vos blocs d'escalade en 3D. Éditeur virtuel bouldering en ligne." />
<title>SetRsoft – 3D bouldering route editor</title>
<meta name="description" content="SetRsoft is an online 3D editor for creating bouldering problems and climbing routes. Virtual routesetting, 3D simulation, and sequence sharing." />
<meta name="keywords" content="3D climbing editor, bouldering editor, online routesetting, create climbing route, virtual climbing wall, 3D bouldering" />
<meta property="og:title" content="SetRsoft – 3D bouldering route editor" />
<meta property="og:description" content="Create and share your 3D climbing problems online with a virtual bouldering editor." />

Copilot uses AI. Check for mistakes.
<meta property="og:type" content="website" />
<meta property="og:url" content="https://setrsoft.com" />
<meta name="twitter:card" content="summary_large_image" />
<link rel="canonical" href="https://setrsoft.com" />
</head>
<body>
<div id="root"></div>
Expand Down
49 changes: 48 additions & 1 deletion frontend/package-lock.json

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

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"posthog-js": "^1.364.7",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-helmet-async": "^2.0.1",
"react-i18next": "^16.6.0",
"react-router-dom": "^7.13.1",
"three": "^0.177.0",
Expand Down
6 changes: 6 additions & 0 deletions frontend/public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
User-agent: *
Allow: /
Disallow: /editor/
Disallow: /gym/

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disallow: /gym/ will not match the /gym route (no trailing slash) used by the router, so crawlers may still index the gym dashboard. Consider changing this to Disallow: /gym (and similarly decide whether /editor itself should be blocked in addition to /editor/*).

Suggested change
Disallow: /gym/
Disallow: /gym

Copilot uses AI. Check for mistakes.

Sitemap: https://setrsoft.com/sitemap.xml
18 changes: 18 additions & 0 deletions frontend/public/sitemap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://setrsoft.com/</loc>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
Comment on lines +1 to +6

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR title suggests this change only adds a sitemap, but this PR also introduces runtime SEO metadata via react-helmet-async (HelmetProvider + per-page meta) and updates robots.txt/README/links. Consider updating the PR title/description to reflect the broader SEO scope so reviewers know what to focus on.

Copilot uses AI. Check for mistakes.
</url>
<url>
<loc>https://setrsoft.com/partners</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://setrsoft.com/contact</loc>
<changefreq>yearly</changefreq>
<priority>0.5</priority>
</url>
</urlset>
4 changes: 2 additions & 2 deletions frontend/src/features/editor/EditorApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function EditorApp() {
posthog.capture('editor session opened', { wall_id: wallId, session_id: session_data.id });
}
}
}, [session_data?.id, wallModels.length, handleLoad]);
}, [session_data?.id, wallId, wallModels.length, handleLoad]);

useEffect(() => {
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
Expand Down Expand Up @@ -152,7 +152,7 @@ function EditorApp() {
))}
</div>
</div>

<MainCanvas wallModels={wallModels} />
<HoldInspector />
<Tutorial />
Expand Down
25 changes: 16 additions & 9 deletions frontend/src/features/showcase/ContactPage.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { useTranslation, Trans } from 'react-i18next';
import { Helmet } from 'react-helmet-async';

const EMAIL = 'contact@setrsoft.com';

export function ContactPage() {
const { t } = useTranslation();

return (
<div className="flex flex-col items-center justify-center py-24 px-4 text-center animate-fade-in">
<h1 className="text-3xl font-bold text-white mb-6">{t('contact.title')}</h1>
<p className="text-on-surface-variant text-lg">
<Trans
i18nKey="contact.body"
components={{ emailLink: <a href={`mailto:${EMAIL}`} className="text-mint hover:underline" /> }}
/>
</p>
</div>
<>
<Helmet>
<title>Contact – SetRsoft</title>
<meta name="description" content="Contactez l'équipe SetRsoft pour intégrer l'éditeur 3D dans votre salle d'escalade bouldering." />
Comment on lines +12 to +13

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The SEO metadata is hardcoded in French. It should be localized using t() to support the application's multilingual capabilities.

Comment on lines +12 to +13

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The page <title> and meta description are hard-coded in French even though the UI text uses i18next translations. To avoid incorrect language metadata for non-FR users/crawlers, consider using translated values (new i18n keys per locale) instead of hard-coded strings.

Suggested change
<title>Contact – SetRsoft</title>
<meta name="description" content="Contactez l'équipe SetRsoft pour intégrer l'éditeur 3D dans votre salle d'escalade bouldering." />
<title>{t('contact.metaTitle')}</title>
<meta name="description" content={t('contact.metaDescription')} />

Copilot uses AI. Check for mistakes.
</Helmet>
<div className="flex flex-col items-center justify-center py-24 px-4 text-center animate-fade-in">
<h1 className="text-3xl font-bold text-white mb-6">{t('contact.title')}</h1>
<p className="text-on-surface-variant text-lg">
<Trans
i18nKey="contact.body"
components={{ emailLink: <a href={`mailto:${EMAIL}`} className="text-mint hover:underline" /> }}
/>
</p>
</div>
</>
);
}
19 changes: 18 additions & 1 deletion frontend/src/features/showcase/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { Helmet } from 'react-helmet-async';
import { ROUTES } from '@/core/config';
import { posthog } from '@/shared/analytics/posthog';
import demoVideo from '@/assets/demo-CISe8V-c.mp4';
Expand Down Expand Up @@ -38,6 +39,21 @@ export function HomePage() {
}, []);

return (
<>
<Helmet>
<title>SetRsoft – Éditeur 3D Bouldering en ligne</title>
<meta name="description" content="Éditeur 3D virtuel pour créer vos blocs d'escalade bouldering. Simulation, routesetting, partage de séquences." />
<script type="application/ld+json">{JSON.stringify({
Comment on lines +43 to +46

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The page <title> and meta description are hard-coded in French, while the rest of the page is localized via i18next. This will show French metadata even when the active language is English/DE/etc; consider sourcing these values from translation keys (e.g., t('seo.home.title'), t('seo.home.description')) and adding those keys to each locale JSON.

Copilot uses AI. Check for mistakes.
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "SetRsoft",
"applicationCategory": "SportsApplication",
"operatingSystem": "Web",
"description": "Éditeur 3D de blocs d'escalade bouldering en ligne. Routesetting virtuel.",
"url": "https://setrsoft.com",
"offers": { "@type": "Offer", "price": "0" }
})}</script>
Comment on lines +44 to +55

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The SEO title, description, and JSON-LD structured data are hardcoded in French. Since the project uses react-i18next, these strings should be localized using the t() function to ensure the metadata matches the user's selected language.

Comment on lines +46 to +55

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This structured-data <script> is created inline with JSON.stringify(...) on every render. Consider hoisting the JSON-LD object/string outside the component or memoizing it to avoid unnecessary work during re-renders (especially if the component state updates).

Copilot uses AI. Check for mistakes.
</Helmet>
<div className="flex flex-col gap-24 py-12 md:py-20 animate-fade-in">

{/* Hero Section */}
Expand All @@ -60,7 +76,7 @@ export function HomePage() {

<div className="flex flex-col sm:flex-row gap-4 mt-4">
<a
href="https://setrsoft.github.io/holds-dataset-hub/"
href="https://dataset.setrsoft.com/"
target="_blank"
rel="noopener noreferrer"
className="bg-surface-high text-white font-medium px-6 py-2.5 rounded-sm hover:bg-surface-lowest transition-colors border border-transparent hover:border-ghost-border/50 text-sm"
Expand Down Expand Up @@ -174,5 +190,6 @@ export function HomePage() {
</section>

</div>
</>
);
}
25 changes: 16 additions & 9 deletions frontend/src/features/showcase/PartnersPage.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { useTranslation, Trans } from 'react-i18next';
import { Helmet } from 'react-helmet-async';

const EMAIL = 'contact@setrsoft.com';

export function PartnersPage() {
const { t } = useTranslation();

return (
<div className="flex flex-col items-center justify-center py-24 px-4 text-center animate-fade-in">
<h1 className="text-3xl font-bold text-white mb-6">{t('partners.title')}</h1>
<p className="text-on-surface-variant max-w-xl text-lg leading-relaxed">
<Trans
i18nKey="partners.body"
components={{ emailLink: <a href={`mailto:${EMAIL}`} className="text-mint hover:underline" /> }}
/>
</p>
</div>
<>
<Helmet>
<title>Partenaires – SetRsoft</title>
<meta name="description" content="Salles d'escalade partenaires de SetRsoft, logiciel de routesetting 3D bouldering." />
Comment on lines +12 to +13

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The SEO metadata is hardcoded in French. It should be localized using t() to support the application's multilingual capabilities.

</Helmet>
Comment on lines +11 to +14

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The page <title> and meta description are hard-coded in French even though the UI text uses i18next translations. To avoid incorrect language metadata for non-FR users/crawlers, consider using translated values (new i18n keys per locale) instead of hard-coded strings.

Copilot uses AI. Check for mistakes.
<div className="flex flex-col items-center justify-center py-24 px-4 text-center animate-fade-in">
<h1 className="text-3xl font-bold text-white mb-6">{t('partners.title')}</h1>
<p className="text-on-surface-variant max-w-xl text-lg leading-relaxed">
<Trans
i18nKey="partners.body"
components={{ emailLink: <a href={`mailto:${EMAIL}`} className="text-mint hover:underline" /> }}
/>
</p>
</div>
</>
);
}
13 changes: 8 additions & 5 deletions frontend/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { HelmetProvider } from 'react-helmet-async';
import { AuthProvider } from '@/shared/auth';
import { App } from '@/app/App';
import './index.css';
Expand All @@ -11,10 +12,12 @@ const queryClient = new QueryClient();

createRoot(document.getElementById('root')!).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<AuthProvider>
<App />
</AuthProvider>
</QueryClientProvider>
<HelmetProvider>
<QueryClientProvider client={queryClient}>
<AuthProvider>
<App />
</AuthProvider>
</QueryClientProvider>
</HelmetProvider>
</StrictMode>
);
Loading