Repository: Sls0n/Prismify Branch: main Commit: 339ac7a56cf4 Files: 155 Total size: 412.6 KB Directory structure: gitextract_iv9ddlq0/ ├── .eslintrc.json ├── .gitignore ├── .vscode/ │ └── settings.json ├── README.md ├── app/ │ ├── (routes)/ │ │ ├── about/ │ │ │ └── page.tsx │ │ ├── articles/ │ │ │ ├── [slug]/ │ │ │ │ ├── loading.tsx │ │ │ │ └── page.tsx │ │ │ ├── page.tsx │ │ │ └── sitemap.ts │ │ └── layout.tsx │ ├── admin/ │ │ ├── layout.tsx │ │ └── write-article/ │ │ ├── blog-form.tsx │ │ └── page.tsx │ ├── api/ │ │ ├── article/ │ │ │ └── post/ │ │ │ └── route.ts │ │ ├── auth/ │ │ │ └── [...nextauth]/ │ │ │ └── route.ts │ │ └── user/ │ │ └── settings/ │ │ └── route.ts │ ├── error.tsx │ ├── layout.tsx │ ├── loading.tsx │ ├── manifest.json │ ├── not-found.tsx │ ├── page.tsx │ ├── robots.ts │ └── sitemap.ts ├── components/ │ ├── articles/ │ │ └── article-card.tsx │ ├── auth-modal.tsx │ ├── clarity-script.tsx │ ├── color-picker.tsx │ ├── editor/ │ │ ├── background-image-canvas.tsx │ │ ├── background-options/ │ │ │ ├── custom-gradient-picker.tsx │ │ │ ├── image-gradient-picker.tsx │ │ │ ├── index.tsx │ │ │ ├── noise-slider.tsx │ │ │ ├── normal-gradient-picker.tsx │ │ │ └── pattern-picker.tsx │ │ ├── browser-frames.tsx │ │ ├── canvas-area.tsx │ │ ├── canvas-options/ │ │ │ ├── canvas-roundness-slider.tsx │ │ │ ├── index.tsx │ │ │ └── resolution-button.tsx │ │ ├── frame-options/ │ │ │ ├── additional-frame-options.tsx │ │ │ ├── frame-picker.tsx │ │ │ └── index.tsx │ │ ├── image-context-menu.tsx │ │ ├── image-options/ │ │ │ ├── add-image-button.tsx │ │ │ ├── index.tsx │ │ │ ├── inset-option.tsx │ │ │ ├── roundness-option.tsx │ │ │ ├── scale-options.tsx │ │ │ └── shadow-settings.tsx │ │ ├── main-image-area.tsx │ │ ├── mobile-view-image-options.tsx │ │ ├── moveable-component.tsx │ │ ├── noise.tsx │ │ ├── perspective-options/ │ │ │ ├── index.tsx │ │ │ └── rotate-options.tsx │ │ ├── position-options/ │ │ │ ├── index.tsx │ │ │ ├── position-control.tsx │ │ │ └── translate-control.tsx │ │ ├── selecto-component.tsx │ │ ├── sidebar-buttons.tsx │ │ ├── sidebar.tsx │ │ ├── text-context-menu.tsx │ │ ├── text-layers.tsx │ │ ├── text-options/ │ │ │ ├── add-text-layer.tsx │ │ │ ├── font-settings.tsx │ │ │ └── index.tsx │ │ ├── tiptap-moveable.tsx │ │ └── undo-redo-buttons.tsx │ ├── export-options.tsx │ ├── footer.tsx │ ├── icons/ │ │ ├── index.tsx │ │ └── info.icon.tsx │ ├── loader.tsx │ ├── navbar.tsx │ ├── navlinks.tsx │ ├── popup-color-picker.tsx │ ├── profile-dialog.tsx │ ├── settings-dialog.tsx │ ├── sign-in-form.tsx │ ├── spinner/ │ │ ├── spinner.module.css │ │ └── spinner.tsx │ ├── ui/ │ │ ├── accordion.tsx │ │ ├── back-button.tsx │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── circular-slider.tsx │ │ ├── context-menu.tsx │ │ ├── dialog.tsx │ │ ├── drawer.tsx │ │ ├── dropdown-menu.tsx │ │ ├── gradient-text.tsx │ │ ├── input.tsx │ │ ├── popover.tsx │ │ ├── scroll-area.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── skeleton.tsx │ │ ├── slider.tsx │ │ ├── spotlight-button.tsx │ │ ├── style/ │ │ │ └── checkbox.module.css │ │ ├── switch.tsx │ │ ├── tabs.tsx │ │ ├── text-area.tsx │ │ ├── text.tsx │ │ ├── theme-button.tsx │ │ ├── toast.tsx │ │ ├── toaster.tsx │ │ └── tooltip.tsx │ └── user-dropdown.tsx ├── components.json ├── docker-compose.yml ├── hooks/ │ ├── canvas-area-hooks/ │ │ ├── use-automatic-aspect-ratio-switcher.ts │ │ ├── use-resize-observer.ts │ │ └── use-screen-size-warning-toast.ts │ ├── use-editor.ts │ ├── use-event-listener.ts │ ├── use-isomorphic-layout-effect.ts │ ├── use-media-query.ts │ ├── use-on-click-outside.ts │ └── use-toast.ts ├── index.d.ts ├── libs/ │ ├── prismadb.ts │ └── validators/ │ ├── article-post-validator.ts │ └── user-settings-validator.ts ├── license.md ├── middleware.ts ├── next.config.js ├── package.json ├── postcss.config.js ├── prettier.config.js ├── prisma/ │ └── schema.prisma ├── providers/ │ └── index.tsx ├── store/ │ ├── use-active-index.ts │ ├── use-background-options.ts │ ├── use-color-extractor.ts │ ├── use-frame-options.ts │ ├── use-image-options.ts │ ├── use-image-quality.ts │ ├── use-moveable.ts │ ├── use-resize-canvas.ts │ └── use-tiptap.ts ├── styles/ │ └── globals.css ├── tailwind.config.js ├── tsconfig.json ├── utils/ │ ├── auth-options.ts │ ├── button-utils.ts │ ├── helper-fns.ts │ ├── presets/ │ │ ├── gradients.ts │ │ ├── qualities.ts │ │ ├── resolutions.ts │ │ ├── shadows.ts │ │ └── solid-colors.ts │ └── tiptap-extensions.ts └── workers/ └── background-removal.worker.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc.json ================================================ { "extends": "next/core-web-vitals" } ================================================ FILE: .gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env .env*.local # vercel .vercel # typescript *.tsbuildinfo next-env.d.ts Checkpoint.tsx ================================================ FILE: .vscode/settings.json ================================================ { "tailwindCSS.experimental.classRegex": [ ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], ["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] ], "editor.fontLigatures": true, "editor.renderWhitespace": "none", "tailwindCSS.includeLanguages": { "html": "html", "javascript": "javascript", "typescript": "typescript", "css": "css" }, "editor.quickSuggestions": { "strings": true }, "typescript.tsserver.experimental.enableProjectDiagnostics": true } ================================================ FILE: README.md ================================================ ## Prismify Prismify is a web app that aims to revitalize & enhance boring images/screenshots. With prismify, you can effortlessly enhance your images/screenshots. ![Prismify](https://github.com/Sls0n/Prismify/assets/102340248/d37df848-59da-4e26-8dbc-451562ef6c55) ## Preview ![image](https://github.com/Sls0n/Prismify/assets/102340248/5e004ca7-d53a-400e-993c-a34cd9bdc829) ![prismify_ss](https://github.com/Sls0n/Prismify/assets/102340248/33323217-59ba-48a1-a494-09fa8658f354) ## Tech Stacks - Typescript - Next - React - Tailwind - Prisma - Zustand ## Perfect lighthouse score ![prismify-render-1704521997749](https://github.com/Sls0n/Prismify/assets/102340248/1f268d3e-cd9b-4d88-88da-247607ccbc45) ## Setup Run the following command to install dependencies and generate the Prisma client: ```bash pnpm run setup ``` ================================================ FILE: app/(routes)/about/page.tsx ================================================ /* eslint-disable react/no-unescaped-entities */ import React from 'react' import BackButton from '@/components/ui/back-button' import { Text } from '@/components/ui/text' import type { Metadata } from 'next' import Image from 'next/image' import { GradientText } from '@/components/ui/gradient-text' export const metadata: Metadata = { title: 'About - Prismify', description: 'Read details about Prismify.', openGraph: { title: 'About - Prismify', description: 'Read details about Prismify.', }, alternates: { canonical: 'https://prismify.vercel.app/about', }, } export default function page() { return (
{/* Title */} About — Prismify
About Prismify

Hey there, fellow entrepreneurs! Ever felt like banging your head against the wall trying to create eye-catching designs for your product or social media? I hear you! But guess what? Prismify's here to save the day.

{' '}

Let's dive into the struggles we all face:

  • Zero Design Skills: Who has time to become a design pro? With Prismify, you don't need to! It's like having a magic wand for your visuals.
  • Clock's Ticking: Time is money, they say. Prismify respects that – swift, efficient, and no-nonsense design tools.
  • Quality Matters: We all want our stuff to look top-notch. Prismify keeps your visuals sharp and professional without the designer price tag.

Now, hold on to your hats! Here's how Prismify makes design a cakewalk:

  • Browser Frame Mockups: Pop your product into a fancy browser frame in a snap!
  • Custom Gradients: Gradient backgrounds that'll make your graphics pop like fireworks! With also adaptive gradients support!
  • Text Tricks & Notes: Jazz up your visuals with text enhancements and handy annotations.
  • Color Magic & 3D Vibes: Prismify's color game and 3D effects? Mind-blowing.
  • Hi-Res & Lightning-Speed Edits: Get crisp, high-res images without the wait. Lightning-fast editing? You got it!

Perks for You:

  • Time Saver: Spend less time stressing over designs, more time making things happen.
  • Pro-Level Designs: No more 'amateur hour' visuals. Prismify gives you that polished, pro look.
  • Versatility Rules: Social media, websites, product shots – Prismify's got your back for any visual need.

Feeling the design itch? Scratch it with Prismify!

"Transform Your Visuals Today! Try Prismify – Your Visual Upgrade Shortcut."

Wrapping it Up: Prismify's not just a tool; it's your secret weapon for killer SaaS visuals and social media. Remember to show, not just tell – demos and examples make all the difference. Get ready to rock those visuals!

) } ================================================ FILE: app/(routes)/articles/[slug]/loading.tsx ================================================ import Loader from '@/components/loader' export default function Loading() { return } ================================================ FILE: app/(routes)/articles/[slug]/page.tsx ================================================ import BackButton from '@/components/ui/back-button' import { Badge } from '@/components/ui/badge' import { Text } from '@/components/ui/text' import prismadb from '@/libs/prismadb' import { generateBadgeVariant, generateFormattedBlogDate, separateCommas, } from '@/utils/helper-fns' import { Calendar } from 'lucide-react' import type { Metadata } from 'next' import Image from 'next/image' import { notFound } from 'next/navigation' import { cache } from 'react' type ArticleProps = { params: Promise<{ slug: string }> } export const revalidate = 3600 // 1 hour export async function generateStaticParams() { const blogs = await prismadb.article.findMany({ select: { slug: true, }, }) return blogs.map((blog) => ({ slug: blog.slug, })) } const getBlog = cache(async (slug: string) => { const blog = await prismadb.article.findFirst({ where: { slug, }, }) return blog }) export async function generateMetadata(props: ArticleProps) { const params = await props.params; const blog = await getBlog(params.slug) if (!blog) { return } const metadata: Metadata = { title: `${blog.title} - Prismify`, description: blog.summary, openGraph: { title: `${blog.title} - Prismify`, description: blog?.summary || 'N/A', locale: 'en_US', url: `https://prismify.vercel.app/articles/${blog.slug}`, type: 'article', images: [ { url: blog.imageUrl ?? 'https://prismify.vercel.app/opengraph-image.jpg', width: 1280, height: 720, alt: blog.title, }, ], }, twitter: { creator: '@xSls0n_007', title: `${blog.title} - Prismify`, description: blog.summary || 'N/A', card: 'summary_large_image', images: [ { url: blog.imageUrl ?? 'https://prismify.vercel.app/opengraph-image.jpg', width: 1280, height: 720, alt: blog.title, }, ], }, alternates: { canonical: `https://prismify.vercel.app/articles/${blog.slug}`, }, publisher: 'Prismify', } return metadata } export default async function ArticlePage(props: ArticleProps) { const params = await props.params; const blog = await getBlog(params.slug) if (!blog) { return notFound() } return (
{/* Time published/updated */}
{/* Title */} {blog.title ?? 'N/A'} {/* Category */} {blog?.category && (
{separateCommas(blog?.category)!.map((category) => ( {category} ))}
)}
{blog?.imageUrl && (
{'cover {/*
Picture
*/}
)} {blog?.content && (
)}
) } ================================================ FILE: app/(routes)/articles/page.tsx ================================================ import prismadb from '@/libs/prismadb' import { Text } from '@/components/ui/text' import ArticleCard from '@/components/articles/article-card' import { formatDate, separateCommas } from '@/utils/helper-fns' import type { Metadata } from 'next' import { unstable_cache as cache } from 'next/cache' export const metadata: Metadata = { title: 'Articles - Prismify', description: 'Read latest articles from Prismify.', openGraph: { title: 'Articles - Prismify', description: 'Read latest articles from Prismify.', type: 'article', url: 'https://prismify.vercel.app/articles', }, alternates: { canonical: 'https://prismify.vercel.app/articles', }, } const getCachedArticles = cache( async () => { return await prismadb.article.findMany({ orderBy: { createdAt: 'desc', }, }) }, ['articles'], { revalidate: 60 * 60, // 1 hour tags: ['articles'], } ) export default async function Article() { const articles = await getCachedArticles() return (
Articles Read latest articles from Prismify
) } ================================================ FILE: app/(routes)/articles/sitemap.ts ================================================ import prismadb from '@/libs/prismadb' import { MetadataRoute } from 'next' export default async function sitemap(): Promise { const response = await prismadb.article.findMany({ select: { slug: true, updatedAt: true, }, }) const sitemapUrls: MetadataRoute.Sitemap = response?.map((p) => { return { url: `https://prismify.vercel.app/articles/${p.slug}`, lastModified: new Date(p.updatedAt), changeFrequency: 'monthly', } }) return sitemapUrls } ================================================ FILE: app/(routes)/layout.tsx ================================================ import Footer from '@/components/footer' import React from 'react' export default function AdminLayout({ children, }: { children: React.ReactNode }) { return (
{children}
) } ================================================ FILE: app/admin/layout.tsx ================================================ import React from 'react' export default function AdminLayout({ children, }: { children: React.ReactNode }) { return (
{children}
) } ================================================ FILE: app/admin/write-article/blog-form.tsx ================================================ 'use client' import { EditorProvider, useCurrentEditor } from '@tiptap/react' import { Bold, Code, Heading, ImagePlus, Italic, Link, List, ListOrdered, Quote, Space, Strikethrough, Underline, } from 'lucide-react' import { useCallback, useEffect, useState } from 'react' // import { FileUpload } from '@/components/file-upload' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { cn } from '@/utils/button-utils' import { extensions } from '@/utils/tiptap-extensions' import { useMutation } from '@tanstack/react-query' import { Text } from '@/components/ui/text' import { useTiptap } from '@/store/use-tiptap' import axios, { AxiosError } from 'axios' import { toast } from '@/hooks/use-toast' import { Textarea } from '@/components/ui/text-area' export default function BlogForm() { const [blogOutput, setBlogOutput] = useState('') const [title, setTitle] = useState('') const [summary, setSummary] = useState('') const [category, setCategory] = useState('') const [slug, setSlug] = useState('') const [mainBlogImg, setMainBlogImg] = useState('') const { mutate: publishBlog, isLoading: isPublishing } = useMutation({ mutationFn: async () => { const res = await axios.post('/api/article/post', { title, summary, category, slug, imageUrl: mainBlogImg, content: blogOutput, }) return res }, onSuccess: () => { toast({ title: 'Blog published successfully', }) }, onError: (err) => { if (err instanceof AxiosError) { if (err.response?.status === 400) { toast({ title: err?.response?.data?.[0]?.message, variant: 'destructive', }) } if (err.response?.status === 401) { toast({ title: 'You are not authorized to publish this blog', description: 'Please login to publish this blog', variant: 'destructive', }) } if (err.response?.status === 409) { toast({ title: 'Blog with this slug already exists', description: 'Please modify the slug a bit.', variant: 'destructive', }) } if (err.response?.status === 500) { toast({ title: 'Something went wrong', description: 'Please try again later', variant: 'destructive', }) } } console.log(err) }, }) const publishBlogHandler = () => { publishBlog() } const slugifyTitle = async (title: string) => { const slugify = (await import('slugify')).default setSlug(slugify(title, { lower: true, strict: true })) } return ( <>
Title { setTitle(e.target.value) slugifyTitle(e.target.value) }} required />
Summary