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
114 changes: 60 additions & 54 deletions app/.vitepress/config.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import { defineConfig, type DefaultTheme, type UserConfig } from "vitepress";
import { transformerTwoslash } from "@shikijs/vitepress-twoslash";
import tailwindcss from "@tailwindcss/vite";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { ModuleDetectionKind, ModuleKind, ModuleResolutionKind } from "typescript";
import { defineConfig, type DefaultTheme } from "vitepress";
import { groupIconMdPlugin, groupIconVitePlugin } from "vitepress-plugin-group-icons";
import { Path, pipe } from "@duplojs/utils";
import { withMermaid } from "vitepress-plugin-mermaid";
import tailwindcss from "@tailwindcss/vite";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const hostname = "https://duplojs.dev";
const ogImage = new URL("/images/ogImage.png", hostname).toString();

export default pipe(
{
export default withMermaid(
defineConfig({
title: "DuploJS",
base: "/",
appearance: false,
cleanUrls: true,
sitemap: {
hostname,
},

sitemap: { hostname },

head: [
[
"link",
Expand All @@ -26,6 +28,28 @@ export default pipe(
href: "/images/logo.ico",
},
],
[
"link",
{
rel: "preconnect",
href: "https://fonts.googleapis.com",
},
],
[
"link",
{
rel: "preconnect",
href: "https://fonts.gstatic.com",
crossorigin: "",
},
],
[
"link",
{
rel: "stylesheet",
href: "https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Space+Grotesk:wght@700;800&family=Fragment+Mono:ital,wght@0,400;1,400&display=swap",
},
],
[
"meta",
{
Expand Down Expand Up @@ -55,34 +79,22 @@ export default pipe(
},
],
],

themeConfig: {
logo: "/images/logo.png",
wip: {
title: "WIP",
button: "Request this page",
},
socialLinks: [
{
icon: "github",
link: "https://github.com/duplojs/discover",
},
{
icon: "npm",
link: "https://www.npmjs.com/package/@duplojs/discover",
},
{
icon: "linkedin",
link: "https://linkedin.com/company/duplojs",
},
{
icon: "discord",
link: "https://discord.gg/5d6Ze5Wuqm",
},
],
search: {
provider: "local",
},
},
search: { provider: "local" },
} satisfies DefaultTheme.Config,

markdown: {
config: (md) => {
md.use(groupIconMdPlugin);
Expand Down Expand Up @@ -112,67 +124,61 @@ export default pipe(
}),
],
},

vite: {
plugins: [
groupIconVitePlugin(),
tailwindcss(),
],
resolve: {
alias: {
"@": Path.resolveRelative([import.meta.dirname, ".."]),
"@": path.resolve(__dirname, ".."),
},
},
server: {
host: "0.0.0.0",
},
},

locales: {
fr: {
label: "Français",
lang: "fr",
link: "/fr/",
root: {
label: "English",
lang: "en-US",
themeConfig: {
nav: [],
sidebar: {},
docFooter: {
prev: "Page précédente",
next: "Page suivante",
},
outline: {
label: "Sur cette page",
prev: "Previous page",
next: "Next page",
},
returnToTopLabel: "Retour en haut",
darkModeSwitchLabel: "Mode sombre",
outline: { label: "On this page" },
returnToTopLabel: "Return to top",
darkModeSwitchLabel: "Dark mode",
footer: {
copyright: "Copyright © 2025-présent Contributeurs de DuploJS",
message: "Diffusé sous licence MIT.",
copyright: "Copyright © 2025-present DuploJS contributors",
message: "Released under the MIT License.",
},
},
},
root: {
label: "English",
lang: "en",
link: "/en/",
fr: {
label: "Français",
lang: "fr-FR",
themeConfig: {
nav: [],
sidebar: {},
docFooter: {
prev: "Previous page",
next: "Next page",
},
outline: {
label: "On this page",
prev: "Page précédente",
next: "Page suivante",
},
returnToTopLabel: "Return to top",
darkModeSwitchLabel: "Dark mode",
outline: { label: "Sur cette page" },
returnToTopLabel: "Retour en haut",
darkModeSwitchLabel: "Mode sombre",
footer: {
copyright: "Copyright © 2025-present DuploJS contributors",
message: "Released under the MIT License.",
copyright: "Copyright © 2025-présent Contributeurs de DuploJS",
message: "Diffusé sous licence MIT.",
},
},
},
},
} satisfies UserConfig<DefaultTheme.Config>,
defineConfig,
withMermaid,
}),
);
122 changes: 122 additions & 0 deletions app/.vitepress/theme/Layout.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<script setup lang="ts">
import { onMounted, onUnmounted } from "vue";
import { useScrollReveal } from "./composables/useScrollReveal";
import AppNav from "./components/AppNav.vue";
import AppFooter from "./components/AppFooter.vue";
import HeroSection from "./components/sections/HeroSection.vue";
import ConvictionSection from "./components/sections/ConvictionSection.vue";
import PipelineSection from "./components/sections/PipelineSection.vue";
import TypeFlowSection from "./components/sections/TypeFlowSection.vue";
import CheckersSection from "./components/sections/CheckersSection.vue";
import ComparisonSection from "./components/sections/ComparisonSection.vue";
import BenchmarkSection from "./components/sections/BenchmarkSection.vue";
import EcosystemSection from "./components/sections/EcosystemSection.vue";
import GettingStartedSection from "./components/sections/GettingStartedSection.vue";
import TestimonialsSection from "./components/sections/TestimonialsSection.vue";
import CtaSection from "./components/sections/CtaSection.vue";
import CommunitySection from "./components/sections/CommunitySection.vue";

useScrollReveal();

let rafId: number | null = null;
let trailDots: HTMLElement[] = [];

onMounted(() => {
if (window.matchMedia("(hover: none)").matches) {
return;
}
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
return;
}

const count = 4;
trailDots = Array.from({ length: count }, () => {
const dot = document.createElement("div");
dot.className = "pixel-trail";
dot.setAttribute("aria-hidden", "true");
document.body.appendChild(dot);
return dot;
});

let mx = -100,
my = -100;
const positions = Array(count).fill(null).map(() => ({
x: -100,
y: -100,
}));

window.addEventListener("mousemove", (ev) => {
mx = ev.clientX;
my = ev.clientY;
}, { passive: true });

function frame() {
positions[0] = {
x: mx,
y: my,
};
for (let i = 1; i < count; i++) {
const curr = positions[i]!;
const prev = positions[i - 1]!;
positions[i] = {
x: curr.x + ((prev.x - curr.x) * 0.35),
y: curr.y + ((prev.y - curr.y) * 0.35),
};
}
trailDots.forEach((dot, i) => {
const pos = positions[i]!;
dot.style.transform = `translate(${pos.x - 4}px, ${pos.y - 4}px)`;
dot.style.opacity = (1 - (i * 0.22)).toFixed(2);
});
rafId = requestAnimationFrame(frame);
}
rafId = requestAnimationFrame(frame);
});

onUnmounted(() => {
if (rafId !== null) {
cancelAnimationFrame(rafId);
}
trailDots.forEach((dot) => void dot.remove());
trailDots = [];
});
</script>

<template>
<div>
<div
class="rails"
aria-hidden="true"
/>

<AppNav />

<main>
<HeroSection />

<ConvictionSection />

<PipelineSection />

<TypeFlowSection />

<CheckersSection />

<ComparisonSection />

<BenchmarkSection />

<EcosystemSection />

<GettingStartedSection />

<TestimonialsSection />

<CtaSection />

<CommunitySection />
</main>

<AppFooter />
</div>
</template>
Loading
Loading