- 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.
232 lines
17 KiB
Vue
232 lines
17 KiB
Vue
<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>
|