feat(site): vitrine recadrée IDE multi-projet + section Download desktop (D2)
Some checks failed
CI / Build & test (Node 22) (push) Successful in 10m11s
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
CI / Build & test (Node 24) (push) Has been cancelled
Deploy site (production) / build-and-deploy (push) Has been cancelled

i18n EN/FR (miroir) : hero, section « workspace » et carte feature 11 recadrés vers l'IDE multi-projet (arbre unique, onglets, dock bas), + clés de la section Download. Nouvelle section DownloadSection.vue (app de bureau : Linux/Windows/macOS, runtime embarqué, auto-update ; boutons vers la page des releases et le build depuis les sources) + entrée de nav « Download ».

Mockup WorkspaceShowcase redessiné sur le nouvel IDE : barre d'activité, arbre unifié multi-projet (api/web + worktree + session), éditeur à onglets, diff, dock terminaux à onglets, barre de statut. build:site vert, 0 tiret.
This commit is contained in:
2026-07-17 18:14:39 +02:00
parent 0532331795
commit ef21b03d9c
7 changed files with 242 additions and 186 deletions

View File

@@ -7,6 +7,7 @@ import ProblemSection from './components/ProblemSection.vue';
import FeaturesSection from './components/FeaturesSection.vue';
import ShowcaseSection from './components/ShowcaseSection.vue';
import WorkspaceShowcase from './components/WorkspaceShowcase.vue';
import DownloadSection from './components/DownloadSection.vue';
import RemoteGitSection from './components/RemoteGitSection.vue';
import WorkGroupsSection from './components/WorkGroupsSection.vue';
import HowItWorksSection from './components/HowItWorksSection.vue';
@@ -65,6 +66,7 @@ const glowStyle = {
<FeaturesSection />
<ShowcaseSection />
<WorkspaceShowcase />
<DownloadSection />
<RemoteGitSection />
<WorkGroupsSection />
<HowItWorksSection />

View File

@@ -9,6 +9,7 @@ const { t } = useI18n();
const navLinks = [
{ href: '#features', key: 'navFeatures' },
{ href: '#workspace', key: 'navWorkspace' },
{ href: '#download', key: 'navDownload' },
{ href: '#how', key: 'navHow' },
{ href: '#security', key: 'navSecurity' },
{ href: '#faq', key: 'navFaq' },

View File

@@ -0,0 +1,68 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import { RELEASES, DESKTOP_SRC } from '../lib/links';
const { t } = useI18n();
const platforms = [
{ key: 'dlLinux', hint: 'dlLinuxHint' },
{ key: 'dlWin', hint: 'dlWinHint' },
{ key: 'dlMac', hint: 'dlMacHint' },
] as const;
const bullets = ['dlNoNode', 'dlBundled', 'dlAutoUpdate'] as const;
</script>
<template>
<section id="download" class="mx-auto max-w-[1200px] scroll-mt-[84px] px-6 pb-[100px]">
<div v-reveal class="mb-[40px] max-w-[720px]">
<div class="mb-3 font-mono text-xs uppercase tracking-[0.12em] text-emerald-400">{{ t('dlKicker') }}</div>
<h2 class="m-0 mb-4 text-[clamp(26px,3vw,38px)] font-semibold leading-[1.15] tracking-[-0.025em] text-zinc-50">
{{ t('dlTitle') }}
</h2>
<p class="m-0 text-[16.5px] leading-[1.6] text-zinc-400">{{ t('dlBody') }}</p>
</div>
<div v-reveal class="grid gap-4 md:grid-cols-3">
<div
v-for="p in platforms"
:key="p.key"
class="flex flex-col gap-1 rounded-[14px] border border-zinc-800 bg-[#09090b] p-5 shadow-card"
>
<div class="flex items-center gap-2 text-zinc-100">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-emerald-400" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><polyline points="7 10 12 15 17 10" /><line x1="12" x2="12" y1="15" y2="3" /></svg>
<span class="text-[15px] font-semibold">{{ t(p.key) }}</span>
</div>
<span class="pl-[26px] font-mono text-[12.5px] text-zinc-500">{{ t(p.hint) }}</span>
</div>
</div>
<ul v-reveal class="mt-6 flex flex-wrap gap-x-6 gap-y-2">
<li v-for="b in bullets" :key="b" class="flex items-center gap-2 text-[14px] text-zinc-300">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" class="text-emerald-400" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
{{ t(b) }}
</li>
</ul>
<div v-reveal class="mt-7 flex flex-wrap items-center gap-3">
<a
:href="RELEASES"
target="_blank"
rel="noopener"
class="inline-flex items-center gap-2 rounded-lg bg-emerald-500 px-4 py-2.5 text-[14.5px] font-semibold text-emerald-950 no-underline transition-colors hover:bg-emerald-400 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-500/70"
>
{{ t('dlGet') }}
</a>
<a
:href="DESKTOP_SRC"
target="_blank"
rel="noopener"
class="inline-flex items-center gap-2 rounded-lg border border-zinc-800 px-4 py-2.5 text-[14.5px] font-medium text-zinc-300 no-underline transition-colors hover:border-emerald-500/50 hover:text-emerald-400 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-500/70"
>
{{ t('dlSource') }}
</a>
</div>
<p v-reveal class="mt-4 max-w-[720px] text-[13.5px] leading-[1.55] text-zinc-500">{{ t('dlNote') }}</p>
</section>
</template>

View File

@@ -3,7 +3,7 @@ import { useI18n } from 'vue-i18n';
const { t } = useI18n();
// Lignes du diff (fidèle à DiffViewer : add emerald-950/40, del rose-950/40).
// Lignes du diff (fidele a DiffViewer : add -> emerald-950/40, del -> rose-950/40).
type DiffLine = { o: string; n: string; type: 'ctx' | 'add' | 'del' | 'hunk'; text: string };
const diffLines: DiffLine[] = [
{ o: '', n: '', type: 'hunk', text: '@@ -12,7 +12,9 @@ export function session(' },
@@ -27,7 +27,7 @@ function marker(type: DiffLine['type']): string {
<template>
<section id="workspace" class="mx-auto max-w-[1200px] scroll-mt-[84px] px-6 pb-[100px]">
<div v-reveal class="mb-[40px] max-w-[720px]">
<div v-reveal class="mb-[40px] max-w-[760px]">
<div class="mb-3 font-mono text-xs uppercase tracking-[0.12em] text-emerald-400">{{ t('wsKicker') }}</div>
<h2 class="m-0 mb-4 text-[clamp(26px,3vw,38px)] font-semibold leading-[1.15] tracking-[-0.025em] text-zinc-50">
{{ t('wsTitle') }}
@@ -35,156 +35,95 @@ function marker(type: DiffLine['type']): string {
<p class="m-0 text-[16.5px] leading-[1.6] text-zinc-400">{{ t('wsBody') }}</p>
</div>
<!-- maquette IDE : fidèle à WorkspaceView (/workspace) -->
<!-- maquette IDE multi-projet : barre d'activite, arbre unifie, onglets d'editeur, dock bas -->
<div v-reveal class="overflow-hidden rounded-[14px] border border-zinc-800 bg-[#09090b] shadow-card">
<!-- en-tête IDE : retour · repo · badge git · SegmentedControl Editor/Diff -->
<header class="flex flex-wrap items-center gap-3 border-b border-zinc-800 px-3 py-2">
<span class="inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-zinc-400">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6" /></svg>
{{ t('mWorktrees') }}
<!-- barre superieure -->
<header class="flex items-center gap-3 border-b border-zinc-800 px-3 py-2">
<span class="flex items-center gap-2 text-sm font-medium text-zinc-200">
<img src="/assets/arboretum-mark.png" alt="" width="16" height="16" class="h-4 w-4 object-contain" />
Arboretum
</span>
<span class="truncate text-sm font-medium text-zinc-200">api</span>
<!-- badge git (fidèle à GitStatusBadge) -->
<span class="flex items-center gap-2 text-xs">
<span class="flex items-center gap-1 font-mono text-zinc-300">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="6" x2="6" y1="3" y2="15" /><circle cx="18" cy="6" r="3" /><circle cx="6" cy="18" r="3" /><path d="M18 9a9 9 0 0 1-9 9" /></svg>
feat/auth
<span class="ml-auto flex items-center gap-1.5 text-[11px] text-zinc-500">
<span class="h-1.5 w-1.5 rounded-full bg-amber-400"></span>{{ t('waiting') }}
</span>
<span class="text-emerald-400">2</span>
<span class="text-amber-400">3</span>
</span>
<!-- SegmentedControl Editor/Diff (fidèle à SegmentedControl) -->
<div class="ml-auto inline-flex rounded-lg border border-zinc-800 bg-zinc-950/40 p-0.5">
<span class="inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-xs font-medium text-zinc-400">{{ t('wsEditor') }}</span>
<span class="inline-flex items-center gap-1.5 rounded-md bg-zinc-800 px-2.5 py-1 text-xs font-medium text-zinc-100">{{ t('wsDiff') }}</span>
</div>
</header>
<!-- corps : 3 colonnes (desktop) / empilé (mobile) -->
<div class="flex flex-col md:flex-row">
<!-- colonne gauche : arbre de fichiers + panneau commit -->
<aside class="flex min-w-0 flex-col border-b border-zinc-800 md:w-[262px] md:flex-none md:border-b-0 md:border-r">
<!-- arbre de fichiers (fidèle à FileTree / FileTreeNode) -->
<div class="flex flex-col">
<div class="flex items-center gap-1 px-2 py-1.5 text-[11px] font-semibold uppercase tracking-wide text-zinc-500">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-2.5a1 1 0 0 1-.8-.4l-.9-1.2A1 1 0 0 0 14 3h-3a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z" /><path d="M20 21a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1h-2.9a1 1 0 0 1-.88-.55l-.42-.85a1 1 0 0 0-.92-.6H11a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z" /><path d="M3 5a2 2 0 0 0 2 2h3" /><path d="M3 3v13a2 2 0 0 0 2 2h3" /></svg>
{{ t('wsFiles') }}
</div>
<div class="px-1 pb-2 font-mono text-xs">
<!-- src/ (dossier ouvert) -->
<div class="flex items-center gap-1 rounded py-0.5 pl-[6px] pr-2 text-zinc-300">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-zinc-500" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
<span class="truncate">src</span>
</div>
<div class="flex items-center gap-1 rounded py-0.5 pl-[18px] pr-2 text-zinc-300">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-zinc-500" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg>
<span class="truncate">core</span>
</div>
<div class="flex items-center justify-between gap-1 rounded bg-zinc-800 py-0.5 pl-[30px] pr-2 text-zinc-100">
<span class="flex min-w-0 items-center gap-1">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-zinc-500" aria-hidden="true"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /><path d="M14 2v4a2 2 0 0 0 2 2h4" /></svg>
<span class="truncate">session.ts</span>
<div class="flex">
<!-- barre d'activite -->
<nav class="flex w-11 flex-none flex-col items-center gap-1 border-r border-zinc-800 bg-zinc-900/50 py-2 text-zinc-500">
<span class="flex h-9 w-9 items-center justify-center rounded-lg bg-zinc-700 text-zinc-100" :title="t('wsFiles')">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-2.5a1 1 0 0 1-.8-.4l-.9-1.2A1 1 0 0 0 14 3h-3a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1Z" /><path d="M3 5a2 2 0 0 0 2 2h3" /><path d="M3 3v13a2 2 0 0 0 2 2h3" /></svg>
</span>
<span class="shrink-0 font-bold text-amber-400">M</span>
<span class="flex h-9 w-9 items-center justify-center rounded-lg" :title="t('wsChanges')">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="18" cy="18" r="3" /><circle cx="6" cy="6" r="3" /><path d="M13 6h3a2 2 0 0 1 2 2v7" /><path d="M11 18H8a2 2 0 0 1-2-2V9" /></svg>
</span>
<span class="flex h-9 w-9 items-center justify-center rounded-lg" :title="t('wsTerminal')">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m7 11 2-2-2-2" /><path d="M11 13h4" /><rect width="18" height="18" x="3" y="3" rx="2" /></svg>
</span>
</nav>
<!-- arbre unifie : plusieurs projets a la fois -->
<aside class="hidden w-[248px] flex-none flex-col border-r border-zinc-800 md:flex">
<div class="px-3 py-2 text-[11px] font-semibold uppercase tracking-wide text-zinc-500">Projects</div>
<div class="px-1 pb-2 font-mono text-xs">
<!-- projet api (ouvert) -->
<div class="flex items-center gap-1 rounded py-0.5 pl-1.5 pr-2 text-zinc-200">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-zinc-500" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
<span class="font-medium">api</span>
</div>
<!-- worktree feat/auth (actif) -->
<div class="flex items-center gap-1 rounded bg-zinc-800/70 py-0.5 pl-[18px] pr-2 text-zinc-100">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-zinc-500" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-zinc-500" aria-hidden="true"><line x1="6" x2="6" y1="3" y2="15" /><circle cx="18" cy="6" r="3" /><circle cx="6" cy="18" r="3" /><path d="M18 9a9 9 0 0 1-9 9" /></svg>
<span class="truncate">feat/auth</span>
</div>
<!-- session claude corrélée -->
<div class="flex items-center gap-1.5 rounded py-0.5 pl-[38px] pr-2 text-zinc-300">
<span class="inline-flex items-center gap-1 rounded bg-sky-950 px-1.5 py-[1px] text-[10px] text-sky-300"><span class="h-1.5 w-1.5 animate-pulse-sky rounded-full bg-sky-300"></span>{{ t('busy') }}</span>
<span class="truncate">claude</span>
</div>
<!-- fichiers du worktree -->
<div class="flex items-center justify-between gap-1 rounded bg-zinc-800 py-0.5 pl-[30px] pr-2 text-zinc-100">
<span class="truncate">session.ts</span><span class="font-bold text-amber-400">M</span>
</div>
<div class="flex items-center justify-between gap-1 rounded py-0.5 pl-[30px] pr-2 text-zinc-300">
<span class="flex min-w-0 items-center gap-1">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-zinc-500" aria-hidden="true"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /><path d="M14 2v4a2 2 0 0 0 2 2h4" /></svg>
<span class="truncate">token.ts</span>
</span>
<span class="shrink-0 font-bold text-emerald-400">A</span>
<span class="truncate">token.ts</span><span class="font-bold text-emerald-400">A</span>
</div>
<div class="flex items-center gap-1 rounded py-0.5 pl-[18px] pr-2 text-zinc-300">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-zinc-500" aria-hidden="true"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /><path d="M14 2v4a2 2 0 0 0 2 2h4" /></svg>
<span class="truncate">app.ts</span>
</div>
<div class="flex items-center gap-1 rounded py-0.5 pl-[6px] pr-2 text-zinc-300">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-zinc-500" aria-hidden="true"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /><path d="M14 2v4a2 2 0 0 0 2 2h4" /></svg>
<span class="truncate">package.json</span>
</div>
</div>
</div>
<!-- panneau commit (fidèle à CommitPanel) -->
<div class="flex flex-col border-t border-zinc-800">
<div class="flex items-center gap-1 px-2 py-1 text-[11px] font-semibold uppercase tracking-wide text-zinc-500">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="18" cy="18" r="3" /><circle cx="6" cy="6" r="3" /><path d="M13 6h3a2 2 0 0 1 2 2v7" /><path d="M11 18H8a2 2 0 0 1-2-2V9" /></svg>
{{ t('wsChanges') }}
<span class="text-emerald-500">2</span>
</div>
<!-- Staged (1) -->
<div class="px-1">
<div class="flex items-center px-1 py-0.5 text-[11px] text-zinc-500">{{ t('wsStaged') }} (1)</div>
<div class="group flex items-center gap-2 rounded bg-zinc-800 px-2 py-0.5 text-xs text-zinc-100">
<span class="flex min-w-0 flex-1 items-center gap-2">
<span class="w-3 shrink-0 text-center font-mono font-bold text-emerald-400">A</span>
<span class="min-w-0 flex-1 truncate font-mono">token.ts<span class="text-zinc-600"> src/core</span></span>
<span class="shrink-0 text-emerald-500">+24</span>
</span>
<button type="button" class="shrink-0 rounded p-0.5 text-zinc-400 hover:bg-zinc-700 hover:text-zinc-200" :title="t('wsUnstage')" :aria-label="t('wsUnstage')">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14" /></svg>
</button>
</div>
</div>
<!-- Changes (1) -->
<div class="px-1 pb-1">
<div class="flex items-center px-1 py-0.5 text-[11px] text-zinc-500">{{ t('wsChanges') }} (1)</div>
<div class="group flex items-center gap-2 rounded px-2 py-0.5 text-xs text-zinc-300 hover:bg-zinc-800/60">
<span class="flex min-w-0 flex-1 items-center gap-2">
<span class="w-3 shrink-0 text-center font-mono font-bold text-amber-400">M</span>
<span class="min-w-0 flex-1 truncate font-mono">session.ts<span class="text-zinc-600"> src/core</span></span>
<span class="shrink-0 text-emerald-500">+2</span>
<span class="shrink-0 text-rose-500">1</span>
</span>
<span class="flex shrink-0 items-center gap-0.5 text-zinc-400">
<button type="button" class="rounded p-0.5 hover:bg-zinc-700" :title="t('wsDiscard')" :aria-label="t('wsDiscard')">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 14 4 9l5-5" /><path d="M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5v0a5.5 5.5 0 0 1-5.5 5.5H11" /></svg>
</button>
<button type="button" class="rounded p-0.5 hover:bg-zinc-700" :title="t('wsStage')" :aria-label="t('wsStage')">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14" /><path d="M12 5v14" /></svg>
</button>
</span>
</div>
</div>
<!-- zone de commit -->
<div class="flex flex-col gap-1 border-t border-zinc-800 p-2">
<div class="resize-none rounded-md border border-zinc-700 bg-zinc-950/40 px-2 py-1.5 text-xs text-zinc-200">{{ t('wsCommitMsg') }}</div>
<label class="flex items-center gap-1.5 text-[11px] text-zinc-400">
<span class="inline-flex h-3.5 w-3.5 items-center justify-center rounded-[3px] border border-zinc-600 bg-zinc-950"></span>
{{ t('wsAmend') }}
</label>
<div class="flex items-center gap-2">
<span class="inline-flex items-center gap-1.5 rounded-md bg-emerald-500 px-2.5 py-1 text-xs font-semibold text-emerald-950">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
{{ t('wsCommitBtn') }}
</span>
<span class="inline-flex items-center gap-1.5 rounded-md border border-zinc-700 bg-zinc-800 px-2.5 py-1 text-xs font-medium text-zinc-200">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v12" /><path d="m17 8-5-5-5 5" /><path d="M5 21h14" /></svg>
{{ t('wsPushBtn') }} 2
</span>
<!-- projet web (replie) -->
<div class="flex items-center gap-1 rounded py-0.5 pl-1.5 pr-2 text-zinc-300">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-zinc-500" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg>
<span class="font-medium">web</span>
</div>
<div class="flex items-center gap-1 rounded py-0.5 pl-1.5 pr-2 text-zinc-300">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-zinc-500" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg>
<span class="font-medium">docs</span>
</div>
</div>
</aside>
<!-- colonne centrale : diff (fidèle à DiffViewer) -->
<main class="flex min-w-0 flex-1 flex-col border-b border-zinc-800 md:border-b-0">
<!-- centre : onglets d'editeur + diff + dock terminaux -->
<main class="flex min-w-0 flex-1 flex-col">
<!-- onglets d'editeur (plusieurs fichiers, potentiellement de projets differents) -->
<div class="flex items-stretch overflow-x-auto border-b border-zinc-800 text-xs">
<span class="flex items-center gap-1.5 border-r border-zinc-800 bg-[#09090b] px-3 py-1.5 text-zinc-100">
session.ts <span class="h-1.5 w-1.5 rounded-full bg-amber-400"></span>
</span>
<span class="flex items-center gap-1.5 border-r border-zinc-800 px-3 py-1.5 text-zinc-400">token.ts</span>
<span class="flex items-center gap-1.5 border-r border-zinc-800 px-3 py-1.5 text-zinc-500">app.vue</span>
</div>
<!-- barre : bascule Editor/Diff + chemin -->
<div class="flex items-center gap-2 border-b border-zinc-800 px-2 py-1">
<div class="inline-flex rounded-lg border border-zinc-800 bg-zinc-950/40 p-0.5">
<span class="inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-xs font-medium text-zinc-400">{{ t('wsEditor') }}</span>
<span class="inline-flex items-center gap-1.5 rounded-md bg-zinc-800 px-2.5 py-1 text-xs font-medium text-zinc-100">{{ t('wsDiff') }}</span>
<span class="rounded-md px-2.5 py-1 text-xs font-medium text-zinc-400">{{ t('wsEditor') }}</span>
<span class="rounded-md bg-zinc-800 px-2.5 py-1 text-xs font-medium text-zinc-100">{{ t('wsDiff') }}</span>
</div>
<span class="truncate font-mono text-[11px] text-zinc-500">src/core/session.ts</span>
</div>
<div class="min-h-0 flex-1 overflow-auto">
<div class="flex items-center gap-3 border-b border-zinc-800 px-3 py-1 text-[11px] text-zinc-500">
<span class="text-emerald-500">+2</span>
<span class="text-rose-500">1</span>
<span class="rounded bg-emerald-950 px-1 text-emerald-400">{{ t('wsStaged') }}</span>
<span class="truncate font-mono text-[11px] text-zinc-500">api · src/core/session.ts</span>
<span class="ml-auto inline-flex items-center gap-1.5 rounded-md bg-emerald-500 px-2.5 py-1 text-xs font-semibold text-emerald-950">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
{{ t('wsCommitBtn') }}
</span>
</div>
<!-- diff (fidele a DiffViewer) -->
<div class="overflow-x-auto">
<table class="w-full border-collapse font-mono text-xs leading-5">
<tbody>
@@ -196,36 +135,40 @@ function marker(type: DiffLine['type']): string {
</tbody>
</table>
</div>
</div>
</main>
<!-- colonne droite : terminal de la session corrélée (fidèle au style des terminaux du site) -->
<section class="flex min-w-0 flex-col md:w-[300px] md:flex-none md:border-l md:border-zinc-800">
<div class="flex items-center gap-1 border-b border-zinc-800 px-2 py-1 text-[11px] font-semibold uppercase tracking-wide text-zinc-500">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m7 11 2-2-2-2" /><path d="M11 13h4" /><rect width="18" height="18" x="3" y="3" rx="2" ry="2" /></svg>
<!-- dock terminaux en bas (onglets par session) -->
<div class="border-t border-zinc-800">
<div class="flex items-stretch border-b border-zinc-800 text-[11px]">
<span class="flex items-center gap-1 px-2 py-1.5 font-semibold uppercase tracking-wide text-zinc-500">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m7 11 2-2-2-2" /><path d="M11 13h4" /><rect width="18" height="18" x="3" y="3" rx="2" /></svg>
{{ t('wsTerminal') }}
</div>
<!-- barre de titre repo · branche + badge d'état -->
<div class="flex items-center gap-2 border-b border-zinc-800 px-[11px] py-2">
<span class="truncate font-mono text-[11.5px] text-zinc-300">api · feat/auth</span>
<span class="ml-auto inline-flex items-center gap-1.5 rounded bg-sky-950 px-2 py-[3px] font-mono text-[11px] text-sky-300">
<span class="h-1.5 w-1.5 flex-none animate-pulse-sky rounded-full bg-sky-300"></span>{{ t('busy') }}
</span>
<span class="flex items-center gap-1.5 border-l border-zinc-800 bg-[#09090b] px-2.5 py-1.5 font-mono text-zinc-100">
<span class="h-1.5 w-1.5 animate-pulse-sky rounded-full bg-sky-300"></span>api · feat/auth
</span>
<span class="flex items-center gap-1.5 border-l border-zinc-800 px-2.5 py-1.5 font-mono text-zinc-400">web · main</span>
</div>
<div class="min-h-[180px] flex-1 p-[11px] font-mono text-[11px] leading-[1.75] text-zinc-300">
<div class="min-h-[112px] p-[11px] font-mono text-[11px] leading-[1.7] text-zinc-300">
<div class="text-emerald-400">● Edit session.ts</div>
<div class="text-emerald-400">+ const token = sign(...)</div>
<div class="text-red-400">- legacy cookie auth</div>
<div class="text-emerald-400"> Write token.ts</div>
<div class="text-zinc-500">running tests…</div>
<div class="text-emerald-400">✓ 24 passed</div>
<div>
<span class="text-emerald-400">$</span>
<span class="ml-1 inline-block h-[11px] w-1.5 animate-blink align-middle bg-zinc-200"></span>
<div><span class="text-emerald-400">$</span><span class="ml-1 inline-block h-[11px] w-1.5 animate-blink align-middle bg-zinc-200"></span></div>
</div>
</div>
</section>
</main>
</div>
<!-- barre de statut -->
<footer class="flex items-center gap-3 border-t border-zinc-800 px-3 py-1 text-[11px] text-zinc-500">
<span class="flex items-center gap-1 font-mono text-zinc-400">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="6" x2="6" y1="3" y2="15" /><circle cx="18" cy="6" r="3" /><circle cx="6" cy="18" r="3" /><path d="M18 9a9 9 0 0 1-9 9" /></svg>
feat/auth
</span>
<span class="text-emerald-400">↑2</span>
<span class="text-amber-400">○3</span>
<span class="ml-auto">api · {{ t('busy') }}</span>
</footer>
</div>
</section>
</template>

View File

@@ -1,7 +1,8 @@
// Messages EN : copie fidèle du dict() du design Arboretum.dc.html.
export default {
navFeatures: 'Features',
navWorkspace: 'Workspace',
navWorkspace: 'IDE',
navDownload: 'Download',
navHow: 'How it works',
navSecurity: 'Security',
navFaq: 'FAQ',
@@ -9,7 +10,7 @@ export default {
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.',
'A multi-project AI IDE, native and web: run many Claude Code sessions across every git worktree in one window, 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.",
@@ -109,8 +110,8 @@ export default {
copyUrl: 'Copy URL',
// New feature cards (P7→P12: IDE worktree)
feat11Title: 'A real IDE for every worktree',
feat11Desc: 'Browse the file tree, edit in a full Monaco editor, and read changes as inline diffs, all in the /workspace view.',
feat11Title: 'A real multi-project IDE',
feat11Desc: 'One window for every project: a unified tree (project, worktree, session), a tabbed Monaco editor with inline diffs across projects, and Claude terminals docked below.',
feat12Title: 'Diff & selective staging',
feat12Desc: 'Stage, unstage or discard file by file, then commit (all or staged), amend, fetch and pull, without leaving the dashboard.',
feat13Title: 'Remote git services',
@@ -121,10 +122,10 @@ export default {
feat15Desc: 'A filesystem watcher streams changes the moment a file moves on disk: diffs and status update live, no refresh needed.',
// Workspace IDE showcase
wsKicker: 'The worktree IDE',
wsTitle: 'A full IDE for every worktree, in the browser',
wsKicker: 'The multi-project IDE',
wsTitle: 'One IDE for all your projects, native or web',
wsBody:
'Open any worktree in /workspace: a file tree on the left, a Monaco editor and inline diff in the center, and the correlated session terminal on the right. Stage changes file by file, write a commit message, push, all without leaving the page, on any device.',
'Open every project in a single window: a unified tree (project, worktree, Claude session) on the left, a tabbed Monaco editor with inline diffs in the center, Claude terminals docked at the bottom, and a git panel for staging, commits and push. No more one IDE window per project. Run it as a native desktop app or serve it in your browser, on any device.',
wsEditor: 'Editor',
wsDiff: 'Diff',
wsFiles: 'Files',
@@ -155,4 +156,22 @@ export default {
// Security pillar (encrypted secrets)
sec5Title: 'Encrypted secrets',
sec5Desc: 'Remote git credentials are encrypted at rest (AES-256-GCM) and never returned in clear by the API.',
// Desktop app (download)
dlKicker: 'Native desktop app',
dlTitle: 'Download Arboretum for your desktop',
dlBody:
'A native app for Linux, Windows and macOS. It bundles the daemon and its Node runtime, so there is nothing else to install: launch it and your multi-project IDE opens, already signed in. Tray, launch at login and auto-update included. Prefer the terminal? Keep running it in the browser with a single command.',
dlLinux: 'Linux',
dlLinuxHint: 'AppImage and .deb',
dlWin: 'Windows',
dlWinHint: 'NSIS installer and portable',
dlMac: 'macOS',
dlMacHint: 'dmg (best-effort, unsigned)',
dlNoNode: 'No Node install needed',
dlBundled: 'Daemon and runtime bundled',
dlAutoUpdate: 'Auto-update on Windows and Linux',
dlGet: 'Get the app',
dlSource: 'Build from source',
dlNote: 'Installers are published on the releases page once a desktop build is tagged. Until then, build it from source (see packages/desktop).',
};

View File

@@ -1,7 +1,8 @@
// Messages FR : copie fidèle du dict() du design Arboretum.dc.html.
export default {
navFeatures: 'Fonctionnalités',
navWorkspace: 'Workspace',
navWorkspace: 'IDE',
navDownload: 'Télécharger',
navHow: 'Comment ça marche',
navSecurity: 'Sécurité',
navFaq: 'FAQ',
@@ -9,7 +10,7 @@ export default {
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.",
"Un IDE IA multi-projet, natif et web : lancez plusieurs sessions Claude Code à travers chaque worktree git dans une seule fenêtre, 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.",
@@ -109,8 +110,8 @@ export default {
copyUrl: "Copier l'URL",
// Nouvelles cartes de fonctionnalités (P7→P12 : IDE worktree)
feat11Title: 'Un vrai IDE pour chaque worktree',
feat11Desc: "Parcourez l'arbre de fichiers, éditez dans un éditeur Monaco complet, et lisez les changements en diff inline, le tout dans la vue /workspace.",
feat11Title: 'Un vrai IDE multi-projet',
feat11Desc: "Une seule fenêtre pour tous vos projets : un arbre unifié (projet, worktree, session), un éditeur Monaco à onglets avec diffs inline entre projets, et les terminaux Claude en dock bas.",
feat12Title: 'Diff & staging sélectif',
feat12Desc: 'Indexez, désindexez ou annulez fichier par fichier, puis commitez (tout ou indexé), amendez, fetchez et pullez, sans quitter le dashboard.',
feat13Title: 'Services git distants',
@@ -120,11 +121,11 @@ export default {
feat15Title: 'Synchro fichiers temps réel',
feat15Desc: "Un watcher du système de fichiers pousse les changements dès qu'un fichier bouge sur le disque : diffs et statut se mettent à jour en direct, sans rafraîchir.",
// Showcase IDE workspace
wsKicker: "L'IDE worktree",
wsTitle: 'Un IDE complet pour chaque worktree, dans le navigateur',
// Showcase IDE multi-projet
wsKicker: "L'IDE multi-projet",
wsTitle: 'Un seul IDE pour tous vos projets, natif ou web',
wsBody:
"Ouvrez n'importe quel worktree dans /workspace : un arbre de fichiers à gauche, un éditeur Monaco et le diff inline au centre, et le terminal de la session corrélée à droite. Indexez les changements fichier par fichier, rédigez un message de commit, poussez, sans quitter la page, depuis n'importe quel appareil.",
"Ouvrez tous vos projets dans une seule fenêtre : un arbre unifié (projet, worktree, session Claude) à gauche, un éditeur Monaco à onglets avec diffs inline au centre, les terminaux Claude en dock bas, et un panneau Git pour l'indexation, les commits et le push. Fini un IDE par projet. En app de bureau native ou servi dans votre navigateur, sur n'importe quel appareil.",
wsEditor: 'Éditeur',
wsDiff: 'Diff',
wsFiles: 'Fichiers',
@@ -155,4 +156,22 @@ export default {
// Pilier sécurité (secrets chiffrés)
sec5Title: 'Secrets chiffrés',
sec5Desc: "Les identifiants git distants sont chiffrés au repos (AES-256-GCM) et jamais renvoyés en clair par l'API.",
// App de bureau (téléchargement)
dlKicker: 'App de bureau native',
dlTitle: 'Téléchargez Arboretum pour votre bureau',
dlBody:
"Une app native pour Linux, Windows et macOS. Elle embarque le daemon et son runtime Node : rien d'autre à installer, lancez-la et votre IDE multi-projet s'ouvre, déjà authentifié. Tray, lancement au login et auto-update inclus. Vous préférez le terminal ? Continuez à la lancer dans le navigateur en une commande.",
dlLinux: 'Linux',
dlLinuxHint: 'AppImage et .deb',
dlWin: 'Windows',
dlWinHint: 'Installeur NSIS et portable',
dlMac: 'macOS',
dlMacHint: 'dmg (best-effort, non signé)',
dlNoNode: "Pas besoin d'installer Node",
dlBundled: 'Daemon et runtime embarqués',
dlAutoUpdate: 'Auto-update sur Windows et Linux',
dlGet: "Obtenir l'app",
dlSource: 'Builder depuis les sources',
dlNote: 'Les installeurs sont publiés sur la page des releases dès qu\'un build desktop est taggé. En attendant, buildez depuis les sources (voir packages/desktop).',
};

View File

@@ -4,3 +4,7 @@
export const REPO = 'https://git.lidge.fr/johanleroy/arboretum';
export const LICENSE = `${REPO}/src/branch/main/LICENSE`;
export const COFFEE = 'https://buymeacoffee.com/johanleroy';
// App de bureau : page des releases (les installeurs y apparaissent après un tag desktop-vX.Y.Z)
// et sources du paquet Electron pour un build local.
export const RELEASES = `${REPO}/releases`;
export const DESKTOP_SRC = `${REPO}/src/branch/main/packages/desktop`;