refactor #5
@ -1,10 +1,10 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import eslint from "@eslint/js";
|
|
||||||
import prettier from "eslint-plugin-prettier/recommended";
|
|
||||||
import react from "eslint-plugin-react/configs/recommended.js";
|
import react from "eslint-plugin-react/configs/recommended.js";
|
||||||
import globals from "globals";
|
import prettier from "eslint-plugin-prettier/recommended";
|
||||||
import tseslint from "typescript-eslint";
|
import tseslint from "typescript-eslint";
|
||||||
|
import eslint from "@eslint/js";
|
||||||
|
import globals from "globals";
|
||||||
|
|
||||||
export default tseslint.config(
|
export default tseslint.config(
|
||||||
{
|
{
|
||||||
@ -33,14 +33,10 @@ export default tseslint.config(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
rules: {
|
rules: {
|
||||||
"@typescript-eslint/no-unused-vars": [
|
"@typescript-eslint/no-unused-vars": [1, { argsIgnorePattern: "^_" }],
|
||||||
1,
|
|
||||||
{
|
|
||||||
argsIgnorePattern: "^_",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-namespace": 0,
|
"@typescript-eslint/no-namespace": 0,
|
||||||
"react/react-in-jsx-scope": false,
|
"react/react-in-jsx-scope": "warn",
|
||||||
|
"react/jsx-filename-extension": [1, { extensions: [".tsx"] }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -27,7 +27,7 @@ function GitHubIcon(props: React.ComponentPropsWithoutRef<"svg">) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Header() {
|
function Header() {
|
||||||
let [isScrolled, setIsScrolled] = useState(false);
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function onScroll() {
|
function onScroll() {
|
||||||
|
|||||||
@ -5,6 +5,8 @@ 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 {
|
||||||
@ -32,20 +34,22 @@ 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 (let child of node.children!) {
|
for (const 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) {
|
if (isRoot) slugify.reset();
|
||||||
slugify.reset();
|
|
||||||
}
|
|
||||||
if (node.type === "heading" || node.type === "paragraph") {
|
if (node.type === "heading" || node.type === "paragraph") {
|
||||||
let content = toString(node).trim();
|
const content = toString(node).trim();
|
||||||
|
|
||||||
if (node.type === "heading" && node.attributes?.level! <= 2) {
|
if (node.type === "heading" && node.attributes?.level! <= 2) {
|
||||||
let hash = node.attributes?.id ?? slugify(content);
|
let hash = node.attributes?.id ?? slugify(content);
|
||||||
sections.push({ content, hash, subsections: [] });
|
sections.push({ content, hash, subsections: [] });
|
||||||
@ -53,7 +57,7 @@ function extractSections(node: Node, sections: Section[], isRoot: boolean = true
|
|||||||
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 (let child of node.children!) {
|
for (const child of node.children!) {
|
||||||
extractSections(child, sections, false);
|
extractSections(child, sections, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,18 +115,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, any> = {},
|
options: Record<string, SearchOptionValue> = {},
|
||||||
): SearchResult[] {
|
): SearchResult[] {
|
||||||
const results = sectionIndex.search(query, {
|
const results = sectionIndex.search(query, {
|
||||||
...options,
|
...options,
|
||||||
enrich: true,
|
enrich: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (results.length === 0) {
|
if (results.length === 0) return [];
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return results[0].result.map((item: any) => ({
|
const searchResults = results[0].result as unknown as { id: string; doc: { title: string; pageTitle: string } }[];
|
||||||
|
|
||||||
|
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,7 +41,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,14 @@ import type { ReactNode } from "react";
|
|||||||
|
|
||||||
import { Tag as MarkdocTag } from "@markdoc/markdoc";
|
import { Tag as MarkdocTag } from "@markdoc/markdoc";
|
||||||
|
|
||||||
|
type TagAttributesValue = string | number | boolean | null | undefined | object | unknown;
|
||||||
|
|
||||||
export class Tag extends MarkdocTag {
|
export class Tag extends MarkdocTag {
|
||||||
constructor(name: string | ReactNode, attributes: Record<string, any>, children: RenderableTreeNode[]) {
|
constructor(
|
||||||
|
name: string | ReactNode,
|
||||||
|
attributes: Record<string, TagAttributesValue>,
|
||||||
|
children: RenderableTreeNode[],
|
||||||
|
) {
|
||||||
// Workaround for TypeScript's type system
|
// Workaround for TypeScript's type system
|
||||||
super(name as unknown as string, attributes, children);
|
super(name as unknown as string, attributes, children);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { Fence } from "@syntax/Fence";
|
|||||||
import { Tag } from "./Tag";
|
import { Tag } from "./Tag";
|
||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
|
|
||||||
let documentSlugifyMap = new Map();
|
const documentSlugifyMap = new Map();
|
||||||
|
|
||||||
const nodes = {
|
const nodes = {
|
||||||
document: {
|
document: {
|
||||||
|
|||||||
@ -36,10 +36,10 @@ const tags = {
|
|||||||
alt: { type: String },
|
alt: { type: String },
|
||||||
caption: { type: String },
|
caption: { type: String },
|
||||||
},
|
},
|
||||||
render: ({ src, alt = "", caption }: { src: string; alt: string; caption: string }) => (
|
render: (props: { src: string; alt: string; caption: string }) => (
|
||||||
<figure>
|
<figure>
|
||||||
<img src={src} alt={alt} loading="lazy" />
|
<img src={props.src} alt={props.alt} loading="lazy" />
|
||||||
<figcaption>{caption}</figcaption>
|
<figcaption>{props.caption}</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -85,8 +85,8 @@ const tags = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
img: {
|
img: {
|
||||||
render: ({ src, alt = "", className = "" }: { src: string; alt: string; className: string }) => (
|
render: (props: { src: string; alt: string; className: string }) => (
|
||||||
<img src={src} alt={alt} className={className} loading="lazy" />
|
<img src={props.src} alt={props.alt} className={props.className} loading="lazy" />
|
||||||
),
|
),
|
||||||
attributes: {
|
attributes: {
|
||||||
src: { type: String },
|
src: { type: String },
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
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,8 +1,10 @@
|
|||||||
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 (
|
||||||
<>
|
<>
|
||||||
@ -16,7 +18,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 { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
type ThemeProviderProps = {
|
type ThemeProviderProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
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