diff --git a/.gitignore b/.gitignore
index fd3dbb5..73123d0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,3 +34,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+# bun
+bun.lock
diff --git a/app/page.tsx b/app/page.tsx
index 6d78596..c4c80bd 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -51,14 +51,15 @@ export default function Home() {
Get Started
- Playground
+ Subscribe Now
diff --git a/app/playground/layout.tsx b/app/playground/layout.tsx
deleted file mode 100644
index bd0f70b..0000000
--- a/app/playground/layout.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { PropsWithChildren } from "react";
-import { getMetadata } from "@/app/layout";
-
-export const metadata = getMetadata({
- title: "Playground",
- description: "Test and experiment with DocuBook markdown components in real-time",
- image: "img-playground.png",
- });
-
-export default function PlaygroundLayout({ children }: PropsWithChildren) {
- return (
-
- {children}
-
- );
-}
diff --git a/app/playground/page.tsx b/app/playground/page.tsx
deleted file mode 100644
index 2247a7d..0000000
--- a/app/playground/page.tsx
+++ /dev/null
@@ -1,395 +0,0 @@
-"use client";
-
-import { useState, useEffect, useRef } from "react";
-import { ScrollArea } from "@/components/ui/scroll-area";
-import { Separator } from "@/components/ui/separator";
-import { toast } from "sonner";
-import {
- List,
- ListOrdered,
- Heading2,
- Heading3,
- Code,
- Quote,
- ImageIcon,
- Link as LinkIcon,
- Table,
- Maximize2,
- Minimize2,
- Type,
- ChevronDown,
- Notebook,
- Component,
- Youtube as YoutubeIcon,
- HelpCircle,
- LayoutGrid,
- MousePointer2,
- Rows,
- LayoutPanelTop,
- Laptop2,
- Copy,
- Download,
- RotateCcw,
- Calendar
-} from "lucide-react";
-import { Button as UIButton } from "@/components/ui/button";
-import { cn } from "@/lib/utils";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-import {
- handleParagraphClick,
- handleHeading2Click,
- handleHeading3Click,
- handleBulletListClick,
- handleNumberedListClick,
- handleLinkClick,
- handleImageClick,
- handleBlockquoteClick,
- handleCodeBlockClick,
- handleTableClick,
- handleNoteClick,
- handleComponentClick,
- handleMetadataClick,
- } from "@/components/playground/MarkComponent";
-
-import "@/styles/editor.css";
-
-const ToolbarButton = ({ icon: Icon, label, onClick }: { icon: any, label: string, onClick?: () => void }) => (
-
-
-
-);
-
-const ToolbarSeparator = () => (
-
-);
-
-const MobileMessage = () => (
-
-
-
Desktop View Recommended
-
- The Playground works best on larger screens. Please switch to a desktop device for the best experience.
-
-
-);
-
-export default function PlaygroundPage() {
- const [markdown, setMarkdown] = useState("");
- const [isFullscreen, setIsFullscreen] = useState(false);
- const [isMobile, setIsMobile] = useState(false);
- const [lineCount, setLineCount] = useState(1);
- const editorRef = useRef
(null);
- const lineNumbersRef = useRef(null);
-
- useEffect(() => {
- const checkMobile = () => {
- setIsMobile(window.innerWidth < 768);
- };
-
- checkMobile();
- window.addEventListener('resize', checkMobile);
-
- return () => {
- window.removeEventListener('resize', checkMobile);
- };
- }, []);
-
- useEffect(() => {
- // Update line count when markdown content changes
- const lines = markdown.split('\n').length;
- setLineCount(Math.max(lines, 1));
- }, [markdown]);
-
- // Sync scroll position between editor and line numbers
- useEffect(() => {
- const textarea = editorRef.current;
- const lineNumbers = lineNumbersRef.current;
-
- if (!textarea || !lineNumbers) return;
-
- const handleScroll = () => {
- lineNumbers.scrollTop = textarea.scrollTop;
- };
-
- textarea.addEventListener('scroll', handleScroll);
- return () => textarea.removeEventListener('scroll', handleScroll);
- }, []);
-
- const toggleFullscreen = () => {
- setIsFullscreen(!isFullscreen);
- };
-
- const handleCopy = async () => {
- try {
- await navigator.clipboard.writeText(markdown);
- toast.success('Content copied to clipboard');
- } catch (err) {
- toast.error('Failed to copy content');
- }
- };
-
- const handleDownload = () => {
- try {
- const blob = new Blob([markdown], { type: 'text/markdown' });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = 'index.mdx';
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
- toast.success('Content downloaded successfully');
- } catch (err) {
- toast.error('Failed to download content');
- }
- };
-
- const handleReset = () => {
- if (markdown.trim()) {
- toast.custom((t) => (
-
-
Clear editor content?
-
This action cannot be undone.
-
- {
- setMarkdown('');
- toast.success('all content in the editor has been cleaned');
- toast.dismiss(t);
- }}
- >
- Clear
-
- toast.dismiss(t)}
- >
- Cancel
-
-
-
- ), { duration: 10000 });
- }
- };
-
- const insertAtCursor = (textArea: HTMLTextAreaElement, text: string) => {
- const start = textArea.selectionStart;
- const end = textArea.selectionEnd;
- const before = markdown.substring(0, start);
- const after = markdown.substring(end);
-
- const needsLeadingNewline = before && !before.endsWith('\n\n') ? '\n\n' : '';
- const needsTrailingNewline = after && !after.startsWith('\n\n') ? '\n\n' : '';
-
- const newText = `${before}${needsLeadingNewline}${text}${needsTrailingNewline}${after}`;
- setMarkdown(newText);
-
- requestAnimationFrame(() => {
- textArea.focus();
- const newPosition = before.length + needsLeadingNewline.length + text.length + 1;
- textArea.setSelectionRange(newPosition, newPosition);
- });
- };
-
- if (isMobile) {
- return ;
- }
-
- return (
-
-
-
-
-
DocuPLAY
-
- Test and experiment with DocuBook markdown components in real-time
-
-
-
-
-
-
-
-
-
-
-
- {markdown.trim() && (
- <>
-
-
- Copy
-
-
-
- Download
-
-
-
- Reset
-
-
- >
- )}
-
-
- {isFullscreen ? (
- <>
-
- Exit Fullscreen
- >
- ) : (
- <>
-
- Fullscreen
- >
- )}
-
-
-
- handleMetadataClick(insertAtCursor)} />
-
- handleParagraphClick(insertAtCursor)} />
- handleHeading2Click(insertAtCursor)} />
- handleHeading3Click(insertAtCursor)} />
- handleBulletListClick(insertAtCursor)} />
- handleNumberedListClick(insertAtCursor)} />
-
- handleLinkClick(insertAtCursor)} />
- handleImageClick(insertAtCursor)} />
- handleBlockquoteClick(insertAtCursor)} />
- handleCodeBlockClick(insertAtCursor)} />
- handleTableClick(insertAtCursor)} />
-
-
-
-
-
-
-
-
-
-
- Note
-
-
- Danger
-
-
- Warning
-
-
- Success
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Stepper
-
-
-
- Card
-
-
-
- Button
-
-
-
- Accordion
-
-
-
- Tabs
-
-
-
- Youtube
-
-
-
- Tooltip
-
-
-
-
-
-
-
-
- {Array.from({ length: lineCount }).map((_, i) => (
-
- ))}
-
-
-
-
-
-
-
- );
-}
diff --git a/components/playground/MarkComponent.tsx b/components/playground/MarkComponent.tsx
deleted file mode 100644
index 07d3dde..0000000
--- a/components/playground/MarkComponent.tsx
+++ /dev/null
@@ -1,252 +0,0 @@
-type InsertAtCursor = (textArea: HTMLTextAreaElement, text: string) => void;
-
-// toolbar handler
-export const handleMetadataClick = (insertAtCursor: InsertAtCursor) => {
- const textArea = document.querySelector("textarea");
- if (textArea) {
- const metadata = `---
-title: Post Title
-description: Your Post Description
-date: ${new Date().toISOString().split("T")[0]}
-image: example-img.png
----\n\n`;
-
- insertAtCursor(textArea, metadata);
- }
-};
-
-export const handleParagraphClick = (insertAtCursor: InsertAtCursor) => {
- const textArea = document.querySelector("textarea");
- if (textArea) {
- insertAtCursor(textArea, "this is regular text, **bold text**, *italic text*\n");
- }
-};
-
-export const handleHeading2Click = (insertAtCursor: InsertAtCursor) => {
- const textArea = document.querySelector("textarea");
- if (textArea) {
- insertAtCursor(textArea, "## Heading 2\n");
- }
-};
-
-export const handleHeading3Click = (insertAtCursor: InsertAtCursor) => {
- const textArea = document.querySelector("textarea");
- if (textArea) {
- insertAtCursor(textArea, "### Heading 3\n");
- }
-};
-
-export const handleBulletListClick = (insertAtCursor: InsertAtCursor) => {
- const textArea = document.querySelector("textarea");
- if (textArea) {
- insertAtCursor(textArea, "- List One\n- List Two\n- Other List\n");
- }
-};
-
-export const handleNumberedListClick = (insertAtCursor: InsertAtCursor) => {
- const textArea = document.querySelector("textarea");
- if (textArea) {
- insertAtCursor(textArea, "1. Number One\n2. Number Two\n3. Number Three\n");
- }
-};
-
-export const handleLinkClick = (insertAtCursor: InsertAtCursor) => {
- const textArea = document.querySelector("textarea");
- if (textArea) {
- insertAtCursor(textArea, "[Visit OpenAI](https://www.openai.com)\n");
- }
-};
-
-export const handleImageClick = (insertAtCursor: InsertAtCursor) => {
- const textArea = document.querySelector("textarea");
- if (textArea) {
- insertAtCursor(textArea, "\n");
- }
-};
-
-export const handleBlockquoteClick = (insertAtCursor: InsertAtCursor) => {
- const textArea = document.querySelector("textarea");
- if (textArea) {
- insertAtCursor(textArea, "> The overriding design goal for Markdown's formatting syntax is to make it as readable as possible.\n");
- }
-};
-
-export const handleCodeBlockClick = (insertAtCursor: InsertAtCursor) => {
- const textArea = document.querySelector("textarea");
- if (textArea) {
- insertAtCursor(
- textArea,
- "```javascript:main.js showLineNumbers {3-4}\nfunction isRocketAboutToCrash() {\n // Check if the rocket is stable\n if (!isStable()) {\n NoCrash(); // Prevent the crash\n }\n}\n```\n"
- );
- }
-};
-
-export const handleTableClick = (insertAtCursor: InsertAtCursor) => {
- const textArea = document.querySelector("textarea");
- if (textArea) {
- insertAtCursor(
- textArea,
- `| **Feature** | **Description** |
-| ------------------------------- | ----------------------------------------------------- |
-| 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. |
-
-`
- );
- }
-};
-
-export const handleNoteClick = (insertAtCursor: InsertAtCursor, type: string) => {
- return () => {
- const textArea = document.querySelector("textarea");
- if (textArea) {
- const noteTemplate = `\n This is a ${type} message.\n\n`;
- insertAtCursor(textArea, noteTemplate);
- }
- };
- };
-
- export const handleComponentClick = (insertAtCursor: InsertAtCursor, component: string) => {
- return () => {
- const textArea = document.querySelector("textarea");
- if (!textArea) return;
-
- const templates: { [key: string]: string } = {
- stepper: `
-
- Content for step 1
-
-
- Content for step 2
-
-\n`,
- card: `
- This is how you use a card with an icon and a link. Clicking on this card brings you to the Card Group page.
-\n`,
- button: `\n`,
- accordion: `
- this is an example of plain text content from the accordion component and below is markdown ;
- 1. number one
- 2. number two
- 3. number three
-\n`,
- youtube: `\n`,
- tooltip: `What do you know about ? Create interactive nested documentations using MDX.\n`,
- tabs: `
-
- Tab 1
- Tab 2
-
-
- Content for tab 1
-
-
- Content for tab 2
-
-\n`
- };
-
- insertAtCursor(textArea, templates[component]);
- };
- };
-
-// slash command handler
-export const MARK_COMPONENTS = [
- { label: "Metadata", value: `---
- title: Post Title
- description: Your Post Description
- date: ${new Date().toISOString().split("T")[0]}
- image: example-img.png
- ---\n\n` },
-
- { label: "Heading 2", value: "## Heading 2\n" },
- { label: "Heading 3", value: "### Heading 3\n" },
-
- { label: "Paragraph", value: "this is regular text, **bold text**, *italic text*\n" },
-
- { label: "Bullet List", value: "- List One\n- List Two\n- Other List\n" },
- { label: "Numbered List", value: "1. Number One\n2. Number Two\n3. Number Three\n" },
-
- { label: "Blockquote", value: "> The overriding design goal for Markdown's formatting syntax is to make it as readable as possible.\n" },
-
- { label: "Code Block", value: "```javascript:main.js showLineNumbers {3-4}\nfunction isRocketAboutToCrash() {\n // Check if the rocket is stable\n if (!isStable()) {\n NoCrash(); // Prevent the crash\n }\n}\n```\n" },
-
- { label: "Table", value: `| **Feature** | **Description** |
- | ------------------------------- | ----------------------------------------------------- |
- | 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. |
-
- ` },
-
- { label: "Link", value: "[Visit OpenAI](https://www.openai.com)\n" },
- { label: "Image", value: "\n" },
-
- // ⭐ Komponen Interaktif DocuBook ⭐
- { label: "Stepper", value: `
-
- Content for step 1
-
-
- Content for step 2
-
- \n` },
-
- { label: "Button", value: `\n` },
-
- { label: "Accordion", value: `
- This is an example of plain text content inside the accordion component.
- 1. Number One
- 2. Number Two
- 3. Number Three
- \n` },
-
- { label: "Youtube", value: `\n` },
-
- { label: "Tooltip", value: `What do you know about ?\n` },
-
- { label: "Tabs", value: `
-
- Tab 1
- Tab 2
-
-
- Content for tab 1
-
-
- Content for tab 2
-
- \n` },
-
- { label: "Note", value: `
- This is a note message.
- \n` },
-
- { label: "Danger", value: `
- This is a danger message.
- \n` },
-
- { label: "Warning", value: `
- This is a warning message.
- \n` },
-
- { label: "Success", value: `
- This is a success message.
- \n` }
-
-];
diff --git a/docu.json b/docu.json
index 1d64772..1dc8bc9 100644
--- a/docu.json
+++ b/docu.json
@@ -8,8 +8,7 @@
"menu": [
{ "title": "Docs", "href": "/docs/getting-started/introduction" },
{ "title": "Changelog", "href": "/changelog" },
- { "title": "Blog", "href": "/blog" },
- { "title": "Playground", "href": "/playground" }
+ { "title": "Blog", "href": "/blog" }
]
},
"footer": {