Compare commits
No commits in common. "7807fee775e5a6a3d722f327c909324a3d5ba7f9" and "2f29df49b8d7efa0bd713d1bfb20104703ade367" have entirely different histories.
7807fee775
...
2f29df49b8
@ -1,4 +1,3 @@
|
|||||||
import React from "react";
|
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
type IframeProps = {
|
type IframeProps = {
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
export function Image(props: { src: string; alt: string } & React.ComponentPropsWithoutRef<"img">) {
|
export function Image(props: { src: string; alt: string } & React.ComponentPropsWithoutRef<"img">) {
|
||||||
return <img {...props} src={props.src} alt={props.alt} loading="lazy" />;
|
return <img {...props} src={props.src} alt={props.alt} loading="lazy" />;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import { usePageContext } from "vike-react/usePageContext";
|
import { usePageContext } from "vike-react/usePageContext";
|
||||||
import { prefetch } from "vike/client/router";
|
import { prefetch } from "vike/client/router";
|
||||||
import React from "react";
|
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
export function Link(props: React.AnchorHTMLAttributes<HTMLAnchorElement> & { href: string; className?: string }) {
|
export function Link(props: React.AnchorHTMLAttributes<HTMLAnchorElement> & { href: string }) {
|
||||||
const { urlPathname } = usePageContext();
|
const { urlPathname } = usePageContext();
|
||||||
const isActive = props.href === "/" ? urlPathname === props.href : urlPathname.startsWith(props.href);
|
const isActive = props.href === "/" ? urlPathname === props.href : urlPathname.startsWith(props.href);
|
||||||
const isSameDomain = !(props.href.startsWith("http") || props.href.startsWith("mailto"));
|
const isSameDomain = !(props.href.startsWith("http") || props.href.startsWith("mailto"));
|
||||||
|
|||||||
41
app/components/common/Mermaid.tsx
Normal file
41
app/components/common/Mermaid.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import MermaidRenderer from "react-mermaid2";
|
||||||
|
|
||||||
|
type MermaidProps = {
|
||||||
|
path: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Mermaid(props: MermaidProps) {
|
||||||
|
return (
|
||||||
|
<MermaidRenderer
|
||||||
|
chart={`
|
||||||
|
sequenceDiagram
|
||||||
|
autonumber
|
||||||
|
|
||||||
|
box rgba(139,92,246,.1) Navigateur
|
||||||
|
actor Utilisateur
|
||||||
|
end
|
||||||
|
|
||||||
|
box rgba(139,92,246,.1) Serveur
|
||||||
|
participant Routeur
|
||||||
|
participant Contrôleur
|
||||||
|
participant Modèle
|
||||||
|
participant Vue
|
||||||
|
end
|
||||||
|
|
||||||
|
participant Base de données
|
||||||
|
|
||||||
|
Utilisateur->>Routeur: Je veux voir la page d'accueil
|
||||||
|
Routeur->>Contrôleur: Appelle la méthode \`home\`
|
||||||
|
alt Si des données sont nécessaires
|
||||||
|
Contrôleur->>Modèle: Demande les données
|
||||||
|
Modèle->>Base de données: Récupère les données
|
||||||
|
Base de données-->>Modèle: Retourne les données
|
||||||
|
Modèle-->>Contrôleur: Retourne les données
|
||||||
|
end
|
||||||
|
Contrôleur->>Vue: Demande le HTML
|
||||||
|
Vue-->>Contrôleur: Retourne le HTML généré
|
||||||
|
Contrôleur->>Utilisateur: Retourne le HTML généré
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import type { Dispatch, SetStateAction } from "react";
|
import type { Dispatch, SetStateAction } from "react";
|
||||||
|
|
||||||
import React, { createContext, useContext, useEffect, useState } from "react";
|
import { createContext, useContext, useEffect, useState } from "react";
|
||||||
import { Button } from "@syntax/Button";
|
import { Button } from "@syntax/Button";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { Link } from "@/components/common/Link";
|
import { Link } from "@/components/common/Link";
|
||||||
import React from "react";
|
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
const variantStyles = {
|
const variantStyles = {
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { ClipboardDocumentIcon } from "@heroicons/react/24/outline";
|
import { ClipboardDocumentIcon } from "@heroicons/react/24/outline";
|
||||||
import { prismThemes } from "@/data/themes/prism";
|
import { prismThemes } from "@/data/themes/prism";
|
||||||
import React, { Fragment, useMemo } from "react";
|
|
||||||
import { Highlight } from "prism-react-renderer";
|
import { Highlight } from "prism-react-renderer";
|
||||||
import { useTheme } from "@/hooks/useTheme";
|
import { useTheme } from "@/hooks/useTheme";
|
||||||
|
import { Fragment, useMemo } from "react";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { Button } from "./Button";
|
import { Button } from "./Button";
|
||||||
import Prism from "prismjs";
|
import Prism from "prismjs";
|
||||||
@ -40,7 +40,7 @@ export default function CSRSnippet({
|
|||||||
{label}
|
{label}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<pre className={clsx(className, { "pt-11": label })} style={style}>
|
<pre className={clsx(className, { "pt-11": !!label })} style={style}>
|
||||||
<code>
|
<code>
|
||||||
{tokens.map((line, lineIndex) => (
|
{tokens.map((line, lineIndex) => (
|
||||||
<Fragment key={lineIndex}>
|
<Fragment key={lineIndex}>
|
||||||
@ -69,7 +69,7 @@ export default function CSRSnippet({
|
|||||||
<Button
|
<Button
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"absolute right-2 w-8 h-8 aspect-square opacity-0 group-hover:opacity-50 hover:opacity-100 transition-opacity",
|
"absolute right-2 w-8 h-8 aspect-square opacity-0 group-hover:opacity-50 hover:opacity-100 transition-opacity",
|
||||||
label ? "top-10" : "top-2",
|
!!label ? "top-10" : "top-2",
|
||||||
)}
|
)}
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { Icon } from "@syntax/Icon";
|
import { Icon } from "@syntax/Icon";
|
||||||
import React from "react";
|
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
@ -37,12 +36,10 @@ export function Callout({
|
|||||||
type?: keyof typeof styles;
|
type?: keyof typeof styles;
|
||||||
collapsible?: boolean;
|
collapsible?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const IconComponent = icons[type];
|
let IconComponent = icons[type];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={clsx("my-8 flex flex-col rounded-3xl p-6", styles[type].container)}>
|
||||||
className={clsx("my-8 flex flex-col rounded-3xl p-6", styles[type].container, { "cursor-pointer": collapsible })}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-6">
|
<div className="flex items-center gap-6">
|
||||||
<IconComponent className="h-8 w-8 flex-none" />
|
<IconComponent className="h-8 w-8 flex-none" />
|
||||||
<p className={clsx("!m-0 font-display text-xl text-balance", styles[type].title)}>{title}</p>
|
<p className={clsx("!m-0 font-display text-xl text-balance", styles[type].title)}>{title}</p>
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { usePageContext } from "vike-react/usePageContext";
|
import { usePageContext } from "vike-react/usePageContext";
|
||||||
import { ClockIcon } from "@heroicons/react/24/outline";
|
import { ClockIcon } from "@heroicons/react/24/outline";
|
||||||
import { navigation } from "@/lib/navigation";
|
import { navigation } from "@/lib/navigation";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
type DocsHeaderProps = {
|
type DocsHeaderProps = {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { PrevNextLinks } from "@syntax/PrevNextLinks";
|
|||||||
import { collectSections } from "@/lib/sections";
|
import { collectSections } from "@/lib/sections";
|
||||||
import { DocsHeader } from "@syntax/DocsHeader";
|
import { DocsHeader } from "@syntax/DocsHeader";
|
||||||
import { Prose } from "@syntax/Prose";
|
import { Prose } from "@syntax/Prose";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export function DocsLayout({
|
export function DocsLayout({
|
||||||
children,
|
children,
|
||||||
@ -18,7 +17,7 @@ export function DocsLayout({
|
|||||||
estimatedReadingTime?: string;
|
estimatedReadingTime?: string;
|
||||||
nodes: Array<Node>;
|
nodes: Array<Node>;
|
||||||
}) {
|
}) {
|
||||||
const tableOfContents = collectSections(nodes);
|
let tableOfContents = collectSections(nodes);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { clientOnly } from "vike-react/clientOnly";
|
import { clientOnly } from "vike-react/clientOnly";
|
||||||
import { SSRSnippet } from "./SSRSnippet";
|
import { SSRSnippet } from "./SSRSnippet";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
const CSRSnippet = clientOnly(() => import("./CSRSnippet"));
|
const CSRSnippet = clientOnly(() => import("./CSRSnippet"));
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,8 @@ import blurIndigoImage from "@/images/blur-indigo.webp";
|
|||||||
import blurCyanImage from "@/images/blur-cyan.webp";
|
import blurCyanImage from "@/images/blur-cyan.webp";
|
||||||
import { Image } from "@/components/common/Image";
|
import { Image } from "@/components/common/Image";
|
||||||
import { Highlight } from "prism-react-renderer";
|
import { Highlight } from "prism-react-renderer";
|
||||||
import React, { Fragment } from "react";
|
|
||||||
import { Button } from "@syntax/Button";
|
import { Button } from "@syntax/Button";
|
||||||
|
import { Fragment } from "react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
const codeLanguage = "javascript";
|
const codeLanguage = "javascript";
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useId } from "react";
|
import { useId } from "react";
|
||||||
|
|
||||||
export function HeroBackground(props: React.ComponentPropsWithoutRef<"svg">) {
|
export function HeroBackground(props: React.ComponentPropsWithoutRef<"svg">) {
|
||||||
const id = useId();
|
const id = useId();
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { PluginsIcon } from "@syntax/icons/PluginsIcon";
|
|||||||
import { PresetsIcon } from "@syntax/icons/PresetsIcon";
|
import { PresetsIcon } from "@syntax/icons/PresetsIcon";
|
||||||
import { ThemingIcon } from "@syntax/icons/ThemingIcon";
|
import { ThemingIcon } from "@syntax/icons/ThemingIcon";
|
||||||
import { WarningIcon } from "@syntax/icons/WarningIcon";
|
import { WarningIcon } from "@syntax/icons/WarningIcon";
|
||||||
import React, { useId } from "react";
|
import { useId } from "react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
const icons = {
|
const icons = {
|
||||||
@ -32,8 +32,8 @@ export function Icon({
|
|||||||
color?: keyof typeof iconStyles;
|
color?: keyof typeof iconStyles;
|
||||||
icon: keyof typeof icons;
|
icon: keyof typeof icons;
|
||||||
} & Omit<React.ComponentPropsWithoutRef<"svg">, "color">) {
|
} & Omit<React.ComponentPropsWithoutRef<"svg">, "color">) {
|
||||||
const id = useId();
|
let id = useId();
|
||||||
const IconComponent = icons[icon];
|
let IconComponent = icons[icon];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg aria-hidden="true" viewBox="0 0 32 32" fill="none" className={clsx(className, iconStyles[color])} {...props}>
|
<svg aria-hidden="true" viewBox="0 0 32 32" fill="none" className={clsx(className, iconStyles[color])} {...props}>
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
function LogomarkPaths() {
|
function LogomarkPaths() {
|
||||||
|
// return (
|
||||||
|
// <g fill="none" stroke="#38BDF8" strokeLinejoin="round" strokeWidth={3}>
|
||||||
|
// <path d="M10.308 5L18 17.5 10.308 30 2.615 17.5 10.308 5z" />
|
||||||
|
// <path d="M18 17.5L10.308 5h15.144l7.933 12.5M18 17.5h15.385L25.452 30H10.308L18 17.5z" />
|
||||||
|
// </g>
|
||||||
|
// );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<defs>
|
<defs>
|
||||||
@ -28,6 +33,25 @@ function LogomarkPaths() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function LogoWithText(props: React.ComponentPropsWithoutRef<"svg">) {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 231 38" {...props}>
|
||||||
|
<LogomarkPaths />
|
||||||
|
<text
|
||||||
|
className="hidden lg:block fill-zinc-900 dark:fill-zinc-100"
|
||||||
|
fontFamily="Inter Variable, sans-serif"
|
||||||
|
fontSize={24}
|
||||||
|
fontWeight="bold"
|
||||||
|
letterSpacing="-.02em"
|
||||||
|
x={74}
|
||||||
|
y={26}
|
||||||
|
>
|
||||||
|
Memento Dev
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function Logo(props: React.ComponentPropsWithoutRef<"svg">) {
|
export function Logo(props: React.ComponentPropsWithoutRef<"svg">) {
|
||||||
return (
|
return (
|
||||||
<svg viewBox="0 0 58 38" {...props}>
|
<svg viewBox="0 0 58 38" {...props}>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { Suspense, useCallback, useEffect, useState } from "react";
|
import { Suspense, useCallback, useEffect, useState } from "react";
|
||||||
import { usePageContext } from "vike-react/usePageContext";
|
import { usePageContext } from "vike-react/usePageContext";
|
||||||
import { Dialog, DialogPanel } from "@headlessui/react";
|
import { Dialog, DialogPanel } from "@headlessui/react";
|
||||||
import { Navigation } from "@syntax/Navigation";
|
import { Navigation } from "@syntax/Navigation";
|
||||||
@ -32,12 +32,11 @@ function CloseOnNavigation({ close }: { close: () => void }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function MobileNavigation() {
|
export function MobileNavigation() {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
let [isOpen, setIsOpen] = useState(false);
|
||||||
const close = useCallback(() => setIsOpen(false), [setIsOpen]);
|
let close = useCallback(() => setIsOpen(false), [setIsOpen]);
|
||||||
|
|
||||||
function onLinkClick(event: React.MouseEvent<HTMLAnchorElement>) {
|
function onLinkClick(event: React.MouseEvent<HTMLAnchorElement>) {
|
||||||
const link = event.currentTarget;
|
let link = event.currentTarget;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
link.pathname + link.search + link.hash ===
|
link.pathname + link.search + link.hash ===
|
||||||
window.location.pathname + window.location.search + window.location.hash
|
window.location.pathname + window.location.search + window.location.hash
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/solid";
|
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/solid";
|
||||||
import { usePageContext } from "vike-react/usePageContext";
|
import { usePageContext } from "vike-react/usePageContext";
|
||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import { Link } from "@/components/common/Link";
|
import { Link } from "@/components/common/Link";
|
||||||
import { navigation } from "@/lib/navigation";
|
import { navigation } from "@/lib/navigation";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
type NavigationItemProps = {
|
type NavigationItemProps = {
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { usePageContext } from "vike-react/usePageContext";
|
import { usePageContext } from "vike-react/usePageContext";
|
||||||
import { Link } from "@/components/common/Link";
|
import { Link } from "@/components/common/Link";
|
||||||
import { navigation } from "@/lib/navigation";
|
import { navigation } from "@/lib/navigation";
|
||||||
import React from "react";
|
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
function ArrowIcon(props: React.ComponentPropsWithoutRef<"svg">) {
|
function ArrowIcon(props: React.ComponentPropsWithoutRef<"svg">) {
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import React from "react";
|
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
export function Prose<T extends React.ElementType = "div">({
|
export function Prose<T extends React.ElementType = "div">({
|
||||||
@ -8,7 +7,7 @@ export function Prose<T extends React.ElementType = "div">({
|
|||||||
}: React.ComponentPropsWithoutRef<T> & {
|
}: React.ComponentPropsWithoutRef<T> & {
|
||||||
as?: T;
|
as?: T;
|
||||||
}) {
|
}) {
|
||||||
const Component = as ?? "div";
|
let Component = as ?? "div";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { Link } from "@/components/common/Link";
|
import { Link } from "@/components/common/Link";
|
||||||
import { Icon } from "@syntax/Icon";
|
import { Icon } from "@syntax/Icon";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export function QuickLinks({ children }: { children: React.ReactNode }) {
|
export function QuickLinks({ children }: { children: React.ReactNode }) {
|
||||||
return <div className="not-prose my-12 grid grid-cols-1 gap-6 sm:grid-cols-2">{children}</div>;
|
return <div className="not-prose my-12 grid grid-cols-1 gap-6 sm:grid-cols-2">{children}</div>;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Highlight, Prism } from "prism-react-renderer";
|
import { Highlight, Prism } from "prism-react-renderer";
|
||||||
import { prismThemes } from "@/data/themes/prism";
|
import { prismThemes } from "@/data/themes/prism";
|
||||||
import React, { Fragment, useMemo } from "react";
|
|
||||||
import { useTheme } from "@/hooks/useTheme";
|
import { useTheme } from "@/hooks/useTheme";
|
||||||
|
import { Fragment, useMemo } from "react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
export function SSRSnippet({
|
export function SSRSnippet({
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useId, useState, useEffect, createContext, useContext, Fragment } from "react";
|
import { useId, useState, useEffect, createContext, useContext, Fragment } from "react";
|
||||||
import { SearchResult } from "@/services/FlexSearchService";
|
import { SearchResult } from "@/services/FlexSearchService";
|
||||||
import { Dialog, DialogPanel } from "@headlessui/react";
|
import { Dialog, DialogPanel } from "@headlessui/react";
|
||||||
import { useDebounce } from "@/hooks/useDebounce";
|
import { useDebounce } from "@/hooks/useDebounce";
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import type { Data } from "@/pages/docs/+data";
|
|||||||
import { clientOnly } from "vike-react/clientOnly";
|
import { clientOnly } from "vike-react/clientOnly";
|
||||||
import { useData } from "vike-react/useData";
|
import { useData } from "vike-react/useData";
|
||||||
import { SSRSnippet } from "./SSRSnippet";
|
import { SSRSnippet } from "./SSRSnippet";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
const CSRSnippet = clientOnly(() => import("./CSRSnippet"));
|
const CSRSnippet = clientOnly(() => import("./CSRSnippet"));
|
||||||
|
|
||||||
|
|||||||
@ -1,23 +1,25 @@
|
|||||||
import React, { useCallback, useEffect, useState } from "react";
|
"use client";
|
||||||
|
|
||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { Link } from "@/components/common/Link";
|
import { Link } from "@/components/common/Link";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
import { type Section, type Subsection } from "@/lib/sections";
|
import { type Section, type Subsection } from "@/lib/sections";
|
||||||
|
|
||||||
export function TableOfContents({ tableOfContents }: { tableOfContents: Array<Section> }) {
|
export function TableOfContents({ tableOfContents }: { tableOfContents: Array<Section> }) {
|
||||||
const [currentSection, setCurrentSection] = useState(tableOfContents[0]?.id);
|
let [currentSection, setCurrentSection] = useState(tableOfContents[0]?.id);
|
||||||
|
|
||||||
const getHeadings = useCallback((tableOfContents: Array<Section>) => {
|
let getHeadings = useCallback((tableOfContents: Array<Section>) => {
|
||||||
return tableOfContents
|
return tableOfContents
|
||||||
.flatMap((node) => [node.id, ...node.children.map((child) => child.id)])
|
.flatMap((node) => [node.id, ...node.children.map((child) => child.id)])
|
||||||
.map((id) => {
|
.map((id) => {
|
||||||
const el = document.getElementById(id);
|
let el = document.getElementById(id);
|
||||||
if (!el) return null;
|
if (!el) return null;
|
||||||
|
|
||||||
const style = window.getComputedStyle(el);
|
let style = window.getComputedStyle(el);
|
||||||
const scrollMt = parseFloat(style.scrollMarginTop);
|
let scrollMt = parseFloat(style.scrollMarginTop);
|
||||||
|
|
||||||
const top = window.scrollY + el.getBoundingClientRect().top - scrollMt;
|
let top = window.scrollY + el.getBoundingClientRect().top - scrollMt;
|
||||||
return { id, top };
|
return { id, top };
|
||||||
})
|
})
|
||||||
.filter((x): x is { id: string; top: number } => x !== null);
|
.filter((x): x is { id: string; top: number } => x !== null);
|
||||||
@ -33,8 +35,11 @@ export function TableOfContents({ tableOfContents }: { tableOfContents: Array<Se
|
|||||||
let current = headings[0]?.id;
|
let current = headings[0]?.id;
|
||||||
|
|
||||||
for (const heading of headings) {
|
for (const heading of headings) {
|
||||||
if (top < heading.top - 10) break;
|
if (top >= heading.top - 10) {
|
||||||
current = heading.id;
|
current = heading.id;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setCurrentSection(current);
|
setCurrentSection(current);
|
||||||
}
|
}
|
||||||
@ -46,9 +51,12 @@ export function TableOfContents({ tableOfContents }: { tableOfContents: Array<Se
|
|||||||
}, [getHeadings, tableOfContents]);
|
}, [getHeadings, tableOfContents]);
|
||||||
|
|
||||||
function isActive(section: Section | Subsection) {
|
function isActive(section: Section | Subsection) {
|
||||||
if (section.id === currentSection) return true;
|
if (section.id === currentSection) {
|
||||||
if (!section.children) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (!section.children) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return section.children.findIndex(isActive) > -1;
|
return section.children.findIndex(isActive) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Label, Listbox, ListboxButton, ListboxOption, ListboxOptions } from "@headlessui/react";
|
import { Label, Listbox, ListboxButton, ListboxOption, ListboxOptions } from "@headlessui/react";
|
||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTheme } from "@/hooks/useTheme";
|
import { useTheme } from "@/hooks/useTheme";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
@ -33,8 +33,8 @@ function DarkIcon(props: React.ComponentPropsWithoutRef<"svg">) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ThemeSelector(props: React.ComponentPropsWithoutRef<typeof Listbox<"div">>) {
|
export function ThemeSelector(props: React.ComponentPropsWithoutRef<typeof Listbox<"div">>) {
|
||||||
const [mounted, setMounted] = useState(false);
|
let [mounted, setMounted] = useState(false);
|
||||||
const { theme, setTheme } = useTheme();
|
let { theme, setTheme } = useTheme();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export function InstallationIcon({
|
export function InstallationIcon({
|
||||||
id,
|
id,
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export function LightbulbIcon({ id, color }: { id: string; color?: React.ComponentProps<typeof Gradient>["color"] }) {
|
export function LightbulbIcon({ id, color }: { id: string; color?: React.ComponentProps<typeof Gradient>["color"] }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export function PluginsIcon({ id, color }: { id: string; color?: React.ComponentProps<typeof Gradient>["color"] }) {
|
export function PluginsIcon({ id, color }: { id: string; color?: React.ComponentProps<typeof Gradient>["color"] }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export function PresetsIcon({ id, color }: { id: string; color?: React.ComponentProps<typeof Gradient>["color"] }) {
|
export function PresetsIcon({ id, color }: { id: string; color?: React.ComponentProps<typeof Gradient>["color"] }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export function QuestionIcon({ id, color }: { id: string; color?: React.ComponentProps<typeof Gradient>["color"] }) {
|
export function QuestionIcon({ id, color }: { id: string; color?: React.ComponentProps<typeof Gradient>["color"] }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export function ThemingIcon({ id, color }: { id: string; color?: React.ComponentProps<typeof Gradient>["color"] }) {
|
export function ThemingIcon({ id, color }: { id: string; color?: React.ComponentProps<typeof Gradient>["color"] }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
import { DarkMode, Gradient, LightMode } from "@syntax/Icon";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export function WarningIcon({ id, color }: { id: string; color?: React.ComponentProps<typeof Gradient>["color"] }) {
|
export function WarningIcon({ id, color }: { id: string; color?: React.ComponentProps<typeof Gradient>["color"] }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import react from "eslint-plugin-react/configs/recommended.js";
|
|
||||||
import prettier from "eslint-plugin-prettier/recommended";
|
|
||||||
import tseslint from "typescript-eslint";
|
|
||||||
import eslint from "@eslint/js";
|
import eslint from "@eslint/js";
|
||||||
|
import prettier from "eslint-plugin-prettier/recommended";
|
||||||
|
import react from "eslint-plugin-react/configs/recommended.js";
|
||||||
import globals from "globals";
|
import globals from "globals";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
export default tseslint.config(
|
export default tseslint.config(
|
||||||
{
|
{
|
||||||
@ -33,10 +33,14 @@ export default tseslint.config(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
rules: {
|
rules: {
|
||||||
"@typescript-eslint/no-unused-vars": [1, { argsIgnorePattern: "^_" }],
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
argsIgnorePattern: "^_",
|
||||||
|
},
|
||||||
|
],
|
||||||
"@typescript-eslint/no-namespace": 0,
|
"@typescript-eslint/no-namespace": 0,
|
||||||
"react/react-in-jsx-scope": "warn",
|
"react/react-in-jsx-scope": false,
|
||||||
"react/jsx-filename-extension": [1, { extensions: [".tsx"] }],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
37
app/hooks/useAutoComplete.ts
Normal file
37
app/hooks/useAutoComplete.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import type { SearchResult } from "@/lib/search";
|
||||||
|
|
||||||
|
import { useDebounce } from "./useDebounce";
|
||||||
|
import { useState, useEffect, use } from "react";
|
||||||
|
|
||||||
|
export function useAutoComplete(onSearch: (query: string) => Promise<SearchResult[]>) {
|
||||||
|
const [results, setResults] = useState<SearchResult[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [isOpened, setIsOpened] = useState(false);
|
||||||
|
const [query, setQuery] = useDebounce();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Attach the event listener to the window
|
||||||
|
function handleKeyDown(event: KeyboardEvent) {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
setIsOpened(false);
|
||||||
|
} else if (event.key === "K" && (event.ctrlKey || event.metaKey)) {
|
||||||
|
event.preventDefault();
|
||||||
|
setIsOpened(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("keydown", handleKeyDown);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
results,
|
||||||
|
isLoading,
|
||||||
|
isOpened,
|
||||||
|
query,
|
||||||
|
setQuery,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -2,21 +2,19 @@ import { MobileNavigation } from "@syntax/MobileNavigation";
|
|||||||
import { usePageContext } from "vike-react/usePageContext";
|
import { usePageContext } from "vike-react/usePageContext";
|
||||||
import { ThemeProvider } from "@/providers/ThemeProvider";
|
import { ThemeProvider } from "@/providers/ThemeProvider";
|
||||||
import { ThemeSelector } from "@syntax/ThemeSelector";
|
import { ThemeSelector } from "@syntax/ThemeSelector";
|
||||||
import { clientOnly } from "vike-react/clientOnly";
|
|
||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import { ToastContainer } from "react-toastify";
|
|
||||||
import { Navigation } from "@syntax/Navigation";
|
import { Navigation } from "@syntax/Navigation";
|
||||||
import { Link } from "@/components/common/Link";
|
import { Link } from "@/components/common/Link";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Search } from "@syntax/Search";
|
||||||
import { Hero } from "@syntax/Hero";
|
import { Hero } from "@syntax/Hero";
|
||||||
import { Logo } from "@syntax/Logo";
|
import { Logo, LogoWithText } from "@syntax/Logo";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
import "./tailwind.css";
|
import "./tailwind.css";
|
||||||
import "./prism.css";
|
import "./prism.css";
|
||||||
import "unfonts.css";
|
import "unfonts.css";
|
||||||
|
import { ToastContainer } from "react-toastify";
|
||||||
const Search = clientOnly(() => import("@syntax/Search").then((module) => module.Search));
|
|
||||||
|
|
||||||
function GitHubIcon(props: React.ComponentPropsWithoutRef<"svg">) {
|
function GitHubIcon(props: React.ComponentPropsWithoutRef<"svg">) {
|
||||||
return (
|
return (
|
||||||
@ -27,7 +25,7 @@ function GitHubIcon(props: React.ComponentPropsWithoutRef<"svg">) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Header() {
|
function Header() {
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
let [isScrolled, setIsScrolled] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function onScroll() {
|
function onScroll() {
|
||||||
@ -61,7 +59,7 @@ function Header() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="-my-5 mr-6 sm:mr-8 md:mr-0">
|
<div className="-my-5 mr-6 sm:mr-8 md:mr-0">
|
||||||
<Search fallback={<div className="h-6 w-6 animate-pulse rounded-full bg-slate-200 dark:bg-slate-700" />} />
|
<Search />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative flex basis-0 justify-end gap-6 sm:gap-8 md:grow">
|
<div className="relative flex basis-0 justify-end gap-6 sm:gap-8 md:grow">
|
||||||
|
|||||||
@ -119,17 +119,16 @@ export const navigation: NavigationSection[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function doesLinkSubitemExist(link: NavigationLink, subitemHref: string): boolean {
|
|
||||||
return link.subitems.some((subitem) => subitem.href === subitemHref);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findNavigationLink(namespace: string, href: string): NavigationLink | undefined {
|
export function findNavigationLink(namespace: string, href: string): NavigationLink | undefined {
|
||||||
const currentUrl = `/${namespace}/${href}`.replace(/\/+/g, "/").replace(/\/$/, "");
|
const currentUrl = `/${namespace}/${href}`.replace(/\/+/g, "/").replace(/\/$/, "");
|
||||||
|
|
||||||
const foundLink = navigation
|
const foundLink = navigation
|
||||||
.flatMap((section) => section.links)
|
.flatMap((section) => section.links)
|
||||||
.find((link) => {
|
.find((link) => {
|
||||||
return link.href === currentUrl || doesLinkSubitemExist(link, currentUrl);
|
link.href === currentUrl ||
|
||||||
|
link.subitems.some((subitem) => {
|
||||||
|
subitem.href === currentUrl;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return foundLink;
|
return foundLink;
|
||||||
|
|||||||
@ -5,8 +5,6 @@ import glob from "fast-glob";
|
|||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
|
|
||||||
type SearchOptionValue = string | number | boolean | null | undefined | object | unknown;
|
|
||||||
|
|
||||||
const slugify = slugifyWithCounter();
|
const slugify = slugifyWithCounter();
|
||||||
|
|
||||||
interface Node {
|
interface Node {
|
||||||
@ -34,30 +32,28 @@ export interface SearchResult {
|
|||||||
|
|
||||||
function toString(node: Node): string {
|
function toString(node: Node): string {
|
||||||
let str = node.type === "text" && typeof node.attributes?.content === "string" ? node.attributes.content : "";
|
let str = node.type === "text" && typeof node.attributes?.content === "string" ? node.attributes.content : "";
|
||||||
|
|
||||||
if ("children" in node) {
|
if ("children" in node) {
|
||||||
for (const child of node.children!) {
|
for (let child of node.children!) {
|
||||||
str += toString(child);
|
str += toString(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractSections(node: Node, sections: Section[], isRoot: boolean = true): void {
|
function extractSections(node: Node, sections: Section[], isRoot: boolean = true): void {
|
||||||
if (isRoot) slugify.reset();
|
if (isRoot) {
|
||||||
|
slugify.reset();
|
||||||
|
}
|
||||||
if (node.type === "heading" || node.type === "paragraph") {
|
if (node.type === "heading" || node.type === "paragraph") {
|
||||||
const content = toString(node).trim();
|
let content = toString(node).trim();
|
||||||
|
if (node.type === "heading" && node.attributes?.level! <= 2) {
|
||||||
if (node.attributes?.level && node.type === "heading" && node.attributes.level <= 2) {
|
let hash = node.attributes?.id ?? slugify(content);
|
||||||
const hash = node.attributes?.id ?? slugify(content);
|
|
||||||
sections.push({ content, hash, subsections: [] });
|
sections.push({ content, hash, subsections: [] });
|
||||||
} else {
|
} else {
|
||||||
sections[sections.length - 1].subsections.push(content);
|
sections[sections.length - 1].subsections.push(content);
|
||||||
}
|
}
|
||||||
} else if ("children" in node) {
|
} else if ("children" in node) {
|
||||||
for (const child of node.children!) {
|
for (let child of node.children!) {
|
||||||
extractSections(child, sections, false);
|
extractSections(child, sections, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,18 +111,18 @@ export function buildSearchIndex(pagesDir: string): FlexSearch.Document<SearchRe
|
|||||||
export function search(
|
export function search(
|
||||||
sectionIndex: FlexSearch.Document<SearchResult>,
|
sectionIndex: FlexSearch.Document<SearchResult>,
|
||||||
query: string,
|
query: string,
|
||||||
options: Record<string, SearchOptionValue> = {},
|
options: Record<string, any> = {},
|
||||||
): SearchResult[] {
|
): SearchResult[] {
|
||||||
const results = sectionIndex.search(query, {
|
const results = sectionIndex.search(query, {
|
||||||
...options,
|
...options,
|
||||||
enrich: true,
|
enrich: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (results.length === 0) return [];
|
if (results.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const searchResults = results[0].result as unknown as { id: string; doc: { title: string; pageTitle: string } }[];
|
return results[0].result.map((item: any) => ({
|
||||||
|
|
||||||
return searchResults.map((item) => ({
|
|
||||||
url: item.id,
|
url: item.id,
|
||||||
title: item.doc.title,
|
title: item.doc.title,
|
||||||
pageTitle: item.doc.pageTitle,
|
pageTitle: item.doc.pageTitle,
|
||||||
|
|||||||
@ -41,8 +41,7 @@ function isH3Node(node: Node): node is H3Node {
|
|||||||
|
|
||||||
function getNodeText(node: Node) {
|
function getNodeText(node: Node) {
|
||||||
let text = "";
|
let text = "";
|
||||||
|
for (let child of node.children ?? []) {
|
||||||
for (const child of node.children ?? []) {
|
|
||||||
if (child.type === "text") {
|
if (child.type === "text") {
|
||||||
text += child.attributes.content;
|
text += child.attributes.content;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,21 @@
|
|||||||
|
// Workaround about undefined import only in production build
|
||||||
|
|
||||||
import type { RenderableTreeNode } from "@markdoc/markdoc";
|
import type { RenderableTreeNode } from "@markdoc/markdoc";
|
||||||
import type { ReactNode } from "react";
|
|
||||||
|
|
||||||
import { Tag as MarkdocTag } from "@markdoc/markdoc";
|
export class Tag<N extends string = string, A extends Record<string, any> = Record<string, any>> {
|
||||||
|
readonly $$mdtype = "Tag" as const;
|
||||||
|
|
||||||
type TagAttributesValue = string | number | boolean | null | undefined | object | unknown;
|
static isTag = (tag: any): tag is Tag => {
|
||||||
|
return !!(tag?.$$mdtype === "Tag");
|
||||||
|
};
|
||||||
|
|
||||||
export class Tag extends MarkdocTag {
|
name: N;
|
||||||
constructor(
|
attributes: A;
|
||||||
name: string | ReactNode,
|
children: RenderableTreeNode[];
|
||||||
attributes: Record<string, TagAttributesValue>,
|
|
||||||
children: RenderableTreeNode[],
|
constructor(name = "div" as N, attributes = {} as A, children: RenderableTreeNode[] = []) {
|
||||||
) {
|
this.name = name;
|
||||||
// Workaround for TypeScript's type system
|
this.attributes = attributes;
|
||||||
super(name as unknown as string, attributes, children);
|
this.children = children;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
import type { Config, Node } from "@markdoc/markdoc";
|
import type { Config, Node } from "@markdoc/markdoc";
|
||||||
|
|
||||||
import { slugifyWithCounter } from "@sindresorhus/slugify";
|
import { slugifyWithCounter } from "@sindresorhus/slugify";
|
||||||
import { nodes as defaultNodes } from "@markdoc/markdoc";
|
|
||||||
import { DocsLayout } from "@syntax/DocsLayout";
|
import { DocsLayout } from "@syntax/DocsLayout";
|
||||||
import { Link } from "@/components/common/Link";
|
import Markdoc from "@markdoc/markdoc";
|
||||||
import { Fence } from "@syntax/Fence";
|
import { Fence } from "@syntax/Fence";
|
||||||
import { Tag } from "./Tag";
|
|
||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
|
import { Link } from "@/components/common/Link";
|
||||||
|
|
||||||
const documentSlugifyMap = new Map();
|
const { nodes: defaultNodes, Tag } = Markdoc;
|
||||||
|
|
||||||
|
let documentSlugifyMap = new Map();
|
||||||
|
|
||||||
const nodes = {
|
const nodes = {
|
||||||
document: {
|
document: {
|
||||||
@ -18,7 +19,7 @@ const nodes = {
|
|||||||
documentSlugifyMap.set(config, slugifyWithCounter());
|
documentSlugifyMap.set(config, slugifyWithCounter());
|
||||||
|
|
||||||
return new Tag(
|
return new Tag(
|
||||||
this.render as unknown as string,
|
this.render,
|
||||||
{
|
{
|
||||||
frontmatter: yaml.load(node.attributes.frontmatter),
|
frontmatter: yaml.load(node.attributes.frontmatter),
|
||||||
estimatedReadingTime: config?.variables?.estimatedReadingTime,
|
estimatedReadingTime: config?.variables?.estimatedReadingTime,
|
||||||
|
|||||||
@ -1,9 +1,16 @@
|
|||||||
import { QuickLink, QuickLinks } from "@syntax/QuickLinks";
|
import { QuickLink, QuickLinks } from "@syntax/QuickLinks";
|
||||||
import { TabContent, Tabs } from "@/components/md/Tabs";
|
import { TabContent, Tabs } from "@/components/md/Tabs";
|
||||||
|
// import { Fence2 } from "@/components/syntax/Fence2";
|
||||||
|
import { Callout } from "@syntax/Callout";
|
||||||
|
// import fs from "fs/promises";
|
||||||
|
// import { Tag } from "./Tag";
|
||||||
|
import React from "react";
|
||||||
import { Snippet } from "@/components/syntax/Snippet";
|
import { Snippet } from "@/components/syntax/Snippet";
|
||||||
import { Iframe } from "@/components/common/Iframe";
|
import { Iframe } from "@/components/common/Iframe";
|
||||||
import { Callout } from "@syntax/Callout";
|
import { Mermaid } from "@/components/common/Mermaid";
|
||||||
import React from "react";
|
// import path from "path";
|
||||||
|
|
||||||
|
// const __dirname = path.resolve();
|
||||||
|
|
||||||
const tags = {
|
const tags = {
|
||||||
callout: {
|
callout: {
|
||||||
@ -36,10 +43,10 @@ const tags = {
|
|||||||
alt: { type: String },
|
alt: { type: String },
|
||||||
caption: { type: String },
|
caption: { type: String },
|
||||||
},
|
},
|
||||||
render: (props: { src: string; alt: string; caption: string }) => (
|
render: ({ src, alt = "", caption }: { src: string; alt: string; caption: string }) => (
|
||||||
<figure>
|
<figure>
|
||||||
<img src={props.src} alt={props.alt} loading="lazy" />
|
<img src={src} alt={alt} loading="lazy" />
|
||||||
<figcaption>{props.caption}</figcaption>
|
<figcaption>{caption}</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -84,9 +91,15 @@ const tags = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mermaid: {
|
||||||
|
render: Mermaid,
|
||||||
|
attributes: {
|
||||||
|
path: { type: String },
|
||||||
|
},
|
||||||
|
},
|
||||||
img: {
|
img: {
|
||||||
render: (props: { src: string; alt: string; className: string }) => (
|
render: ({ src, alt = "", className = "" }: { src: string; alt: string; className: string }) => (
|
||||||
<img src={props.src} alt={props.alt} className={props.className} loading="lazy" />
|
<img src={src} alt={alt} className={className} loading="lazy" />
|
||||||
),
|
),
|
||||||
attributes: {
|
attributes: {
|
||||||
src: { type: String },
|
src: { type: String },
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import logoUrl from "@/assets/logo.svg";
|
import logoUrl from "@/assets/logo.svg";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export default function HeadDefault() {
|
export default function HeadDefault() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
import { usePageContext } from "vike-react/usePageContext";
|
import { usePageContext } from "vike-react/usePageContext";
|
||||||
import { Link } from "@/components/common/Link";
|
import { Link } from "@/components/common/Link";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const { is404 } = usePageContext();
|
const { is404 } = usePageContext();
|
||||||
|
|
||||||
if (is404) {
|
if (is404) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -18,7 +16,7 @@ export default function Page() {
|
|||||||
Désolé, nous ne pouvons pas trouver la page que vous recherchez.
|
Désolé, nous ne pouvons pas trouver la page que vous recherchez.
|
||||||
</p>
|
</p>
|
||||||
<Link href="/" className="mt-8 text-sm font-medium text-slate-900 dark:text-white">
|
<Link href="/" className="mt-8 text-sm font-medium text-slate-900 dark:text-white">
|
||||||
Retour à l'accueil
|
Retour à l'accueil
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { render } from "vike/abort";
|
|||||||
|
|
||||||
export type Data = Awaited<ReturnType<typeof data>>;
|
export type Data = Awaited<ReturnType<typeof data>>;
|
||||||
|
|
||||||
export async function data(_pageContext: PageContext) {
|
export async function data(pageContext: PageContext) {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
|
|
||||||
const doc = await docsService.getDoc("docs", "index");
|
const doc = await docsService.getDoc("docs", "index");
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ThemeContext, type Theme } from "@/contexts/ThemeContext";
|
import { ThemeContext, type Theme } from "@/contexts/ThemeContext";
|
||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
type ThemeProviderProps = {
|
type ThemeProviderProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import path from "path";
|
||||||
|
|
||||||
type SnippetsCache = Map<string, string>;
|
type SnippetsCache = Map<string, string>;
|
||||||
|
|
||||||
class SnippetsService {
|
class SnippetsService {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user