Files
arboretum/packages/site/src/components/WorkspaceShowcase.vue
Johan LEROY 8a8fac75e6 feat(site): showcase IDE workspace, diff/staging et services git distants
- WorkspaceShowcase.vue : maquette fidèle de la vue /workspace (arbre de
  fichiers + panneau commit staging/amend + diff coloré + terminal corrélé).
- RemoteGitSection.vue : connexions GitHub/GitLab/Gitea (chiffrées at-rest)
  + barre de progression de clone.
- 5 cartes features, pilier sécurité « Encrypted secrets », 3 FAQ, ancre nav.
- i18n EN/FR à parité ; meta description orientée IDE. Bump 0.2.0.
2026-06-27 16:04:19 +02:00

232 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
// Lignes du diff (fidèle à 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(' },
{ o: '12', n: '12', type: 'ctx', text: ' const claims = verify(req);' },
{ o: '13', n: '13', type: 'ctx', text: ' if (!claims) return null;' },
{ o: '14', n: '', type: 'del', text: ' return signLegacyCookie(claims);' },
{ o: '', n: '14', type: 'add', text: ' const token = sign(claims, { ttl: 900 });' },
{ o: '', n: '15', type: 'add', text: ' return refreshable(token);' },
{ o: '15', n: '16', type: 'ctx', text: '}' },
];
function rowClass(type: DiffLine['type']): string {
if (type === 'add') return 'bg-emerald-950/40 text-emerald-200';
if (type === 'del') return 'bg-rose-950/40 text-rose-200';
return 'text-zinc-300';
}
function marker(type: DiffLine['type']): string {
return type === 'add' ? '+' : type === 'del' ? '-' : ' ';
}
</script>
<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 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') }}
</h2>
<p class="m-0 text-[16.5px] leading-[1.6] text-zinc-400">{{ t('wsBody') }}</p>
</div>
<!-- maquette IDE : fidèle à WorkspaceView (/workspace) -->
<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') }}
</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>
<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>
</span>
<span class="shrink-0 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>
</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>
</div>
</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">
<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>
</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>
</div>
<div class="overflow-x-auto">
<table class="w-full border-collapse font-mono text-xs leading-5">
<tbody>
<tr v-for="(l, i) in diffLines" :key="i" :class="l.type === 'hunk' ? 'bg-zinc-900/60 text-sky-300/80' : rowClass(l.type)">
<td class="w-10 select-none px-2 text-right text-zinc-600">{{ l.o }}</td>
<td class="w-10 select-none px-2 text-right text-zinc-600">{{ l.n }}</td>
<td class="whitespace-pre px-2">{{ l.type === 'hunk' ? l.text : marker(l.type) + l.text }}</td>
</tr>
</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>
{{ 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>
</div>
<div class="min-h-[180px] flex-1 p-[11px] font-mono text-[11px] leading-[1.75] 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>
</div>
</section>
</div>
</div>
</section>
</template>