Skip to content
Open
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
389 changes: 389 additions & 0 deletions docs/windsurf-ide-research-and-migration-report.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/components/guides/GuidesHomePage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const pathways = [
links: [
{ href: '/guides/session-recovery', labelKey: 'guides.home.pathway_feature.link_session' },
{ href: '/guides/ccs', labelKey: 'guides.home.pathway_feature.link_ccs' },
{ href: '/guides/windsurf', labelKey: 'guides.home.pathway_feature.link_windsurf' },
{ href: '/guides/ck-with-codex', labelKey: 'guides.home.pathway_feature.link_codex' },
{ href: '/guides/ide-config', labelKey: 'guides.home.pathway_feature.link_ide' },
{ href: '/guides/coexistence', labelKey: 'guides.home.pathway_feature.link_coexistence' },
Expand Down
7 changes: 7 additions & 0 deletions src/components/guides/TabNavigation.astro
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface Props {
| 'happy-ccs'
| 'mobile-coding'
| 'remote-control'
| 'windsurf'
| 'ide-config'
| 'coexistence'
| 'token-tips'
Expand Down Expand Up @@ -154,6 +155,12 @@ const navSections = [
icon: `<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9 3.75H6.912a2.25 2.25 0 0 0-2.15 1.588L2.35 13.177a2.25 2.25 0 0 0-.1.661V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661L19.24 5.338a2.25 2.25 0 0 0-2.15-1.588H15M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859M12 3v8.25m0 0-3-3m3 3 3-3"/></svg>`,
href: '/guides/remote-control',
},
{
id: 'windsurf',
label: t('guides.windsurf.title'),
icon: `<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 16.5c2.25 0 3-1.5 5.25-1.5s3 1.5 5.25 1.5 3-1.5 5.25-1.5" /><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 11.25c2.25 0 3-1.5 5.25-1.5s3 1.5 5.25 1.5 3-1.5 5.25-1.5" /><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6c2.25 0 3-1.5 5.25-1.5S12 6 14.25 6s3-1.5 5.25-1.5" /></svg>`,
href: '/guides/windsurf',
},
{
id: 'ide-config',
label: t('guides.ide_config.title'),
Expand Down
203 changes: 203 additions & 0 deletions src/components/guides/WindsurfDeepDiveGuide.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
---
import { useTranslatedPath, type Language } from '@/i18n';
import '@/styles/global.css';
import {
windsurfDeepDiveCopy,
type WindsurfDeepDiveTopic,
} from '@/data/guides/windsurf';
import {
ArrowLeft,
CheckCircle2,
Compass,
FileText,
GitBranch,
Layers3,
ListChecks,
} from 'lucide-astro';

interface Props {
lang: Language;
topic: WindsurfDeepDiveTopic;
}

const { lang, topic } = Astro.props;
const copy = windsurfDeepDiveCopy[lang][topic];
const translatePath = useTranslatedPath(lang);
const accentDots = ['bg-teal-500', 'bg-sky-500', 'bg-amber-500', 'bg-rose-500', 'bg-slate-500'];
const backLabel = lang === 'vi' ? 'Guide Windsurf' : 'Windsurf guide';
---

<article class="space-y-10">
<a
href={translatePath('/guides/windsurf')}
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-teal-700 dark:text-slate-300 dark:hover:text-teal-300"
>
<ArrowLeft class="h-4 w-4" stroke-width={2.1} />
{backLabel}
</a>

<section class="relative overflow-hidden rounded-3xl border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-950 md:p-8">
<div class="absolute inset-x-0 top-0 h-1 bg-gradient-to-r from-teal-400 via-sky-400 to-amber-300"></div>
<div class="grid gap-8 lg:grid-cols-[1fr_320px] lg:items-end">
<div class="max-w-3xl space-y-5">
<div class="flex flex-wrap items-center gap-3">
<span class="inline-flex items-center gap-2 rounded-full border border-teal-200 bg-teal-50 px-3 py-1 text-xs font-bold uppercase tracking-[0.14em] text-teal-700 dark:border-teal-800/70 dark:bg-teal-950/30 dark:text-teal-300">
<Compass class="h-3.5 w-3.5" stroke-width={2.1} />
{copy.hero.kicker}
</span>
<span class="rounded-full border border-slate-200 px-3 py-1 text-xs font-semibold text-slate-500 dark:border-slate-800 dark:text-slate-400">
{copy.hero.badge}
</span>
</div>
<h1 class="text-4xl font-black tracking-tight text-slate-950 dark:text-white md:text-5xl">
{copy.hero.title}
</h1>
<p class="text-lg leading-relaxed text-slate-600 dark:text-slate-300">
{copy.hero.body}
</p>
</div>

<div class="rounded-2xl border border-slate-200 bg-slate-50 p-5 dark:border-slate-800 dark:bg-slate-900/55">
<div class="flex items-center gap-2 text-sm font-bold text-slate-900 dark:text-white">
<GitBranch class="h-4 w-4 text-teal-600 dark:text-teal-300" stroke-width={2.1} />
{copy.checklistTitle}
</div>
<ul class="mt-4 space-y-2">
{copy.checklist.map((item) => (
<li class="flex items-center gap-2 text-sm text-slate-600 dark:text-slate-300">
<CheckCircle2 class="h-4 w-4 shrink-0 text-teal-600 dark:text-teal-300" stroke-width={2.1} />
<span>{item}</span>
</li>
))}
</ul>
</div>
</div>
</section>

<section class="grid gap-4 lg:grid-cols-[0.78fr_1.22fr]">
<div class="space-y-3">
<div class="inline-flex h-11 w-11 items-center justify-center rounded-xl bg-slate-900 text-white dark:bg-white dark:text-slate-950">
<Layers3 class="h-5 w-5" stroke-width={2.1} />
</div>
<h2 class="text-2xl font-bold tracking-tight text-slate-900 dark:text-white">
{copy.mapTitle}
</h2>
<p class="text-base leading-relaxed text-slate-600 dark:text-slate-300">
{copy.mapIntro}
</p>
</div>

<div class="space-y-3">
{copy.diagram && (
<section class="overflow-hidden rounded-2xl border border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-950">
<div class="border-b border-slate-200 bg-slate-50 px-5 py-4 dark:border-slate-800 dark:bg-slate-900/60">
<h3 class="text-lg font-bold text-slate-900 dark:text-white">{copy.diagram.title}</h3>
<p class="mt-1 text-sm leading-relaxed text-slate-600 dark:text-slate-300">{copy.diagram.body}</p>
</div>
<div class="grid gap-3 p-5 md:grid-cols-3">
{copy.diagram.nodes.map((node, index) => (
<div class="relative min-w-0 rounded-xl border border-slate-200 bg-slate-50 p-4 dark:border-slate-800 dark:bg-slate-900/55">
<div class="flex items-center justify-between gap-3">
<span class={`inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-sm font-black text-white ${accentDots[index % accentDots.length]}`}>
{index + 1}
</span>
{index < copy.diagram.nodes.length - 1 && (
<span class="hidden text-xs font-black uppercase tracking-[0.14em] text-slate-400 md:inline">
-&gt;
</span>
)}
</div>
<p class="mt-3 text-xs font-bold uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{node.label}</p>
<h4 class="mt-1 break-words text-base font-bold text-slate-900 dark:text-white">{node.title}</h4>
<p class="mt-2 text-sm leading-relaxed text-slate-600 dark:text-slate-300">{node.body}</p>
</div>
))}
</div>
<div class="border-t border-slate-200 bg-teal-50 px-5 py-4 text-sm leading-relaxed text-teal-800 dark:border-slate-800 dark:bg-teal-950/25 dark:text-teal-200">
{copy.diagram.feedback}
</div>
</section>
)}
{copy.sections.map((section, index) => (
<section class="rounded-2xl border border-slate-200 bg-white p-5 dark:border-slate-800 dark:bg-slate-950">
<div class="flex items-start gap-3">
<span class={`mt-2 h-2.5 w-2.5 shrink-0 rounded-full ${accentDots[index % accentDots.length]}`}></span>
<div class="min-w-0 space-y-3">
<div>
<h3 class="text-lg font-bold text-slate-900 dark:text-white">{section.title}</h3>
<p class="mt-1 text-sm leading-relaxed text-slate-600 dark:text-slate-300">{section.body}</p>
</div>
<ul class="grid min-w-0 gap-2 md:grid-cols-3">
{section.points.map((point) => (
<li class="min-w-0 break-words rounded-xl border border-slate-200 bg-slate-50 p-3 text-sm leading-relaxed text-slate-600 dark:border-slate-800 dark:bg-slate-900/60 dark:text-slate-300">
{point}
</li>
))}
</ul>
</div>
</div>
</section>
))}
</div>
</section>

{copy.practical && (
<section class="space-y-5">
<div class="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
<div class="space-y-2">
<div class="inline-flex items-center gap-2 rounded-full border border-sky-200 bg-sky-50 px-3 py-1 text-xs font-bold uppercase tracking-[0.14em] text-sky-700 dark:border-sky-800/70 dark:bg-sky-950/30 dark:text-sky-300">
<ListChecks class="h-3.5 w-3.5" stroke-width={2.1} />
{copy.practical.kicker}
</div>
<h2 class="text-2xl font-bold tracking-tight text-slate-900 dark:text-white">
{copy.practical.title}
</h2>
</div>
<p class="max-w-2xl text-sm leading-relaxed text-slate-600 dark:text-slate-300">
{copy.practical.intro}
</p>
</div>

<div class="grid gap-4 lg:grid-cols-2">
{copy.practical.blocks.map((block) => (
<section class="overflow-hidden rounded-2xl border border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-950">
<div class="space-y-3 p-5">
<div class="flex items-start gap-3">
<div class="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-slate-100 text-slate-700 dark:bg-slate-900 dark:text-slate-200">
<FileText class="h-5 w-5" stroke-width={2.1} />
</div>
<div class="min-w-0">
<p class="text-xs font-bold uppercase tracking-[0.14em] text-sky-700 dark:text-sky-300">
{block.label}
</p>
<h3 class="mt-1 text-lg font-bold text-slate-900 dark:text-white">{block.title}</h3>
</div>
</div>
<p class="text-sm leading-relaxed text-slate-600 dark:text-slate-300">{block.body}</p>
{block.items && (
<ul class="grid gap-2 sm:grid-cols-2">
{block.items.map((item) => (
<li class="flex gap-2 text-sm leading-relaxed text-slate-600 dark:text-slate-300">
<CheckCircle2 class="mt-0.5 h-4 w-4 shrink-0 text-teal-600 dark:text-teal-300" stroke-width={2.1} />
<span>{item}</span>
</li>
))}
</ul>
)}
</div>
{block.code && (
<div class="border-t border-slate-200 bg-slate-100 dark:border-slate-800 dark:bg-slate-900/90">
<div class="flex items-center justify-between gap-3 border-b border-slate-200 px-4 py-2 dark:border-slate-800">
<span class="truncate text-xs font-bold uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">
{block.codeLabel}
</span>
</div>
<pre class="max-h-[440px] overflow-x-auto p-4 text-xs leading-relaxed text-slate-700 dark:text-slate-200"><code>{block.code}</code></pre>
</div>
)}
</section>
))}
</div>
</section>
)}
</article>
22 changes: 22 additions & 0 deletions src/components/guides/WindsurfGuide.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
import { getLangFromUrl, type Language } from '@/i18n';
import WindsurfHero from './windsurf/windsurf-hero.astro';
import WindsurfSurfaceLadder from './windsurf/windsurf-surface-ladder.astro';
import WindsurfSystemMap from './windsurf/windsurf-system-map.astro';
import WindsurfCustomizationMap from './windsurf/windsurf-customization-map.astro';
import WindsurfMigrationCard from './windsurf/windsurf-migration-card.astro';
import WindsurfNextSteps from './windsurf/windsurf-next-steps.astro';

interface Props { lang?: Language }
const { lang } = Astro.props;
const currentLang = lang || getLangFromUrl(Astro.url);
---

<div class="mt-6 space-y-12">
<WindsurfHero lang={currentLang} />
<WindsurfSurfaceLadder lang={currentLang} />
<WindsurfSystemMap lang={currentLang} />
<WindsurfCustomizationMap lang={currentLang} />
<WindsurfMigrationCard lang={currentLang} />
<WindsurfNextSteps lang={currentLang} />
</div>
57 changes: 57 additions & 0 deletions src/components/guides/windsurf/windsurf-customization-map.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
import type { Language } from '@/i18n';
import { windsurfCopy } from '@/data/guides/windsurf';
import { FileCode2, GitBranch, MemoryStick, ScrollText, ShieldCheck, Workflow } from 'lucide-astro';

interface Props { lang: Language }
const { lang } = Astro.props;
const copy = windsurfCopy[lang];
const icons = [ScrollText, GitBranch, MemoryStick, Workflow, FileCode2, ShieldCheck] as const;
---

<section class="space-y-6">
<div class="max-w-3xl space-y-2">
<h2 class="text-2xl font-bold tracking-tight text-slate-900 dark:text-white md:text-3xl">
{copy.customizationTitle}
</h2>
<p class="text-base leading-relaxed text-slate-600 dark:text-slate-300">
{copy.customizationIntro}
</p>
</div>

<div class="grid gap-4 md:grid-cols-2">
{copy.customization.map(([name, path, description], index) => {
const Icon = icons[index];
return (
<article class="rounded-2xl border border-slate-200 bg-white p-5 dark:border-slate-800 dark:bg-slate-950">
<div class="flex items-start gap-3">
<div class="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl border border-slate-200 bg-slate-50 text-slate-700 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200">
<Icon class="h-5 w-5" stroke-width={2.1} />
</div>
<div class="min-w-0 space-y-2">
<div class="flex flex-wrap items-center gap-2">
<h3 class="text-base font-bold text-slate-900 dark:text-white">{name}</h3>
<code class="rounded-md bg-slate-100 px-2 py-1 text-xs text-slate-600 dark:bg-slate-800/90 dark:text-slate-300">
{path}
</code>
</div>
<p class="text-sm leading-relaxed text-slate-600 dark:text-slate-300">{description}</p>
</div>
</div>
</article>
);
})}
</div>

<article class="rounded-2xl border border-amber-200 bg-amber-50/70 p-5 dark:border-amber-800/60 dark:bg-amber-950/20">
<div class="flex items-start gap-3">
<div class="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300">
<ShieldCheck class="h-5 w-5" stroke-width={2.1} />
</div>
<div class="space-y-1">
<h3 class="text-base font-bold text-slate-900 dark:text-white">{copy.hooksTitle}</h3>
<p class="text-sm leading-relaxed text-slate-700 dark:text-slate-300">{copy.hooksBody}</p>
</div>
</div>
</article>
</section>
Loading