docu version 1.8.5
This commit is contained in:
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,3 +1,31 @@
|
|||||||
|
## [1.8.5] - 2025-05-10
|
||||||
|
|
||||||
|
> Add sponsor card on single docs page
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Expandables Leftbar
|
||||||
|
- sponsor badges or ads
|
||||||
|
- boolean show/hide `edit on github`
|
||||||
|
- with the same code run anywhere (bun or nodejs)
|
||||||
|
- add fronmatter (metadata) to playground editor
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
|
||||||
|
- adjusment docu.json
|
||||||
|
- adjustment navbar, footer and components
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- bun compability rename .js to common js
|
||||||
|
- cli manage packageManager on package.json
|
||||||
|
- inconsistent design moved to better UI/UX
|
||||||
|
- error handle render footer.social
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- remove confused and verbose cli on installer
|
||||||
|
|
||||||
## [1.8.0] - 2025-03-01
|
## [1.8.0] - 2025-03-01
|
||||||
|
|
||||||
> Now looks more modern and clean which is a big change in layout and design
|
> Now looks more modern and clean which is a big change in layout and design
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { formatDate2, stringToDate } from "@/lib/utils";
|
|||||||
import { getMetadata } from "@/app/layout";
|
import { getMetadata } from "@/app/layout";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { AuroraText } from "@/components/ui/aurora";
|
||||||
|
import { ShineBorder } from "@/components/ui/shine-border";
|
||||||
import docuConfig from "@/docu.json";
|
import docuConfig from "@/docu.json";
|
||||||
|
|
||||||
export const metadata = getMetadata({
|
export const metadata = getMetadata({
|
||||||
@@ -16,16 +18,17 @@ export default async function BlogIndexPage() {
|
|||||||
(a, b) => stringToDate(b.date).getTime() - stringToDate(a.date).getTime()
|
(a, b) => stringToDate(b.date).getTime() - stringToDate(a.date).getTime()
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div className="w-full mx-auto flex flex-col gap-1 sm:min-h-[91vh] min-h-[88vh] py-2">
|
<div className="flex flex-col items-center justify-center px-2 py-8 text-center sm:py-36">
|
||||||
<div className="mb-7 flex flex-col gap-2">
|
<div className="w-full max-w-[800px] pb-8">
|
||||||
<h1 className="text-2xl font-extrabold">
|
<AuroraText className="text-lg"># Stay Informed, Stay Ahead</AuroraText>
|
||||||
Blog Posts
|
<h1 className="mb-4 text-2xl font-bold sm:text-5xl">
|
||||||
|
Blog Posts
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-lg text-muted-foreground mt-2">
|
<p className="mb-8 sm:text-xl text-muted-foreground">
|
||||||
Discover the latest updates, tutorials, and insights on {meta.title}.
|
Explore updates, tips, and deep dives from the {meta.title}.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid md:grid-cols-3 sm:grid-cols-2 grid-cols-1 sm:gap-8 gap-4 mb-5">
|
<div className="text-left grid md:grid-cols-3 sm:grid-cols-2 grid-cols-1 sm:gap-8 gap-4 my-6">
|
||||||
{blogs.map((blog) => (
|
{blogs.map((blog) => (
|
||||||
<BlogCard {...blog} slug={blog.slug} key={blog.slug} />
|
<BlogCard {...blog} slug={blog.slug} key={blog.slug} />
|
||||||
))}
|
))}
|
||||||
@@ -45,9 +48,8 @@ function BlogCard({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={`/blog/${slug}`}
|
href={`/blog/${slug}`}
|
||||||
className="flex flex-col gap-2 items-start border rounded-md py-5 px-3 min-h-[400px]"
|
className="flex flex-col gap-2 items-start border rounded-md max-h-[420px] min-h-[420px]"
|
||||||
>
|
>
|
||||||
<h3 className="text-md font-semibold -mt-1 pr-7">{title}</h3>
|
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Image
|
<Image
|
||||||
src={cover}
|
src={cover}
|
||||||
@@ -55,11 +57,14 @@ function BlogCard({
|
|||||||
width={400}
|
width={400}
|
||||||
height={150}
|
height={150}
|
||||||
quality={80}
|
quality={80}
|
||||||
className="w-full rounded-md object-cover h-[180px] border"
|
className="w-full rounded-md object-cover h-[200px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted-foreground">{description}</p>
|
<div className="flex flex-col items-start px-3 py-3 gap-2 mb-auto">
|
||||||
<div className="flex items-center justify-between w-full mt-auto">
|
<h3 className="text-md font-semibold line-clamp-2">{title}</h3>
|
||||||
|
<p className="text-sm text-muted-foreground line-clamp-3">{description}</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between w-full px-3 mb-6">
|
||||||
<p className="text-[13px] text-muted-foreground">
|
<p className="text-[13px] text-muted-foreground">
|
||||||
Published on {formatDate2(date)}
|
Published on {formatDate2(date)}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -2,42 +2,23 @@ import { Suspense } from "react";
|
|||||||
import { getChangelogEntries } from "@/lib/changelog";
|
import { getChangelogEntries } from "@/lib/changelog";
|
||||||
import { VersionEntry } from "@/components/changelog/version-entry";
|
import { VersionEntry } from "@/components/changelog/version-entry";
|
||||||
import { VersionToc } from "@/components/changelog/version-toc";
|
import { VersionToc } from "@/components/changelog/version-toc";
|
||||||
import { getMetadata } from "@/app/layout";
|
|
||||||
import docuConfig from "@/docu.json";
|
|
||||||
import { FloatingVersionToc } from "@/components/changelog/floating-version";
|
import { FloatingVersionToc } from "@/components/changelog/floating-version";
|
||||||
|
|
||||||
export const metadata = getMetadata({
|
|
||||||
title: "Changelog",
|
|
||||||
description: "Latest updates and improvements to DocuBook",
|
|
||||||
image: "release-note.png",
|
|
||||||
});
|
|
||||||
|
|
||||||
export default async function ChangelogPage() {
|
export default async function ChangelogPage() {
|
||||||
const entries = await getChangelogEntries();
|
const entries = await getChangelogEntries();
|
||||||
const { meta } = docuConfig;
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col w-full">
|
|
||||||
<div className="border-b">
|
|
||||||
<div className="py-8">
|
|
||||||
<h1 className="text-2xl font-extrabold">Changelog</h1>
|
|
||||||
<p className="text-lg text-muted-foreground mt-2">
|
|
||||||
Latest updates and improvements to {meta.title}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="md:container py-8">
|
return (
|
||||||
<div className="flex items-start gap-8">
|
<div className="flex items-start">
|
||||||
<Suspense fallback={<div className="lg:flex hidden flex-[1.5] min-w-[238px]" />}>
|
<Suspense fallback={<div className="lg:flex hidden flex-[1.5]" />}>
|
||||||
<VersionToc
|
<VersionToc
|
||||||
versions={entries.map(({ version, date }) => ({ version, date }))}
|
versions={entries.map(({ version, date }) => ({ version, date }))}
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
||||||
<main className="flex-1 lg:flex-[5.25] min-w-0">
|
<main className="flex-1 md:flex-[5.25] min-w-0 max-w-[800px]">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute left-0 top-0 h-full w-px bg-border lg:block hidden" />
|
<div className="absolute left-0 top-0 h-full w-px bg-border md:block hidden" />
|
||||||
<div className="lg:pl-12 pl-0 lg:pt-8">
|
<div className="md:px-12 md:py-8 max-md:py-10">
|
||||||
{entries.map((entry, index) => (
|
{entries.map((entry, index) => (
|
||||||
<section
|
<section
|
||||||
id={`version-${entry.version}`}
|
id={`version-${entry.version}`}
|
||||||
@@ -50,8 +31,6 @@ export default async function ChangelogPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* Floating TOC for smaller screens */}
|
{/* Floating TOC for smaller screens */}
|
||||||
{entries.length > 0 && (
|
{entries.length > 0 && (
|
||||||
<FloatingVersionToc
|
<FloatingVersionToc
|
||||||
|
|||||||
@@ -87,14 +87,18 @@ export default async function DocsPage({ params: { slug = [] } }: PageProps) {
|
|||||||
<h1 className="text-3xl !-mt-0.5">{title}</h1>
|
<h1 className="text-3xl !-mt-0.5">{title}</h1>
|
||||||
<p className="-mt-4 text-muted-foreground text-[16.5px]">{description}</p>
|
<p className="-mt-4 text-muted-foreground text-[16.5px]">{description}</p>
|
||||||
<div>{res.content}</div>
|
<div>{res.content}</div>
|
||||||
<div className="my-8 flex justify-end items-center border-b-2 border-x-muted-foreground">
|
<div
|
||||||
|
className={`my-8 flex items-center border-b-2 border-dashed border-x-muted-foreground ${
|
||||||
|
docuConfig.repository?.editLink ? "justify-between" : "justify-end"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{docuConfig.repository?.editLink && <EditThisPage filePath={filePath} />}
|
||||||
{date && (
|
{date && (
|
||||||
<p className="text-[13px] text-muted-foreground">
|
<p className="text-[13px] text-muted-foreground">
|
||||||
Published on {formatDate2(date)}
|
Published on {formatDate2(date)}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{/* <EditThisPage filePath={filePath} /> */}
|
</div>
|
||||||
</div>
|
|
||||||
<Pagination pathname={pathName} />
|
<Pagination pathname={pathName} />
|
||||||
</Typography>
|
</Typography>
|
||||||
<ScrollToTop />
|
<ScrollToTop />
|
||||||
|
|||||||
@@ -187,23 +187,24 @@ export default function PlaygroundPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertAtCursor = (textArea: HTMLTextAreaElement, text: string) => {
|
const insertAtCursor = (textArea: HTMLTextAreaElement, text: string) => {
|
||||||
const start = textArea.selectionStart;
|
const start = textArea.selectionStart;
|
||||||
const end = textArea.selectionEnd;
|
const end = textArea.selectionEnd;
|
||||||
const before = markdown.substring(0, start);
|
const before = markdown.substring(0, start);
|
||||||
const after = markdown.substring(end);
|
const after = markdown.substring(end);
|
||||||
|
|
||||||
// Menambahkan satu baris kosong sebelum dan sesudah komponen
|
const needsLeadingNewline = before && !before.endsWith('\n\n') ? '\n\n' : '';
|
||||||
const newText = `${before}${text}\n${after}`;
|
const needsTrailingNewline = after && !after.startsWith('\n\n') ? '\n\n' : '';
|
||||||
|
|
||||||
|
const newText = `${before}${needsLeadingNewline}${text}${needsTrailingNewline}${after}`;
|
||||||
setMarkdown(newText);
|
setMarkdown(newText);
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
textArea.focus();
|
textArea.focus();
|
||||||
const newPosition = start + text.length + 1;
|
const newPosition = before.length + needsLeadingNewline.length + text.length + 1;
|
||||||
textArea.setSelectionRange(newPosition, newPosition);
|
textArea.setSelectionRange(newPosition, newPosition);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return <MobileMessage />;
|
return <MobileMessage />;
|
||||||
|
|||||||
37
components/Sponsor.tsx
Normal file
37
components/Sponsor.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import docuConfig from "@/docu.json";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export function Sponsor() {
|
||||||
|
const sponsor = docuConfig.sponsor;
|
||||||
|
const item = sponsor?.item;
|
||||||
|
|
||||||
|
if (!item) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-4">
|
||||||
|
<h2 className="mb-4 text-sm font-medium">{sponsor.title || "Sponsor"}</h2>
|
||||||
|
<Link
|
||||||
|
href={item.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex flex-col justify-center gap-2 p-4 border rounded-lg hover:shadow transition-shadow"
|
||||||
|
>
|
||||||
|
<div className="relative w-8 h-8 flex-shrink-0">
|
||||||
|
<Image
|
||||||
|
src={item.image}
|
||||||
|
alt={item.title}
|
||||||
|
fill
|
||||||
|
className="object-contain"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="text-center sm:text-left">
|
||||||
|
<h3 className="text-sm font-medium">{item.title}</h3>
|
||||||
|
<p className="text-muted-foreground text-sm">{item.description}</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Sponsor;
|
||||||
@@ -51,7 +51,7 @@ export function FloatingVersionToc({ versions }: FloatingVersionTocProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed bottom-4 right-4 lg:hidden z-50">
|
<div className="fixed bottom-4 right-4 md:hidden z-50">
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button variant="outline" className="rounded-full shadow-lg px-4 py-2 flex items-center gap-2">
|
<Button variant="outline" className="rounded-full shadow-lg px-4 py-2 flex items-center gap-2">
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ interface VersionEntryProps {
|
|||||||
isLast?: boolean;
|
isLast?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function VersionEntry({
|
export function VersionEntry({
|
||||||
version,
|
version,
|
||||||
date,
|
date,
|
||||||
description,
|
description,
|
||||||
image,
|
image,
|
||||||
changes,
|
changes,
|
||||||
isLast
|
isLast
|
||||||
}: VersionEntryProps) {
|
}: VersionEntryProps) {
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ export function VersionEntry({
|
|||||||
{formatDate2(date)}
|
{formatDate2(date)}
|
||||||
</time>
|
</time>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{description && (
|
{description && (
|
||||||
<p className="text-dark text-xl">{description}</p>
|
<p className="text-dark text-xl">{description}</p>
|
||||||
)}
|
)}
|
||||||
@@ -69,8 +69,8 @@ export function VersionEntry({
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{Object.entries(changes).map(([type, items]) => (
|
{Object.entries(changes).map(([type, items]) => (
|
||||||
items && items.length > 0 && (
|
items && items.length > 0 && (
|
||||||
<ChangeGroup
|
<ChangeGroup
|
||||||
key={type}
|
key={type}
|
||||||
type={type as keyof typeof changes}
|
type={type as keyof typeof changes}
|
||||||
changes={items}
|
changes={items}
|
||||||
expanded={expanded}
|
expanded={expanded}
|
||||||
@@ -82,10 +82,10 @@ export function VersionEntry({
|
|||||||
{/* Show more/less button */}
|
{/* Show more/less button */}
|
||||||
{Object.values(changes).some(items => items && items.length > 5) && (
|
{Object.values(changes).some(items => items && items.length > 5) && (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setExpanded(!expanded)}
|
onClick={() => setExpanded(!expanded)}
|
||||||
className="mt-4 text-muted-foreground hover:text-foreground"
|
className="mt-4 text-muted-foreground hover:bg-transparent hover:text-accent border-none"
|
||||||
>
|
>
|
||||||
{expanded ? (
|
{expanded ? (
|
||||||
<>
|
<>
|
||||||
@@ -110,4 +110,4 @@ export function VersionEntry({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { cn, formatDate2 } from "@/lib/utils";
|
import { cn, formatDate2 } from "@/lib/utils";
|
||||||
import { History } from "lucide-react";
|
import { History, PanelLeftOpen, PanelLeftClose } from "lucide-react";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
interface VersionTocProps {
|
interface VersionTocProps {
|
||||||
versions: Array<{
|
versions: Array<{
|
||||||
@@ -14,33 +15,30 @@ interface VersionTocProps {
|
|||||||
|
|
||||||
export function VersionToc({ versions }: VersionTocProps) {
|
export function VersionToc({ versions }: VersionTocProps) {
|
||||||
const [activeId, setActiveId] = useState<string | null>(null);
|
const [activeId, setActiveId] = useState<string | null>(null);
|
||||||
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Handle initial hash
|
|
||||||
const hash = window.location.hash.slice(1);
|
const hash = window.location.hash.slice(1);
|
||||||
if (hash) {
|
if (hash) {
|
||||||
setActiveId(hash);
|
setActiveId(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up intersection observer
|
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
if (entry.isIntersecting) {
|
if (entry.isIntersecting) {
|
||||||
const id = entry.target.id;
|
const id = entry.target.id;
|
||||||
setActiveId(id);
|
setActiveId(id);
|
||||||
// Use pushState instead of replaceState to maintain history
|
window.history.pushState(null, "", `#${id}`);
|
||||||
window.history.pushState(null, '', `#${id}`);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
threshold: 0.2,
|
threshold: 0.2,
|
||||||
rootMargin: '-20% 0px -60% 0px'
|
rootMargin: "-20% 0px -60% 0px",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Observe version elements
|
|
||||||
versions.forEach(({ version }) => {
|
versions.forEach(({ version }) => {
|
||||||
const element = document.getElementById(`v${version}`);
|
const element = document.getElementById(`v${version}`);
|
||||||
if (element) observer.observe(element);
|
if (element) observer.observe(element);
|
||||||
@@ -50,41 +48,64 @@ export function VersionToc({ versions }: VersionTocProps) {
|
|||||||
}, [versions]);
|
}, [versions]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="lg:flex hidden toc flex-[1.5] min-w-[238px] pt-8 sticky top-16 h-[calc(100vh-4rem)]">
|
<aside
|
||||||
<div className="flex flex-col gap-2 w-full">
|
className={cn(
|
||||||
<div className="flex items-center gap-2 mb-2">
|
"sticky top-16 h-[calc(100vh-4rem)] border-r bg-background transition-all duration-300 z-20 hidden md:flex",
|
||||||
<History className="w-4 h-4" />
|
collapsed ? "w-[48px]" : "w-[250px]"
|
||||||
<h3 className="font-medium text-sm">Version History</h3>
|
)}
|
||||||
</div>
|
>
|
||||||
<ScrollArea className="h-full">
|
{/* Toggle Button */}
|
||||||
<div className="flex flex-col gap-1.5 text-sm dark:text-stone-300/85 text-stone-800 pr-4">
|
<div className="absolute top-0 right-0 py-2 px-0 ml-6 z-30">
|
||||||
{versions.map(({ version, date }) => (
|
<Button
|
||||||
<a
|
size="icon"
|
||||||
key={version}
|
variant="outline"
|
||||||
href={`#v${version}`}
|
className="hover:bg-transparent hover:text-inherit border-none text-muted-foreground"
|
||||||
className={cn(
|
onClick={() => setCollapsed((prev) => !prev)}
|
||||||
"hover:text-foreground transition-colors py-1",
|
>
|
||||||
activeId === `v${version}` && "font-medium text-primary"
|
{collapsed ? <PanelLeftOpen size={18} /> : <PanelLeftClose size={18} />}
|
||||||
)}
|
</Button>
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const element = document.getElementById(`v${version}`);
|
|
||||||
if (element) {
|
|
||||||
element.scrollIntoView({ behavior: 'smooth' });
|
|
||||||
setActiveId(`v${version}`);
|
|
||||||
window.history.pushState(null, '', `#v${version}`);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
v{version}
|
|
||||||
<span className="text-xs text-muted-foreground ml-2">
|
|
||||||
{formatDate2(date)}
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</ScrollArea>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
{!collapsed && (
|
||||||
|
<div className="flex flex-col gap-2 w-full pt-8 pr-2">
|
||||||
|
<div className="flex mb-2">
|
||||||
|
<h2 className="font-semibold text-lg">Changelog</h2>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<History className="w-4 h-4" />
|
||||||
|
<h3 className="font-medium text-sm">Version History</h3>
|
||||||
|
</div>
|
||||||
|
<ScrollArea className="h-full pr-2">
|
||||||
|
<div className="flex flex-col gap-1.5 text-sm dark:text-stone-300/85 text-stone-800 pr-4">
|
||||||
|
{versions.map(({ version, date }) => (
|
||||||
|
<a
|
||||||
|
key={version}
|
||||||
|
href={`#v${version}`}
|
||||||
|
className={cn(
|
||||||
|
"hover:text-foreground transition-colors py-1",
|
||||||
|
activeId === `v${version}` && "font-medium text-primary"
|
||||||
|
)}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const element = document.getElementById(`v${version}`);
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({ behavior: "smooth" });
|
||||||
|
setActiveId(`v${version}`);
|
||||||
|
window.history.pushState(null, "", `#v${version}`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
v{version}
|
||||||
|
<span className="text-xs text-muted-foreground ml-2">
|
||||||
|
{formatDate2(date)}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default function DocsBreadcrumb({ paths }: { paths: string[] }) {
|
|||||||
<BreadcrumbLink>Docs</BreadcrumbLink>
|
<BreadcrumbLink>Docs</BreadcrumbLink>
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
{paths.map((path, index) => (
|
{paths.map((path, index) => (
|
||||||
<Fragment key={path}>
|
<Fragment key={`${path}-${index}`}>
|
||||||
<BreadcrumbSeparator />
|
<BreadcrumbSeparator />
|
||||||
<BreadcrumbItem>
|
<BreadcrumbItem>
|
||||||
{index < paths.length - 1 ? (
|
{index < paths.length - 1 ? (
|
||||||
|
|||||||
@@ -4,21 +4,42 @@ import { ROUTES } from "@/lib/routes-config";
|
|||||||
import SubLink from "./sublink";
|
import SubLink from "./sublink";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
|
|
||||||
export default function DocsMenu({ isSheet = false }) {
|
interface DocsMenuProps {
|
||||||
|
isSheet?: boolean;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DocsMenu({ isSheet = false, className = "" }: DocsMenuProps) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
// Skip rendering if not on a docs page
|
||||||
if (!pathname.startsWith("/docs")) return null;
|
if (!pathname.startsWith("/docs")) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3.5 mt-5 pr-2 pb-6">
|
<nav
|
||||||
{ROUTES.map((item, index) => {
|
aria-label="Documentation navigation"
|
||||||
const modifiedItems = {
|
className={className}
|
||||||
...item,
|
>
|
||||||
href: `/docs${item.href}`,
|
<ul className="flex flex-col gap-3.5 mt-5 pr-2 pb-6">
|
||||||
level: 0,
|
{ROUTES.map((item, index) => {
|
||||||
isSheet,
|
// Normalize href - hapus leading/trailing slashes
|
||||||
};
|
const normalizedHref = `/${item.href.replace(/^\/+|\/+$/g, '')}`;
|
||||||
return <SubLink key={item.title + index} {...modifiedItems} />;
|
const itemHref = `/docs${normalizedHref}`;
|
||||||
})}
|
|
||||||
</div>
|
const modifiedItems = {
|
||||||
|
...item,
|
||||||
|
href: itemHref,
|
||||||
|
level: 0,
|
||||||
|
isSheet,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={`${item.title}-${index}`}>
|
||||||
|
<SubLink {...modifiedItems} />
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,24 +9,22 @@ interface EditThisPageProps {
|
|||||||
|
|
||||||
const EditThisPage: React.FC<EditThisPageProps> = ({ filePath }) => {
|
const EditThisPage: React.FC<EditThisPageProps> = ({ filePath }) => {
|
||||||
const { repository } = docuConfig;
|
const { repository } = docuConfig;
|
||||||
|
|
||||||
|
if (!repository?.editLink || !repository.url || !repository.editPathTemplate) return null;
|
||||||
|
|
||||||
const editUrl = `${repository.url}${repository.editPathTemplate.replace("{filePath}", filePath)}`;
|
const editUrl = `${repository.url}${repository.editPathTemplate.replace("{filePath}", filePath)}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ textAlign: 'right' }}>
|
<div className="text-right">
|
||||||
<Link
|
<Link
|
||||||
href={editUrl}
|
href={editUrl}
|
||||||
target='_blank'
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
style={{
|
aria-label="Edit this page on Git"
|
||||||
display: 'inline-flex',
|
className="inline-flex items-center gap-2 text-sm font-medium text-muted-foreground no-underline"
|
||||||
alignItems: 'center',
|
|
||||||
gap: '0.5rem',
|
|
||||||
textDecoration: 'none',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<span className='text-primary text-sm max-[480px]:hidden'>Edit this page</span>
|
<span className="flex justify-start items-center gap-1">Edit this page
|
||||||
<SquarePenIcon className="w-4 h-4 text-primary" />
|
<SquarePenIcon className="w-4 h-4" /></span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export function Footer() {
|
|||||||
const { footer } = docuConfig;
|
const { footer } = docuConfig;
|
||||||
const { meta } = docuConfig;
|
const { meta } = docuConfig;
|
||||||
return (
|
return (
|
||||||
<footer className="w-full py-4 border-t lg:py-8 bg-background">
|
<footer className="w-full py-4 px-2 border-t lg:py-8 bg-background">
|
||||||
<div className="container flex flex-wrap items-center justify-between text-sm">
|
<div className="container flex flex-wrap items-center justify-between text-sm">
|
||||||
<div className="items-start justify-center hidden gap-4 lg:flex-col lg:flex lg:w-3/5">
|
<div className="items-start justify-center hidden gap-4 lg:flex-col lg:flex lg:w-3/5">
|
||||||
<h3 className="text-lg font-bold font-code">{meta.title}</h3>
|
<h3 className="text-lg font-bold font-code">{meta.title}</h3>
|
||||||
@@ -16,12 +16,9 @@ export function Footer() {
|
|||||||
<FooterButtons />
|
<FooterButtons />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-start justify-center w-full gap-4 mt-4 xl:items-end lg:w-2/5">
|
<div className="flex flex-col items-center justify-center w-full gap-4 mt-4 lg:items-end lg:w-2/5">
|
||||||
<p className="text-center text-muted-foreground">
|
<p className="text-center text-muted-foreground">
|
||||||
Copyright © {new Date().getFullYear()} {footer.copyright} - Made with{" "}
|
Copyright © {new Date().getFullYear()} {footer.copyright} - <MadeWith />
|
||||||
<Link href="https://www.docubook.pro" target="_blank" rel="noopener noreferrer" className="underline underline-offset-2">
|
|
||||||
DocuBook
|
|
||||||
</Link>
|
|
||||||
</p>
|
</p>
|
||||||
<div className="hidden lg:flex">
|
<div className="hidden lg:flex">
|
||||||
<ModeToggle />
|
<ModeToggle />
|
||||||
@@ -33,12 +30,20 @@ export function Footer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function FooterButtons() {
|
export function FooterButtons() {
|
||||||
const { footer } = docuConfig;
|
const footer = docuConfig?.footer;
|
||||||
|
|
||||||
|
// Jangan render apapun jika tidak ada data sosial
|
||||||
|
if (!footer?.social || !Array.isArray(footer.social) || footer.social.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{footer.social?.map((item) => {
|
{footer.social.map((item) => {
|
||||||
const IconComponent = (LucideIcons[item.iconName as keyof typeof LucideIcons] ?? LucideIcons["Globe"]) as unknown as React.FC<{ className?: string }>;
|
const IconComponent =
|
||||||
|
(LucideIcons[item.iconName as keyof typeof LucideIcons] ??
|
||||||
|
LucideIcons["Globe"]) as React.FC<{ className?: string }>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
key={item.name}
|
key={item.name}
|
||||||
@@ -54,3 +59,15 @@ export function FooterButtons() {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function MadeWith() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span className="text-muted-foreground">Made with </span>
|
||||||
|
<span className="text-primary">
|
||||||
|
<Link href="https://www.docubook.pro" target="_blank" rel="noopener noreferrer" className="underline underline-offset-2 text-muted-foreground">
|
||||||
|
DocuBook
|
||||||
|
</Link></span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
"use client"
|
||||||
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Sheet,
|
Sheet,
|
||||||
SheetClose,
|
SheetClose,
|
||||||
@@ -5,20 +7,42 @@ import {
|
|||||||
SheetHeader,
|
SheetHeader,
|
||||||
SheetTrigger,
|
SheetTrigger,
|
||||||
} from "@/components/ui/sheet";
|
} from "@/components/ui/sheet";
|
||||||
import { Logo, NavMenu } from "./navbar";
|
import { Logo, NavMenu } from "@/components/navbar";
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { AlignLeftIcon } from "lucide-react";
|
import { AlignLeftIcon, PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
||||||
import { FooterButtons } from "./footer";
|
import { FooterButtons } from "@/components/footer";
|
||||||
import { DialogTitle } from "./ui/dialog";
|
import { DialogTitle } from "@/components/ui/dialog";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import DocsMenu from "./docs-menu";
|
import DocsMenu from "@/components/docs-menu";
|
||||||
import { ModeToggle } from "./theme-toggle";
|
import { ModeToggle } from "@/components/theme-toggle";
|
||||||
|
|
||||||
export function Leftbar() {
|
export function Leftbar() {
|
||||||
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="lg:flex hidden flex-[1.5] min-w-[238px] sticky top-16 flex-col h-[93.75vh] overflow-y-auto">
|
<aside
|
||||||
<ScrollArea className="py-4">
|
className={`sticky lg:flex hidden top-16 h-[calc(100vh-4rem)] border-r bg-background transition-all duration-300
|
||||||
<DocsMenu />
|
${collapsed ? "w-[48px]" : "w-[250px]"} flex flex-col pr-2`}
|
||||||
|
>
|
||||||
|
{/* Toggle Button */}
|
||||||
|
<div className="absolute top-0 right-0 py-6 px-0 ml-6 z-10 -mt-4">
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant="outline"
|
||||||
|
className="hover:bg-transparent hover:text-inherit border-none text-muted-foreground"
|
||||||
|
onClick={() => setCollapsed((prev) => !prev)}
|
||||||
|
>
|
||||||
|
{collapsed ? (
|
||||||
|
<PanelLeftOpen size={18} />
|
||||||
|
) : (
|
||||||
|
<PanelLeftClose size={18} />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Scrollable DocsMenu */}
|
||||||
|
<ScrollArea className="flex-1 px-2 pb-4">
|
||||||
|
{!collapsed && <DocsMenu />}
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
@@ -36,7 +60,7 @@ export function SheetLeftbar() {
|
|||||||
<DialogTitle className="sr-only">Menu</DialogTitle>
|
<DialogTitle className="sr-only">Menu</DialogTitle>
|
||||||
<SheetHeader>
|
<SheetHeader>
|
||||||
<SheetClose className="px-5" asChild>
|
<SheetClose className="px-5" asChild>
|
||||||
<Logo />
|
<span className="px-2"><Logo /></span>
|
||||||
</SheetClose>
|
</SheetClose>
|
||||||
</SheetHeader>
|
</SheetHeader>
|
||||||
<div className="flex flex-col gap-4 overflow-y-auto">
|
<div className="flex flex-col gap-4 overflow-y-auto">
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export function Navbar() {
|
|||||||
<div className="flex items-center gap-5">
|
<div className="flex items-center gap-5">
|
||||||
<SheetLeftbar />
|
<SheetLeftbar />
|
||||||
<div className="flex items-center gap-6">
|
<div className="flex items-center gap-6">
|
||||||
<div className="hidden sm:flex">
|
<div className="hidden lg:flex">
|
||||||
<Logo />
|
<Logo />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -25,7 +25,7 @@ export function Navbar() {
|
|||||||
<div className="items-center hidden gap-4 text-sm font-medium lg:flex text-muted-foreground">
|
<div className="items-center hidden gap-4 text-sm font-medium lg:flex text-muted-foreground">
|
||||||
<NavMenu />
|
<NavMenu />
|
||||||
</div>
|
</div>
|
||||||
<Separator className="hidden sm:flex my-4 h-9" orientation="vertical" />
|
<Separator className="hidden lg:flex my-4 h-9" orientation="vertical" />
|
||||||
<Search />
|
<Search />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,9 +8,15 @@ import {
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { SheetClose } from "@/components/ui/sheet";
|
import { SheetClose } from "@/components/ui/sheet";
|
||||||
import { ChevronDown, ChevronRight } from "lucide-react";
|
import { ChevronDown, ChevronRight } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState, useMemo } from "react";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
|
|
||||||
|
interface SubLinkProps extends EachRoute {
|
||||||
|
level: number;
|
||||||
|
isSheet: boolean;
|
||||||
|
parentHref?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function SubLink({
|
export default function SubLink({
|
||||||
title,
|
title,
|
||||||
href,
|
href,
|
||||||
@@ -18,19 +24,45 @@ export default function SubLink({
|
|||||||
noLink,
|
noLink,
|
||||||
level,
|
level,
|
||||||
isSheet,
|
isSheet,
|
||||||
}: EachRoute & { level: number; isSheet: boolean }) {
|
parentHref = "",
|
||||||
|
}: SubLinkProps) {
|
||||||
const path = usePathname();
|
const path = usePathname();
|
||||||
const [isOpen, setIsOpen] = useState(level == 0);
|
const [isOpen, setIsOpen] = useState(level === 0);
|
||||||
|
|
||||||
|
// Full path including parent's href
|
||||||
|
const fullHref = `${parentHref}${href}`;
|
||||||
|
|
||||||
|
// Check if current path exactly matches this link's href
|
||||||
|
const isExactActive = useMemo(() => path === fullHref, [path, fullHref]);
|
||||||
|
|
||||||
|
// Check if any child is active (for parent items)
|
||||||
|
const hasActiveChild = useMemo(() => {
|
||||||
|
if (!items) return false;
|
||||||
|
return items.some(item => {
|
||||||
|
const childHref = `${fullHref}${item.href}`;
|
||||||
|
return path.startsWith(childHref) && path !== fullHref;
|
||||||
|
});
|
||||||
|
}, [items, path, fullHref]);
|
||||||
|
|
||||||
|
// Auto-expand if current path is a child of this item
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (path == href || path.includes(href)) setIsOpen(true);
|
if (items && (path.startsWith(fullHref) && path !== fullHref)) {
|
||||||
}, [href, path]);
|
setIsOpen(true);
|
||||||
|
}
|
||||||
|
}, [path, fullHref, items]);
|
||||||
|
|
||||||
const Comp = (
|
// Only apply active styles if it's an exact match and not a parent with active children
|
||||||
<Anchor activeClassName="text-primary font-medium" href={href}>
|
const Comp = useMemo(() => (
|
||||||
|
<Anchor
|
||||||
|
activeClassName={!hasActiveChild ? "text-primary font-medium" : ""}
|
||||||
|
href={fullHref}
|
||||||
|
className={cn(
|
||||||
|
hasActiveChild && "font-medium text-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
{title}
|
{title}
|
||||||
</Anchor>
|
</Anchor>
|
||||||
);
|
), [title, fullHref, hasActiveChild]);
|
||||||
|
|
||||||
const titleOrLink = !noLink ? (
|
const titleOrLink = !noLink ? (
|
||||||
isSheet ? (
|
isSheet ? (
|
||||||
@@ -39,7 +71,12 @@ export default function SubLink({
|
|||||||
Comp
|
Comp
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<h4 className="font-medium sm:text-sm text-primary">{title}</h4>
|
<h4 className={cn(
|
||||||
|
"font-medium sm:text-sm",
|
||||||
|
hasActiveChild ? "text-foreground" : "text-primary"
|
||||||
|
)}>
|
||||||
|
{title}
|
||||||
|
</h4>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!items) {
|
if (!items) {
|
||||||
@@ -47,36 +84,47 @@ export default function SubLink({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-1 w-full">
|
<div className={cn("flex flex-col gap-1 w-full")}>
|
||||||
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
|
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<CollapsibleTrigger className="w-full pr-5">
|
<CollapsibleTrigger
|
||||||
<div className="flex items-center justify-between cursor-pointer w-full">
|
className="w-full pr-5 text-left"
|
||||||
|
aria-expanded={isOpen}
|
||||||
|
aria-controls={`collapsible-${fullHref.replace(/[^a-zA-Z0-9]/g, '-')}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between w-full">
|
||||||
{titleOrLink}
|
{titleOrLink}
|
||||||
<span>
|
<span className="ml-2">
|
||||||
{!isOpen ? (
|
{!isOpen ? (
|
||||||
<ChevronRight className="h-[0.9rem] w-[0.9rem]" />
|
<ChevronRight className="h-[0.9rem] w-[0.9rem]" aria-hidden="true" />
|
||||||
) : (
|
) : (
|
||||||
<ChevronDown className="h-[0.9rem] w-[0.9rem]" />
|
<ChevronDown className="h-[0.9rem] w-[0.9rem]" aria-hidden="true" />
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent>
|
<CollapsibleContent
|
||||||
|
id={`collapsible-${fullHref.replace(/[^a-zA-Z0-9]/g, '-')}`}
|
||||||
|
className={cn(
|
||||||
|
"overflow-hidden transition-all duration-200 ease-in-out",
|
||||||
|
isOpen ? "animate-collapsible-down" : "animate-collapsible-up"
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col items-start sm:text-sm dark:text-stone-300/85 text-stone-800 ml-0.5 mt-2.5 gap-3",
|
"flex flex-col items-start sm:text-sm dark:text-stone-300/85 text-stone-800 ml-0.5 mt-2.5 gap-3",
|
||||||
level > 0 && "pl-4 border-l ml-1.5"
|
level > 0 && "pl-4 border-l ml-1.5"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{items?.map((innerLink) => {
|
{items?.map((innerLink) => (
|
||||||
const modifiedItems = {
|
<SubLink
|
||||||
...innerLink,
|
key={`${fullHref}${innerLink.href}`}
|
||||||
href: `${href + innerLink.href}`,
|
{...innerLink}
|
||||||
level: level + 1,
|
href={innerLink.href}
|
||||||
isSheet,
|
level={level + 1}
|
||||||
};
|
isSheet={isSheet}
|
||||||
return <SubLink key={modifiedItems.href} {...modifiedItems} />;
|
parentHref={fullHref}
|
||||||
})}
|
/>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
|
|||||||
@@ -2,19 +2,25 @@ import { getDocsTocs } from "@/lib/markdown";
|
|||||||
import TocObserver from "./toc-observer";
|
import TocObserver from "./toc-observer";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { ListIcon } from "lucide-react";
|
import { ListIcon } from "lucide-react";
|
||||||
|
import Sponsor from "./Sponsor";
|
||||||
|
|
||||||
|
|
||||||
export default async function Toc({ path }: { path: string }) {
|
export default async function Toc({ path }: { path: string }) {
|
||||||
const tocs = await getDocsTocs(path);
|
const tocs = await getDocsTocs(path);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="lg:flex hidden toc flex-[1.5] min-w-[238px] py-9 sticky top-16 h-[96.95vh]">
|
<div className="lg:flex hidden toc flex-[1.5] min-w-[238px] py-9 sticky top-16 h-[96.95vh]">
|
||||||
<div className="flex flex-col gap-3 w-full pl-2">
|
<div className="flex flex-col gap-6 w-full pl-2 h-full">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex flex-col gap-3">
|
||||||
<ListIcon className="w-5 h-5" /><h3 className="font-medium text-sm">On this page</h3>
|
<div className="flex items-center gap-2">
|
||||||
|
<ListIcon className="w-5 h-5" />
|
||||||
|
<h3 className="font-medium text-sm">On this page</h3>
|
||||||
|
</div>
|
||||||
|
<ScrollArea className="pb-2 pt-0.5 overflow-y-auto">
|
||||||
|
<TocObserver data={tocs} />
|
||||||
|
</ScrollArea>
|
||||||
</div>
|
</div>
|
||||||
<ScrollArea className="pb-2 pt-0.5 overflow-y-auto">
|
<Sponsor />
|
||||||
<TocObserver data={tocs} />
|
|
||||||
</ScrollArea>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -33,8 +33,9 @@
|
|||||||
"favicon": "/favicon.ico"
|
"favicon": "/favicon.ico"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/mywildancloud/docubook",
|
"url": "https://gitlab.com/mywildancloud/docubook",
|
||||||
"editPathTemplate": "/blob/main/{filePath}"
|
"editPathTemplate": "/blob/main/{filePath}",
|
||||||
|
"editLink": false
|
||||||
},
|
},
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|||||||
448
package-lock.json
generated
448
package-lock.json
generated
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "docubook",
|
"name": "docubook",
|
||||||
"version": "1.8.0",
|
"version": "1.8.5",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "docubook",
|
"name": "docubook",
|
||||||
"version": "1.8.0",
|
"version": "1.8.5",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-accordion": "^1.2.0",
|
"@radix-ui/react-accordion": "^1.2.0",
|
||||||
"@radix-ui/react-avatar": "^1.1.0",
|
"@radix-ui/react-avatar": "^1.1.0",
|
||||||
"@radix-ui/react-collapsible": "^1.1.0",
|
"@radix-ui/react-collapsible": "^1.1.0",
|
||||||
"@radix-ui/react-dialog": "^1.1.1",
|
"@radix-ui/react-dialog": "^1.1.6",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||||
"@radix-ui/react-popover": "^1.1.6",
|
"@radix-ui/react-popover": "^1.1.6",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.0",
|
"@radix-ui/react-scroll-area": "^1.2.0",
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
"@radix-ui/react-toggle-group": "^1.1.2",
|
"@radix-ui/react-toggle-group": "^1.1.2",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"cmdk": "1.0.0",
|
||||||
"framer-motion": "^12.4.1",
|
"framer-motion": "^12.4.1",
|
||||||
"geist": "^1.3.1",
|
"geist": "^1.3.1",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
@@ -89,6 +90,15 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.27.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
|
||||||
|
"integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@eslint-community/eslint-utils": {
|
"node_modules/@eslint-community/eslint-utils": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
|
||||||
@@ -2482,6 +2492,438 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cmdk": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-dialog": "1.0.5",
|
||||||
|
"@radix-ui/react-primitive": "1.0.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.0.0",
|
||||||
|
"react-dom": "^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-dialog": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/primitive": "1.0.1",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.1",
|
||||||
|
"@radix-ui/react-context": "1.0.1",
|
||||||
|
"@radix-ui/react-dismissable-layer": "1.0.5",
|
||||||
|
"@radix-ui/react-focus-guards": "1.0.1",
|
||||||
|
"@radix-ui/react-focus-scope": "1.0.4",
|
||||||
|
"@radix-ui/react-id": "1.0.1",
|
||||||
|
"@radix-ui/react-portal": "1.0.4",
|
||||||
|
"@radix-ui/react-presence": "1.0.1",
|
||||||
|
"@radix-ui/react-primitive": "1.0.3",
|
||||||
|
"@radix-ui/react-slot": "1.0.2",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.0.1",
|
||||||
|
"aria-hidden": "^1.1.1",
|
||||||
|
"react-remove-scroll": "2.5.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/primitive": "1.0.1",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.1",
|
||||||
|
"@radix-ui/react-primitive": "1.0.3",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.0.1",
|
||||||
|
"@radix-ui/react-use-escape-keydown": "1.0.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-use-escape-keydown": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-focus-guards": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-focus-scope": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.1",
|
||||||
|
"@radix-ui/react-primitive": "1.0.3",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-id": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-id/node_modules/@radix-ui/react-use-layout-effect": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-portal": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/react-primitive": "1.0.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-presence": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.1",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-presence/node_modules/@radix-ui/react-use-layout-effect": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/react-slot": "1.0.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-use-controllable-state": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/@radix-ui/react-use-controllable-state/node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cmdk/node_modules/react-remove-scroll": {
|
||||||
|
"version": "2.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz",
|
||||||
|
"integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"react-remove-scroll-bar": "^2.3.3",
|
||||||
|
"react-style-singleton": "^2.2.1",
|
||||||
|
"tslib": "^2.1.0",
|
||||||
|
"use-callback-ref": "^1.3.0",
|
||||||
|
"use-sidecar": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/collapse-white-space": {
|
"node_modules/collapse-white-space": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "docubook",
|
"name": "docubook",
|
||||||
"version": "1.8.0",
|
"version": "1.8.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
"@radix-ui/react-accordion": "^1.2.0",
|
"@radix-ui/react-accordion": "^1.2.0",
|
||||||
"@radix-ui/react-avatar": "^1.1.0",
|
"@radix-ui/react-avatar": "^1.1.0",
|
||||||
"@radix-ui/react-collapsible": "^1.1.0",
|
"@radix-ui/react-collapsible": "^1.1.0",
|
||||||
"@radix-ui/react-dialog": "^1.1.1",
|
"@radix-ui/react-dialog": "^1.1.6",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||||
"@radix-ui/react-popover": "^1.1.6",
|
"@radix-ui/react-popover": "^1.1.6",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.0",
|
"@radix-ui/react-scroll-area": "^1.2.0",
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
"@radix-ui/react-toggle-group": "^1.1.2",
|
"@radix-ui/react-toggle-group": "^1.1.2",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "1.0.0",
|
||||||
"framer-motion": "^12.4.1",
|
"framer-motion": "^12.4.1",
|
||||||
"geist": "^1.3.1",
|
"geist": "^1.3.1",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
@@ -55,5 +55,5 @@
|
|||||||
"tailwindcss": "^3.4.10",
|
"tailwindcss": "^3.4.10",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af"
|
"packageManager": "npm@11.3.0"
|
||||||
}
|
}
|
||||||
|
|||||||
6199
pnpm-lock.yaml
generated
6199
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@
|
|||||||
@import url("../styles/syntax.css");
|
@import url("../styles/syntax.css");
|
||||||
@import url("../styles/editor.css");
|
@import url("../styles/editor.css");
|
||||||
|
|
||||||
|
/* ocean */
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
--background: 210 50% 95%;
|
--background: 210 50% 95%;
|
||||||
@@ -82,7 +83,7 @@ pre {
|
|||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre > code {
|
pre>code {
|
||||||
display: grid;
|
display: grid;
|
||||||
max-width: inherit !important;
|
max-width: inherit !important;
|
||||||
padding: 14px 0 !important;
|
padding: 14px 0 !important;
|
||||||
@@ -90,7 +91,7 @@ pre > code {
|
|||||||
|
|
||||||
.code-line {
|
.code-line {
|
||||||
padding: 0.75px 16px;
|
padding: 0.75px 16px;
|
||||||
@apply border-l-2 border-transparent;
|
@apply border-l-2 border-transparent
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-number::before {
|
.line-number::before {
|
||||||
@@ -112,6 +113,42 @@ pre > code {
|
|||||||
@apply px-2 -mb-8 w-full text-sm pb-5 font-medium mt-5 font-code;
|
@apply px-2 -mb-8 w-full text-sm pb-5 font-medium mt-5 font-code;
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlight-comp > code {
|
.highlight-comp>code {
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.line-clamp-3 {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-clamp-2 {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.animate-shine {
|
||||||
|
--animate-shine: shine var(--duration) infinite linear;
|
||||||
|
animation: var(--animate-shine);
|
||||||
|
background-size: 200% 200%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shine {
|
||||||
|
0% {
|
||||||
|
background-position: 0% 0%;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-position: 100% 100%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 0% 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ const config = {
|
|||||||
"./src/**/*.{ts,tsx}",
|
"./src/**/*.{ts,tsx}",
|
||||||
],
|
],
|
||||||
prefix: "",
|
prefix: "",
|
||||||
|
safelist: ["line-clamp-3","line-clam-2"],
|
||||||
theme: {
|
theme: {
|
||||||
container: {
|
container: {
|
||||||
center: true,
|
center: true,
|
||||||
padding: '2rem',
|
padding: '2rem',
|
||||||
screens: {
|
screens: {
|
||||||
'2xl': '1300px'
|
'2xl': '1440px'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
extend: {
|
extend: {
|
||||||
|
|||||||
Reference in New Issue
Block a user