feat: Add packages/site — landing vitrine (Vue 3 + Tailwind 4)
- Scaffold packages/site with 11 Vue components: hero (animated terminal), features, showcase, work groups, how-it-works, security, FAQ accordion, footer - i18n EN/FR via vue-i18n with 82+ keys, autodetect navigator.language, toggle without reload - Animated timeline (terminal, dialog, toast) ported from design, scroll-reveal, copy buttons (2 instances) - Self-hosted JetBrains Mono fonts (@fontsource), optimized assets (mark 28KB, og-cover 1200×630) - SEO: canonical, og:image, theme-color, robots.txt, sitemap.xml for git-arboretum.com - Monorepo integration (decoupled from npm release): build:site / dev:site / preview:site scripts - CI: add build:site step to ci.yml for early detection; create .gitea/workflows/prod.yml for FTPS deploy (lftp mirror) to git-arboretum.com - Validated builds ✓ and UI rendering (EN/FR headless test) ✓
This commit is contained in:
60
packages/site/src/i18n/content.ts
Normal file
60
packages/site/src/i18n/content.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
// Listes structurées (FAQ, options de dialogue) — gardées hors des messages
|
||||
// vue-i18n et indexées par locale, pour rester du texte pur typé (pas d'innerHTML).
|
||||
import type { AppLocale } from './index';
|
||||
|
||||
export interface FaqItem {
|
||||
q: string;
|
||||
a: string;
|
||||
}
|
||||
|
||||
export const FAQS: Record<AppLocale, FaqItem[]> = {
|
||||
en: [
|
||||
{
|
||||
q: 'Is Arboretum an IDE?',
|
||||
a: "No — it's a command center. It doesn't replace your editor; it supervises the AI coding agents working across your repos: spawning sessions, surfacing their state, and letting you answer them.",
|
||||
},
|
||||
{
|
||||
q: 'Does it need the cloud?',
|
||||
a: 'No. Arboretum is local-first. The daemon runs on your own machine and binds to 127.0.0.1 by default. Your code and sessions never leave your network unless you choose to expose the dashboard.',
|
||||
},
|
||||
{
|
||||
q: 'How do I access it remotely?',
|
||||
a: 'Through Tailscale Serve. Expose the dashboard to your tailnet and reach it securely from any device — no port forwarding, no public endpoint.',
|
||||
},
|
||||
{
|
||||
q: 'Does it work on mobile?',
|
||||
a: 'Yes. The dashboard is an installable PWA. Add it to your home screen, get Web Push the moment a session needs you, and approve or reject a prompt with a single tap.',
|
||||
},
|
||||
{
|
||||
q: 'Is my code safe?',
|
||||
a: 'Arboretum binds to localhost, hashes access tokens at rest, and enforces strict origin checks on every request. Remote access runs through Tailscale rather than an open port.',
|
||||
},
|
||||
],
|
||||
fr: [
|
||||
{
|
||||
q: 'Arboretum est-il un IDE ?',
|
||||
a: "Non — c'est un poste de commandement. Il ne remplace pas votre éditeur ; il supervise les agents de code IA qui travaillent sur vos repos : lancement des sessions, affichage de leur état, et réponse à leurs questions.",
|
||||
},
|
||||
{
|
||||
q: 'A-t-il besoin du cloud ?',
|
||||
a: 'Non. Arboretum est local-first. Le daemon tourne sur votre propre machine et se lie à 127.0.0.1 par défaut. Votre code et vos sessions ne quittent jamais votre réseau sauf si vous exposez le dashboard.',
|
||||
},
|
||||
{
|
||||
q: 'Comment y accéder à distance ?',
|
||||
a: "Via Tailscale Serve. Exposez le dashboard à votre tailnet et accédez-y en sécurité depuis n'importe quel appareil — sans redirection de port ni endpoint public.",
|
||||
},
|
||||
{
|
||||
q: 'Fonctionne-t-il sur mobile ?',
|
||||
a: "Oui. Le dashboard est une PWA installable. Ajoutez-le à l'écran d'accueil, recevez un Web Push dès qu'une session a besoin de vous, et approuvez ou refusez une question d'un toucher.",
|
||||
},
|
||||
{
|
||||
q: 'Mon code est-il en sécurité ?',
|
||||
a: "Arboretum se lie à localhost, hashe les tokens au repos, et applique une vérification d'origine stricte sur chaque requête. L'accès distant passe par Tailscale plutôt qu'un port ouvert.",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const DLG_OPTS: Record<AppLocale, string[]> = {
|
||||
en: ['Yes, apply the change', "Yes, and don't ask again", 'No, keep current version'],
|
||||
fr: ['Oui, appliquer le changement', 'Oui, et ne plus demander', 'Non, garder la version actuelle'],
|
||||
};
|
||||
93
packages/site/src/i18n/en.ts
Normal file
93
packages/site/src/i18n/en.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
// Messages EN — copie fidèle du dict() du design Arboretum.dc.html.
|
||||
export default {
|
||||
navFeatures: 'Features',
|
||||
navHow: 'How it works',
|
||||
navSecurity: 'Security',
|
||||
navFaq: 'FAQ',
|
||||
gitea: 'View on Gitea',
|
||||
heroBadge: 'Mission control for AI coding agents',
|
||||
heroTitle: 'Mission control for your AI coding agents',
|
||||
heroSub:
|
||||
'Run many Claude Code sessions across every git worktree — and supervise them from any device, even your phone.',
|
||||
getStarted: 'Get started',
|
||||
probKicker: 'The problem',
|
||||
probTitle: "Many agents across worktrees is chaos — and you're chained to one terminal.",
|
||||
probBody:
|
||||
'Sessions scattered across tabs. No idea which agent is waiting on you. Step away from the desk and everything stalls. Arboretum is the single pane of glass.',
|
||||
featKicker: 'Everything in one view',
|
||||
featTitle: 'Built to supervise agents — not to be an IDE',
|
||||
feat1Title: 'Worktree-first',
|
||||
feat1Desc: 'Every session is pinned to a git worktree. You see branches, not chaos.',
|
||||
feat2Title: 'Many sessions, one view',
|
||||
feat2Desc: 'Run parallel Claude Code agents side by side in a single live grid.',
|
||||
feat3Title: 'Fine-grained states',
|
||||
feat3Desc: 'tell at a glance what each agent is doing.',
|
||||
feat4Title: 'Supervise anywhere',
|
||||
feat4Desc: 'Installable PWA. Check in from your laptop, tablet, or phone.',
|
||||
feat5Title: 'Web Push alerts',
|
||||
feat5Desc: 'Get pinged the moment a session needs a decision from you.',
|
||||
feat6Title: 'Answer without a terminal',
|
||||
feat6Desc: 'Respond to agent prompts and dialogs right from the dashboard.',
|
||||
feat7Title: 'Work groups',
|
||||
feat7Desc: 'Group repos and launch the same feature across all of them at once.',
|
||||
feat8Title: 'Local-first & secure',
|
||||
feat8Desc: 'Binds to localhost, hashes tokens, remote access via Tailscale.',
|
||||
scAKicker: 'Worktree-first dashboard',
|
||||
scATitle: 'See what needs you — before anything stalls',
|
||||
scABody:
|
||||
'A live list of every worktree and its session state. The Needs attention rail floats the agents waiting on a decision to the top, so nothing sits blocked while you are heads-down elsewhere.',
|
||||
scBKicker: 'Multi-terminal grid',
|
||||
scBTitle: "Every agent's output, streaming live",
|
||||
scBBody:
|
||||
'Watch all your sessions at once — real terminal output, color-coded by state, updating in real time. Click any pane to take over, answer a prompt, or stop a run.',
|
||||
scCKicker: 'On your phone',
|
||||
scCTitle: 'Unblock an agent from the kitchen',
|
||||
scCBody:
|
||||
'Install the PWA and get a Web Push the second a session needs you. Tap the notification, read the prompt, answer the dialog — your agent keeps moving while you are away.',
|
||||
grpKicker: 'Work groups · cross-repo',
|
||||
grpTitle: 'Ship one feature across every repo at once',
|
||||
grpBody:
|
||||
'Group the repos that move together — a service, its client, its docs. Launch the same task to all of them and Arboretum spins up a worktree and an agent in each, then tracks them as one unit.',
|
||||
howKicker: 'How it works',
|
||||
howTitle: 'Zero install. Three steps.',
|
||||
step1Title: 'Launch the daemon',
|
||||
step1Desc: 'One command. No install, no config, no account.',
|
||||
step2Title: 'Open the dashboard',
|
||||
step2Desc: 'It serves a web dashboard on localhost. Install it as a PWA on any device.',
|
||||
step3Title: 'Spawn & supervise',
|
||||
step3Desc: 'Start Claude Code sessions on any worktree and watch them from anywhere.',
|
||||
secKicker: 'Security',
|
||||
secTitle: 'A web terminal you can actually trust',
|
||||
secIntro: 'Built local-first. Nothing is exposed unless you choose to — and even then, on your terms.',
|
||||
sec1Title: 'Localhost by default',
|
||||
sec1Desc: 'Binds to 127.0.0.1. Nothing leaves your machine unless you say so.',
|
||||
sec2Title: 'Hashed tokens',
|
||||
sec2Desc: 'Access tokens are hashed (sha256) at rest. No plaintext secrets on disk.',
|
||||
sec3Title: 'Strict origin checks',
|
||||
sec3Desc: 'Every request is origin-validated. No cross-site surprises.',
|
||||
sec4Title: 'Remote via Tailscale',
|
||||
sec4Desc: 'Reach it over Tailscale Serve — no public port, no port forwarding.',
|
||||
faqTitle: 'Questions, answered',
|
||||
ctaTitle: 'Take command of your agents',
|
||||
ctaBody: 'One command to launch. Supervise everything from one place.',
|
||||
ctaSource: 'View the source on Gitea',
|
||||
footTag: 'a garden of branches, one pane of glass.',
|
||||
license: 'MIT License',
|
||||
coffee: 'Buy me a coffee',
|
||||
mDash: 'Dashboard',
|
||||
mGroups: 'Groups',
|
||||
mSettings: 'Settings',
|
||||
mHelp: 'Help',
|
||||
mSearch: 'Search',
|
||||
mAttn: 'Needs attention',
|
||||
waiting: 'waiting',
|
||||
busy: 'busy',
|
||||
idle: 'idle',
|
||||
approve: 'Approve',
|
||||
reject: 'Reject',
|
||||
toastTitle: 'Session waiting for you',
|
||||
toastBody: 'api · feat/auth needs a decision',
|
||||
dlgQ: 'Apply this change to login.ts?',
|
||||
copy: 'Copy',
|
||||
copied: 'Copied',
|
||||
};
|
||||
93
packages/site/src/i18n/fr.ts
Normal file
93
packages/site/src/i18n/fr.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
// Messages FR — copie fidèle du dict() du design Arboretum.dc.html.
|
||||
export default {
|
||||
navFeatures: 'Fonctionnalités',
|
||||
navHow: 'Comment ça marche',
|
||||
navSecurity: 'Sécurité',
|
||||
navFaq: 'FAQ',
|
||||
gitea: 'Voir sur Gitea',
|
||||
heroBadge: 'Poste de commandement pour agents de code IA',
|
||||
heroTitle: 'Le poste de commandement de vos agents de code IA',
|
||||
heroSub:
|
||||
"Lancez plusieurs sessions Claude Code à travers chaque worktree git — et supervisez-les depuis n'importe quel appareil, même votre téléphone.",
|
||||
getStarted: 'Commencer',
|
||||
probKicker: 'Le problème',
|
||||
probTitle: "Plein d'agents à travers les worktrees, c'est le chaos — et vous êtes cloué à un seul terminal.",
|
||||
probBody:
|
||||
"Des sessions éparpillées dans des onglets. Aucune idée de quel agent vous attend. Vous quittez le bureau et tout se fige. Arboretum, c'est la vitre unique.",
|
||||
featKicker: 'Tout dans une seule vue',
|
||||
featTitle: 'Conçu pour superviser les agents — pas pour être un IDE',
|
||||
feat1Title: "Worktree d'abord",
|
||||
feat1Desc: 'Chaque session est rattachée à un worktree git. Vous voyez des branches, pas du chaos.',
|
||||
feat2Title: 'Plusieurs sessions, une vue',
|
||||
feat2Desc: 'Lancez des agents Claude Code en parallèle, côte à côte dans une seule grille en direct.',
|
||||
feat3Title: 'États fins',
|
||||
feat3Desc: "sachez d'un coup d'œil ce que fait chaque agent.",
|
||||
feat4Title: 'Supervisez partout',
|
||||
feat4Desc: "PWA installable. Surveillez depuis l'ordinateur, la tablette ou le téléphone.",
|
||||
feat5Title: 'Alertes Web Push',
|
||||
feat5Desc: "Soyez prévenu dès qu'une session attend une décision.",
|
||||
feat6Title: 'Répondre sans terminal',
|
||||
feat6Desc: 'Répondez aux questions et dialogues des agents directement depuis le dashboard.',
|
||||
feat7Title: 'Work groups',
|
||||
feat7Desc: 'Regroupez des repos et lancez la même feature dans tous à la fois.',
|
||||
feat8Title: 'Local-first & sécurisé',
|
||||
feat8Desc: 'Se lie à localhost, hashe les tokens, accès distant via Tailscale.',
|
||||
scAKicker: 'Dashboard worktree-first',
|
||||
scATitle: 'Voyez ce qui vous attend — avant que ça ne bloque',
|
||||
scABody:
|
||||
"Une liste en direct de chaque worktree et de l'état de sa session. Le bandeau À traiter fait remonter les agents en attente d'une décision, pour que rien ne reste bloqué pendant que vous êtes concentré ailleurs.",
|
||||
scBKicker: 'Grille multi-terminaux',
|
||||
scBTitle: 'La sortie de chaque agent, en direct',
|
||||
scBBody:
|
||||
"Surveillez toutes vos sessions en même temps — vraie sortie terminal, couleur selon l'état, mise à jour en temps réel. Cliquez sur un panneau pour reprendre la main, répondre, ou arrêter une exécution.",
|
||||
scCKicker: 'Sur votre téléphone',
|
||||
scCTitle: 'Débloquez un agent depuis la cuisine',
|
||||
scCBody:
|
||||
"Installez la PWA et recevez un Web Push dès qu'une session a besoin de vous. Touchez la notification, lisez la question, répondez au dialogue — votre agent continue pendant que vous êtes loin du bureau.",
|
||||
grpKicker: 'Work groups · cross-repo',
|
||||
grpTitle: "Livrez une feature dans tous les repos d'un coup",
|
||||
grpBody:
|
||||
'Regroupez les repos qui avancent ensemble — un service, son client, sa doc. Lancez la même tâche à tous ; Arboretum crée un worktree et un agent dans chacun, puis les suit comme un seul ensemble.',
|
||||
howKicker: 'Comment ça marche',
|
||||
howTitle: 'Zéro install. Trois étapes.',
|
||||
step1Title: 'Lancez le daemon',
|
||||
step1Desc: "Une commande. Pas d'install, pas de config, pas de compte.",
|
||||
step2Title: 'Ouvrez le dashboard',
|
||||
step2Desc: "Il sert un dashboard web sur localhost. Installez-le en PWA sur n'importe quel appareil.",
|
||||
step3Title: 'Lancez & supervisez',
|
||||
step3Desc: "Démarrez des sessions Claude Code sur n'importe quel worktree et surveillez-les de partout.",
|
||||
secKicker: 'Sécurité',
|
||||
secTitle: 'Un terminal web en qui vous pouvez avoir confiance',
|
||||
secIntro: "Conçu local-first. Rien n'est exposé sauf si vous le décidez — et toujours selon vos règles.",
|
||||
sec1Title: 'Localhost par défaut',
|
||||
sec1Desc: 'Se lie à 127.0.0.1. Rien ne quitte votre machine sans votre accord.',
|
||||
sec2Title: 'Tokens hashés',
|
||||
sec2Desc: 'Les tokens sont hashés (sha256) au repos. Aucun secret en clair sur le disque.',
|
||||
sec3Title: "Vérification d'origine stricte",
|
||||
sec3Desc: 'Chaque requête est validée par origine. Pas de surprise cross-site.',
|
||||
sec4Title: 'Distant via Tailscale',
|
||||
sec4Desc: 'Accédez-y via Tailscale Serve — aucun port public, aucune redirection de port.',
|
||||
faqTitle: 'Vos questions, nos réponses',
|
||||
ctaTitle: 'Prenez le commandement de vos agents',
|
||||
ctaBody: 'Une commande pour lancer. Tout superviser depuis un seul endroit.',
|
||||
ctaSource: 'Voir le code sur Gitea',
|
||||
footTag: 'un jardin de branches, une seule vitre.',
|
||||
license: 'Licence MIT',
|
||||
coffee: 'Paye-moi un café',
|
||||
mDash: 'Tableau de bord',
|
||||
mGroups: 'Groupes',
|
||||
mSettings: 'Réglages',
|
||||
mHelp: 'Aide',
|
||||
mSearch: 'Rechercher',
|
||||
mAttn: 'À traiter',
|
||||
waiting: 'en attente',
|
||||
busy: 'occupé',
|
||||
idle: 'au repos',
|
||||
approve: 'Approuver',
|
||||
reject: 'Refuser',
|
||||
toastTitle: 'Une session vous attend',
|
||||
toastBody: 'api · feat/auth attend une décision',
|
||||
dlgQ: 'Appliquer ce changement à login.ts ?',
|
||||
copy: 'Copier',
|
||||
copied: 'Copié',
|
||||
};
|
||||
37
packages/site/src/i18n/index.ts
Normal file
37
packages/site/src/i18n/index.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { createI18n } from 'vue-i18n';
|
||||
import en from './en';
|
||||
import fr from './fr';
|
||||
|
||||
export type AppLocale = 'en' | 'fr';
|
||||
|
||||
const STORAGE_KEY = 'arb-lang';
|
||||
|
||||
// Détection fidèle au design : localStorage prioritaire, sinon langue navigateur
|
||||
// (fr → FR, sinon EN).
|
||||
function initialLocale(): AppLocale {
|
||||
let saved: string | null = null;
|
||||
try {
|
||||
saved = localStorage.getItem(STORAGE_KEY);
|
||||
} catch {
|
||||
/* localStorage indisponible (mode privé, etc.) */
|
||||
}
|
||||
if (saved === 'fr' || saved === 'en') return saved;
|
||||
const nav = (typeof navigator !== 'undefined' && navigator.language ? navigator.language : '').toLowerCase();
|
||||
return nav.indexOf('fr') === 0 ? 'fr' : 'en';
|
||||
}
|
||||
|
||||
export const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: initialLocale(),
|
||||
fallbackLocale: 'en',
|
||||
messages: { en, fr },
|
||||
});
|
||||
|
||||
export function setLocale(locale: AppLocale): void {
|
||||
i18n.global.locale.value = locale;
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, locale);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user