feat: Add estimated reading time to DocsHeader and DocsLayout
This commit is contained in:
parent
d0dca7d956
commit
b7ad77d73b
@ -1,19 +1,32 @@
|
||||
import { usePageContext } from "vike-react/usePageContext";
|
||||
import { ClockIcon } from "@heroicons/react/24/outline";
|
||||
import { navigation } from "@/lib/navigation";
|
||||
|
||||
export function DocsHeader({ title }: { title?: string }) {
|
||||
type DocsHeaderProps = {
|
||||
title?: string;
|
||||
estimatedReadingTime?: string;
|
||||
};
|
||||
|
||||
export function DocsHeader(props: DocsHeaderProps) {
|
||||
const { urlPathname } = usePageContext();
|
||||
|
||||
const section = navigation.find((section) => section.links.find((link) => link.href === urlPathname));
|
||||
|
||||
if (!title && !section) {
|
||||
if (!props.title && !section) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<header className="mb-9 space-y-1">
|
||||
{section && <p className="font-display text-sm font-medium text-violet-500">{section.title}</p>}
|
||||
{title && <h1 className="font-display text-3xl tracking-tight text-slate-900 dark:text-white">{title}</h1>}
|
||||
{props.title && (
|
||||
<h1 className="font-display text-3xl tracking-tight text-slate-900 dark:text-white">{props.title}</h1>
|
||||
)}
|
||||
{props.estimatedReadingTime && (
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400 inline-flex items-center gap-1">
|
||||
<ClockIcon className="w-4" /> {props.estimatedReadingTime}
|
||||
</p>
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
@ -9,10 +9,12 @@ import { Prose } from "@syntax/Prose";
|
||||
export function DocsLayout({
|
||||
children,
|
||||
frontmatter: { title },
|
||||
estimatedReadingTime,
|
||||
nodes,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
frontmatter: { title?: string };
|
||||
estimatedReadingTime?: string;
|
||||
nodes: Array<Node>;
|
||||
}) {
|
||||
let tableOfContents = collectSections(nodes);
|
||||
@ -21,7 +23,7 @@ export function DocsLayout({
|
||||
<>
|
||||
<div className="max-w-2xl min-w-0 flex-auto px-4 py-16 lg:max-w-none lg:pr-0 lg:pl-8 xl:px-16">
|
||||
<article>
|
||||
<DocsHeader title={title} />
|
||||
<DocsHeader title={title} estimatedReadingTime={estimatedReadingTime} />
|
||||
<Prose>{children}</Prose>
|
||||
</article>
|
||||
<PrevNextLinks />
|
||||
|
||||
@ -18,6 +18,7 @@ const nodes = {
|
||||
this.render,
|
||||
{
|
||||
frontmatter: yaml.load(node.attributes.frontmatter),
|
||||
estimatedReadingTime: config?.variables?.estimatedReadingTime,
|
||||
nodes: node.children,
|
||||
},
|
||||
node.transformChildren(config),
|
||||
@ -12,6 +12,7 @@
|
||||
"@fontsource-variable/inter": "^5.2.5",
|
||||
"@fontsource-variable/lexend": "^5.2.5",
|
||||
"@headlessui/react": "^2.2.0",
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"@markdoc/markdoc": "^0.5.1",
|
||||
"@sindresorhus/slugify": "^2.2.1",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
@ -28,6 +29,7 @@
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-highlight-words": "^0.21.0",
|
||||
"reading-time-estimator": "^1.12.0",
|
||||
"simple-functional-loader": "^1.2.1",
|
||||
"telefunc": "^0.1.87",
|
||||
"unplugin-fonts": "^1.3.1",
|
||||
|
||||
@ -7,10 +7,10 @@ import tags from "@/markdoc/tags";
|
||||
import React from "react";
|
||||
|
||||
export default function Page() {
|
||||
const { doc } = useData<Data>();
|
||||
const { doc, estimatedReadingTime } = useData<Data>();
|
||||
|
||||
const parsedDoc = Markdoc.parse(doc.content);
|
||||
const transformedDoc = Markdoc.transform(parsedDoc, { nodes, tags, variables: {} });
|
||||
const transformedDoc = Markdoc.transform(parsedDoc, { nodes, tags, variables: { estimatedReadingTime } });
|
||||
|
||||
return Markdoc.renderers.react(transformedDoc, React);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { PageContext } from "vike/types";
|
||||
|
||||
import { docsService } from "@/services/DocsService";
|
||||
import { readingTime } from "reading-time-estimator";
|
||||
import { useConfig } from "vike-react/useConfig";
|
||||
import buildTitle from "@/pages/buildTitle";
|
||||
import { render } from "vike/abort";
|
||||
@ -18,7 +19,7 @@ export async function data(pageContext: PageContext) {
|
||||
throw render(404);
|
||||
}
|
||||
|
||||
console.log({ doc });
|
||||
const readingTimeObject = readingTime(doc.content, 300, "fr");
|
||||
|
||||
config({
|
||||
title: buildTitle(doc.title),
|
||||
@ -27,5 +28,5 @@ export async function data(pageContext: PageContext) {
|
||||
|
||||
docsService.transform(doc);
|
||||
|
||||
return { doc };
|
||||
return { doc, estimatedReadingTime: readingTimeObject.text };
|
||||
}
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
import React from "react";
|
||||
import { useData } from "vike-react/useData";
|
||||
import type { Data } from "./+data.js";
|
||||
|
||||
export default function Page() {
|
||||
const movie = useData<Data>();
|
||||
return (
|
||||
<>
|
||||
<h1>{movie.title}</h1>
|
||||
Release Date: {movie.release_date}
|
||||
<br />
|
||||
Director: {movie.director}
|
||||
<br />
|
||||
Producer: {movie.producer}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
// https://vike.dev/data
|
||||
|
||||
import type { PageContextServer } from "vike/types";
|
||||
import type { MovieDetails } from "../types.js";
|
||||
import { useConfig } from "vike-react/useConfig";
|
||||
|
||||
export type Data = Awaited<ReturnType<typeof data>>;
|
||||
|
||||
export const data = async (pageContext: PageContextServer) => {
|
||||
// https://vike.dev/useConfig
|
||||
const config = useConfig();
|
||||
|
||||
const response = await fetch(`https://brillout.github.io/star-wars/api/films/${pageContext.routeParams.id}.json`);
|
||||
let movie = (await response.json()) as MovieDetails;
|
||||
|
||||
config({
|
||||
// Set <title>
|
||||
title: movie.title,
|
||||
});
|
||||
|
||||
// We remove data we don't need because the data is passed to
|
||||
// the client; we should minimize what is sent over the network.
|
||||
movie = minimize(movie);
|
||||
|
||||
return movie;
|
||||
};
|
||||
|
||||
function minimize(movie: MovieDetails): MovieDetails {
|
||||
const { id, title, release_date, director, producer } = movie;
|
||||
const minimizedMovie = { id, title, release_date, director, producer };
|
||||
return minimizedMovie;
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
import React from "react";
|
||||
import { useData } from "vike-react/useData";
|
||||
import type { Data } from "./+data.js";
|
||||
|
||||
export default function Page() {
|
||||
const movies = useData<Data>();
|
||||
return (
|
||||
<>
|
||||
<h1>Star Wars Movies</h1>
|
||||
<ol>
|
||||
{movies.map(({ id, title, release_date }) => (
|
||||
<li key={id}>
|
||||
<a href={`/star-wars/${id}`}>{title}</a> ({release_date})
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
<p>
|
||||
Source: <a href="https://brillout.github.io/star-wars">brillout.github.io/star-wars</a>.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
// https://vike.dev/data
|
||||
|
||||
import type { Movie, MovieDetails } from "../types.js";
|
||||
import { useConfig } from "vike-react/useConfig";
|
||||
|
||||
export type Data = Awaited<ReturnType<typeof data>>;
|
||||
|
||||
export const data = async () => {
|
||||
// https://vike.dev/useConfig
|
||||
const config = useConfig();
|
||||
|
||||
const response = await fetch("https://brillout.github.io/star-wars/api/films.json");
|
||||
const moviesData = (await response.json()) as MovieDetails[];
|
||||
|
||||
config({
|
||||
// Set <title>
|
||||
title: `${moviesData.length} Star Wars Movies`,
|
||||
});
|
||||
|
||||
// We remove data we don't need because the data is passed to the client; we should
|
||||
// minimize what is sent over the network.
|
||||
const movies = minimize(moviesData);
|
||||
|
||||
return movies;
|
||||
};
|
||||
|
||||
function minimize(movies: MovieDetails[]): Movie[] {
|
||||
return movies.map((movie) => {
|
||||
const { title, release_date, id } = movie;
|
||||
return { title, release_date, id };
|
||||
});
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
export type Movie = {
|
||||
id: string;
|
||||
title: string;
|
||||
release_date: string;
|
||||
};
|
||||
|
||||
export type MovieDetails = Movie & {
|
||||
director: string;
|
||||
producer: string;
|
||||
};
|
||||
@ -1,14 +0,0 @@
|
||||
import type { Data } from "./+data";
|
||||
import React from "react";
|
||||
import { useData } from "vike-react/useData";
|
||||
import { TodoList } from "./TodoList.js";
|
||||
|
||||
export default function Page() {
|
||||
const data = useData<Data>();
|
||||
return (
|
||||
<>
|
||||
<h1>To-do List</h1>
|
||||
<TodoList initialTodoItems={data.todo} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
export const config = {
|
||||
prerender: false,
|
||||
};
|
||||
@ -1,11 +0,0 @@
|
||||
// https://vike.dev/data
|
||||
import { todos } from "../../database/todoItems";
|
||||
import type { PageContextServer } from "vike/types";
|
||||
|
||||
export type Data = {
|
||||
todo: { text: string }[];
|
||||
};
|
||||
|
||||
export default async function data(_pageContext: PageContextServer): Promise<Data> {
|
||||
return { todo: todos };
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
// We use Telefunc (https://telefunc.com) for data mutations. Being able to use Telefunc for fetching initial data is work-in-progress (https://vike.dev/data-fetching#tools).
|
||||
|
||||
import { todos } from "../../database/todoItems";
|
||||
|
||||
export async function onNewTodo({ text }: { text: string }) {
|
||||
todos.push({ text });
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
import { onNewTodo } from "./TodoList.telefunc";
|
||||
import React, { useState } from "react";
|
||||
|
||||
export function TodoList({ initialTodoItems }: { initialTodoItems: { text: string }[] }) {
|
||||
const [todoItems, setTodoItems] = useState(initialTodoItems);
|
||||
const [newTodo, setNewTodo] = useState("");
|
||||
return (
|
||||
<>
|
||||
<ul>
|
||||
{todoItems.map((todoItem, index) => (
|
||||
// biome-ignore lint:
|
||||
<li key={index}>{todoItem.text}</li>
|
||||
))}
|
||||
</ul>
|
||||
<div>
|
||||
<form
|
||||
onSubmit={async (ev) => {
|
||||
ev.preventDefault();
|
||||
|
||||
// Optimistic UI update
|
||||
setTodoItems((prev) => [...prev, { text: newTodo }]);
|
||||
try {
|
||||
await onNewTodo({ text: newTodo });
|
||||
setNewTodo("");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// rollback
|
||||
setTodoItems((prev) => prev.slice(0, -1));
|
||||
}
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
onChange={(ev) => setNewTodo(ev.target.value)}
|
||||
value={newTodo}
|
||||
className={
|
||||
"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-purple-500 focus:border-purple-500 w-full sm:w-auto p-2 mr-1 mb-1"
|
||||
}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className={
|
||||
"text-white bg-purple-700 hover:bg-purple-800 focus:ring-2 focus:outline-hidden focus:ring-purple-300 font-medium rounded-lg text-sm w-full sm:w-auto p-2"
|
||||
}
|
||||
>
|
||||
Add to-do
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
98
app/pnpm-lock.yaml
generated
98
app/pnpm-lock.yaml
generated
@ -26,6 +26,9 @@ importers:
|
||||
'@headlessui/react':
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@heroicons/react':
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0(react@19.0.0)
|
||||
'@markdoc/markdoc':
|
||||
specifier: ^0.5.1
|
||||
version: 0.5.1(@types/react@19.0.10)(react@19.0.0)
|
||||
@ -74,6 +77,9 @@ importers:
|
||||
react-highlight-words:
|
||||
specifier: ^0.21.0
|
||||
version: 0.21.0(react@19.0.0)
|
||||
reading-time-estimator:
|
||||
specifier: ^1.12.0
|
||||
version: 1.12.0
|
||||
simple-functional-loader:
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1
|
||||
@ -719,6 +725,11 @@ packages:
|
||||
react: ^18 || ^19 || ^19.0.0-rc
|
||||
react-dom: ^18 || ^19 || ^19.0.0-rc
|
||||
|
||||
'@heroicons/react@2.2.0':
|
||||
resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==}
|
||||
peerDependencies:
|
||||
react: '>= 16 || ^19.0.0-rc'
|
||||
|
||||
'@humanfs/core@0.19.1':
|
||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
@ -1401,6 +1412,10 @@ packages:
|
||||
deep-is@0.1.4:
|
||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||
|
||||
deepmerge@4.3.1:
|
||||
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
define-data-property@1.1.4:
|
||||
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -1425,6 +1440,19 @@ packages:
|
||||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
dom-serializer@2.0.0:
|
||||
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
|
||||
|
||||
domelementtype@2.3.0:
|
||||
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
|
||||
|
||||
domhandler@5.0.3:
|
||||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
domutils@3.2.2:
|
||||
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -1449,6 +1477,10 @@ packages:
|
||||
resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
es-abstract@1.23.9:
|
||||
resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -1772,6 +1804,9 @@ packages:
|
||||
highlight-words-core@1.2.3:
|
||||
resolution: {integrity: sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ==}
|
||||
|
||||
htmlparser2@8.0.2:
|
||||
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
|
||||
|
||||
http-errors@2.0.0:
|
||||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@ -1867,6 +1902,10 @@ packages:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
is-plain-object@5.0.0:
|
||||
resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-regex@1.2.1:
|
||||
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -2180,6 +2219,9 @@ packages:
|
||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
parse-srcset@1.0.2:
|
||||
resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==}
|
||||
|
||||
path-browserify@1.0.1:
|
||||
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
|
||||
|
||||
@ -2310,6 +2352,9 @@ packages:
|
||||
resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
reading-time-estimator@1.12.0:
|
||||
resolution: {integrity: sha512-A17wehSIG6bxen84mf5m+MP4bDDZytcfMOfJWDp1DT8StGRgljtD58zC6E+fSnQZvU5mfsjiJZ7BLhr9kHYoQQ==}
|
||||
|
||||
real-require@0.2.0:
|
||||
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
|
||||
engines: {node: '>= 12.13.0'}
|
||||
@ -2385,6 +2430,9 @@ packages:
|
||||
safer-buffer@2.1.2:
|
||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||
|
||||
sanitize-html@2.15.0:
|
||||
resolution: {integrity: sha512-wIjst57vJGpLyBP8ioUbg6ThwJie5SuSIjHxJg53v5Fg+kUK+AXlb7bK3RNXpp315MvwM+0OBGCV6h5pPHsVhA==}
|
||||
|
||||
scheduler@0.25.0:
|
||||
resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==}
|
||||
|
||||
@ -3298,6 +3346,10 @@ snapshots:
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
|
||||
'@heroicons/react@2.2.0(react@19.0.0)':
|
||||
dependencies:
|
||||
react: 19.0.0
|
||||
|
||||
'@humanfs/core@0.19.1': {}
|
||||
|
||||
'@humanfs/node@0.16.6':
|
||||
@ -4007,6 +4059,8 @@ snapshots:
|
||||
|
||||
deep-is@0.1.4: {}
|
||||
|
||||
deepmerge@4.3.1: {}
|
||||
|
||||
define-data-property@1.1.4:
|
||||
dependencies:
|
||||
es-define-property: 1.0.1
|
||||
@ -4029,6 +4083,24 @@ snapshots:
|
||||
dependencies:
|
||||
esutils: 2.0.3
|
||||
|
||||
dom-serializer@2.0.0:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
domhandler: 5.0.3
|
||||
entities: 4.5.0
|
||||
|
||||
domelementtype@2.3.0: {}
|
||||
|
||||
domhandler@5.0.3:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
|
||||
domutils@3.2.2:
|
||||
dependencies:
|
||||
dom-serializer: 2.0.0
|
||||
domelementtype: 2.3.0
|
||||
domhandler: 5.0.3
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
@ -4050,6 +4122,8 @@ snapshots:
|
||||
graceful-fs: 4.2.11
|
||||
tapable: 2.2.1
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
es-abstract@1.23.9:
|
||||
dependencies:
|
||||
array-buffer-byte-length: 1.0.2
|
||||
@ -4535,6 +4609,13 @@ snapshots:
|
||||
|
||||
highlight-words-core@1.2.3: {}
|
||||
|
||||
htmlparser2@8.0.2:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
domhandler: 5.0.3
|
||||
domutils: 3.2.2
|
||||
entities: 4.5.0
|
||||
|
||||
http-errors@2.0.0:
|
||||
dependencies:
|
||||
depd: 2.0.0
|
||||
@ -4634,6 +4715,8 @@ snapshots:
|
||||
|
||||
is-number@7.0.0: {}
|
||||
|
||||
is-plain-object@5.0.0: {}
|
||||
|
||||
is-regex@1.2.1:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
@ -4918,6 +5001,8 @@ snapshots:
|
||||
dependencies:
|
||||
callsites: 3.1.0
|
||||
|
||||
parse-srcset@1.0.2: {}
|
||||
|
||||
path-browserify@1.0.1: {}
|
||||
|
||||
path-exists@4.0.0: {}
|
||||
@ -5043,6 +5128,10 @@ snapshots:
|
||||
|
||||
react@19.0.0: {}
|
||||
|
||||
reading-time-estimator@1.12.0:
|
||||
dependencies:
|
||||
sanitize-html: 2.15.0
|
||||
|
||||
real-require@0.2.0: {}
|
||||
|
||||
reflect.getprototypeof@1.0.10:
|
||||
@ -5143,6 +5232,15 @@ snapshots:
|
||||
|
||||
safer-buffer@2.1.2: {}
|
||||
|
||||
sanitize-html@2.15.0:
|
||||
dependencies:
|
||||
deepmerge: 4.3.1
|
||||
escape-string-regexp: 4.0.0
|
||||
htmlparser2: 8.0.2
|
||||
is-plain-object: 5.0.0
|
||||
parse-srcset: 1.0.2
|
||||
postcss: 8.5.3
|
||||
|
||||
scheduler@0.25.0: {}
|
||||
|
||||
search-insights@2.17.3: {}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user