diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4c790f8..ecf695e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -27,8 +27,6 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4 - with: - version: 8 - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/app/globals.css b/app/globals.css index 1f0dab6..cbe3810 100644 --- a/app/globals.css +++ b/app/globals.css @@ -4,78 +4,88 @@ @custom-variant dark (&:is(.dark *)); :root { - --background: oklch(0.98 0 0); - --foreground: oklch(0.98 0 0); - --card: oklch(0.12 0 0); - --card-foreground: oklch(0.98 0 0); - --popover: oklch(0.12 0 0); - --popover-foreground: oklch(0.98 0 0); - --primary: oklch(0.75 0.18 195); - --primary-foreground: oklch(0.1 0 0); - --secondary: oklch(0.28 0 0); - --secondary-foreground: oklch(0.98 0 0); - --muted: oklch(0.22 0 0); - --muted-foreground: oklch(0.65 0 0); - --accent: oklch(0.65 0.18 45); - --accent-foreground: oklch(0.1 0 0); - --destructive: oklch(0.577 0.245 27.325); + /* ── DashAI Brand v2 · Neutral Dark (May 2026) ── */ + --background: #191817; + --foreground: #FEFEFF; + --card: #1F1E1D; + --card-foreground: #FEFEFF; + --popover: #1F1E1D; + --popover-foreground: #FEFEFF; + --primary: #2C7AFF; + --primary-foreground: #191817; + --secondary: #25231F; + --secondary-foreground: #FEFEFF; + --muted: #1B1A19; + --muted-foreground: #B5B0A8; + --accent: #FFA578; + --accent-foreground: #191817; + --destructive: oklch(0.577 0.245 27.325); --destructive-foreground: oklch(0.985 0 0); - --border: oklch(0.28 0 0); - --input: oklch(0.28 0 0); - --ring: oklch(0.75 0.18 195); - --chart-1: oklch(0.75 0.18 195); - --chart-2: oklch(0.65 0.18 45); - --chart-3: oklch(0.6 0.15 240); - --chart-4: oklch(0.7 0.15 280); - --chart-5: oklch(0.55 0.12 210); - --radius: 0.75rem; - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); + --border: #2E2C29; + --input: #2E2C29; + --ring: #2C7AFF; + --chart-1: #2C7AFF; + --chart-2: #FFA578; + --chart-3: #90F1C4; + --chart-4: #A54DA9; + --chart-5: #A7C7FF; + --radius: 0.375rem; + --sidebar: #131211; + --sidebar-foreground: #FEFEFF; + --sidebar-primary: #2C7AFF; + --sidebar-primary-foreground: #FEFEFF; + --sidebar-accent: #25231F; + --sidebar-accent-foreground: #FEFEFF; + --sidebar-border: #2E2C29; + --sidebar-ring: #2C7AFF; + + /* Module accent palette */ + --color-datasets: #FFA578; + --color-models: #2C7AFF; + --color-generative: #90F1C4; + --color-plugins: #A54DA9; + --brand-light: #A7C7FF; } .dark { - --background: oklch(0.08 0 0); - --foreground: oklch(0.98 0 0); - --card: oklch(0.12 0 0); - --card-foreground: oklch(0.98 0 0); - --popover: oklch(0.12 0 0); - --popover-foreground: oklch(0.98 0 0); - --primary: oklch(0.75 0.18 195); - --primary-foreground: oklch(0.1 0 0); - --secondary: oklch(0.28 0 0); - --secondary-foreground: oklch(0.98 0 0); - --muted: oklch(0.22 0 0); - --muted-foreground: oklch(0.65 0 0); - --accent: oklch(0.65 0.18 45); - --accent-foreground: oklch(0.1 0 0); - --destructive: oklch(0.577 0.245 27.325); - --destructive-foreground: oklch(0.985 0 0); - --border: oklch(0.28 0 0); - --input: oklch(0.28 0 0); - --ring: oklch(0.75 0.18 195); - --chart-1: oklch(0.75 0.18 195); - --chart-2: oklch(0.65 0.18 45); - --chart-3: oklch(0.6 0.15 240); - --chart-4: oklch(0.7 0.15 280); - --chart-5: oklch(0.55 0.12 210); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(0.269 0 0); - --sidebar-ring: oklch(0.439 0 0); + /* same palette — site is always dark */ + --background: #191817; + --foreground: #FEFEFF; + --card: #1F1E1D; + --card-foreground: #FEFEFF; + --popover: #1F1E1D; + --popover-foreground: #FEFEFF; + --primary: #2C7AFF; + --primary-foreground: #191817; + --secondary: #25231F; + --secondary-foreground: #FEFEFF; + --muted: #1B1A19; + --muted-foreground: #B5B0A8; + --accent: #FFA578; + --accent-foreground: #191817; + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: #2E2C29; + --input: #2E2C29; + --ring: #2C7AFF; + --chart-1: #2C7AFF; + --chart-2: #FFA578; + --chart-3: #90F1C4; + --chart-4: #A54DA9; + --chart-5: #A7C7FF; + --sidebar: #131211; + --sidebar-foreground: #FEFEFF; + --sidebar-primary: #2C7AFF; + --sidebar-primary-foreground: #FEFEFF; + --sidebar-accent: #25231F; + --sidebar-accent-foreground: #FEFEFF; + --sidebar-border: #2E2C29; + --sidebar-ring: #2C7AFF; } @theme inline { - /* optional: --font-sans, --font-serif, --font-mono if they are applied in the layout.tsx */ + --font-sans: 'Geist', 'Geist Fallback', ui-sans-serif, system-ui, sans-serif; + --font-mono: 'Geist Mono', 'Geist Mono Fallback', ui-monospace, Menlo, monospace; --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); @@ -112,7 +122,65 @@ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); + + /* Module palette — usable as bg-datasets, text-generative, etc. */ + --color-datasets: var(--color-datasets); + --color-models: var(--color-models); + --color-generative: var(--color-generative); + --color-plugins: var(--color-plugins); + --color-brand-light: var(--brand-light); +} + +/* ── Light-section paper palette ── */ +:root { + --paper: #FEFEFF; + --paper-deep: #F4F4F2; + --paper-line: #E3E2DF; + --on-light: #191817; + --on-light-mute: #4A4744; + --on-light-faint: #6C685F; + --ink-deep: #131211; + --ink-line: #2E2C29; + --ease-out-bold: cubic-bezier(.16,1,.3,1); +} + +/* ── Marquee ── */ +@keyframes marquee-scroll { + from { transform: translateX(0); } + to { transform: translateX(-50%); } } +.marquee-track { + animation: marquee-scroll 32s linear infinite; + white-space: nowrap; + display: flex; + width: max-content; +} + +/* ── Subtle grid (hero bg) ── */ +.bg-grid-subtle { + background-image: + repeating-linear-gradient(90deg, transparent 0 80px, rgba(255,255,255,.022) 80px 81px), + repeating-linear-gradient(0deg, transparent 0 40px, rgba(255,255,255,.015) 40px 41px); + mask-image: linear-gradient(180deg, black 0%, black 74%, transparent 100%); + -webkit-mask-image: linear-gradient(180deg, black 0%, black 74%, transparent 100%); +} + +/* ── Hero visual entrance ── */ +@keyframes heroIn { + from { opacity: 0; transform: translateY(20px) scale(.98); } + to { opacity: 1; transform: translateY(0) scale(1); } +} +.hero-visual-in { + animation: heroIn 1s cubic-bezier(.16,1,.3,1) .15s both; +} + +/* ── Code syntax colors ── */ +.code-kw { color: #A7C7FF; } +.code-fn { color: #90F1C4; } +.code-str { color: #FFA578; } +.code-num { color: #FEE8FF; } +.code-com { color: #8C877F; font-style: italic; } +.code-cls { color: #FEE8FF; } @layer base { * { @@ -120,5 +188,8 @@ } body { @apply bg-background text-foreground; + font-feature-settings: "ss01","ss03","cv09","cv11"; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; } } diff --git a/app/layout.tsx b/app/layout.tsx index 070369f..99accd4 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -8,22 +8,13 @@ const _geist = Geist({ subsets: ["latin"] }) const _geistMono = Geist_Mono({ subsets: ["latin"] }) export const metadata: Metadata = { - title: "DashAI - Plataforma Open Source de IA", + title: "dashAI - Full Open. Full Extensible.", description: - "Integra, experimenta y visualiza modelos de IA mediante una interfaz visual intuitiva. Democratizando el acceso al machine learning.", + "An open-source no-code Visual ML platform. No paywall, no closed components, no external API keys required.", generator: "v0.app", icons: { - icon: [ - { - url: "/icon-light-32x32.png", - media: "(prefers-color-scheme: light)", - }, - { - url: "/icon-dark-32x32.png", - media: "(prefers-color-scheme: dark)", - }, - ], - apple: "/apple-icon.png", + icon: "/images/dashai-isotype.svg", + apple: "/images/dashai-isotype.svg", }, } diff --git a/app/page.tsx b/app/page.tsx index 27d43db..5f8f82e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -4,12 +4,13 @@ import { useEffect } from "react" import i18n from "i18next" import { Navbar } from "@/components/navbar" import { HeroSection } from "@/components/hero-section" +import { ManifestoSection } from "@/components/manifesto-section" import { FeaturesSection } from "@/components/features-section" -//import { ModulesCarousel } from "@/components/modules-carousel" +import { ShowcaseSection } from "@/components/showcase-section" +import { ExtensibleSection } from "@/components/extensible-section" import { DownloadSection } from "@/components/download-section" -import { SupportedBySection } from "@/components/supported-by-section" import { CommunitySection } from "@/components/community-section" -import { ContactSection } from "@/components/contact-section" +import { SupportedBySection } from "@/components/supported-by-section" import { Footer } from "@/components/footer" import "./i18n" @@ -26,11 +27,13 @@ export default function Home() {
+ + + -
) diff --git a/components/community-section.tsx b/components/community-section.tsx index 304ae2f..173fd64 100644 --- a/components/community-section.tsx +++ b/components/community-section.tsx @@ -1,120 +1,165 @@ "use client" -import { Card, CardContent } from "@/components/ui/card" -import { Github, BookOpen, Code2, Package } from "lucide-react" -import { siteConfig } from "@/lib/config" +import { useState } from "react" import { useTranslation } from "react-i18next" -const communityLinks = [ - { - icon: Github, - key: "github", - defaultTitle: "GitHub", - defaultDescription: "Explore the source code", - link: siteConfig.github.url, - // stats: "2.5k stars", - }, - // { - // icon: Package, - // title: "Plugin Hub", - // description: "Browse community plugins", - // link: siteConfig.resources.pluginHub, - // // stats: "100+ plugins", - // }, - { - icon: BookOpen, - key: "docs", - defaultTitle: "Docs", - defaultDescription: "Visit the technical documentation", - link: siteConfig.docs.url, - // stats: "Complete guides", - }, - { - icon: Code2, - key: "contribute", - defaultTitle: "Contribute", - defaultDescription: "Improve the project", - link: siteConfig.github.contribute, - // stats: "Open to all", - }, -] - export function CommunitySection() { - const { t } = useTranslation() + const { i18n } = useTranslation() + const lang = i18n.language?.startsWith("es") ? "es" : "en" + const [email, setEmail] = useState("") + const [subscribed, setSubscribed] = useState(false) + + function handleSubscribe(e: React.FormEvent) { + e.preventDefault() + setSubscribed(true) + } + return ( -
-
-
-

- {t("community:title", { defaultValue: "Built and extended by the community" })} +
+ {/* Head */} +
+
+
+ + [ 05 ] {lang === "es" ? "Comunidad" : "Community"} +
+
+
+

+ {lang === "es" + ? <>Construyamos esto juntos. + : <>Let's build this together.}

-

- {t("community:description", { - defaultValue: - "DashAI's plugin architecture enables anyone to contribute new capabilities. Build plugins, improve the core, and shape the future of open source AI tools.", - })} +

+ {lang === "es" + ? "dashAI es código abierto y comunidad abierta. Únete a la conversación, propone componentes, comparte experimentos." + : "dashAI is open source and open community. Join the conversation, propose components, share experiments."}

+
-
- {communityLinks.map((item, index) => { - const Icon = item.icon - const title = t(`community:cards.${item.key}.title`, { - defaultValue: item.defaultTitle, - }) - const description = t(`community:cards.${item.key}.description`, { - defaultValue: item.defaultDescription, - }) - return ( - window.open(item.link, "_blank", "noopener,noreferrer")} - > - -
- -
-

{title}

-

{description}

- {item.stats} -
-
- ) - })} + {/* Cards */} +
+ {/* Contact */} +
+
+ + + + {lang === "es" ? "Contacto" : "Contact"} +
+

+ {lang === "es" ? "Escribinos directamente." : "Reach out directly."} +

+

+ {lang === "es" + ? "Consultas, colaboraciones, integraciones institucionales o simplemente para contar qué estás construyendo con dashAI." + : "Questions, collaborations, institutional integrations, or just to share what you're building with dashAI."} +

+ + dashai.nocode@gmail.com → +
- {/* - -

{"Ready to build your first plugin?"}

-

- { - "Our comprehensive documentation guides you through creating plugins and contributing to the core platform" - } -

-
- - + {/* Discord */} +
+
+ + + + Discord +
+

+ {lang === "es" ? "Súmate al servidor de Discord." : "Join the Discord server."} +

+

+ {lang === "es" + ? "Soporte en vivo, anuncios de release, hilos por módulo (Datasets, Modelos, Generativo, Plugins) y el canal #show-and-tell para que compartas tus experimentos." + : "Live support, release announcements, threads per module (Datasets, Models, Generative, Plugins) and a #show-and-tell channel to share your experiments."} +

+ { (e.currentTarget as HTMLElement).style.opacity = "0.85" }} + onMouseLeave={e => { (e.currentTarget as HTMLElement).style.opacity = "1" }} + > + {lang === "es" ? "Unirme al Discord" : "Join Discord"} → + +
+ + {/* Newsletter */} +
+
+ + + + Newsletter +
+

+ {lang === "es" ? "Una actualización mensual." : "One monthly digest."} +

+

+ {lang === "es" + ? "Nuevas features, modelos agregados al catálogo, plugins de la comunidad, papers relevantes. Sin spam, sin promos — solo lo que importa para tu workflow." + : "New features, models added to the catalog, community plugins, relevant papers. No spam, no promos — just what matters for your workflow."} +

+ {subscribed ? ( +
+ {lang === "es" ? "¡Suscripto!" : "Subscribed!"}
- - */} + ) : ( +
+ setEmail(e.target.value)} + placeholder={lang === "es" ? "tu@email.com" : "you@email.com"} + required + aria-label="Email" + className="flex-1 min-w-0 px-3.5 py-2.5 text-sm rounded-md border border-border bg-transparent text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary transition-colors" + /> + +
+ )} +

+ {lang === "es" + ? "· Cancela cuando quieras · 0 emails comerciales" + : "· Unsubscribe anytime · 0 commercial emails"} +

+
) diff --git a/components/download-section.tsx b/components/download-section.tsx index 5f9127c..020f36c 100644 --- a/components/download-section.tsx +++ b/components/download-section.tsx @@ -1,178 +1,230 @@ "use client" -import { Button } from "@/components/ui/button" -import { Card, CardContent } from "@/components/ui/card" -import { Download, Package } from "lucide-react" import { useEffect, useState } from "react" import { useTranslation } from "react-i18next" import { findAsset, formatBytes, getLatestRelease } from "@/lib/github" +import { siteConfig } from "@/lib/config" const SOURCEFORGE_URL = "https://sourceforge.net/projects/dashai/files/latest/download" +const TRACKER_URL = process.env.NEXT_PUBLIC_TRACKER_URL -type DownloadEntry = { - id: "mac_intel" | "mac_arm" | "windows" - icon: typeof Package - key: string - defaultPlatform: string - version: string - size: string - defaultFormat: string - link: string +async function trackClick(buttonId: string) { + if (!TRACKER_URL) return + try { await fetch(`${TRACKER_URL}/click/${buttonId}`, { method: "POST" }) } catch {} } -const FALLBACK_DOWNLOADS: DownloadEntry[] = [ - { - id: "mac_intel", - icon: Package, - key: "macIntel", - defaultPlatform: "macOS Intel processors", - version: "", - size: "", - defaultFormat: ".dmg", - link: SOURCEFORGE_URL, - }, - { - id: "mac_arm", - icon: Package, - key: "macArm", - defaultPlatform: "macOS ARM processors", - version: "", - size: "", - defaultFormat: ".dmg", - link: SOURCEFORGE_URL, - }, - { - id: "windows", - icon: Package, - key: "windows", - defaultPlatform: "Windows", - version: "", - size: "", - defaultFormat: ".exe", - link: SOURCEFORGE_URL, - }, -] +type ExeEntry = { id: "mac_intel" | "mac_arm" | "windows"; os: string; ext: string; link: string; version: string; size: string } -const TRACKER_URL = process.env.NEXT_PUBLIC_TRACKER_URL +const FALLBACK: ExeEntry[] = [ + { id: "windows", os: "Windows", ext: ".exe", link: SOURCEFORGE_URL, version: "", size: "" }, + { id: "mac_arm", os: "macOS (ARM)", ext: ".dmg", link: SOURCEFORGE_URL, version: "", size: "" }, + { id: "mac_intel",os: "macOS (Intel)", ext: ".dmg",link: SOURCEFORGE_URL, version: "", size: "" }, +] -async function trackClick(buttonId: string) { - if (!TRACKER_URL) return - try { - await fetch(`${TRACKER_URL}/click/${buttonId}`, { - method: "POST", - }) - } catch { - // non-blocking — don't let tracking errors affect the download +function CopyButton({ text }: { text: string }) { + const [copied, setCopied] = useState(false) + const copy = () => { + navigator.clipboard.writeText(text).catch(() => {}) + setCopied(true) + setTimeout(() => setCopied(false), 1400) } + return ( + + ) } export function DownloadSection() { - const { t } = useTranslation() - const [downloads, setDownloads] = useState(FALLBACK_DOWNLOADS) + const { i18n } = useTranslation() + const lang = i18n.language?.startsWith("es") ? "es" : "en" + const [exes, setExes] = useState(FALLBACK) + const [releaseTag, setReleaseTag] = useState("v0.9.3-alpha") useEffect(() => { - getLatestRelease().then((release) => { + getLatestRelease().then(release => { if (!release) return - setDownloads( - FALLBACK_DOWNLOADS.map((entry) => { - const asset = findAsset(release.assets, entry.id) - if (!asset) return entry - return { - ...entry, - version: release.tag_name, - size: formatBytes(asset.size), - link: asset.browser_download_url, - } - }) - ) + setReleaseTag(release.tag_name) + setExes(FALLBACK.map(entry => { + const asset = findAsset(release.assets, entry.id) + if (!asset) return entry + return { ...entry, version: release.tag_name, size: formatBytes(asset.size), link: asset.browser_download_url } + })) }) }, []) + const pipCmd = "pip install dashai\ndashai" + return ( -
-
-
-

- {t("download:title", { defaultValue: "Download DashAI" })} +
+ {/* Horizontal line pattern */} +
+ + {/* Head */} +
+
+
+ + [ 04 ] {lang === "es" ? "Descarga" : "Download"} +
+
+
+

+ {lang === "es" ? <>Descargá dashAI. : <>Download dashAI.}

- - {t("download:badge", { defaultValue: "Beta Version" })} - -

- {t("download:description", { - defaultValue: - "This early version lets you explore DashAI's main features. We appreciate your feedback to help us improve before the official release.", - })} +

+ {releaseTag} + ·MIT License + ·Linux · macOS · Windows + ·{lang === "es" ? "Mayo 2026" : "May 2026"} +
+
+
+ + {/* 3-col grid */} +
+ {/* Card 1 — executables */} +
+
+ 01 + {lang === "es" ? "Ejecutables (recomendado)" : "Executables (recommended)"} +
+

{lang === "es" ? "Instalador nativo" : "Native installer"}

+

+ {lang === "es" + ? "Sin Python ni configuración. Descargá el ejecutable de tu sistema operativo desde GitHub Releases." + : "No Python, no setup. Download the executable for your OS from GitHub Releases."} +

+ + + {lang === "es" ? "Ver releases" : "View releases"} → + +
+ + {/* Card 2 — pip */} +
+
+ 02 + PyPI +
+

{lang === "es" ? "Instalación con pip" : "Install with pip"}

+

+ {lang === "es" + ? <>dashai levanta el servidor y abre el navegador en localhost:8000. + : <>dashai starts the server and opens the browser at localhost:8000.}

+
+ +
$ pip install dashai
+
$ dashai
+
+ # {lang === "es" ? "http://localhost:8000/ — El navegador abre solo." : "http://localhost:8000/ — Browser opens on its own."} +
+
+ + {lang === "es" ? "Ver en PyPI" : "View on PyPI"} → +
-
-
- {downloads.map((download) => { - const Icon = download.icon - const platform = t(`download:cards.${download.key}.platform`, { - defaultValue: download.defaultPlatform, - }) - const format = t(`download:cards.${download.key}.format`, { - defaultValue: download.defaultFormat, - }) - return ( - - -
- -
-

{platform}

-
- {download.version && ( -

- {download.version} • {download.size} -

- )} -

{format}

-
- -
-
- ) - })} + {/* Card 3 — requirements */} +
+
+ + {lang === "es" ? "Requisitos" : "Requirements"}
+

{lang === "es" ? "Lo que necesitás" : "What you need"}

+
    + {[ + { k: "Python", v: "≥ 3.10", note: lang === "es" ? "Solo para instalación pip" : "Only for pip install" }, + { k: "RAM", v: "8 GB", note: lang === "es" ? "16 GB para LLMs locales" : "16 GB for local LLMs" }, + { k: "OS", v: "Linux · macOS · Windows", note: lang === "es" ? "Ejecutables nativos disponibles" : "Native executables available" }, + ].map(r => ( +
  • + {r.k} + + {r.v} + {r.note} + +
  • + ))} +
+
+
- - -
-
-

- {t("download:pip.title", { defaultValue: "Install via pip" })} -

-

- {t("download:pip.subtitle", { - defaultValue: "For developers who prefer command-line installation", - })} -

-
-
-
- - {t("download:pip.command", { defaultValue: "pip install dashai" })} - -
-
-
-
-
+ {/* Footer row */} +
+
+ {lang === "es" ? "¿Querés modificar el núcleo? Cloná el repo:" : "Want to modify the core? Clone the repo:"} + + git clone github.com/DashAISoftware/DashAI + +
+
diff --git a/components/extensible-section.tsx b/components/extensible-section.tsx new file mode 100644 index 0000000..a2b948b --- /dev/null +++ b/components/extensible-section.tsx @@ -0,0 +1,181 @@ +"use client" + +import { useTranslation } from "react-i18next" + +const PAPER = "#FEFEFF" +const PAPER_LINE = "#E3E2DF" +const ON_LIGHT = "#191817" +const ON_LIGHT_MUTE = "#4A4744" +const ON_LIGHT_FAINT = "#6C685F" +const PLUGINS_COLOR = "#1E63D8" +const BRAND_ON_LIGHT = "#1E63D8" +const CODE_BG = "#131211" +const CODE_HEADER_BG = "#191817" +const CODE_BORDER = "#2E2C29" + +const MODULES = [ + { num: "01", name: "BaseTask", role: { es: "Tarea", en: "Task" }, desc: { es: "Paradigmas extensibles: clasificación, regresión, NLP, traducción, generación.", en: "Extensible paradigms: classification, regression, NLP, translation, generation." } }, + { num: "02", name: "BaseModel", role: { es: "Modelo predictivo", en: "Predictive model"}, desc: { es: "Random Forest a transformers. Subclase + schema, listo para HPO.", en: "Random Forest to transformers. Subclass + schema, ready for HPO." } }, + { num: "03", name: "BaseGenerativeModel", role: { es: "Modelo generativo", en: "Generative model"}, desc: { es: "LLMs e imagen como ciudadanos de primera clase.", en: "LLMs and image as first-class citizens." } }, + { num: "04", name: "BaseMetric", role: { es: "Métrica", en: "Metric" }, desc: { es: "Classification, Regression, Translation. Aparece sola en la UI.", en: "Classification, Regression, Translation. Auto-appears in UI." } }, + { num: "05", name: "BaseOptimizer", role: { es: "Optimización HPO", en: "HPO optimizer" }, desc: { es: "Optuna + HyperOpt. Estrategias bayesianas, evolutivas, QMC.", en: "Optuna + HyperOpt. Bayesian, evolutionary, QMC strategies." } }, + { num: "06", name: "BaseDataLoader", role: { es: "Carga de datos", en: "Data loader" }, desc: { es: "CSV, JSON, imágenes, audio. Cada loader declara su formato.", en: "CSV, JSON, images, audio. Each loader declares its format." } }, + { num: "07", name: "BaseExplorer", role: { es: "EDA / exploración", en: "EDA / exploration"}, desc: { es: "Visualizaciones, perfilado, distribuciones — tipado.", en: "Visualizations, profiling, distributions — typed." } }, + { num: "08", name: "BaseConverter", role: { es: "Conversor", en: "Converter" }, desc: { es: "Transformaciones tipadas entre formatos y representaciones.", en: "Typed transformations between formats and representations." } }, + { num: "09", name: "BaseGlobalExplainer", role: { es: "XAI global", en: "Global XAI" }, desc: { es: "Permutation importance, surrogate, partial dependence.", en: "Permutation importance, surrogate, partial dependence." } }, + { num: "10", name: "BaseLocalExplainer", role: { es: "XAI local", en: "Local XAI" }, desc: { es: "SHAP, LIME, counterfactuals con abstracción dedicada.", en: "SHAP, LIME, counterfactuals with dedicated abstraction." } }, + { num: "11", name: "BaseJob", role: { es: "Orquestación", en: "Orchestration" }, desc: { es: "Train, predict, explain, explore, convert, generate, evaluate, optimize.", en: "Train, predict, explain, explore, convert, generate, evaluate, optimize." } }, + { num: "12", name: "BaseGenerativeTask", role: { es: "Tarea generativa", en: "Generative task" }, desc: { es: "Generación condicional, text-to-image, seq-to-seq.", en: "Conditional generation, text-to-image, seq-to-seq." } }, +] + +export function ExtensibleSection() { + const { i18n } = useTranslation() + const lang = i18n.language?.startsWith("es") ? "es" : "en" + + return ( +
+ {/* Section head */} +
+
+
+ + [ 03 ] {lang === "es" ? "Extensibilidad" : "Extensibility"} +
+
+
+
+ {"{ II }"} + + {lang === "es" ? "Doce abstracciones" : "Twelve abstractions"} +
+

+ Full Extensible. +

+

+ {lang === "es" + ? "Cada rol funcional tiene su clase base. Veinte a cuarenta líneas para un componente nuevo. La UI emerge del schema, no se programa." + : "Each functional role has its base class. Twenty to forty lines for a new component. The UI emerges from the schema — it isn't programmed."} +

+
+
+ + {/* 12 Modules grid */} +
+ {MODULES.map(m => ( +
(e.currentTarget.style.background = "#F4F4F2")} + onMouseLeave={e => (e.currentTarget.style.background = "")} + > +
{m.num}
+
{m.name}
+
{lang === "es" ? m.role.es : m.role.en}
+
{lang === "es" ? m.desc.es : m.desc.en}
+
+ {[14, 22, 14].map((w, i) => )} +
+
+ ))} +
+ + {/* Arch grid */} +
+ {/* Arch points */} +
+ {[ + { ico: "⬡", title: "Schema-driven UI", body: { es: "El servidor expone el schema Pydantic; el frontend React renderiza el formulario sin conocerlo previamente.", en: "The server exposes the Pydantic schema; the React frontend renders the form without prior knowledge." } }, + { ico: "↻", title: lang === "es" ? "Hot-install desde la UI" : "Hot-install from the UI", body: { es: "Un click instala una extensión desde PyPI. Sin reiniciar, sin shell externa.", en: "One click installs an extension from PyPI. No restart, no external shell." } }, + { ico: "▶", title: lang === "es" ? "API HTTP pública" : "Public HTTP API", body: { es: <>Cualquier cliente HTTP — Python, Jupyter, CLI — consulta componentes y lanza jobs. GET /component/, POST /job/., en: <>Any HTTP client — Python, Jupyter, CLI — queries components and launches jobs. GET /component/, POST /job/. } }, + { ico: "⚡", title: "FastAPI + React, not desktop", body: { es: "El servidor corre en cualquier máquina, accesible desde el navegador — local, red o remoto.", en: "The server runs anywhere, reachable from any browser — local, network or remote." } }, + ].map(p => ( +
+
+ {p.ico} +
+
+

{p.title}

+

{lang === "es" ? p.body.es : p.body.en}

+
+
+ ))} +
+ + {/* Code block */} +
+
+
+ + + +
+ my_classifier.py +
+
+            {`\
+`}# {lang === "es" ? "Un clasificador completo en dashAI" : "A complete classifier in dashAI"}{`
+`}from{` dashai.base `}import{` `}BaseModel{`
+`}from{` dashai.core.schema_fields `}import{` schema_field, optimizer_int_field
+`}from{` sklearn.ensemble `}import{` RandomForestClassifier
+
+`}class{` `}MyForestSchema{`(BaseSchema):
+    n_estimators: `}schema_field{`(
+        `}optimizer_int_field{`(ge=`}1{`),
+        placeholder={
+            `}"optimize"{`: `}False{`, `}"fixed_value"{`: `}100{`,
+            `}"lower_bound"{`: `}50{`, `}"upper_bound"{`: `}200{`,
+        },
+        description=`}"{lang === "es" ? "Número de árboles" : "Number of trees"}"{`,
+    )
+    max_depth: `}schema_field{`(
+        `}optimizer_int_field{`(ge=`}1{`),
+        placeholder={
+            `}"optimize"{`: `}False{`, `}"fixed_value"{`: `}2{`,
+            `}"lower_bound"{`: `}2{`, `}"upper_bound"{`: `}10{`,
+        },
+        description=`}"{lang === "es" ? "Profundidad máxima" : "Maximum depth"}"{`,
+    )
+
+`}class{` `}MyForest{`(`}BaseModel{`):
+    SCHEMA = MyForestSchema
+
+    `}def{` `}train{`(`}self{`, X, y):
+        `}self{`.model = RandomForestClassifier(
+            **`}self{`.params
+        ).fit(X, y)
+
+    `}def{` `}predict{`(`}self{`, X):
+        `}return{` `}self{`.model.predict(X)`}
+          
+
+ {lang === "es" ? "33 líneas · UI auto-generada" : "33 lines · UI auto-generated"} + python ≥ 3.10 +
+
+
+
+ ) +} diff --git a/components/features-section.tsx b/components/features-section.tsx index 2036451..372ba86 100644 --- a/components/features-section.tsx +++ b/components/features-section.tsx @@ -1,141 +1,182 @@ "use client" -import { useEffect, useRef, useState } from "react" -import { Trans, useTranslation } from "react-i18next" +import { useTranslation } from "react-i18next" -const featureItems = [ - { - id: 1, - key: "dataExplorer", - image: "features/eda_module.png", - defaultTitle: "Data Explorer", - defaultDescription: - "Explore, clean, and prepare your data in an intuitive visual environment: perform dynamic, visual, and guided exploratory data analyses (EDA) directly within DashAI.", - }, - { - id: 2, - key: "mlExperimentation", - image: "features/ml_experimentation.png", - defaultTitle: "ML Experimentation", - defaultDescription: - "Set up and manage your ML experiments with an intuitive interface. Select models, configure its hyperparameters, and define evaluation metrics without writing a single line of code.", - }, - { - id: 3, - key: "resultsVisualization", - image: "features/results.png", - defaultTitle: "Results Visualization", - defaultDescription: - "Compare model performance across multiple experiments. Interactive dashboards help you identify the best performing models and understand their behavior through comprehensive metrics and visualizations.", - }, - { - id: 4, - key: "modelExplainability", - image: "features/explainability_module.png", - defaultTitle: "Model Explainability", - defaultDescription: - "Understand how your models make decisions. Generate feature importance plots, SHAP values, and interpretable visualizations that help you trust and debug your AI models.", - }, - { - id: 5, - key: "generativeModule", - image: "features/generative_module.png", - defaultTitle: "Generative Module", - defaultDescription: - "Experiment with generative AI models seamlessly. Generate text, images, and other content while visualizing the generation process and fine-tuning parameters in real-time.", - }, -] +const PAPER = "#FEFEFF" +const PAPER_LINE = "#E3E2DF" +const ON_LIGHT = "#191817" +const ON_LIGHT_MUTE = "#4A4744" +const ON_LIGHT_FAINT = "#6C685F" +const BRAND_ON_LIGHT = "#1E63D8" export function FeaturesSection() { - const { t } = useTranslation() - const [visibleFeatures, setVisibleFeatures] = useState>(new Set()) - const featureRefs = useRef<(HTMLDivElement | null)[]>([]) - - useEffect(() => { - const observers = featureRefs.current.map((ref, index) => { - if (!ref) return null - - const observer = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting) { - setVisibleFeatures((prev) => new Set(prev).add(index)) - } - }) - }, - { threshold: 0.2 } - ) - - observer.observe(ref) - return observer - }) - - return () => { - observers.forEach((observer) => observer?.disconnect()) - } - }, []) + const { i18n } = useTranslation() + const lang = i18n.language?.startsWith("es") ? "es" : "en" return ( -
-
-
- -

- Powerful Features -

-
-

- {t("features:description", { - defaultValue: - "Discover the powerful features that make DashAI the ultimate platform for AI experimentation", - })} +

+ {/* Section head */} +
+
+
+ + [ 01 ] {lang === "es" ? "Apertura" : "Openness"} +
+
+
+
+ {"{ I }"} + + {lang === "es" ? "Cuatro pruebas" : "Four proofs"} +
+

+ Full Open. +

+

+ {lang === "es" + ? "La apertura no es una declaración de licencia: es una propiedad medible del sistema." + : "Openness isn't a license statement: it's a measurable property of the system."}

+
-
- {featureItems.map((feature, index) => { - const isEven = index % 2 === 0 - const isVisible = visibleFeatures.has(index) - const title = t(`features:items.${feature.key}.title`, { - defaultValue: feature.defaultTitle, - }) - const description = t(`features:items.${feature.key}.description`, { - defaultValue: feature.defaultDescription, - }) + {/* 4 Proofs */} +
+ {[ + { + num: "01", sub: lang === "es" ? "Licencia" : "License", + title: lang === "es" ? <>Licencia MIT, sin copyleft. : <>MIT licensed, no copyleft., + body: lang === "es" + ? "Integrable en sistemas institucionales y productos comerciales. Sin obligación de liberar lo que construís encima." + : "Integrable into institutional systems and commercial products. No obligation to release what you build on top.", + }, + { + num: "02", sub: lang === "es" ? "Núcleo" : "Core", + title: lang === "es" ? <>Sin paywall productivo. : <>No productive paywall., + body: lang === "es" + ? "Scheduling, deployment, RBAC y gobernanza viven en el núcleo. El proyecto que clonás hoy es el que llega a producción." + : "Scheduling, deployment, RBAC and governance live in the core. The project you clone today is what reaches production.", + }, + { + num: "03", sub: lang === "es" ? "Ecosistema" : "Ecosystem", + title: lang === "es" ? <>Sin componentes cerrados. : <>No closed components., + body: lang === "es" + ? "Toda extensión oficial comparte la licencia del núcleo. Auditable, modificable, forkeable bajo los mismos términos." + : "Every official extension shares the core license. Auditable, modifiable, forkable under the same terms.", + }, + { + num: "04", sub: lang === "es" ? "Ejecución" : "Execution", + title: lang === "es" ? <>Sin claves API externas requeridas. : <>No external API keys required., + body: lang === "es" + ? "Los modelos del núcleo corren con pesos abiertos. El sistema de plugins te permite agregar cualquier modelo —local o vía API— pero el núcleo nunca exige dependencias externas." + : "Core models run entirely on open weights. The plugin system lets you add any model — local or API-backed — but nothing external is ever required.", + }, + ].map(p => ( +
+
+ {p.num} / {p.sub} +
+

+ {p.title} +

+

{p.body}

+
+ ))} +
- return ( -
{ - featureRefs.current[index] = el - }} - className={`flex flex-col ${ - isEven ? "lg:flex-row" : "lg:flex-row-reverse" - } gap-12 items-center transition-all duration-1000 ${ - isVisible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-12" - }`} - > -
-

{title}

-

- {description} -

-
+ {/* Catalog header */} +
+

+ {lang === "es" ? "Cobertura nativa, ejecución local." : "Native coverage, local execution."} +

+ + {lang === "es" ? "⊳ La apertura puesta a prueba" : "⊳ Openness, in practice"} + +
-
-
- {feature.defaultTitle} -
-
-
-
- ) - })} -
+ {/* Catalog grid */} +
+ {[ + { + kind: lang === "es" ? "Tabular" : "Tabular", + swatchColor: BRAND_ON_LIGHT, + title: lang === "es" ? "Clasificación & regresión" : "Classification & regression", + count: "15 + 15", + body: lang === "es" + ? <>Treinta modelos sobre scikit-learn y boosting. Cada uno como subclase de BaseModel. + : <>Thirty models over scikit-learn and boosting. Each one as a subclass of BaseModel., + tags: ["RandomForest","XGBoost","LightGBM","SVM","MLP","+25"], + countColor: BRAND_ON_LIGHT, + }, + { + kind: lang === "es" ? "NLP + Traducción" : "NLP + Translation", + swatchColor: BRAND_ON_LIGHT, + title: lang === "es" ? "Transformers y seq-to-seq" : "Transformers & seq-to-seq", + count: "15 + 9", + body: lang === "es" + ? <>Clasificadores y modelos de traducción neural con métricas BLEU · ChrF · TER nativas. + : <>Classifiers and neural translation models with native BLEU · ChrF · TER metrics., + tags: ["DistilBERT","DeBERTa-v3","ModernBERT","NLLB","OpusMT","+19"], + countColor: BRAND_ON_LIGHT, + }, + { + kind: lang === "es" ? "Modelos generativos" : "Generative models", + swatchColor: BRAND_ON_LIGHT, + title: lang === "es" ? "LLMs locales" : "Local LLMs", + count: "5", + body: lang === "es" + ? "Llama, Mistral, Mixtral, Qwen y SmolLM con pesos abiertos. Generación como tarea nativa." + : "Llama, Mistral, Mixtral, Qwen and SmolLM on open weights. Generation as a native task.", + tags: ["Llama","Mistral","Mixtral","Qwen","SmolLM"], + countColor: BRAND_ON_LIGHT, + }, + { + kind: lang === "es" ? "Visión generativa" : "Generative vision", + swatchColor: BRAND_ON_LIGHT, + title: lang === "es" ? "Texto a imagen" : "Text to image", + count: "11", + body: lang === "es" + ? "PixArt-Sigma, SDXL, Stable Diffusion y variantes ControlNet. Detección automática de CUDA." + : "PixArt-Sigma, SDXL, Stable Diffusion and ControlNet variants. Automatic CUDA detection.", + tags: ["PixArt-Sigma","SDXL","SD v3","SD v2","ControlNet","+6"], + countColor: BRAND_ON_LIGHT, + }, + ].map(c => ( +
+
+ + {c.kind} +
+

{c.title}

+
{c.count}
+

{c.body}

+
    + {c.tags.map(tag => ( +
  • + {tag} +
  • + ))} +
+
+ ))}
) diff --git a/components/footer.tsx b/components/footer.tsx index 63de12f..fe56d1b 100644 --- a/components/footer.tsx +++ b/components/footer.tsx @@ -1,138 +1,144 @@ "use client" -import { Github, Twitter, Linkedin } from "lucide-react" -import { siteConfig } from "@/lib/config" import { useTranslation } from "react-i18next" - -const productLinks = [ - { - href: "#features", - defaultLabel: "Features", - key: "features", - external: false, - }, - { - href: siteConfig.resources.changelog, - defaultLabel: "Changelog", - key: "changelog", - external: true, - }, -] - -const resourceLinks = [ - { - href: siteConfig.docs.url, - defaultLabel: "Documentation", - key: "documentation", - external: true, - }, -] - -const communityLinks = [ - { - href: siteConfig.github.url, - defaultLabel: "GitHub", - key: "github", - external: true, - }, - { - href: siteConfig.github.contribute, - defaultLabel: "Contribute", - key: "contribute", - external: true, - }, -] +import { siteConfig } from "@/lib/config" export function Footer() { - const { t } = useTranslation() + const { i18n } = useTranslation() + const lang = i18n.language?.startsWith("es") ? "es" : "en" + return ( -
-
-
-
-

- {t("footer:brand.title", { defaultValue: "DashAI" })} -

-

- {t("footer:brand.description", { - defaultValue: "Open source platform to democratize access to artificial intelligence.", - })} -

-
-
-

- {t("footer:sections.product.title", { defaultValue: "Product" })} -

- -
-
-

- {t("footer:sections.resources.title", { defaultValue: "Resources" })} -

- +
+ {/* 4-col grid */} +
+ {/* Brand col */} +
+
+ dash.AI
-
-

- {t("footer:sections.community.title", { defaultValue: "Community" })} -

- + {/* Palette ribbon */} + +

+ {lang === "es" + ? "Proyecto open-source desarrollado en CENIA, Chile. Licencia MIT. Visual ML Full Open · Full Extensible." + : "Open-source project developed at CENIA, Chile. MIT licensed. Visual ML, Full Open · Full Extensible."} +

-
-

- {t("footer:copyright", { defaultValue: "© 2025 DashAI" })} -

-
- - - - {/* - - - - - */} -
+ {/* Project col */} +
+
+ {lang === "es" ? "Proyecto" : "Project"} +
+
    + {[ + { label: { es: "GitHub", en: "GitHub" }, href: siteConfig.github.url, external: true }, + { label: { es: "Documentación", en: "Documentation" }, href: siteConfig.docs.url, external: true }, + { label: { es: "Descargar", en: "Download" }, href: "#download", external: false }, + { label: { es: "La aplicación", en: "The application" }, href: "#showcase", external: false }, + ].map(l => ( +
  • + + {lang === "es" ? l.label.es : l.label.en} + +
  • + ))} +
+
+ + {/* Resources col */} +
+
+ {lang === "es" ? "Recursos" : "Resources"} +
+
    + {[ + { label: { es: "Paper", en: "Paper" }, href: "#" }, + { label: { es: "Tutoriales", en: "Tutorials" }, href: "#" }, + { label: { es: "Colaboradores", en: "Collaborators" }, href: "#colaboradores" }, + { label: { es: "Notas de release", en: "Release notes" }, href: siteConfig.resources.changelog }, + ].map(l => ( +
  • + + {lang === "es" ? l.label.es : l.label.en} + +
  • + ))} +
+
+ + {/* Community col */} +
+
+ {lang === "es" ? "Comunidad" : "Community"} +
+
    + {[ + { label: { es: "Discord", en: "Discord" }, href: "https://discord.gg/dashai" }, + { label: { es: "Contribuir", en: "Contribute" }, href: siteConfig.github.contribute }, + { label: { es: "Issues", en: "Issues" }, href: siteConfig.github.issues }, + { label: { es: "PyPI", en: "PyPI" }, href: "https://pypi.org/project/dashai/" }, + ].map(l => ( +
  • + + {lang === "es" ? l.label.es : l.label.en} + +
  • + ))} +
+
+
+ + {/* Bottom bar */} +
+
+ © 2026 DashAI + MIT License + CENIA · Chile +
+
diff --git a/components/hero-section.tsx b/components/hero-section.tsx index 0674659..6542052 100644 --- a/components/hero-section.tsx +++ b/components/hero-section.tsx @@ -1,103 +1,181 @@ "use client" -import { Button } from "@/components/ui/button" -import { Github, BookOpen, Download } from "lucide-react" -import Image from "next/image" +import { useTranslation } from "react-i18next" +import { Upload, FlaskConical, Sparkles, Puzzle } from "lucide-react" import { siteConfig } from "@/lib/config" -import { useTranslation, Trans } from "react-i18next" -export function HeroSection() { - const { t } = useTranslation() - const scrollToSection = (id: string) => { - const element = document.getElementById(id) - if (element) { - element.scrollIntoView({ behavior: "smooth" }) - } - } +function AppMockup({ lang }: { lang: string }) { return ( -
- {/* Decorative lines background */} -
- - - - - - +
+ {/* Chrome */} +
+
+ + + +
+
+ localhost:8000 +
+ + {lang === "es" ? "ES" : "EN"} ▾ +
-
-
-
- DashAI Logo + {/* Body */} +
+ {/* Header row */} +
+
+ dash.AI
+
+ Datasets + Models + Generative + Plugins +
+
- -

- {"The "} - open source - {" platform for AI experimentation"} -

-
- -

- { - t("hero:description", "Integrate, experiment, and visualize AI model results through an intuitive visual interface. Built with an extensible plugin architecture for limitless customization.") - } + {/* Welcome */} +

+

+ Welcome to dashAI! +

+

+ Pick a module to start

+
+ + {/* Module grid */} +
+ {[ + { cls: "border-[rgba(255,165,120,.32)]", ico: , icoStyle: { background: "rgba(121,49,12,.45)", color: "#FFA578" }, name: "Datasets", tags: ["IMPORT","CLEAN","EDA"], tagStyle: { background: "rgba(121,49,12,.40)", color: "#FFA578" } }, + { cls: "border-[rgba(44,122,255,.32)]", ico: , icoStyle: { background: "rgba(44,122,255,.20)", color: "#A7C7FF" }, name: "Models", tags: ["TABULAR","NLP","REGR"], tagStyle: { background: "rgba(44,122,255,.20)", color: "#A7C7FF" } }, + { cls: "border-[rgba(144,241,196,.32)]", ico: , icoStyle: { background: "rgba(0,89,103,.45)", color: "#90F1C4" }, name: "Generative", tags: ["LLM","T2I","INFER"], tagStyle: { background: "rgba(0,89,103,.55)", color: "#90F1C4" } }, + { cls: "border-[rgba(165,77,169,.32)]", ico: , icoStyle: { background: "rgba(165,77,169,.28)",color: "#FEE8FF" }, name: "Plugins", tags: ["INSTALL","EXTEND"], tagStyle: { background: "rgba(165,77,169,.28)", color: "#FEE8FF" } }, + ].map(m => ( +
+
{m.ico}
+
{m.name}
+
+ {m.tags.map(tag => ( + {tag} + ))} +
+
+ ))} +
+
+
+ ) +} + +export function HeroSection() { + const { i18n } = useTranslation("hero") + const lang = i18n.language?.startsWith("es") ? "es" : "en" + + const scrollTo = (id: string) => document.getElementById(id)?.scrollIntoView({ behavior: "smooth" }) + + return ( +
+ {/* Grid overlay */} +
-
- -

+ +

+ {lang === "es" + ? "Una plataforma open-source de Visual ML sin código. Sin paywall, sin componentes cerrados, sin claves API externas." + : "An open-source no-code Visual ML platform. No paywall, no closed components, no external API keys."} +

+ +
+ - + (e.currentTarget.style.background = "rgba(254,254,255,.08)")} + onMouseLeave={e => (e.currentTarget.style.background = "")} > - - - {t("hero:documentation", "Documentation")} - - + {lang === "es" ? "Ver en GitHub" : "View on GitHub"} +
+
- {/*
-
-
- {"100% Open Source"} -
-
-
- {"No complex setup"} -
-
*/} + {/* Visual */} +
+
+
) } diff --git a/components/manifesto-section.tsx b/components/manifesto-section.tsx new file mode 100644 index 0000000..ce4e21d --- /dev/null +++ b/components/manifesto-section.tsx @@ -0,0 +1,131 @@ +"use client" + +import { useTranslation } from "react-i18next" + +function pipeline(lang: string) { + return lang === "es" ? [ + { label: "Ingesta de Datos", color: "var(--foreground)" }, + { label: "Exploración Visual", color: "#A7C7FF" }, + { label: "Preprocesamiento", color: "var(--muted-foreground)" }, + { label: "Entrenamiento", color: "#A7C7FF" }, + { label: "Evaluación", color: "var(--foreground)" }, + { label: "Explicabilidad", color: "var(--muted-foreground)" }, + ] : [ + { label: "Data Ingestion", color: "var(--foreground)" }, + { label: "Visual Exploration", color: "#A7C7FF" }, + { label: "Preprocessing", color: "var(--muted-foreground)" }, + { label: "Model Training", color: "#A7C7FF" }, + { label: "Evaluation", color: "var(--foreground)" }, + { label: "Explainability", color: "var(--muted-foreground)" }, + ] +} + +function MarqueeStrip({ lang }: { lang: string }) { + const PIPELINE = pipeline(lang) + const items = [...PIPELINE, ...PIPELINE, ...PIPELINE, ...PIPELINE] + + return ( + + ) +} + +export function ManifestoSection() { + const { i18n } = useTranslation() + const lang = i18n.language?.startsWith("es") ? "es" : "en" + + return ( + <> + + +
+ {/* Label */} +
+ + {lang === "es" ? "Manifiesto" : "Manifesto"} + +
+ + {/* Body */} +
+ {lang === "es" ? ( + <> +

+ Una plataforma{" "} + abierta{" "} + no es{" "} + la que se distribuye gratis + : es la que se{" "} + comporta{" "} + de forma abierta. +

+

+ dashAI es{" "} + Full Open{" "} + +{" "} + Full Extensible.{" "} + Esa es la línea editorial. +

+ + ) : ( + <> +

+ An{" "} + open{" "} + platform isn't{" "} + one distributed for free + : it's one that{" "} + behaves{" "} + openly. +

+

+ dashAI is{" "} + Full Open{" "} + +{" "} + Full Extensible.{" "} + That's the editorial line. +

+ + )} +
+
+ + ) +} diff --git a/components/navbar.tsx b/components/navbar.tsx index 80b2969..30b9860 100644 --- a/components/navbar.tsx +++ b/components/navbar.tsx @@ -1,208 +1,141 @@ "use client" import { useState, useEffect } from "react" -import { Button } from "@/components/ui/button" -import { Download, Github, Menu, X, Globe, ChevronDown } from "lucide-react" -import Image from "next/image" import { cn } from "@/lib/utils" import { siteConfig } from "@/lib/config" import { useTranslation } from "react-i18next" -const LANGUAGES = [ - { code: "en", label: "English", flag: "🇺🇸" }, - { code: "es", label: "Español", flag: "🇨🇱" }, -] +export function Navbar() { + const [isScrolled, setIsScrolled] = useState(false) + const [mobileOpen, setMobileOpen] = useState(false) + const { i18n, t } = useTranslation("navbar") -function LanguageSwitcher() { - const { i18n } = useTranslation() - const [isOpen, setIsOpen] = useState(false) + const lang = i18n.language?.startsWith("es") ? "es" : "en" + const setLang = (l: string) => { i18n.changeLanguage(l); setMobileOpen(false) } - const currentLang = LANGUAGES.find((l) => l.code === i18n.language) ?? LANGUAGES[0] + useEffect(() => { + const h = () => setIsScrolled(window.scrollY > 10) + window.addEventListener("scroll", h) + return () => window.removeEventListener("scroll", h) + }, []) - const changeLanguage = (code: string) => { - i18n.changeLanguage(code) - setIsOpen(false) + const scrollTo = (id: string) => { + document.getElementById(id)?.scrollIntoView({ behavior: "smooth" }) + setMobileOpen(false) } - // Close on outside click - useEffect(() => { - if (!isOpen) return - const handler = (e: MouseEvent) => { - const target = e.target as HTMLElement - if (!target.closest("[data-lang-switcher]")) setIsOpen(false) - } - document.addEventListener("mousedown", handler) - return () => document.removeEventListener("mousedown", handler) - }, [isOpen]) + const navLinks = ["manifesto", "open", "showcase", "extensible", "download"] return ( -
- +