fix release version 1.11.0
This commit is contained in:
373
CHANGELOG.md
373
CHANGELOG.md
@@ -1,373 +0,0 @@
|
||||
## [1.9.0] - 2025-05-19
|
||||
|
||||
> New Keyboard component to show keyboard shortcut on docs page
|
||||
|
||||
### Added
|
||||
|
||||
- New Keyboard component with props show, type, children
|
||||
- Snippet keyboard component
|
||||
|
||||
### Improved
|
||||
|
||||
- Support custom content
|
||||
- Support platform type (mac or window)
|
||||
- Support automatic rendering of platform-specific key symbols
|
||||
- Rename lowercase to camelCase for markdown component
|
||||
|
||||
## [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
|
||||
|
||||
> Now looks more modern and clean which is a big change in layout and design
|
||||
|
||||
### Added
|
||||
|
||||
- Social footer
|
||||
- Toggle group
|
||||
- Site description {meta.description} in footer
|
||||
- Site title {meta.title} in footer
|
||||
|
||||
### Improved
|
||||
|
||||
- Header design changes
|
||||
- Footer design changes
|
||||
- New functions in theme provider
|
||||
- Object changes in docu.json
|
||||
|
||||
### Fixed
|
||||
|
||||
- Updates to path structure components
|
||||
- Groups to organize components
|
||||
|
||||
## [1.7.0] - 2025-02-23
|
||||
|
||||
> Remove the old function in the search dialog and replace it with a new and more optimal feature
|
||||
|
||||
### Added
|
||||
|
||||
- Up and down navigation : search dialog.tsx
|
||||
- Enter (return) to select : search dialog.tsx
|
||||
- Escape to close the dialog : search dialog.tsx
|
||||
|
||||
### Improved
|
||||
|
||||
- Maintenance for anchor components
|
||||
- Anchor.tsx adjustments for all elements that use it
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove suboptimal search features
|
||||
|
||||
## [1.6.0] - 2025-02-21
|
||||
|
||||
> New Feature Card Groups with arrays for more Flexible Content
|
||||
|
||||
### Added
|
||||
|
||||
- Card Groups Components
|
||||
- Props : href to url link
|
||||
- Props : horizontal boolean
|
||||
|
||||
### Improved
|
||||
|
||||
- Card props styling
|
||||
- Compability for Cards components
|
||||
- {children} support for card content
|
||||
|
||||
### Removed
|
||||
|
||||
- remove unused props cards components
|
||||
|
||||
## [1.5.0] - 2025-02-18
|
||||
|
||||
> Minor Update - improved features and responsiveness on all devices
|
||||
|
||||
### Added
|
||||
|
||||
- New dialog footer on searchbox above @media 768px
|
||||
- Icon X for close dialog on searcbox as esc on medium screen
|
||||
|
||||
### Improved
|
||||
|
||||
- Responsive Leftbar components on large screen
|
||||
- Menu Trigger on medium screen
|
||||
- Responsive Navbar components on medium screen
|
||||
- Better UX for searchbox dialog
|
||||
- tooltips components can be written together with regular paragraphs
|
||||
|
||||
### Fixed
|
||||
|
||||
- Responsive issue
|
||||
- Compatibility for Bun
|
||||
- Changes postcss.config.js to .cjs for Bun
|
||||
- all CLI installer and updater not working
|
||||
- adjustments for package managers npm, pnpm, bun, yarn
|
||||
|
||||
## [1.4.2] - 2025-02-16
|
||||
|
||||
> Complex Content for Accordion Component props {children}
|
||||
|
||||
### Added
|
||||
|
||||
- New Props with {children} in accordion
|
||||
- Compatibility for markdown in accordion
|
||||
- Nested components inside an accordion
|
||||
- New icon on note components
|
||||
- add CLI npx @docubook/create@latest
|
||||
- add CLI npx @docubook/update@latest
|
||||
|
||||
### Improved
|
||||
|
||||
- Better UI design for accordion
|
||||
- Styling Note components on markdown
|
||||
- Change accordion output on playground
|
||||
- Change accordion output on snippet
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove depcreated props on accordion
|
||||
- Remove CLI npx update_docu
|
||||
- Remove CLI npx create_docu
|
||||
|
||||
## [1.4.0] - 2025-02-11
|
||||
|
||||
> Floating Button Version with Dynamic Tag {version} on Changelog page
|
||||
|
||||
### Added
|
||||
|
||||
- New components / changelog floating-version.tsx
|
||||
- Button popover to open version-toc below @media 1024px
|
||||
- Dynamic tag by section ID #version
|
||||
- Dynamic url tag #version
|
||||
- Dynamic version indicator on floating version when scrolling section by ID
|
||||
|
||||
### Improved
|
||||
|
||||
- change icon version history
|
||||
- responsive version-toc
|
||||
- improvement components to changelog page
|
||||
|
||||
## [1.3.8] - 2025-02-08
|
||||
|
||||
> Responsive Table of Content
|
||||
|
||||
### Added
|
||||
|
||||
- Components terminal MagicUI
|
||||
- Components card Shadcn
|
||||
- New mob-toc for a better experience on mobile devices
|
||||
- New Components scroll to top button
|
||||
- Scroll to top :blog-post
|
||||
- Scroll to top :docs-post
|
||||
|
||||
### Improved
|
||||
|
||||
- lib/markdown for generated dynamic toc on markdown
|
||||
- Responsive Table of Content below @media 1024px
|
||||
- Improve docs page
|
||||
|
||||
## [1.3.6] - 2025-02-01
|
||||
|
||||
> Appears more modern editor for Docu Play
|
||||
|
||||

|
||||
|
||||
### Added
|
||||
|
||||
- Line Number for editor
|
||||
- editor.css
|
||||
|
||||
### Improved
|
||||
|
||||
- Better Design for Editor
|
||||
- Similar to Github Editor
|
||||
- Moved Handler Element (copy, download, reset and fullscreen) on Header
|
||||
|
||||
## [1.3.5] - 2025-01-30
|
||||
|
||||
> it's Easy to Write Markdown with Playground
|
||||
|
||||

|
||||
|
||||
### Added
|
||||
|
||||
- New Playground Page
|
||||
- New Playground Layout
|
||||
- Toolbar for Markdown Components
|
||||
- Fullscreen Mode to Focus Editing Your Content
|
||||
- Copy to Clipboard Your Content
|
||||
- Download Your Content as index.mdx
|
||||
- Reset Your Content without refresh the Browser
|
||||
- Only Large Screen for Better Experience
|
||||
|
||||
## [1.3.1] - 2025-01-20
|
||||
|
||||
> Snippet Feature to Easily Write Markdown and Call DocuBook Components
|
||||
|
||||

|
||||
|
||||
### Added
|
||||
|
||||
- New Feature Snippet for Markdown Components
|
||||
- Support Snippet for Visual Studio Code
|
||||
|
||||
### Removed
|
||||
|
||||
- remove props icon and props description for accordion components
|
||||
|
||||
## [1.3.0] - 2024-12-31
|
||||
|
||||
> Release Note Feature to Make it Easier to Write Changelogs
|
||||
|
||||
### Added
|
||||
|
||||
- New Release Note Feature
|
||||
- New Layout for Changelog page
|
||||
- New Changelog page
|
||||
- Add Release Note Component
|
||||
- Easily write release notes directly from the CHANGELOG.md file
|
||||
- TOC for versioning
|
||||
- Write with the markdown tag
|
||||
- Add lib / changelog.ts
|
||||
|
||||
### Improved
|
||||
|
||||
- Improvement Responsive feature image for Version Entry
|
||||
- Improvement Layout for changelog page
|
||||
- Improvement Padding on mobile devices
|
||||
- Only use containers of md size
|
||||
- Improvement syntax.css for ul>li classes
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix og:image not showing on Page.tsx
|
||||
- Fix text-indent on class li
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove excessive padding
|
||||
- Remove Logo on Footer
|
||||
|
||||
## [1.2.0] - 2024-12-22
|
||||
|
||||
> New Accordion Component : Support content plain text, html and all markdown component
|
||||
|
||||
### Added
|
||||
|
||||
- add New Accordion
|
||||
|
||||
### Improved
|
||||
|
||||
- Props Improvement
|
||||
- Support Dynamic Content for Accordion
|
||||
|
||||
## [1.1.0] - 2024-12-15
|
||||
|
||||
> Minor Update : Easily manage set up with docu.json
|
||||
|
||||
### Added
|
||||
|
||||
- add docu.json file
|
||||
- add openGraph (title, description, image)
|
||||
- add Dynamic metadata
|
||||
- Generate metadata as openGraph
|
||||
- openGraph support for .mdx
|
||||
|
||||
### Improved
|
||||
|
||||
- routes-config from json
|
||||
- Frontmatter improvement
|
||||
- Edit the content of footer.tsx simply via the docu.json file
|
||||
- Edit the content of navbar.tsx simply via the docu.json file
|
||||
|
||||
## [1.0.7] - 2024-12-14
|
||||
|
||||
> Easily updates your DocuBook Version with CLI npx update_docu
|
||||
|
||||
### Added
|
||||
|
||||
- CLI npx update_docu (update features into docubook existing directory)
|
||||
- Playground (easily to written content)
|
||||
- New Button component
|
||||
- Navbar external link conditions
|
||||
- CLI npx create_docu
|
||||
|
||||
### Improved
|
||||
|
||||
- Searchbar Improvement
|
||||
- Navigation Improvement
|
||||
- Edit on Github Improvement
|
||||
|
||||
### Removed
|
||||
- Remove CLI npx create-docu (on this version not usage dash `-`)
|
||||
|
||||
## [1.0.6] - 2024-11-24
|
||||
|
||||
> New Components, Fix and Improvement
|
||||
|
||||
### Added
|
||||
|
||||
- New Card component
|
||||
- New Tooltips component
|
||||
|
||||
### Fixed
|
||||
|
||||
- change root folder
|
||||
|
||||
### Improved
|
||||
|
||||
- logo on navbar & footer
|
||||
- easily change logo
|
||||
|
||||
## [1.0.5] - 2024-11-16
|
||||
|
||||
> Add New Features and Improvement for this version
|
||||
|
||||
### Added
|
||||
|
||||
- New Youtube component
|
||||
- edit this page - easily manage directory content via the github repo
|
||||
- support installation via cli commant npx create-docu
|
||||
|
||||
### Improved
|
||||
|
||||
- keyboard shortcut command + k or ctrl + k to open search dialog
|
||||
|
||||
## [1.0.0] - 2024-11-10
|
||||
|
||||
> Initial release of DocuBook to create interactive nested docs with MDX
|
||||
|
||||
### Added
|
||||
|
||||
- Initial release of DocuBook
|
||||
- Basic documentation structure
|
||||
- Markdown support with MDX
|
||||
- Responsive design
|
||||
- Search functionality
|
||||
- Dark mode support
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
**DocuBook** is a documentation web project designed to provide a simple and user-friendly interface for accessing various types of documentation. This site is crafted for developers and teams who need quick access to references, guides, and essential documents.
|
||||
|
||||
> **Note**: This application is a fork of [AriaDocs](https://github.com/nisabmohd/Aria-Docs), created by [Nisab Mohd](https://github.com/nisabmohd). DocuBook provides an alternative to the documentation solution found on [Mintlify](https://mintlify.com/), utilizing `.mdx` (Markdown + JSX) for content creation and management.
|
||||
|
||||
[](https://vercel.com/import/project?template=https://github.com/gitfromwildan/docubook)
|
||||
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
import { Typography } from "@/components/typography";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { Author, getAllBlogStaticPaths, getBlogForSlug } from "@/lib/markdown";
|
||||
import { ArrowLeftIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { formatDate } from "@/lib/utils";
|
||||
import { ScrollToTop } from "@/components/scroll-to-top";
|
||||
|
||||
type PageProps = {
|
||||
params: { slug: string };
|
||||
};
|
||||
|
||||
export async function generateMetadata({ params: { slug } }: PageProps) {
|
||||
const res = await getBlogForSlug(slug);
|
||||
if (!res) return null;
|
||||
const { frontmatter } = res;
|
||||
return {
|
||||
title: frontmatter.title,
|
||||
description: frontmatter.description,
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const val = await getAllBlogStaticPaths();
|
||||
if (!val) return [];
|
||||
return val.map((it) => ({ slug: it }));
|
||||
}
|
||||
|
||||
export default async function BlogPage({ params: { slug } }: PageProps) {
|
||||
const res = await getBlogForSlug(slug);
|
||||
if (!res) notFound();
|
||||
return (
|
||||
<div className="lg:w-[60%] sm:[95%] md:[75%] mx-auto">
|
||||
<Link
|
||||
className={buttonVariants({
|
||||
variant: "link",
|
||||
className: "!mx-0 !px-0 mb-7 !-ml-1 ",
|
||||
})}
|
||||
href="/blog"
|
||||
>
|
||||
<ArrowLeftIcon className="w-4 h-4 mr-1.5" /> Back to blog
|
||||
</Link>
|
||||
<div className="flex flex-col gap-3 pb-7 w-full mb-2">
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{formatDate(res.frontmatter.date)}
|
||||
</p>
|
||||
<h1 className="sm:text-4xl text-3xl font-extrabold">
|
||||
{res.frontmatter.title}
|
||||
</h1>
|
||||
<div className="mt-6 flex flex-col gap-3">
|
||||
<p className="text-sm text-muted-foreground">Posted by</p>
|
||||
<Authors authors={res.frontmatter.authors} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="!w-full">
|
||||
<Typography>{res.content}</Typography>
|
||||
</div>
|
||||
<ScrollToTop />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Authors({ authors }: { authors: Author[] }) {
|
||||
return (
|
||||
<div className="flex items-center gap-8 flex-wrap">
|
||||
{authors.map((author) => {
|
||||
return (
|
||||
<Link
|
||||
href={author.handleUrl}
|
||||
className="flex items-center gap-2"
|
||||
key={author.username}
|
||||
>
|
||||
<Avatar className="w-10 h-10">
|
||||
<AvatarImage src={author.avatar} />
|
||||
<AvatarFallback>
|
||||
{author.username.slice(0, 2).toUpperCase()}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="">
|
||||
<p className="text-sm font-medium">{author.username}</p>
|
||||
<p className="font-code text-[13px] text-muted-foreground">
|
||||
@{author.handle}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { PropsWithChildren } from "react";
|
||||
|
||||
export default function BlogLayout({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<div className="flex flex-col items-start justify-center pt-8 pb-10 w-full mx-auto">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Author, BlogMdxFrontmatter, getAllBlogs } from "@/lib/markdown";
|
||||
import { formatDate2, stringToDate } from "@/lib/utils";
|
||||
import { getMetadata } from "@/app/layout";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { AuroraText } from "@/components/ui/aurora";
|
||||
import { ShineBorder } from "@/components/ui/shine-border";
|
||||
import docuConfig from "@/docu.json";
|
||||
|
||||
export const metadata = getMetadata({
|
||||
title: "Blog",
|
||||
description: "Discover the latest updates, tutorials, and insights on DocuBook.",
|
||||
});
|
||||
const { meta } = docuConfig;
|
||||
export default async function BlogIndexPage() {
|
||||
const blogs = (await getAllBlogs()).sort(
|
||||
(a, b) => stringToDate(b.date).getTime() - stringToDate(a.date).getTime()
|
||||
);
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center px-2 py-8 text-center sm:py-36">
|
||||
<div className="w-full max-w-[800px] pb-8">
|
||||
<AuroraText className="text-lg"># Stay Informed, Stay Ahead</AuroraText>
|
||||
<h1 className="mb-4 text-2xl font-bold sm:text-5xl">
|
||||
Blog Posts
|
||||
</h1>
|
||||
<p className="mb-8 sm:text-xl text-muted-foreground">
|
||||
Explore updates, tips, and deep dives from the {meta.title}.
|
||||
</p>
|
||||
</div>
|
||||
<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) => (
|
||||
<BlogCard {...blog} slug={blog.slug} key={blog.slug} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BlogCard({
|
||||
date,
|
||||
title,
|
||||
description,
|
||||
slug,
|
||||
cover,
|
||||
authors,
|
||||
}: BlogMdxFrontmatter & { slug: string }) {
|
||||
return (
|
||||
<Link
|
||||
href={`/blog/${slug}`}
|
||||
className="flex flex-col gap-2 items-start border rounded-md max-h-[420px] min-h-[420px]"
|
||||
>
|
||||
<div className="w-full">
|
||||
<Image
|
||||
src={cover}
|
||||
alt={title}
|
||||
width={400}
|
||||
height={150}
|
||||
quality={80}
|
||||
className="w-full rounded-md object-cover h-[200px]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col items-start px-3 py-3 gap-2 mb-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">
|
||||
Published on {formatDate2(date)}
|
||||
</p>
|
||||
<AvatarGroup users={authors} />
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function AvatarGroup({ users, max = 4 }: { users: Author[]; max?: number }) {
|
||||
const displayUsers = users.slice(0, max);
|
||||
const remainingUsers = Math.max(users.length - max, 0);
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
{displayUsers.map((user, index) => (
|
||||
<Avatar
|
||||
key={user.username}
|
||||
className={`inline-block border-2 w-9 h-9 border-background ${
|
||||
index !== 0 ? "-ml-3" : ""
|
||||
} `}
|
||||
>
|
||||
<AvatarImage src={user.avatar} alt={user.username} />
|
||||
<AvatarFallback>
|
||||
{user.username.slice(0, 2).toUpperCase()}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
))}
|
||||
{remainingUsers > 0 && (
|
||||
<Avatar className="-ml-3 inline-block border-2 border-background hover:translate-y-1 transition-transform">
|
||||
<AvatarFallback>+{remainingUsers}</AvatarFallback>
|
||||
</Avatar>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export default function ChangelogLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-start gap-8">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Suspense } from "react";
|
||||
import { getChangelogEntries } from "@/lib/changelog";
|
||||
import { VersionEntry } from "@/components/changelog/version-entry";
|
||||
import { VersionToc } from "@/components/changelog/version-toc";
|
||||
import { FloatingVersionToc } from "@/components/changelog/floating-version";
|
||||
|
||||
export default async function ChangelogPage() {
|
||||
const entries = await getChangelogEntries();
|
||||
|
||||
return (
|
||||
<div className="flex items-start">
|
||||
<Suspense fallback={<div className="lg:flex hidden flex-[1.5]" />}>
|
||||
<VersionToc
|
||||
versions={entries.map(({ version, date }) => ({ version, date }))}
|
||||
/>
|
||||
</Suspense>
|
||||
|
||||
<main className="flex-1 md:flex-[5.25] min-w-0 max-w-[800px]">
|
||||
<div className="relative">
|
||||
<div className="absolute left-0 top-0 h-full w-px bg-border md:block hidden" />
|
||||
<div className="md:px-12 md:py-8 max-md:py-10">
|
||||
{entries.map((entry, index) => (
|
||||
<section
|
||||
id={`version-${entry.version}`}
|
||||
key={entry.version}
|
||||
className="scroll-mt-20" // Tambahkan margin atas saat scroll
|
||||
>
|
||||
<VersionEntry {...entry} isLast={index === entries.length - 1} />
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{/* Floating TOC for smaller screens */}
|
||||
{entries.length > 0 && (
|
||||
<FloatingVersionToc
|
||||
versions={entries.map(({ version, date }) => ({ version, date }))}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import EditThisPage from "@/components/edit-on-github";
|
||||
import { formatDate2 } from "@/lib/utils";
|
||||
import docuConfig from "@/docu.json";
|
||||
import MobToc from "@/components/mob-toc";
|
||||
import { ScrollToTop } from "@/components/scroll-to-top";
|
||||
|
||||
const { meta } = docuConfig;
|
||||
|
||||
@@ -78,11 +77,9 @@ export default async function DocsPage({ params: { slug = [] } }: PageProps) {
|
||||
|
||||
return (
|
||||
<div className="flex items-start gap-10">
|
||||
<div className="flex-[4.5] pt-10">
|
||||
<DocsBreadcrumb paths={slug} />
|
||||
<div className="mb-8">
|
||||
<div className="flex-[4.5] pt-4 lg:pt-10">
|
||||
<MobToc tocs={tocs} />
|
||||
</div>
|
||||
<DocsBreadcrumb paths={slug} />
|
||||
<Typography>
|
||||
<h1 className="text-3xl !-mt-0.5">{title}</h1>
|
||||
<p className="-mt-4 text-muted-foreground text-[16.5px]">{description}</p>
|
||||
@@ -101,7 +98,6 @@ export default async function DocsPage({ params: { slug = [] } }: PageProps) {
|
||||
</div>
|
||||
<Pagination pathname={pathName} />
|
||||
</Typography>
|
||||
<ScrollToTop />
|
||||
</div>
|
||||
<Toc path={pathName} />
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Leftbar } from "@/components/leftbar";
|
||||
import { MDXProviderWrapper } from "@/components/markdown/mdx-provider";
|
||||
|
||||
export default function DocsLayout({
|
||||
children,
|
||||
@@ -9,10 +8,8 @@ export default function DocsLayout({
|
||||
return (
|
||||
<div className="flex items-start gap-8">
|
||||
<Leftbar key="leftbar" />
|
||||
<div className="flex-[5.25]">
|
||||
<MDXProviderWrapper>
|
||||
<div className="flex-[5.25] p-4">
|
||||
{children}
|
||||
</MDXProviderWrapper>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function Home() {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center px-2 py-8 text-center sm:py-36">
|
||||
<Link
|
||||
href="/changelog"
|
||||
href="/docs/getting-started/changelog"
|
||||
className="mb-5 sm:text-lg flex items-center gap-2 underline underline-offset-4 sm:-mt-12"
|
||||
>
|
||||
<div className="z-10 flex min-h-10 items-center justify-center max-[800px]:mt-10">
|
||||
@@ -25,7 +25,7 @@ export default function Home() {
|
||||
)}
|
||||
>
|
||||
<AnimatedShinyText className="inline-flex items-center justify-center px-4 py-1 transition ease-out hover:text-neutral-100 hover:duration-300 hover:dark:text-neutral-200">
|
||||
<span>🚀 New Version - Release v1.9.0</span>
|
||||
<span>🚀 New Version - Release v1.11.0</span>
|
||||
<ArrowRightIcon className="ml-1 size-3 transition-transform duration-300 ease-in-out group-hover:translate-x-0.5" />
|
||||
</AnimatedShinyText>
|
||||
</div>
|
||||
|
||||
44
components/GithubStart.tsx
Normal file
44
components/GithubStart.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
|
||||
const GitHubStarButton: React.FC = () => {
|
||||
const [stars, setStars] = useState<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('https://api.github.com/repos/gitfromwildan/docubook')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data.stargazers_count !== undefined) {
|
||||
setStars(data.stargazers_count);
|
||||
}
|
||||
})
|
||||
.catch((error) => console.error('Failed to fetch stars:', error));
|
||||
}, []);
|
||||
|
||||
const formatStars = (count: number) =>
|
||||
count >= 1000 ? `${(count / 1000).toFixed(1)}K` : `${count}`;
|
||||
|
||||
return (
|
||||
<Link
|
||||
href="https://github.com/gitfromwildan/docubook"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center rounded-full px-3 py-1.5 text-sm font-medium text-muted-foreground border no-underline"
|
||||
>
|
||||
<svg
|
||||
height="16"
|
||||
width="16"
|
||||
viewBox="0 0 16 16"
|
||||
aria-hidden="true"
|
||||
className="fill-current mr-1.5"
|
||||
>
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8a8 8 0 005.47 7.59c.4.07.55-.17.55-.38v-1.32c-2.22.48-2.69-1.07-2.69-1.07-.36-.92-.89-1.17-.89-1.17-.73-.5.06-.49.06-.49.81.06 1.23.83 1.23.83.72 1.23 1.89.88 2.35.67.07-.52.28-.88.5-1.08-1.77-.2-3.64-.88-3.64-3.93 0-.87.31-1.58.82-2.14-.08-.2-.36-1.01.08-2.12 0 0 .67-.21 2.2.82a7.7 7.7 0 012.01-.27 7.7 7.7 0 012.01.27c1.53-1.03 2.2-.82 2.2-.82.44 1.11.16 1.92.08 2.12.51.56.82 1.27.82 2.14 0 3.06-1.87 3.73-3.65 3.93.29.25.54.73.54 1.48v2.2c0 .21.15.46.55.38A8 8 0 0016 8c0-4.42-3.58-8-8-8z" />
|
||||
</svg>
|
||||
{stars !== null ? formatStars(stars) : '...'}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default GitHubStarButton;
|
||||
@@ -1,45 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
type ChangeType = "Added" | "Improved" | "Fixed" | "Deprecated" | "Removed";
|
||||
|
||||
interface ChangeGroupProps {
|
||||
type: ChangeType;
|
||||
changes: string[];
|
||||
expanded: boolean;
|
||||
}
|
||||
|
||||
const typeColors: Record<ChangeType, string> = {
|
||||
Added: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
|
||||
Improved: "bg-blue-500/10 text-blue-600 dark:text-blue-400",
|
||||
Fixed: "bg-amber-500/10 text-amber-600 dark:text-amber-400",
|
||||
Deprecated: "bg-red-500/10 text-red-600 dark:text-red-400",
|
||||
Removed: "bg-slate-500/10 text-slate-600 dark:text-slate-400"
|
||||
};
|
||||
|
||||
export function ChangeGroup({ type, changes, expanded }: ChangeGroupProps) {
|
||||
const visibleChanges = expanded ? changes : changes.slice(0, 5);
|
||||
const hasMore = changes.length > 5;
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<Badge variant="outline" className={cn("font-medium", typeColors[type])}>
|
||||
{type}
|
||||
</Badge>
|
||||
<ul className="list-disc list-inside space-y-2 text-muted-foreground pl-2">
|
||||
{visibleChanges.map((change, i) => (
|
||||
<li key={i} id="changelog" className="text-sm leading-relaxed marker:text-muted-foreground/60">
|
||||
{change}
|
||||
</li>
|
||||
))}
|
||||
{!expanded && hasMore && (
|
||||
<li id="changelog-more" className="text-sm text-muted-foreground/60">
|
||||
+{changes.length - 5} more improvements
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { History } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
interface FloatingVersionTocProps {
|
||||
versions: { version: string; date: string }[];
|
||||
}
|
||||
|
||||
export function FloatingVersionToc({ versions }: FloatingVersionTocProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [activeVersion, setActiveVersion] = useState(versions[0]?.version || "");
|
||||
|
||||
useEffect(() => {
|
||||
const handleIntersection = (entries: IntersectionObserverEntry[]) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
setActiveVersion(entry.target.id.replace("version-", ""));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver(handleIntersection, {
|
||||
root: null,
|
||||
rootMargin: "-64px 0px -50% 0px",
|
||||
threshold: 0.25,
|
||||
});
|
||||
|
||||
versions.forEach(({ version }) => {
|
||||
const section = document.getElementById(`version-${version}`);
|
||||
if (section) observer.observe(section);
|
||||
});
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [versions]);
|
||||
|
||||
const handleScrollToVersion = (version: string) => {
|
||||
const element = document.getElementById(`version-${version}`);
|
||||
if (element) {
|
||||
setTimeout(() => {
|
||||
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}, 100);
|
||||
setActiveVersion(version);
|
||||
setOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-4 right-4 md:hidden z-50">
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline" className="rounded-full shadow-lg px-4 py-2 flex items-center gap-2">
|
||||
<History className="w-5 h-5" />
|
||||
Version - {activeVersion}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-56 p-2 bg-background shadow-md rounded-lg">
|
||||
<ScrollArea className="h-72">
|
||||
<h2 className="px-4 py-2 font-semibold">Version History</h2>
|
||||
<ul className="space-y-1">
|
||||
{versions.map(({ version }) => (
|
||||
<li key={version}>
|
||||
<Separator />
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={cn("w-full justify-start text-sm", {
|
||||
"text-primary font-bold": activeVersion === version,
|
||||
})}
|
||||
onClick={() => handleScrollToVersion(version)}
|
||||
>
|
||||
v.{version}
|
||||
</Button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</ScrollArea>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { VersionTag } from "./version-tag";
|
||||
import { ChangeGroup } from "./change-group";
|
||||
import { formatDate2 } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Image from "next/image";
|
||||
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
interface VersionEntryProps {
|
||||
version: string;
|
||||
date: string;
|
||||
description?: string;
|
||||
image?: string;
|
||||
changes: {
|
||||
Added?: string[];
|
||||
Improved?: string[];
|
||||
Fixed?: string[];
|
||||
Deprecated?: string[];
|
||||
Removed?: string[];
|
||||
};
|
||||
isLast?: boolean;
|
||||
}
|
||||
|
||||
export function VersionEntry({
|
||||
version,
|
||||
date,
|
||||
description,
|
||||
image,
|
||||
changes,
|
||||
isLast
|
||||
}: VersionEntryProps) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
return (
|
||||
<div id={`v${version}`} className="relative scroll-mt-24">
|
||||
<div className="relative pb-12">
|
||||
{/* Version header */}
|
||||
<div className="flex flex-col gap-4 mb-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<VersionTag version={version} />
|
||||
<time className="text-sm text-muted-foreground">
|
||||
{formatDate2(date)}
|
||||
</time>
|
||||
</div>
|
||||
|
||||
{description && (
|
||||
<p className="text-dark text-xl">{description}</p>
|
||||
)}
|
||||
|
||||
{image && (
|
||||
<div className="relative w-full h-0 pb-[56.25%] rounded-lg overflow-hidden border">
|
||||
<Image
|
||||
src={image}
|
||||
alt={`Version ${version} preview`}
|
||||
fill
|
||||
className="object-cover"
|
||||
priority
|
||||
quality={90}
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Changes */}
|
||||
<div className="space-y-6">
|
||||
{Object.entries(changes).map(([type, items]) => (
|
||||
items && items.length > 0 && (
|
||||
<ChangeGroup
|
||||
key={type}
|
||||
type={type as keyof typeof changes}
|
||||
changes={items}
|
||||
expanded={expanded}
|
||||
/>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Show more/less button */}
|
||||
{Object.values(changes).some(items => items && items.length > 5) && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
className="mt-4 text-muted-foreground hover:bg-transparent hover:text-accent border-none"
|
||||
>
|
||||
{expanded ? (
|
||||
<>
|
||||
Show less
|
||||
<ChevronUpIcon className="ml-2 h-4 w-4" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Show more
|
||||
<ChevronDownIcon className="ml-2 h-4 w-4" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Version divider */}
|
||||
{!isLast && (
|
||||
<div className="absolute left-0 bottom-0 w-full">
|
||||
<Separator className="my-8" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function VersionTag({ version }: { version: string }) {
|
||||
return (
|
||||
<span className={cn(
|
||||
"inline-flex items-center rounded-full px-2.5 py-0.5 text-sm font-medium",
|
||||
"bg-primary/10 text-primary"
|
||||
)}>
|
||||
v{version}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { cn, formatDate2 } from "@/lib/utils";
|
||||
import { History, PanelLeftOpen, PanelLeftClose } from "lucide-react";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
interface VersionTocProps {
|
||||
versions: Array<{
|
||||
version: string;
|
||||
date: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export function VersionToc({ versions }: VersionTocProps) {
|
||||
const [activeId, setActiveId] = useState<string | null>(null);
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const hash = window.location.hash.slice(1);
|
||||
if (hash) {
|
||||
setActiveId(hash);
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
const id = entry.target.id;
|
||||
setActiveId(id);
|
||||
window.history.pushState(null, "", `#${id}`);
|
||||
}
|
||||
});
|
||||
},
|
||||
{
|
||||
threshold: 0.2,
|
||||
rootMargin: "-20% 0px -60% 0px",
|
||||
}
|
||||
);
|
||||
|
||||
versions.forEach(({ version }) => {
|
||||
const element = document.getElementById(`v${version}`);
|
||||
if (element) observer.observe(element);
|
||||
});
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [versions]);
|
||||
|
||||
return (
|
||||
<aside
|
||||
className={cn(
|
||||
"sticky top-16 h-[calc(100vh-4rem)] border-r bg-background transition-all duration-300 z-20 hidden md:flex",
|
||||
collapsed ? "w-[48px]" : "w-[250px]"
|
||||
)}
|
||||
>
|
||||
{/* Toggle Button */}
|
||||
<div className="absolute top-0 right-0 py-2 px-0 ml-6 z-30">
|
||||
<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>
|
||||
|
||||
{/* 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>
|
||||
);
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import { Fragment } from "react";
|
||||
|
||||
export default function DocsBreadcrumb({ paths }: { paths: string[] }) {
|
||||
return (
|
||||
<div className="pb-5">
|
||||
<div className="pb-5 max-lg:pt-12">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
|
||||
@@ -11,26 +11,26 @@ import { Logo, NavMenu } from "@/components/navbar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { AlignLeftIcon, PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
||||
import { FooterButtons } from "@/components/footer";
|
||||
import { DialogTitle } from "@/components/ui/dialog";
|
||||
import { DialogTitle, DialogDescription } from "@/components/ui/dialog";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import DocsMenu from "@/components/docs-menu";
|
||||
import { ModeToggle } from "@/components/theme-toggle";
|
||||
|
||||
export function Leftbar() {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
|
||||
// Toggle Button Component
|
||||
export function ToggleButton({
|
||||
collapsed,
|
||||
onToggle
|
||||
}: {
|
||||
collapsed: boolean,
|
||||
onToggle: () => void
|
||||
}) {
|
||||
return (
|
||||
<aside
|
||||
className={`sticky lg:flex hidden top-16 h-[calc(100vh-4rem)] border-r bg-background transition-all duration-300
|
||||
${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">
|
||||
<div className="absolute top-0 right-0 py-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)}
|
||||
onClick={onToggle}
|
||||
>
|
||||
{collapsed ? (
|
||||
<PanelLeftOpen size={18} />
|
||||
@@ -39,9 +39,21 @@ export function Leftbar() {
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Leftbar() {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const toggleCollapse = () => setCollapsed(prev => !prev);
|
||||
|
||||
return (
|
||||
<aside
|
||||
className={`sticky lg:flex hidden top-16 h-[calc(100vh-4rem)] border-r bg-background transition-all duration-300
|
||||
${collapsed ? "w-[24px]" : "w-[280px]"} flex flex-col pr-2`}
|
||||
>
|
||||
<ToggleButton collapsed={collapsed} onToggle={toggleCollapse} />
|
||||
{/* Scrollable DocsMenu */}
|
||||
<ScrollArea className="flex-1 px-2 pb-4">
|
||||
<ScrollArea className="flex-1 px-0.5 pb-4">
|
||||
{!collapsed && <DocsMenu />}
|
||||
</ScrollArea>
|
||||
</aside>
|
||||
@@ -57,7 +69,10 @@ export function SheetLeftbar() {
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent className="flex flex-col gap-4 px-0" side="left">
|
||||
<DialogTitle className="sr-only">Menu</DialogTitle>
|
||||
<DialogTitle className="sr-only">Navigation Menu</DialogTitle>
|
||||
<DialogDescription className="sr-only">
|
||||
Main navigation menu with links to different sections
|
||||
</DialogDescription>
|
||||
<SheetHeader>
|
||||
<SheetClose className="px-5" asChild>
|
||||
<span className="px-2"><Logo /></span>
|
||||
|
||||
107
components/markdown/ReleaseMdx.tsx
Normal file
107
components/markdown/ReleaseMdx.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { PlusCircle, Wrench, Zap, AlertTriangle, XCircle } from 'lucide-react';
|
||||
|
||||
interface ReleaseProps extends PropsWithChildren {
|
||||
version: string;
|
||||
title: string;
|
||||
date?: string;
|
||||
}
|
||||
|
||||
function Release({ version, title, date, children }: ReleaseProps) {
|
||||
|
||||
return (
|
||||
<div className="mb-16 group">
|
||||
<div className="mb-6">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="bg-primary/10 text-primary border-2 border-primary/20 rounded-full px-4 py-1.5 text-base font-medium">
|
||||
v{version}
|
||||
</div>
|
||||
{date && (
|
||||
<div className="text-muted-foreground text-sm">
|
||||
{new Date(date).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-foreground/90 mb-3">
|
||||
{title}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="space-y-8">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ChangesProps extends PropsWithChildren {
|
||||
type: 'added' | 'fixed' | 'improved' | 'deprecated' | 'removed';
|
||||
}
|
||||
|
||||
const typeConfig = {
|
||||
added: {
|
||||
label: 'Added',
|
||||
className: 'bg-green-100 dark:bg-green-900/50 text-green-700 dark:text-green-300',
|
||||
icon: PlusCircle,
|
||||
},
|
||||
fixed: {
|
||||
label: 'Fixed',
|
||||
className: 'bg-yellow-100 dark:bg-yellow-900/50 text-yellow-700 dark:text-yellow-300',
|
||||
icon: Wrench,
|
||||
},
|
||||
improved: {
|
||||
label: 'Improved',
|
||||
className: 'bg-cyan-100 dark:bg-cyan-900/50 text-cyan-700 dark:text-cyan-300',
|
||||
icon: Zap,
|
||||
},
|
||||
deprecated: {
|
||||
label: 'Deprecated',
|
||||
className: 'bg-orange-100 dark:bg-orange-900/50 text-orange-700 dark:text-orange-300',
|
||||
icon: AlertTriangle,
|
||||
},
|
||||
removed: {
|
||||
label: 'Removed',
|
||||
className: 'bg-pink-100 dark:bg-pink-900/50 text-pink-700 dark:text-pink-300',
|
||||
icon: XCircle,
|
||||
},
|
||||
} as const;
|
||||
|
||||
function Changes({ type, children }: ChangesProps) {
|
||||
const config = typeConfig[type] || typeConfig.added;
|
||||
|
||||
return (
|
||||
<div className="space-y-3 mb-8">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={cn("px-3 py-1 rounded-full text-sm font-medium flex items-center gap-1.5", config.className)}>
|
||||
<config.icon className="h-3.5 w-3.5" />
|
||||
<span>{config.label}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul className="list-none pl-0 space-y-2 text-foreground/80">
|
||||
{React.Children.map(children, (child, index) => {
|
||||
// Jika teks dimulai dengan - atau *, hapus karakter tersebut
|
||||
const processedChild = typeof child === 'string'
|
||||
? child.trim().replace(/^[-*]\s+/, '')
|
||||
: child;
|
||||
|
||||
return (
|
||||
<li key={index} className="leading-relaxed">
|
||||
{processedChild}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { Release, Changes };
|
||||
|
||||
export default {
|
||||
Release,
|
||||
Changes
|
||||
};
|
||||
@@ -1,17 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { MDXProvider } from '@mdx-js/react';
|
||||
import { components } from './mdx-components';
|
||||
import { MDXRemote, MDXRemoteProps } from 'next-mdx-remote/rsc';
|
||||
import { Kbd } from './KeyboardMdx';
|
||||
|
||||
// Create a properly typed components object
|
||||
const typedComponents = {
|
||||
...components,
|
||||
// Add any default HTML elements you want to override
|
||||
// or keep their default behavior
|
||||
kbd: components.kbd as React.ComponentType<React.HTMLAttributes<HTMLElement>>,
|
||||
Kbd: components.Kbd as React.ComponentType<React.HTMLAttributes<HTMLElement> & { type?: 'window' | 'mac' }>,
|
||||
// Define components mapping
|
||||
const components = {
|
||||
// Keyboard components
|
||||
Kbd: Kbd as React.ComponentType<React.HTMLAttributes<HTMLElement> & { type?: 'window' | 'mac' }>,
|
||||
kbd: Kbd as React.ComponentType<React.HTMLAttributes<HTMLElement> & { type?: 'window' | 'mac' }>,
|
||||
};
|
||||
|
||||
export function MDXProviderWrapper({ children }: { children: React.ReactNode }) {
|
||||
return <MDXProvider components={typedComponents}>{children}</MDXProvider>;
|
||||
interface MDXProviderWrapperProps {
|
||||
source: string;
|
||||
}
|
||||
|
||||
export function MDXProviderWrapper({ source }: MDXProviderWrapperProps) {
|
||||
return (
|
||||
<div className="prose dark:prose-invert max-w-none">
|
||||
<MDXRemote
|
||||
source={source}
|
||||
components={components}
|
||||
options={{
|
||||
parseFrontmatter: true,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,38 +1,128 @@
|
||||
"use client";
|
||||
|
||||
import { ListIcon } from "lucide-react";
|
||||
import { List, ChevronDown, ChevronUp } from "lucide-react";
|
||||
import TocObserver from "./toc-observer";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import * as React from "react";
|
||||
import { useRef, useMemo } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { Button } from "./ui/button";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { useScrollPosition, useActiveSection } from "@/hooks";
|
||||
import { TocItem } from "@/lib/toc";
|
||||
|
||||
interface MobTocProps {
|
||||
tocs: {
|
||||
level: number;
|
||||
text: string;
|
||||
href: string;
|
||||
}[];
|
||||
tocs: TocItem[];
|
||||
}
|
||||
|
||||
const useClickOutside = (ref: React.RefObject<HTMLElement>, callback: () => void) => {
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
if (ref.current && !ref.current.contains(e.target as Node)) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
document.addEventListener('mousedown', handleClick);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClick);
|
||||
};
|
||||
}, [ref, callback]);
|
||||
};
|
||||
|
||||
export default function MobToc({ tocs }: MobTocProps) {
|
||||
const pathname = usePathname();
|
||||
const [isExpanded, setIsExpanded] = React.useState(false);
|
||||
const tocRef = useRef<HTMLDivElement>(null);
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Use custom hooks
|
||||
const { activeId, setActiveId } = useActiveSection(tocs);
|
||||
|
||||
// Only show on /docs pages
|
||||
const isDocsPage = useMemo(() => pathname?.startsWith('/docs'), [pathname]);
|
||||
|
||||
// Toggle expanded state
|
||||
const toggleExpanded = React.useCallback((e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
setIsExpanded(prev => !prev);
|
||||
}, []);
|
||||
|
||||
// Close TOC when clicking outside
|
||||
useClickOutside(tocRef, () => {
|
||||
if (isExpanded) {
|
||||
setIsExpanded(false);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle body overflow when TOC is expanded
|
||||
React.useEffect(() => {
|
||||
if (isExpanded) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.body.style.overflow = '';
|
||||
};
|
||||
}, [isExpanded]);
|
||||
|
||||
// Don't render anything if not on docs page or no TOC items
|
||||
if (!isDocsPage || !tocs?.length) return null;
|
||||
|
||||
const chevronIcon = isExpanded ? (
|
||||
<ChevronUp className="w-4 h-4 text-muted-foreground flex-shrink-0" />
|
||||
) : (
|
||||
<ChevronDown className="w-4 h-4 text-muted-foreground flex-shrink-0" />
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="lg:hidden block w-full">
|
||||
<Accordion type="single" collapsible>
|
||||
<AccordionItem value="toc">
|
||||
<AccordionTrigger className="hover:no-underline">
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
ref={tocRef}
|
||||
className="lg:hidden fixed top-16 left-0 right-0 z-50"
|
||||
initial={{ y: -100, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
exit={{ y: -100, opacity: 0 }}
|
||||
transition={{ duration: 0.2, ease: 'easeInOut' }}
|
||||
>
|
||||
<div className="w-full bg-background/95 backdrop-blur-sm border-b border-stone-200 dark:border-stone-800 shadow-sm">
|
||||
<div className="md:px-8 px-4 py-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-full justify-between h-auto py-2 px-2 -mx-1 rounded-md hover:bg-transparent hover:text-inherit"
|
||||
onClick={toggleExpanded}
|
||||
aria-label={isExpanded ? 'Collapse table of contents' : 'Expand table of contents'}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<ListIcon className="w-4 h-4" />
|
||||
<List className="w-4 h-4 text-muted-foreground flex-shrink-0" />
|
||||
<span className="font-medium text-sm">On this page</span>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="h-auto py-2">
|
||||
<TocObserver data={tocs} />
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
{chevronIcon}
|
||||
</Button>
|
||||
|
||||
<AnimatePresence>
|
||||
{isExpanded && (
|
||||
<motion.div
|
||||
ref={contentRef}
|
||||
className="mt-2 pb-2 max-h-[60vh] overflow-y-auto px-1 -mx-1"
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
transition={{ duration: 0.2, ease: 'easeInOut' }}
|
||||
>
|
||||
<TocObserver
|
||||
data={tocs}
|
||||
activeId={activeId}
|
||||
onActiveIdChange={setActiveId}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,15 @@ export function Logo() {
|
||||
|
||||
return (
|
||||
<Link href="/" className="flex items-center gap-1.5">
|
||||
<Image src={navbar.logo.src} alt={navbar.logo.alt} width="24" height="24" />
|
||||
<div className="relative w-8 h-8">
|
||||
<Image
|
||||
src={navbar.logo.src}
|
||||
alt={navbar.logo.alt}
|
||||
fill
|
||||
sizes="32px"
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
<h2 className="font-bold font-code text-md">{navbar.logoText}</h2>
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -1,52 +1,86 @@
|
||||
"use client";
|
||||
|
||||
import { ArrowUpIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import Link from "next/link";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function ScrollToTop() {
|
||||
const [show, setShow] = useState(false);
|
||||
interface ScrollToTopProps {
|
||||
className?: string;
|
||||
showIcon?: boolean;
|
||||
offset?: number; // Optional offset in pixels from the trigger point
|
||||
}
|
||||
|
||||
export function ScrollToTop({
|
||||
className,
|
||||
showIcon = true,
|
||||
offset = 0
|
||||
}: ScrollToTopProps) {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
const checkScroll = useCallback(() => {
|
||||
// Calculate 50% of viewport height
|
||||
const halfViewportHeight = window.innerHeight * 0.5;
|
||||
// Check if scrolled past half viewport height (plus any offset)
|
||||
const scrolledPastHalfViewport = window.scrollY > (halfViewportHeight + offset);
|
||||
|
||||
// Only update state if it changes to prevent unnecessary re-renders
|
||||
if (scrolledPastHalfViewport !== isVisible) {
|
||||
setIsVisible(scrolledPastHalfViewport);
|
||||
}
|
||||
}, [isVisible, offset]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
// Check if user has scrolled to bottom
|
||||
const scrolledToBottom =
|
||||
window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 100;
|
||||
// Initial check
|
||||
checkScroll();
|
||||
|
||||
if (scrolledToBottom) {
|
||||
setShow(true);
|
||||
} else {
|
||||
setShow(false);
|
||||
}
|
||||
// Set up scroll listener with debounce for better performance
|
||||
let timeoutId: NodeJS.Timeout;
|
||||
const handleScroll = () => {
|
||||
if (timeoutId) clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(checkScroll, 100);
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
if (timeoutId) clearTimeout(timeoutId);
|
||||
};
|
||||
}, [checkScroll]);
|
||||
|
||||
const scrollToTop = useCallback((e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}, []);
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
};
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"lg:hidden fixed top-16 items-center z-50 w-full transition-all duration-300",
|
||||
show ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-full pointer-events-none"
|
||||
"mt-4 pt-4 border-t border-stone-200 dark:border-stone-800",
|
||||
"transition-opacity duration-300",
|
||||
isVisible ? 'opacity-100' : 'opacity-0',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="flex justify-center items-center pt-3 mx-auto">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2 rounded-full shadow-md bg-background/80 backdrop-blur-sm border-primary/20 hover:bg-background hover:text-primary"
|
||||
<Link
|
||||
href="#"
|
||||
onClick={scrollToTop}
|
||||
className={cn(
|
||||
"inline-flex items-center text-sm text-muted-foreground hover:text-foreground",
|
||||
"transition-all duration-200 hover:translate-y-[-1px]"
|
||||
)}
|
||||
aria-label="Scroll to top"
|
||||
>
|
||||
<ArrowUpIcon className="h-4 w-4" />
|
||||
<span className="font-medium">Scroll to Top</span>
|
||||
</Button>
|
||||
</div>
|
||||
{showIcon && <ArrowUpIcon className="mr-1 h-3.5 w-3.5 flex-shrink-0" />}
|
||||
<span>Scroll to Top</span>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
DialogTrigger,
|
||||
DialogClose,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
} from "@/components/ui/dialog";
|
||||
import Anchor from "./anchor";
|
||||
import { advanceSearch, cn } from "@/lib/utils";
|
||||
@@ -110,16 +111,20 @@ export default function Search() {
|
||||
</div>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="p-0 max-w-[650px] sm:top-[38%] top-[45%] !rounded-md">
|
||||
<DialogTitle className="sr-only">Search</DialogTitle>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="sr-only">Search Documentation</DialogTitle>
|
||||
</DialogHeader>
|
||||
<DialogDescription className="sr-only">
|
||||
Search through the documentation
|
||||
</DialogDescription>
|
||||
<input
|
||||
value={searchedInput}
|
||||
onChange={(e) => setSearchedInput(e.target.value)}
|
||||
placeholder="Type something to search..."
|
||||
autoFocus
|
||||
className="h-14 px-6 bg-transparent border-b text-[14px] outline-none"
|
||||
className="h-14 px-6 bg-transparent border-b text-[14px] outline-none w-full"
|
||||
aria-label="Search documentation"
|
||||
/>
|
||||
</DialogHeader>
|
||||
{filteredResults.length == 0 && searchedInput && (
|
||||
<p className="text-muted-foreground mx-auto mt-2 text-sm">
|
||||
No results found for{" "}
|
||||
@@ -149,11 +154,20 @@ export default function Search() {
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center w-fit h-full py-3 gap-1.5 px-2",
|
||||
"flex items-center w-full h-full py-3 gap-1.5 px-2 justify-between",
|
||||
level > 1 && "border-l pl-4"
|
||||
)}
|
||||
>
|
||||
<FileTextIcon className="h-[1.1rem] w-[1.1rem] mr-1" /> {item.title}
|
||||
<div className="flex items-center">
|
||||
<FileTextIcon className="h-[1.1rem] w-[1.1rem] mr-1" />
|
||||
<span>{item.title}</span>
|
||||
</div>
|
||||
{isActive && (
|
||||
<div className="hidden md:flex items-center text-xs text-muted-foreground">
|
||||
<span>Return</span>
|
||||
<CornerDownLeftIcon className="h-3 w-3 ml-1" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Anchor>
|
||||
</DialogClose>
|
||||
|
||||
@@ -3,26 +3,62 @@
|
||||
import { getDocsTocs } from "@/lib/markdown";
|
||||
import clsx from "clsx";
|
||||
import Link from "next/link";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { useState, useRef, useEffect, useCallback } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { ScrollToTop } from "./scroll-to-top";
|
||||
import { TocItem } from "@/lib/toc";
|
||||
|
||||
type Props = { data: Awaited<ReturnType<typeof getDocsTocs>> };
|
||||
interface TocObserverProps {
|
||||
data: TocItem[];
|
||||
activeId?: string | null;
|
||||
onActiveIdChange?: (id: string | null) => void;
|
||||
}
|
||||
|
||||
export default function TocObserver({ data }: Props) {
|
||||
const [activeId, setActiveId] = useState<string | null>(null);
|
||||
export default function TocObserver({
|
||||
data,
|
||||
activeId: externalActiveId,
|
||||
onActiveIdChange
|
||||
}: TocObserverProps) {
|
||||
const [internalActiveId, setInternalActiveId] = useState<string | null>(null);
|
||||
const observer = useRef<IntersectionObserver | null>(null);
|
||||
const [clickedId, setClickedId] = useState<string | null>(null);
|
||||
const itemRefs = useRef<Map<string, HTMLAnchorElement>>(new Map());
|
||||
|
||||
// Use external activeId if provided, otherwise use internal state
|
||||
const activeId = externalActiveId !== undefined ? externalActiveId : internalActiveId;
|
||||
const setActiveId = onActiveIdChange || setInternalActiveId;
|
||||
|
||||
// Handle intersection observer for auto-highlighting
|
||||
useEffect(() => {
|
||||
const handleIntersect = (entries: IntersectionObserverEntry[]) => {
|
||||
const visibleEntry = entries.find((entry) => entry.isIntersecting);
|
||||
if (visibleEntry) {
|
||||
setActiveId(visibleEntry.target.id);
|
||||
const visibleEntries = entries.filter(entry => entry.isIntersecting);
|
||||
|
||||
// Find the most recently scrolled-into-view element
|
||||
const mostVisibleEntry = visibleEntries.reduce((prev, current) => {
|
||||
// Prefer the entry that's more visible or higher on the page
|
||||
const prevRatio = prev?.intersectionRatio || 0;
|
||||
const currentRatio = current.intersectionRatio;
|
||||
|
||||
if (currentRatio > prevRatio) return current;
|
||||
if (currentRatio === prevRatio &&
|
||||
current.boundingClientRect.top < prev.boundingClientRect.top) {
|
||||
return current;
|
||||
}
|
||||
return prev;
|
||||
}, visibleEntries[0]);
|
||||
|
||||
if (mostVisibleEntry && !clickedId) {
|
||||
const newActiveId = mostVisibleEntry.target.id;
|
||||
if (newActiveId !== activeId) {
|
||||
setActiveId(newActiveId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
observer.current = new IntersectionObserver(handleIntersect, {
|
||||
root: null,
|
||||
rootMargin: "-20px 0px 0px 0px",
|
||||
threshold: 0.1,
|
||||
rootMargin: "-20% 0px -70% 0px", // Adjusted margins for better section detection
|
||||
threshold: [0, 0.1, 0.5, 0.9, 1], // Multiple thresholds for better accuracy
|
||||
});
|
||||
|
||||
const elements = data.map((item) =>
|
||||
@@ -35,6 +71,11 @@ export default function TocObserver({ data }: Props) {
|
||||
}
|
||||
});
|
||||
|
||||
// Set initial active ID if none is set
|
||||
if (!activeId && elements[0]) {
|
||||
setActiveId(elements[0].id);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (observer.current) {
|
||||
elements.forEach((el) => {
|
||||
@@ -44,26 +85,180 @@ export default function TocObserver({ data }: Props) {
|
||||
});
|
||||
}
|
||||
};
|
||||
}, [data]);
|
||||
}, [data, clickedId]);
|
||||
|
||||
const handleLinkClick = useCallback((id: string) => {
|
||||
setClickedId(id);
|
||||
setActiveId(id);
|
||||
|
||||
// Reset the clicked state after a delay to allow for smooth scrolling
|
||||
const timer = setTimeout(() => {
|
||||
setClickedId(null);
|
||||
}, 1000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [setActiveId]);
|
||||
|
||||
// Function to check if an item has children
|
||||
const hasChildren = (currentId: string, currentLevel: number) => {
|
||||
const currentIndex = data.findIndex(item => item.href.slice(1) === currentId);
|
||||
if (currentIndex === -1 || currentIndex === data.length - 1) return false;
|
||||
|
||||
const nextItem = data[currentIndex + 1];
|
||||
return nextItem.level > currentLevel;
|
||||
};
|
||||
|
||||
// Calculate scroll progress for the active section
|
||||
const [scrollProgress, setScrollProgress] = useState(0);
|
||||
const [activeSectionIndex, setActiveSectionIndex] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
if (!activeId) return;
|
||||
|
||||
const activeElement = document.getElementById(activeId);
|
||||
if (!activeElement) return;
|
||||
|
||||
const rect = activeElement.getBoundingClientRect();
|
||||
const windowHeight = window.innerHeight;
|
||||
const elementTop = rect.top;
|
||||
const elementHeight = rect.height;
|
||||
|
||||
// Calculate how much of the element is visible
|
||||
let progress = 0;
|
||||
if (elementTop < windowHeight) {
|
||||
progress = Math.min(1, (windowHeight - elementTop) / (windowHeight + elementHeight));
|
||||
}
|
||||
|
||||
setScrollProgress(progress);
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, [activeId]);
|
||||
|
||||
// Update active section index when activeId changes
|
||||
useEffect(() => {
|
||||
if (activeId) {
|
||||
const index = data.findIndex(item => item.href.slice(1) === activeId);
|
||||
if (index !== -1) {
|
||||
setActiveSectionIndex(index);
|
||||
}
|
||||
}
|
||||
}, [activeId, data]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2.5 text-sm dark:text-stone-300/85 text-stone-800 ml-0.5">
|
||||
{data.map(({ href, level, text }) => {
|
||||
<div className="relative">
|
||||
<div className="relative text-sm text-stone-600 dark:text-stone-400">
|
||||
<div className="flex flex-col gap-0">
|
||||
{data.map(({ href, level, text }, index) => {
|
||||
const id = href.slice(1);
|
||||
const isActive = activeId === id;
|
||||
const indent = level > 1 ? (level - 1) * 20 : 0;
|
||||
const isParent = hasChildren(id, level);
|
||||
const isLastInLevel = index === data.length - 1 || data[index + 1].level <= level;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={clsx({
|
||||
"pl-0": level == 2,
|
||||
"pl-4": level == 3,
|
||||
"pl-8 ": level == 4,
|
||||
"font-medium text-primary": activeId == href.slice(1),
|
||||
<div key={href} className="relative">
|
||||
{/* Simple L-shaped connector */}
|
||||
{level > 1 && (
|
||||
<div
|
||||
className={clsx("absolute top-0 h-full w-6", {
|
||||
"left-[6px]": indent === 20, // Level 2
|
||||
"left-[22px]": indent === 40, // Level 3
|
||||
"left-[38px]": indent === 60, // Level 4
|
||||
})}
|
||||
>
|
||||
{/* Vertical line */}
|
||||
<div className={clsx(
|
||||
"absolute left-0 top-0 h-full w-px",
|
||||
isActive ? "bg-primary/20" : "bg-stone-300 dark:bg-stone-600"
|
||||
)}>
|
||||
{isActive && (
|
||||
<motion.div
|
||||
className="absolute left-0 top-0 w-full h-full bg-primary origin-top"
|
||||
initial={{ scaleY: 0 }}
|
||||
animate={{ scaleY: scrollProgress }}
|
||||
transition={{ duration: 0.3 }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Horizontal line */}
|
||||
<div className={clsx(
|
||||
"absolute left-0 top-1/2 h-px w-6",
|
||||
isActive ? "bg-primary/20" : "bg-stone-300 dark:bg-stone-600"
|
||||
)}>
|
||||
{isActive && (
|
||||
<motion.div
|
||||
className="absolute left-0 top-0 h-full w-full bg-primary origin-left"
|
||||
initial={{ scaleX: 0 }}
|
||||
animate={{ scaleX: scrollProgress }}
|
||||
transition={{ duration: 0.3, delay: 0.1 }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Link
|
||||
href={href}
|
||||
onClick={() => handleLinkClick(id)}
|
||||
className={clsx(
|
||||
"relative flex items-center py-2 transition-colors",
|
||||
{
|
||||
"text-primary dark:text-primary-400 font-medium": isActive,
|
||||
"text-stone-600 dark:text-stone-400 hover:text-stone-900 dark:hover:text-stone-200": !isActive,
|
||||
}
|
||||
)}
|
||||
style={{
|
||||
paddingLeft: `${indent}px`,
|
||||
marginLeft: level > 1 ? '12px' : '0',
|
||||
}}
|
||||
ref={(el) => {
|
||||
const map = itemRefs.current;
|
||||
if (el) {
|
||||
map.set(id, el);
|
||||
} else {
|
||||
map.delete(id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* Circle indicator */}
|
||||
<div className="relative w-4 h-4 flex items-center justify-center flex-shrink-0">
|
||||
<div className={clsx(
|
||||
"w-1.5 h-1.5 rounded-full transition-all duration-300 relative z-10",
|
||||
{
|
||||
"bg-primary scale-100": isActive,
|
||||
"bg-stone-300 dark:bg-stone-600 scale-75 group-hover:scale-100 group-hover:bg-primary/50": !isActive,
|
||||
}
|
||||
)}>
|
||||
{isActive && (
|
||||
<motion.div
|
||||
className="absolute inset-0 rounded-full bg-primary/20"
|
||||
initial={{ scale: 1 }}
|
||||
animate={{ scale: 1.8 }}
|
||||
transition={{
|
||||
duration: 2,
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse"
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span className="truncate text-sm">
|
||||
{text}
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
{/* Add scroll to top link at the bottom of TOC */}
|
||||
<ScrollToTop className="mt-6" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ export default async function Toc({ path }: { path: string }) {
|
||||
const tocs = await getDocsTocs(path);
|
||||
|
||||
return (
|
||||
<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-6 w-full pl-2 h-full">
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="lg:flex hidden toc flex-[1.5] min-w-[238px] py-9 sticky top-16 h-[calc(100vh-4rem)]">
|
||||
<div className="flex flex-col h-full w-full px-2 gap-2 mb-auto">
|
||||
<div className="flex items-center gap-2">
|
||||
<ListIcon className="w-5 h-5" />
|
||||
<ListIcon className="w-4 h-4" />
|
||||
<h3 className="font-medium text-sm">On this page</h3>
|
||||
</div>
|
||||
<ScrollArea className="pb-2 pt-0.5 overflow-y-auto">
|
||||
<div className="flex-shrink-0 min-h-0 max-h-[calc(70vh-4rem)]">
|
||||
<ScrollArea className="h-full">
|
||||
<TocObserver data={tocs} />
|
||||
</ScrollArea>
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,7 @@ export const InteractiveHoverButton = React.forwardRef<
|
||||
</div>
|
||||
<div className="absolute top-0 z-10 flex h-full w-full translate-x-12 items-center justify-center gap-2 text-primary-foreground opacity-0 transition-all duration-300 group-hover:-translate-x-5 group-hover:opacity-100">
|
||||
<span>{children}</span>
|
||||
<ArrowRight />
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
|
||||
512
contents/docs/getting-started/changelog/index.mdx
Normal file
512
contents/docs/getting-started/changelog/index.mdx
Normal file
@@ -0,0 +1,512 @@
|
||||
---
|
||||
title: Changelog
|
||||
description: List of latest changes and updates on DocuBook
|
||||
date: 24-05-2025
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
> This changelog contains a list of all the changes made to the DocuBook template. It will be updated with each new release and will include information about new features, bug fixes, and other improvements.
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.11.0
|
||||
</div>
|
||||
|
||||
<Release version="1.11.0" date="2025-05-25" title="New Release Note components support multiple products or multiple changelogs">
|
||||
<Changes type="added">
|
||||
- New ReleaseNote component for structured changelog display
|
||||
- Added support for categorized changes (added, fixed, improved, deprecated, removed)
|
||||
- Integrated Lucide icons for better visual hierarchy
|
||||
- Support for multiple release notes
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Enhanced documentation with comprehensive usage examples
|
||||
- Better component organization and styling
|
||||
- Semantic versioning support
|
||||
- Nested release notes support
|
||||
</Changes>
|
||||
|
||||
<Changes type="removed">
|
||||
- Removed old changelog page in favor of the new ReleaseMdx component
|
||||
- Removed changelog.md
|
||||
- Removed changelog/page.tsx
|
||||
- Removed changelog.ts
|
||||
- Removed components/changelog
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.10.1
|
||||
</div>
|
||||
|
||||
<Release version="1.10.1" date="2025-05-24" title="Accessibility Improvements and Bug Fixes">
|
||||
<Changes type="fixed">
|
||||
- Added missing DialogDescription components for better accessibility
|
||||
- Fixed image aspect ratio issues in navbar logo
|
||||
- Resolved console warnings for missing image sizes
|
||||
- Improved keyboard navigation in search component
|
||||
- Fixed mobile layout for search result items
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Added proper ARIA labels for screen readers
|
||||
- Enhanced focus management in dialogs
|
||||
- Optimized image loading with proper sizing attributes
|
||||
- Better mobile experience with responsive design fixes
|
||||
</Changes>
|
||||
|
||||
<Changes type="removed">
|
||||
- Remove blog page
|
||||
- Remove blog functions on markdown
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.10.0
|
||||
</div>
|
||||
|
||||
<Release version="1.10.0" date="2025-05-21" title="Sidebar Improvements and Mobile TOC Enhancements">
|
||||
<Changes type="added">
|
||||
- New reusable ToggleButton component with animation
|
||||
- Mobile-friendly Table of Contents (TOC) component
|
||||
- Click-outside handler for better mobile navigation
|
||||
- Smooth scroll behavior for TOC navigation
|
||||
- Active section highlighting in TOC
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Sidebar now has a collapsible design
|
||||
- Enhanced mobile responsiveness for TOC
|
||||
- Better visual hierarchy in sidebar navigation
|
||||
- Smoother animations for sidebar toggle
|
||||
- Optimized TOC performance with intersection observer
|
||||
- Improved accessibility with proper ARIA labels
|
||||
- Better spacing and alignment in mobile view
|
||||
</Changes>
|
||||
|
||||
<Changes type="fixed">
|
||||
- Fixed sidebar toggle button positioning
|
||||
- Resolved TOC highlighting issues during scroll
|
||||
- Fixed z-index conflicts in mobile view
|
||||
- Addressed minor UI glitches in dark mode
|
||||
- Fixed TOC not updating on route changes
|
||||
- Resolved scroll jank on mobile devices
|
||||
- Fixed incorrect active state in navigation
|
||||
</Changes>
|
||||
|
||||
<Changes type="deprecated">
|
||||
- No longer support changelog.md
|
||||
- No longer support changelog/page.tsx (will be removed in future update)
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.9.0
|
||||
</div>
|
||||
|
||||
<Release version="1.9.0" date="2025-05-19" title="New Keyboard component to show keyboard shortcut on docs page">
|
||||
<Changes type="added">
|
||||
- New Keyboard component with props show, type, children
|
||||
- Snippet keyboard component
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Support custom content
|
||||
- Support platform type (mac or window)
|
||||
- Support automatic rendering of platform-specific key symbols
|
||||
- Rename lowercase to camelCase for markdown component
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.8.5
|
||||
</div>
|
||||
|
||||
<Release version="1.8.5" date="2025-05-10" title="Add sponsor card on single docs page">
|
||||
<Changes type="added">
|
||||
- Expandables Leftbar
|
||||
- Sponsor badges or ads
|
||||
- Boolean show/hide 'edit on github'
|
||||
- With the same code run anywhere (bun or nodejs)
|
||||
- Add frontmatter (metadata) to playground editor
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Adjustment docu.json
|
||||
- Adjustment navbar, footer and components
|
||||
</Changes>
|
||||
|
||||
<Changes type="fixed">
|
||||
- Bun compatibility: rename .js to common js
|
||||
- CLI manage packageManager on package.json
|
||||
- Inconsistent design moved to better UI/UX
|
||||
- Error handle render footer.social
|
||||
</Changes>
|
||||
|
||||
<Changes type="removed">
|
||||
- Remove confusing and verbose CLI on installer
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.8.0
|
||||
</div>
|
||||
|
||||
<Release version="1.8.0" date="2025-03-01" title="Now looks more modern and clean which is a big change in layout and design">
|
||||
<Changes type="added">
|
||||
- Social footer
|
||||
- Toggle group
|
||||
- Site description in footer
|
||||
- Site title in footer
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Header design changes
|
||||
- Footer design changes
|
||||
- New functions in theme provider
|
||||
- Object changes in docu.json
|
||||
</Changes>
|
||||
|
||||
<Changes type="fixed">
|
||||
- Updates to path structure components
|
||||
- Groups to organize components
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.7.0
|
||||
</div>
|
||||
|
||||
<Release version="1.7.0" date="2025-02-23" title="Remove the old function in the search dialog and replace it with a new and more optimal feature">
|
||||
<Changes type="added">
|
||||
- Up and down navigation in search dialog
|
||||
- Enter (return) to select in search dialog
|
||||
- Escape to close the dialog
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Maintenance for anchor components
|
||||
- Anchor.tsx adjustments for all elements that use it
|
||||
</Changes>
|
||||
|
||||
<Changes type="removed">
|
||||
- Remove suboptimal search features
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.6.0
|
||||
</div>
|
||||
|
||||
<Release version="1.6.0" date="2025-02-21" title="New Feature Card Groups with arrays for more Flexible Content">
|
||||
<Changes type="added">
|
||||
- Card Groups Components
|
||||
- Props: href to url link
|
||||
- Props: horizontal boolean
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Card props styling
|
||||
- Compatibility for Cards components
|
||||
- Support for children props in card content
|
||||
</Changes>
|
||||
|
||||
<Changes type="removed">
|
||||
- Remove unused props cards components
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.5.0
|
||||
</div>
|
||||
|
||||
<Release version="1.5.0" date="2025-02-18" title="Minor Update - improved features and responsiveness on all devices">
|
||||
<Changes type="added">
|
||||
- New dialog footer on searchbox above medium screens
|
||||
- Icon X for close dialog on searchbox (ESC key on medium screen)
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Responsive Leftbar components on large screens
|
||||
- Menu Trigger on medium screens
|
||||
- Responsive Navbar components on medium screens
|
||||
- Better UX for searchbox dialog
|
||||
- Tooltips components can be written together with regular paragraphs
|
||||
</Changes>
|
||||
|
||||
<Changes type="fixed">
|
||||
- Responsive issues
|
||||
- Compatibility for Bun
|
||||
- Changes postcss.config.js to .cjs for Bun
|
||||
- All CLI installer and updater not working
|
||||
- Adjustments for package managers (npm, pnpm, bun, yarn)
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.4.2
|
||||
</div>
|
||||
|
||||
<Release version="1.4.2" date="2025-02-16" title="Complex Content for Accordion Component props children">
|
||||
<Changes type="added">
|
||||
- New Props with children in accordion
|
||||
- Compatibility for markdown in accordion
|
||||
- Nested components inside an accordion
|
||||
- New icon on note components
|
||||
- Add CLI npx @docubook/create@latest
|
||||
- Add CLI npx @docubook/update@latest
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Better UI design for accordion
|
||||
- Styling Note components on markdown
|
||||
- Change accordion output on playground
|
||||
- Change accordion output on snippet
|
||||
</Changes>
|
||||
|
||||
<Changes type="removed">
|
||||
- Remove deprecated props on accordion
|
||||
- Remove CLI npx update_docu
|
||||
- Remove CLI npx create_docu
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.4.0
|
||||
</div>
|
||||
|
||||
<Release version="1.4.0" date="2025-02-11" title="Floating Button Version with Dynamic Tag version on Changelog page">
|
||||
<Changes type="added">
|
||||
- New components / changelog floating-version.tsx
|
||||
- Button popover to open version-toc below large screens
|
||||
- Dynamic tag by section ID #version
|
||||
- Dynamic url tag #version
|
||||
- Dynamic version indicator on floating version when scrolling section by ID
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Change icon version history
|
||||
- Responsive version-toc
|
||||
- Improvement components to changelog page
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.3.8
|
||||
</div>
|
||||
|
||||
<Release version="1.3.8" date="2025-02-08" title="Responsive Table of Content">
|
||||
<Changes type="added">
|
||||
- Components terminal MagicUI
|
||||
- Components card Shadcn
|
||||
- New mob-toc for a better experience on mobile devices
|
||||
- New Components scroll to top button
|
||||
- Scroll to top: blog-post
|
||||
- Scroll to top: docs-post
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- lib/markdown for generated dynamic toc on markdown
|
||||
- Responsive Table of Content below large screens
|
||||
- Improve docs page
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.3.6
|
||||
</div>
|
||||
|
||||
<Release version="1.3.6" date="2025-02-01" title="Appears more modern editor for Docu Play">
|
||||
|
||||
<Changes type="added">
|
||||
- Line Number for editor
|
||||
- editor.css
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Better Design for Editor
|
||||
- Similar to Github Editor
|
||||
- Moved Handler Element (copy, download, reset and fullscreen) on Header
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.3.5
|
||||
</div>
|
||||
|
||||
<Release version="1.3.5" date="2025-01-30" title="It's Easy to Write Markdown with Playground">
|
||||
|
||||
<Changes type="added">
|
||||
- New Playground Page
|
||||
- New Playground Layout
|
||||
- Toolbar for Markdown Components
|
||||
- Fullscreen Mode to Focus Editing Your Content
|
||||
- Copy to Clipboard Your Content
|
||||
- Download Your Content as index.mdx
|
||||
- Reset Your Content without refresh the Browser
|
||||
- Only Large Screen for Better Experience
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.3.1
|
||||
</div>
|
||||
|
||||
<Release version="1.3.1" date="2025-01-20" title="Snippet Feature to Easily Write Markdown and Call DocuBook Components">
|
||||
|
||||
<Changes type="added">
|
||||
- New Feature Snippet for Markdown Components
|
||||
- Support Snippet for Visual Studio Code
|
||||
</Changes>
|
||||
|
||||
<Changes type="removed">
|
||||
- Remove props icon and props description for accordion components
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.3.0
|
||||
</div>
|
||||
|
||||
<Release version="1.3.0" date="2024-12-31" title="Release Note Feature to Make it Easier to Write Changelogs">
|
||||
<Changes type="added">
|
||||
- New Release Note Feature
|
||||
- New Layout for Changelog page
|
||||
- New Changelog page
|
||||
- Add Release Note Component
|
||||
- Easily write release notes directly from the CHANGELOG.md file
|
||||
- TOC for versioning
|
||||
- Write with the markdown tag
|
||||
- Add lib / changelog.ts
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Improvement Responsive feature image for Version Entry
|
||||
- Improvement Layout for changelog page
|
||||
- Improvement Padding on mobile devices
|
||||
- Only use containers of md size
|
||||
- Improvement syntax.css for ul>li classes
|
||||
</Changes>
|
||||
|
||||
<Changes type="fixed">
|
||||
- Fix og:image not showing on Page.tsx
|
||||
- Fix text-indent on class li
|
||||
</Changes>
|
||||
|
||||
<Changes type="removed">
|
||||
- Remove excessive padding
|
||||
- Remove Logo on Footer
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.2.0
|
||||
</div>
|
||||
|
||||
<Release version="1.2.0" date="2024-12-22" title="New Accordion Component: Support content plain text, html and all markdown component">
|
||||
<Changes type="added">
|
||||
- Add New Accordion Component
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Props Improvement
|
||||
- Support Dynamic Content for Accordion
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.1.0
|
||||
</div>
|
||||
|
||||
<Release version="1.1.0" date="2024-12-15" title="Minor Update: Easily manage set up with docu.json">
|
||||
<Changes type="added">
|
||||
- Add docu.json file
|
||||
- Add openGraph (title, description, image)
|
||||
- Add Dynamic metadata
|
||||
- Generate metadata as openGraph
|
||||
- OpenGraph support for .mdx
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Routes-config from json
|
||||
- Frontmatter improvement
|
||||
- Edit the content of footer.tsx simply via the docu.json file
|
||||
- Edit the content of navbar.tsx simply via the docu.json file
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.0.7
|
||||
</div>
|
||||
|
||||
<Release version="1.0.7" date="2024-12-14" title="Easily updates your DocuBook Version with CLI npx update_docu">
|
||||
<Changes type="added">
|
||||
- CLI npx update_docu (update features into docubook existing directory)
|
||||
- Playground (easily to written content)
|
||||
- New Button component
|
||||
- Navbar external link conditions
|
||||
- CLI npx create_docu
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Searchbar Improvement
|
||||
- Navigation Improvement
|
||||
- Edit on Github Improvement
|
||||
</Changes>
|
||||
|
||||
<Changes type="removed">
|
||||
- Remove CLI npx create-docu (on this version not usage dash `-`)
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.0.6
|
||||
</div>
|
||||
|
||||
<Release version="1.0.6" date="2024-11-24" title="New Components, Fix and Improvement">
|
||||
<Changes type="added">
|
||||
- New Card component
|
||||
- New Tooltips component
|
||||
</Changes>
|
||||
|
||||
<Changes type="fixed">
|
||||
- Change root folder
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Logo on navbar & footer
|
||||
- Easily change logo
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.0.5
|
||||
</div>
|
||||
|
||||
<Release version="1.0.5" date="2024-11-16" title="Add New Features and Improvement for this version">
|
||||
<Changes type="added">
|
||||
- New Youtube component
|
||||
- Edit this page - easily manage directory content via the github repo
|
||||
- Support installation via CLI command npx create-docu
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Keyboard shortcut command + k or ctrl + k to open search dialog
|
||||
</Changes>
|
||||
</Release>
|
||||
|
||||
<div className="sr-only">
|
||||
### v 1.0.0
|
||||
</div>
|
||||
|
||||
<Release version="1.0.0" date="2024-11-10" title="Initial release of DocuBook to create interactive nested docs with MDX">
|
||||
<Changes type="added">
|
||||
- Initial release of DocuBook
|
||||
- Basic documentation structure
|
||||
- Markdown support with MDX
|
||||
- Responsive design
|
||||
- Search functionality
|
||||
- Dark mode support
|
||||
</Changes>
|
||||
</Release>
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Accordion
|
||||
description: This section previews the Accordion component.
|
||||
description: A component used to create collapsible content that can be hidden and shown again.
|
||||
date: 22-12-2024
|
||||
---
|
||||
|
||||
I have implemented the `accordion` component into markdown which is ready to use.
|
||||
The Accordion component is a collapsible content container that can be used to hide and show content again. It is a flexible component that can be used to display any content, including HTML elements, markdown components, and plain text.
|
||||
|
||||
## Preview
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Button
|
||||
description: This section previews the Button component.
|
||||
description: A component used to create buttons that can be used to trigger actions or navigate to other pages.
|
||||
date: 14-12-2024
|
||||
---
|
||||
|
||||
I have implemented the `button` component into markdown which is ready to use.
|
||||
The Button component is a component used to create buttons that can be used to trigger actions or navigate to other pages.
|
||||
|
||||
## Preview
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Card Group
|
||||
description: This section previews the Card Group component.
|
||||
description: A component used to create card groups that can be used to display multiple cards in a compact and organized way.
|
||||
date: 20-02-2025
|
||||
---
|
||||
|
||||
I have implemented the `card group` component into markdown which is ready to use.
|
||||
The CardGroup component is a component used to create card groups that can be used to display multiple cards in a compact and organized way.
|
||||
|
||||
## Preview
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Cards
|
||||
description: This section previews the Cards component.
|
||||
description: A component used to create cards that can be used to display content in a compact and organized way.
|
||||
date: 20-02-2025
|
||||
---
|
||||
|
||||
I have implemented the `cards` component into markdown which is ready to use.
|
||||
The Card component is a component used to create cards that can be used to display content in a compact and organized way.
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Code Block
|
||||
description: This section previews the Code Block features in markdown.
|
||||
description: A component used to display code snippets with optional line numbering and line highlighting.
|
||||
date: 14-12-2024
|
||||
---
|
||||
|
||||
The Code Block in this documentation allows you to display code snippets with optional line numbering and line highlighting.
|
||||
The Code Block component is a component used to display code snippets with optional line numbering and line highlighting.
|
||||
|
||||
## Preview
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Image
|
||||
description: This section provides an overview of how DocuBook handles images in Markdown.
|
||||
description: A component used to display images in Markdown.
|
||||
date: 14-12-2024
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Link
|
||||
description: This section provides an overview of how DocuBook handles links in Markdown.
|
||||
description: A component used to create links that can be used to navigate to other pages.
|
||||
date: 14-12-2024
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Note
|
||||
description: This section previews the Note component.
|
||||
description: A component used to display different types of messages such as general notes, warnings, or success notifications.
|
||||
date: 14-12-2024
|
||||
---
|
||||
|
||||
|
||||
@@ -4,43 +4,127 @@ description: The Release Note component makes it easy for you to write updates f
|
||||
date: 31-12-2024
|
||||
---
|
||||
|
||||
## Usage
|
||||
The Release Note component makes it easy for you to write and display changelogs in a structured and organized way. This component consists of two main parts: `Release` and `Changes` which can be used to display version, date, release title, and a list of changes categorized by type.
|
||||
|
||||
To fill in the Release Note content that will be displayed on the Changelog page, open the `CHANGELOG.md` file, write it with the markdown tag.
|
||||
## Basic Usage
|
||||
|
||||
<Accordion title="Example" defaultOpen={true}>
|
||||
```plaintext:CHANGELOG.md
|
||||
## [1.3.0] - 2024-12-31
|
||||
Here is a basic example of using the Release Note component:
|
||||
|
||||
> Release Note Feature to Make it Easier to Write Changelogs
|
||||
```jsx
|
||||
<Release version="1.10.1" date="2025-05-24" title="Accessibility Improvements and Bug Fixes">
|
||||
<Changes type="added">
|
||||
- New feature to improve accessibility
|
||||
- Keyboard navigation support for dialog components
|
||||
</Changes>
|
||||
<Changes type="fixed">
|
||||
- Bug fix for mobile menu
|
||||
- Fixed loading issues on documentation pages
|
||||
</Changes>
|
||||
</Release>
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Added
|
||||
|
||||
- New Release Note Feature
|
||||
- New Layout for Changelog page
|
||||
- New Changelog page
|
||||
- add Release Note Component
|
||||
- Easily write release notes directly from the CHANGELOG.md file
|
||||
- Toc for versioning
|
||||
- Write with the markdown tag
|
||||
- add lib / changelog.ts
|
||||
```
|
||||
<Accordion title="Render As" defaultOpen={true}>
|
||||
<Release version="1.10.1" date="2025-05-24" title="Accessibility Improvements and Bug Fixes">
|
||||
<Changes type="added">
|
||||
- New feature to improve accessibility
|
||||
- Keyboard navigation support for dialog components
|
||||
</Changes>
|
||||
<Changes type="fixed">
|
||||
- Bug fix for mobile menu
|
||||
- Fixed loading issues on documentation pages
|
||||
</Changes>
|
||||
</Release>
|
||||
</Accordion>
|
||||
|
||||
## Preview : Render Output
|
||||
## Release Component
|
||||
|
||||
<Accordion title="Show Render">
|
||||

|
||||
</Accordion>
|
||||
The `Release` component is used to display key information about a release version, such as version number, release date, and title.
|
||||
|
||||
## Changes Category
|
||||
### Release Props
|
||||
|
||||
| **Category** | **Description** |
|
||||
| -------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| **Added** | Features or functionalities that are newly introduced. |
|
||||
| **Improved** | Enhancements or optimizations made to existing features or performance. |
|
||||
| **Fixed** | Bugs, issues, or errors that have been resolved in this version. |
|
||||
| **Deprecated** | Features or functionalities that are no longer recommended for use and may be removed in the future. |
|
||||
| **Removed** | Features or functionalities that have been completely taken out of the product. |
|
||||
| Prop | Type | Required | Description |
|
||||
|------|------|----------|-------------|
|
||||
| `version` | string | ✅ | Version number to display (without "v" prefix) |
|
||||
| `title` | string | ✅ | Title or name of the release |
|
||||
| `date` | string | ❌ | Release date in a valid format (example: "2025-05-24") |
|
||||
| `children` | ReactNode | ✅ | Child content, typically `Changes` components |
|
||||
|
||||
```jsx
|
||||
<Release
|
||||
version="1.10.1"
|
||||
date="2025-05-24"
|
||||
title="Accessibility Improvements and Bug Fixes"
|
||||
>
|
||||
{/* Changes content here */}
|
||||
</Release>
|
||||
```
|
||||
|
||||
## Changes Component
|
||||
|
||||
The `Changes` component is used to group changes by category with appropriate icons and colors.
|
||||
|
||||
### Changes Props
|
||||
|
||||
| Prop | Type | Required | Description |
|
||||
|------|------|----------|-------------|
|
||||
| `type` | string | ✅ | Type of change: 'added', 'fixed', 'improved', 'deprecated', or 'removed' |
|
||||
| `children` | ReactNode | ✅ | List of changes, can be text with Markdown formatting |
|
||||
|
||||
### Changes Note
|
||||
|
||||
| Category | Description |
|
||||
|----------|-------------|
|
||||
| `added` | New features or functionality added |
|
||||
| `fixed` | Bugs or issues that have been fixed |
|
||||
| `improved` | Enhancements or optimizations to existing features |
|
||||
| `deprecated` | Features that are not recommended and may be removed in future |
|
||||
| `removed` | Features that have been completely removed |
|
||||
|
||||
### Changes Example
|
||||
|
||||
```jsx
|
||||
<Changes type="added">
|
||||
- New feature to improve accessibility
|
||||
- Keyboard navigation support for dialog components
|
||||
</Changes>
|
||||
|
||||
<Changes type="fixed">
|
||||
- Bug fix for mobile menu
|
||||
- Fixed loading issues on documentation pages
|
||||
</Changes>
|
||||
```
|
||||
|
||||
## Complete Implementation
|
||||
|
||||
Here is a complete example of using the Release Note component in an MDX file:
|
||||
|
||||
````plaintext
|
||||
<!-- hidden heading for TOC -->
|
||||
|
||||
<Release version="1.10.1" date="2025-05-24" title="Accessibility Improvements and Bug Fixes">
|
||||
<Changes type="added">
|
||||
- Keyboard navigation for all interactive components
|
||||
- Screen reader support for table components
|
||||
- Dark mode feature with system preference detection
|
||||
</Changes>
|
||||
|
||||
<Changes type="fixed">
|
||||
- Fixed mobile menu bug that wouldn't close when navigating to another page
|
||||
- Fixed loading issues on documentation pages
|
||||
- Fixed display issues in Safari browser
|
||||
</Changes>
|
||||
|
||||
<Changes type="improved">
|
||||
- Improved page loading performance
|
||||
- Optimized JavaScript bundle size
|
||||
- Enhanced responsive design across all viewports
|
||||
</Changes>
|
||||
</Release>
|
||||
````
|
||||
|
||||
## Usage Tips
|
||||
|
||||
1. **Date Format**: Use a consistent date format for all releases.
|
||||
2. **Version Ordering**: Arrange versions in reverse chronological order (newest version at the top).
|
||||
3. **List Items**: You can use standard Markdown list format (`-` or `*`) or write text directly, the component will handle the formatting.
|
||||
4. **TOC**: Use hidden headings to ensure each version is detected in the Table of Contents. Use `<div className="sr-only">### v 1.10.1</div>`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Stepper
|
||||
description: This section previews the stepper component.
|
||||
description: A component used to display step-by-step instructions directly within the markdown render.
|
||||
date: 14-12-2024
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Tabs
|
||||
description: This section previews the Tabs component.
|
||||
description: Organize content into multiple sections with switchable tabs.
|
||||
date: 14-12-2024
|
||||
---
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Tooltips
|
||||
description: This section previews the Tooltips component.
|
||||
description: A component used to display additional information when hovering over a word or phrase.
|
||||
date: 19-02-2025
|
||||
---
|
||||
|
||||
I have implemented the `tooltips` component into markdown which is ready to use.
|
||||
I have implemented the `tooltips` component into markdown which allows you to add additional information to a word or phrase when hovering. This feature is useful for providing definitions, explanations, or any other additional information that can enhance the user experience.
|
||||
|
||||
## Usage
|
||||
You can use tooltips in your Markdown content to provide additional information when hovering over a word or phrase.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Youtube
|
||||
description: This section previews the Youtube video embed component.
|
||||
description: A component used to embed YouTube videos directly into your documentation.
|
||||
date: 14-12-2024
|
||||
---
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ DocuBook is proudly **open-source**! 🎉 We believe in creating an accessible,
|
||||
| ------------------------------- | ----------------------------------------------------- |
|
||||
| MDX Support | Write interactive documentation with MDX. |
|
||||
| Nested Pages | Organize content in a nested, hierarchical structure. |
|
||||
| Blog Section | Include a dedicated blog section. |
|
||||
| Pagination | Split content across multiple pages. |
|
||||
| Syntax Highlighting | Highlight code for better readability. |
|
||||
| Code Line Highlighting & Titles | Highlight specific lines with descriptive titles. |
|
||||
|
||||
@@ -4,9 +4,7 @@ description: This section provides an overview of Project Structure.
|
||||
date: 29-11-2024
|
||||
---
|
||||
|
||||
## Project Structure Overview
|
||||
|
||||
### app
|
||||
## app
|
||||
|
||||
This folder contains the main application code for Next.js, managing layouts, routing, and specific content pages. It is organized to support both the `docs` and `blog` sections, with dedicated pages and layouts for each section.
|
||||
|
||||
@@ -14,16 +12,12 @@ This folder contains the main application code for Next.js, managing layouts, ro
|
||||
app // Main Next.js application folder
|
||||
├── page.tsx // Hero page - the entry point of the app showcasing key content
|
||||
├── layout.tsx // Main layout file, applies global navigation and footer
|
||||
├── blog
|
||||
│ ├── page.tsx // Blog list page, displaying all blog posts with titles, dates, and excerpts
|
||||
│ └── [slug]
|
||||
│ └── page.tsx // Dynamic blog post page for viewing individual posts with rich content
|
||||
└── docs
|
||||
├── layout.tsx // Documentation layout with sidebar, providing easy navigation across doc pages
|
||||
└── [[...slug]] // Catch-all route to handle nested documentation pages, allowing flexible doc structure
|
||||
```
|
||||
|
||||
### components
|
||||
## components
|
||||
|
||||
This folder contains all reusable components used throughout the project. It’s structured to categorize components, making it easy to locate and manage UI elements, Markdown customizations, and navigation.
|
||||
|
||||
@@ -37,7 +31,7 @@ components // Reusable components
|
||||
└── navbar.tsx // Main navigation component for the site header, managing links and responsive behavior
|
||||
```
|
||||
|
||||
### styles
|
||||
## styles
|
||||
|
||||
This folder contains global and component-specific CSS files, allowing for project-wide styling consistency and customizations.
|
||||
|
||||
@@ -48,7 +42,7 @@ styles // CSS files
|
||||
└── overrides.css // Additional custom styles that override specific component or plugin defaults
|
||||
```
|
||||
|
||||
### contents
|
||||
## contents
|
||||
|
||||
This folder stores all Markdown content for the documentation and blog sections, with clear organization by content type. Each Markdown file represents a single piece of content, with frontmatter used for metadata.
|
||||
|
||||
@@ -57,13 +51,10 @@ contents // Markdown content for blogs and documentation
|
||||
├── docs // Documentation content, structured to support nested sections and pages
|
||||
│ ├── getting-started // Main Folder at the Contents
|
||||
│ └─── installation // Subfolder for tutorial-style content
|
||||
│ └── index.mdx // Step-by-step tutorial on a specific topic
|
||||
└── blogs // Blog content, organized by post
|
||||
├── intro-to-project.md // Blog post introducing the project and its goals
|
||||
└── dev-updates.md // Post discussing recent development updates and new features
|
||||
| └── index.mdx // Step-by-step tutorial on a specific topic
|
||||
```
|
||||
|
||||
### public
|
||||
## public
|
||||
|
||||
This folder holds all static assets, such as images, videos, fonts, and icons. These files are directly accessible via URL, so it’s important to avoid sensitive or private content here.
|
||||
|
||||
@@ -76,18 +67,19 @@ public // Publicly accessible assets
|
||||
└── videos // Video files for media content, if any
|
||||
```
|
||||
|
||||
### lib
|
||||
## lib
|
||||
|
||||
This folder contains helper functions and utilities used across the application, such as Markdown parsing and routing logic. These utilities help keep the codebase clean and organized by separating out common functionality.
|
||||
|
||||
```
|
||||
lib // Utility or helper functions
|
||||
├── changelog.ts // Change log for the app, used to display recent changes and updates
|
||||
├── markdown.ts // Markdown parsing logic, converts Markdown to HTML and adds custom components
|
||||
├── routes-config.ts // Routing configuration for docs, maps URLs to content files for dynamic routing
|
||||
└── utils.tsx // General utility functions used across the app, such as data formatting and validation helpers
|
||||
```
|
||||
|
||||
### Additional
|
||||
## Additional
|
||||
|
||||
- **`package.json`**: Contains metadata about the project, dependencies, and scripts for building and running the application.
|
||||
- **`tailwind.config.ts`**: Configures Tailwind CSS, allowing customization of theme colors, fonts, and responsive breakpoints specific to this project.
|
||||
|
||||
@@ -1,157 +1,127 @@
|
||||
---
|
||||
title: Quick Start Guide
|
||||
description: This section provides an overview of Quick Start Guide.
|
||||
date: 29-11-2024
|
||||
description: Get up and running with DocuBook in minutes with this comprehensive guide
|
||||
date: 20-05-2025
|
||||
---
|
||||
|
||||
Follow these simple instructions to set up your project efficiently and begin creating content without delay!:
|
||||
Welcome to DocuBook! This guide will help you set up your documentation site quickly and efficiently.
|
||||
|
||||
## Homepage
|
||||
## Prerequisites
|
||||
|
||||
to change the homepage you can open the app folder.
|
||||
Before we begin, make sure you have the following installed:
|
||||
|
||||
- [Git](https://git-scm.com/)
|
||||
- [Node.js 18+](https://nodejs.org/) or [Bun 1.0+](https://bun.sh/)
|
||||
- A package manager (npm, yarn, or pnpm)
|
||||
|
||||
## Installation
|
||||
|
||||
<Note type="note" title="Note">
|
||||
Follow the instructions on the [installation page](/docs/getting-started/installation) to install the required dependencies and set up your project.
|
||||
</Note>
|
||||
|
||||
## Project Setup
|
||||
|
||||
### Customize Branding
|
||||
|
||||
<Stepper>
|
||||
<StepperItem title="Step 1: App">
|
||||
Open app folder.
|
||||
<StepperItem title="Step 1: Add Favicon">
|
||||
Place your favicon at `public/favicon.ico`
|
||||
</StepperItem>
|
||||
<StepperItem title="Step 2: Page.tsx">
|
||||
Edit page.tsx file for home content.
|
||||
<StepperItem title="Step 2: Add Logo">
|
||||
Place your logo at `public/images/docu.svg` (SVG format recommended)
|
||||
</StepperItem>
|
||||
<StepperItem title="Step 3: Update Site Info">
|
||||
Edit `docu.json` to update site name, description, and other settings
|
||||
</StepperItem>
|
||||
</Stepper>
|
||||
|
||||
## Docu.json
|
||||
Change logo, menu, routes and footer directly in docu.json
|
||||
## Creating Content
|
||||
|
||||
<Accordion title="default code">
|
||||
```json:docu.json
|
||||
### File Structure
|
||||
|
||||
DocuBook uses the following structure:
|
||||
|
||||
````plaintext
|
||||
contents/
|
||||
docs/
|
||||
getting-started/
|
||||
quick-start-guide/ <-- You are here
|
||||
index.mdx
|
||||
guides/
|
||||
components/
|
||||
index.mdx
|
||||
````
|
||||
|
||||
### Adding New Pages
|
||||
|
||||
<Stepper>
|
||||
<StepperItem title="Step 1: Create a New Folder">
|
||||
Create a new folder in `contents/docs/getting-started/` for your content section
|
||||
```bash
|
||||
mkdir -p contents/docs/getting-started/your-page-title
|
||||
```
|
||||
</StepperItem>
|
||||
<StepperItem title="Step 2: Add MDX File">
|
||||
Create an `index.mdx` file with frontmatter:
|
||||
````markdown
|
||||
---
|
||||
title: Your Page Title
|
||||
description: Brief description of your page
|
||||
date: 20-05-2025
|
||||
---
|
||||
|
||||
# Your Content Here
|
||||
|
||||
Start writing your documentation using Markdown or MDX components.
|
||||
````
|
||||
</StepperItem>
|
||||
<StepperItem title="Step 3: Add to Navigation">
|
||||
Update the navigation in `docu.json`:
|
||||
```json:docu.json showLineNumbers {8}
|
||||
{
|
||||
"navbar": {
|
||||
"logo": {
|
||||
"src": "/images/docu.svg",
|
||||
"alt": "DocuBook Logo"
|
||||
},
|
||||
"logoText": "DocuBook",
|
||||
"menu": [
|
||||
{ "title": "Docs", "href": "/docs/getting-started/introduction" },
|
||||
{ "title": "Changelog", "href": "/changelog" },
|
||||
{ "title": "Blog", "href": "/blog" },
|
||||
{ "title": "Playground", "href": "/playground" }
|
||||
]
|
||||
},
|
||||
"footer": {
|
||||
"copyright": "DocuBook",
|
||||
"social": [
|
||||
{
|
||||
"name": "Gitlab",
|
||||
"url": "https://gitlab.com/mywildancloud/docubook",
|
||||
"iconName": "GitlabIcon"
|
||||
},
|
||||
{
|
||||
"name": "Instagram",
|
||||
"url": "https://www.instagram.com/wildan.nrs/",
|
||||
"iconName": "InstagramIcon"
|
||||
},
|
||||
{
|
||||
"name": "Facebook",
|
||||
"url": "https://www.facebook.com/wildan.nrsh",
|
||||
"iconName": "FacebookIcon"
|
||||
},
|
||||
{
|
||||
"name": "Twitter",
|
||||
"url": "https://x.com/wildan_nrss",
|
||||
"iconName": "TwitterIcon"
|
||||
},
|
||||
{
|
||||
"name": "Youtube",
|
||||
"url": "https://www.youtube.com/@wildan.nrs_",
|
||||
"iconName": "YoutubeIcon"
|
||||
}
|
||||
]
|
||||
},
|
||||
"meta": {
|
||||
"baseURL": "https://docubook.pro",
|
||||
"title": "DocuBook",
|
||||
"description": "DocuBook is a modern documentation platform for building, deploying, and managing your docs with ease.",
|
||||
"favicon": "/favicon.ico"
|
||||
},
|
||||
"repository": {
|
||||
"url": "https://gitlab.com/mywildancloud/docubook",
|
||||
"editPathTemplate": "/blob/main/{filePath}"
|
||||
},
|
||||
"routes": [
|
||||
{
|
||||
"title": "Getting Started",
|
||||
"href": "/getting-started",
|
||||
"noLink": true,
|
||||
"items": [
|
||||
{ "title": "Introduction", "href": "/introduction" },
|
||||
{ "title": "Installation", "href": "/installation" },
|
||||
{ "title": "Quick Start Guide", "href": "/quick-start-guide" },
|
||||
{ "title": "Project Structure", "href": "/project-structure" },
|
||||
{
|
||||
"title": "Components",
|
||||
"href": "/components",
|
||||
"items": [
|
||||
{ "title": "Stepper", "href": "/stepper" },
|
||||
{ "title": "Tabs", "href": "/tabs" },
|
||||
{ "title": "Note", "href": "/note" },
|
||||
{ "title": "Code Block", "href": "/code-block" },
|
||||
{ "title": "Image", "href": "/image" },
|
||||
{ "title": "Link", "href": "/link" },
|
||||
{ "title": "Youtube", "href": "/youtube" },
|
||||
{ "title": "Tooltips", "href": "/tooltips" },
|
||||
{ "title": "Cards", "href": "/card" },
|
||||
{ "title": "Card Group", "href": "/card-group" },
|
||||
{ "title": "Button", "href": "/button" },
|
||||
{ "title": "Accordion", "href": "/accordion" },
|
||||
{ "title": "Release Note", "href": "/release-note" },
|
||||
{ "title": "Custom", "href": "/custom" }
|
||||
]
|
||||
},
|
||||
{ "title": "Customize", "href": "/customize" }
|
||||
{ "title": "Your Page Title", "href": "/your-page-title" } // Add your page here separated by comma `,`
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</StepperItem>
|
||||
</Stepper>
|
||||
|
||||
## Development
|
||||
|
||||
Start the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
## Change Logo & Favicon
|
||||
Visit [http://localhost:3000](http://localhost:3000) to see your site.
|
||||
|
||||
Save your logo, give it the name docu.svg then for the favicon, save your logo, give it the name favicon.ico
|
||||
## Building for Production
|
||||
|
||||
<Stepper>
|
||||
<StepperItem title="Step 1: Public">
|
||||
Open `public` folder then upload your logo save as **favicon.ico**
|
||||
</StepperItem>
|
||||
<StepperItem title="Step 2: Public/images">
|
||||
Open `public` folder --> `images` then upload your logo and save as **docu.svg**
|
||||
</StepperItem>
|
||||
</Stepper>
|
||||
When you're ready to deploy:
|
||||
|
||||
## Contents
|
||||
```bash
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
|
||||
Open **contents/docs/getting-started** folder add new folder to add content.
|
||||
## Need Help?
|
||||
|
||||
<Stepper>
|
||||
<StepperItem title="Step 1: Add new Folder">
|
||||
for example adding a folder for routes `how-to-install-myplugin`
|
||||
</StepperItem>
|
||||
<StepperItem title="Step 2: Add new File">
|
||||
add `index.mdx` file in that folder
|
||||
</StepperItem>
|
||||
<StepperItem title="Step 3: Writing the Content">
|
||||
To write content, you must first add metadata to the first line.
|
||||
````plaintext
|
||||
---
|
||||
title : Title
|
||||
description : Description
|
||||
date : 10-12-2024
|
||||
---
|
||||
<--- this is where you write the content --->
|
||||
````
|
||||
<Note type="warning" title="Warning">
|
||||
To write markdown content you can use all the [Components](/docs/getting-started/components)
|
||||
</Note>
|
||||
</StepperItem>
|
||||
</Stepper>
|
||||
<Note type="info">
|
||||
Check out the [Components Guide](/docs/getting-started/components) to learn about all available components.
|
||||
</Note>
|
||||
|
||||
<Note type="warning">
|
||||
Make sure to commit your changes to version control before deploying.
|
||||
</Note>
|
||||
|
||||
15
docu.json
15
docu.json
@@ -6,9 +6,9 @@
|
||||
},
|
||||
"logoText": "DocuBook",
|
||||
"menu": [
|
||||
{ "title": "Home", "href": "/" },
|
||||
{ "title": "Docs", "href": "/docs/getting-started/introduction" },
|
||||
{ "title": "Changelog", "href": "/changelog" },
|
||||
{ "title": "Blog", "href": "/blog" }
|
||||
{ "title": "Community", "href": "https://docubook.pro" }
|
||||
]
|
||||
},
|
||||
"footer": {
|
||||
@@ -48,17 +48,17 @@
|
||||
"favicon": "/favicon.ico"
|
||||
},
|
||||
"repository": {
|
||||
"url": "https://gitlab.com/mywildancloud/docubook",
|
||||
"url": "https://github.com/gitfromwildan/docubook",
|
||||
"editPathTemplate": "/blob/main/{filePath}",
|
||||
"editLink": true
|
||||
},
|
||||
"sponsor": {
|
||||
"title": "Our Sponsor",
|
||||
"title": "Hosted on",
|
||||
"item": {
|
||||
"title": "Vercel",
|
||||
"description": "Deploy your Next.js app with zero configuration.",
|
||||
"description": "Deploy your DocuBook app with zero configuration.",
|
||||
"image": "/images/vercel.png",
|
||||
"url": "https://vercel.com"
|
||||
"url": "https://vercel.com/import/project?template=https://github.com/gitfromwildan/docubook"
|
||||
}
|
||||
},
|
||||
"routes": [
|
||||
@@ -92,7 +92,8 @@
|
||||
{ "title": "Custom", "href": "/custom" }
|
||||
]
|
||||
},
|
||||
{ "title": "Customize", "href": "/customize" }
|
||||
{ "title": "Customize", "href": "/customize" },
|
||||
{ "title": "Changelog", "href": "/changelog" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
2
hooks/index.ts
Normal file
2
hooks/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './useScrollPosition';
|
||||
export * from './useActiveSection';
|
||||
68
hooks/useActiveSection.ts
Normal file
68
hooks/useActiveSection.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import { TocItem } from '@/lib/toc';
|
||||
|
||||
export function useActiveSection(tocs: TocItem[]) {
|
||||
const [activeId, setActiveId] = useState<string | null>(null);
|
||||
const observerRef = useRef<IntersectionObserver | null>(null);
|
||||
const clickedIdRef = useRef<string | null>(null);
|
||||
|
||||
// Handle intersection observer for active section
|
||||
useEffect(() => {
|
||||
if (typeof document === 'undefined' || !tocs.length) return;
|
||||
|
||||
const handleIntersect = (entries: IntersectionObserverEntry[]) => {
|
||||
if (clickedIdRef.current) return;
|
||||
|
||||
const visibleEntries = entries.filter(entry => entry.isIntersecting);
|
||||
if (!visibleEntries.length) return;
|
||||
|
||||
// Find the most visible entry
|
||||
const mostVisibleEntry = visibleEntries.reduce((prev, current) => {
|
||||
return current.intersectionRatio > prev.intersectionRatio ? current : prev;
|
||||
}, visibleEntries[0]);
|
||||
|
||||
const newActiveId = mostVisibleEntry.target.id;
|
||||
if (newActiveId !== activeId) {
|
||||
setActiveId(newActiveId);
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize intersection observer
|
||||
observerRef.current = new IntersectionObserver(handleIntersect, {
|
||||
root: null,
|
||||
rootMargin: '0px 0px -80% 0px',
|
||||
threshold: 0.1,
|
||||
});
|
||||
|
||||
// Observe all headings
|
||||
tocs.forEach(toc => {
|
||||
const element = document.getElementById(toc.href.slice(1));
|
||||
if (element) {
|
||||
observerRef.current?.observe(element);
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
observerRef.current?.disconnect();
|
||||
};
|
||||
}, [tocs, activeId]);
|
||||
|
||||
const handleLinkClick = useCallback((id: string) => {
|
||||
clickedIdRef.current = id;
|
||||
setActiveId(id);
|
||||
|
||||
// Reset clicked state after scroll completes
|
||||
const timer = setTimeout(() => {
|
||||
clickedIdRef.current = null;
|
||||
}, 1000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
activeId,
|
||||
setActiveId,
|
||||
handleLinkClick,
|
||||
};
|
||||
}
|
||||
28
hooks/useScrollPosition.ts
Normal file
28
hooks/useScrollPosition.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
|
||||
export function useScrollPosition(threshold = 0.5) {
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
|
||||
const handleScroll = useCallback(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
const scrollPosition = window.scrollY;
|
||||
const viewportHeight = window.innerHeight;
|
||||
const shouldBeSticky = scrollPosition > viewportHeight * threshold;
|
||||
|
||||
setIsScrolled(prev => shouldBeSticky !== prev ? shouldBeSticky : prev);
|
||||
}, [threshold]);
|
||||
|
||||
// Add scroll event listener
|
||||
useEffect(() => {
|
||||
// Initial check
|
||||
handleScroll();
|
||||
|
||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, [handleScroll]);
|
||||
|
||||
return isScrolled;
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import Button from "@/components/markdown/ButtonMdx";
|
||||
import Accordion from "@/components/markdown/AccordionMdx";
|
||||
import CardGroup from "@/components/markdown/CardGroupMdx";
|
||||
import Kbd from "@/components/markdown/KeyboardMdx";
|
||||
import { Release, Changes } from "@/components/markdown/ReleaseMdx";
|
||||
|
||||
// add custom components
|
||||
const components = {
|
||||
@@ -46,6 +47,8 @@ const components = {
|
||||
Accordion,
|
||||
CardGroup,
|
||||
Kbd,
|
||||
Release,
|
||||
Changes,
|
||||
};
|
||||
|
||||
// can be used for other pages like blogs, Guides etc
|
||||
@@ -179,13 +182,15 @@ const postProcess = () => (tree: any) => {
|
||||
});
|
||||
};
|
||||
|
||||
export type Author = {
|
||||
avatar?: string;
|
||||
handle: string;
|
||||
username: string;
|
||||
handleUrl: string;
|
||||
};
|
||||
// export type Author = {
|
||||
// avatar?: string;
|
||||
// handle: string;
|
||||
// username: string;
|
||||
// handleUrl: string;
|
||||
// };
|
||||
|
||||
// Blog related types and functions have been removed
|
||||
/*
|
||||
export type BlogMdxFrontmatter = BaseMdxFrontmatter & {
|
||||
date: string;
|
||||
authors: Author[];
|
||||
@@ -199,20 +204,26 @@ export async function getAllBlogStaticPaths() {
|
||||
return res.map((file) => file.split(".")[0]);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllBlogs() {
|
||||
const blogFolder = path.join(process.cwd(), "/contents/blogs/");
|
||||
const files = await fs.readdir(blogFolder);
|
||||
const uncheckedRes = await Promise.all(
|
||||
files.map(async (file) => {
|
||||
if (!file.endsWith(".mdx")) return undefined;
|
||||
try {
|
||||
const filepath = path.join(process.cwd(), `/contents/blogs/${file}`);
|
||||
const rawMdx = await fs.readFile(filepath, "utf-8");
|
||||
return {
|
||||
...justGetFrontmatterFromMD<BlogMdxFrontmatter>(rawMdx),
|
||||
slug: file.split(".")[0],
|
||||
};
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
);
|
||||
return uncheckedRes.filter((it) => !!it) as (BlogMdxFrontmatter & {
|
||||
@@ -221,11 +232,13 @@ export async function getAllBlogs() {
|
||||
}
|
||||
|
||||
export async function getBlogForSlug(slug: string) {
|
||||
const blogFile = path.join(process.cwd(), "/contents/blogs/", `${slug}.mdx`);
|
||||
try {
|
||||
const blogFile = path.join(process.cwd(), "/contents/blogs/", `${slug}.mdx`);
|
||||
const rawMdx = await fs.readFile(blogFile, "utf-8");
|
||||
return await parseMdx<BlogMdxFrontmatter>(rawMdx);
|
||||
} catch {
|
||||
return undefined;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
9
lib/toc.ts
Normal file
9
lib/toc.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface TocItem {
|
||||
level: number;
|
||||
text: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export interface MobTocProps {
|
||||
tocs: TocItem[];
|
||||
}
|
||||
2363
package-lock.json
generated
2363
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docubook",
|
||||
"version": "1.9.0",
|
||||
"version": "1.11.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@@ -9,7 +9,6 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdx-js/react": "^3.1.0",
|
||||
"@radix-ui/react-accordion": "^1.2.0",
|
||||
"@radix-ui/react-avatar": "^1.1.0",
|
||||
"@radix-ui/react-collapsible": "^1.1.0",
|
||||
@@ -28,7 +27,7 @@
|
||||
"framer-motion": "^12.4.1",
|
||||
"geist": "^1.3.1",
|
||||
"gray-matter": "^4.0.3",
|
||||
"lucide-react": "^0.435.0",
|
||||
"lucide-react": "^0.511.0",
|
||||
"next": "^14.2.6",
|
||||
"next-mdx-remote": "^5.0.0",
|
||||
"next-themes": "^0.3.0",
|
||||
|
||||
@@ -98,13 +98,3 @@
|
||||
border: none;
|
||||
border-radius: 8px; /* Sudut melengkung pada iframe */
|
||||
}
|
||||
|
||||
li#changelog {
|
||||
position: relative;
|
||||
padding-left: 1.5em;
|
||||
text-indent: -1.2rem;
|
||||
}
|
||||
|
||||
[id^="version-"] {
|
||||
scroll-margin-top: 4rem; /* sesuaikan dengan tinggi navbar */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user