feat(portfolio): migrate to nextJS
This commit is contained in:
parent
2d600d724d
commit
953379b5e8
42 changed files with 476 additions and 2761 deletions
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -41,10 +41,10 @@
|
|||
"katex",
|
||||
"kunukn",
|
||||
"Librewolf",
|
||||
"linaria",
|
||||
"mhchem",
|
||||
"microflash",
|
||||
"nodedotjs",
|
||||
"nojs",
|
||||
"noopener",
|
||||
"noto",
|
||||
"planetscale",
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"es2020": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"react-refresh",
|
||||
"prettier",
|
||||
"simple-import-sort"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"react-refresh/only-export-components": "warn",
|
||||
"simple-import-sort/imports": "error",
|
||||
"simple-import-sort/exports": "error"
|
||||
}
|
||||
}
|
8
apps/portfolio/.eslintrc.cjs
Normal file
8
apps/portfolio/.eslintrc.cjs
Normal file
|
@ -0,0 +1,8 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
extends: ["next/core-web-vitals", "@developomp-site/eslint-config"],
|
||||
rules: {
|
||||
"react-hooks/exhaustive-deps": "off",
|
||||
"react/no-unescaped-entities": "off",
|
||||
},
|
||||
}
|
35
apps/portfolio/.gitignore
vendored
Normal file
35
apps/portfolio/.gitignore
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
# 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*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
|
@ -1,26 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<meta name="description" content="developomp's portfolio" />
|
||||
<title>portfolio</title>
|
||||
|
||||
<!-- OpenGraph -->
|
||||
<meta property="og:title" content="Portfolio" />
|
||||
<meta property="og:site_name" content="developomp's portfolio" />
|
||||
<meta property="og:description" content="developomp's Portfolio" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://portfolio.developomp.com" />
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://portfolio.developomp.com/favicon.svg"
|
||||
/>
|
||||
</head>
|
||||
<body class="dark">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
11
apps/portfolio/next.config.js
Normal file
11
apps/portfolio/next.config.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: "export",
|
||||
distDir: "dist",
|
||||
images: { unoptimized: true },
|
||||
experimental: {
|
||||
externalDir: true,
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
|
@ -1,52 +1,47 @@
|
|||
{
|
||||
"name": "@developomp-site/portfolio",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview",
|
||||
"clean": "rm -rf .turbo node_modules dist vite.config.ts.timestamp-*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/noto-sans-kr": "^5.0.5",
|
||||
"@fontsource/source-code-pro": "^5.0.5",
|
||||
"@linaria/core": "^4.2.10",
|
||||
"@linaria/react": "^4.3.8",
|
||||
"hoofd": "^1.7.0",
|
||||
"react": "^18.2.0",
|
||||
"react-collapse": "^5.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"wouter": "^2.11.0"
|
||||
"dev": "open-cli http://localhost:5174 && next dev -p 5174",
|
||||
"build": "next build",
|
||||
"lint": "next lint",
|
||||
"clean": "rm -rf .next .turbo build node_modules"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@developomp-site/blog": "workspace:*",
|
||||
"@developomp-site/content": "workspace:*",
|
||||
"@developomp-site/eslint-config": "workspace:*",
|
||||
"@developomp-site/prettier-config": "workspace:*",
|
||||
"@developomp-site/tailwind-config": "workspace:*",
|
||||
"@linaria/babel-preset": "^4.4.5",
|
||||
"@linaria/vite": "^4.2.11",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
||||
"@typescript-eslint/parser": "^5.61.0",
|
||||
"@vitejs/plugin-react": "^4.0.2",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "^8.44.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.2",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"postcss": "^8.4.25",
|
||||
"@fontsource/noto-sans-kr": "^5.0.5",
|
||||
"@fontsource/source-code-pro": "^5.0.5",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@kunukn/react-collapse": "^2.2.10",
|
||||
"@types/highlight.js": "^10.1.0",
|
||||
"@types/katex": "^0.16.2",
|
||||
"@types/node": "20.4.5",
|
||||
"@types/react-collapse": "^5.0.1",
|
||||
"@types/react-dom": "18.2.7",
|
||||
"@types/react": "18.2.17",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
||||
"@typescript-eslint/parser": "^6.2.0",
|
||||
"autoprefixer": "10.4.14",
|
||||
"eslint-config-next": "13.4.12",
|
||||
"eslint": "8.45.0",
|
||||
"highlight.js": "^11.8.0",
|
||||
"katex": "^0.16.8",
|
||||
"next": "13.4.12",
|
||||
"open-cli": "^7.2.0",
|
||||
"postcss": "8.4.27",
|
||||
"prettier-plugin-tailwindcss": "^0.4.1",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-tailwindcss": "^0.3.0",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"typescript": "^5.1.6",
|
||||
"vite": "^4.4.2",
|
||||
"vite-plugin-dynamic-import": "^1.5.0",
|
||||
"vite-tsconfig-paths": "^4.2.0"
|
||||
"react-collapse": "^5.1.1",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "18.2.0",
|
||||
"tailwindcss": "3.3.3",
|
||||
"typescript": "5.1.6"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default {
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
|
|
BIN
apps/portfolio/public/nojs.avif
Normal file
BIN
apps/portfolio/public/nojs.avif
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
|
@ -1,41 +0,0 @@
|
|||
import { useTitleTemplate } from "hoofd"
|
||||
import { type FC } from "react"
|
||||
import { Route, Switch } from "wouter"
|
||||
|
||||
import Header from "@/components/Header"
|
||||
import Home from "@/routes/Home"
|
||||
import Loading from "@/routes/Loading"
|
||||
import NotFound from "@/routes/NotFound"
|
||||
import Project from "@/routes/Project"
|
||||
|
||||
const App: FC = () => {
|
||||
useTitleTemplate("Portfolio | %s")
|
||||
// no need to set title and meta tags here
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<div className="mb-10 mt-20 w-full max-w-screen-md px-4">
|
||||
<Switch>
|
||||
<Route path="/">
|
||||
<Home />
|
||||
</Route>
|
||||
|
||||
<Route path="/project/:id">
|
||||
<Project />
|
||||
</Route>
|
||||
|
||||
<Route path="/loading">
|
||||
<Loading />
|
||||
</Route>
|
||||
|
||||
<Route>
|
||||
<NotFound />
|
||||
</Route>
|
||||
</Switch>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
|
@ -11,11 +11,7 @@ html {
|
|||
}
|
||||
|
||||
body {
|
||||
@apply flex min-h-screen w-full flex-col items-center;
|
||||
}
|
||||
|
||||
#root {
|
||||
@apply m-0 flex h-full w-full scroll-m-16 flex-col items-center p-0;
|
||||
@apply m-0 flex h-full min-h-screen w-full scroll-m-16 flex-col items-center p-0;
|
||||
}
|
||||
|
||||
ul,
|
85
apps/portfolio/src/app/layout.tsx
Normal file
85
apps/portfolio/src/app/layout.tsx
Normal file
|
@ -0,0 +1,85 @@
|
|||
import "@fortawesome/fontawesome-svg-core/styles.css"
|
||||
import "@fontsource/noto-sans-kr/400.css"
|
||||
import "@fontsource/noto-sans-kr/700.css"
|
||||
import "@fontsource/source-code-pro"
|
||||
import "@developomp-site/blog/src/styles/anchor.scss"
|
||||
import "@developomp-site/blog/src/styles/blockQuote.scss"
|
||||
import "@developomp-site/blog/src/styles/button.scss"
|
||||
import "@developomp-site/blog/src/styles/callout.scss"
|
||||
import "@developomp-site/blog/src/styles/checkbox.scss"
|
||||
import "@developomp-site/blog/src/styles/code.scss"
|
||||
import "@developomp-site/blog/src/styles/colorChip.scss"
|
||||
import "@developomp-site/blog/src/styles/heading.scss"
|
||||
import "@developomp-site/blog/src/styles/hr.scss"
|
||||
import "@developomp-site/blog/src/styles/img.scss"
|
||||
import "@developomp-site/blog/src/styles/katex.scss"
|
||||
import "@developomp-site/blog/src/styles/kbd.scss"
|
||||
import "@developomp-site/blog/src/styles/list.scss"
|
||||
import "@developomp-site/blog/src/styles/mark.scss"
|
||||
import "@developomp-site/blog/src/styles/scrollbar.scss"
|
||||
import "@developomp-site/blog/src/styles/subSup.scss"
|
||||
import "@developomp-site/blog/src/styles/table.scss"
|
||||
import "./global.scss"
|
||||
|
||||
import { type Metadata } from "next"
|
||||
import Image from "next/image"
|
||||
|
||||
import Header from "@/components/Header"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL("https://portfolio.developomp.com"),
|
||||
title: {
|
||||
template: "pomp's portfolio | %s",
|
||||
default: "",
|
||||
},
|
||||
description: "developomp's portfolio",
|
||||
openGraph: {
|
||||
title: "pomp's portfolio",
|
||||
siteName: "developomp's portfolio",
|
||||
description: "developomp's portfolio",
|
||||
type: "website",
|
||||
url: "https://portfolio.developomp.com",
|
||||
images: "https://portfolio.developomp.com/favicon.svg",
|
||||
},
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" className="dark">
|
||||
<head>
|
||||
<link
|
||||
rel="shortcut icon"
|
||||
type="image/svg+xml"
|
||||
href="/favicon.svg"
|
||||
/>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>
|
||||
<figure>
|
||||
<Image
|
||||
src="/img/nojs.avif"
|
||||
height={500}
|
||||
width={544}
|
||||
alt="No javascript?"
|
||||
/>
|
||||
<figcaption>
|
||||
Image compressed down to 4.5kB because you probably
|
||||
have potato internet :D
|
||||
</figcaption>
|
||||
</figure>
|
||||
</noscript>
|
||||
|
||||
<Header />
|
||||
<div className="mb-10 mt-20 w-full max-w-screen-md px-4">
|
||||
{children}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
21
apps/portfolio/src/app/not-found.tsx
Normal file
21
apps/portfolio/src/app/not-found.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { type Metadata } from "next"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL("https://portfolio.developomp.com"),
|
||||
title: "404",
|
||||
openGraph: {
|
||||
title: "pomp's portfolio | Page Not Found",
|
||||
},
|
||||
}
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<>
|
||||
<h1 className="w-fit px-4 py-2 text-5xl dark:bg-dark-text-default dark:text-dark-ui-bg">
|
||||
404
|
||||
</h1>
|
||||
|
||||
<h2 className="glitch layers text-5xl">Page Not Found</h2>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -1,9 +1,6 @@
|
|||
import "./style.scss"
|
||||
|
||||
import portfolio from "@developomp-site/content/dist/portfolio.json"
|
||||
import type { PortfolioProject } from "@developomp-site/content/src/types/types"
|
||||
import { useMeta, useTitle } from "hoofd"
|
||||
import { type FC } from "react"
|
||||
import { Metadata } from "next"
|
||||
|
||||
import Badge from "@/components/Badge"
|
||||
import ProjectCard from "@/components/ProjectCard"
|
||||
|
@ -27,10 +24,12 @@ for (const projectID in portfolio.projects) {
|
|||
)
|
||||
}
|
||||
|
||||
const Home: FC = () => {
|
||||
useTitle("Home")
|
||||
useMeta({ property: "og:title", content: "Home" })
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL("https://blog.developomp.com"),
|
||||
title: "pomp's portfolio | Home",
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<h1 className="mb-8">developomp's Portfolio</h1>
|
||||
|
@ -40,5 +39,3 @@ const Home: FC = () => {
|
|||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
97
apps/portfolio/src/app/project/[id]/page.tsx
Normal file
97
apps/portfolio/src/app/project/[id]/page.tsx
Normal file
|
@ -0,0 +1,97 @@
|
|||
import "./style.scss"
|
||||
|
||||
import Toc from "@developomp-site/blog/src/app/[category]/[[...slug]]/Toc"
|
||||
import portfolio from "@developomp-site/content/dist/portfolio.json"
|
||||
import { faGithub } from "@fortawesome/free-brands-svg-icons"
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||
import { type Metadata } from "next"
|
||||
import Link from "next/link"
|
||||
|
||||
import Badge from "@/components/Badge"
|
||||
|
||||
interface Data {
|
||||
title: string
|
||||
toc?: string
|
||||
content: string
|
||||
|
||||
image: string // image url
|
||||
overview: string
|
||||
badges: string[]
|
||||
repo: string
|
||||
}
|
||||
|
||||
interface Params {
|
||||
id: keyof typeof portfolio.projects
|
||||
}
|
||||
|
||||
interface Props {
|
||||
params: Params
|
||||
}
|
||||
|
||||
async function getData(id: keyof typeof portfolio.projects): Promise<Data> {
|
||||
const content = await import(
|
||||
`@developomp-site/content/dist/content/projects/${id}.json`
|
||||
)
|
||||
const data = portfolio.projects[id]
|
||||
|
||||
return {
|
||||
content: content.content,
|
||||
toc: content.toc,
|
||||
title: data.name,
|
||||
image: data.image,
|
||||
overview: data.overview,
|
||||
badges: data.badges,
|
||||
repo: data.repo,
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateStaticParams(): Promise<Params[]> {
|
||||
return (
|
||||
Object.keys(portfolio.projects) as (keyof typeof portfolio.projects)[]
|
||||
).map((id) => ({ id }))
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const data = await getData(params.id)
|
||||
return {
|
||||
metadataBase: new URL("https://portfolio.developomp.com"),
|
||||
title: `pomp's portfolio | ${data.title}`,
|
||||
openGraph: {
|
||||
title: `pomp's portfolio | ${data.title}`,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default async function Project({ params }: Props) {
|
||||
const data = await getData(params.id)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="mb-4 text-4xl">{data.title}</h1>
|
||||
<Link
|
||||
href={data.repo}
|
||||
className="text-dark-text-default duration-100 hover:text-gray-400"
|
||||
>
|
||||
<FontAwesomeIcon className="h-12" icon={faGithub} />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-wrap">
|
||||
{data.badges.map((slug) => {
|
||||
return <Badge key={slug} slug={slug} />
|
||||
})}
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
<Toc data={data.toc} />
|
||||
|
||||
{/* page content */}
|
||||
<div
|
||||
className="project-description"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: data.content,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
"use client"
|
||||
|
||||
import "./style.scss"
|
||||
|
||||
import { type Badge as BadgeType } from "@developomp-site/content/src/types/types"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Link from "next/link"
|
||||
import { type FC } from "react"
|
||||
import { Link } from "wouter"
|
||||
|
||||
const Header: FC = () => {
|
||||
return (
|
||||
|
@ -7,9 +7,10 @@ const Header: FC = () => {
|
|||
<div className="my-0 flex h-16 w-full max-w-5xl items-center">
|
||||
<Link
|
||||
className="flex items-center"
|
||||
to="/"
|
||||
href="/"
|
||||
aria-label="homepage"
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
className="m-4 block h-10 cursor-pointer"
|
||||
src="/favicon.svg"
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import "./style.scss"
|
||||
|
||||
import { type FC } from "react"
|
||||
|
||||
const Loading: FC = () => {
|
||||
return (
|
||||
<div className="loading flex animate-bounce flex-col items-center justify-center text-center">
|
||||
<h2 className="text-3xl">Loading...</h2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Loading
|
|
@ -1,3 +0,0 @@
|
|||
import Loading from "./Loading"
|
||||
|
||||
export default Loading
|
|
@ -1,18 +0,0 @@
|
|||
.loading {
|
||||
@apply transition-opacity ease-in;
|
||||
|
||||
animation: fade-in 2s;
|
||||
@keyframes fade-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
25% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import "./style.scss"
|
||||
|
||||
import { PortfolioProject } from "@developomp-site/content/src/types/types"
|
||||
import { type FC, useEffect, useState } from "react"
|
||||
import Link from "next/link"
|
||||
|
||||
import Badge from "@/components/Badge"
|
||||
|
||||
|
@ -10,26 +10,23 @@ interface ProjectCardProps {
|
|||
project: PortfolioProject
|
||||
}
|
||||
|
||||
const ProjectCard: FC<ProjectCardProps> = ({ projectID, project }) => {
|
||||
const [badges, setBadges] = useState<JSX.Element[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
setBadges(
|
||||
project.badges.map((badge) => <Badge key={badge} slug={badge} />)
|
||||
)
|
||||
}, [project.badges])
|
||||
|
||||
export default function ProjectCard({ projectID, project }: ProjectCardProps) {
|
||||
return (
|
||||
<a href={`/project/${projectID}`}>
|
||||
<Link href={`/project/${projectID}`}>
|
||||
<div className="project">
|
||||
<h2>{project.name}</h2>
|
||||
<h2 className="mb-4">{project.name}</h2>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
className="mb-4 w-full object-cover"
|
||||
src={project.image}
|
||||
alt="project thumbnail"
|
||||
/>
|
||||
|
||||
<div className="flex flex-wrap">{badges}</div>
|
||||
<div className="flex flex-wrap">
|
||||
{project.badges.map((badge) => (
|
||||
<Badge key={badge} slug={badge} />
|
||||
))}
|
||||
</div>
|
||||
<hr className="my-1" />
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
|
@ -37,8 +34,6 @@ const ProjectCard: FC<ProjectCardProps> = ({ projectID, project }) => {
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProjectCard
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
import "./style.scss"
|
||||
|
||||
import { styled } from "@linaria/react"
|
||||
import { type FC, useState } from "react"
|
||||
import { Collapse } from "react-collapse"
|
||||
|
||||
const StyledTocToggleButton = styled.button`
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
text-align: left;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
`
|
||||
|
||||
const StyledCollapseContainer = styled.div`
|
||||
* {
|
||||
transition: height 200ms ease-out;
|
||||
}
|
||||
`
|
||||
|
||||
const Toc: FC<{ data?: string }> = (props) => {
|
||||
const [isTocOpened, setIsTocOpened] = useState(false)
|
||||
|
||||
if (!props.data) return <></>
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledTocToggleButton
|
||||
className="text-light-text-high-contrast dark:text-dark-text-high-contrast"
|
||||
onClick={() => {
|
||||
setIsTocOpened((prev) => !prev)
|
||||
}}
|
||||
>
|
||||
<strong className="flex items-center justify-center gap-1 fill-light-text-high-contrast dark:fill-dark-text-high-contrast">
|
||||
Table of Contents
|
||||
{isTocOpened ? (
|
||||
// Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc.
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="1em"
|
||||
viewBox="0 0 320 512"
|
||||
>
|
||||
<path d="M182.6 137.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8H288c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-128-128z" />
|
||||
</svg>
|
||||
) : (
|
||||
// Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc.
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="1em"
|
||||
viewBox="0 0 320 512"
|
||||
>
|
||||
<path d="M137.4 374.6c12.5 12.5 32.8 12.5 45.3 0l128-128c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8L32 192c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9l128 128z" />
|
||||
</svg>
|
||||
)}
|
||||
</strong>
|
||||
</StyledTocToggleButton>
|
||||
<StyledCollapseContainer>
|
||||
<Collapse isOpened={isTocOpened}>
|
||||
<div dangerouslySetInnerHTML={{ __html: props.data }} />
|
||||
</Collapse>
|
||||
</StyledCollapseContainer>
|
||||
<hr />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Toc
|
|
@ -1,3 +0,0 @@
|
|||
import Toc from "./Toc"
|
||||
|
||||
export default Toc
|
|
@ -1,18 +0,0 @@
|
|||
import "@fontsource/noto-sans-kr/400.css"
|
||||
import "@fontsource/noto-sans-kr/700.css"
|
||||
import "@fontsource/source-code-pro"
|
||||
import "./index.scss"
|
||||
import "@developomp-site/blog/src/styles/anchor.scss"
|
||||
import "@developomp-site/blog/src/styles/callout.scss"
|
||||
import "@developomp-site/blog/src/styles/colorChip.scss"
|
||||
import "@developomp-site/blog/src/styles/heading.scss"
|
||||
import "@developomp-site/blog/src/styles/img.scss"
|
||||
import "@developomp-site/blog/src/styles/mark.scss"
|
||||
|
||||
import ReactDOM from "react-dom/client"
|
||||
|
||||
import App from "./App.tsx"
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<App />
|
||||
)
|
|
@ -1,3 +0,0 @@
|
|||
import Home from "./Home"
|
||||
|
||||
export default Home
|
|
@ -1,5 +0,0 @@
|
|||
.projects {
|
||||
h2 {
|
||||
@apply mb-4;
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
import { useMeta, useTitle } from "hoofd"
|
||||
import { type FC } from "react"
|
||||
|
||||
import Loading from "@/components/Loading"
|
||||
|
||||
const LoadingPage: FC = () => {
|
||||
useTitle("Loading")
|
||||
useMeta({ property: "og:title", content: "Loading" })
|
||||
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
export default LoadingPage
|
|
@ -1,26 +0,0 @@
|
|||
import "./style.css"
|
||||
|
||||
import { useMeta, useTitle } from "hoofd"
|
||||
import { type FC } from "react"
|
||||
|
||||
const NotFound: FC = () => {
|
||||
useTitle("404")
|
||||
useMeta({ property: "og:title", content: "Page Not Found" })
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="w-fit px-4 py-2 text-7xl dark:bg-dark-text-default dark:text-dark-ui-bg">
|
||||
404
|
||||
</h1>
|
||||
|
||||
<h2
|
||||
className="glitch layers text-8xl"
|
||||
data-text="404 ERROR 404 ERROR"
|
||||
>
|
||||
Page Not Found
|
||||
</h2>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotFound
|
|
@ -1,3 +0,0 @@
|
|||
import NotFound from "./NotFound"
|
||||
|
||||
export default NotFound
|
|
@ -1,389 +0,0 @@
|
|||
/* glitch */
|
||||
|
||||
.glitch span {
|
||||
animation: paths 5s step-end infinite;
|
||||
}
|
||||
|
||||
.glitch::before {
|
||||
animation: paths 5s step-end infinite, opacity 5s step-end infinite,
|
||||
font 8s step-end infinite, movement 10s step-end infinite;
|
||||
}
|
||||
|
||||
.glitch::after {
|
||||
animation: paths 5s step-end infinite, opacity 5s step-end infinite,
|
||||
font 7s step-end infinite, movement 8s step-end infinite;
|
||||
}
|
||||
|
||||
/* layers */
|
||||
|
||||
.layers {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.layers::before,
|
||||
.layers::after {
|
||||
content: attr(data-text);
|
||||
position: absolute;
|
||||
width: 110%;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.layers::before {
|
||||
top: 10px;
|
||||
left: 15px;
|
||||
color: #e0287d;
|
||||
}
|
||||
|
||||
.layers::after {
|
||||
top: 5px;
|
||||
left: -10px;
|
||||
color: #1bc7fb;
|
||||
}
|
||||
|
||||
/* keyframes */
|
||||
|
||||
@keyframes paths {
|
||||
0% {
|
||||
clip-path: polygon(
|
||||
0% 43%,
|
||||
83% 43%,
|
||||
83% 22%,
|
||||
23% 22%,
|
||||
23% 24%,
|
||||
91% 24%,
|
||||
91% 26%,
|
||||
18% 26%,
|
||||
18% 83%,
|
||||
29% 83%,
|
||||
29% 17%,
|
||||
41% 17%,
|
||||
41% 39%,
|
||||
18% 39%,
|
||||
18% 82%,
|
||||
54% 82%,
|
||||
54% 88%,
|
||||
19% 88%,
|
||||
19% 4%,
|
||||
39% 4%,
|
||||
39% 14%,
|
||||
76% 14%,
|
||||
76% 52%,
|
||||
23% 52%,
|
||||
23% 35%,
|
||||
19% 35%,
|
||||
19% 8%,
|
||||
36% 8%,
|
||||
36% 31%,
|
||||
73% 31%,
|
||||
73% 16%,
|
||||
1% 16%,
|
||||
1% 56%,
|
||||
50% 56%,
|
||||
50% 8%
|
||||
);
|
||||
}
|
||||
|
||||
5% {
|
||||
clip-path: polygon(
|
||||
0% 29%,
|
||||
44% 29%,
|
||||
44% 83%,
|
||||
94% 83%,
|
||||
94% 56%,
|
||||
11% 56%,
|
||||
11% 64%,
|
||||
94% 64%,
|
||||
94% 70%,
|
||||
88% 70%,
|
||||
88% 32%,
|
||||
18% 32%,
|
||||
18% 96%,
|
||||
10% 96%,
|
||||
10% 62%,
|
||||
9% 62%,
|
||||
9% 84%,
|
||||
68% 84%,
|
||||
68% 50%,
|
||||
52% 50%,
|
||||
52% 55%,
|
||||
35% 55%,
|
||||
35% 87%,
|
||||
25% 87%,
|
||||
25% 39%,
|
||||
15% 39%,
|
||||
15% 88%,
|
||||
52% 88%
|
||||
);
|
||||
}
|
||||
|
||||
30% {
|
||||
clip-path: polygon(
|
||||
0% 53%,
|
||||
93% 53%,
|
||||
93% 62%,
|
||||
68% 62%,
|
||||
68% 37%,
|
||||
97% 37%,
|
||||
97% 89%,
|
||||
13% 89%,
|
||||
13% 45%,
|
||||
51% 45%,
|
||||
51% 88%,
|
||||
17% 88%,
|
||||
17% 54%,
|
||||
81% 54%,
|
||||
81% 75%,
|
||||
79% 75%,
|
||||
79% 76%,
|
||||
38% 76%,
|
||||
38% 28%,
|
||||
61% 28%,
|
||||
61% 12%,
|
||||
55% 12%,
|
||||
55% 62%,
|
||||
68% 62%,
|
||||
68% 51%,
|
||||
0% 51%,
|
||||
0% 92%,
|
||||
63% 92%,
|
||||
63% 4%,
|
||||
65% 4%
|
||||
);
|
||||
}
|
||||
|
||||
45% {
|
||||
clip-path: polygon(
|
||||
0% 33%,
|
||||
2% 33%,
|
||||
2% 69%,
|
||||
58% 69%,
|
||||
58% 94%,
|
||||
55% 94%,
|
||||
55% 25%,
|
||||
33% 25%,
|
||||
33% 85%,
|
||||
16% 85%,
|
||||
16% 19%,
|
||||
5% 19%,
|
||||
5% 20%,
|
||||
79% 20%,
|
||||
79% 96%,
|
||||
93% 96%,
|
||||
93% 50%,
|
||||
5% 50%,
|
||||
5% 74%,
|
||||
55% 74%,
|
||||
55% 57%,
|
||||
96% 57%,
|
||||
96% 59%,
|
||||
87% 59%,
|
||||
87% 65%,
|
||||
82% 65%,
|
||||
82% 39%,
|
||||
63% 39%,
|
||||
63% 92%,
|
||||
4% 92%,
|
||||
4% 36%,
|
||||
24% 36%,
|
||||
24% 70%,
|
||||
1% 70%,
|
||||
1% 43%,
|
||||
15% 43%,
|
||||
15% 28%,
|
||||
23% 28%,
|
||||
23% 71%,
|
||||
90% 71%,
|
||||
90% 86%,
|
||||
97% 86%,
|
||||
97% 1%,
|
||||
60% 1%,
|
||||
60% 67%,
|
||||
71% 67%,
|
||||
71% 91%,
|
||||
17% 91%,
|
||||
17% 14%,
|
||||
39% 14%,
|
||||
39% 30%,
|
||||
58% 30%,
|
||||
58% 11%,
|
||||
52% 11%,
|
||||
52% 83%,
|
||||
68% 83%
|
||||
);
|
||||
}
|
||||
|
||||
76% {
|
||||
clip-path: polygon(
|
||||
0% 26%,
|
||||
15% 26%,
|
||||
15% 73%,
|
||||
72% 73%,
|
||||
72% 70%,
|
||||
77% 70%,
|
||||
77% 75%,
|
||||
8% 75%,
|
||||
8% 42%,
|
||||
4% 42%,
|
||||
4% 61%,
|
||||
17% 61%,
|
||||
17% 12%,
|
||||
26% 12%,
|
||||
26% 63%,
|
||||
73% 63%,
|
||||
73% 43%,
|
||||
90% 43%,
|
||||
90% 67%,
|
||||
50% 67%,
|
||||
50% 41%,
|
||||
42% 41%,
|
||||
42% 46%,
|
||||
50% 46%,
|
||||
50% 84%,
|
||||
96% 84%,
|
||||
96% 78%,
|
||||
49% 78%,
|
||||
49% 25%,
|
||||
63% 25%,
|
||||
63% 14%
|
||||
);
|
||||
}
|
||||
|
||||
90% {
|
||||
clip-path: polygon(
|
||||
0% 41%,
|
||||
13% 41%,
|
||||
13% 6%,
|
||||
87% 6%,
|
||||
87% 93%,
|
||||
10% 93%,
|
||||
10% 13%,
|
||||
89% 13%,
|
||||
89% 6%,
|
||||
3% 6%,
|
||||
3% 8%,
|
||||
16% 8%,
|
||||
16% 79%,
|
||||
0% 79%,
|
||||
0% 99%,
|
||||
92% 99%,
|
||||
92% 90%,
|
||||
5% 90%,
|
||||
5% 60%,
|
||||
0% 60%,
|
||||
0% 48%,
|
||||
89% 48%,
|
||||
89% 13%,
|
||||
80% 13%,
|
||||
80% 43%,
|
||||
95% 43%,
|
||||
95% 19%,
|
||||
80% 19%,
|
||||
80% 85%,
|
||||
38% 85%,
|
||||
38% 62%
|
||||
);
|
||||
}
|
||||
|
||||
1%,
|
||||
7%,
|
||||
33%,
|
||||
47%,
|
||||
78%,
|
||||
93% {
|
||||
clip-path: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes movement {
|
||||
0% {
|
||||
top: 0px;
|
||||
left: -20px;
|
||||
}
|
||||
|
||||
15% {
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
60% {
|
||||
top: 5px;
|
||||
left: -10px;
|
||||
}
|
||||
|
||||
75% {
|
||||
top: -5px;
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 10px;
|
||||
left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes opacity {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
5% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
45% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
76% {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
90% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
1%,
|
||||
7%,
|
||||
33%,
|
||||
47%,
|
||||
78%,
|
||||
93% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes font {
|
||||
0% {
|
||||
font-weight: 100;
|
||||
color: #e0287d;
|
||||
filter: blur(3px);
|
||||
}
|
||||
|
||||
20% {
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
filter: blur(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
font-weight: 300;
|
||||
color: #1bc7fb;
|
||||
filter: blur(2px);
|
||||
}
|
||||
|
||||
60% {
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
filter: blur(0);
|
||||
}
|
||||
|
||||
90% {
|
||||
font-weight: 500;
|
||||
color: #e0287d;
|
||||
filter: blur(6px);
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
import "./style.scss"
|
||||
|
||||
import portfolio from "@developomp-site/content/dist/portfolio.json"
|
||||
import { useMeta, useTitle } from "hoofd"
|
||||
import { type FC, useEffect, useState } from "react"
|
||||
import { useRoute } from "wouter"
|
||||
|
||||
import Badge from "@/components/Badge"
|
||||
import Loading from "@/components/Loading"
|
||||
import Toc from "@/components/Toc"
|
||||
import NotFound from "@/routes/NotFound"
|
||||
|
||||
export interface PageData {
|
||||
title: string
|
||||
toc?: string
|
||||
content: string
|
||||
|
||||
image: string // image url
|
||||
overview: string
|
||||
badges: string[]
|
||||
repo: string
|
||||
}
|
||||
|
||||
const Project: FC = () => {
|
||||
const [pageData, setPageData] = useState<PageData | undefined>(undefined)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
|
||||
const [match, params] = useRoute("/project/:id")
|
||||
|
||||
useTitle(pageData?.title || "Loading")
|
||||
useMeta({ property: "og:title", content: pageData?.title })
|
||||
|
||||
useEffect(() => {
|
||||
if (!match) return
|
||||
;(async () => {
|
||||
try {
|
||||
if (!(params.id in portfolio.projects)) return
|
||||
|
||||
const data =
|
||||
portfolio.projects[
|
||||
params.id as keyof typeof portfolio.projects
|
||||
]
|
||||
|
||||
const fetched_content = await import(
|
||||
`@developomp-site/content/dist/content/projects/${params.id}.json`
|
||||
)
|
||||
|
||||
setPageData({
|
||||
content: fetched_content.content,
|
||||
toc: fetched_content.toc,
|
||||
title: data.name,
|
||||
image: data.image,
|
||||
overview: data.overview,
|
||||
badges: data.badges,
|
||||
repo: data.repo,
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
setIsLoading(false)
|
||||
})()
|
||||
}, [match, params])
|
||||
|
||||
if (!match) return <NotFound />
|
||||
if (isLoading) return <Loading />
|
||||
if (!pageData) return <NotFound />
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="mb-4 text-4xl">{pageData.title}</h1>
|
||||
{/* <!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --> */}
|
||||
<a href={pageData.repo}>
|
||||
<svg
|
||||
className="h-12 fill-dark-text-default duration-100 hover:fill-gray-400"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 496 512"
|
||||
>
|
||||
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex flex-wrap">
|
||||
{pageData.badges.map((slug) => {
|
||||
return <Badge key={slug} slug={slug} />
|
||||
})}
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
<Toc data={pageData.toc} />
|
||||
|
||||
{/* page content */}
|
||||
<div
|
||||
className="project-description"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: pageData.content,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Project
|
|
@ -1,3 +0,0 @@
|
|||
import Project from "./Project"
|
||||
|
||||
export default Project
|
|
@ -1,47 +0,0 @@
|
|||
type Theme = "dark" | "light" | "system"
|
||||
|
||||
/**
|
||||
* Reads theme from local storage
|
||||
*/
|
||||
export function readTheme(): Theme {
|
||||
const data = localStorage.getItem("theme")
|
||||
|
||||
if (
|
||||
!data || // data is falsy
|
||||
(data && data != "dark" && data != "light" && data != "system") // data is a non-empty string that's not a valid Theme
|
||||
) {
|
||||
saveTheme("system")
|
||||
return "system"
|
||||
}
|
||||
|
||||
return data as Theme
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves and sets the theme of the site at the same time
|
||||
*/
|
||||
export function saveTheme(theme: Theme): void {
|
||||
localStorage.setItem("theme", theme)
|
||||
setTheme(theme)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the theme of the site without saving it
|
||||
*/
|
||||
export function setTheme(theme: Theme): void {
|
||||
if (theme === "dark") document.documentElement.classList.add("dark")
|
||||
else document.documentElement.classList.remove("dark")
|
||||
}
|
||||
|
||||
// watch theme preference state
|
||||
window
|
||||
.matchMedia("(prefers-color-scheme: dark)")
|
||||
.addEventListener("change", ({ matches }) => {
|
||||
// only respond to the event if the theme is set to system
|
||||
if (readTheme() != "system") return
|
||||
|
||||
document.documentElement.classList.add("dark")
|
||||
document.documentElement.classList.remove("dark")
|
||||
|
||||
setTheme(matches ? "dark" : "light")
|
||||
})
|
1
apps/portfolio/src/vite-env.d.ts
vendored
1
apps/portfolio/src/vite-env.d.ts
vendored
|
@ -1 +0,0 @@
|
|||
/// <reference types="vite/client" />
|
|
@ -1,6 +1,9 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
presets: [require("@developomp-site/tailwind-config/tailwind.config")],
|
||||
darkMode: "class",
|
||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||
module.exports = {
|
||||
presets: [require("@developomp-site/tailwind-config/tailwind.config.js")],
|
||||
content: [
|
||||
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -1,32 +1,35 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
/* alias */
|
||||
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
"build/types/**/*.ts",
|
||||
"dist/types/**/*.ts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
import linaria from "@linaria/vite"
|
||||
import react from "@vitejs/plugin-react"
|
||||
import { defineConfig } from "vite"
|
||||
import dynamicImport from "vite-plugin-dynamic-import"
|
||||
import tsconfigPaths from "vite-tsconfig-paths"
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), linaria(), dynamicImport(), tsconfigPaths()],
|
||||
build: {
|
||||
outDir: "dist",
|
||||
},
|
||||
server: {
|
||||
open: true,
|
||||
port: 5174,
|
||||
},
|
||||
})
|
|
@ -22,12 +22,6 @@
|
|||
"target": "portfolio",
|
||||
"cleanUrls": true,
|
||||
"public": "apps/portfolio/dist",
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "**",
|
||||
"destination": "/index.html"
|
||||
}
|
||||
],
|
||||
"ignore": ["**/.*"]
|
||||
}
|
||||
]
|
||||
|
|
1945
pnpm-lock.yaml
generated
1945
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue