From 84373f40eadf9f09fe96dd0e4274eec80678483e Mon Sep 17 00:00:00 2001 From: Naymur Rahman <66328660+naymurdev@users.noreply.github.com> Date: Wed, 25 Feb 2026 02:22:40 +0600 Subject: [PATCH 01/24] feat: add unified fullscreen tool playground shell --- app/(tools)/background-snippets/page.tsx | 20 ++- app/(tools)/clip-paths/page.tsx | 14 +- app/(tools)/color-lab/page.tsx | 18 ++- app/(tools)/layout.tsx | 8 +- app/(tools)/mesh-gradients/page.tsx | 14 +- app/(tools)/shadows/page.tsx | 12 +- app/(tools)/svg-line-draw/page.tsx | 12 +- components/common/tool-playground-shell.tsx | 164 ++++++++++++++++++++ 8 files changed, 235 insertions(+), 27 deletions(-) create mode 100644 components/common/tool-playground-shell.tsx diff --git a/app/(tools)/background-snippets/page.tsx b/app/(tools)/background-snippets/page.tsx index 8acb861..077a53c 100644 --- a/app/(tools)/background-snippets/page.tsx +++ b/app/(tools)/background-snippets/page.tsx @@ -2,6 +2,7 @@ import BackgroundSnippetsGenerator from "@/components/view/background-snippets"; import { siteConfig } from "@/lib/utils"; import type { Metadata } from "next"; import React from "react"; +import { ToolPlaygroundShell } from "@/components/common/tool-playground-shell"; export const metadata: Metadata = { title: "Background Snippets Generator", description: @@ -56,11 +57,18 @@ export const metadata: Metadata = { }, }; function page() { - return ( - <> - - - ); + return ( + + + + ); } -export default page; +export default page; \ No newline at end of file diff --git a/app/(tools)/clip-paths/page.tsx b/app/(tools)/clip-paths/page.tsx index a8f080b..79db408 100644 --- a/app/(tools)/clip-paths/page.tsx +++ b/app/(tools)/clip-paths/page.tsx @@ -2,6 +2,7 @@ import ClipPathGenerator from "@/components/view/clip-path"; import { siteConfig } from "@/lib/utils"; import type { Metadata } from "next"; import React from "react"; +import { ToolPlaygroundShell } from "@/components/common/tool-playground-shell"; export const metadata: Metadata = { title: "SVG Clip-Path Generator", description: @@ -52,10 +53,17 @@ export const metadata: Metadata = { function page() { return ( - <> + - + ); } -export default page; +export default page; \ No newline at end of file diff --git a/app/(tools)/color-lab/page.tsx b/app/(tools)/color-lab/page.tsx index 982627a..0a7e766 100644 --- a/app/(tools)/color-lab/page.tsx +++ b/app/(tools)/color-lab/page.tsx @@ -3,6 +3,7 @@ import ColorConverter from "@/components/view/colors"; import { siteConfig } from "@/lib/utils"; import type { Metadata } from "next"; import React, { Suspense } from "react"; +import { ToolPlaygroundShell } from "@/components/common/tool-playground-shell"; export const metadata: Metadata = { title: "Color Lab – Generate Color Palettes, Convert Codes & Build Shadcn Themes", @@ -85,12 +86,17 @@ const PageLoading = () => { }; function page() { return ( - <> - }> - - - + + + ); } -export default page; +export default page; \ No newline at end of file diff --git a/app/(tools)/layout.tsx b/app/(tools)/layout.tsx index a96df1a..1f92d2d 100644 --- a/app/(tools)/layout.tsx +++ b/app/(tools)/layout.tsx @@ -3,12 +3,10 @@ import type React from "react"; function Toolslayout({ children }: { children: React.ReactNode }) { return ( - <> +
-
- {children} -
- +
{children}
+
); } diff --git a/app/(tools)/mesh-gradients/page.tsx b/app/(tools)/mesh-gradients/page.tsx index 2e76fa3..34f9496 100644 --- a/app/(tools)/mesh-gradients/page.tsx +++ b/app/(tools)/mesh-gradients/page.tsx @@ -2,6 +2,7 @@ import { ShaderGradientGenerator } from "@/components/view/mesh-gradient"; import { siteConfig } from "@/lib/utils"; import type { Metadata } from "next"; import React from "react"; +import { ToolPlaygroundShell } from "@/components/common/tool-playground-shell"; export const metadata: Metadata = { title: "Mesh-Gradient Generator", description: @@ -52,10 +53,17 @@ export const metadata: Metadata = { function page() { return ( - <> + - + ); } -export default page; +export default page; \ No newline at end of file diff --git a/app/(tools)/shadows/page.tsx b/app/(tools)/shadows/page.tsx index 593a017..aa87481 100644 --- a/app/(tools)/shadows/page.tsx +++ b/app/(tools)/shadows/page.tsx @@ -1,4 +1,5 @@ import ShadowGenerator from "@/components/view/shadow"; +import { ToolPlaygroundShell } from "@/components/common/tool-playground-shell"; import { siteConfig } from "@/lib/utils"; import type { Metadata } from "next"; import React from "react"; @@ -53,9 +54,16 @@ export const metadata: Metadata = { function page() { return ( - <> + - + ); } diff --git a/app/(tools)/svg-line-draw/page.tsx b/app/(tools)/svg-line-draw/page.tsx index 1727e1c..57832a4 100644 --- a/app/(tools)/svg-line-draw/page.tsx +++ b/app/(tools)/svg-line-draw/page.tsx @@ -1,4 +1,5 @@ import SVGLineDrawGenerator from "@/components/view/svg-line-draw"; +import { ToolPlaygroundShell } from "@/components/common/tool-playground-shell"; import { siteConfig } from "@/lib/utils"; import type { Metadata } from "next"; import React, { Suspense } from "react"; @@ -71,11 +72,18 @@ const PageLoading = () => { }; function page() { return ( - <> + }> - + ); } diff --git a/components/common/tool-playground-shell.tsx b/components/common/tool-playground-shell.tsx new file mode 100644 index 0000000..7bab65b --- /dev/null +++ b/components/common/tool-playground-shell.tsx @@ -0,0 +1,164 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { cn } from "@/lib/utils"; +import { Copy, Download, EllipsisVertical, RotateCcw } from "lucide-react"; +import { useState } from "react"; +import toast from "react-hot-toast"; + +interface ToolPlaygroundShellProps { + title: string; + description: string; + examples: string[]; + docs: string; + exportLabel: string; + exportCode: string; + children: React.ReactNode; +} + +export function ToolPlaygroundShell({ + title, + description, + examples, + docs, + exportLabel, + exportCode, + children, +}: ToolPlaygroundShellProps) { + const [topTab, setTopTab] = useState("playground"); + const [drawerOpen, setDrawerOpen] = useState(false); + const [filter, setFilter] = useState(""); + + const copyCode = async () => { + await navigator.clipboard.writeText(exportCode); + toast.success("Copied"); + }; + + return ( +
+
+
+

{title}

+

{description}

+
+ + + + Playground + Examples + Docs + + + +
+ + + +
+ + + + + + + setDrawerOpen(true)}> + Export code + + Copy + location.reload()}>Reset + + +
+ +
+ + +
+
+
{children}
+
+
+
+ +
+
+

Export

+ +
+
+

{exportLabel}

+
{exportCode}
+
+ + +
+
+
+
+ ); +} From f59885fa9fc245e420913bb97be2252705b6fdec Mon Sep 17 00:00:00 2001 From: Naymur Rahman <66328660+naymurdev@users.noreply.github.com> Date: Wed, 25 Feb 2026 02:41:17 +0600 Subject: [PATCH 02/24] fix: make svg line draw a full-page playground view --- app/(tools)/layout.tsx | 18 ++- app/(tools)/svg-line-draw/page.tsx | 21 +-- components/view/svg-line-draw/index.tsx | 188 ++---------------------- 3 files changed, 37 insertions(+), 190 deletions(-) diff --git a/app/(tools)/layout.tsx b/app/(tools)/layout.tsx index 1f92d2d..4ac2fa7 100644 --- a/app/(tools)/layout.tsx +++ b/app/(tools)/layout.tsx @@ -1,11 +1,25 @@ +"use client"; + import ToolsHeader from "@/components/common/tools-header"; +import { usePathname } from "next/navigation"; import type React from "react"; function Toolslayout({ children }: { children: React.ReactNode }) { + const pathname = usePathname(); + const isLineDraw = pathname === "/svg-line-draw"; + return (
- -
{children}
+ {!isLineDraw && } +
+ {children} +
); } diff --git a/app/(tools)/svg-line-draw/page.tsx b/app/(tools)/svg-line-draw/page.tsx index 57832a4..cbd44ce 100644 --- a/app/(tools)/svg-line-draw/page.tsx +++ b/app/(tools)/svg-line-draw/page.tsx @@ -1,8 +1,8 @@ import SVGLineDrawGenerator from "@/components/view/svg-line-draw"; -import { ToolPlaygroundShell } from "@/components/common/tool-playground-shell"; import { siteConfig } from "@/lib/utils"; import type { Metadata } from "next"; import React, { Suspense } from "react"; + export const metadata: Metadata = { title: "SVG Line Draw – Sketch & Animate Hand-Drawn Lines for the Web", description: @@ -39,7 +39,7 @@ export const metadata: Metadata = { siteName: siteConfig.name, images: [ { - url: siteConfig.lineDrawOgImage, // Replace with relevant OG image + url: siteConfig.lineDrawOgImage, width: 1200, height: 630, alt: `SVG Line Draw by ${siteConfig.name}`, @@ -55,6 +55,7 @@ export const metadata: Metadata = { creator: "@naymur_dev", }, }; + const PageLoading = () => { return ( <> @@ -70,20 +71,12 @@ const PageLoading = () => { ); }; + function page() { return ( - - }> - - - + }> + + ); } diff --git a/components/view/svg-line-draw/index.tsx b/components/view/svg-line-draw/index.tsx index b326058..7bb51fd 100644 --- a/components/view/svg-line-draw/index.tsx +++ b/components/view/svg-line-draw/index.tsx @@ -32,10 +32,8 @@ import { cn } from "@/lib/utils"; import { ScrollArea } from "@radix-ui/react-scroll-area"; import { TabsTrigger } from "@radix-ui/react-tabs"; import { - Check, ChevronDown, ChevronUp, - ChevronsDown, CodeIcon, Copy, Edit2, @@ -44,7 +42,6 @@ import { PencilLine, Play, RefreshCcw, - RefreshCw, Save, Trash2, X, @@ -59,7 +56,7 @@ import { CopyCode } from "../mesh-gradient/copy-code"; import { AnimateSvg } from "./animate-svg"; import { CodePreview } from "./code-preview"; import { CustomLineInput } from "./custom-line-input"; -import { compoentCode, examplesSvgPath } from "./data"; +import { compoentCode } from "./data"; import { DrawingCanvas } from "./drawing-canvas"; import { SavedEditedPathsTab } from "./edited-paths"; import { ExamplePaths } from "./example-paths"; @@ -85,7 +82,7 @@ type AnimationSettings = { function SVGLineDrawGenerator() { const { theme } = useTheme(); const [activePresets, setActivePresets] = useQueryState("presets"); - const [exampleViewBox, setExampleViewBox] = useQueryState("viewBox", { + const [exampleViewBox, _setExampleViewBox] = useQueryState("viewBox", { defaultValue: "0 0 250 100", }); const [editPath, setEditPath] = useQueryState( @@ -96,12 +93,7 @@ function SVGLineDrawGenerator() { "customDrawLine", parseAsBoolean.withDefault(false), ); - const [viewAll, setViewAll] = useState(false); const [currentPath, setCurrentPath] = useState(""); - const [animationKeys, setAnimationKeys] = useState>( - {}, - ); - const [copiedIndex, setCopiedIndex] = useState(null); const [savedPaths, _setSavedPaths] = useState([]); const [previewKey, setPreviewKey] = useState(0); const [_showEditor, setShowEditor] = useState(false); @@ -193,13 +185,6 @@ function SVGLineDrawGenerator() { // setShowEditor(true); // }; - const reloadAnimation = (index: number) => { - setAnimationKeys((prev) => ({ - ...prev, - [index]: (prev[index] || 0) + 1, - })); - }; - // Generate code for the current animation const generateCode = () => { const pathsCode = @@ -246,161 +231,16 @@ function SVGLineDrawGenerator() { } }; - const copyDynamicCode = (index: number) => { - const example = examplesSvgPath[index]; - - // Generate the full component code - const code = ``; - - navigator.clipboard.writeText(code).then(() => { - setCopiedIndex(index); - toast.success("Component code copied", { - description: `${example.name} component code copied to clipboard`, - }); - - // Reset copied state after 2 seconds - setTimeout(() => { - setCopiedIndex(null); - }, 2000); - }); - }; return ( -
- - +
- {!viewAll && ( -
- )} - - {(viewAll ? examplesSvgPath : examplesSvgPath.slice(0, 10)).map( - (example, index) => ( -
-
{ - setCurrentPath(example.path); - setExampleViewBox(example.viewBox); - setActivePresets(example.id); - }} - onKeyUp={() => { - setCurrentPath(example.path); - setExampleViewBox(example.viewBox); - setActivePresets(example.id); - }} - onKeyDown={() => { - setCurrentPath(example.path); - setExampleViewBox(example.viewBox); - setActivePresets(example.id); - }} - onKeyPress={() => { - setCurrentPath(example.path); - setExampleViewBox(example.viewBox); - setActivePresets(example.id); - }} - > - -

- {example.name} -

-
- -
- - -
-
- ), - )} -
-
{/* Left Column - Examples and Animation Settings */}