Skip to content
Merged
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
33 changes: 18 additions & 15 deletions src/components/AlgoViz.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
getAlgorithmDescription,
getAlgorithmMetaTitle,
getAlgorithmMetaDescription,
defaultLocale,
locales,
} from '@i18n/translations'
import { algorithms, categories } from '@lib/algorithms'
import { usePlayback } from '@hooks/usePlayback'
Expand All @@ -26,13 +28,16 @@ const COLLAPSE_THRESHOLD = 100
const MOBILE_BREAKPOINT = 768

function getAlgorithmUrl(locale: string, algoId: string): string {
return locale === 'es' ? `/es/${algoId}` : `/${algoId}`
return locale === defaultLocale ? `/${algoId}` : `/${locale}/${algoId}`
}

function getAlgorithmIdFromPath(pathname: string): string | null {
const cleaned = pathname.replace(/\/$/, '')
if (cleaned === '' || cleaned === '/es') return null
if (cleaned.startsWith('/es/')) return cleaned.slice(4)
if (cleaned === '') return null
for (const locale of locales) {
if (cleaned === `/${locale}`) return null
if (cleaned.startsWith(`/${locale}/`)) return cleaned.slice(locale.length + 2)
}
return cleaned.slice(1)
}

Expand Down Expand Up @@ -231,7 +236,7 @@ export default function AlgoViz({ locale = 'en', initialAlgorithmId }: AlgoVizPr
style={{
width: sidebar.isDragging ? sidebar.width : sidebar.collapsed ? 0 : SIDEBAR_MAX,
}}
aria-label={locale === 'es' ? 'Categorías de algoritmos' : 'Algorithm categories'}
aria-label={t.sidebarAriaLabel}
aria-hidden={sidebar.collapsed}
inert={sidebar.collapsed || undefined}
>
Expand Down Expand Up @@ -296,19 +301,19 @@ export default function AlgoViz({ locale = 'en', initialAlgorithmId }: AlgoVizPr
className={`fixed top-0 left-0 bottom-0 w-[280px] bg-black z-50 border-r border-white/8 transition-transform duration-300 ease-in-out ${
mobileSidebarOpen ? 'translate-x-0' : '-translate-x-full'
}`}
aria-label={locale === 'es' ? 'Categorías de algoritmos' : 'Algorithm categories'}
aria-label={t.sidebarAriaLabel}
aria-hidden={!mobileSidebarOpen}
inert={!mobileSidebarOpen || undefined}
>
<div className="h-full flex flex-col">
<div className="flex items-center justify-between px-4 py-3 border-b border-white/8">
<span className="text-sm font-semibold text-white font-heading">
{locale === 'es' ? 'Algoritmos' : 'Algorithms'}
{t.mobileMenuTitle}
</span>
<button
onClick={() => setMobileSidebarOpen(false)}
className="w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/6 text-neutral-400 hover:text-white transition-colors"
aria-label={locale === 'es' ? 'Cerrar menú' : 'Close menu'}
aria-label={t.closeMenu}
>
<svg
className="w-4 h-4"
Expand Down Expand Up @@ -394,7 +399,7 @@ export default function AlgoViz({ locale = 'en', initialAlgorithmId }: AlgoVizPr
? 0
: CODEPANEL_MAX,
}}
aria-label={locale === 'es' ? 'Panel de código y detalles' : 'Code and details panel'}
aria-label={t.codePanelAriaLabel}
aria-hidden={codePanel.collapsed}
inert={codePanel.collapsed || undefined}
>
Expand Down Expand Up @@ -446,19 +451,17 @@ export default function AlgoViz({ locale = 'en', initialAlgorithmId }: AlgoVizPr
className={`fixed top-0 right-0 bottom-0 w-[min(360px,90vw)] bg-black z-50 border-l border-white/8 transition-transform duration-300 ease-in-out ${
mobileCodePanelOpen ? 'translate-x-0' : 'translate-x-full'
}`}
aria-label={locale === 'es' ? 'Panel de código y detalles' : 'Code and details panel'}
aria-label={t.codePanelAriaLabel}
aria-hidden={!mobileCodePanelOpen}
inert={!mobileCodePanelOpen || undefined}
>
<div className="h-full flex flex-col">
<div className="flex items-center justify-between px-4 py-3 border-b border-white/8">
<span className="text-sm font-semibold text-white font-heading">
{locale === 'es' ? 'Código' : 'Code'}
</span>
<span className="text-sm font-semibold text-white font-heading">{t.tabCode}</span>
<button
onClick={() => setMobileCodePanelOpen(false)}
className="w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/6 text-neutral-400 hover:text-white transition-colors"
aria-label={locale === 'es' ? 'Cerrar panel' : 'Close panel'}
aria-label={t.closePanel}
>
<svg
className="w-4 h-4"
Expand Down Expand Up @@ -501,7 +504,7 @@ export default function AlgoViz({ locale = 'en', initialAlgorithmId }: AlgoVizPr
<button
onClick={() => setMobileSidebarOpen(true)}
className="w-8 h-8 flex items-center justify-center rounded-md hover:bg-white/6 text-neutral-400 hover:text-white transition-colors shrink-0"
aria-label={locale === 'es' ? 'Abrir menú' : 'Open menu'}
aria-label={t.openMenu}
>
<svg
className="w-4 h-4"
Expand Down Expand Up @@ -589,7 +592,7 @@ export default function AlgoViz({ locale = 'en', initialAlgorithmId }: AlgoVizPr
<button
onClick={() => setMobileCodePanelOpen(true)}
className="w-8 h-8 flex items-center justify-center rounded-md hover:bg-white/6 text-neutral-400 hover:text-white transition-colors shrink-0"
aria-label={locale === 'es' ? 'Ver código' : 'View code'}
aria-label={t.viewCode}
>
<svg
className="w-4 h-4"
Expand Down
44 changes: 36 additions & 8 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,21 @@ export default function Header({
<button
onClick={onToggleMobileSidebar}
className="flex items-center justify-center w-7 h-7 rounded-md hover:bg-white/6 transition-colors text-neutral-400 hover:text-white shrink-0"
aria-label={locale === 'es' ? 'Abrir menú' : 'Open menu'}
aria-label={t.openMenu}
>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
/>
</svg>
</button>
) : sidebarCollapsed ? (
Expand Down Expand Up @@ -129,7 +140,10 @@ export default function Header({
)}
</a>
{selectedAlgorithm && (
<nav aria-label="Breadcrumb" className="flex items-center gap-1.5 min-w-0 overflow-hidden">
<nav
aria-label="Breadcrumb"
className="flex items-center gap-1.5 min-w-0 overflow-hidden"
>
<span className="text-neutral-600 shrink-0">/</span>
<span className="text-xs text-neutral-500 hidden md:inline shrink-0">
{getCategoryName(locale, selectedAlgorithm.category)}
Expand Down Expand Up @@ -167,10 +181,21 @@ export default function Header({
<button
onClick={onToggleMobileCodePanel}
className="flex items-center justify-center w-7 h-7 rounded-md hover:bg-white/6 transition-colors text-neutral-400 hover:text-white"
aria-label={locale === 'es' ? 'Ver código' : 'View code'}
aria-label={t.viewCode}
>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5" />
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5"
/>
</svg>
</button>
)}
Expand All @@ -196,7 +221,10 @@ export default function Header({
</svg>
</button>
)}
<nav aria-label={locale === 'es' ? 'Idioma' : 'Language'} className="flex items-center gap-0.5 bg-white/6 rounded-lg p-0.5 border border-white/8">
<nav
aria-label={t.languageLabel}
className="flex items-center gap-0.5 bg-white/6 rounded-lg p-0.5 border border-white/8"
>
{locales.map((l) => (
<a
key={l}
Expand Down
28 changes: 28 additions & 0 deletions src/i18n/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ export interface Translations {
resizeSidebar: string
resizeCodePanel: string

// Mobile / aria labels
sidebarAriaLabel: string
codePanelAriaLabel: string
mobileMenuTitle: string
openMenu: string
closeMenu: string
closePanel: string
viewCode: string
languageLabel: string

// 404
notFoundTitle: string
notFoundDescription: string
Expand Down Expand Up @@ -107,6 +117,15 @@ export const translations: Record<Locale, Translations> = {
resizeSidebar: 'Resize sidebar',
resizeCodePanel: 'Resize code panel',

sidebarAriaLabel: 'Algorithm categories',
codePanelAriaLabel: 'Code and details panel',
mobileMenuTitle: 'Algorithms',
openMenu: 'Open menu',
closeMenu: 'Close menu',
closePanel: 'Close panel',
viewCode: 'View code',
languageLabel: 'Language',

notFoundTitle: '404 — Page not found',
notFoundDescription: "The page you're looking for doesn't exist or has been moved.",
backToHome: 'Back to home',
Expand Down Expand Up @@ -1043,6 +1062,15 @@ The puzzle was invented by mathematician Édouard Lucas in 1883. Legend says mon
resizeSidebar: 'Redimensionar barra lateral',
resizeCodePanel: 'Redimensionar panel de código',

sidebarAriaLabel: 'Categorías de algoritmos',
codePanelAriaLabel: 'Panel de código y detalles',
mobileMenuTitle: 'Algoritmos',
openMenu: 'Abrir menú',
closeMenu: 'Cerrar menú',
closePanel: 'Cerrar panel',
viewCode: 'Ver código',
languageLabel: 'Idioma',

notFoundTitle: '404 — Página no encontrada',
notFoundDescription: 'Lo sentimos, no pudimos encontrar la página que estás buscando.',
backToHome: 'Volver al inicio',
Expand Down
10 changes: 5 additions & 5 deletions src/pages/[algorithm].astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import Layout from "@layouts/Layout.astro"
import AlgoViz from "@components/AlgoViz"
import { algorithms } from "@lib/algorithms"
import { getAlgorithmMetaTitle, getAlgorithmMetaDescription } from "@i18n/translations"
import { getAlgorithmMetaTitle, getAlgorithmMetaDescription, defaultLocale } from "@i18n/translations"
import type { GetStaticPaths } from "astro"

export const getStaticPaths: GetStaticPaths = () => {
Expand All @@ -14,10 +14,10 @@ export const getStaticPaths: GetStaticPaths = () => {
const { algorithm } = Astro.params as { algorithm: string }
const algo = algorithms.find((a) => a.id === algorithm)!

const title = getAlgorithmMetaTitle("en", algo.id, algo.name)
const description = getAlgorithmMetaDescription("en", algo.id)
const title = getAlgorithmMetaTitle(defaultLocale, algo.id, algo.name)
const description = getAlgorithmMetaDescription(defaultLocale, algo.id)
---

<Layout locale="en" title={title} description={description}>
<AlgoViz locale="en" initialAlgorithmId={algorithm} client:load />
<Layout locale={defaultLocale} title={title} description={description}>
<AlgoViz locale={defaultLocale} initialAlgorithmId={algo.id} client:load />
</Layout>
27 changes: 27 additions & 0 deletions src/pages/[lang]/[algorithm].astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
import Layout from "@layouts/Layout.astro"
import AlgoViz from "@components/AlgoViz"
import { algorithms } from "@lib/algorithms"
import { getAlgorithmMetaTitle, getAlgorithmMetaDescription, type Locale, locales, defaultLocale } from "@i18n/translations"
import type { GetStaticPaths } from "astro"

export const getStaticPaths: GetStaticPaths = () => {
return locales
.filter((lang) => lang !== defaultLocale)
.flatMap((lang) =>
algorithms.map((algo) => ({
params: { lang, algorithm: algo.id },
}))
)
}

const { algorithm, lang } = Astro.params as { algorithm: string, lang: Locale }
const algo = algorithms.find((a) => a.id === algorithm)!

const title = getAlgorithmMetaTitle(lang, algo.id, algo.name)
const description = getAlgorithmMetaDescription(lang, algo.id)
---

<Layout locale={lang} title={title} description={description}>
<AlgoViz locale={lang} initialAlgorithmId={algo.id} client:load />
</Layout>
20 changes: 20 additions & 0 deletions src/pages/[lang]/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
import Layout from "@layouts/Layout.astro"
import AlgoViz from "@components/AlgoViz"
import { locales, defaultLocale } from "@i18n/translations"
import type { GetStaticPaths } from "astro"
import type { Locale } from "@i18n/translations"

export const getStaticPaths: GetStaticPaths = () => {
return locales
.filter((lang) => lang !== defaultLocale)
.map((lang) => ({ params: { lang } }))
}

const { lang } = Astro.params as { lang: Locale }

---

<Layout locale={lang}>
<AlgoViz locale={lang} client:load />
</Layout>
23 changes: 0 additions & 23 deletions src/pages/es/[algorithm].astro

This file was deleted.

8 changes: 0 additions & 8 deletions src/pages/es/index.astro

This file was deleted.

5 changes: 3 additions & 2 deletions src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
---
import Layout from "@layouts/Layout.astro"
import AlgoViz from "@components/AlgoViz"
import { defaultLocale } from "@/i18n/translations"
---

<Layout locale="en">
<AlgoViz locale="en" client:load />
<Layout locale={defaultLocale}>
<AlgoViz locale={defaultLocale} client:load />
</Layout>