From 648021c1c0031b092ee8cb4a9ecc709cd84902e8 Mon Sep 17 00:00:00 2001 From: hknsh Date: Sun, 10 Nov 2024 17:25:53 +0000 Subject: [PATCH] feat: first commit --- .gitignore | 24 +++ LICENSE | 21 +++ README.md | 11 ++ biome.json | 30 ++++ bun.lockb | Bin 0 -> 107078 bytes components.json | 20 +++ index.html | 26 +++ package.json | 40 +++++ postcss.config.js | 6 + public/favicon.ico | Bin 0 -> 10462 bytes public/robots.txt | 19 +++ src/assets/frameworks/astro.svg | 6 + src/assets/frameworks/express.svg | 6 + src/assets/frameworks/fastify.svg | 10 ++ src/assets/frameworks/jest.svg | 13 ++ src/assets/frameworks/nest.svg | 13 ++ src/assets/frameworks/react.svg | 6 + src/assets/frameworks/tailwind.svg | 4 + src/assets/knedita-light.svg | 9 + src/assets/languages/js.svg | 14 ++ src/assets/languages/rust.svg | 13 ++ src/assets/languages/ts.svg | 14 ++ src/assets/tech/actions.svg | 6 + src/assets/tech/docker.svg | 6 + src/assets/tech/git.svg | 13 ++ src/assets/tech/github.svg | 7 + src/assets/tech/linux.svg | 8 + src/assets/tech/mongo.svg | 4 + src/assets/tech/postgres.svg | 8 + src/assets/tech/prisma.svg | 13 ++ src/assets/tech/swagger.svg | 17 ++ src/components/Header.tsx | 42 +++++ src/components/IconList.tsx | 19 +++ src/components/Icons.ts | 27 +++ src/components/TechnologiesTab.tsx | 37 ++++ src/components/ui/button.tsx | 57 +++++++ src/components/ui/card.tsx | 83 +++++++++ src/components/ui/carousel.tsx | 260 +++++++++++++++++++++++++++++ src/components/ui/scroll-area.tsx | 46 +++++ src/components/ui/separator.tsx | 29 ++++ src/components/ui/tabs.tsx | 53 ++++++ src/i18n.ts | 14 ++ src/index.tsx | 35 ++++ src/lib/utils.ts | 6 + src/locale/en.json | 16 ++ src/locale/ja.json | 16 ++ src/locale/pt.json | 16 ++ src/pages/Home/index.tsx | 115 +++++++++++++ src/pages/_404.tsx | 8 + src/style.css | 87 ++++++++++ tailwind.config.js | 57 +++++++ tsconfig.json | 23 +++ vite.config.ts | 23 +++ 53 files changed, 1456 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 biome.json create mode 100755 bun.lockb create mode 100644 components.json create mode 100644 index.html create mode 100644 package.json create mode 100644 postcss.config.js create mode 100755 public/favicon.ico create mode 100644 public/robots.txt create mode 100644 src/assets/frameworks/astro.svg create mode 100644 src/assets/frameworks/express.svg create mode 100644 src/assets/frameworks/fastify.svg create mode 100644 src/assets/frameworks/jest.svg create mode 100644 src/assets/frameworks/nest.svg create mode 100644 src/assets/frameworks/react.svg create mode 100644 src/assets/frameworks/tailwind.svg create mode 100644 src/assets/knedita-light.svg create mode 100644 src/assets/languages/js.svg create mode 100644 src/assets/languages/rust.svg create mode 100644 src/assets/languages/ts.svg create mode 100644 src/assets/tech/actions.svg create mode 100644 src/assets/tech/docker.svg create mode 100644 src/assets/tech/git.svg create mode 100644 src/assets/tech/github.svg create mode 100644 src/assets/tech/linux.svg create mode 100644 src/assets/tech/mongo.svg create mode 100644 src/assets/tech/postgres.svg create mode 100644 src/assets/tech/prisma.svg create mode 100644 src/assets/tech/swagger.svg create mode 100644 src/components/Header.tsx create mode 100644 src/components/IconList.tsx create mode 100644 src/components/Icons.ts create mode 100644 src/components/TechnologiesTab.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/card.tsx create mode 100644 src/components/ui/carousel.tsx create mode 100644 src/components/ui/scroll-area.tsx create mode 100644 src/components/ui/separator.tsx create mode 100644 src/components/ui/tabs.tsx create mode 100644 src/i18n.ts create mode 100644 src/index.tsx create mode 100644 src/lib/utils.ts create mode 100644 src/locale/en.json create mode 100644 src/locale/ja.json create mode 100644 src/locale/pt.json create mode 100644 src/pages/Home/index.tsx create mode 100644 src/pages/_404.tsx create mode 100644 src/style.css create mode 100644 tailwind.config.js create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5467b13 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 hknsh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7f226b5 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# `hksh's house` + +Created using Preact + Vite + +## Getting Started + +- `npm run dev` - Starts a dev server at http://localhost:5173/ + +- `npm run build` - Builds for production, emitting to `dist/`. Prerenders all found routes in app to static HTML + +- `npm run preview` - Starts a server at http://localhost:4173/ to test production build locally diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..639289e --- /dev/null +++ b/biome.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "ignore": ["./src/components/ui/*.tsx"] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + } +} diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..135208c20dc933e079a682f66c22b0f9b12df694 GIT binary patch literal 107078 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 + + + + + + + hknsh's house + + + + + + + + + + + + + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..c73b710 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@radix-ui/react-icons": "^1.3.1", + "@radix-ui/react-scroll-area": "^1.2.0", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-tabs": "^1.1.1", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "embla-carousel-react": "^8.3.1", + "i18next": "^23.16.5", + "lucide-react": "^0.454.0", + "preact": "^10.22.1", + "preact-iso": "^2.8.1", + "preact-render-to-string": "^6.5.11", + "react-i18next": "^15.1.1", + "tailwind-merge": "^2.5.4", + "tailwindcss-animate": "^1.0.7" + }, + "devDependencies": { + "@biomejs/biome": "1.9.4", + "@preact/preset-vite": "^2.9.0", + "@types/node": "^22.8.6", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.47", + "tailwindcss": "^3.4.14", + "typescript": "^5.6.3", + "vite": "^5.3.3" + }, + "eslintConfig": { + "extends": "preact" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..7b75c83 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100755 index 0000000000000000000000000000000000000000..3dbe9b62d58688ebbe054e7136cf783603b2b362 GIT binary patch literal 10462 zcmd5?YfM!~5I*=Qs95oZ6s4fnS5&N2+Ne?R6*VSm)Tl*~(p3K$e;A|27SU*;@rTyd z_-GJ|q6q{w=^rR0-o_XkQ~2S70JVxSniv!TpC~e&Z)FdMb9eWii&x{2xw~g}=9}3) z`#xA!82=?DS^S$|{XN>UqAknf#A1>412g2Hs8NNAA-eNZ}sHZX@WUK194rG>B};f$5@0poYq?Kk#~7~X{<*%2%$*_Fl{0(t-LGFinlWPrZQZ(+u3fuE zIzkzApxd`^Ur*R=G5o{nr_907C#?J@v(eA!(WB|ekt1~L)-7Gw0p+j-2CxG8UYuDGBdo z)~s13OzbJJ6?W4(7AWm7rk&%VwY8Pjty?E_V`5^&7=|n@Elp{!!*$Dn2^>lAxpe7L z6K0{S62tjfxo5)rb$cn-qu1w^D_0VHNW$1L^szUL z>)a=2>--!$c1%LjqeqWu^5n@KVl68xv&-MSc|+a0b?Z>B!!^mLOqoLHSCX*481#SS zGD$U@QR+J9Nc3U1-l|or=;h0o!sVq)m;B1z`d**s&z~==A=X)g9nLSH&zY6}`}gl} zW7X8uD0N_qF@pEhy?b}9j?RZke)Hx{+kb#LJ)Utgby(N~Nji1vR7hjg`E<(j^Yf*9 zN=;2Q`OjdxU!AtjJoa;vHf`Eu!U?Jj^YpZ7(`fD5wZ=U#d`dzr&e&Y}pZU4eCA0Of zUcD+|0=0*2qr;N&!Gi}=d3m{w+tk!V0|yRN+NY$X*m{UXVkpvLmi?!Yi*}MA4<0-S zY95M+h@kP~$5U)2%Mn8z=bIhRwjR!L zlJI_2I&K;ET5iHQ(5Z*IqTkE)0b|)&uF7MN3_5%EtWNhO+Xbu#`m_q)EgLs(oVyyd zapOj%ZFqP%B_t$tC>t?i1Z8GslH1?ZsZ#}RQBhF`eTRS4BVfP6+~Tm&=Fy*b@7_(X zUcC~2>+0&XW7v%s9UX1=$0tvoi18(L#*Q5;@PI94t`z<1mP;mdaZey=|Ni|td!;-s zE{-q`;^X6${`V$hmZ@+dllKKUlRD3K*#G3^<v?m!aMF?AasEyEud3{I+1h0u#=6TnFsml{y0k43MFUeuTc|JWt6&oa4}bI-FqS z*!P!|l+e9<_r$rZM~@y}aO2qr2*w&lD^=K@?1M+%ozJzi+$F_i4%nn%&l0nR1Ts|F7~_Y*RS_0lUM=LA})8+*DTQD&6zXDzW2d+NKa4K>SA4Y z?)gy}l|JUcJ9qB9%Q^BZcO}}M3ruxEACvtBI{FSqA0IhZkSZrr#* zICtUx#66Dz6Z;O_tznG~=Q!*Z|GgBAZ`Hu}PN!TN%+sv_LmoH%hptT$M5jvhTq z6%`etKLx_plwV+Z1amNi?`jpy#=6LI+;!nQfS2!ToxBIeH@?n(BgA)3?R|=@gDS=O z1$&J1=g$j99x|1#2bqs`I$H-e6@>Q~aJ;ZiXJZUxm&@3}_9YRx<_Ggw6LTQHp(@AR z>UCbygsc@~eZhC2P{pdl!T)8%X`={T!Iun$`e2`?izQe&_Nj>f8|Dv9*eCB`#yLOO zxTQY6S!6TgThhlsLpcbXk#XL~9EY=6J@czJMjkSh;cgt`ErB^CMC7vs^IMHao^Q0m zk=FA6(ING*8>H}-;uD7yE8Zvm@yhDsvv2`sg}0~JoLOCcRxQ%7_SA=G%g29j(`=EZ ziWgy7?Wqudz#m;4J`qEEytR0C)JN9SUH=y7al2rUpq*s9aTC66$J(}bmF+CsofYsN F{s*qXTW$aV literal 0 HcmV?d00001 diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..5af1fb9 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,19 @@ +User-agent: * +Allow: / + +User-agent: Teoma +Disallow:/ +User-agent: Gigabot +Disallow:/ +User-agent: Robozilla +Disallow:/ +User-agent: Nutch +Disallow:/ +User-agent: baiduspider +Disallow:/ +User-agent: naverbot +Disallow:/ +User-agent: yeti +Disallow:/ +User-agent: psbot +Disallow:/ \ No newline at end of file diff --git a/src/assets/frameworks/astro.svg b/src/assets/frameworks/astro.svg new file mode 100644 index 0000000..a3724ee --- /dev/null +++ b/src/assets/frameworks/astro.svg @@ -0,0 +1,6 @@ + + + diff --git a/src/assets/frameworks/express.svg b/src/assets/frameworks/express.svg new file mode 100644 index 0000000..76ebd33 --- /dev/null +++ b/src/assets/frameworks/express.svg @@ -0,0 +1,6 @@ + + + diff --git a/src/assets/frameworks/fastify.svg b/src/assets/frameworks/fastify.svg new file mode 100644 index 0000000..3a4aed8 --- /dev/null +++ b/src/assets/frameworks/fastify.svg @@ -0,0 +1,10 @@ + + + + diff --git a/src/assets/frameworks/jest.svg b/src/assets/frameworks/jest.svg new file mode 100644 index 0000000..e0347b1 --- /dev/null +++ b/src/assets/frameworks/jest.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/src/assets/frameworks/nest.svg b/src/assets/frameworks/nest.svg new file mode 100644 index 0000000..ef28921 --- /dev/null +++ b/src/assets/frameworks/nest.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/src/assets/frameworks/react.svg b/src/assets/frameworks/react.svg new file mode 100644 index 0000000..61a4bb0 --- /dev/null +++ b/src/assets/frameworks/react.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/frameworks/tailwind.svg b/src/assets/frameworks/tailwind.svg new file mode 100644 index 0000000..b340a38 --- /dev/null +++ b/src/assets/frameworks/tailwind.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/assets/knedita-light.svg b/src/assets/knedita-light.svg new file mode 100644 index 0000000..edb60ba --- /dev/null +++ b/src/assets/knedita-light.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/src/assets/languages/js.svg b/src/assets/languages/js.svg new file mode 100644 index 0000000..84652d9 --- /dev/null +++ b/src/assets/languages/js.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/src/assets/languages/rust.svg b/src/assets/languages/rust.svg new file mode 100644 index 0000000..a7408fc --- /dev/null +++ b/src/assets/languages/rust.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/src/assets/languages/ts.svg b/src/assets/languages/ts.svg new file mode 100644 index 0000000..d44639d --- /dev/null +++ b/src/assets/languages/ts.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/src/assets/tech/actions.svg b/src/assets/tech/actions.svg new file mode 100644 index 0000000..f14f3a1 --- /dev/null +++ b/src/assets/tech/actions.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/assets/tech/docker.svg b/src/assets/tech/docker.svg new file mode 100644 index 0000000..435c54d --- /dev/null +++ b/src/assets/tech/docker.svg @@ -0,0 +1,6 @@ + + + diff --git a/src/assets/tech/git.svg b/src/assets/tech/git.svg new file mode 100644 index 0000000..310412b --- /dev/null +++ b/src/assets/tech/git.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/src/assets/tech/github.svg b/src/assets/tech/github.svg new file mode 100644 index 0000000..5aa8261 --- /dev/null +++ b/src/assets/tech/github.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/assets/tech/linux.svg b/src/assets/tech/linux.svg new file mode 100644 index 0000000..19999dc --- /dev/null +++ b/src/assets/tech/linux.svg @@ -0,0 +1,8 @@ + + + diff --git a/src/assets/tech/mongo.svg b/src/assets/tech/mongo.svg new file mode 100644 index 0000000..091350c --- /dev/null +++ b/src/assets/tech/mongo.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/assets/tech/postgres.svg b/src/assets/tech/postgres.svg new file mode 100644 index 0000000..4e5a3a1 --- /dev/null +++ b/src/assets/tech/postgres.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/assets/tech/prisma.svg b/src/assets/tech/prisma.svg new file mode 100644 index 0000000..b20adab --- /dev/null +++ b/src/assets/tech/prisma.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/src/assets/tech/swagger.svg b/src/assets/tech/swagger.svg new file mode 100644 index 0000000..806b4bb --- /dev/null +++ b/src/assets/tech/swagger.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..843fe0a --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,42 @@ +import { useTranslation } from "react-i18next"; + +export function Header() { + const { t } = useTranslation(); + const scroll = (id: string) => { + const section = document.getElementById(id); + if (section) { + section.scrollIntoView({ behavior: "smooth" }); + } + }; + + return ( +
+
+
+ +
+
+
+ ); +} diff --git a/src/components/IconList.tsx b/src/components/IconList.tsx new file mode 100644 index 0000000..821a941 --- /dev/null +++ b/src/components/IconList.tsx @@ -0,0 +1,19 @@ +import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; + +export function IconList({ icons }) { + return ( +
+ +
+ {Object.entries(icons).map(([key, IconComponent]) => ( +
+ {key} + {key} +
+ ))} +
+ +
+
+ ); +} diff --git a/src/components/Icons.ts b/src/components/Icons.ts new file mode 100644 index 0000000..f77eb16 --- /dev/null +++ b/src/components/Icons.ts @@ -0,0 +1,27 @@ +import Astro from "../assets/frameworks/astro.svg"; +import Express from "../assets/frameworks/express.svg"; +import Fastify from "../assets/frameworks/fastify.svg"; +import Jest from "../assets/frameworks/jest.svg"; +import Nest from "../assets/frameworks/nest.svg"; +import Javascript from "../assets/languages/js.svg"; +import Typescript from "../assets/languages/ts.svg"; +import Rust from "../assets/languages/rust.svg"; +import Docker from "../assets/tech/docker.svg"; +import Git from "../assets/tech/git.svg"; +import Linux from "../assets/tech/linux.svg"; +import Prisma from "../assets/tech/prisma.svg"; +import Swagger from "../assets/tech/swagger.svg"; +import Actions from "../assets/tech/actions.svg"; +import MongoDB from "../assets/tech/mongo.svg"; +import Postgres from "../assets/tech/postgres.svg"; +import Tailwind from "../assets/frameworks/tailwind.svg"; +import React from "../assets/frameworks/react.svg"; + +export const Icons = { + languages: { Javascript, Typescript, Rust }, + backend_frameworks: { Nest, Express, Fastify }, + frontend_frameworks: { Astro, React, Tailwind }, + devops: { Docker, Linux, Git, Actions }, + docs_tests: { Swagger, Jest }, + db_orm: { Prisma, Postgres, MongoDB }, +}; diff --git a/src/components/TechnologiesTab.tsx b/src/components/TechnologiesTab.tsx new file mode 100644 index 0000000..96e8de6 --- /dev/null +++ b/src/components/TechnologiesTab.tsx @@ -0,0 +1,37 @@ +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Icons } from "@/components/Icons"; +import { IconList } from "@/components/IconList"; + +export function TechnologiesTab() { + return ( + + + Languages + Back-end + Front-end + DevOps + Docs & Tests + Databases & ORM + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..d18eb21 --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,57 @@ +import { Slot } from "@radix-ui/react-slot"; +import { type VariantProps, cva } from "class-variance-authority"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", + outline: + "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-10 rounded-md px-8", + icon: "h-9 w-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + }, +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx new file mode 100644 index 0000000..78cd0c3 --- /dev/null +++ b/src/components/ui/card.tsx @@ -0,0 +1,83 @@ +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +Card.displayName = "Card"; + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardHeader.displayName = "CardHeader"; + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardTitle.displayName = "CardTitle"; + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardDescription.displayName = "CardDescription"; + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardContent.displayName = "CardContent"; + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardFooter.displayName = "CardFooter"; + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, +}; diff --git a/src/components/ui/carousel.tsx b/src/components/ui/carousel.tsx new file mode 100644 index 0000000..5d18aff --- /dev/null +++ b/src/components/ui/carousel.tsx @@ -0,0 +1,260 @@ +import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons"; +import useEmblaCarousel, { + type UseEmblaCarouselType, +} from "embla-carousel-react"; +import * as React from "react"; + +import { Button } from "@/components/ui/button"; +import { cn } from "@/lib/utils"; + +type CarouselApi = UseEmblaCarouselType[1]; +type UseCarouselParameters = Parameters; +type CarouselOptions = UseCarouselParameters[0]; +type CarouselPlugin = UseCarouselParameters[1]; + +type CarouselProps = { + opts?: CarouselOptions; + plugins?: CarouselPlugin; + orientation?: "horizontal" | "vertical"; + setApi?: (api: CarouselApi) => void; +}; + +type CarouselContextProps = { + carouselRef: ReturnType[0]; + api: ReturnType[1]; + scrollPrev: () => void; + scrollNext: () => void; + canScrollPrev: boolean; + canScrollNext: boolean; +} & CarouselProps; + +const CarouselContext = React.createContext(null); + +function useCarousel() { + const context = React.useContext(CarouselContext); + + if (!context) { + throw new Error("useCarousel must be used within a "); + } + + return context; +} + +const Carousel = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & CarouselProps +>( + ( + { + orientation = "horizontal", + opts, + setApi, + plugins, + className, + children, + ...props + }, + ref, + ) => { + const [carouselRef, api] = useEmblaCarousel( + { + ...opts, + axis: orientation === "horizontal" ? "x" : "y", + }, + plugins, + ); + const [canScrollPrev, setCanScrollPrev] = React.useState(false); + const [canScrollNext, setCanScrollNext] = React.useState(false); + + const onSelect = React.useCallback((api: CarouselApi) => { + if (!api) { + return; + } + + setCanScrollPrev(api.canScrollPrev()); + setCanScrollNext(api.canScrollNext()); + }, []); + + const scrollPrev = React.useCallback(() => { + api?.scrollPrev(); + }, [api]); + + const scrollNext = React.useCallback(() => { + api?.scrollNext(); + }, [api]); + + const handleKeyDown = React.useCallback( + (event: React.KeyboardEvent) => { + if (event.key === "ArrowLeft") { + event.preventDefault(); + scrollPrev(); + } else if (event.key === "ArrowRight") { + event.preventDefault(); + scrollNext(); + } + }, + [scrollPrev, scrollNext], + ); + + React.useEffect(() => { + if (!api || !setApi) { + return; + } + + setApi(api); + }, [api, setApi]); + + React.useEffect(() => { + if (!api) { + return; + } + + onSelect(api); + api.on("reInit", onSelect); + api.on("select", onSelect); + + return () => { + api?.off("select", onSelect); + }; + }, [api, onSelect]); + + return ( + +
+ {children} +
+
+ ); + }, +); +Carousel.displayName = "Carousel"; + +const CarouselContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { carouselRef, orientation } = useCarousel(); + + return ( +
+
+
+ ); +}); +CarouselContent.displayName = "CarouselContent"; + +const CarouselItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { orientation } = useCarousel(); + + return ( +
+ ); +}); +CarouselItem.displayName = "CarouselItem"; + +const CarouselPrevious = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "icon", ...props }, ref) => { + const { orientation, scrollPrev, canScrollPrev } = useCarousel(); + + return ( + + ); +}); +CarouselPrevious.displayName = "CarouselPrevious"; + +const CarouselNext = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "icon", ...props }, ref) => { + const { orientation, scrollNext, canScrollNext } = useCarousel(); + + return ( + + ); +}); +CarouselNext.displayName = "CarouselNext"; + +export { + type CarouselApi, + Carousel, + CarouselContent, + CarouselItem, + CarouselPrevious, + CarouselNext, +}; diff --git a/src/components/ui/scroll-area.tsx b/src/components/ui/scroll-area.tsx new file mode 100644 index 0000000..2f16a5f --- /dev/null +++ b/src/components/ui/scroll-area.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" + +import { cn } from "@/lib/utils" + +const ScrollArea = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + {children} + + + + +)) +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName + +const ScrollBar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = "vertical", ...props }, ref) => ( + + + +)) +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName + +export { ScrollArea, ScrollBar } diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx new file mode 100644 index 0000000..6d7f122 --- /dev/null +++ b/src/components/ui/separator.tsx @@ -0,0 +1,29 @@ +import * as React from "react" +import * as SeparatorPrimitive from "@radix-ui/react-separator" + +import { cn } from "@/lib/utils" + +const Separator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, orientation = "horizontal", decorative = true, ...props }, + ref + ) => ( + + ) +) +Separator.displayName = SeparatorPrimitive.Root.displayName + +export { Separator } diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx new file mode 100644 index 0000000..85d83be --- /dev/null +++ b/src/components/ui/tabs.tsx @@ -0,0 +1,53 @@ +import * as React from "react" +import * as TabsPrimitive from "@radix-ui/react-tabs" + +import { cn } from "@/lib/utils" + +const Tabs = TabsPrimitive.Root + +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsList.displayName = TabsPrimitive.List.displayName + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsContent.displayName = TabsPrimitive.Content.displayName + +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/src/i18n.ts b/src/i18n.ts new file mode 100644 index 0000000..20d36d2 --- /dev/null +++ b/src/i18n.ts @@ -0,0 +1,14 @@ +import i18n from "i18next"; +import { initReactI18next } from "react-i18next"; +import enJSON from "./locale/en.json"; +import jaJSON from "./locale/ja.json"; +import ptJSON from "./locale/pt.json"; + +i18n.use(initReactI18next).init({ + resources: { + en: { ...enJSON }, + ja: { ...jaJSON }, + pt: { ...ptJSON }, + }, + lng: "en", +}); diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 0000000..bb66910 --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,35 @@ +import { + LocationProvider, + Route, + Router, + hydrate, + prerender as ssr, +} from "preact-iso"; + +import { Home } from "@/pages/Home"; +import { Header } from "./components/Header.jsx"; +import { NotFound } from "./pages/_404.jsx"; +import "./style.css"; +import './i18n'; + +export function App() { + return ( + +
+
+ + + + +
+ + ); +} + +if (typeof window !== "undefined") { + hydrate(, document.getElementById("app")); +} + +export async function prerender(data) { + return await ssr(); +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..ac680b3 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/src/locale/en.json b/src/locale/en.json new file mode 100644 index 0000000..d62c31e --- /dev/null +++ b/src/locale/en.json @@ -0,0 +1,16 @@ +{ + "translation": { + "header.home": "Home", + "header.projects": "Projects", + "header.social": "Social", + "header.git": "Git", + "hello": "Hello, I'm hknsh.", + "introduction": "I am a self-taught JavaScript programmer focused on back-end development, a Linux enthusiast, a big fan of indie and rhythm games, I read various mangas and I study other languages.", + "i18n": "Also available in:", + "description": "I started studying programming back in 2017 when I was 11 years old! Since then I've been creating a few projects to improve my skills.", + "technologies": "Technologies", + "projects": "Projects & Contributions", + "knedita": "An open-source social media.", + "biome": "Contributed to the Portuguese translation of the documentation" + } +} diff --git a/src/locale/ja.json b/src/locale/ja.json new file mode 100644 index 0000000..ac0bb8a --- /dev/null +++ b/src/locale/ja.json @@ -0,0 +1,16 @@ +{ + "translation": { + "header.home": "ホーム", + "header.projects": "プロジェクト", + "header.social": "ソーシャル", + "header.git": "Git", + "hello": "こんにちは, ハックントッシュです.", + "introduction": "僕は独学のJavaScriptプログラマーで、バックエンド開発に注力しています。Linux愛好者で、インディーゲームやリズムゲームの大ファンです。さまざまなマンガを読んでおり、他の言語も勉強しています。", + "i18n": "他の言語でも利用できます:", + "description": "2017年、まだ11歳のときにプログラミングの勉強を始めました。それ以来、スキルを磨くためにいくつかのプロジェクトを作ってきました。", + "technologies": "技術", + "projects": "プロジェクトと貢献", + "knedita": "オープンソースのSNS。", + "biome": "ポルトガル語のドキュメント翻訳に貢献しました" + } +} diff --git a/src/locale/pt.json b/src/locale/pt.json new file mode 100644 index 0000000..0cc40e7 --- /dev/null +++ b/src/locale/pt.json @@ -0,0 +1,16 @@ +{ + "translation": { + "header.home": "Início", + "header.projects": "Projetos", + "header.social": "Social", + "header.git": "Git", + "hello": "Olá, sou hknsh.", + "introduction": "Sou um programador autodidata JavaScript focado em desenvolvimento back-end, entusiasta Linux, grande fã de jogos indie e de ritmo, leio vários mangás e estudo outros idiomas.", + "i18n": "Também disponível em:", + "description": "Comecei a estudar programação em 2017, quando eu tinha apenas 11 anos. Desde então venho criando alguns projetos para aprimorar minhas habilidades", + "technologies": "Tecnologias", + "projects": "Projetos & Contribuições", + "knedita": "Uma rede social open-source.", + "biome": "Contribuí na tradução em Português da documentação" + } +} diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx new file mode 100644 index 0000000..3812536 --- /dev/null +++ b/src/pages/Home/index.tsx @@ -0,0 +1,115 @@ +import { TechnologiesTab } from "@/components/TechnologiesTab"; +import { Card, CardContent } from "@/components/ui/card"; +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "@/components/ui/carousel"; +import { Separator } from "@/components/ui/separator"; +import { useState } from "preact/hooks"; +import { useTranslation } from "react-i18next"; +import { useEffect } from "react"; + +export function Home() { + const { + t, + i18n: { changeLanguage, language }, + } = useTranslation(); + const [, setCurrentLanguage] = useState(language); + + useEffect(() => { + const storedLanguage = localStorage.getItem("language"); + if (storedLanguage) { + setCurrentLanguage(storedLanguage); + changeLanguage(storedLanguage); + } + }, [changeLanguage]); + + const handleChangeLanguage = (lang: string) => { + setCurrentLanguage(lang); + changeLanguage(lang); + localStorage.setItem("language", lang); + }; + return ( +
+
+
+

{t("hello")}

+

{t("introduction")}

+
+

{t("i18n")}

+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+

{t("description")}

+
+ + + +
+
+

{t("technologies")}

+ +
+
+ + + +
+
+

{t("projects")}

+ + + + +
+ + + + Project Knedita + + + +
+
+
+ + +
+
+
+
+
+ ); +} diff --git a/src/pages/_404.tsx b/src/pages/_404.tsx new file mode 100644 index 0000000..36182c8 --- /dev/null +++ b/src/pages/_404.tsx @@ -0,0 +1,8 @@ +export function NotFound() { + return ( +
+

404: Not Found

+

It's gone :(

+
+ ); +} diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..7c594af --- /dev/null +++ b/src/style.css @@ -0,0 +1,87 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --radius: 0.5rem; + } + .dark { + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 0 0% 9%; + --secondary: 0 0% 14.9%; + --secondary-foreground: 0 0% 98%; + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; + --accent: 0 0% 14.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } +} +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} + +@layer components { + .link { + @apply underline decoration-indigo-500 decoration-2 underline-offset-8 transition delay-100 ease-in-out hover:text-neutral-50 hover:decoration-indigo-600 text-lg md:text-xl; + } + .flex-center { + @apply flex items-center justify-center; + } + .neutral-btn { + @apply flex-center max-w-sm rounded-xl bg-neutral-800 p-3 text-sm transition ease-in hover:bg-neutral-900 md:p-4 md:text-base; + } + .neutral-btn-lg { + @apply flex-center w-48 rounded-xl bg-neutral-800 p-3 text-base transition ease-in hover:bg-neutral-900 md:p-4 md:text-base; + } + .text { + @apply text-base md:text-lg font-medium; + } + .title { + @apply text-xl md:text-4xl decoration-2 underline decoration-indigo-500 underline-offset-[12px] + } +} diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..a53c1ea --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,57 @@ +/** @type {import('tailwindcss').Config} */ +export default { + darkMode: ["class"], + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: { + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + colors: { + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + chart: { + 1: "hsl(var(--chart-1))", + 2: "hsl(var(--chart-2))", + 3: "hsl(var(--chart-3))", + 4: "hsl(var(--chart-4))", + 5: "hsl(var(--chart-5))", + }, + }, + }, + }, + plugins: [require("tailwindcss-animate")], +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..75cbefb --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "noEmit": true, + "allowJs": true, + "checkJs": true, + + /* Preact Config */ + "jsx": "react-jsx", + "jsxImportSource": "preact", + "skipLibCheck": true, + "paths": { + "react": ["./node_modules/preact/compat/"], + "react-dom": ["./node_modules/preact/compat/"], + "@/*": ["./src/*"] + }, + + "baseUrl": "." + }, + "include": ["node_modules/vite/client.d.ts", "**/*"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..e81c97c --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,23 @@ +import path from "node:path"; +import preact from "@preact/preset-vite"; +import { defineConfig } from "vite"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + preact({ + prerender: { + enabled: true, + renderTarget: "#app", + additionalPrerenderRoutes: ["/404"], + previewMiddlewareEnabled: true, + previewMiddlewareFallback: "/404", + }, + }), + ], + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, + }, +});