restructure project to use turborepo

This commit is contained in:
Kim, Jimin 2022-12-09 15:10:43 +09:00
parent 670ab793da
commit 90c535ebb2
181 changed files with 18477 additions and 10909 deletions

30
apps/blog/.eslintrc Normal file
View file

@ -0,0 +1,30 @@
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:json/recommended",
"prettier"
],
"settings": {
"node": {
"tryExtensions": [".js", ".jsx", ".json"]
},
"react": {
"version": "18.0"
}
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"react/jsx-uses-vars": "error",
"react/react-in-jsx-scope": ["off"]
}
}

176
apps/blog/.gitignore vendored Normal file
View file

@ -0,0 +1,176 @@
# auto generated files
/src/data/**
!/src/data/NavbarData.tsx
/public/img/skills.svg
/public/img/projects.svg
# production
build/
# Created by https://www.toptal.com/developers/gitignore/api/firebase,node,git,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=firebase,node,git,visualstudiocode
### Firebase ###
.idea
**/node_modules/*
**/.firebaserc
### Firebase Patch ###
.runtimeconfig.json
.firebase/
### Git ###
# Created by git for backups. To disable backups in Git:
# $ git config --global mergetool.keepBackup false
*.orig
# Created by git when using merge tools for conflicts
*.BACKUP.*
*.BASE.*
*.LOCAL.*
*.REMOTE.*
*_BACKUP_*.txt
*_BASE_*.txt
*_LOCAL_*.txt
*_REMOTE_*.txt
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
.env*.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Storybook build outputs
.out
.storybook-out
storybook-static
# rollup.js default build output
dist/
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# Temporary folders
tmp/
temp/
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/firebase,node,git,visualstudiocode

View file

@ -0,0 +1,35 @@
import fs from "fs"
import {
contentDirectoryPath,
iconsDirectoryPath,
mapFilePath,
portfolioFilePath,
searchIndexFilePath,
} from "./config"
export default function clean() {
deleteDirectory(contentDirectoryPath)
deleteDirectory(iconsDirectoryPath)
deleteFile(mapFilePath)
deleteFile(portfolioFilePath)
deleteFile(searchIndexFilePath)
deleteFile("./public/img/skills.svg")
deleteFile("./public/img/projects.svg")
}
function deleteDirectory(path: string) {
try {
fs.rmSync(path, { recursive: true })
// eslint-disable-next-line no-empty
} catch (err) {}
}
function deleteFile(path: string) {
try {
fs.unlinkSync(path)
// eslint-disable-next-line no-empty
} catch (err) {}
}

View file

@ -0,0 +1,8 @@
export const markdownPath = "./markdown" // where it will look for markdown documents
export const outPath = "./src/data" // path to the json database
export const contentDirectoryPath = `${outPath}/content`
export const iconsDirectoryPath = `${outPath}/icons`
export const mapFilePath = `${outPath}/map.json`
export const portfolioFilePath = `${outPath}/portfolio.json`
export const searchIndexFilePath = `${outPath}/search.json`

View file

@ -0,0 +1,84 @@
/**
* @file Read markdown files and write their content and metadata to json files which can then be imported by React.
* - File and directory names starting with an underscore (_) are ignored.
* - Symbolic links are not supported.
* - The filename-to-URL converter isn't perfect. Some non-URL-friendly filenames might cause problems.
* - series must start with a number followed by an underscore
*/
import fs from "fs"
import { mapFilePath, markdownPath, portfolioFilePath } from "./config"
import { recursiveParse } from "./recursiveParse"
import { saveIndex } from "./searchIndex"
import postProcess from "./postProcess"
import clean from "./clean"
import { Map, ParseMode, SeriesMap, PortfolioData } from "../types/types"
export const map: Map = {
date: {},
tags: {},
meta: {
tags: [],
},
posts: {},
series: {},
unsearchable: {},
}
export const seriesMap: SeriesMap = {}
export const portfolioData: PortfolioData = {
skills: new Set(),
projects: {},
}
/**
* Delete previously generated files
*/
clean()
/**
* Checking
*/
if (!fs.lstatSync(markdownPath).isDirectory())
throw Error("Invalid markdown path")
if (!fs.lstatSync(markdownPath + "/posts").isDirectory())
throw Error(`Cannot find directory: ${markdownPath + "/posts"}`)
if (!fs.lstatSync(markdownPath + "/unsearchable").isDirectory())
throw Error(`Cannot find directory: ${markdownPath + "/posts"}`)
if (!fs.lstatSync(markdownPath + "/series").isDirectory())
throw Error(`Cannot find directory: ${markdownPath + "/posts"}`)
/**
* Parse
*/
recursiveParse(ParseMode.POSTS, markdownPath + "/posts")
recursiveParse(ParseMode.UNSEARCHABLE, markdownPath + "/unsearchable")
recursiveParse(ParseMode.SERIES, markdownPath + "/series")
recursiveParse(ParseMode.PORTFOLIO, markdownPath + "/portfolio")
/**
* Post-process
*/
postProcess()
/**
* Save results
*/
fs.writeFileSync(mapFilePath, JSON.stringify(map))
fs.writeFileSync(
portfolioFilePath,
JSON.stringify({
...portfolioData,
skills: Array.from(portfolioData.skills),
})
)
saveIndex()

View file

@ -0,0 +1,125 @@
import markdownIt from "markdown-it" // rendering markdown
import markdownItTexMath from "markdown-it-texmath" // rendering mathematical expression
import markdownItAnchor from "markdown-it-anchor" // markdown anchor
import markdownItTaskCheckbox from "markdown-it-task-checkbox" // a TODO list checkboxes
import markDownItMark from "markdown-it-mark" // text highlighting
import markdownItSub from "markdown-it-sub" // markdown subscript
import markdownItSup from "markdown-it-sup" // markdown superscript
import markdownItFootnote from "markdown-it-footnote" // markdown footnote
import highlightLines from "markdown-it-highlight-lines" // highlighting specific lines in code blocks
import matter from "gray-matter"
import toc from "markdown-toc" // table of contents generation
import hljs from "highlight.js" // code block syntax highlighting
import katex from "katex" // rendering mathematical expression
import "katex/contrib/mhchem" // chemical formula
import { JSDOM } from "jsdom" // HTML DOM parsing
import { nthIndex } from "./util"
import { MarkdownData, ParseMode } from "../types/types"
const md = markdownIt({
// https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md
highlight: (str, lang) => {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(str, { language: lang }).value
// eslint-disable-next-line no-empty
} catch (error) {}
}
return "" // use external default escaping
},
html: true,
})
.use(markdownItTexMath, {
engine: katex,
delimiters: "dollars",
})
.use(markdownItAnchor, {
permalink: true,
permalinkBefore: true,
permalinkSymbol: "#",
})
.use(markdownItTaskCheckbox)
.use(markDownItMark)
.use(markdownItSub)
.use(markdownItSup)
.use(highlightLines)
.use(markdownItFootnote)
/**
* parse the front matter if it exists
*
* @param {string} markdownRaw - raw unparsed text data of the markdown file
* @param {string} path - filename of the markdown file
* @param {ParseMode} mode
*/
export default function parseMarkdown(
markdownRaw: string,
path: string,
mode: ParseMode
): MarkdownData {
const fileHasFrontMatter = markdownRaw.startsWith("---")
const frontMatter = fileHasFrontMatter
? matter(markdownRaw.slice(0, nthIndex(markdownRaw, "---", 2) + 3)).data
: {}
if (fileHasFrontMatter) {
if (mode != ParseMode.PORTFOLIO) {
if (!frontMatter.title)
throw Error(`Title is not defined in file: ${path}`)
if (mode != ParseMode.UNSEARCHABLE && !frontMatter.date)
throw Error(`Date is not defined in file: ${path}`)
}
if (mode === ParseMode.PORTFOLIO) {
if (frontMatter.overview) {
frontMatter.overview = md.render(frontMatter.overview)
}
}
}
//
// work with rendered DOM
//
const dom = new JSDOM(
md.render(
fileHasFrontMatter
? markdownRaw.slice(nthIndex(markdownRaw, "---", 2) + 3)
: markdownRaw
) || ""
)
// add .hljs class to all block codes
dom.window.document.querySelectorAll("pre > code").forEach((item) => {
item.classList.add("hljs")
})
// add parent div to tables (horizontally scroll table on small displays)
dom.window.document.querySelectorAll("table").forEach((item) => {
// `element` is the element you want to wrap
const parent = item.parentNode
if (!parent) return // stop if table doesn't have a parent node
const wrapper = dom.window.document.createElement("div")
wrapper.style.overflowX = "auto"
parent.replaceChild(wrapper, item)
wrapper.appendChild(item)
})
frontMatter.content = dom.window.document.documentElement.innerHTML
return frontMatter as MarkdownData
}
export function generateToc(markdownRaw: string): string {
return md.render(toc(markdownRaw).content)
}

View file

@ -0,0 +1,8 @@
<div class="badge">
<div class="badge-box" style="background-color: <%= badge.hex %>">
<div class="icon-container <%= badge.isDark ? 'white' : 'black' %>">
<%- badge.svg %>
</div>
</div>
<%= badge.title %>
</div>

View file

@ -0,0 +1,5 @@
<div class="items-wrapper">
<% badges.forEach((badge) => { %>
<%- include("badge.ejs", { badge }) %>
<% }) %>
</div>

View file

@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" width="480" height="800">
<style>
<%= style %>
</style>
<foreignObject x="0" y="0" width="100%" height="100%">
<div
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<% for (let key in data) { %>
<h2><%- key %></h2>
<% if(data[key] instanceof Array){ %>
<%- include("badges.ejs", { badges: data[key] }) %>
<% } else{ %>
<% for (let subKey in data[key]) { %>
<h3><%- subKey %></h3>
<%- include("badges.ejs", { badges: data[key][subKey] }) %>
<% } %>
<% } %>
<% } %>
</div>
</foreignObject>
</svg>

After

Width:  |  Height:  |  Size: 638 B

View file

@ -0,0 +1,15 @@
{
"Programming Languages": [
"javascript",
"typescript",
"python",
"rust",
"csharp"
],
"Front End": {
"Web": ["react", "svelte", "tailwindcss"],
"Desktop": ["gtk", "electron", "tauri"]
},
"Game Development": ["unity", "godotengine Godot"],
"Etc": ["figma", "markdown"]
}

View file

@ -0,0 +1,61 @@
svg {
/* from github */
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
sans-serif, Apple Color Emoji, Segoe UI Emoji;
font-size: 14px;
color: #777777;
}
h1,
h2,
h3,
h4,
h5,
h6 {
text-align: center;
}
.items-wrapper {
display: grid;
grid-template-columns: repeat(5, 1fr);
column-gap: 10px;
row-gap: 15px;
}
.badge {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
gap: 5px;
}
.badge-box {
display: flex;
justify-content: center;
align-items: center;
border-radius: 7px;
width: 70px;
height: 70px;
}
.icon-container > svg {
height: 40px !important;
}
.white {
color: white;
fill: white;
}
.black {
color: black;
fill: black;
}

View file

@ -0,0 +1,135 @@
import ejs from "ejs"
import { optimize } from "svgo"
import { readFileSync, writeFileSync } from "fs"
import tinycolor from "tinycolor2"
import { map, seriesMap } from "."
import { Badge } from "../src/components/Badge"
import skills from "./portfolio/skills.json"
export default function postProcess() {
sortDates()
fillTags()
parseSeries()
generatePortfolioSVGs()
}
function sortDates() {
const TmpDate = map.date
map.date = {}
Object.keys(TmpDate)
.sort()
.forEach((sortedDateKey) => {
map.date[sortedDateKey] = TmpDate[sortedDateKey]
})
}
function fillTags() {
map.meta.tags = Object.keys(map.tags)
}
function parseSeries() {
// sort series map
for (const seriesURL in seriesMap) {
seriesMap[seriesURL].sort((a, b) => {
if (a.index < b.index) return -1
if (a.index > b.index) return 1
return 0
})
}
// series length and order
for (const seriesURL in seriesMap) {
map.series[seriesURL].length = seriesMap[seriesURL].length
map.series[seriesURL].order = seriesMap[seriesURL].map((item) => item.url)
}
}
function generatePortfolioSVGs() {
/**
* render skills.svg
*/
// todo: wait add ejs once it's available
const style = readFileSync("./generate/portfolio/style.css", "utf-8")
const data: {
[key: string]: Badge[] | { [key: string]: Badge[] }
} = {}
// C O G N I T O - H A Z A R D
// THIS PART OF THE CODE WAS WRITTEN IN 3 AM
// C O G N I T O - H A Z A R D
for (const key in skills) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (skills[key] instanceof Array) {
if (!data[key]) {
data[key] = []
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
;(skills[key] as string[]).forEach((badge) =>
(data[key] as Badge[]).push(parseBadge(badge))
)
} else {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
for (const subKey in skills[key]) {
if (!data[key]) data[key] = {}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (!data[key][subKey]) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
data[key][subKey] = []
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
skills[key][subKey].forEach((badge: string) =>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
(data[key][subKey] as Badge[]).push(parseBadge(badge))
)
}
}
}
const renderedSVG = ejs.render(
readFileSync("./generate/portfolio/skills.ejs", "utf-8"),
{ style, data },
{ views: ["./generate/portfolio"] }
)
writeFileSync(
"./public/img/skills.svg",
optimize(renderedSVG, { multipass: true }).data
)
}
function parseBadge(badgeRaw: string): Badge {
const isMultiWord = badgeRaw.includes(" ")
const words = badgeRaw.split(" ")
const icon = isMultiWord
? // eslint-disable-next-line @typescript-eslint/no-var-requires
require("simple-icons")[words[0]]
: // eslint-disable-next-line @typescript-eslint/no-var-requires
require("simple-icons")[badgeRaw]
const color = tinycolor(icon.hex).lighten(5).desaturate(5)
return {
svg: icon.svg,
hex: color.toHexString(),
isDark: color.isDark(),
title: isMultiWord ? words.slice(1).join(" ") : icon.title,
}
}

View file

@ -0,0 +1,107 @@
import fs from "fs"
import readTimeEstimate from "read-time-estimate" // post read time estimation
import { path2FileOrFolderName, path2URL } from "../util"
import parseMarkdown from "../parseMarkdown"
import { ParseMode } from "../../types/types"
import parsePost from "./parsePost"
import parseSeries from "./parseSeries"
import parseUnsearchable from "./parseUnsearchable"
import parsePortfolio from "./parsePortfolio"
/**
* Data that's passed from {@link parseFile} to other function
*/
export interface DataToPass {
path: string
urlPath: string
markdownRaw: string
markdownData: {
content: string
[key: string]: unknown
}
humanizedDuration: string
totalWords: number
}
/**
* A recursive function that calls itself for every files and directories that it finds
*
* @param {ParseMode} mode - parse mode
* @param {string} path - path of file or folder
*/
export function recursiveParse(mode: ParseMode, path: string): void {
// get name of the file or folder that's currently being parsed
const fileOrFolderName = path2FileOrFolderName(path)
// stop if the file or folder starts with a underscore
if (fileOrFolderName.startsWith("_")) return
const stats = fs.lstatSync(path)
// if it's a directory, call this function to every files/directories in it
// if it's a file, parse it and then save it to file
if (stats.isDirectory()) {
fs.readdirSync(path).map((childPath) => {
recursiveParse(mode, `${path}/${childPath}`)
})
} else if (stats.isFile()) {
parseFile(mode, path)
}
}
/**
* Parse a markdown file
*
* @param {ParseMode} mode - decides which function to use to parse the file
* @param {string} path - path of the markdown file
*/
function parseFile(mode: ParseMode, path: string): void {
// stop if it is not a markdown file
if (!path.endsWith(".md")) {
console.log(`Ignoring non markdown file at: ${path}`)
return
}
/**
* Parse markdown
*/
const markdownRaw = fs.readFileSync(path, "utf8")
const markdownData = parseMarkdown(markdownRaw, path, mode)
const { humanizedDuration, totalWords } = readTimeEstimate(
markdownData.content,
275,
12,
500,
["img", "Image"]
)
const dataToPass: DataToPass = {
path,
urlPath: path2URL(path),
markdownRaw,
markdownData,
humanizedDuration,
totalWords,
}
switch (mode) {
case ParseMode.POSTS:
parsePost(dataToPass)
break
case ParseMode.SERIES:
parseSeries(dataToPass)
break
case ParseMode.UNSEARCHABLE:
parseUnsearchable(dataToPass)
break
case ParseMode.PORTFOLIO:
parsePortfolio(dataToPass)
break
}
}

View file

@ -0,0 +1,74 @@
import tinycolor from "tinycolor2"
import { contentDirectoryPath, iconsDirectoryPath } from "../config"
import { generateToc } from "../parseMarkdown"
import { writeToFile } from "../util"
import { portfolioData } from ".."
import { DataToPass } from "."
export default function parsePortfolio(data: DataToPass): void {
const { urlPath, markdownRaw, markdownData } = data
if (urlPath.endsWith(".kr")) {
const contentID = urlPath.slice(0, urlPath.length - 3)
if (portfolioData.projects[contentID]) {
portfolioData.projects[contentID] = {
...portfolioData.projects[contentID],
overview_kr: markdownData.overview as string,
}
} else {
portfolioData.projects[contentID] = {
name: "",
image: "",
overview_en: "",
overview_kr: markdownData.overview as string,
badges: [],
repo: "",
}
}
} else {
if (markdownData.badges) {
;(markdownData.badges as string[]).forEach((slug) => {
// todo: handle cases when icon is not on simple-icons
portfolioData.skills.add(slug)
// eslint-disable-next-line @typescript-eslint/no-var-requires
const icon = require("simple-icons")[slug]
const color = tinycolor(icon.hex).lighten(5).desaturate(5)
// save svg icon
writeToFile(
`${iconsDirectoryPath}/${icon.slug}.json`,
JSON.stringify({
svg: icon.svg,
hex: color.toHexString(),
isDark: color.isDark(),
title: icon.title,
})
)
})
}
portfolioData.projects[urlPath] = {
name: markdownData.name as string,
image: markdownData.image as string,
overview_en: markdownData.overview as string,
overview_kr: portfolioData.projects[urlPath]
? portfolioData.projects[urlPath].overview_kr
: "",
badges: (markdownData.badges as string[]) || [],
repo: (markdownData.repo as string) || "",
}
}
writeToFile(
`${contentDirectoryPath}${urlPath}.json`,
JSON.stringify({
content: markdownData.content,
toc: generateToc(markdownRaw),
})
)
}

View file

@ -0,0 +1,71 @@
import { contentDirectoryPath } from "../config"
import { generateToc } from "../parseMarkdown"
import { PostData } from "../../types/types"
import { addDocument } from "../searchIndex"
import { writeToFile } from "../util"
import { map } from ".."
import { DataToPass } from "."
export default function parsePost(data: DataToPass): void {
const { urlPath, markdownRaw, markdownData, humanizedDuration, totalWords } =
data
const postData: PostData = {
title: markdownData.title as string,
date: "",
readTime: humanizedDuration,
wordCount: totalWords,
tags: [],
}
/**
* Dates
*/
const postDate = new Date(markdownData.date as string)
postData.date = postDate.toLocaleString("default", {
month: "short",
day: "numeric",
year: "numeric",
})
const YYYY_MM_DD = postDate.toISOString().split("T")[0]
if (map.date[YYYY_MM_DD]) {
map.date[YYYY_MM_DD].push(urlPath)
} else {
map.date[YYYY_MM_DD] = [urlPath]
}
/**
* Tags
*/
postData.tags = markdownData.tags as string[]
if (postData.tags) {
postData.tags.forEach((tag) => {
if (map.tags[tag]) {
map.tags[tag].push(urlPath)
} else {
map.tags[tag] = [urlPath]
}
})
}
/**
*
*/
map.posts[urlPath] = postData
addDocument({
title: markdownData.title,
body: markdownData.content,
url: urlPath,
})
writeToFile(
`${contentDirectoryPath}${urlPath}.json`,
JSON.stringify({
content: markdownData.content,
toc: generateToc(markdownRaw),
})
)
}

View file

@ -0,0 +1,146 @@
import { contentDirectoryPath } from "../config"
import { generateToc } from "../parseMarkdown"
import { PostData } from "../../types/types"
import { addDocument } from "../searchIndex"
import { writeToFile } from "../util"
import { map, seriesMap } from ".."
import { DataToPass } from "."
export default function parseSeries(data: DataToPass): void {
const {
path,
urlPath: _urlPath,
markdownRaw,
markdownData,
humanizedDuration,
totalWords,
} = data
// last part of the url without the slash
let lastPath = _urlPath.slice(_urlPath.lastIndexOf("/") + 1)
if (!lastPath.includes("_") && !lastPath.startsWith("0"))
throw Error(`Invalid series file name at: "${path}"`)
// if file is a series descriptor or not (not = regular series post)
const isFileDescriptor = lastPath.startsWith("0") && !lastPath.includes("_")
// series post url
if (isFileDescriptor) {
lastPath = ""
} else {
lastPath = lastPath
.slice(lastPath.indexOf("_") + 1) // get string after the series index
.replace(/\/$/, "") // remove trailing slash
}
// get url until right before the lastPath
const urlUntilLastPath = _urlPath.slice(0, _urlPath.lastIndexOf("/") + 1)
// remove trailing slash if it's a regular series post
const urlPath =
(isFileDescriptor
? urlUntilLastPath.replace(/\/$/, "")
: urlUntilLastPath) + lastPath
// todo: separate interface for series descriptor (no word count and read time)
const postData: PostData = {
title: markdownData.title as string,
date: "",
readTime: humanizedDuration,
wordCount: totalWords,
tags: [],
}
/**
* Date
*/
const postDate = new Date(markdownData.date as string)
postData.date = postDate.toLocaleString("default", {
month: "short",
day: "numeric",
year: "numeric",
})
const YYYY_MM_DD = postDate.toISOString().split("T")[0]
if (map.date[YYYY_MM_DD]) {
map.date[YYYY_MM_DD].push(urlPath)
} else {
map.date[YYYY_MM_DD] = [urlPath]
}
/**
* Tags
*/
postData.tags = markdownData.tags as string[]
if (postData.tags) {
postData.tags.forEach((tag) => {
if (map.tags[tag]) {
map.tags[tag].push(urlPath)
} else {
map.tags[tag] = [urlPath]
}
})
}
/**
*
*/
addDocument({
title: markdownData.title,
body: markdownData.content,
url: urlPath,
})
map.posts[urlPath] = postData
// series markdown starting with 0 is a series descriptor
if (isFileDescriptor) {
map.series[urlPath] = {
...postData,
order: [],
length: 0,
}
} else {
// put series post in appropriate series
for (const key of Object.keys(map.series)) {
if (urlPath.includes(key)) {
const index = parseInt(
_urlPath.slice(
_urlPath.lastIndexOf("/") + 1,
_urlPath.lastIndexOf("_")
)
)
if (isNaN(index)) throw Error(`Invalid series index at: ${path}`)
const itemToPush = {
index: index,
url: urlPath,
}
if (seriesMap[key]) {
seriesMap[key].push(itemToPush)
} else {
seriesMap[key] = [itemToPush]
}
break
}
}
}
/**
* Save content
*/
writeToFile(
`${contentDirectoryPath}${urlPath}.json`,
JSON.stringify({
content: markdownData.content,
toc: generateToc(markdownRaw),
})
)
}

View file

@ -0,0 +1,36 @@
import { contentDirectoryPath } from "../config"
import { addDocument } from "../searchIndex"
import { writeToFile } from "../util"
import { map } from ".."
import { DataToPass } from "."
export default function parseUnsearchable(data: DataToPass): void {
const { urlPath: _urlPath, markdownData } = data
// convert path like /XXX/YYY/ZZZ to /YYY/ZZZ
const urlPath = _urlPath.slice(_urlPath.slice(1).indexOf("/") + 1)
if (!urlPath.endsWith(".kr.md")) {
addDocument({
title: markdownData.title,
body: markdownData.content,
url: urlPath,
})
// Parse data that will be written to map.js
map.unsearchable[urlPath] = {
title: markdownData.title as string,
}
}
/**
* Save content
*/
writeToFile(
`${contentDirectoryPath}/unsearchable${urlPath}.json`,
JSON.stringify({
content: markdownData.content,
})
)
}

View file

@ -0,0 +1,26 @@
/**
* @file generate index for searching
*/
import fs from "fs"
import elasticlunr from "elasticlunr"
import { searchIndexFilePath } from "./config"
const elasticlunrIndex = elasticlunr(function () {
this.addField("title" as never)
this.addField("body" as never)
this.setRef("url" as never)
})
export function addDocument(doc: {
title?: unknown
body?: string
url?: string
}) {
elasticlunrIndex.addDoc(doc)
}
export function saveIndex() {
fs.writeFileSync(searchIndexFilePath, JSON.stringify(elasticlunrIndex))
}

View file

@ -0,0 +1,52 @@
import fs from "fs"
import { relative } from "path"
import { markdownPath } from "./config"
/**
* converts file path to url path that will be used in the url (starts with a slash)
*
* @param {string} pathToConvert
*/
export function path2URL(pathToConvert: string): string {
return `/${relative(markdownPath, pathToConvert)}`
.replace(/\.[^/.]+$/, "") // remove the file extension
.replace(/ /g, "-") // replace all space with a dash
}
/**
* Returns the text after the last slash
*
* @param {string} inputPath - path to parse
*/
export function path2FileOrFolderName(inputPath: string): string {
// remove trailing slash
if (inputPath[-1] == "/") inputPath = inputPath.slice(0, inputPath.length - 1)
// get the last section
return inputPath.slice(inputPath.lastIndexOf("/") + 1)
}
// gets the nth occurance of a pattern in string
// returns -1 if nothing is found
// https://stackoverflow.com/a/14482123/12979111
export function nthIndex(str: string, pat: string, n: number) {
let i = -1
while (n-- && i++ < str.length) {
i = str.indexOf(pat, i)
if (i < 0) break
}
return i
}
export function writeToFile(filePath: string, dataToWrite: string) {
// create directory to put the files
fs.mkdirSync(filePath.slice(0, filePath.lastIndexOf("/")), {
recursive: true,
})
// write content to the file
fs.writeFileSync(filePath, dataToWrite)
}

View file

@ -0,0 +1,95 @@
---
name: developomp-site
overview: my websites for blogging, portfolio, resume, etc.
image: /img/portfolio/developomp.com.png
repo: https://github.com/developomp/developomp-site
badges:
- typescript
- javascript
- nodedotjs
- firebase
- amazonaws
- react
- html5
- css3
---
## Intro
developomp.com is a website I built for blogging, data hosting, portfolio, resume, etc.
It is a static, single page application built with [react](https://reactjs.org) framework.
It is hosted on [google firebase](https://firebase.google.com), and domain registered with [AWS](https://aws.amazon.com) Route 53.
## How it's used
The portfolio page doubles as a project description page where it lists some interesting aspects of the project.
It's basically a developer's note where I show parts I put extra effort and want people to know about it.
## How it works
The build process of the site can be subdivided into three major stages: _content generation_, _site building_, and _deployment_.
### 1. content generation
Before the site ever gets to deal with react stuff, a sort of content pre-processing should take place.
In this stage, markdown files are rendered to HTML, svg images are constructed, and json files containing metadata are generated.
These files are all saved in the `src/data` directory with exceptions for some image files which are saved in the `public/img` directory.
#### A. HTML generation
The [markdown files](https://github.com/developomp/developomp-site/tree/master/markdown) are rendered to HTML using the [markdown-it](https://github.com/markdown-it/markdown-it) library.
Various extensions are used in this stage to extend markdown features such as footnotes, mathematical expressions, and code blocks.
- Check the [test post](/posts/test-post) to see all markdown related features.
- The conversion logic can be found in the [`generate/parsemarkdown.ts`](https://github.com/developomp/developomp-site/blob/master/generate/parseMarkdown.ts) file.
#### B. images
After the all the text contents are parsed, svg images are constructed.
First, icons from [simple-icons](https://github.com/simple-icons/simple-icons) that are used by the site are copied to the `src/data/icons` directory.
Then, other images such as the "programming skills" stats that can be seen in the [portfolio](/portfolio) page and in my [github profile](https://github.com/developomp#skills) are generated using the [EJS](https://ejs.co) library.
- The code can be found in [`generate/portfolio`](https://github.com/developomp/developomp-site/tree/master/generate/portfolio).
#### C. metadata
After dealing with all the contents, json files containing metadata are generated.
These can then be imported by react for searching and listing.
Files generated in this stage includes:
- `src/data/map.json` (contains information about regular blog posts and pages)
- `src/data/portfolio.json` (contains information about portfolio related data)
- `src/data/search.json` (contains searchable [elasticlunr](https://github.com/weixsong/elasticlunr.js) index for the search page)
### 2. site building
Good old react build process using [react-scripts](https://www.npmjs.com/package/react-scripts).
### 3. deployment
The site is deployed to firebase.
## Features
### Reactive UI
The site is designed to work on displays of any sizes.
Horizontal overflows are properly dealt with in small displays,
and contents have a maximum width so it looks beautiful on ultra-wide displays.
### Searching
The search feature usually involves a server or service like [algolia](https://www.algolia.com).
However, the searching logic is in the client side so there's no task that requires a server aside from static site hosting.
## Limitations
Because all the computation is done in the client-side,
there is a possibility for the site to be too slow to use some users with outdated hardware (especially mobile users).
Also, since the search operation also happens in the client-side,
the client has to download every blog posts in the site for the search feature to work.
This may be a issue for users with slow/limited access to the internet.

View file

@ -0,0 +1,37 @@
---
name: future projects
overview: Next big projects ^TM^
image: /img/icon.png
---
These are the projects I'll be working on in the future. Ordered alphabetically.
## Exyle.io
- [exyle.io](https://github.com/exyleio)
A browser-based online multiplayer fps game.
## are we arm yet
- [GitHub](https://github.com/Are-we-ARM-yet)
Inspired by the "are we X yet?" websites from the rust community, are-we-arm-yet is a website that lists all information related to ARM desktop adoption.
## babel-compressor
Slow but efficient compression algorithm based on [the library of babel](https://libraryofbabel.info/theory.html).
## bevy-earth
Satellite image viewing utility built with [bevy](https://github.com/bevyengine/bevy).
## boy-lang
- [GitHub](https://github.com/boy-lang)
My own programming language built with rust and LLVM.
## katoku
A FOSS cross-platform alternative [Kakaotalk](https://www.kakaocorp.com) client.

View file

@ -0,0 +1,21 @@
---
name: Arch Linux setup script
overview: My Arch Linux desktop setup
image: /img/portfolio/linux-setup-script.png
repo: https://github.com/developomp/setup
badges:
- linux
- python
---
## Introduction
Properly setting up a desktop takes a lot of time.
Installing all of your favorite applications and configuring them to your liking is no easy task.
The primary purpose of this project is to solve this exact problem by automating the process of installation and configuration of applications and system.
## How does it work?
[Github pages](https://pages.github.com) allows the developers to deploy a static site directly from their repositories.
I have set up a [github action](https://docs.github.com/en/actions) so that the content of the bootstrap script gets copied over to the `index.html` file in the `gh-pages` branch so it can be downloaded from https://setup.developomp.com/.
This script then clones the rest of the repository upon execution so it can start doing its thing.

View file

@ -0,0 +1,28 @@
---
name: Llama Bot
overview: A discord bot.
image: /img/portfolio/llama-bot.png
repo: https://github.com/llama-bot
badges:
- firebase
- nodedotjs
- javascript
- typescript
- svelte
- html5
- css3
- express
---
The llama bot is a discord bot made for the [Llama's Pyjamas community discord server](discord.gg/2fsar34APa).
It is written in typescript and uses the [sapphire framework](https://sapphirejs.dev).
It is unique in that it uses google firebase for most, if not all of its tasks and that it has a graphical web interface.
## Web interface
The web interface is written in typescript and svelte.
## Documentation
The documentation is built using docusaurus.

View file

@ -0,0 +1,16 @@
---
name: Mocha Downloader
overview: A cross-platform desktop download manager built with web technologies.
image: /img/portfolio/mocha-downloader.png
repo: https://github.com/Mocha-Downloader
badges:
- typescript
- javascript
- nodedotjs
- electron
- react
- html5
- css3
---
Mocha Downloader is a cross-platform desktop download manager built with web technologies.

View file

@ -0,0 +1,93 @@
---
name: pomky
overview: A gtk-based, [conky](https://github.com/brndnmtthws/conky)-like system monitor written in rust.
image: /img/portfolio/pomky.png
repo: https://github.com/developomp/pomky
badges:
- rust
- gtk
- cairographics
---
## Introduction
If you're into desktop customization, chances are, you're using (or used)
[rainmeter][rainmeter]. In case you don't know what that is, it is by far the
most popular desktop customization tool. Think of Windows 7 widgets on steroid.
However, rainmeter only works in the Windows Operating System. Which means Linux
users like me have to look elsewhere for alternatives. Fortunately, there are
projects like [conky][conky] and [polybar][polybar], so getting started should
not be too difficult especially with the endless supply of ideas, references,
and guides from communities such as [r/unixporn][unixporn].
When I first switched to Linux back in 2017, I was somewhat satisfied with my
simple conky widgets, but I knew I had to eventually do something about its
primitive configuration system that prevented me from making anything with
complexity without looking like a card pyramid that could collapse at the
slightest disturbance. So one day in December 2021, after finishing
[The Rust Book][the-rust-book], I decided to make my own tailor-made system
monitor as my first rust project.
## Challenges
### What framework to use
When I first started the project, I considered using [tauri][tauri] which is
basically [ElectronJS][electronjs] but with rust & WebKit for backend and is
much more lightweight.
However, that plan quickly fall apart when it turned out to be impossible to
make a window that acted like it's part of the desktop (like the task bar)
instead of a regular window without access to the lower level code. In technical
terms, I wasn't able to mark the window as `_NET_WM_WINDOW_TYPE_DESKTOP`
([FreeDesktop Documentation][freedesktop-docs]). This is now possible thanks to
[tauri-apps/tao#522][tauri-always-on-bottom] PR being merged, but at the time,
there was no simple and clean solution.
After going through different options, I ended up implementing everything from
scratch using the [rust binding for gtk][gtk-rs]. This allowed me to simply set
a `WindowTypeHint` ([GDK documentation][gdk-docs]) and expect everything to work
flawlessly. This also allowed me to use powerful GUI design tools such as
[glade][glade].
### Drawing graphs
Although GTK doesn't provide any usable built-in graph & chart components,
developers can still implement their own using the
[Cairo Graphics Library][cairographics] which is part of the
[GTK architecture][gtk-architecture].
After reading some documentations and way more google searches than I'd like to
admit, I was able to make a simple graph and bar component I was happy with.
## Future
Although the end result looks rather marvelous if you ask me, there are several
rough edges I'd like to smooth out. For starters, it acts erratically on
[Wayland][wayland] (getting a title bar all of a sudden, moving out of its set
position, etc.), gets drawn over other window when switching workspaces, has
higher CPU usage than other system monitors, has unpredictable CPU spikes, etc.
Which is why in the future, I'll be using [eww][eww]: yet another Linux widget
system written in rust. The way it works is very similar to pomky behind the
scenes (uses gtk, draws with cairo, custom components, all the good stuff), but
it is better than pomky in almost every conceivable way. It is more configurable
, more lightweight, more modular, and solves the previously mentioned issues.
[rainmeter]: https://www.rainmeter.net "rainmeter"
[conky]: https://github.com/brndnmtthws/conky "conky"
[polybar]: https://github.com/polybar/polybar "polybar"
[unixporn]: https://www.reddit.com/r/unixporn "unixporn"
[the-rust-book]: https://doc.rust-lang.org/book "The Rust Book"
[tauri]: https://tauri.app "tauri"
[electronjs]: https://www.electronjs.org "ElectronJS"
[freedesktop-docs]: https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm45299620502752 "Freedesktop Documentation"
[tauri-always-on-bottom]: https://github.com/tauri-apps/tao/pull/522 "tauri-apps/tao PR #522"
[gtk-rs]: https://gtk-rs.org "gtk-rs"
[gdk-docs]: https://docs.gtk.org/gdk3/enum.WindowTypeHint.html#desktop "GDK Documentation"
[glade]: https://wiki.gnome.org/Apps/Glade "Glade"
[cairographics]: https://www.cairographics.org "Cairo Graphics"
[gtk-architecture]: https://www.gtk.org/docs/architecture "GTK architecture"
[wayland]: https://wayland.freedesktop.org "Wayland"
[eww]: https://github.com/elkowar/eww "eww"

View file

@ -0,0 +1,29 @@
---
overview: 유니티 게임을 위한 모드. 인-게임 UI와 OBS 오버레이를 제공.
---
## 개요
War Brokers Mods, 줄여서 WBM은 게임 [War Brokers](https://warbrokers.io)를 위한 모드입니다.
## 모드
C#으로 만들어졌으며, [BepInEx](https://github.com/BepInEx/BepInEx) 프레임워크를 사용하여 게임의 여러 측면을 패치합니다.
## OBS 오버레이
<p align="center">
<img alt="OBS 오버레이" src="/img/portfolio/wbm-overlays.png" />
</p>
[OBS 스튜디오](https://github.com/obsproject/obs-studio)를 위한 오버레이.
웹 기술을 이용하여 제작되었습니다 (자바스크립트, CSS, HTML 등).
## 설치기
<p align="center">
<img alt="설치기" src="/img/portfolio/wbm-installer.png" />
</p>
간단한 크로스 플랫폼 설칙 및 업데이트 관리기.
[tauri](https://github.com/tauri-apps/tauri), [rust](https://github.com/rust-lang/rust), [svelte](https://github.com/sveltejs/svelte), 그리고 [tailwind css](https://github.com/tailwindlabs/tailwindcss)를 사용해 만들어졌습니다.

View file

@ -0,0 +1,41 @@
---
name: War Brokers Mods
overview: A game mod for a unity game. Provides in-game UI and OBS overlays.
image: /img/portfolio/wbm.png
repo: https://github.com/War-Brokers-Mods
badges:
- unity
- csharp
- dotnet
- javascript
- html5
- css3
- svelte
- tailwindcss
- rust
- tauri
---
## Intro
War Brokers Mods (WBM) is a mod for the game [War Brokers](https://warbrokers.io).
## The mod
Built with C#, it uses [BepInEx](https://github.com/BepInEx/BepInEx) framework to patch different aspects of the game.
## OBS Overlay
<p align="center">
<img alt="Overlay image" src="/img/portfolio/wbm-overlays.png" />
</p>
Overlays for [OBS studio](https://github.com/obsproject/obs-studio). Built with standard web technologies (html, css, js).
## Installer
<p align="center">
<img alt="Installer image" src="/img/portfolio/wbm-installer.png" />
</p>
A simple cross-platform installer and update manager. Built with [tauri](https://github.com/tauri-apps/tauri), [rust](https://github.com/rust-lang/rust), [svelte](https://github.com/sveltejs/svelte), and [tailwind css](https://github.com/tailwindlabs/tailwindcss).

View file

@ -0,0 +1,17 @@
---
name: War Brokers Timeline
overview: A list of events happened in the War Brokers community in a chronological order.
image: /img/portfolio/wbtimeline.png
repo: https://github.com/developomp/wbtimeline
badges:
- deno
- rust
- webassembly
- javascript
- typescript
- css3
- sass
- html5
---
<!-- add yew to badges -->

View file

@ -0,0 +1,102 @@
---
title: Finding the ultimate browser
date: 2022-03-24
tags:
- story
- browser
---
## Intro
When I made the switch to Linux, I had to reconsider every choice I've made throughout the entire time I've been using Windows.
Most of them were trivial choices, some took a bit of time but I eventually figured it out but one problem stood out to be much more difficult than the others:
Which browser should I use?
Spoiler alert, I'm still waiting for the _ultimate browser_^TM^ but at least now I have something to share.
Make yourself comfortable because you're in for a ride.
This is my journey to find the ultimate browser.
## The beginning
For us to talk about browsers, we first have to go all the way back to the early 2000s,
when the only computer in my house was a old windows XP PC with a CRT monitor that was probably as old as me.
When I was old enough to understand language, my father introduced me to my first browser: The Internet explorer (abbreviated to IE from this point onward).
At the time, it was everything I wished for and more, but little did I know,
IE was already on the decline while another browser was quietly climbing up the market share.
<p align="center">
<img alt="browser market share" src="/img/posts/linux-setup-script/browser-market-share-trend.png" style="max-width: 100%;" />
<br />
source: <a href="https://gs.statcounter.com/browser-market-share/desktop/worldwide/#monthly-200901-202203" target="_blank">statcounter.com</a>
</p>
One day, probably after my father upgraded the PC to Windows 7,
the default browser was changed to some colorful ball looking thing.
And its name was Google Chrome.
Not much have changed with my browsing experience as I didn't use much internet back then - I didn't even know that YouTube was a thing -
but the switch is worth mentioning because it made Chrome the browser that I grew up with instead of IE.
## Switching to Linux
By the time I was in grade 8 I considered myself to be quite a tech-savvy person.
I knew how the internet worked behind the scene, I was able code basic programs, had some experience with Machine Learning and Linux,
was interested in various online privacy and security issues, and was no stranger to the DIY culture.
That, added with the fact that Microsoft was making Windows worse by day made me make the switch to Linux.
And along the way, I ditched Google Chrome for Chromium.
In hindsight, I could have chose a better browser like firefox but I chose Chromium because I couldn't <kbd>Ctrl</kbd>+<kbd>W</kbd> away pinned tabs.
Sounds silly now but it was a big deal back then since the only browser I was familiar with was Google Chrome.
Anyways, despite the poor decision,
this is probably the most important day in my search for the ultimate browser since it was the first major change I made on my own.
## Not enough
When I made the switched to Chromium, I was disappointed to see no changes in my browsing experience whatsoever.
Maybe if I used more advanced features I would have felt the difference but Chromium even supported account syncing back then
so I didn't experience any.
Familiarity isn't what I singed up for when I switched to Linux so I needed to find a new browser.
After constantly switching browser every couple of weeks for the next two years,
trying many, many different browsers, I finally settled on one: librewolf.
## Is this it?
I could write an entire post just listing what librewolf does things right but to keep things simple:
it is not an obscure browser, it is secure, and it respects my privacy.
To put it simply, it was the ultimate browser I was desperately looking for.
After configuring librewolf to suit my need, I was happiest I've ever been using a browser.
It created no cookies I didn't need, all my favorite extensions were there, and most importantly, I felt secure.
Not a single site was broken (at the time), and the only problem I had was the lack of performance.
I had to use chromium for io games that needed juicy 3 digit fps but other than that, I was satisfied.
I used librewolf all the way until I entered college.
## I came for copper but I found gold
Librewolf slowly lost its charm when firefox - the browser librewolf is based on -
was going in a direction I didn't like and some college related sites started breaking on librewolf.
I also never got used to opening chromium every other day.
One day, I was so fed up with the problems librewolf had that I decided to replace librewolf.
I considered using raw chromium again since they removed much of google-specific code,
but then I remembered that ungoogled chromium was a thing.
When I first saw ungoogled chromium way beck when I was trying different browsers,
it didn't really piqued my interest because back then I was heavily reliant on google's services
but now I barely use them at all so I knew it would work perfectly for me now.
I quickly configured ungoogled chromium to delete cookies and histories on exit, installed some of my favorite extensions,
and changed some security related settings and I was shocked to see how closely it resembled the feelings of librewolf.
As a added bonus, I don't have to open another browser to play io games.
## Conclusion
For now, I'm more than satisfied with ungoogled chromium but it's still far from being perfect.
Though most if not all google-specific code was removed,
the original code is written by Google and some of the borderline spyware features could potentially find its way to my computer.
Currently I'm not actively looking for the ultimate browser (and I don't think it even exists yet),
but I'm ready ditch ungoogled chromium the first chance I get.
I'll make sure to make a follow-up post if that ever happens.

View file

@ -0,0 +1,148 @@
---
title: Test post
date: 2021-07-26
tags:
- test
---
<!-- comment -->
This post exists to test various features such as markdown-to-html conversion, table of contents generation, and metadata parsing.<br />
## Link
<a href="/search">Go to search</a>
## Image
<img src="/icon/icon.svg" alt="developomp icon" width="100">
## Video
<div style="padding: 56.25% 0px 0px; position: relative;"><iframe src="https://www.youtube.com/embed/0jQRrChzdDQ?cc_load_policy=1&iv_load_policy=3&rel=0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen scrolling="no" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%;"></iframe></div>
## Table
| align right | align center | align left |
| ----------: | :----------: | :--------- |
| one | A | 1 |
| two | B | 2 |
| three | C | 3 |
## List
- Unordered list item
- Unordered list item
- unordered list sub-item
- unordered list sub-item
- [ ] Unordered task list item (unchecked)
- [x] Unordered task list item (checked)
- [ ] unordered task list sub-item (unchecked)
- [x] unordered task list sub-item (checked)
1. Ordered list item
2. Ordered list item
1. ordered list sub item
2. ordered list sub item
3. [ ] Ordered list task item (unchecked)
4. [x] Ordered list task item (checked)
1. [ ] Ordered list task sub-item (unchecked)
2. [x] Ordered list task sub-item (checked)
## Footnote
css only causes pain[^css_bad] and python is overrated[^python_is_overrated].
## Code
Here's a `code`.
```python {7,12,14-15}
print("And here's a language-specific code block")
# with comments and line highlighting!
x = 256
y = 256
print(x is y) # True. id(x) is indeed equal to id(y)
z = 257
w = 257
print(z is w) # False. id(z) is not equal to id(w)
# Apparently python does this to save memory usage.
# All integers between -5 and 256 share the same id.
```
## Text styling
> blockquote
>
> > nested blockquote
**bold**<br />
_italic_<br />
~~strikethrough~~<br />
<u>underlined</u><br />
==marked==<br />
this is a ^superscript^ (soon^TM^)<br />
and this is a ~subscript~ (H~2~O)
## CSS styling
<p align="center">
centered paragraph
</p>
<p style="color:rgb(255,0,0)">
RED
</p>
## Key
Do you remember the first time you pressed <kbd>Ctrl</kbd>+<kbd>C</kbd> in terminal?
## TeX
[$KaTeX$](https://katex.org/docs/supported.html) syntax is supported.
using [mhchem](https://mhchem.github.io/MathJax-mhchem) for chemical formula.
### Inline
$e=mc^2$ is actually $e^2=(mc^2)^2 + (pc)^2$.
### Block
$$
\ce{6 CO2 + 6 H2O <=>[{photosynthesis}][{respiration}] C6H12O6 + 6 O2}
$$
## headers
Headers have different size and indentation depending on their level.
- Post title: `h1`
- this section: `h2`
### h3
Lorem ipsum blah blah.
#### h4
Lorem ipsum blah blah.
##### h5
Lorem ipsum blah blah.
###### h6
Lorem ipsum blah blah.
<!-- Footnotes -->
[^css_bad]: Based on my experience building this website, Dec 2021.
[^python_is_overrated]: Based on my infinite wisdom, Dec 2021.

View file

@ -0,0 +1,16 @@
---
title: my quotes
date: 2021-08-01
---
We all have to constantly make small choices in our lives.
These choices include whether to study just 10 more minutes, doing just one more push-ups,
or waiting just 1 more second before getting mad at someone you care and love.
They may seem insignificant, but when put together, makes a huge difference.
Of course, even a 10 year old could tell what's the right decision to make in these situations.
However, many of us even fail to recognize the choices in the first place.
This is why I made a list of short, rememberable proverbs-like quotes so it serves as a guide not just for me but for other people too.
I wish the very best of luck to everyone who stumbled upon my blog.

View file

@ -0,0 +1,6 @@
---
title: My Quote NO.10
date: 2021-03-22
---
> People who earns highest respect from me are those who appreciate criticism.

View file

@ -0,0 +1,14 @@
---
title: My Quote NO.1
date: 2021-03-22
---
> What did you do when everyone in the world was running?
Procrastination has got to be the single worst thing that prevents people from fulfilling their dream.
One could easily find themselves spending hours sitting on the desk with no work done.
<!-- switch from 3rd to 2nd person point of view -->
One easy way to combat this is to surround yourself with hard-working people, however, this is not always possible.
In this case, it is helpful to remind yourself that there are people (possibly your colleague, classmate, etc.) working right now as you are procrastinating.

View file

@ -0,0 +1,13 @@
---
title: My Quote NO.2
date: 2021-03-22
---
> The 1000 miles you've walked so far are less important than another mile you are willing to walk.
At some point in everyone's career, after they passed the "mt. stupid" and the "valley of despair" of the Dunning-Kruger effect, they stop trying to learn new things.
They only work with what they already know and are familiar with, and never venture out into the forest of infinite knowledge.
Though this is less likely to happen in an environment with constant pressure (say for example, a school), not all jobs have this luxury.
<!-- This is why ... -->

View file

@ -0,0 +1,10 @@
---
title: My Quote NO.3
date: 2021-03-22
---
> Yesterday is a lecture for today.
Don't forget the peaks and the valleys of your life.
<!-- Experience => wisdom so always try to find something to learn in your life. -->

View file

@ -0,0 +1,8 @@
---
title: My Quote NO.4
date: 2021-03-22
---
> Those who see only the present lose their future.<br />
> Those who see only the future lose both the present and the future. <br />
> Only those who can see both the present and the future are given the future.

View file

@ -0,0 +1,8 @@
---
title: My Quote NO.5
date: 2021-03-22
---
> Words of wisdom deepens the more you think about it.
They should not be taken lightly.

View file

@ -0,0 +1,8 @@
---
title: My Quote NO.6
date: 2021-03-22
---
> The quickest way to learn the preciousness of time is to stare at a clock for 5 minutes.
This small investment will take you farther than you think.

View file

@ -0,0 +1,8 @@
---
title: My Quote NO.7
date: 2021-03-22
---
> Escape from the valleys of darkness doesn't happen in an instant.
It also often requires outside help.

View file

@ -0,0 +1,6 @@
---
title: My Quote NO.8
date: 2021-03-22
---
> Mind is like a sword. It will get dull if you stop sharpening it.

View file

@ -0,0 +1,6 @@
---
title: My Quote NO.9
date: 2021-03-22
---
> If you think too much about the answer, you'll forget what the question was.

View file

@ -0,0 +1,29 @@
---
title: 소개
---
## 누구세요?
이름: 김지민<br />
거주지: 서울, 대한만국<br />
출생: 2002년<br />
MBTI: [논리적인 사색가 INTP-A](https://www.16personalities.com/ko/성격유형-intp)
<p align="center">
<img alt="MBTI result" src="/img/mbti.png" style="display: block; margin-left: auto; margin-right: auto; max-width: 100%; width: 600px" />
2022년 4월 14일 <a href="https://16personalities.com">16personalities.com</a> 검사 결과
</p>
## 링크
- [깃허브](https://github.com/developomp)
- [목표](/goals)
## 연락
아래 기재된 방법 외에 다른 방법으로 연락을 할 시 답변을 드리지 못 할 수 있습니다.
| 플랫폼 | 아이디 | 답변 시간 |
| --------------------------------: | :----------------------------------: | :-------- |
| [디스코드](https://discord.com) | developomp#0001 (501277805540147220) | 즉각 |
| [지메일](https://mail.google.com) | developomp@gmail.com | 2~4일 |

View file

@ -0,0 +1,23 @@
---
title: About
---
## Who am I?
Name: Jimin Kim<br />
Location: South Korea<br />
Year of Birth: 2002
## Links
- [Github](https://github.com/developomp)
- [Goals](/goals)
## Contact
I may not be able to reply if contacted by any other methods other than what's listed below.
| Platform | ID | Response time |
| -------------------------------: | :----------------------------------: | :------------ |
| [Discord](https://discord.com) | developomp#0001 (501277805540147220) | Immediate |
| [Gmail](https://mail.google.com) | developomp@gmail.com | 2~4 days |

View file

@ -0,0 +1,73 @@
---
title: 목표
---
## 프로그래밍
- 깃허브에서 별 X개 얻기 (본인 미포함)
- [x] 10
- [ ] 100
- [ ] 500
- [ ] 1,000
- [ ] 10,000
- 깃허브에서 1년에 X개의 기여하기 (1월 1일 ~ 12월 31일)
- [x] 100
- [x] 500
- [x] 1000
- [ ] 2000
- [ ] 3000
- X일동안 하루에 적어도 한개의 커밋 push하기
- [x] 30
- [x] 50
- [x] 100
- [ ] 200
- [ ] 300
- [ ] 365
- [ ] 500
- [ ] 리눅스 커널에 코드 기여하기
- [ ] severity rating 7.0 (high, NVD CVSS v3)이상의 CVE 제출하기
- 알고리즘 문제풀이 ([solved.ac](https://solved.ac))
- 모든 X 문제 풀기
- [x] [브론즈 V](https://solved.ac/problems/level/1)
- [ ] [브론즈 IV](https://solved.ac/problems/level/2)
- X티어 달성하기
- [x] 브론즈
- [x] 실버 (현재 실버 I)
- [ ] 골드
- [ ] 플래티넘
- [ ] 다이아
- ~~루비~~
- X자리수 랭킹 달성하기
- [ ] 4
- [ ] 3
## 기술
- 분당 X타 치기 (규칙): 일분동안 타자를 친 후 맞은 타의 개수를 센다. 테스트는 [10fastfingers.com](https://10fastfingers.com/typing-test)를 통해 진행한다. 타자 속도는 일관성이 있어야 하며 요구시 같은 결과를 낼 수 있어야 한다.)
- 한국어
- [x] 100
- [x] 150
- [x] 200
- [ ] 250
- [ ] 300
- 영어
- [x] 100
- [x] 150
- [x] 200
- [x] 250
- [ ] 300
- [x] 키보드 안보고 타자치기
## Etc
- [ ] 유튜브에 만족할만한 퀄리티의 100만 조회수 이상의 영상 올리기
- [ ] 취직
- [ ] FAANG (또는 이에 준하는) 기업에 취직
- [ ] 집 구매
- [ ] 기술적 특이점 목격하기
- [ ] 달에 가기
- X번째 생일 축하하기
- [x] 15
- [x] 20
- [ ] 25
- [ ] 30

View file

@ -0,0 +1,73 @@
---
title: Goals
---
## Programming
- Get a total of X stars on github (not counting mine)
- [x] 10
- [ ] 100
- [ ] 500
- [ ] 1,000
- [ ] 10,000
- Make X github contributions in a year (jan 1 ~ dec 31)
- [x] 100
- [x] 500
- [x] 1000
- [ ] 2000
- [ ] 3000
- Push at least one commit every day for X days
- [x] 30
- [x] 50
- [x] 100
- [ ] 200
- [ ] 300
- [ ] 365
- [ ] 500
- [ ] Get my code into the linux kernel
- [ ] submit a CVE with the severity rating 7.0 (high) or above (NVD CVSS v3)
- Algorithm problem solving ([solved.ac](https://solved.ac))
- solve all X problems
- [x] [Bronze V](https://solved.ac/problems/level/1)
- [ ] [브론즈 IV](https://solved.ac/problems/level/2)
- Reach X tier
- [x] Bronze
- [x] Silver (Currently Silver I)
- [ ] Gold
- [ ] Platinum
- [ ] Diamond
- ~~Ruby~~
- Reach X digit global ranking
- [ ] 4
- [ ] 3
## Lean how to
- Type at least X letters per minute (rules: count the number of correct keystrokes made in one minute. Use [10fastfingers.com](https://10fastfingers.com/typing-test) for testing. The typing speed must be consistent and could be replicated on-demand.)
- Korean
- [x] 100
- [x] 150
- [x] 200
- [ ] 250
- [ ] 300
- English
- [x] 100
- [x] 150
- [x] 200
- [x] 250
- [ ] 300
- [x] Type without looking at the keyboard
## Etc
- [ ] Make a high quality video that I'm proud of with at least 1M views on YouTube
- [ ] Get a job
- [ ] Get a job at FAANG (or some future equivalent of it)
- [ ] Buy a house
- [ ] Witness the technological singularity
- [ ] Go to the moon
- Celebrate my Xth birthday
- [x] 15
- [x] 20
- [ ] 25
- [ ] 30

View file

@ -0,0 +1,34 @@
---
title: 이력서
---
## 김지민
[![깃허브](https://img.shields.io/badge/깃허브-black?style=for-the-badge&logo=github)](https://github.com/developomp)
[![포트폴리오](https://img.shields.io/badge/포트폴리오-grey?style=for-the-badge)](/portfolio)
프론트엔드 엔지니어 지망생
무엇이든지 직접 만들어보아야 직성이 풀리고 궁금한 것이 있으면 나사 하나,
전선 하나까지 뜯어서 원리를 이해하기 전까지 만족할 줄 모르는 천성 개발자 김지민입니다.
특징:
- [아치 리눅스](https://archlinux.org)를 사용중 ([깃허브](https://github.com/developomp/setup))
- 한국어와 영어를 원어민 수준으로 구사 할 수 있음
이메일: developomp@gmail.com
## 교육
### [홍익대학교](https://wwwce.hongik.ac.kr) 컴퓨터공학과
- 2022년 3월 - 현재
## 깃허브
<img alt="github metrics" src="https://raw.githubusercontent.com/developomp/developomp/master/github-metrics.svg" style="display: block; margin-left: auto; margin-right: auto; max-width: 100%;">
## 기술
<img alt="programming skills" src="/img/skills.svg" style="display: block; margin-left: auto; margin-right: auto; max-width: 100%;" />

View file

@ -0,0 +1,34 @@
---
title: Resume
---
## Jimin Kim
[![Github](https://img.shields.io/badge/github-black?style=for-the-badge&logo=github)](https://github.com/developomp)
[![Portfolio](https://img.shields.io/badge/portfolio-grey?style=for-the-badge)](/portfolio)
Frontend engineer wannabe
A natural-born developer who has got to create everything with his own hand.
He won't be satisfied until he breaks everything down to its components and understands what's behind it.
Characteristics:
- daily drives [arch linux](https://archlinux.org)
- can fluently speak, read, and write English and Korean at a native level
Email: developomp@gmail.com
## Education
### [Hongik university](https://wwwce.hongik.ac.kr) computer science major
- Mar 2022 - now
## Github
<img alt="github metrics" src="https://raw.githubusercontent.com/developomp/developomp/master/github-metrics.svg" style="display: block; margin-left: auto; margin-right: auto; max-width: 100%;">
## Skills
<img alt="programming skills" src="/img/skills.svg" style="display: block; margin-left: auto; margin-right: auto; max-width: 100%;" />

89
apps/blog/package.json Normal file
View file

@ -0,0 +1,89 @@
{
"name": "@developomp-site/blog",
"version": "0.0.0",
"private": true,
"scripts": {
"generate": "ts-node -O '{\"module\":\"commonjs\"}' --files ./generate",
"dev": "npm run generate && react-scripts start",
"build": "npm run generate && react-scripts build",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf build"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-regular-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"elasticlunr": "^0.9.5",
"highlight.js": "^11.7.0",
"katex": "^0.16.3",
"local-storage-fallback": "^4.1.2",
"react": "^18.2.0",
"react-collapse": "^5.1.1",
"react-date-range": "^1.4.0",
"react-device-detect": "^2.2.2",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-helmet-async": "^1.3.0",
"react-router-dom": "^6.4.4",
"react-scripts": "^5.0.1",
"react-select": "^5.7.0",
"react-tooltip": "^4.5.1",
"styled-components": "^5.3.6"
},
"devDependencies": {
"@developomp-site/tsconfig": "workspace:0.0.0",
"@developomp-site/eslint-config": "workspace:0.0.0",
"@types/ejs": "^3.1.1",
"@types/elasticlunr": "^0.9.5",
"@types/highlight.js": "^10.1.0",
"@types/jsdom": "^20.0.1",
"@types/katex": "^0.14.0",
"@types/markdown-it": "^12.2.3",
"@types/node": "^18.11.10",
"@types/react": "^18.0.26",
"@types/react-collapse": "^5.0.1",
"@types/react-date-range": "^1.4.4",
"@types/react-dom": "^18.0.9",
"@types/react-select": "^5.0.1",
"@types/styled-components": "^5.1.26",
"@types/svgo": "^3.0.0",
"@types/tinycolor2": "^1.4.3",
"ejs": "^3.1.8",
"gray-matter": "^4.0.3",
"jsdom": "^20.0.3",
"jspdf": "^2.5.1",
"markdown-it": "^13.0.1",
"markdown-it-anchor": "^8.6.5",
"markdown-it-attrs": "^4.1.4",
"markdown-it-footnote": "^3.0.3",
"markdown-it-highlight-lines": "^1.0.2",
"markdown-it-mark": "^3.0.1",
"markdown-it-sub": "^1.0.0",
"markdown-it-sup": "^1.0.0",
"markdown-it-task-checkbox": "^1.0.6",
"markdown-it-texmath": "^1.0.0",
"markdown-toc": "^1.2.0",
"prettier": "^2.8.0",
"read-time-estimate": "^0.0.3",
"simple-icons": "^7.21.0",
"svgo": "^3.0.2",
"tinycolor2": "^1.4.2",
"ts-node": "^10.9.1",
"tslint-config-prettier": "^1.18.0",
"typescript": "^4.9.3"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

View file

@ -0,0 +1,4 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="800" height="800" fill="black"/>
<path d="M217.617 226.262H184.863C180.632 226.262 176.23 226.77 171.66 227.785C167.259 228.632 163.197 230.07 159.473 232.102C155.749 234.133 152.702 236.757 150.332 239.973C147.962 243.189 146.777 247.082 146.777 251.652V252.16V341.281C146.777 354.315 144.915 365.826 141.191 375.812C137.467 385.63 132.643 394.178 126.719 401.457C133.828 410.598 138.822 420.415 141.699 430.91C144.746 441.405 146.27 451.223 146.27 460.363V550.246C146.27 556.171 147.793 560.91 150.84 564.465C153.887 567.85 157.526 570.474 161.758 572.336C165.99 574.029 170.221 575.129 174.453 575.637C178.685 575.975 181.986 576.145 184.355 576.145H217.109V642.922H183.848C176.908 642.922 169.46 642.16 161.504 640.637C153.717 639.283 145.931 636.997 138.145 633.781C130.527 630.734 123.164 626.757 116.055 621.848C109.115 616.939 102.936 611.014 97.5195 604.074C92.2721 597.134 88.0404 589.178 84.8242 580.207C81.7773 571.236 80.2539 561.079 80.2539 549.738V460.871C80.2539 451.9 77.8841 445.214 73.1445 440.812C68.5742 436.411 62.1419 434.211 53.8477 434.211C49.1081 434.211 44.707 433.365 40.6445 431.672C36.582 429.81 33.0273 427.44 29.9805 424.562C27.1029 421.516 24.8177 417.961 23.125 413.898C21.4323 409.836 20.5859 405.52 20.5859 400.949C20.5859 396.379 21.4323 392.147 23.125 388.254C24.8177 384.191 27.1029 380.721 29.9805 377.844C33.0273 374.797 36.4974 372.427 40.3906 370.734C44.4531 368.872 48.7695 367.857 53.3398 367.688C62.4805 367.688 69.3359 365.487 73.9062 361.086C78.4766 356.516 80.7617 349.914 80.7617 341.281V251.652C80.7617 237.095 83.724 224.146 89.6484 212.805C95.5729 201.464 103.444 191.9 113.262 184.113C123.079 176.327 134.167 170.402 146.523 166.34C159.049 162.277 171.829 160.246 184.863 160.246H217.617V226.262ZM595.47 371.242C595.47 386.307 593.608 399.764 589.884 411.613C586.16 423.462 581.082 433.957 574.65 443.098C568.387 452.069 561.108 459.771 552.814 466.203C544.52 472.635 535.802 477.883 526.662 481.945C517.69 486.008 508.55 488.97 499.24 490.832C490.099 492.694 481.466 493.625 473.341 493.625H324.045V427.609H473.341C481.805 426.932 489.422 425.24 496.193 422.531C503.133 419.654 509.058 415.845 513.966 411.105C518.875 406.366 522.684 400.695 525.392 394.094C528.101 387.323 529.455 379.706 529.455 371.242V341.281C528.608 332.987 526.831 325.37 524.123 318.43C521.414 311.49 517.69 305.565 512.951 300.656C508.38 295.747 502.795 291.939 496.193 289.23C489.591 286.353 481.974 284.914 473.341 284.914H324.552C315.75 284.914 309.064 287.199 304.494 291.77C299.923 296.34 297.638 302.941 297.638 311.574V583H231.623V311.574C231.623 294.647 234.67 280.259 240.763 268.41C247.026 256.561 254.728 246.997 263.869 239.719C273.179 232.44 283.25 227.193 294.084 223.977C304.917 220.591 314.904 218.898 324.045 218.898H473.341C488.237 218.898 501.61 220.845 513.459 224.738C525.308 228.462 535.718 233.54 544.689 239.973C553.83 246.236 561.532 253.514 567.795 261.809C574.227 270.103 579.474 278.82 583.537 287.961C587.768 296.932 590.815 306.073 592.677 315.383C594.539 324.523 595.47 333.156 595.47 341.281V371.242ZM778.07 400.949C778.07 405.52 777.223 409.836 775.53 413.898C773.838 417.961 771.468 421.516 768.421 424.562C765.543 427.44 762.073 429.81 758.011 431.672C754.118 433.365 749.886 434.211 745.316 434.211C736.683 434.211 729.997 436.411 725.257 440.812C720.687 445.214 718.402 451.9 718.402 460.871L718.909 549.738C718.909 561.079 717.301 571.236 714.085 580.207C710.869 589.178 706.553 597.134 701.136 604.074C695.719 611.014 689.456 616.939 682.347 621.848C675.407 626.757 668.043 630.734 660.257 633.781C652.64 636.997 644.853 639.283 636.898 640.637C629.111 642.16 621.748 642.922 614.808 642.922H582.054L581.546 576.145H614.808C617.008 576.145 620.224 575.975 624.456 575.637C628.688 575.129 632.835 574.029 636.898 572.336C641.129 570.474 644.769 567.85 647.816 564.465C650.862 560.91 652.386 556.171 652.386 550.246V460.363C652.386 451.223 653.909 441.405 656.956 430.91C660.172 420.415 665.166 410.598 671.937 401.457C666.012 394.178 661.103 385.63 657.21 375.812C653.486 365.826 651.624 354.315 651.624 341.281V252.16V251.652C650.778 246.913 649.17 242.935 646.8 239.719C644.599 236.503 641.806 233.879 638.421 231.848C635.036 229.816 631.227 228.378 626.995 227.531C622.933 226.685 618.701 226.262 614.3 226.262H581.038V160.246H614.3C628.011 160.246 641.129 162.447 653.655 166.848C666.181 171.079 677.184 177.258 686.663 185.383C696.312 193.339 703.929 202.987 709.515 214.328C715.101 225.5 717.894 237.941 717.894 251.652C717.894 252.837 717.978 260.878 718.148 275.773C718.317 290.5 718.402 312.336 718.402 341.281C718.402 349.914 720.602 356.516 725.003 361.086C729.404 365.487 736.175 367.688 745.316 367.688C749.886 367.857 754.118 368.872 758.011 370.734C762.073 372.427 765.543 374.797 768.421 377.844C771.468 380.721 773.838 384.191 775.53 388.254C777.223 392.147 778.07 396.379 778.07 400.949Z" fill="#F4F4F4"/>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -0,0 +1,6 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<circle cx="400" cy="400" r="400" fill="black"/>
<path d="M217.617 226.262H184.863C180.632 226.262 176.23 226.77 171.66 227.785C167.259 228.632 163.197 230.07 159.473 232.102C155.749 234.133 152.702 236.757 150.332 239.973C147.962 243.189 146.777 247.082 146.777 251.652V252.16V341.281C146.777 354.315 144.915 365.826 141.191 375.812C137.467 385.63 132.643 394.178 126.719 401.457C133.828 410.598 138.822 420.415 141.699 430.91C144.746 441.405 146.27 451.223 146.27 460.363V550.246C146.27 556.171 147.793 560.91 150.84 564.465C153.887 567.85 157.526 570.474 161.758 572.336C165.99 574.029 170.221 575.129 174.453 575.637C178.685 575.975 181.986 576.145 184.355 576.145H217.109V642.922H183.848C176.908 642.922 169.46 642.16 161.504 640.637C153.717 639.283 145.931 636.997 138.145 633.781C130.527 630.734 123.164 626.757 116.055 621.848C109.115 616.939 102.936 611.014 97.5195 604.074C92.2721 597.134 88.0404 589.178 84.8242 580.207C81.7773 571.236 80.2539 561.079 80.2539 549.738V460.871C80.2539 451.9 77.8841 445.214 73.1445 440.812C68.5742 436.411 62.1419 434.211 53.8477 434.211C49.1081 434.211 44.707 433.365 40.6445 431.672C36.582 429.81 33.0273 427.44 29.9805 424.562C27.1029 421.516 24.8177 417.961 23.125 413.898C21.4323 409.836 20.5859 405.52 20.5859 400.949C20.5859 396.379 21.4323 392.147 23.125 388.254C24.8177 384.191 27.1029 380.721 29.9805 377.844C33.0273 374.797 36.4974 372.427 40.3906 370.734C44.4531 368.872 48.7695 367.857 53.3398 367.688C62.4805 367.688 69.3359 365.487 73.9062 361.086C78.4766 356.516 80.7617 349.914 80.7617 341.281V251.652C80.7617 237.095 83.724 224.146 89.6484 212.805C95.5729 201.464 103.444 191.9 113.262 184.113C123.079 176.327 134.167 170.402 146.523 166.34C159.049 162.277 171.829 160.246 184.863 160.246H217.617V226.262ZM595.47 371.242C595.47 386.307 593.608 399.764 589.884 411.613C586.16 423.462 581.082 433.957 574.65 443.098C568.387 452.069 561.108 459.771 552.814 466.203C544.52 472.635 535.802 477.883 526.662 481.945C517.69 486.008 508.55 488.97 499.24 490.832C490.099 492.694 481.466 493.625 473.341 493.625H324.045V427.609H473.341C481.805 426.932 489.422 425.24 496.193 422.531C503.133 419.654 509.058 415.845 513.966 411.105C518.875 406.366 522.684 400.695 525.392 394.094C528.101 387.323 529.455 379.706 529.455 371.242V341.281C528.608 332.987 526.831 325.37 524.123 318.43C521.414 311.49 517.69 305.565 512.951 300.656C508.38 295.747 502.795 291.939 496.193 289.23C489.591 286.353 481.974 284.914 473.341 284.914H324.552C315.75 284.914 309.064 287.199 304.494 291.77C299.923 296.34 297.638 302.941 297.638 311.574V583H231.623V311.574C231.623 294.647 234.67 280.259 240.763 268.41C247.026 256.561 254.728 246.997 263.869 239.719C273.179 232.44 283.25 227.193 294.084 223.977C304.917 220.591 314.904 218.898 324.045 218.898H473.341C488.237 218.898 501.61 220.845 513.459 224.738C525.308 228.462 535.718 233.54 544.689 239.973C553.83 246.236 561.532 253.514 567.795 261.809C574.227 270.103 579.474 278.82 583.537 287.961C587.768 296.932 590.815 306.073 592.677 315.383C594.539 324.523 595.47 333.156 595.47 341.281V371.242ZM778.07 400.949C778.07 405.52 777.223 409.836 775.53 413.898C773.838 417.961 771.468 421.516 768.421 424.562C765.543 427.44 762.073 429.81 758.011 431.672C754.118 433.365 749.886 434.211 745.316 434.211C736.683 434.211 729.997 436.411 725.257 440.812C720.687 445.214 718.402 451.9 718.402 460.871L718.909 549.738C718.909 561.079 717.301 571.236 714.085 580.207C710.869 589.178 706.553 597.134 701.136 604.074C695.719 611.014 689.456 616.939 682.347 621.848C675.407 626.757 668.043 630.734 660.257 633.781C652.64 636.997 644.853 639.283 636.898 640.637C629.111 642.16 621.748 642.922 614.808 642.922H582.054L581.546 576.145H614.808C617.008 576.145 620.224 575.975 624.456 575.637C628.688 575.129 632.835 574.029 636.898 572.336C641.129 570.474 644.769 567.85 647.816 564.465C650.862 560.91 652.386 556.171 652.386 550.246V460.363C652.386 451.223 653.909 441.405 656.956 430.91C660.172 420.415 665.166 410.598 671.937 401.457C666.012 394.178 661.103 385.63 657.21 375.812C653.486 365.826 651.624 354.315 651.624 341.281V252.16V251.652C650.778 246.913 649.17 242.935 646.8 239.719C644.599 236.503 641.806 233.879 638.421 231.848C635.036 229.816 631.227 228.378 626.995 227.531C622.933 226.685 618.701 226.262 614.3 226.262H581.038V160.246H614.3C628.011 160.246 641.129 162.447 653.655 166.848C666.181 171.079 677.184 177.258 686.663 185.383C696.312 193.339 703.929 202.987 709.515 214.328C715.101 225.5 717.894 237.941 717.894 251.652C717.894 252.837 717.978 260.878 718.148 275.773C718.317 290.5 718.402 312.336 718.402 341.281C718.402 349.914 720.602 356.516 725.003 361.086C729.404 365.487 736.175 367.688 745.316 367.688C749.886 367.857 754.118 368.872 758.011 370.734C762.073 372.427 765.543 374.797 768.421 377.844C771.468 380.721 773.838 384.191 775.53 388.254C777.223 392.147 778.07 396.379 778.07 400.949Z" fill="#F4F4F4"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -0,0 +1,3 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M217.617 226.262H184.863C180.632 226.262 176.23 226.77 171.66 227.785C167.259 228.632 163.197 230.07 159.473 232.102C155.749 234.133 152.702 236.757 150.332 239.973C147.962 243.189 146.777 247.082 146.777 251.652V252.16V341.281C146.777 354.315 144.915 365.826 141.191 375.812C137.467 385.63 132.643 394.178 126.719 401.457C133.828 410.598 138.822 420.415 141.699 430.91C144.746 441.405 146.27 451.223 146.27 460.363V550.246C146.27 556.171 147.793 560.91 150.84 564.465C153.887 567.85 157.526 570.474 161.758 572.336C165.99 574.029 170.221 575.129 174.453 575.637C178.685 575.975 181.986 576.145 184.355 576.145H217.109V642.922H183.848C176.908 642.922 169.46 642.16 161.504 640.637C153.717 639.283 145.931 636.997 138.145 633.781C130.527 630.734 123.164 626.757 116.055 621.848C109.115 616.939 102.936 611.014 97.5195 604.074C92.2721 597.134 88.0404 589.178 84.8242 580.207C81.7773 571.236 80.2539 561.079 80.2539 549.738V460.871C80.2539 451.9 77.8841 445.214 73.1445 440.812C68.5742 436.411 62.1419 434.211 53.8477 434.211C49.1081 434.211 44.707 433.365 40.6445 431.672C36.582 429.81 33.0273 427.44 29.9805 424.562C27.1029 421.516 24.8177 417.961 23.125 413.898C21.4323 409.836 20.5859 405.52 20.5859 400.949C20.5859 396.379 21.4323 392.147 23.125 388.254C24.8177 384.191 27.1029 380.721 29.9805 377.844C33.0273 374.797 36.4974 372.427 40.3906 370.734C44.4531 368.872 48.7695 367.857 53.3398 367.688C62.4805 367.688 69.3359 365.487 73.9062 361.086C78.4766 356.516 80.7617 349.914 80.7617 341.281V251.652C80.7617 237.095 83.724 224.146 89.6484 212.805C95.5729 201.464 103.444 191.9 113.262 184.113C123.079 176.327 134.167 170.402 146.523 166.34C159.049 162.277 171.829 160.246 184.863 160.246H217.617V226.262ZM595.47 371.242C595.47 386.307 593.608 399.764 589.884 411.613C586.16 423.462 581.082 433.957 574.65 443.098C568.387 452.069 561.108 459.771 552.814 466.203C544.52 472.635 535.802 477.883 526.662 481.945C517.69 486.008 508.55 488.97 499.24 490.832C490.099 492.694 481.466 493.625 473.341 493.625H324.045V427.609H473.341C481.805 426.932 489.422 425.24 496.193 422.531C503.133 419.654 509.058 415.845 513.966 411.105C518.875 406.366 522.684 400.695 525.392 394.094C528.101 387.323 529.455 379.706 529.455 371.242V341.281C528.608 332.987 526.831 325.37 524.123 318.43C521.414 311.49 517.69 305.565 512.951 300.656C508.38 295.747 502.795 291.939 496.193 289.23C489.591 286.353 481.974 284.914 473.341 284.914H324.552C315.75 284.914 309.064 287.199 304.494 291.77C299.923 296.34 297.638 302.941 297.638 311.574V583H231.623V311.574C231.623 294.647 234.67 280.259 240.763 268.41C247.026 256.561 254.728 246.997 263.869 239.719C273.179 232.44 283.25 227.193 294.084 223.977C304.917 220.591 314.904 218.898 324.045 218.898H473.341C488.237 218.898 501.61 220.845 513.459 224.738C525.308 228.462 535.718 233.54 544.689 239.973C553.83 246.236 561.532 253.514 567.795 261.809C574.227 270.103 579.474 278.82 583.537 287.961C587.768 296.932 590.815 306.073 592.677 315.383C594.539 324.523 595.47 333.156 595.47 341.281V371.242ZM778.07 400.949C778.07 405.52 777.223 409.836 775.53 413.898C773.838 417.961 771.468 421.516 768.421 424.562C765.543 427.44 762.073 429.81 758.011 431.672C754.118 433.365 749.886 434.211 745.316 434.211C736.683 434.211 729.997 436.411 725.257 440.812C720.687 445.214 718.402 451.9 718.402 460.871L718.909 549.738C718.909 561.079 717.301 571.236 714.085 580.207C710.869 589.178 706.553 597.134 701.136 604.074C695.719 611.014 689.456 616.939 682.347 621.848C675.407 626.757 668.043 630.734 660.257 633.781C652.64 636.997 644.853 639.283 636.898 640.637C629.111 642.16 621.748 642.922 614.808 642.922H582.054L581.546 576.145H614.808C617.008 576.145 620.224 575.975 624.456 575.637C628.688 575.129 632.835 574.029 636.898 572.336C641.129 570.474 644.769 567.85 647.816 564.465C650.862 560.91 652.386 556.171 652.386 550.246V460.363C652.386 451.223 653.909 441.405 656.956 430.91C660.172 420.415 665.166 410.598 671.937 401.457C666.012 394.178 661.103 385.63 657.21 375.812C653.486 365.826 651.624 354.315 651.624 341.281V252.16V251.652C650.778 246.913 649.17 242.935 646.8 239.719C644.599 236.503 641.806 233.879 638.421 231.848C635.036 229.816 631.227 228.378 626.995 227.531C622.933 226.685 618.701 226.262 614.3 226.262H581.038V160.246H614.3C628.011 160.246 641.129 162.447 653.655 166.848C666.181 171.079 677.184 177.258 686.663 185.383C696.312 193.339 703.929 202.987 709.515 214.328C715.101 225.5 717.894 237.941 717.894 251.652C717.894 252.837 717.978 260.878 718.148 275.773C718.317 290.5 718.402 312.336 718.402 341.281C718.402 349.914 720.602 356.516 725.003 361.086C729.404 365.487 736.175 367.688 745.316 367.688C749.886 367.857 754.118 368.872 758.011 370.734C762.073 372.427 765.543 374.797 768.421 377.844C771.468 380.721 773.838 384.191 775.53 388.254C777.223 392.147 778.07 396.379 778.07 400.949Z" fill="#F4F4F4" />
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -0,0 +1,5 @@
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="500" height="500" fill="black"/>
<path d="M145.094 150.189H126.197C123.756 150.189 121.217 150.482 118.58 151.068C116.041 151.557 113.697 152.387 111.549 153.559C109.4 154.73 107.643 156.244 106.275 158.1C104.908 159.955 104.225 162.201 104.225 164.838V165.131V216.547C104.225 224.066 103.15 230.707 101.002 236.469C98.8535 242.133 96.0703 247.064 92.6523 251.264C96.7539 256.537 99.6348 262.201 101.295 268.256C103.053 274.311 103.932 279.975 103.932 285.248V337.104C103.932 340.521 104.811 343.256 106.568 345.307C108.326 347.26 110.426 348.773 112.867 349.848C115.309 350.824 117.75 351.459 120.191 351.752C122.633 351.947 124.537 352.045 125.904 352.045H144.801V390.57H125.611C121.607 390.57 117.311 390.131 112.721 389.252C108.229 388.471 103.736 387.152 99.2441 385.297C94.8496 383.539 90.6016 381.244 86.5 378.412C82.4961 375.58 78.9316 372.162 75.8066 368.158C72.7793 364.154 70.3379 359.564 68.4824 354.389C66.7246 349.213 65.8457 343.354 65.8457 336.811V285.541C65.8457 280.365 64.4785 276.508 61.7441 273.969C59.1074 271.43 55.3965 270.16 50.6113 270.16C47.877 270.16 45.3379 269.672 42.9941 268.695C40.6504 267.621 38.5996 266.254 36.8418 264.594C35.1816 262.836 33.8633 260.785 32.8867 258.441C31.9102 256.098 31.4219 253.607 31.4219 250.971C31.4219 248.334 31.9102 245.893 32.8867 243.646C33.8633 241.303 35.1816 239.301 36.8418 237.641C38.5996 235.883 40.6016 234.516 42.8477 233.539C45.1914 232.465 47.6816 231.879 50.3184 231.781C55.5918 231.781 59.5469 230.512 62.1836 227.973C64.8203 225.336 66.1387 221.527 66.1387 216.547V164.838C66.1387 156.439 67.8477 148.969 71.2656 142.426C74.6836 135.883 79.2246 130.365 84.8887 125.873C90.5527 121.381 96.9492 117.963 104.078 115.619C111.305 113.275 118.678 112.104 126.197 112.104H145.094V150.189ZM363.086 233.832C363.086 242.523 362.012 250.287 359.863 257.123C357.715 263.959 354.785 270.014 351.074 275.287C347.461 280.463 343.262 284.906 338.477 288.617C333.691 292.328 328.662 295.355 323.389 297.699C318.213 300.043 312.939 301.752 307.568 302.826C302.295 303.9 297.314 304.438 292.627 304.438H206.494V266.352H292.627C297.51 265.961 301.904 264.984 305.811 263.422C309.814 261.762 313.232 259.564 316.064 256.83C318.896 254.096 321.094 250.824 322.656 247.016C324.219 243.109 325 238.715 325 233.832V216.547C324.512 211.762 323.486 207.367 321.924 203.363C320.361 199.359 318.213 195.941 315.479 193.109C312.842 190.277 309.619 188.08 305.811 186.518C302.002 184.857 297.607 184.027 292.627 184.027H206.787C201.709 184.027 197.852 185.346 195.215 187.982C192.578 190.619 191.26 194.428 191.26 199.408V356H153.174V199.408C153.174 189.643 154.932 181.342 158.447 174.506C162.061 167.67 166.504 162.152 171.777 157.953C177.148 153.754 182.959 150.727 189.209 148.871C195.459 146.918 201.221 145.941 206.494 145.941H292.627C301.221 145.941 308.936 147.064 315.771 149.311C322.607 151.459 328.613 154.389 333.789 158.1C339.062 161.713 343.506 165.912 347.119 170.697C350.83 175.482 353.857 180.512 356.201 185.785C358.643 190.961 360.4 196.234 361.475 201.605C362.549 206.879 363.086 211.859 363.086 216.547V233.832ZM468.432 250.971C468.432 253.607 467.943 256.098 466.967 258.441C465.99 260.785 464.623 262.836 462.865 264.594C461.205 266.254 459.203 267.621 456.859 268.695C454.613 269.672 452.172 270.16 449.535 270.16C444.555 270.16 440.697 271.43 437.963 273.969C435.326 276.508 434.008 280.365 434.008 285.541L434.301 336.811C434.301 343.354 433.373 349.213 431.518 354.389C429.662 359.564 427.172 364.154 424.047 368.158C420.922 372.162 417.309 375.58 413.207 378.412C409.203 381.244 404.955 383.539 400.463 385.297C396.068 387.152 391.576 388.471 386.986 389.252C382.494 390.131 378.246 390.57 374.242 390.57H355.346L355.053 352.045H374.242C375.512 352.045 377.367 351.947 379.809 351.752C382.25 351.459 384.643 350.824 386.986 349.848C389.428 348.773 391.527 347.26 393.285 345.307C395.043 343.256 395.922 340.521 395.922 337.104V285.248C395.922 279.975 396.801 274.311 398.559 268.256C400.414 262.201 403.295 256.537 407.201 251.264C403.783 247.064 400.951 242.133 398.705 236.469C396.557 230.707 395.482 224.066 395.482 216.547V165.131V164.838C394.994 162.104 394.066 159.809 392.699 157.953C391.43 156.098 389.818 154.584 387.865 153.412C385.912 152.24 383.715 151.41 381.273 150.922C378.93 150.434 376.488 150.189 373.949 150.189H354.76V112.104H373.949C381.859 112.104 389.428 113.373 396.654 115.912C403.881 118.354 410.229 121.918 415.697 126.605C421.264 131.195 425.658 136.762 428.881 143.305C432.104 149.75 433.715 156.928 433.715 164.838C433.715 165.521 433.764 170.16 433.861 178.754C433.959 187.25 434.008 199.848 434.008 216.547C434.008 221.527 435.277 225.336 437.816 227.973C440.355 230.512 444.262 231.781 449.535 231.781C452.172 231.879 454.613 232.465 456.859 233.539C459.203 234.516 461.205 235.883 462.865 237.641C464.623 239.301 465.99 241.303 466.967 243.646C467.943 245.893 468.432 248.334 468.432 250.971Z" fill="#F4F4F4"/>
<circle cx="250" cy="250" r="240" stroke="#F4F4F4" stroke-width="20"/>
</svg>

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -0,0 +1,4 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="800" height="800" fill="black"/>
<path d="M217.617 226.262H184.863C180.632 226.262 176.23 226.77 171.66 227.785C167.259 228.632 163.197 230.07 159.473 232.102C155.749 234.133 152.702 236.757 150.332 239.973C147.962 243.189 146.777 247.082 146.777 251.652V252.16V341.281C146.777 354.315 144.915 365.826 141.191 375.812C137.467 385.63 132.643 394.178 126.719 401.457C133.828 410.598 138.822 420.415 141.699 430.91C144.746 441.405 146.27 451.223 146.27 460.363V550.246C146.27 556.171 147.793 560.91 150.84 564.465C153.887 567.85 157.526 570.474 161.758 572.336C165.99 574.029 170.221 575.129 174.453 575.637C178.685 575.975 181.986 576.145 184.355 576.145H217.109V642.922H183.848C176.908 642.922 169.46 642.16 161.504 640.637C153.717 639.283 145.931 636.997 138.145 633.781C130.527 630.734 123.164 626.757 116.055 621.848C109.115 616.939 102.936 611.014 97.5195 604.074C92.2721 597.134 88.0404 589.178 84.8242 580.207C81.7773 571.236 80.2539 561.079 80.2539 549.738V460.871C80.2539 451.9 77.8841 445.214 73.1445 440.812C68.5742 436.411 62.1419 434.211 53.8477 434.211C49.1081 434.211 44.707 433.365 40.6445 431.672C36.582 429.81 33.0273 427.44 29.9805 424.562C27.1029 421.516 24.8177 417.961 23.125 413.898C21.4323 409.836 20.5859 405.52 20.5859 400.949C20.5859 396.379 21.4323 392.147 23.125 388.254C24.8177 384.191 27.1029 380.721 29.9805 377.844C33.0273 374.797 36.4974 372.427 40.3906 370.734C44.4531 368.872 48.7695 367.857 53.3398 367.688C62.4805 367.688 69.3359 365.487 73.9062 361.086C78.4766 356.516 80.7617 349.914 80.7617 341.281V251.652C80.7617 237.095 83.724 224.146 89.6484 212.805C95.5729 201.464 103.444 191.9 113.262 184.113C123.079 176.327 134.167 170.402 146.523 166.34C159.049 162.277 171.829 160.246 184.863 160.246H217.617V226.262ZM595.47 371.242C595.47 386.307 593.608 399.764 589.884 411.613C586.16 423.462 581.082 433.957 574.65 443.098C568.387 452.069 561.108 459.771 552.814 466.203C544.52 472.635 535.802 477.883 526.662 481.945C517.69 486.008 508.55 488.97 499.24 490.832C490.099 492.694 481.466 493.625 473.341 493.625H324.045V427.609H473.341C481.805 426.932 489.422 425.24 496.193 422.531C503.133 419.654 509.058 415.845 513.966 411.105C518.875 406.366 522.684 400.695 525.392 394.094C528.101 387.323 529.455 379.706 529.455 371.242V341.281C528.608 332.987 526.831 325.37 524.123 318.43C521.414 311.49 517.69 305.565 512.951 300.656C508.38 295.747 502.795 291.939 496.193 289.23C489.591 286.353 481.974 284.914 473.341 284.914H324.552C315.75 284.914 309.064 287.199 304.494 291.77C299.923 296.34 297.638 302.941 297.638 311.574V583H231.623V311.574C231.623 294.647 234.67 280.259 240.763 268.41C247.026 256.561 254.728 246.997 263.869 239.719C273.179 232.44 283.25 227.193 294.084 223.977C304.917 220.591 314.904 218.898 324.045 218.898H473.341C488.237 218.898 501.61 220.845 513.459 224.738C525.308 228.462 535.718 233.54 544.689 239.973C553.83 246.236 561.532 253.514 567.795 261.809C574.227 270.103 579.474 278.82 583.537 287.961C587.768 296.932 590.815 306.073 592.677 315.383C594.539 324.523 595.47 333.156 595.47 341.281V371.242ZM778.07 400.949C778.07 405.52 777.223 409.836 775.53 413.898C773.838 417.961 771.468 421.516 768.421 424.562C765.543 427.44 762.073 429.81 758.011 431.672C754.118 433.365 749.886 434.211 745.316 434.211C736.683 434.211 729.997 436.411 725.257 440.812C720.687 445.214 718.402 451.9 718.402 460.871L718.909 549.738C718.909 561.079 717.301 571.236 714.085 580.207C710.869 589.178 706.553 597.134 701.136 604.074C695.719 611.014 689.456 616.939 682.347 621.848C675.407 626.757 668.043 630.734 660.257 633.781C652.64 636.997 644.853 639.283 636.898 640.637C629.111 642.16 621.748 642.922 614.808 642.922H582.054L581.546 576.145H614.808C617.008 576.145 620.224 575.975 624.456 575.637C628.688 575.129 632.835 574.029 636.898 572.336C641.129 570.474 644.769 567.85 647.816 564.465C650.862 560.91 652.386 556.171 652.386 550.246V460.363C652.386 451.223 653.909 441.405 656.956 430.91C660.172 420.415 665.166 410.598 671.937 401.457C666.012 394.178 661.103 385.63 657.21 375.812C653.486 365.826 651.624 354.315 651.624 341.281V252.16V251.652C650.778 246.913 649.17 242.935 646.8 239.719C644.599 236.503 641.806 233.879 638.421 231.848C635.036 229.816 631.227 228.378 626.995 227.531C622.933 226.685 618.701 226.262 614.3 226.262H581.038V160.246H614.3C628.011 160.246 641.129 162.447 653.655 166.848C666.181 171.079 677.184 177.258 686.663 185.383C696.312 193.339 703.929 202.987 709.515 214.328C715.101 225.5 717.894 237.941 717.894 251.652C717.894 252.837 717.978 260.878 718.148 275.773C718.317 290.5 718.402 312.336 718.402 341.281C718.402 349.914 720.602 356.516 725.003 361.086C729.404 365.487 736.175 367.688 745.316 367.688C749.886 367.857 754.118 368.872 758.011 370.734C762.073 372.427 765.543 374.797 768.421 377.844C771.468 380.721 773.838 384.191 775.53 388.254C777.223 392.147 778.07 396.379 778.07 400.949Z" fill="#F4F4F4"/>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

View file

@ -0,0 +1,4 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="400" cy="400" r="400" fill="black"/>
<path d="M217.617 226.262H184.863C180.632 226.262 176.23 226.77 171.66 227.785C167.259 228.632 163.197 230.07 159.473 232.102C155.749 234.133 152.702 236.757 150.332 239.973C147.962 243.189 146.777 247.082 146.777 251.652V252.16V341.281C146.777 354.315 144.915 365.826 141.191 375.812C137.467 385.63 132.643 394.178 126.719 401.457C133.828 410.598 138.822 420.415 141.699 430.91C144.746 441.405 146.27 451.223 146.27 460.363V550.246C146.27 556.171 147.793 560.91 150.84 564.465C153.887 567.85 157.526 570.474 161.758 572.336C165.99 574.029 170.221 575.129 174.453 575.637C178.685 575.975 181.986 576.145 184.355 576.145H217.109V642.922H183.848C176.908 642.922 169.46 642.16 161.504 640.637C153.717 639.283 145.931 636.997 138.145 633.781C130.527 630.734 123.164 626.757 116.055 621.848C109.115 616.939 102.936 611.014 97.5195 604.074C92.2721 597.134 88.0404 589.178 84.8242 580.207C81.7773 571.236 80.2539 561.079 80.2539 549.738V460.871C80.2539 451.9 77.8841 445.214 73.1445 440.812C68.5742 436.411 62.1419 434.211 53.8477 434.211C49.1081 434.211 44.707 433.365 40.6445 431.672C36.582 429.81 33.0273 427.44 29.9805 424.562C27.1029 421.516 24.8177 417.961 23.125 413.898C21.4323 409.836 20.5859 405.52 20.5859 400.949C20.5859 396.379 21.4323 392.147 23.125 388.254C24.8177 384.191 27.1029 380.721 29.9805 377.844C33.0273 374.797 36.4974 372.427 40.3906 370.734C44.4531 368.872 48.7695 367.857 53.3398 367.688C62.4805 367.688 69.3359 365.487 73.9062 361.086C78.4766 356.516 80.7617 349.914 80.7617 341.281V251.652C80.7617 237.095 83.724 224.146 89.6484 212.805C95.5729 201.464 103.444 191.9 113.262 184.113C123.079 176.327 134.167 170.402 146.523 166.34C159.049 162.277 171.829 160.246 184.863 160.246H217.617V226.262ZM595.47 371.242C595.47 386.307 593.608 399.764 589.884 411.613C586.16 423.462 581.082 433.957 574.65 443.098C568.387 452.069 561.108 459.771 552.814 466.203C544.52 472.635 535.802 477.883 526.662 481.945C517.69 486.008 508.55 488.97 499.24 490.832C490.099 492.694 481.466 493.625 473.341 493.625H324.045V427.609H473.341C481.805 426.932 489.422 425.24 496.193 422.531C503.133 419.654 509.058 415.845 513.966 411.105C518.875 406.366 522.684 400.695 525.392 394.094C528.101 387.323 529.455 379.706 529.455 371.242V341.281C528.608 332.987 526.831 325.37 524.123 318.43C521.414 311.49 517.69 305.565 512.951 300.656C508.38 295.747 502.795 291.939 496.193 289.23C489.591 286.353 481.974 284.914 473.341 284.914H324.552C315.75 284.914 309.064 287.199 304.494 291.77C299.923 296.34 297.638 302.941 297.638 311.574V583H231.623V311.574C231.623 294.647 234.67 280.259 240.763 268.41C247.026 256.561 254.728 246.997 263.869 239.719C273.179 232.44 283.25 227.193 294.084 223.977C304.917 220.591 314.904 218.898 324.045 218.898H473.341C488.237 218.898 501.61 220.845 513.459 224.738C525.308 228.462 535.718 233.54 544.689 239.973C553.83 246.236 561.532 253.514 567.795 261.809C574.227 270.103 579.474 278.82 583.537 287.961C587.768 296.932 590.815 306.073 592.677 315.383C594.539 324.523 595.47 333.156 595.47 341.281V371.242ZM778.07 400.949C778.07 405.52 777.223 409.836 775.53 413.898C773.838 417.961 771.468 421.516 768.421 424.562C765.543 427.44 762.073 429.81 758.011 431.672C754.118 433.365 749.886 434.211 745.316 434.211C736.683 434.211 729.997 436.411 725.257 440.812C720.687 445.214 718.402 451.9 718.402 460.871L718.909 549.738C718.909 561.079 717.301 571.236 714.085 580.207C710.869 589.178 706.553 597.134 701.136 604.074C695.719 611.014 689.456 616.939 682.347 621.848C675.407 626.757 668.043 630.734 660.257 633.781C652.64 636.997 644.853 639.283 636.898 640.637C629.111 642.16 621.748 642.922 614.808 642.922H582.054L581.546 576.145H614.808C617.008 576.145 620.224 575.975 624.456 575.637C628.688 575.129 632.835 574.029 636.898 572.336C641.129 570.474 644.769 567.85 647.816 564.465C650.862 560.91 652.386 556.171 652.386 550.246V460.363C652.386 451.223 653.909 441.405 656.956 430.91C660.172 420.415 665.166 410.598 671.937 401.457C666.012 394.178 661.103 385.63 657.21 375.812C653.486 365.826 651.624 354.315 651.624 341.281V252.16V251.652C650.778 246.913 649.17 242.935 646.8 239.719C644.599 236.503 641.806 233.879 638.421 231.848C635.036 229.816 631.227 228.378 626.995 227.531C622.933 226.685 618.701 226.262 614.3 226.262H581.038V160.246H614.3C628.011 160.246 641.129 162.447 653.655 166.848C666.181 171.079 677.184 177.258 686.663 185.383C696.312 193.339 703.929 202.987 709.515 214.328C715.101 225.5 717.894 237.941 717.894 251.652C717.894 252.837 717.978 260.878 718.148 275.773C718.317 290.5 718.402 312.336 718.402 341.281C718.402 349.914 720.602 356.516 725.003 361.086C729.404 365.487 736.175 367.688 745.316 367.688C749.886 367.857 754.118 368.872 758.011 370.734C762.073 372.427 765.543 374.797 768.421 377.844C771.468 380.721 773.838 384.191 775.53 388.254C777.223 392.147 778.07 396.379 778.07 400.949Z" fill="#F4F4F4"/>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -0,0 +1,3 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M217.617 226.262H184.863C180.632 226.262 176.23 226.77 171.66 227.785C167.259 228.632 163.197 230.07 159.473 232.102C155.749 234.133 152.702 236.757 150.332 239.973C147.962 243.189 146.777 247.082 146.777 251.652V252.16V341.281C146.777 354.315 144.915 365.826 141.191 375.812C137.467 385.63 132.643 394.178 126.719 401.457C133.828 410.598 138.822 420.415 141.699 430.91C144.746 441.405 146.27 451.223 146.27 460.363V550.246C146.27 556.171 147.793 560.91 150.84 564.465C153.887 567.85 157.526 570.474 161.758 572.336C165.99 574.029 170.221 575.129 174.453 575.637C178.685 575.975 181.986 576.145 184.355 576.145H217.109V642.922H183.848C176.908 642.922 169.46 642.16 161.504 640.637C153.717 639.283 145.931 636.997 138.145 633.781C130.527 630.734 123.164 626.757 116.055 621.848C109.115 616.939 102.936 611.014 97.5195 604.074C92.2721 597.134 88.0404 589.178 84.8242 580.207C81.7773 571.236 80.2539 561.079 80.2539 549.738V460.871C80.2539 451.9 77.8841 445.214 73.1445 440.812C68.5742 436.411 62.1419 434.211 53.8477 434.211C49.1081 434.211 44.707 433.365 40.6445 431.672C36.582 429.81 33.0273 427.44 29.9805 424.562C27.1029 421.516 24.8177 417.961 23.125 413.898C21.4323 409.836 20.5859 405.52 20.5859 400.949C20.5859 396.379 21.4323 392.147 23.125 388.254C24.8177 384.191 27.1029 380.721 29.9805 377.844C33.0273 374.797 36.4974 372.427 40.3906 370.734C44.4531 368.872 48.7695 367.857 53.3398 367.688C62.4805 367.688 69.3359 365.487 73.9062 361.086C78.4766 356.516 80.7617 349.914 80.7617 341.281V251.652C80.7617 237.095 83.724 224.146 89.6484 212.805C95.5729 201.464 103.444 191.9 113.262 184.113C123.079 176.327 134.167 170.402 146.523 166.34C159.049 162.277 171.829 160.246 184.863 160.246H217.617V226.262ZM595.47 371.242C595.47 386.307 593.608 399.764 589.884 411.613C586.16 423.462 581.082 433.957 574.65 443.098C568.387 452.069 561.108 459.771 552.814 466.203C544.52 472.635 535.802 477.883 526.662 481.945C517.69 486.008 508.55 488.97 499.24 490.832C490.099 492.694 481.466 493.625 473.341 493.625H324.045V427.609H473.341C481.805 426.932 489.422 425.24 496.193 422.531C503.133 419.654 509.058 415.845 513.966 411.105C518.875 406.366 522.684 400.695 525.392 394.094C528.101 387.323 529.455 379.706 529.455 371.242V341.281C528.608 332.987 526.831 325.37 524.123 318.43C521.414 311.49 517.69 305.565 512.951 300.656C508.38 295.747 502.795 291.939 496.193 289.23C489.591 286.353 481.974 284.914 473.341 284.914H324.552C315.75 284.914 309.064 287.199 304.494 291.77C299.923 296.34 297.638 302.941 297.638 311.574V583H231.623V311.574C231.623 294.647 234.67 280.259 240.763 268.41C247.026 256.561 254.728 246.997 263.869 239.719C273.179 232.44 283.25 227.193 294.084 223.977C304.917 220.591 314.904 218.898 324.045 218.898H473.341C488.237 218.898 501.61 220.845 513.459 224.738C525.308 228.462 535.718 233.54 544.689 239.973C553.83 246.236 561.532 253.514 567.795 261.809C574.227 270.103 579.474 278.82 583.537 287.961C587.768 296.932 590.815 306.073 592.677 315.383C594.539 324.523 595.47 333.156 595.47 341.281V371.242ZM778.07 400.949C778.07 405.52 777.223 409.836 775.53 413.898C773.838 417.961 771.468 421.516 768.421 424.562C765.543 427.44 762.073 429.81 758.011 431.672C754.118 433.365 749.886 434.211 745.316 434.211C736.683 434.211 729.997 436.411 725.257 440.812C720.687 445.214 718.402 451.9 718.402 460.871L718.909 549.738C718.909 561.079 717.301 571.236 714.085 580.207C710.869 589.178 706.553 597.134 701.136 604.074C695.719 611.014 689.456 616.939 682.347 621.848C675.407 626.757 668.043 630.734 660.257 633.781C652.64 636.997 644.853 639.283 636.898 640.637C629.111 642.16 621.748 642.922 614.808 642.922H582.054L581.546 576.145H614.808C617.008 576.145 620.224 575.975 624.456 575.637C628.688 575.129 632.835 574.029 636.898 572.336C641.129 570.474 644.769 567.85 647.816 564.465C650.862 560.91 652.386 556.171 652.386 550.246V460.363C652.386 451.223 653.909 441.405 656.956 430.91C660.172 420.415 665.166 410.598 671.937 401.457C666.012 394.178 661.103 385.63 657.21 375.812C653.486 365.826 651.624 354.315 651.624 341.281V252.16V251.652C650.778 246.913 649.17 242.935 646.8 239.719C644.599 236.503 641.806 233.879 638.421 231.848C635.036 229.816 631.227 228.378 626.995 227.531C622.933 226.685 618.701 226.262 614.3 226.262H581.038V160.246H614.3C628.011 160.246 641.129 162.447 653.655 166.848C666.181 171.079 677.184 177.258 686.663 185.383C696.312 193.339 703.929 202.987 709.515 214.328C715.101 225.5 717.894 237.941 717.894 251.652C717.894 252.837 717.978 260.878 718.148 275.773C718.317 290.5 718.402 312.336 718.402 341.281C718.402 349.914 720.602 356.516 725.003 361.086C729.404 365.487 736.175 367.688 745.316 367.688C749.886 367.857 754.118 368.872 758.011 370.734C762.073 372.427 765.543 374.797 768.421 377.844C771.468 380.721 773.838 384.191 775.53 388.254C777.223 392.147 778.07 396.379 778.07 400.949Z" fill="#F4F4F4"/>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 KiB

View file

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 350 350" style="enable-background:new 0 0 350 350;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FF66AA;}
.st1{opacity:0.15;}
.st2{clip-path:url(#SVGID_2_);}
.st3{fill:#F1F1F2;}
.st4{fill:#231F20;}
.st5{fill:#808184;}
.st6{fill:#929497;}
.st7{fill:#636466;}
.st8{fill:#59595C;}
.st9{fill:#A7A8AB;}
.st10{fill:#6D6E70;}
.st11{fill:#221F1F;}
.st12{fill:#404041;}
.st13{fill:#58595B;}
.st14{fill:#BBBDBF;}
.st15{fill:#FFFFFF;}
.st16{fill:#A6A8AB;}
.st17{fill:#E6E7E8;}
.st18{fill:#D0D2D3;}
</style>
<g id="Cookie">
<circle class="st0" cx="175" cy="175" r="143.7"/>
</g>
<g class="st1">
<defs>
<circle id="SVGID_1_" class="st1" cx="175" cy="175" r="150"/>
</defs>
<clipPath id="SVGID_2_">
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
</clipPath>
<g class="st2">
<polygon class="st3" points="-81.2,336.9 175,-106.9 431.2,336.9 "/>
<polygon class="st4" points="68.8,443.7 325,0 581.2,443.7 "/>
<polygon class="st5" points="-409.8,363.4 -153.6,-80.3 102.6,363.4 "/>
<polygon class="st6" points="61.4,158.2 317.6,-285.6 573.8,158.2 "/>
<polygon class="st7" points="-222.4,380 33.8,-63.7 290,380 "/>
<polygon class="st8" points="-100.1,646.9 156.1,203.1 412.3,646.9 "/>
<polygon class="st9" points="-164.9,147.2 -67.6,-21.3 29.7,147.2 "/>
<polygon class="st9" points="-83.4,523.8 13.9,355.3 111.2,523.8 "/>
<polygon class="st10" points="134.3,220.8 231.6,52.3 328.9,220.8 "/>
<polygon class="st9" points="298.7,485.7 396,317.2 493.2,485.7 "/>
<polygon class="st8" points="108.4,621.4 157,537.2 205.7,621.4 "/>
<polygon class="st11" points="278.7,305.9 327.3,221.7 375.9,305.9 "/>
<polygon class="st11" points="46.2,400 143.5,231.5 240.8,400 "/>
<polygon class="st5" points="108.8,-86.6 36.5,38.6 231.1,38.6 158.8,-86.6 "/>
<polygon class="st5" points="322,-86.6 298.4,-45.7 395.7,-45.7 372,-86.6 "/>
<polygon class="st12" points="35.3,332.5 132.6,164 229.9,332.5 "/>
<polygon class="st5" points="236.1,369.2 333.3,200.7 430.6,369.2 "/>
<polygon class="st8" points="-51.9,428.8 -3.8,345.5 44.3,428.8 "/>
<polygon class="st13" points="123.2,104.8 171.3,21.5 219.4,104.8 "/>
<polygon class="st11" points="283,90.2 331.1,6.9 379.2,90.2 "/>
<polygon class="st5" points="-61.9,331.6 -37.8,289.9 -13.8,331.6 "/>
<polygon class="st8" points="132.5,621.6 156.5,579.9 180.6,621.6 "/>
<polygon class="st8" points="216.7,465.6 240.7,423.9 264.7,465.6 "/>
<polygon class="st12" points="197.1,259.7 245.2,176.4 293.3,259.7 "/>
<polygon class="st3" points="283,239.3 331.1,156 379.2,239.3 "/>
<polygon class="st9" points="-49.5,78.8 -1.4,-4.5 46.7,78.8 "/>
<polygon class="st9" points="-97.9,225.4 -49.8,142.1 -1.7,225.4 "/>
<polygon class="st5" points="288.2,-20 312.2,-61.6 336.3,-20 "/>
<polygon class="st14" points="187.2,116.2 211.3,74.6 235.3,116.2 "/>
<polygon class="st15" points="231.9,63.8 238.3,52.8 244.7,63.8 "/>
<polygon class="st10" points="28.9,117.8 77,34.5 125.1,117.8 "/>
<polygon class="st16" points="110.8,331.9 158.9,248.6 207,331.9 "/>
<polygon class="st4" points="107,263.1 125.1,231.7 143.1,263.1 "/>
<polygon class="st9" points="214,285.3 238,243.6 262.1,285.3 "/>
<polygon class="st9" points="208.6,167.2 232.6,125.6 256.7,167.2 "/>
<polygon class="st3" points="226,102.8 274.1,19.5 322.1,102.8 "/>
<polygon class="st16" points="1.2,143.6 49.3,60.3 97.4,143.6 "/>
<polygon class="st8" points="14,272.2 32.3,240.4 50.6,272.2 "/>
<polygon class="st16" points="265.4,230.6 283.7,198.8 302,230.6 "/>
<polygon class="st15" points="156.7,84.8 175,53.1 193.3,84.8 "/>
<polygon class="st13" points="156.7,282.1 175,250.3 193.3,282.1 "/>
<polygon class="st8" points="-19.7,88 -1.4,56.2 17,88 "/>
<polygon class="st8" points="238.4,360.2 256.7,328.5 275,360.2 "/>
<polygon class="st5" points="307.5,135.6 325.8,103.8 344.1,135.6 "/>
<polygon class="st14" points="45.3,233.1 63.6,201.3 82,233.1 "/>
<polygon class="st3" points="72.3,295.1 90.6,263.3 109,295.1 "/>
<polygon class="st3" points="27.6,167.7 46,136 64.3,167.7 "/>
<polygon class="st8" points="113.7,123.7 122.8,107.9 132,123.7 "/>
<polygon class="st14" points="50.8,152.9 59.9,137.1 69.1,152.9 "/>
<polygon class="st17" points="107.7,84.4 116.9,68.5 126.1,84.4 "/>
<polygon class="st11" points="69,360.2 87.3,328.5 105.6,360.2 "/>
<polygon class="st8" points="112.2,419.2 130.5,387.4 148.8,419.2 "/>
<polygon class="st8" points="516.7,288.1 535.1,256.3 553.4,288.1 "/>
<polygon class="st5" points="187.7,61.9 206,30.1 224.4,61.9 "/>
<polygon class="st15" points="283.4,128.2 301.7,96.5 320,128.2 "/>
<polygon class="st8" points="316.6,622.1 325.8,606.2 335,622.1 "/>
<polygon class="st9" points="262.4,411.2 271.6,395.4 280.7,411.2 "/>
<polygon class="st8" points="577.5,394.4 586.7,378.5 595.9,394.4 "/>
<polygon class="st5" points="-97.4,339.9 -79.1,308.2 -60.7,339.9 "/>
<polygon class="st5" points="277.7,315.5 296.1,283.7 314.4,315.5 "/>
<polygon class="st9" points="286.9,268.6 296.1,252.7 305.2,268.6 "/>
<polygon class="st18" points="210.1,128.5 221,109.6 231.9,128.5 "/>
<polygon class="st5" points="94.9,-41.1 104,-57 113.2,-41.1 "/>
<polygon class="st8" points="386.7,121.9 405,90.2 423.4,121.9 "/>
<polygon class="st8" points="463,336.8 472.2,320.9 481.4,336.8 "/>
</g>
</g>
<path id="osu_x21__x5F_txt_x5F_Path_2_" class="st15" d="M100.1,206.4c-4.7,0-8.8-0.8-12.3-2.3s-6.4-3.7-8.6-6.4
c-2.3-2.7-4-5.9-5.2-9.6c-1.2-3.7-1.7-7.6-1.7-11.9c0-4.3,0.6-8.3,1.7-12c1.2-3.7,2.9-7,5.2-9.7c2.3-2.7,5.2-4.9,8.6-6.5
s7.6-2.4,12.3-2.4c4.7,0,8.8,0.8,12.3,2.4c3.5,1.6,6.4,3.7,8.8,6.5c2.3,2.7,4,6,5.2,9.7c1.1,3.7,1.7,7.7,1.7,12
c0,4.3-0.6,8.2-1.7,11.9c-1.1,3.7-2.8,6.9-5.2,9.6c-2.3,2.7-5.2,4.9-8.8,6.4C109,205.7,104.8,206.4,100.1,206.4z M100.1,194.3
c4.2,0,7.2-1.6,9-4.7c1.8-3.1,2.7-7.6,2.7-13.4c0-5.8-0.9-10.3-2.7-13.4c-1.8-3.1-4.8-4.7-9-4.7c-4.1,0-7.1,1.6-8.9,4.7
c-1.8,3.1-2.7,7.6-2.7,13.4c0,5.8,0.9,10.3,2.7,13.4C93,192.8,96,194.3,100.1,194.3z M151.9,179.8c-4.2-1.2-7.5-3-9.8-5.3
c-2.4-2.4-3.5-5.9-3.5-10.6c0-5.7,2-10.1,6.1-13.4c4.1-3.2,9.6-4.8,16.7-4.8c2.9,0,5.8,0.3,8.6,0.8c2.8,0.5,5.7,1.3,8.6,2.4
c-0.2,1.9-0.5,4-1.1,6.1c-0.6,2.1-1.3,3.9-2.1,5.5c-1.8-0.7-3.8-1.4-5.9-2c-2.2-0.6-4.5-0.8-6.8-0.8c-2.5,0-4.5,0.4-5.9,1.2
c-1.4,0.8-2.1,2-2.1,3.8c0,1.6,0.5,2.8,1.5,3.5c1,0.7,2.4,1.3,4.3,1.9l6.4,1.9c2.1,0.6,4,1.3,5.7,2.2c1.7,0.9,3.1,1.9,4.3,3.2
c1.2,1.3,2.1,2.8,2.8,4.7c0.7,1.9,1,4.2,1,6.8c0,2.8-0.6,5.3-1.7,7.7c-1.2,2.4-2.8,4.5-5,6.2c-2.2,1.8-4.9,3.1-8,4.2
c-3.1,1-6.7,1.5-10.7,1.5c-1.8,0-3.4-0.1-4.9-0.2c-1.5-0.1-2.9-0.3-4.3-0.6c-1.4-0.3-2.7-0.6-4.1-1c-1.3-0.4-2.8-0.9-4.4-1.5
c0.1-2,0.5-4.1,1.1-6.1c0.6-2.1,1.3-4.1,2.2-6c2.5,1,4.8,1.7,7,2.2c2.2,0.5,4.5,0.7,6.9,0.7c1,0,2.2-0.1,3.4-0.3
c1.2-0.2,2.4-0.5,3.4-1c1-0.5,1.9-1.1,2.6-1.9c0.7-0.8,1.1-1.8,1.1-3.1c0-1.8-0.5-3.1-1.6-3.9c-1.1-0.8-2.6-1.5-4.5-2.1L151.9,179.8
z M191.2,147.1c2.7-0.4,5.3-0.7,8-0.7c2.6,0,5.3,0.2,8,0.7v30.7c0,3.1,0.2,5.6,0.7,7.6c0.5,2,1.2,3.6,2.2,4.7c1,1.2,2.3,2,3.8,2.5
c1.5,0.5,3.3,0.7,5.3,0.7c2.8,0,5.1-0.3,7-0.8v-45.4c2.7-0.4,5.3-0.7,7.9-0.7c2.6,0,5.3,0.2,8,0.7v55.8c-2.4,0.8-5.6,1.6-9.5,2.4
c-3.9,0.8-8,1.2-12.3,1.2c-3.8,0-7.5-0.3-11-0.9c-3.5-0.6-6.6-1.9-9.3-3.8c-2.7-1.9-4.8-4.8-6.3-8.5c-1.6-3.7-2.4-8.7-2.4-14.9
V147.1z M257.1,205.1c-0.4-2.8-0.7-5.5-0.7-8.2c0-2.7,0.2-5.5,0.7-8.3c2.8-0.4,5.5-0.7,8.2-0.7c2.7,0,5.5,0.2,8.3,0.7
c0.4,2.8,0.7,5.6,0.7,8.2c0,2.8-0.2,5.5-0.7,8.3c-2.8,0.4-5.6,0.7-8.2,0.7C262.6,205.7,259.9,205.5,257.1,205.1z M256.7,124.4
c2.9-0.4,5.8-0.7,8.6-0.7c2.9,0,5.8,0.2,8.8,0.7l-1.1,54.9c-2.6,0.4-5.1,0.7-7.5,0.7c-2.5,0-5.1-0.2-7.6-0.7L256.7,124.4z"/>
<path id="Rim_6_" class="st15" d="M175,25C92.2,25,25,92.2,25,175c0,82.8,67.2,150,150,150c82.8,0,150-67.2,150-150
C325,92.2,257.8,25,175,25z M175,310c-74.6,0-135-60.4-135-135c0-74.6,60.4-135,135-135s135,60.4,135,135
C310,249.6,249.6,310,175,310z"/>
</svg>

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

View file

@ -0,0 +1,19 @@
<!-- Thank you https://www.uxwing.com -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" version="1.1" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 13333 13333" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<defs>
<linearGradient id="id0" gradientUnits="userSpaceOnUse" x1="6667" y1="13333" x2="6667" y2="0">
<stop offset="0" stop-opacity="1" stop-color="#420C5D"/>
<stop offset="1" stop-opacity="1" stop-color="#951AD1"/>
</linearGradient>
<linearGradient id="id1" gradientUnits="userSpaceOnUse" x1="3659" y1="12734" x2="3659" y2="755">
<stop offset="0" stop-opacity="1" stop-color="#420C5D"/>
<stop offset="1" stop-opacity="1" stop-color="#951AD1"/>
</linearGradient>
</defs>
<circle id="background" fill="#F2E4FF" fill-rule="nonzero" cx="6667" cy="6667" r="6406"/>
<path id="center" fill="url(#id0)" d="M6680 12121l0 -808c2560,-7 4633,-2084 4633,-4646 0,-2562 -2073,-4639 -4633,-4646l0 -808c3006,8 5441,2446 5441,5454 0,3008 -2434,5447 -5441,5454zm0 -2829c1444,-8 2613,-1180 2613,-2626 0,-1446 -1169,-2618 -2613,-2626l0 -808c1890,7 3421,1541 3421,3434 0,1892 -1530,3426 -3421,3434l0 -808zm0 -4039c775,7 1400,637 1400,1413 0,777 -626,1406 -1400,1414l0 -2827zm-6680 1413c0,3682 2985,6667 6667,6667 3682,0 6667,-2985 6667,-6667 0,-3682 -2985,-6666 -6667,-6666 -3682,0 -6667,2985 -6667,6666z"/>
<path fill="url(#id0)" d="M6667 755c-3322,0 -6016,2682 -6016,5990 0,3308 2693,5990 6016,5990l0 -11979z"/>
<path fill="url(#id1)" d="M6667 755c-3322,0 -6016,2682 -6016,5990 0,3308 2693,5990 6016,5990l0 -11979z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/icon/icon_circle.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta property="og:image" content="%PUBLIC_URL%/img/icon.png" />
<meta property="og:type" content="website" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;700&family=Source+Code+Pro&display=swap"
rel="stylesheet"
/>
<title>pomp</title>
</head>
<body>
<noscript>
English: Oops! It seems like JavaScript is not enabled!
<br />
Korean: 이런! 자바스크립트를 사용할 수 없습니다!
</noscript>
<div id="root"></div>
</body>
</html>

View file

@ -0,0 +1,5 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Allow: /
# Sitemap: /sitemap.xml
Disallow:

125
apps/blog/src/App.tsx Normal file
View file

@ -0,0 +1,125 @@
import { useContext, useEffect, useState } from "react"
import { Routes, Route, useNavigate, useLocation } from "react-router-dom"
import styled, { ThemeProvider } from "styled-components"
import { Helmet } from "react-helmet-async"
import { isIE } from "react-device-detect"
import Loading from "./components/Loading"
import Navbar from "./components/Navbar"
import Footer from "./components/Footer"
import Home from "./pages/Home"
import Search from "./pages/Search"
import Page from "./pages/Page"
import NotFound from "./pages/NotFound"
import Portfolio from "./pages/Portfolio"
import theming from "./styles/theming"
import GlobalStyle from "./styles/globalStyle"
import { ActionsEnum, globalContext } from "./globalContext"
const IENotSupported = styled.p`
margin: auto;
font-size: 2rem;
margin-top: 2rem;
text-align: center;
font-family: ${theming.font.regular};
`
const StyledContentContainer = styled.div`
flex: 1 1 auto;
margin-bottom: 3rem;
margin-top: 5rem;
`
export default function App() {
const { globalState, dispatch } = useContext(globalContext)
const { locale } = globalState
const navigate = useNavigate()
const { pathname } = useLocation()
const [isLoading, setIsLoading] = useState(true)
// update url on locale change
useEffect(() => {
navigate(locale + pathname.slice(3))
}, [locale])
useEffect(() => {
// set loading to false if all fonts are loaded
// checks if document.fonts.onloadingdone is supported on the browser
if (typeof document.fonts.onloadingdone != undefined) {
document.fonts.onloadingdone = () => {
setIsLoading(false)
}
} else {
setIsLoading(false)
}
// automatically add locale prefix if it's not specified
if (!pathname.startsWith("/en") && !pathname.startsWith("/kr"))
navigate(`/${globalState.locale}${pathname}`)
}, [])
if (isIE)
return (
<IENotSupported>
Internet Explorer is <b>not supported.</b>
</IENotSupported>
)
return (
<ThemeProvider
theme={{
currentTheme: globalState.theme,
setTheme(setThemeTo) {
dispatch({ type: ActionsEnum.UPDATE_THEME, payload: setThemeTo })
},
}}
>
<Helmet>
<meta property="og:site_name" content="developomp" />
<meta property="og:title" content="Home" />
<meta property="og:description" content="developomp's blog" />
</Helmet>
<GlobalStyle />
<Navbar />
<StyledContentContainer>
{isLoading ? (
<Loading />
) : (
<Routes>
{/*
Using this ugly code because the developers of react-router-dom decided that
removing regex support was a good idea.
https://github.com/remix-run/react-router/issues/7285
*/}
<Route path="en">
<Route index element={<Home />} />
<Route path="search" element={<Search />} />
<Route path="portfolio" element={<Portfolio />} />
<Route path="404" element={<NotFound />} />
<Route path="loading" element={<Loading />} />
<Route path="*" element={<Page />} />
</Route>
<Route path="kr">
<Route index element={<Home />} />
<Route path="search" element={<Search />} />
<Route path="portfolio" element={<Portfolio />} />
<Route path="404" element={<NotFound />} />
<Route path="loading" element={<Loading />} />
<Route path="*" element={<Page />} />
</Route>
</Routes>
)}
</StyledContentContainer>
<Footer />
</ThemeProvider>
)
}

View file

@ -0,0 +1,72 @@
import { useEffect, useState } from "react"
import styled from "styled-components"
import theming from "../styles/theming"
const StyledBadge = styled.div<{ color: string; isDark: boolean }>`
vertical-align: middle;
display: inline-block;
padding: 0.2rem 0.4rem 0 0.4rem;
margin-right: 0.4rem;
margin-bottom: 0.4rem;
font-size: 0.8rem;
background-color: ${(props) => props.color};
color: ${(props) =>
props.isDark ? theming.dark.color1 : theming.light.color1};
`
const StyledSVG = styled.div<{ isDark: boolean }>`
display: inline-block;
vertical-align: middle;
margin-right: 0.2rem;
svg {
height: 16px;
fill: ${(props) =>
props.isDark ? theming.dark.color1 : theming.light.color1} !important;
}
`
export interface Badge {
svg: string
hex: string
isDark: boolean
title: string
}
interface BadgeProps {
slug: string
}
const Badge = (props: BadgeProps) => {
const [badgeData, setBadgeData] = useState<Badge | undefined>(undefined)
const { slug } = props
const getBadgeData = async () => {
return await require(`../data/icons/${slug}.json`)
}
useEffect(() => {
getBadgeData().then((data) => {
setBadgeData(data)
})
}, [])
if (!badgeData) return <></>
return (
<StyledBadge color={badgeData.hex} isDark={badgeData.isDark}>
<StyledSVG
isDark={badgeData.isDark}
dangerouslySetInnerHTML={{ __html: badgeData.svg }}
/>
<span>{badgeData.title}</span>
</StyledBadge>
)
}
export default Badge

View file

@ -0,0 +1,29 @@
import styled, { css } from "styled-components"
import theming from "../styles/theming"
export const cardCSS = css`
margin: auto;
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "white",
dark: theming.dark.backgroundColor2,
})};
padding: 2rem;
border-radius: 6px;
box-shadow: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "0 4px 10px rgb(0 0 0 / 5%), 0 0 1px rgb(0 0 0 / 10%);",
dark: "0 4px 10px rgb(0 0 0 / 30%), 0 0 1px rgb(0 0 0 / 30%);",
})};
@media screen and (max-width: ${theming.size.screen_size1}) {
padding: 1rem;
}
`
const Card = styled.div`
${cardCSS}
`
export default Card

View file

@ -0,0 +1,55 @@
import styled from "styled-components"
import theming from "../../styles/theming"
import GithubLinkIcon from "../GithubLinkIcon"
const StyledFooter = styled.footer`
display: flex;
// congratulation. You've found the lucky 7s
min-height: 7.77rem;
max-height: 7.77rem;
align-items: center;
justify-content: center;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "black",
dark: "white",
})};
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "white",
dark: "black",
})};
`
const StyledFooterContainer = styled.div`
display: flex;
padding: 0 1rem 0 1rem;
justify-content: space-between;
text-align: center;
color: gray;
width: 100%;
max-width: ${theming.size.screen_size2};
`
const Footer = () => {
return (
<StyledFooter>
<StyledFooterContainer>
<div>
Created by <b>developomp</b>
</div>
<GithubLinkIcon link="https://github.com/developomp/developomp-site" />
</StyledFooterContainer>
</StyledFooter>
)
}
export default Footer

View file

@ -0,0 +1,3 @@
import Footer from "./Footer"
export default Footer

View file

@ -0,0 +1,42 @@
import { ReactNode } from "react"
import styled from "styled-components"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faGithub } from "@fortawesome/free-brands-svg-icons"
import theming from "../styles/theming"
const StyledGithubLink = styled.a<{ size?: string }>`
font-size: ${(props) => props.size || "2.5rem"};
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "lightgrey",
dark: "grey",
})};
:hover {
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color0,
dark: theming.dark.color0,
})};
}
`
interface GithubLinkIconProps {
link: string
size?: string
children?: ReactNode
}
const GithubLinkIcon = (props: GithubLinkIconProps) => {
return (
<StyledGithubLink size={props.size} href={props.link} target="_blank">
<FontAwesomeIcon icon={faGithub} />
{props.children}
</StyledGithubLink>
)
}
export default GithubLinkIcon

View file

@ -0,0 +1,145 @@
/**
* inspired by https://codepen.io/avstorm/pen/RwNzPNN
*/
import styled from "styled-components"
import MainContent from "./MainContent"
import theming from "../styles/theming"
const StyledContainer = styled(MainContent)`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
animation: fadein 2s;
@keyframes fadein {
from {
opacity: 0;
}
50% {
opacity: 0;
}
to {
opacity: 1;
}
}
`
const StyledSVG = styled.svg`
--color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color1,
dark: theming.dark.color1,
})};
display: block;
margin: 1rem;
margin-bottom: 4.5rem;
transform: scale(2);
#teabag {
transform-origin: top center;
transform: rotate(3deg);
animation: swingAnimation 2s infinite;
}
#steamL {
stroke-dasharray: 13;
stroke-dashoffset: 13;
animation: steamLargeAnimation 2s infinite;
}
#steamR {
stroke-dasharray: 9;
stroke-dashoffset: 9;
animation: steamSmallAnimation 2s infinite;
}
@keyframes swingAnimation {
50% {
transform: rotate(-3deg);
}
}
@keyframes steamLargeAnimation {
0% {
stroke-dashoffset: 13;
opacity: 0.6;
}
100% {
stroke-dashoffset: 39;
opacity: 0;
}
}
@keyframes steamSmallAnimation {
10% {
stroke-dashoffset: 9;
opacity: 0.6;
}
80% {
stroke-dashoffset: 27;
opacity: 0;
}
100% {
stroke-dashoffset: 27;
opacity: 0;
}
}
`
const Loading = () => {
return (
<StyledContainer>
<StyledSVG
width="37"
height="48"
viewBox="0 0 37 48"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M27.0819 17H3.02508C1.91076 17 1.01376 17.9059 1.0485 19.0197C1.15761 22.5177 1.49703 29.7374 2.5 34C4.07125 40.6778 7.18553 44.8868 8.44856 46.3845C8.79051 46.79 9.29799 47 9.82843 47H20.0218C20.639 47 21.2193 46.7159 21.5659 46.2052C22.6765 44.5687 25.2312 40.4282 27.5 34C28.9757 29.8188 29.084 22.4043 29.0441 18.9156C29.0319 17.8436 28.1539 17 27.0819 17Z"
stroke="var(--color)"
strokeWidth="2"
/>
<path
d="M29 23.5C29 23.5 34.5 20.5 35.5 25.4999C36.0986 28.4926 34.2033 31.5383 32 32.8713C29.4555 34.4108 28 34 28 34"
stroke="var(--color)"
strokeWidth="2"
/>
<path
id="teabag"
fill="var(--color)"
fillRule="evenodd"
clipRule="evenodd"
d="M16 25V17H14V25H12C10.3431 25 9 26.3431 9 28V34C9 35.6569 10.3431 37 12 37H18C19.6569 37 21 35.6569 21 34V28C21 26.3431 19.6569 25 18 25H16ZM11 28C11 27.4477 11.4477 27 12 27H18C18.5523 27 19 27.4477 19 28V34C19 34.5523 18.5523 35 18 35H12C11.4477 35 11 34.5523 11 34V28Z"
/>
<path
id="steamL"
d="M17 1C17 1 17 4.5 14 6.5C11 8.5 11 12 11 12"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
stroke="var(--color)"
/>
<path
id="steamR"
d="M21 6C21 6 21 8.22727 19 9.5C17 10.7727 17 13 17 13"
stroke="var(--color)"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</StyledSVG>
<h2>Loading...</h2>
</StyledContainer>
)
}
export default Loading

View file

@ -0,0 +1,28 @@
import styled, { css } from "styled-components"
import theming from "../styles/theming"
import Card from "./Card"
export const mainContentCSS = css`
margin-top: 1rem;
width: 50%;
img {
max-width: 100%;
}
table img {
max-width: fit-content;
}
@media screen and (max-width: ${theming.size.screen_size1}) {
width: auto;
margin: 1rem;
}
`
const MainContent = styled(Card)`
${mainContentCSS}
`
export default MainContent

View file

@ -0,0 +1,50 @@
import { useContext } from "react"
import styled from "styled-components"
import ReactTooltip from "react-tooltip"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faLanguage } from "@fortawesome/free-solid-svg-icons"
import { ActionsEnum, globalContext } from "../../globalContext"
import theming from "../../styles/theming"
import type { SiteLocale } from "../../globalContext"
interface StyledLocaleToggleButtonProps {
locale: SiteLocale
}
const StyledLocaleToggleButton = styled.button<StyledLocaleToggleButtonProps>`
${theming.styles.navbarButtonStyle}
border: none;
width: 72px;
${(props) => (props.locale == "en" ? "" : "transform: scaleX(-1);")};
`
function LocaleToggleButton() {
const { globalState, dispatch } = useContext(globalContext)
return (
<>
<StyledLocaleToggleButton
data-tip
data-for="locale"
onClick={() => {
dispatch({
type: ActionsEnum.UPDATE_LOCALE,
payload: globalState.locale == "en" ? "kr" : "en",
})
}}
locale={globalState.locale}
>
<FontAwesomeIcon icon={faLanguage} />
</StyledLocaleToggleButton>
<ReactTooltip id="locale" type="dark" effect="solid">
<span>{globalState.locale == "en" ? "English" : "한국어"} </span>
</ReactTooltip>
</>
)
}
export default LocaleToggleButton

View file

@ -0,0 +1,36 @@
import { useContext } from "react"
import styled from "styled-components"
import { Link } from "react-router-dom"
import { StyledLink } from "./Navbar"
import NavbarData from "../../data/NavbarData"
import theming from "../../styles/theming"
import { globalContext } from "../../globalContext"
const StyledNavLinks = styled.div`
display: flex;
height: 100%;
@media only screen and (max-width: ${theming.size.screen_size1}) {
display: none;
}
`
const NavLinks = () => {
const { globalState } = useContext(globalContext)
return (
<StyledNavLinks>
{NavbarData.map((item, index) => (
<Link key={index} to={globalState.locale + item.path}>
<StyledLink>
{globalState.locale == "en" ? item.title_en : item.title_kr}
</StyledLink>
</Link>
))}
</StyledNavLinks>
)
}
export default NavLinks

View file

@ -0,0 +1,95 @@
import { useContext } from "react"
import styled from "styled-components"
import { Link } from "react-router-dom"
import NavLinks from "./NavLinks"
import LocaleToggleButton from "./LocaleToggleButton"
import ThemeToggleButton from "./ThemeToggleButton"
import SearchButton from "./SearchButton"
import ReadProgress from "./ReadProgress"
import Sidebar from "../Sidebar"
import theming from "../../styles/theming"
import { globalContext } from "../../globalContext"
const StyledNav = styled.nav`
/* set z index to arbitrarily high value to prevent other components from drawing over the navbar */
z-index: 9999;
position: fixed;
width: 100%;
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.backgroundColor0,
dark: theming.dark.backgroundColor0,
})};
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color0,
dark: theming.dark.color0,
})};
box-shadow: 0 4px 10px rgb(0 0 0 / 5%);
`
const StyledContainer = styled.div`
margin: 0 auto;
align-items: center;
display: flex;
height: 4rem;
/* account for 20px scrollbar width */
@media only screen and (min-width: calc(${theming.size
.screen_size2} + 20px)) {
width: calc(${theming.size.screen_size2} - 20px);
}
`
const RightButtons = styled.div`
display: flex;
height: 100%;
margin-left: auto;
`
const StyledImg = styled.img`
height: 2.5rem;
display: block;
margin: 1rem;
`
export const StyledLink = styled.div`
${theming.styles.navbarButtonStyle}
`
const Navbar = () => {
const { globalState } = useContext(globalContext)
const { locale } = globalState
return (
<StyledNav>
<StyledContainer>
{/* icon */}
<Link to={`/${locale}`}>
<StyledImg src="/icon/icon_circle.svg" />
</Link>
{/* nav links */}
<NavLinks />
{/* right buttons */}
<RightButtons>
<LocaleToggleButton />
<ThemeToggleButton />
<SearchButton />
</RightButtons>
{/* etc */}
<Sidebar />
</StyledContainer>
<ReadProgress />
</StyledNav>
)
}
export default Navbar

View file

@ -0,0 +1,67 @@
import { useCallback, useEffect, useState } from "react"
import { useLocation } from "react-router-dom"
import styled from "styled-components"
import theming from "../../styles/theming"
const StyledReadProgressBackground = styled.div`
height: 0.2rem;
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: "silver",
dark: "darkslategrey",
})};
`
const StyledReadProgress = styled.div`
height: 100%;
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color0,
dark: theming.dark.color2,
})};
`
const st = "scrollTop"
const sh = "scrollHeight"
const h = document.documentElement
const b = document.body
const ReadProgress = () => {
const [scroll, setScroll] = useState(0)
const location = useLocation()
// https://stackoverflow.com/a/8028584/12979111
const scrollHandler = useCallback(() => {
setScroll(((h[st] || b[st]) / ((h[sh] || b[sh]) - h.clientHeight)) * 100)
}, [])
useEffect(() => {
const resizeObserver = new ResizeObserver(() => {
scrollHandler()
})
resizeObserver.observe(document.body)
window.addEventListener("scroll", scrollHandler)
return () => {
resizeObserver.disconnect()
window.removeEventListener("scroll", scrollHandler)
}
}, [])
// update on path change
useEffect(() => {
setTimeout(() => {
scrollHandler()
}, 100)
}, [location])
return (
<StyledReadProgressBackground>
<StyledReadProgress style={{ width: `${scroll}%` }} />
</StyledReadProgressBackground>
)
}
export default ReadProgress

View file

@ -0,0 +1,32 @@
import { useContext } from "react"
import { Link } from "react-router-dom"
import ReactTooltip from "react-tooltip"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faSearch } from "@fortawesome/free-solid-svg-icons"
import { StyledLink } from "./Navbar"
import { globalContext } from "../../globalContext"
const SearchButton = () => {
const { globalState } = useContext(globalContext)
const { locale } = globalState
return (
<>
<div>
<Link data-tip data-for="search" to={`/${locale}/search`}>
<StyledLink>
<FontAwesomeIcon icon={faSearch} />
</StyledLink>
</Link>
</div>
<ReactTooltip id="search" type="dark" effect="solid">
<span>{locale == "en" ? "Search" : "검색"}</span>
</ReactTooltip>
</>
)
}
export default SearchButton

View file

@ -0,0 +1,57 @@
import { useContext } from "react"
import styled from "styled-components"
import { isMobile } from "react-device-detect"
import ReactTooltip from "react-tooltip"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faMoon, faSun } from "@fortawesome/free-solid-svg-icons"
import theming from "../../styles/theming"
import { ActionsEnum, globalContext } from "../../globalContext"
const StyledThemeButton = styled.button`
${theming.styles.navbarButtonStyle}
border: none;
width: 72px;
${(props) =>
theming.theme(props.theme.currentTheme, {
light: "",
dark: "transform: scaleX(-1);",
})};
`
const ThemeToggleButton = () => {
const { globalState, dispatch } = useContext(globalContext)
const theme = globalState.theme
return (
<>
<StyledThemeButton
data-tip
data-for="theme"
onClick={() =>
dispatch({
type: ActionsEnum.UPDATE_THEME,
payload: theme === "dark" ? "light" : "dark",
})
}
>
{theme == "dark" && <FontAwesomeIcon icon={faMoon} />}
{theme == "light" && <FontAwesomeIcon icon={faSun} />}
</StyledThemeButton>
{!isMobile && (
<ReactTooltip id="theme" type="dark" effect="solid">
{globalState.locale == "en" ? (
<span>Using {theme} theme</span>
) : (
<span>{theme == "dark" ? "어두운" : "밝은"} </span>
)}
</ReactTooltip>
)}
</>
)
}
export default ThemeToggleButton

View file

@ -0,0 +1,3 @@
import Navbar from "./Navbar"
export default Navbar

View file

@ -0,0 +1,124 @@
import { useContext } from "react"
import styled from "styled-components"
import { Link } from "react-router-dom"
import { PostData } from "../../types/types"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import {
faBook,
faCalendar,
faHourglass,
} from "@fortawesome/free-solid-svg-icons"
import Tag from "./Tag"
import TagList from "./TagList"
import MainContent from "./MainContent"
import theming from "../styles/theming"
import { globalContext } from "../globalContext"
const PostCardContainer = styled(Link)`
display: block;
padding: 2rem;
text-decoration: none;
padding: 0;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color2,
dark: theming.dark.color2,
})};
&:hover {
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color2,
dark: theming.dark.color2,
})};
}
`
const StyledPostCard = styled(MainContent)`
box-shadow: 0 4px 10px rgb(0 0 0 / 10%);
text-align: left;
margin-bottom: 2rem;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color1,
dark: theming.dark.color1,
})};
${theming.styles.hoverCard}
`
const StyledTitle = styled.h1`
font-size: 2rem;
font-style: bold;
margin: 0;
margin-bottom: 1rem;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color2,
dark: theming.dark.color2,
})};
`
const StyledMetaContainer = styled.small``
interface PostCardData extends PostData {
content_id: string
}
interface Props {
postData: PostCardData
}
const PostCard = (props: Props) => {
const { postData } = props
const { content_id, wordCount, date, readTime, title, tags } = postData
const { globalState } = useContext(globalContext)
return (
<StyledPostCard>
<PostCardContainer
to={`/${globalState.locale}${content_id.replace(/(.kr)$/g, "")}`}
>
<StyledTitle>
{title || "No title"}
{/* show "(series)" for urls that matches regex "/series/<series-title>" */}
{/\/series\/[^/]*$/.test(content_id) && " (series)"}
</StyledTitle>
<br />
<StyledMetaContainer>
<TagList direction="left">
{tags &&
tags.map((tag) => {
return <Tag key={title + tag} text={tag} />
})}
</TagList>
<hr />
<FontAwesomeIcon icon={faCalendar} />
&nbsp;&nbsp;&nbsp;
{date || "Unknown date"}
&nbsp;&nbsp;&nbsp;&nbsp;
<FontAwesomeIcon icon={faHourglass} />
&nbsp;&nbsp;&nbsp;
{readTime ? readTime + " read" : "unknown read time"}
&nbsp;&nbsp;&nbsp;&nbsp;
<FontAwesomeIcon icon={faBook} />
&nbsp;&nbsp;&nbsp;
{typeof wordCount === "number"
? wordCount + " words"
: "unknown length"}
</StyledMetaContainer>
</PostCardContainer>
</StyledPostCard>
)
}
export default PostCard

View file

@ -0,0 +1,123 @@
import { useCallback, useState } from "react"
import styled, { css } from "styled-components"
import ReactTooltip from "react-tooltip"
import { isMobile } from "react-device-detect"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faEllipsisV, faTimes } from "@fortawesome/free-solid-svg-icons"
import SubMenu from "./SubMenu"
import NavbarData from "../../data/NavbarData"
import theming from "../../styles/theming"
const CommonSidebarToggleButtonStyle = css`
${theming.styles.navbarButtonStyle}
width: 1.5rem;
text-align: center;
cursor: pointer;
@media only screen and (min-width: ${theming.size.screen_size1}) {
display: none;
}
`
const SidebarOpenButton = styled.div`
${CommonSidebarToggleButtonStyle}
`
const SidebarCloseButton = styled.div`
${CommonSidebarToggleButtonStyle}
width: 90%;
height: 4rem;
font-size: 1.1rem;
svg {
margin-top: 0.2rem;
margin-right: 0.5rem;
}
`
const StyledOverlay = styled.div<{ isSidebarOpen: boolean }>`
display: ${(props) => (props.isSidebarOpen ? "block" : "none")};
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 20;
transition-property: opacity;
background-color: rgba(0, 0, 0, 25%);
* {
overflow: scroll;
}
`
const SidebarNav = styled.nav<{ isSidebarOpen: boolean }>`
width: 250px;
height: 100vh;
display: flex;
justify-content: center;
position: fixed;
top: 0;
right: ${(props) => (props.isSidebarOpen ? "0" : "-100%")};
transition: 350ms;
z-index: 30;
overflow-x: hidden;
overflow-y: scroll;
background-color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.backgroundColor0,
dark: theming.dark.backgroundColor0,
})};
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color0,
dark: theming.dark.color0,
})};
`
const SidebarWrap = styled.div`
width: 100%;
`
const Sidebar = () => {
const [isSidebarOpen, setSidebarOpen] = useState(false)
const toggleSidebar = useCallback(() => {
setSidebarOpen((prev) => !prev)
document.body.style.overflow = isSidebarOpen ? "" : "hidden"
}, [isSidebarOpen])
return (
<>
<StyledOverlay isSidebarOpen={isSidebarOpen} onClick={toggleSidebar} />
<SidebarOpenButton data-tip data-for="sidebar" onClick={toggleSidebar}>
<FontAwesomeIcon icon={faEllipsisV}></FontAwesomeIcon>
{!isMobile && (
<ReactTooltip id="sidebar" type="dark" effect="solid">
<span>open sidebar</span>
</ReactTooltip>
)}
</SidebarOpenButton>
<SidebarNav isSidebarOpen={isSidebarOpen}>
<SidebarWrap>
{/* close sidebar button */}
<SidebarCloseButton onClick={toggleSidebar}>
<FontAwesomeIcon icon={faTimes}></FontAwesomeIcon>Close
</SidebarCloseButton>
{/* sidebar items */}
{NavbarData.map((item, index) => {
return <SubMenu item={item} key={index} />
})}
</SidebarWrap>
</SidebarNav>
</>
)
}
export default Sidebar

View file

@ -0,0 +1,69 @@
/**
* @file Submenu item for sidebar
*/
import { useCallback, useContext, useState } from "react"
import { Link } from "react-router-dom"
import styled from "styled-components"
import theming from "../../styles/theming"
import { globalContext } from "../../globalContext"
import type { Item } from "../../data/NavbarData"
const SidebarLink = styled(Link)`
${theming.styles.navbarButtonStyle};
display: flex;
width: 100%;
margin: 0;
border-radius: 0;
justify-content: space-between;
height: 2rem;
align-items: center;
padding: 20px;
list-style: none;
svg {
scale: 1.5;
}
&:hover {
color: inherit;
}
`
const SidebarLabel = styled.span`
margin-left: 1rem;
`
interface Props {
item: Item
}
const SubMenu = (props: Props) => {
const { globalState } = useContext(globalContext)
const [isSubNavOpen, setSubNavOpen] = useState(false)
const handleSidebarLinkClick = useCallback(() => {
setSubNavOpen((prev) => !prev)
}, [isSubNavOpen])
return (
<>
<SidebarLink
to={globalState.locale + props.item.path}
onClick={handleSidebarLinkClick}
>
<div>
{props.item.icon}
<SidebarLabel>
{globalState.locale == "en"
? props.item.title_en
: props.item.title_kr}
</SidebarLabel>
</div>
</SidebarLink>
</>
)
}
export default SubMenu

View file

@ -0,0 +1,3 @@
import Sidebar from "./Sidebar"
export default Sidebar

View file

@ -0,0 +1,35 @@
import { MouseEvent } from "react"
import styled from "styled-components"
import { faHashtag } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import theming from "../styles/theming"
const StyledTag = styled.div`
text-align: center;
margin-right: 0.8rem;
border-radius: 10px;
color: ${(props) =>
theming.theme(props.theme.currentTheme, {
light: theming.light.color2,
dark: theming.dark.color2,
})};
`
interface Props {
text: string
onClick?: (event: MouseEvent<never>) => void
}
const Tag = (props: Props) => {
return (
<StyledTag onClick={props.onClick || undefined}>
<FontAwesomeIcon icon={faHashtag} /> &nbsp;{props.text}
</StyledTag>
)
}
export default Tag

View file

@ -0,0 +1,26 @@
import { ReactNode } from "react"
import styled from "styled-components"
const StyledTagList = styled.div<{ direction: string }>`
display: flex;
flex-wrap: wrap;
row-gap: 0.5rem;
column-gap: 0.5rem;
flex-direction: row;
justify-content: ${({ direction }) => direction};
`
interface Props {
direction?: string
children?: ReactNode | undefined
}
const TagList = (props: Props) => {
return (
<StyledTagList direction={props.direction || "center"}>
{props.children}
</StyledTagList>
)
}
export default TagList

View file

@ -0,0 +1,44 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import {
faHome,
faFileLines,
faUser,
faUserTie,
} from "@fortawesome/free-solid-svg-icons"
// item from sidebar data
export type Item = {
path: string
icon: JSX.Element
title_en: string
title_kr: string
}
const NavbarData: Item[] = [
{
title_en: "Home",
title_kr: "홈",
path: "/",
icon: <FontAwesomeIcon icon={faHome} />,
},
{
title_en: "About",
title_kr: "소개",
path: "/about",
icon: <FontAwesomeIcon icon={faUser} />,
},
{
title_en: "Portfolio",
title_kr: "포트폴리오",
path: "/portfolio",
icon: <FontAwesomeIcon icon={faFileLines} />,
},
{
title_en: "Resume",
title_kr: "이력서",
path: "/resume",
icon: <FontAwesomeIcon icon={faUserTie} />,
},
]
export default NavbarData

View file

@ -0,0 +1,83 @@
import { createContext, useEffect, useReducer } from "react"
import storage from "local-storage-fallback"
import type { Dispatch, ReactNode, ReactElement } from "react"
export type SiteLocale = "en" | "kr"
export type SiteTheme = "dark" | "light"
export enum ActionsEnum {
UPDATE_THEME,
UPDATE_LOCALE,
}
export interface IGlobalState {
locale: SiteLocale
theme: SiteTheme
}
export type GlobalAction =
| {
type: ActionsEnum.UPDATE_THEME
payload: SiteTheme
}
| {
type: ActionsEnum.UPDATE_LOCALE
payload: SiteLocale
}
export interface IGlobalContext {
globalState: IGlobalState
dispatch: Dispatch<GlobalAction>
}
function getDefaultLocale(): SiteLocale {
if (window.location.pathname.startsWith("/en")) return "en"
if (window.location.pathname.startsWith("/kr")) return "kr"
return (storage.getItem("locale") as SiteLocale) || "en"
}
const defaultState: IGlobalState = {
locale: getDefaultLocale(),
theme: (storage.getItem("theme") || "dark") as SiteTheme,
}
export const globalContext = createContext({} as IGlobalContext)
function reducer(state = defaultState, action: GlobalAction): IGlobalState {
switch (action.type) {
case ActionsEnum.UPDATE_THEME:
state.theme = action.payload
break
case ActionsEnum.UPDATE_LOCALE:
state.locale = action.payload
break
default:
break
}
return { ...state }
}
export function GlobalStore(props: { children: ReactNode }): ReactElement {
const [globalState, dispatch] = useReducer(reducer, defaultState)
// save theme when it is changed
useEffect(() => {
storage.setItem("theme", globalState.theme)
}, [globalState.theme])
// save locale when it is changed
useEffect(() => {
storage.setItem("locale", globalState.locale)
}, [globalState.locale])
return (
<globalContext.Provider value={{ globalState, dispatch }}>
{props.children}
</globalContext.Provider>
)
}

Some files were not shown because too many files have changed in this diff Show more