├── .env.example ├── .gitignore ├── .prettierrc ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── astro.config.ts ├── package.json ├── pnpm-lock.yaml ├── public ├── banner.png ├── favicon │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── mstile-150x150.png │ ├── safari-pinned-tab.svg │ └── site.webmanifest ├── robots.txt └── sc1.png ├── src ├── assets │ └── posts │ │ ├── aho-corasick │ │ ├── cover.png │ │ └── trie.png │ │ ├── cat-in-server-room.png │ │ ├── chaotic-self.png │ │ ├── featuring-image-suspense-react-paulvall.png │ │ ├── firstOpt.gif │ │ ├── gopher-ok.png │ │ ├── hacktoberfest-2022.png │ │ ├── hacktoberfest.png │ │ ├── josephus-problem │ │ └── cover.png │ │ ├── programmer-with-chrome.png │ │ ├── secondOpt.gif │ │ ├── temporal-prop.png │ │ ├── typeguards.jpg │ │ ├── vesuvius.png │ │ └── wirths-law-banner.png ├── components │ ├── BlogCard.astro │ ├── Footer.astro │ ├── HeadTags.astro │ ├── Navbar.astro │ ├── RepoCard.astro │ ├── RepoLang.astro │ ├── Spotify │ │ ├── Badge.tsx │ │ ├── NotPlaying.tsx │ │ └── NowPlaying.tsx │ ├── TechBadge.astro │ └── icons │ │ └── Twitter.astro ├── content.config.ts ├── data │ └── blog │ │ ├── abstract-syntax-trees.mdx │ │ ├── aho-corasick-algo.mdx │ │ ├── breaking-into-tech.mdx │ │ ├── chrome-tip-object-copy.mdx │ │ ├── hacktoberfest-2022.mdx │ │ ├── josephus-problem.mdx │ │ ├── me-me-disease.mdx │ │ ├── react-suspense.mdx │ │ ├── temporal-proposal.mdx │ │ ├── the-comma-ok-idiom.mdx │ │ ├── typeguards.mdx │ │ ├── vesuvius-challenge.mdx │ │ └── wirths-law.mdx ├── env.d.ts ├── layouts │ ├── BlogPostLayout.astro │ └── Layout.astro ├── pages │ ├── 404.astro │ ├── api │ │ └── spotify.ts │ ├── contact.astro │ ├── index.astro │ ├── posts │ │ ├── [id].astro │ │ └── index.astro │ └── socials.astro ├── styles │ ├── index.css │ └── posts.css └── utils │ ├── data.ts │ ├── navigation.ts │ ├── readingTime.mjs │ ├── scrambleText.ts │ └── spotify.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | # Spotify API 2 | SPOTIFY_CLIENT_ID= 3 | SPOTIFY_CLIENT_SECRET= 4 | SPOTIFY_REFRESH_TOKEN= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | .output/ 4 | .vercel/ 5 | 6 | # dependencies 7 | node_modules/ 8 | 9 | # logs 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | 23 | # astro specific files for version 2.0.6 24 | .astro 25 | .idea 26 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": true, 4 | "htmlWhitespaceSensitivity": "css", 5 | "insertPragma": false, 6 | "jsxSingleQuote": true, 7 | "printWidth": 80, 8 | "proseWrap": "always", 9 | "quoteProps": "as-needed", 10 | "requirePragma": false, 11 | "semi": false, 12 | "singleQuote": true, 13 | "tabWidth": 2, 14 | "trailingComma": "all", 15 | "useTabs": false, 16 | "astroAllowShorthand": false, 17 | "plugins": ["prettier-plugin-astro", "prettier-plugin-tailwindcss"], 18 | "pluginSearchDirs": false 19 | } 20 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "editor.formatOnSave": true 4 | } 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Paul Valladares 2 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 3 | 4 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🧑🏻‍💻 My personal website 2 | 3 | An attempt to create a miniminal js personal blog using 4 | [Astro](https://astro.build/). 5 | 6 | ![screenshot](/public/sc1.png) 7 | 8 | ## 📚 Stack 9 | 10 | - Platform: [Astro](https://astro.build/) 11 | - Deployment: [Vercel](https://vercel.com/) 12 | - Package manager: [pnpm](https://pnpm.io/) 13 | - CSS: [Tailwind CSS](https://tailwindcss.com/) 14 | 15 | ## 🚀 Project Structure 16 | 17 | Inside of my Astro project, you'll see the following folders and files: 18 | 19 | ```bash 20 | ├── .vscode/ 21 | ├── public/ 22 | ├── src/ 23 | │ ├── assets/ 24 | │ ├── components/ 25 | │ ├── content/ 26 | │ ├── layouts/ 27 | │ ├── pages/ 28 | │ ├── styles/ 29 | │ └── utils/ 30 | ├── .env.example 31 | ├── .gitignore 32 | ├── .prettierrc 33 | ├── astro.config.ts 34 | ├── .LICENSE 35 | ├── package-json 36 | ├── README.md 37 | └── tsconfig.json 38 | ``` 39 | 40 | Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page 41 | is exposed as a route based on its file name. 42 | 43 | There's nothing special about `src/components/`, but that's where you should 44 | place any Astro/React/Vue/Svelte/Preact components. 45 | 46 | Any static assets, like images, can be placed in the `public/` directory. 47 | 48 | This project uses [Tailwind CSS](https://tailwindcss.com/) for styling. The 49 | `tailwind.config.js` file is where you can customize your Tailwind theme. 50 | 51 | I'm using the `assets` folder to store my images and the `content` folder to 52 | store my markdown files so I can take advantage of Astro's built-in image 53 | optimization. 54 | 55 | ## 🧞 Running Locally 56 | 57 | This app requires Node.js v18.4.1 or later. 58 | 59 | ```bash 60 | git clone git@github.com:dreyfus92/astro-portfolio.git 61 | cd astro-portfolio 62 | npm install -g pnpm 63 | pnpm i 64 | pnpm dev 65 | ``` 66 | 67 | ## 📝 License 68 | 69 | This project is licensed under the [MIT license](/LICENSE). 70 | -------------------------------------------------------------------------------- /astro.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config' 2 | import { remarkReadingTime } from './src/utils/readingTime.mjs' 3 | import tailwindcss from '@tailwindcss/vite' 4 | import mdx from '@astrojs/mdx' 5 | import sitemap from '@astrojs/sitemap' 6 | import vercel from '@astrojs/vercel' 7 | import expressiveCode from 'astro-expressive-code' 8 | import icon from 'astro-icon' 9 | 10 | import react from '@astrojs/react' 11 | 12 | // https://astro.build/config 13 | export default defineConfig({ 14 | markdown: { 15 | shikiConfig: { 16 | // Choose from Shiki's built-in themes (or add your own) 17 | // https://github.com/shikijs/shiki/blob/main/docs/themes.md 18 | theme: 'dark-plus', 19 | // Add custom languages 20 | // Note: Shiki has countless langs built-in, including .astro! 21 | // https://github.com/shikijs/shiki/blob/main/docs/languages.md 22 | langs: [], 23 | // Enable word wrap to prevent horizontal scrolling 24 | wrap: true, 25 | }, 26 | remarkPlugins: [remarkReadingTime], 27 | syntaxHighlight: 'shiki', 28 | }, 29 | integrations: [sitemap(), expressiveCode(), mdx(), react(), icon()], 30 | site: 'https://www.paulvall.dev', 31 | output: 'server', 32 | adapter: vercel(), 33 | image: { 34 | remotePatterns: [{ protocol: 'https' }], 35 | }, 36 | vite: { 37 | plugins: [tailwindcss()], 38 | }, 39 | }) 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paulvall.dev", 3 | "author": "Paul Valladares ", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro check --watch & astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview", 10 | "astro": "astro", 11 | "format": "prettier --write ." 12 | }, 13 | "dependencies": { 14 | "@astrojs/check": "0.9.4", 15 | "@astrojs/mdx": "4.0.8", 16 | "@astrojs/react": "4.2.0", 17 | "@astrojs/sitemap": "3.2.1", 18 | "@astrojs/vercel": "8.0.7", 19 | "@iconify-json/ic": "^1.2.2", 20 | "@iconify-json/mdi": "^1.2.3", 21 | "@iconify-json/pixelarticons": "^1.2.2", 22 | "@iconify-json/ri": "^1.2.5", 23 | "@iconify-json/simple-icons": "^1.2.24", 24 | "@iconify-json/system-uicons": "^1.2.2", 25 | "@tailwindcss/vite": "^4.0.6", 26 | "@types/react": "19.0.8", 27 | "@types/react-dom": "19.0.3", 28 | "astro": "^5.3.0", 29 | "astro-capo": "^0.0.1", 30 | "astro-expressive-code": "0.40.2", 31 | "astro-icon": "1.1.5", 32 | "framer-motion": "12.4.2", 33 | "mdast-util-to-string": "4.0.0", 34 | "react": "19.0.0", 35 | "react-dom": "19.0.0", 36 | "reading-time": "1.5.0", 37 | "sharp": "^0.33.5", 38 | "tailwindcss": "^4.0.6", 39 | "typescript": "5.7.3", 40 | "zod": "3.24.2" 41 | }, 42 | "devDependencies": { 43 | "@types/node": "22.13.4", 44 | "prettier": "3.5.1", 45 | "prettier-plugin-astro": "0.14.1", 46 | "prettier-plugin-tailwindcss": "0.6.11" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /public/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/public/banner.png -------------------------------------------------------------------------------- /public/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/public/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/public/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/public/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/public/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/public/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/public/favicon/favicon.ico -------------------------------------------------------------------------------- /public/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/public/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /public/favicon/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 56 | 58 | 60 | 62 | 64 | 65 | 67 | 69 | 70 | 72 | 74 | 76 | 78 | 86 | 88 | 90 | 92 | 94 | 95 | 97 | 99 | 101 | 103 | 105 | 106 | 107 | 108 | 110 | 111 | 112 | 113 | 115 | 117 | 119 | 121 | 123 | 125 | 127 | 128 | 130 | 132 | 134 | 136 | 138 | 140 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /public/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: Googlebot-Image 2 | Disallow: /assets/ 3 | 4 | Sitemap: https://www.paulvall.dev/sitemap-index.xml -------------------------------------------------------------------------------- /public/sc1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/public/sc1.png -------------------------------------------------------------------------------- /src/assets/posts/aho-corasick/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/aho-corasick/cover.png -------------------------------------------------------------------------------- /src/assets/posts/aho-corasick/trie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/aho-corasick/trie.png -------------------------------------------------------------------------------- /src/assets/posts/cat-in-server-room.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/cat-in-server-room.png -------------------------------------------------------------------------------- /src/assets/posts/chaotic-self.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/chaotic-self.png -------------------------------------------------------------------------------- /src/assets/posts/featuring-image-suspense-react-paulvall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/featuring-image-suspense-react-paulvall.png -------------------------------------------------------------------------------- /src/assets/posts/firstOpt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/firstOpt.gif -------------------------------------------------------------------------------- /src/assets/posts/gopher-ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/gopher-ok.png -------------------------------------------------------------------------------- /src/assets/posts/hacktoberfest-2022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/hacktoberfest-2022.png -------------------------------------------------------------------------------- /src/assets/posts/hacktoberfest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/hacktoberfest.png -------------------------------------------------------------------------------- /src/assets/posts/josephus-problem/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/josephus-problem/cover.png -------------------------------------------------------------------------------- /src/assets/posts/programmer-with-chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/programmer-with-chrome.png -------------------------------------------------------------------------------- /src/assets/posts/secondOpt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/secondOpt.gif -------------------------------------------------------------------------------- /src/assets/posts/temporal-prop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/temporal-prop.png -------------------------------------------------------------------------------- /src/assets/posts/typeguards.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/typeguards.jpg -------------------------------------------------------------------------------- /src/assets/posts/vesuvius.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/vesuvius.png -------------------------------------------------------------------------------- /src/assets/posts/wirths-law-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreyfus92/astro-portfolio/3d090f9b13dfacd9972c51a3d6d2298fca8f9208/src/assets/posts/wirths-law-banner.png -------------------------------------------------------------------------------- /src/components/BlogCard.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Icon } from 'astro-icon/components' 3 | 4 | type BlogCardProps = { 5 | title: string 6 | description: string 7 | date: string 8 | link: string 9 | readingTime: { 10 | minutesRead: string 11 | } 12 | } 13 | 14 | const { title, description, date, link, readingTime } = 15 | Astro.props as BlogCardProps 16 | --- 17 | 18 | 23 |

{title}

24 | 25 |

{description}

26 | 27 |
28 |
29 | 33 | 42 |
43 |
44 | 45 |

{readingTime}

46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /src/components/Footer.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Icon } from 'astro-icon/components' 3 | import Twitter from '@components/icons/Twitter.astro' 4 | --- 5 | 6 |
7 | 8 | 98 | -------------------------------------------------------------------------------- /src/components/HeadTags.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import '../styles/index.css' 3 | import { ClientRouter } from 'astro:transitions' 4 | 5 | export interface Props { 6 | title: string 7 | description: string 8 | image?: string 9 | } 10 | 11 | const { 12 | title, 13 | description, 14 | image = new URL('/banner.png', Astro.site), 15 | } = Astro.props 16 | --- 17 | 18 | 19 | {title} 20 | 21 | 22 | 23 | 24 | 29 | 35 | 41 | 42 | 47 | 48 | 49 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/components/Navbar.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { navItems } from '../utils/navigation' 3 | import { SpotifyNowPlaying } from '@components/Spotify/Badge' 4 | 5 | type Props = { 6 | display?: boolean 7 | } 8 | 9 | const { display } = Astro.props 10 | --- 11 | 12 | 37 | -------------------------------------------------------------------------------- /src/components/RepoCard.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import RepoLang from './RepoLang.astro' 3 | const { repo, link, description, language, languageColor } = Astro.props 4 | --- 5 | 6 | 7 |
10 |
11 | 24 |

{repo}

25 |
26 | 27 |
30 |

{repo}

31 | 32 |

33 | {description} 34 |

35 | 36 |
37 |
38 |
39 | -------------------------------------------------------------------------------- /src/components/RepoLang.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { color = '#0DB9D7', lang = 'Go' } = Astro.props 3 | --- 4 | 5 | 6 |

7 | {lang} 10 |

11 |
12 | 13 | 28 | -------------------------------------------------------------------------------- /src/components/Spotify/Badge.tsx: -------------------------------------------------------------------------------- 1 | import { type FC, useEffect, useState } from 'react' 2 | import type { NowPlayingTrackResponse } from '@utils/spotify' 3 | import { NotPlaying } from './NotPlaying' 4 | import { NowPlaying } from './NowPlaying' 5 | 6 | export const SpotifyNowPlaying: FC = () => { 7 | const [spotifyData, setSpotifyData] = useState() 8 | useEffect(() => { 9 | fetch('/api/spotify') 10 | .then((res) => res.json()) 11 | .then((data) => setSpotifyData(data)) 12 | }, []) 13 | console.log(spotifyData) 14 | return ( 15 |
  • 16 | {spotifyData?.isPlaying ? ( 17 | 18 | ) : ( 19 | 20 | )} 21 |
  • 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/components/Spotify/NotPlaying.tsx: -------------------------------------------------------------------------------- 1 | import { animate } from 'framer-motion' 2 | 3 | export const NotPlaying = () => { 4 | const onMouseEnterNotPlaying = (e: any) => { 5 | e.stopPropagation() 6 | animate([ 7 | [ 8 | '#first', 9 | { y: 0, x: 40, rotateZ: 0, opacity: 1 }, 10 | { type: 'spring', bounce: 0.5, duration: 0.5 }, 11 | ], 12 | ]) 13 | } 14 | 15 | const onMouseLeaveNotPlaying = (e: any) => { 16 | e.stopPropagation() 17 | animate([ 18 | ['#first', { y: 0, x: 0, rotateZ: 0, opacity: 0 }, { duration: 0.25 }], 19 | ]) 20 | } 21 | 22 | return ( 23 | 28 | 32 | this guy's just chilling, no tunes playing! 33 | 34 | 46 | 47 | 48 | 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /src/components/Spotify/NowPlaying.tsx: -------------------------------------------------------------------------------- 1 | import { animate } from 'framer-motion' 2 | import type { NowPlayingTrackResponse } from '@utils/spotify' 3 | 4 | export const NowPlaying = ({ props }: { props: NowPlayingTrackResponse }) => { 5 | const onMouseEnterNowPlaying = (e: any) => { 6 | e.stopPropagation() 7 | animate([ 8 | [ 9 | '#first', 10 | { y: 0, x: 40, rotateZ: 0, opacity: 1 }, 11 | { type: 'spring', bounce: 0.5, duration: 0.5 }, 12 | ], 13 | ]) 14 | } 15 | 16 | const onMoustLeaveNowPlaying = (e: any) => { 17 | e.stopPropagation() 18 | animate([ 19 | ['#first', { y: 0, x: 0, rotateZ: 0, opacity: 0 }, { duration: 0.25 }], 20 | ]) 21 | } 22 | 23 | return ( 24 | 29 | 34 | {props.title} - {props.artist} 35 | 36 | 48 | 49 | 50 | 51 | 52 | 53 | ) 54 | } 55 | -------------------------------------------------------------------------------- /src/components/TechBadge.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Image } from 'astro:assets' 3 | 4 | type TechBadgeProps = { 5 | name: string 6 | imgSrc: string 7 | altSrc: string 8 | } 9 | 10 | const { name, imgSrc, altSrc } = Astro.props as TechBadgeProps 11 | --- 12 | 13 |
    14 |
    17 | {altSrc} 18 |
    19 |

    {name}

    20 |
    21 | -------------------------------------------------------------------------------- /src/components/icons/Twitter.astro: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/content.config.ts: -------------------------------------------------------------------------------- 1 | // Import utilities from `astro:content` 2 | import { z, defineCollection } from 'astro:content' 3 | 4 | // Import loaders 5 | import { glob } from 'astro/loaders' 6 | 7 | // Define a schema for each collection you'd like to validate. 8 | const blog = defineCollection({ 9 | loader: glob({ pattern: '**/[^_]*.mdx', base: './src/data/blog' }), 10 | schema: ({ image }) => 11 | z.object({ 12 | title: z.string(), 13 | description: z.string(), 14 | pubDate: z.date(), 15 | draft: z.boolean(), 16 | cover: image(), 17 | coverAlt: z.string(), 18 | }), 19 | }) 20 | 21 | // Export a single `collections` object to register your collection(s) 22 | export const collections = { 23 | posts: blog, 24 | } 25 | -------------------------------------------------------------------------------- /src/data/blog/abstract-syntax-trees.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'What are Abstract Syntax Trees?' 3 | description: 'A brief introduction to Abstract Syntax Trees (ASTs).' 4 | pubDate: 2023-03-08 5 | draft: true 6 | cover: '@assets/posts/cat-in-server-room.png' 7 | coverAlt: 'cat in aserver room' 8 | --- 9 | 10 | # What are Abstract Syntax Trees? 11 | 12 | At a high level an Abastract Syntax Tree (AST) is an intermediate representation 13 | of source code as a tree structure. 14 | 15 | ## But what does that actually mean? 16 | 17 | The AST is a tree-like structure that represents the structure of the code in a 18 | way that is easier to manipulate and analyze than the original source code. Each 19 | node in the tree represents a construct in the code, such as a function, a loop, 20 | or an if statement. The children of each node represent the sub-constructs that 21 | make up the construct. 22 | 23 | ASTs are often used in compiler design to parse source code and generate an 24 | intermediate representation of the code. The intermediate representation can 25 | then be optimized or transformed before generating the final executable code. 26 | ASTs are also used in code analysis tools to perform tasks such as refactoring, 27 | linting, and code completion. 28 | 29 | ## How do I get an AST? 30 | 31 | JavaScript ecosystem has a lot of tools that can generate ASTs from source code. 32 | The most popular one is [Babel](https://babeljs.io/). Babel is a JavaScript 33 | compiler that can parse source code and generate an AST. Babel can also 34 | generates code from an AST. 35 | 36 | Let's see an example of how an AST would look like: 37 | 38 | {/* Image goes here */} 39 | 40 | You can tell by the picture that the root node is the plus sign. The children of 41 | the root node are the number 2 and the multiplication sign which has 2 children 42 | of its own, which are 2 numbers. This follows a hierarchy of signs and can be as 43 | well represented in JSON format. 44 | 45 | ```json 46 | { 47 | "type": "BinaryExpression", 48 | "left": { 49 | "type": "NumericLiteral", 50 | "value": 2 51 | }, 52 | "operator": "+", 53 | "right": { 54 | "type": "BinaryExpression", 55 | "left": { 56 | "type": "NumericLiteral", 57 | "value": 8 58 | }, 59 | "operator": "*", 60 | "right": { 61 | "type": "NumericLiteral", 62 | "value": 9 63 | } 64 | } 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /src/data/blog/aho-corasick-algo.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Unraveling the Aho-Corasick String Matching Algorithm' 3 | description: 4 | 'A brief introduction to the Aho-Corasick string matching algorithm, including 5 | a TypeScript implementation.' 6 | pubDate: 2023-10-09 7 | draft: false 8 | cover: '@assets/posts/aho-corasick/cover.png' 9 | coverAlt: 'Aho-Corasick cover image' 10 | --- 11 | 12 | import firstDiagram from '@assets/posts/aho-corasick/trie.png' 13 | import { Image } from 'astro:assets' 14 | 15 | The other day, I stumbled upon this algorithm and was truly captivated by its 16 | brilliance. I couldn't resist the urge to share it with all of you, so here it 17 | is. 18 | 19 | ## A bit of historical background 20 | 21 | The Aho-Corasick algorithm (hereinafter "A-C") was conceived by Alfred V. Aho 22 | and Margaret J. Corasick. A-C was introduced in their seminal paper titled 23 | "Efficient string matching: An aid to bibliographic search," published in 1975. 24 | This work was aimed at addressing the computational challenges associated with 25 | bibliographic search, which is a crucial aspect of academic research and 26 | literary work management. 27 | 28 | During the 1970s, bibliographic search was emerging as a critical challenge, 29 | especially with the burgeoning volume of academic literature. The need for 30 | efficient string matching algorithms was glaring, as traditional approaches were 31 | notably inefficient when tasked with searching for multiple patterns within 32 | large text corpora. 33 | 34 | A-C is built upon earlier theoretical work in automata theory and the theory of 35 | computation. It leverages the power of trie data structures and finite automata 36 | to perform efficient string matching. The algorithm's elegance lies in its 37 | simplicity and efficiency, making a single pass over the text, regardless of the 38 | number of patterns. 39 | 40 | ## The Trie Structure 41 | 42 | The journey begins with a data structure known as Trie, a type of search tree 43 | used to store a collection of strings. Each node in this tree represents a 44 | character, and a path from the root to a node spells out a word. This structure 45 | serves as the backbone of our algorithm, providing a map for the patterns we 46 | seek. 47 | 48 | ## Constructing the Trie 49 | 50 |
    51 |
    52 | Given a set of words, say `{"he", "she", "his", "hers"}`, the algorithm constructs a Trie. 53 | This process is akin to creating a directory where each word gets its own unique address 54 | based on its characters. As each word is processed, it's broken down into individual characters 55 | which guide the formation of branches in the Trie. The commonality between the starting 56 | characters of different words is utilized to create shared branches, thus optimizing 57 | the structure. 58 | 59 | For example, the words "he" and "hers" both start with the character 'h', so 60 | they share the initial branch. As we move to the next character, new branches 61 | are formed to accommodate the differences. This setup ensures that each word has 62 | a unique path within the Trie, yet common prefixes are represented efficiently. 63 | 64 |
    65 | diagram 66 |
    67 | 68 | ## Crafting Failure Links 69 | 70 | The algorithm then adorns the Trie with failure links, a mechanism to gracefully 71 | retreat and advance along different branches when a mismatch occurs. These links 72 | conect nodes in a manner that encapsulates the shared prefixes among the words, 73 | creating a network of fallbacks. It's akin to having secret passages that 74 | swiftly guide the search to the right path upon encountering a hurdle. 75 | 76 | ## The Search Begins 77 | 78 | With a well-prepared Trie, the search commences. As the algorithm traverses the 79 | text, it follows the branches of the Trie, character by character. Each match 80 | propels it deeper into the Trie, while a mismatch ushers it along the failure 81 | links to the next potential matching position. This dance continues, seamlessly 82 | transitioning between the text and the Trie, leaving no stone unturned. 83 | 84 | ## Harvesting the Matches 85 | 86 | Whenever the traversal reaches the end of a word in the Trie, a match is found! 87 | The position of the match within the text is recorded, and the search marches on 88 | unfazed, in pursuit of other patterns. 89 | 90 | ## The result 91 | 92 | At the conclusion of this expedition, the algorithm presents a comprehensive 93 | list of all ocurrences of the words within the text. A-C's ability to find 94 | multiple patterns in a single pass makes it a cherished asset in the toolbox of 95 | text processing. 96 | 97 | ## Implementation 98 | 99 | For those desiring a more tangible grasp, here is a TypeScript implementation of 100 | the Aho-Corasick algorithm. 101 | 102 | We will start by defining the max number of states in `buildMatchingMachine` and 103 | the max number of characters in the alphabet. 104 | 105 | ```ts 106 | const MAX_STATES = 500 107 | const MAX_CHARS = 26 108 | ``` 109 | 110 | Next we will define the `AhoCorasick class`, which will be responsible for 111 | constructing the Trie and performing the search. 112 | 113 | ```ts 114 | class AhoCorasick { 115 | private out: number[] = new Array(MAX_STATES).fill(0) 116 | private f: number[] = new Array(MAX_STATES).fill(-1) 117 | private g: number[][] = new Array(MAX_STATES) 118 | .fill(null) 119 | .map(() => new Array(MAX_CHARS).fill(-1)) 120 | 121 | constructor(private keywords: string[]) {} 122 | 123 | /** 124 | * Builds the Aho-Corasick machine, initializing the goto, failure, 125 | * and output functions. 126 | * @returns The total number of states in the machine. 127 | */ 128 | 129 | private buildMatchingMachine(): number { 130 | this.out.fill(0) 131 | this.g = new Array(MAX_STATES) 132 | .fill(null) 133 | .map(() => new Array(MAX_CHARS).fill(-1)) 134 | 135 | let states = 1 136 | 137 | for (let i = 0; i < this.keywords.length; ++i) { 138 | const word = this.keywords[i] 139 | let currentState = 0 140 | 141 | for (let j = 0; j < word.length; ++j) { 142 | const ch = word.charCodeAt(j) - 'a'.charCodeAt(0) 143 | 144 | if (this.g[currentState][ch] === -1) { 145 | this.g[currentState][ch] = states++ 146 | } 147 | 148 | currentState = this.g[currentState][ch] 149 | } 150 | 151 | this.out[currentState] |= 1 << i 152 | } 153 | 154 | for (let ch = 0; ch < MAX_CHARS; ++ch) { 155 | if (this.g[0][ch] === -1) { 156 | this.g[0][ch] = 0 157 | } 158 | } 159 | 160 | this.f.fill(-1) 161 | 162 | const q: number[] = [] 163 | for (let ch = 0; ch < MAX_CHARS; ++ch) { 164 | if (this.g[0][ch] !== 0) { 165 | this.f[this.g[0][ch]] = 0 166 | q.push(this.g[0][ch]) 167 | } 168 | } 169 | 170 | while (q.length) { 171 | const state = q.shift()! 172 | 173 | for (let ch = 0; ch < 26; ++ch) { 174 | if (this.g[state][ch] !== -1) { 175 | let failure = this.f[state] 176 | 177 | while (this.g[failure][ch] === -1) { 178 | failure = this.f[failure] 179 | } 180 | 181 | failure = this.g[failure][ch] 182 | this.f[this.g[state][ch]] = failure 183 | 184 | this.out[this.g[state][ch]] |= this.out[failure] 185 | 186 | q.push(this.g[state][ch]) 187 | } 188 | } 189 | } 190 | 191 | return states 192 | } 193 | 194 | /** 195 | * Finds the next state based on the current state and the next input character. 196 | * @param currentState - The current state in the machine. 197 | * @param nextInput - The next input character. 198 | * @returns The next state in the machine. 199 | */ 200 | 201 | private findNextState(currentState: number, nextInput: string): number { 202 | let answer = currentState 203 | const ch = nextInput.charCodeAt(0) - 'a'.charCodeAt(0) 204 | 205 | while (this.g[answer][ch] === -1) { 206 | answer = this.f[answer] 207 | } 208 | 209 | return this.g[answer][ch] 210 | } 211 | 212 | /** 213 | * Searches for all occurrences of the keywords in the given text. 214 | * @param text - The text to search. 215 | */ 216 | 217 | public searchWords(text: string): void { 218 | if (!text) { 219 | console.log('The text to search cannot be empty or null.') 220 | return 221 | } 222 | 223 | this.buildMatchingMachine() 224 | 225 | let currentState = 0 226 | 227 | for (let i = 0; i < text.length; ++i) { 228 | currentState = this.findNextState(currentState, text[i]) 229 | 230 | if (this.out[currentState] === 0) continue 231 | 232 | for (let j = 0; j < this.keywords.length; ++j) { 233 | if (this.out[currentState] & (1 << j)) { 234 | console.log( 235 | `Word ${this.keywords[j]} appears from ${ 236 | i - this.keywords[j].length + 1 237 | } to ${i}`, 238 | ) 239 | } 240 | } 241 | } 242 | } 243 | } 244 | ``` 245 | 246 | And finally we will use the class to search for the words in a given text. 247 | 248 | ```ts 249 | const arr = ['he', 'she', 'hers', 'his', 'a'] 250 | const text = 'ahishers' 251 | const ahoCorasickMachine = new AhoCorasick(arr) 252 | ahoCorasickMachine.searchWords(text) 253 | 254 | // Output: 255 | // Word his appears from 1 to 3 256 | // Word he appears from 4 to 5 257 | // Word she appears from 3 to 5 258 | // Word hers appears from 4 to 7 259 | ``` 260 | 261 | ## Resources 262 | 263 | - [Aho-Corasick algorithm](https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm) 264 | - [Efficient string matching: An aid to bibliographic search](https://dl.acm.org/doi/10.1145/360825.360855) 265 | - [Aho-Corasick Algorithm for Pattern Searching](https://www.geeksforgeeks.org/aho-corasick-algorithm-pattern-searching/) 266 | - [Rikito Taniguchi's implementation of A-C in TypeScript](https://github.com/tanishiking/aho-corasick-js) 267 | -------------------------------------------------------------------------------- /src/data/blog/breaking-into-tech.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'How I decided to break into tech?' 3 | description: 'A brief story of how I decided to break into tech in my late 20s.' 4 | pubDate: 2022-08-16 5 | draft: false 6 | cover: '@assets/posts/cat-in-server-room.png' 7 | coverAlt: 'cat in a server room' 8 | --- 9 | 10 | ## Who am I? 11 | 12 | I am a **civil engineer** graduated from National Autonomous University of 13 | Mexico (UNAM), I've worked in the field for 7 years, specifically in 14 | construction and project management. I've always been interested in technology, 15 | but I never had the opportunity to study it, so I decided to learn it on my own. 16 | 17 | ## So how it all started? 18 | 19 | It was October 22, 2021 when I decided it was time to take a break from civil 20 | engineering due to multiple factors and I knew it was time to try something 21 | else. I was very frightened about the taking this decision cause of what I read 22 | in that time about other people's journey and how some just ditched the journey 23 | and went back to their old jobs. Nonetheless, I decided to keep going with the 24 | plan. After my decision was made, I quitted my job and started learning how to 25 | code. 26 | 27 | My journey started on November, I knew that I needed to look for good resources 28 | to learn from. Some resources popped up on my screen and I started with 29 | [freeCodeCamp](https://www.freecodecamp.org/). After spending the first month 30 | learning the basics of HTML, CSS, and JavaScript. I found about the 31 | [Odin Project](https://www.theodinproject.com/), which was another resource to 32 | keep practicing and learning. I started with the Odin Project and I was able to 33 | finish most of the courses. 34 | 35 | When I found out that I was able to get my hands on React I discovered 36 | [Scrimba's React course](https://scrimba.com/learn/learnreact). Completed the 37 | whole course and fall in love with React's ecosystem. I was able to build a 38 | couple of projects with React. I finished 39 | [Scrimba's Frontend Developer Path](https://scrimba.com/learn/frontend) in 40 | May 2022. After that all pieces of the puzzle started to fit. 41 | 42 | ## What's next? 43 | 44 | After finishing the Odin Project and Scrimba's React course, I decided it was 45 | time to look for a job. I started applying to different job posts and I got a 46 | couple of interviews, until I got a position as a **Frontend Developer** at 47 | [Hackapulco](https://www.hackapulco.com/). I was very excited that I could land 48 | a job in a complete new field. I'm looking forward to keep learning and growing 49 | as a developer. 50 | -------------------------------------------------------------------------------- /src/data/blog/chrome-tip-object-copy.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'How to copy an object from Chrome Dev Tools?' 3 | description: 'A small guide on how to copy and object from Chrome Dev Tools.' 4 | pubDate: 2022-09-16 5 | draft: false 6 | cover: '@assets/posts/programmer-with-chrome.png' 7 | coverAlt: 'programmer-with-chrome' 8 | --- 9 | 10 | ## So how did I got into this? 11 | 12 | The other day I was working on a project from 13 | [Frontend Mentor](https://www.frontendmentor.io/) specifically the REST 14 | Countries API with color theme switcher challenge. So I ran into this problem 15 | where I was trying to define all the types of the whole object that I received 16 | by from the http Response to validate it with Zod. 17 | 18 | I was fetching the data from 19 | [the rest countries API](https://restcountries.com/) to print the response on 20 | the console of Chrome Dev Tools. So I questioned myself, how can I copy the 21 | response from the console? I tried to copy the response but it didn't work. 22 | 23 | So I started to search on the internet and I found this small tip to copy the 24 | response from the console. Next I found out that there were 2 options to copy 25 | the response from the console. 26 | 27 | ## Option 1 28 | 29 | The first option is to right click on the response and click on the 30 | `Store as global variable` option. After that you can copy that temporary 31 | variable from the console writing inside de console `copy(temporaryVariable)`. 32 | Finally you can paste the response wherever you want. 33 | 34 | ![first-option-to-copy-response-gif](@assets/posts/firstOpt.gif) 35 | 36 | ## Option 2 37 | 38 | The second option is to right click on the response and click on the 39 | `Copy object` option. 40 | 41 | ![second-option-to-copy-response-gif](@assets/posts/secondOpt.gif) 42 | -------------------------------------------------------------------------------- /src/data/blog/hacktoberfest-2022.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'How did it go with my first hackathon?' 3 | description: 4 | 'I participated in my first hackathon and I want to share my experience with 5 | you.' 6 | pubDate: 2022-10-17 7 | draft: false 8 | cover: '@assets/posts/hacktoberfest.png' 9 | coverAlt: 'hacktoberfest-image' 10 | --- 11 | 12 | ## So how did I got into this? 13 | 14 | Hacktoberfest is a yearly event that brings together developers from all over 15 | the world to contribute to open source projects and earn rewards. This year, I 16 | had the opportunity to participate in this amazing event, and I must say, it was 17 | a great experience. 18 | 19 | For those who are not familiar with Hacktoberfest, it is a global celebration of 20 | open source software, organized by DigitalOcean and DEV. The aim is to encourage 21 | people to get involved in open source development and to give back to the 22 | community. To participate, all you need to do is make five or more pull requests 23 | to any public GitHub repository during the month of October. 24 | 25 | This year, I decided to participate in Hacktoberfest as a way to challenge 26 | myself and learn new things. I had been wanting to get involved in open source 27 | for a long time, but I never found the time to do so. I figured that this was 28 | the perfect opportunity to start. 29 | 30 | The process of contributing to an open source project was much simpler than I 31 | had anticipated. I started by browsing through the list of projects on GitHub 32 | that were labeled as "good for beginners" and found one that was of interest to 33 | me. The project that I decided to start contributing was 34 | [Astro JS](https://astro.build/). I read through the project's documentation and 35 | got an idea of what needed to be done. Then, I forked the repository, made the 36 | changes, and submitted a pull request. 37 | 38 | I was pleasantly surprised by the friendly and supportive community. The project 39 | maintainers were quick to review my pull request and provide feedback. They also 40 | encouraged me to contribute to more issues and provided guidance on how to get 41 | started. 42 | 43 | Overall, participating in Hacktoberfest was a great experience. Not only did I 44 | get the chance to contribute to an open source project, but I also learned a lot 45 | about Git, GitHub, and the open source community. I'm already looking forward to 46 | Hacktoberfest 2023! 47 | 48 | In conclusion, I would highly recommend Hacktoberfest to anyone who is 49 | interested in open source development. It's a great opportunity to learn new 50 | things, get involved in the open source community, and make a positive impact. 51 | So, mark your calendars for October 2023 and get ready to contribute! 52 | -------------------------------------------------------------------------------- /src/data/blog/josephus-problem.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Understanding the Josephus Problem' 3 | description: 4 | 'A brief explaination on how to approach the Josephus Problem with TypeScript.' 5 | pubDate: 2023-10-23 6 | draft: false 7 | cover: '@assets/posts/josephus-problem/cover.png' 8 | coverAlt: 'josephus-hero' 9 | --- 10 | 11 | The Josephus problem is a famous mathematical puzzle with an intriguing 12 | backstory. As the legend goes, in 67 AD a group of 41 Jewish rebels were trapped 13 | by the Roman army. Preferring death over capture, they decided to form a circle 14 | and kill every third remaining rebel until only one was left. That last rebel 15 | would then commit suicide rather than be taken prisoner. One of the rebels, 16 | Josephus, wanted to figure out where to sit in the circle so he would be the 17 | final survivor. Rather than die, he could then surrender to the Romans. Finding 18 | the safe spot in the circle became known as the Josephus problem. 19 | 20 | While apocryphal, this tale provides a vivid setup for understanding the 21 | problem. Let's break it down step-by-step: 22 | 23 | ## The Problem 24 | 25 | Formally, the Josephus problem is: 26 | 27 | Given `n` people numbered 1 to `n` arranged in a circle, every `mth` person is 28 | eliminated until only one person remains. What is the position of the last 29 | survivor? 30 | 31 | For example, with `n=7` people and `m=3`: 32 | 33 | - Persons 1, 2, and 3 remain 34 | - Person 5 is eliminated next 35 | - Persons 6, 7, and 1 remain 36 | - Person 7 is eliminated 37 | - Persons 1 and 6 remain 38 | - Person 1 is the final survivor 39 | 40 | The solution should work for any `n` and `m` values. 41 | 42 | ## Solving through Examples 43 | 44 | To gain insights, we can work through examples systematically. The key 45 | observations: 46 | 47 | - The survivor is always in an odd-numbered position 48 | - When n is a power of 2, the survivor is person 1 49 | - Between powers of 2, the position increases by 2 each time 50 | 51 | Here are results for different n values with m=3: 52 | 53 | ```text 54 | n Survivor 55 | 1 1 56 | 2 1 57 | 3 3 58 | 4 1 59 | 5 3 60 | 6 5 61 | 7 7 62 | 8 1 63 | 9 3 64 | 10 5 65 | ``` 66 | 67 | ## The Recursive Solution 68 | 69 | The full solution uses: 70 | 71 | - Modular arithmetic 72 | - Expressing `n` as the largest power of 2 plus a remainder 73 | - Recursing on progressively smaller `n` 74 | 75 | If n = 2^a + l where l < 2^a, the survivor is: 76 | 77 | ```ts 78 | function survivor(n: number, m: number): number { 79 | if (n === 1) { 80 | return 1 81 | } 82 | 83 | return (survivor(n - 1, m) + m - 1) % n 84 | } 85 | ``` 86 | 87 | This recursively eliminates 1 person per round until a power of 2 remains. The 88 | final survivor is then `2l + 1`. 89 | 90 | For Josephus with `n=41`, written in binary as 32 + 8 + 1, l=9. The survivor is 91 | 2\*9 + 1 = 19. 92 | 93 | ## Applications 94 | 95 | The Josephus problem applies to rotating processes like round robin scheduling. 96 | It is also commonly used in tech interviews to evaluate recursive programming 97 | skills. Understanding and implementing the solution demonstrates strong 98 | algorithmic abilities. 99 | 100 | I hope this post helped explain the origins, logic, and code behind solving the 101 | famous Josephus problem. 102 | -------------------------------------------------------------------------------- /src/data/blog/me-me-disease.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'The Me Me Disease: Breaking Free from Self-Obsessed ' 3 | description: 'Some personal thoughts on radical self thinking.' 4 | pubDate: 2025-02-13 5 | draft: false 6 | cover: '@assets/posts/chaotic-self.png' 7 | coverAlt: 'a man with racing thoughts' 8 | --- 9 | 10 | Let’s be real—our world today makes it way too easy to get stuck in what I like 11 | to call “the me me disease.” Now, I’m not saying we all have a literal illness, 12 | but it sure feels like every thought and action revolves around our own little 13 | bubble. Social media, reality TV, and a culture obsessed with individual wins 14 | have all chipped in, making self-interest seem like the go-to way of living. 15 | 16 | At the heart of this “me me” mindset is an overblown focus on our own identity. 17 | Every day, we’re hit with messages that tell us our self-worth is measured by 18 | likes, shares, or the next big personal achievement. It turns into a 19 | never-ending chase for validation that, more often than not, leaves us feeling 20 | isolated and anxious instead of genuinely happy. When all we do is dwell on 21 | ourselves, we miss out on the deeper, richer parts of life—like empathy, 22 | connection, and contributing to something bigger than our own wants. 23 | 24 | So, what would life look like if we could just step back and see the bigger 25 | picture? Imagine an “enlightened” version of ourselves—not someone who 26 | completely ditches self-care or self-awareness, but someone who gets that our 27 | individual selves are just one thread in a much larger tapestry. Enlightenment 28 | is about breaking free from our ego’s grip and learning to view the world with a 29 | bit more openness, compassion, and genuine interest in the well-being of others. 30 | 31 | It’s cool to see how both ancient philosophies and modern thinkers echo this 32 | idea. Take Naval Ravikant, for example—a guy who’s as sharp as they come. He 33 | once said, “Desire is a contract you make with yourself to be unhappy.” What a 34 | way to put it! His words really push us to rethink the endless pursuit of 35 | external validation. Instead of chasing after more likes, more achievements, or 36 | more stuff, maybe we should focus on building inner peace and clarity. 37 | 38 | Whether you’re into Buddhism, Stoicism, or just trying to navigate the chaos of 39 | modern life, there’s a lot of truth in stepping away from the non-stop 40 | celebration of our own existence. Real fulfillment comes from realizing how 41 | connected we all are. When we stop trying to be the center of our own universe, 42 | we start to feel the freedom that comes from caring about others, getting 43 | involved in our communities, and finding a purpose that’s bigger than us. 44 | 45 | So, how can we kick the me me disease to the curb? First off, it starts with 46 | awareness. Notice when you’re getting sucked into self-obsession rather than 47 | focusing on genuine connections and contributions. Try out simple practices like 48 | mindfulness meditation to help you hit pause on those autopilot thoughts. And 49 | hey, taking a break from your phone or the endless scroll can really give you 50 | the mental space to remember what matters most. 51 | 52 | On a larger scale, maybe it’s time we all rethink the way our culture glorifies 53 | individualism. Imagine a world where we value community and collective 54 | well-being over solo achievements. That might mean changing how we use social 55 | media, pushing for more meaningful interactions, and creating spaces—both online 56 | and off—where cooperation trumps competition. 57 | 58 | In the end, while the me me disease is all too common in today’s society, 59 | there’s a way out. By challenging our self-centered habits and embracing a more 60 | mindful, compassionate way of living, we not only boost our own well-being but 61 | also help build a kinder, more connected world. Let’s try to do just that. 62 | -------------------------------------------------------------------------------- /src/data/blog/react-suspense.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'What is Suspense and how does it work?' 3 | description: 4 | 'A brief explanation of what Suspense is and how it works in React.' 5 | pubDate: 2023-04-14 6 | draft: false 7 | cover: '@assets/posts/featuring-image-suspense-react-paulvall.png' 8 | coverAlt: 'hero-for-react-suspense' 9 | --- 10 | 11 | In this blog post, we will explore what Suspense is, how it works in React, and 12 | provide some examples to demonstrate its usage. 13 | 14 | ## What is Suspense? 15 | 16 | Suspense is a feature in React that allows you to delay the rendering of a 17 | component until some asynchronous operation, such as fetching data from an API, 18 | has completed. This helps to avoid showing a blank or incomplete page to the 19 | user while they are waiting for the data to load. 20 | 21 | In earlier versions of React, it was difficult to handle loading states for 22 | components that were fetching data asynchronously. The component would be 23 | rendered immediately, and if the data wasn't available yet, you would have to 24 | handle the loading state manually. 25 | 26 | Suspense simplifies this process by providing a way to delay rendering until the 27 | data is available. It also makes it easier to handle loading states and errors 28 | by providing a declarative API for specifying how to handle these scenarios. 29 | 30 | ## How does it work? 31 | 32 | In React, a component is rendered when its parent component calls it. When using 33 | Suspense, the parent component wraps the component that is being loaded 34 | asynchronously with a Suspense component. 35 | 36 | The Suspense component takes a fallback prop, which is displayed while the data 37 | is being loaded. Once the data is available, the component wrapped in Suspense 38 | is rendered. 39 | 40 | Here's an example of how you might use Suspense in React: 41 | 42 | ```jsx 43 | import React, { Suspense } from 'react' 44 | 45 | const MyComponent = React.lazy(() => import('./MyComponent')) 46 | 47 | function App() { 48 | return ( 49 |
    50 | Loading...
    }> 51 | 52 | 53 | 54 | ) 55 | } 56 | 57 | export default App 58 | ``` 59 | 60 | In this example, we are using React.lazy() to lazily load MyComponent. We wrap 61 | it in a Suspense component and provide a fallback component that will be 62 | displayed while MyComponent is loading. Once MyComponent has finished loading, 63 | it will be rendered. 64 | 65 | ## Error boundaries 66 | 67 | In addition to loading states, Suspense can also handle errors that may occur 68 | while fetching data. You can specify an error boundary component to handle any 69 | errors that occur while the component is being loaded. 70 | 71 | Here's an example of how to use an error boundary with Suspense: 72 | 73 | ```jsx 74 | import React, { Suspense } from 'react' 75 | 76 | const MyComponent = React.lazy(() => import('./MyComponent')) 77 | 78 | function ErrorFallback() { 79 | return
    Error loading component
    80 | } 81 | 82 | function App() { 83 | return ( 84 |
    85 | }> 86 | Loading...
    }> 87 | 88 | 89 | 90 | 91 | ) 92 | } 93 | 94 | export default App 95 | ``` 96 | 97 | In this example, we've added an ErrorBoundary component that wraps our Suspense 98 | component. The ErrorBoundary component takes a fallback prop that will be 99 | displayed if an error occurs while loading the component. 100 | 101 | ## Concurrent Mode 102 | 103 | Suspense was introduced in React 16.6, but it wasn't until React 18 that it 104 | became more fully featured with the addition of Concurrent Mode. Concurrent Mode 105 | is a new set of features in React that improve the performance and user 106 | experience of applications. 107 | 108 | One of the main features of Concurrent Mode is that it enables React to work on 109 | multiple tasks at the same time, such as rendering a component and fetching 110 | data. This makes it possible to provide a smoother user experience, especially 111 | on slower devices or networks. 112 | 113 | ## Server-Side Rendering 114 | 115 | Another benefit of Suspense is that it can be used for server-side rendering in 116 | React. When using server-side rendering, the initial HTML is generated on the 117 | server, rather than in the user's browser. This can help to improve the 118 | performance and SEO of your application. 119 | 120 | To use Suspense for server-side rendering, you can use the ReactDOMServer 121 | module, which provides a renderToString function. This function can be used to 122 | render your application to HTML on the server, including any components that are 123 | loaded asynchronously with Suspense. 124 | 125 | ## React.SuspenseList 126 | 127 | React 18 introduced a new feature called SuspenseList, which allows you to 128 | specify how multiple Suspense components should be rendered together. 129 | SuspenseList can be used to display multiple loading states, such as when 130 | multiple components are loading asynchronously at the same time. 131 | 132 | Here's an example of how to use SuspenseList: 133 | 134 | ```jsx 135 | import React, { Suspense } from 'react' 136 | 137 | const Component1 = React.lazy(() => import('./Component1')) 138 | const Component2 = React.lazy(() => import('./Component2')) 139 | 140 | function App() { 141 | return ( 142 |
    143 | 144 | Loading component 1...
    }> 145 | 146 | 147 | Loading component 2...}> 148 | 149 | 150 | 151 | 152 | ) 153 | } 154 | 155 | export default App 156 | ``` 157 | 158 | In this example, we are using SuspenseList to render multiple Suspense 159 | components together. We specify the revealOrder prop as "together", which means 160 | that the components will be displayed together once they are all ready. 161 | 162 | ## Conclusion 163 | 164 | Suspense is a powerful feature in React that simplifies handling loading states 165 | and errors when fetching data asynchronously. With the addition of Concurrent 166 | Mode and SuspenseList, it's now easier than ever to provide a smooth user 167 | experience, even when loading multiple components asynchronously. 168 | 169 | If you're building a React application that fetches data asynchronously, using 170 | Suspense can help to simplify your code and provide a better user experience. 171 | 172 | ## Extra Resources 173 | 174 | - [React Suspense](https://react.dev/reference/react/Suspense) 175 | -------------------------------------------------------------------------------- /src/data/blog/temporal-proposal.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 3 | 'Exploring the Temporal Proposal: Modernizing Date and Time Handling in 4 | JavaScript' 5 | description: 6 | 'Taking a look at the Temporal proposal, which aims to modernize date and time 7 | handling in JavaScript.' 8 | pubDate: 2023-09-19 9 | draft: false 10 | cover: '@assets/posts/temporal-prop.png' 11 | coverAlt: 'hero for temporal proposal' 12 | --- 13 | 14 | ## Introduction 15 | 16 | The handling of dates in ECMAScript has long been a source of frustration for 17 | developers. In response to this, the Temporal proposal was born. Temporal is a 18 | global object, similar to Math, that introduces a modern date and time API to 19 | the ECMAScript language. To dive deeper into the challenges associated with the 20 | existing Date object and the motivations behind Temporal, take a look at this 21 | informative article: 22 | [Fixing JavaScript Date](https://maggiepint.com/2017/04/11/fixing-javascript-date-web-compatibility-and-reality/). 23 | 24 | ## How Temporal Solves Date and Time Challenges 25 | 26 | Temporal addresses these date and time challenges by offering: 27 | 28 | **User-Friendly APIs:** Temporal provides easy-to-use APIs for various date and 29 | time computations, making complex operations more accessible. 30 | 31 | **Time Zone Support:** It boasts first-class support for all time zones, 32 | including daylight-saving time (DST) adjustments, ensuring accurate and reliable 33 | arithmetic across different regions. 34 | 35 | **Immutability:** Temporal enforces immutability, meaning that once a date or 36 | time object is created, it cannot be altered. This helps prevent bugs stemming 37 | from unintended modifications. 38 | 39 | **Strict String Parsing:** Temporal allows you to parse date and time strings in 40 | a strictly specified format, ensuring consistency in data handling. 41 | 42 | **Non-Gregorian Calendars:** Temporal even goes beyond the Gregorian calendar, 43 | accommodating different calendar systems, broadening its usability. 44 | 45 | Additionally, Temporal introduces separate ECMAScript classes for date-only, 46 | time-only, and other specialized use cases. This separation enhances code 47 | readability and minimizes errors that often arise from assumptions about time 48 | zones or values. 49 | 50 | ## Getting Started with Temporal 51 | 52 | For those eager to embark on their Temporal journey, a cookbook is available to 53 | guide you through the fundamentals and intricacies of Temporal. It serves as a 54 | valuable resource for developers of all levels, offering practical examples and 55 | insights. 56 | [Explore the Temporal Cookbook](https://tc39.es/proposal-temporal/docs/cookbook.html) 57 | to kickstart your Temporal exploration. 58 | 59 | ## Understanding Temporal API Documentation 60 | 61 | The Temporal API follows a consistent naming convention, with types that start 62 | with "Plain" (e.g., `Temporal.PlainDate`, `Temporal.PlainTime`, and 63 | `Temporal.PlainDateTime`) for objects without an associated time zone. However, 64 | converting between these types and exact time types (`Temporal.Instant` and 65 | `Temporal.ZonedDateTime`) can introduce ambiguity due to time zones and DST. 66 | Temporal's API empowers developers to configure how this ambiguity is resolved. 67 | 68 | To delve deeper into essential concepts such as exact time, wall-clock time, 69 | time zones, DST, and handling ambiguity, consult the comprehensive 70 | [Temporal API Documentation](https://tc39.es/proposal-temporal/docs/index.html). 71 | 72 | ### Temporal.Now: Instant Gratification 73 | 74 | Temporal introduces a powerful feature known as Temporal.Now, offering real-time 75 | insights into the current system time and time zone. Developers can leverage 76 | this feature to access: 77 | 78 | `Temporal.Now.instant()`: Retrieve the current system exact time. 79 | `Temporal.Now.timeZoneId()`: Obtain the current system time zone. 80 | `Temporal.Now.zonedDateTime(calendar)`: Fetch the current date and wall-clock 81 | time in the system time zone and a specified calendar. 82 | `Temporal.Now.zonedDateTimeISO()`: Access the current date and wall-clock time 83 | in the system time zone using the ISO-8601 calendar. 84 | `Temporal.Now.plainDate(calendar)`: Obtain the current date in the system time 85 | zone and a specified calendar. `Temporal.Now.plainDateISO()`: Retrieve the 86 | current date in the system time zone using the ISO-8601 calendar. 87 | `Temporal.Now.plainTimeISO()`: Retrieve the current wall-clock time in the 88 | system time zone using the ISO-8601 calendar. 89 | `Temporal.Now.plainDateTime(calendar)`: Retrieve the current system date/time in 90 | the system time zone. Note that the resulting object does not remember its time 91 | zone, making it unsuitable for deriving other values in time zones that observe 92 | Daylight Saving Time (DST). 93 | 94 | Here's a quick example: 95 | 96 | ```js 97 | console.log('Initialization complete', Temporal.Now.instant()) 98 | // Example output: 99 | // Initialization complete 2021-01-13T20:57:01.500944804Z 100 | ``` 101 | 102 | ### Temporal.Instant: Capturing Fixed Points in Time 103 | 104 | A `Temporal.Instant` represents a fixed point in time, devoid of calendar or 105 | location considerations. For instance, it can represent a moment like July 20, 106 | 1969, at 20:17 UTC. If you need human-readable local calendar dates or clock 107 | times, Temporal suggests using a combination of `Temporal.TimeZone` and 108 | `Temporal.Calendar` to obtain a `Temporal.ZonedDateTime` or 109 | `Temporal.PlainDateTime`. 110 | 111 | ```js 112 | const instant = Temporal.Instant.from('1969-07-20T20:17Z') 113 | instant.toString() // => '1969-07-20T20:17:00Z' 114 | instant.epochMilliseconds // => -14182980000 115 | ``` 116 | 117 | ### Temporal.ZonedDateTime: Navigating Time Zones and Calendars 118 | 119 | A `Temporal.ZonedDateTime` is a versatile date/time object that considers time 120 | zones and calendars, representing real-world events from the perspective of 121 | specific regions on Earth. Whether it's December 7th, 1995, at 3:24 AM in US 122 | Pacific time within the Gregorian calendar, or any other scenario, 123 | Temporal.ZonedDateTime has you covered. 124 | 125 | ```js 126 | const zonedDateTime = Temporal.ZonedDateTime.from({ 127 | timeZone: 'America/Los_Angeles', 128 | year: 1995, 129 | month: 12, 130 | day: 7, 131 | hour: 3, 132 | minute: 24, 133 | second: 30, 134 | millisecond: 0, 135 | microsecond: 3, 136 | nanosecond: 500, 137 | }) // => 1995-12-07T03:24:30.0000035-08:00[America/Los_Angeles] 138 | ``` 139 | 140 | The broad capabilities of Temporal.ZonedDateTime make it a powerful combination 141 | of Temporal.TimeZone, Temporal.Instant, and Temporal.PlainDateTime. 142 | 143 | ### Temporal.PlainDate: A Date Without Time or Time Zone 144 | 145 | A Temporal.PlainDate object encapsulates a calendar date without any association 146 | with a specific time or time zone. For example, it can represent August 147 | 24th, 2006. 148 | 149 | ```js 150 | const date = Temporal.PlainDate.from({ year: 2006, month: 8, day: 24 }) 151 | date.year // => 2006 152 | date.inLeapYear // => false 153 | date.toString() // => '2006-08-24' 154 | ``` 155 | 156 | Additionally, `Temporal.PlainDate` can be converted to partial dates like 157 | `Temporal.PlainYearMonth` and `Temporal.PlainMonthDay`. 158 | 159 | ### Temporal.PlainTime: Time Without Date or Time Zone 160 | 161 | A `Temporal.PlainTime` object represents a wall-clock time without any 162 | connection to a specific date or time zone. Think of it as a way to represent 163 | times like 7:39 PM. 164 | 165 | ```js 166 | const time = Temporal.PlainTime.from({ 167 | hour: 19, 168 | minute: 39, 169 | second: 9, 170 | millisecond: 68, 171 | microsecond: 346, 172 | nanosecond: 205, 173 | }) 174 | time.second // => 9 175 | time.toString() // => '19:39:09.068346205' 176 | ``` 177 | 178 | ### Temporal.PlainDateTime: Date and Time Without Time Zone 179 | 180 | A `Temporal.PlainDateTime` elegantly represents a combination of calendar date 181 | and wall-clock time without tying itself to a specific time zone. It's perfect 182 | for scenarios like December 7th, 1995, at 3:00 PM in the Gregorian calendar. 183 | 184 | ```js 185 | const dateTime = Temporal.PlainDateTime.from({ 186 | year: 1995, 187 | month: 12, 188 | day: 7, 189 | hour: 15, 190 | }) 191 | const dateTime1 = dateTime.with({ 192 | minute: 17, 193 | second: 19, 194 | }) 195 | ``` 196 | 197 | While `Temporal.PlainDateTime` is a versatile choice, it's essential to note 198 | that for use cases requiring precise time zone adjustments, especially 199 | concerning Daylight Saving Time (DST), consider using `Temporal.ZonedDateTime`. 200 | 201 | ### Temporal.PlainYearMonth: Focusing on the Year and Month 202 | 203 | Sometimes, you need to express a date without specifying the day component. 204 | Temporal.PlainYearMonth serves this purpose, making it suitable for scenarios 205 | like "the October 2020 meeting." 206 | 207 | ```js 208 | const yearMonth = Temporal.PlainYearMonth.from({ year: 2020, month: 10 }) 209 | yearMonth.daysInMonth // => 31 210 | yearMonth.daysInYear // => 366 211 | ``` 212 | 213 | ### Temporal.PlainMonthDay: A Date Without a Year 214 | 215 | `Temporal.PlainMonthDay` represents dates without a year component, making it 216 | ideal for expressing events like "Bastille Day is on the 14th of July." 217 | 218 | ```js 219 | const monthDay = Temporal.PlainMonthDay.from({ month: 7, day: 14 }) 220 | const date = monthDay.toPlainDate({ year: 2030 }) 221 | date.dayOfWeek // => 7 222 | ``` 223 | 224 | ### Temporal.Duration: Measuring Time Intervals 225 | 226 | For measuring time intervals, Temporal introduces `Temporal.Duration`. It's the 227 | go-to choice when dealing with date/time arithmetic or calculating differences 228 | between Temporal objects. 229 | 230 | ```js 231 | const duration = Temporal.Duration.from({ 232 | hours: 130, 233 | minutes: 20, 234 | }) 235 | duration.total({ unit: 'second' }) // => 469200 236 | ``` 237 | 238 | ### Temporal.TimeZone: Managing Time Zone Translations 239 | 240 | `Temporal.TimeZone` is your companion for managing IANA time zones, UTC offsets, 241 | and UTC itself. It facilitates the conversion from UTC to local date/time and 242 | helps find offsets for specific `Temporal.Instant` moments. 243 | 244 | ```js 245 | const timeZone = Temporal.TimeZone.from('Africa/Cairo') 246 | timeZone.getInstantFor('2000-01-01T00:00') // => 1999-12-31T22:00:00Z 247 | timeZone.getPlainDateTimeFor('2000-01-01T00:00Z') // => 2000-01-01T02:00:00 248 | timeZone.getPreviousTransition(Temporal.Now.instant()) // => 2014-09-25T21:00:00Z 249 | timeZone.getNextTransition(Temporal.Now.instant()) // => null 250 | ``` 251 | 252 | ### Temporal.Calendar: Embracing Different Calendar Systems 253 | 254 | While most code aligns with the ISO 8601 calendar, Temporal accommodates various 255 | calendar systems. It's important to note that dates are associated with 256 | `Temporal.Calendar` objects to perform calendar-related operations. 257 | 258 | ```js 259 | const cal = Temporal.Calendar.from('iso8601') 260 | const date = cal.dateFromFields({ year: 1999, month: 12, day: 31 }, {}) 261 | date.monthsInYear // => 12 262 | date.daysInYear // => 365 263 | ``` 264 | 265 | By navigating the comprehensive documentation and utilizing the powerful 266 | features of Temporal, developers can revolutionize their date and time handling 267 | in JavaScript. Temporal brings clarity, precision, and reliability to 268 | date-related operations, making it an invaluable addition to the ECMAScript 269 | ecosystem. 270 | 271 | ## Conclusion 272 | 273 | In conclusion, Temporal represents a significant leap forward in addressing the 274 | long-standing date and time challenges in ECMAScript. With its user-friendly 275 | APIs, time zone support, immutability, and comprehensive documentation, Temporal 276 | equips developers to conquer complex date and time scenarios with confidence. As 277 | you explore Temporal's capabilities and integrate it into your projects, you'll 278 | discover a new level of precision and clarity in date and time handling. 279 | 280 | ## References 281 | 282 | - [Temporal Proposal Documentation](https://tc39.es/proposal-temporal/docs/index.html) 283 | - [Temporal GitHub Repository](https://github.com/tc39/proposal-temporal) 284 | - [Fixing JavaScript Date](https://maggiepint.com/2017/04/11/fixing-javascript-date-web-compatibility-and-reality/) 285 | -------------------------------------------------------------------------------- /src/data/blog/the-comma-ok-idiom.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'The Comma Ok Idiom' 3 | description: 4 | 'Understanding the Comma Ok Idiom in Golang: Ensuring Safe Type Assertions.' 5 | pubDate: 2023-07-10 6 | draft: false 7 | cover: '@assets/posts/gopher-ok.png' 8 | coverAlt: 'gopher-ok' 9 | --- 10 | 11 | Lately I've been enthusiastically investing time and effort into learning the Go 12 | programming language to expand my coding skills. Recognizing the rising 13 | popularity and remarkable performance of Go in various domains, I embarked on 14 | this learning journey with the goal of mastering its unique features and idioms. 15 | 16 | ## Introduction 17 | 18 | As a Golang developer, you've likely encountered situations where you need to 19 | perform type assertions to access values from interfaces. However, performing 20 | type assertions without proper validation can lead to runtime panics. This is 21 | where the "comma ok" idiom comes into play. In this blog post, we will explore 22 | the comma ok idiom in Golang and how it can be used to safely perform type 23 | assertions. 24 | 25 | ## What is the Comma Ok Idiom? 26 | 27 | In Golang, the comma ok idiom is a simple and powerful construct used to check 28 | if a type assertion is successful without triggering a panic. It is primarily 29 | employed when dealing with interfaces to ensure that the asserted value is of 30 | the expected type. 31 | 32 | The syntax of the comma ok idiom is as follows: 33 | 34 | ```go 35 | value, ok := interfaceVariable.(TargetType) 36 | ``` 37 | 38 | I will try to give a couple of examples in order to make this a bit clearer. 39 | 40 | Here, `interfaceVariable` is the variable of type `interface{}`, and 41 | `TargetType` is the type you are attempting to assert. If the type assertion is 42 | successful, value will hold the asserted `value`, and `ok` will be `true`. If 43 | the type assertion fails, `value` will be set to the zero value of `TargetType`, 44 | and `ok` will be `false`. 45 | 46 | ### Example 1: Type Assertion for Basic Types 47 | 48 | ```go 49 | func printIntValue(val interface{}) { 50 | if num, ok := val.(int); ok { 51 | fmt.Println("The value is an integer:", num) 52 | } else { 53 | fmt.Println("The value is not an integer.") 54 | } 55 | } 56 | 57 | func main() { 58 | printIntValue(42) // Output: The value is an integer: 42 59 | printIntValue("hello")// Output: The value is not an integer. 60 | } 61 | ``` 62 | 63 | In the above example, the `printIntValue` function uses the comma ok idiom to 64 | check if the provided interface value is an integer. If the assertion succeeds, 65 | it prints the value; otherwise, it prints an appropriate message. 66 | 67 | ### Example 2: Type Assertion for Custom Types 68 | 69 | ```go 70 | type Person struct { 71 | Name string 72 | Age int 73 | } 74 | 75 | func printPersonName(val interface{}) { 76 | if person, ok := val.(Person); ok { 77 | fmt.Println("Person's name:", person.Name) 78 | } else { 79 | fmt.Println("Not a valid Person type.") 80 | } 81 | } 82 | 83 | func main() { 84 | person := Person{Name: "John", Age: 30} 85 | 86 | printPersonName(person) // Output: Person's name: John 87 | 88 | printPersonName("hello")// Output: Not a valid Person type. 89 | } 90 | ``` 91 | 92 | In this example, we use the comma ok idiom to check if the provided interface 93 | value is of type `Person`. If it is, we can safely access its `Name` field; 94 | otherwise, we handle the case when the assertion fails. 95 | 96 | ### Example 3: Working with Slices 97 | 98 | ```go 99 | func getFirstElement(val interface{}) interface{} { 100 | if slice, ok := val.([]int); ok { 101 | if len(slice) > 0 { 102 | return slice[0] 103 | } 104 | } 105 | return nil 106 | } 107 | 108 | func main() { 109 | numbers := []int{10, 20, 30} 110 | result := getFirstElement(numbers) 111 | fmt.Println("First element:", result) // Output: First element: 10 112 | 113 | text := "hello" 114 | result = getFirstElement(text) 115 | fmt.Println("First element:", result) // Output: First element: 116 | } 117 | ``` 118 | 119 | In this example, we use the comma ok idiom to check if the provided interface 120 | value is a slice of integers. If it is, we retrieve the first element safely, 121 | avoiding index out-of-bounds errors. 122 | 123 | ## Conclusion 124 | 125 | The comma ok idiom is an essential tool in the Golang developer's toolbox for 126 | safely performing type assertions on interface values. By using this idiom, you 127 | can prevent runtime panics and gracefully handle cases where the type assertion 128 | fails. Mastering the comma ok idiom will undoubtedly enhance the robustness and 129 | reliability of your Golang applications. Happy coding! 130 | -------------------------------------------------------------------------------- /src/data/blog/typeguards.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Typeguards in TypeScript' 3 | description: 'What are typeguards in TypeScript and how to use them' 4 | pubDate: 2023-03-03 5 | draft: false 6 | cover: '@assets/posts/typeguards.jpg' 7 | coverAlt: 'banner-with-typescript-logo-and-guard' 8 | --- 9 | 10 | Type guards are a key TypeScript feature that enables you to build code that is 11 | more resistant to runtime mistakes. In this post, we'll look at type guards, how 12 | to use them, and some best practices for putting them in place. 13 | 14 | ## What are type guards? 15 | 16 | **TypeScript** is a typed programming language, which means that variables and 17 | functions have a predefined type. When working with data from external sources 18 | (for example, user input or API answers), it might be difficult to confirm that 19 | the data is of the correct type. Type guards are very handy here. 20 | 21 | A type guard is a piece of code that verifies the type of a variable during 22 | runtime and changes the variable's type as needed. Assume you have a variable 23 | data, which might be a string or an object: 24 | 25 | ```ts 26 | let data: string | object = 'hello' 27 | ``` 28 | 29 | If you wish to access a data property that exists exclusively on the object 30 | type, you must first check the data type: 31 | 32 | ```ts 33 | if (typeof data === 'object') { 34 | console.log(data.property) // error: property does not exist on type string 35 | } 36 | ``` 37 | 38 | The `typeof` operator determines the `data` type, and the if statement 39 | establishes a type guard that guarantees data is an object before accessing its 40 | property property. 41 | 42 | Type guards can be implemented as functions as well. As an example, suppose you 43 | have an interface Person: 44 | 45 | ```ts 46 | interface Person { 47 | name: string 48 | age: number 49 | } 50 | ``` 51 | 52 | You can define a type guard function isPerson that checks whether an object has 53 | the properties name and age: 54 | 55 | ```ts 56 | function isPerson(obj: any): obj is Person { 57 | return obj && typeof obj.name === 'string' && typeof obj.age === 'number' 58 | } 59 | ``` 60 | 61 | The obj is Person syntax tells TypeScript that the function is a type guard that 62 | changes the type of obj to Person. 63 | 64 | Now you can use the isPerson function to check whether an object is a Person: 65 | 66 | ```ts 67 | let data: any = { name: 'Paul', age: 30 } 68 | 69 | if (isPerson(data)) { 70 | console.log(data.name) // "Paul" 71 | } 72 | ``` 73 | 74 | ## Using type guards 75 | 76 | Type guards are most commonly used to handle data from external sources, such as 77 | user input or API responses. For example, let's say you have a function that 78 | fetches data from an API: 79 | 80 | ```ts 81 | async function getData() { 82 | const response = await fetch('https://example.com/api') 83 | const data = await response.json() 84 | return data 85 | } 86 | ``` 87 | 88 | The `fetchData` function returns an object of type any, which means that 89 | TypeScript doesn't know what properties the object has. You can use a type guard 90 | to check the type of the returned data and handle different cases accordingly: 91 | 92 | ```ts 93 | async function fetchData(): Promise { 94 | const response = await fetch('/api/data') 95 | const data = await response.json() 96 | 97 | if (isPerson(data)) { 98 | // handle Person data 99 | } else if (Array.isArray(data)) { 100 | // handle array data 101 | } else { 102 | // handle other data types 103 | } 104 | } 105 | ``` 106 | 107 | ## Best practices 108 | 109 | **Use type guards to make your code more expressive:** Type guards can help make 110 | your code more readable and expressive by making it clear what types of values 111 | your code is expecting. 112 | 113 | **Use user-defined type guards when working with custom types:** If you're 114 | working with custom types, it's a good idea to define your own type guards to 115 | help TypeScript narrow down the types of variables at runtime. 116 | 117 | **Use type guards with switch statements:** Type guards can be especially useful 118 | when used with switch statements. By checking the type of a value with a type 119 | guard in each case statement, you can avoid errors and make your code more 120 | reliable. 121 | 122 | **Avoid using type assertions:** Type assertions can be tempting, but they can 123 | be dangerous if used incorrectly. Instead, use type guards to help TypeScript 124 | infer the types of variables at runtime. 125 | 126 | **Be aware of performance implications:** Type guards can have performance 127 | implications, especially if they're used in performance-critical code. Make sure 128 | to test your code and optimize where necessary. 129 | 130 | ## Conclusion 131 | 132 | Overall, type guards are a powerful feature of TypeScript that can help make 133 | your code more reliable and expressive. By following these best practices, you 134 | can use type guards effectively and avoid common pitfalls. 135 | -------------------------------------------------------------------------------- /src/data/blog/vesuvius-challenge.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Vesuvius Challenge' 3 | description: 'What is the Vesuvius Challenge?' 4 | pubDate: 2025-02-09 5 | draft: false 6 | cover: '@assets/posts/vesuvius.png' 7 | coverAlt: 'cat in a server room' 8 | --- 9 | 10 | It's been a while, hasn't it? Life has a funny way of throwing curveballs – work 11 | deadlines, personal ups and downs, and the inevitable burnout. I stopped writing 12 | here for a while, feeling the time slip away. But lately, I've been feeling the 13 | itch to get back into it. 14 | 15 | Funny enough, what sparked this return was stumbling upon Luke Farrington's 16 | research on the Vesuvius Challenge. It completely captivated me! And, since I'm 17 | planning to dive into AI/ML fundamentals this year, it felt like the perfect 18 | intersection of history, technology, and personal goals to share with you all. 19 | So, let's dive in: the Vesuvius Challenge, and how it's being fueled by the 20 | power of open source and the collaborative spirit of programmers around the 21 | world. 22 | 23 | ## The Challenge: Reading the Unreadable 24 | 25 | The Vesuvius Challenge isn't your typical historical dig. These aren't perfectly 26 | preserved papyrus scrolls. These are scrolls carbonized by the eruption of Mount 27 | Vesuvius in 79 AD, making them incredibly fragile and impossible to physically 28 | unroll without destroying them. 29 | 30 | Think of it like this: imagine trying to read a burnt log that's cracked and 31 | crumbling. Impossible, right? That's where technology comes in. 32 | 33 | ## The Tech Behind the Magic: A Glimpse for "Normies" 34 | 35 | The challenge relies on two main technologies: 36 | 37 | - **3D Scanning:** Imagine a super-powered X-ray machine. Scientists use 38 | high-resolution X-ray micro-computed tomography (micro-CT) to scan the 39 | scrolls, creating incredibly detailed 3D images of their internal layers. It's 40 | like taking a digital cross-section of the scroll at every tiny point. 41 | 42 | - **Machine Learning:** This is where the real magic happens. We're using 43 | artificial intelligence (AI) to "train" computers to identify ink on the 44 | surfaces within those 3D scans. Think of it like teaching a computer to find 45 | tiny hidden clues within a mountain of data. We feed the AI examples of what 46 | ink looks like in the scans, and it learns to recognize similar patterns 47 | throughout the entire scroll. It's like teaching a dog to sniff out a specific 48 | scent. 49 | 50 | ## Open Source: The Secret Ingredient 51 | 52 | So, why is this an open-source story? Because the Vesuvius Challenge isn't just 53 | about the technology. It's about _who_ is building the technology and _how_ 54 | they're doing it. 55 | 56 | - **Collaboration is Key:** The challenge is structured around collaboration. 57 | Anyone with coding skills can jump in and contribute. The organizers provide 58 | the scanned data, and the community works together to develop the algorithms 59 | and tools needed to "unroll" the scrolls virtually. 60 | 61 | - **Openly Available Tools and Techniques:** The algorithms, code, and data used 62 | in the challenge are often released under open-source licenses. This means 63 | that anyone can use, modify, and share them. This allows for rapid innovation 64 | and avoids proprietary "black boxes" that hinder progress. \[_Insert example 65 | of an open-source library being used, e.g., TensorFlow, PyTorch, OpenCV_] 66 | 67 | - **Citizen Scientists Welcome:** You don't need to be a professional programmer 68 | to contribute! The community is incredibly welcoming and provides resources 69 | for beginners. This allows anyone with a passion for history and technology to 70 | participate in this groundbreaking project. \[_Insert a personal anecdote 71 | about seeing contributions from unexpected sources_] 72 | 73 | ## Why This Matters: Beyond the Scrolls 74 | 75 | The Vesuvius Challenge is more than just a fascinating tech demo. It has the 76 | potential to: 77 | 78 | - **Unlock Lost Knowledge:** Imagine the historical insights hidden within those 79 | scrolls! We could learn about philosophy, literature, and everyday life in 80 | ancient Rome. 81 | 82 | - **Advance AI and Imaging Technology:** The techniques developed for the 83 | Vesuvius Challenge could be applied to other fields, such as medical imaging, 84 | non-destructive testing, and even art restoration. 85 | 86 | - **Inspire a New Generation of Researchers:** By making the data and tools 87 | openly available, the challenge encourages more people to get involved in 88 | scientific research and discovery. 89 | 90 | ## Get Involved! 91 | 92 | The Vesuvius Challenge is a testament to the power of open source and the 93 | potential of collaborative problem-solving. 94 | 95 | - **Learn More:** Visit the Vesuvius Challenge 96 | [website](https://scrollprize.org/) to learn more about the project and how 97 | you can get involved. 98 | - **Explore Open Source:** If you're interested in learning more about open 99 | source, check out this 100 | [blog post](https://github.blog/open-source/new-to-open-source-heres-everything-you-need-to-get-started/). 101 | - **Contribute!** Even if you're just starting out, there are ways to 102 | contribute. Join the community forums, experiment with the data, and share 103 | your findings! 104 | 105 | The future of historical research is here, and it's open source. 106 | -------------------------------------------------------------------------------- /src/data/blog/wirths-law.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Wirth's Law" 3 | description: "Understanding Wirth's Law: A Developer's Perspective." 4 | pubDate: 2024-01-14 5 | draft: false 6 | cover: '@assets/posts/wirths-law-banner.png' 7 | coverAlt: "Wirth's law banner" 8 | --- 9 | 10 | Recently, while browsing the [potato.cheap](https://potato.cheap/) website, I 11 | stumbled upon an intriguing topic about Wirth's Law on the homepage. This piqued 12 | my curiosity and inspired me to delve deeper into the subject. 13 | 14 | Wirth's Law, named after the renowned Swiss computer scientist Niklaus Wirth, 15 | presents a somewhat paradoxical idea in today's tech-savvy world: software tends 16 | to slow down faster than hardware speeds up. At first glance, this seems at odds 17 | with the rapid advancements we often see in hardware technology. Yet, this 18 | phenomenon has significant implications for both developers and users alike. In 19 | this post, I aim to explore Wirth's Law in detail, shedding light on its impact 20 | and how it fundamentally influences our interaction with technology in the 21 | modern era. 22 | 23 | ## The Origin of Wirth's Law 24 | 25 | Niklaus Wirth, known for his contributions to programming language design, noted 26 | in 1995 that software efficiency was deteriorating at a pace that outstripped 27 | the speed improvements of hardware. This observation was based on his experience 28 | with software development, where he noticed that new software versions were 29 | often slower than their predecessors, despite running on faster hardware. 30 | 31 | ## Why Does It Occur? 32 | 33 | There are several factors contributing to Wirth's Law: 34 | 35 | **Feature Creep:** As software evolves, features are added to meet new 36 | requirements or consumer demands. While these features add functionality, they 37 | also consume more resources, leading to slower performance. 38 | 39 | **Software Abstraction:** Modern software development heavily relies on layers 40 | of abstraction to simplify programming. These abstractions, though beneficial 41 | for development speed and ease, often result in less efficient code. 42 | 43 | **Resource Availability:** With the advent of powerful hardware, there's a 44 | tendency to be less concerned about optimizing resource usage, leading to 45 | bloated software. 46 | 47 | **Complexity Management:** As software becomes more complex, the overhead 48 | required to manage this complexity can impact performance. 49 | 50 | ## Implications 51 | 52 | Wirth's Law has several implications: 53 | 54 | **User Experience:** Slower software can lead to frustration and decreased 55 | productivity, impacting the user experience. 56 | 57 | **Hardware Upgrades:** It can necessitate more frequent hardware upgrades, which 58 | has environmental and economic impacts. 59 | 60 | **Software Optimization:** It highlights the importance of software optimization 61 | and efficient coding practices. 62 | 63 | ## Combating Wirth's Law 64 | 65 | As developers, we can take steps to mitigate the effects of Wirth's Law: 66 | 67 | **Optimization:** Focus on optimizing code and resources, even in an era of 68 | abundant hardware capabilities. 69 | 70 | **Balancing Features:** Carefully consider the necessity of new features and 71 | their impact on performance. 72 | 73 | **Profiling and Testing:** Regularly profile and test software to identify 74 | performance bottlenecks. 75 | 76 | **User Feedback:** Listen to user feedback regarding software performance and 77 | usability. 78 | 79 | **Continuous Learning:** Stay updated with efficient coding practices and 80 | emerging technologies that can help optimize software. 81 | 82 | ## Examples of Wirth's Law in Web Development 83 | 84 | Wirth's Law, which states that software is getting slower more rapidly than 85 | hardware is becoming faster, has significant implications for web app 86 | development. Here are some ways in which Wirth's Law affects web app 87 | development: 88 | 89 | - **Resource-Intensive Features:** Web apps that incorporate resource-intensive 90 | features, such as real-time data processing or complex animations, can lead to 91 | decreased performance on devices with limited resources. This can result in a 92 | poor user experience and decreased productivity, highlighting the importance 93 | of efficient software optimization. 94 | 95 | - **Software Abstraction in Web Development:** Web development often relies on 96 | software abstractions to cater to diverse hardware configurations. However, 97 | these abstractions can contribute to software bloat and reduced efficiency, in 98 | line with the observations of Wirth's Law. 99 | 100 | - **User Experience Impact:** The user experience of web apps can be 101 | significantly affected by Wirth's Law. As software becomes slower relative to 102 | hardware advancements, users may perceive a decline in app performance, 103 | especially when using the latest apps on older devices, highlighting the 104 | real-world implications of this phenomenon. 105 | 106 | ## Conclusion 107 | 108 | Wirth's Law reminds us that software development is not just about adding 109 | features or relying on hardware improvements. It's a delicate balance between 110 | functionality, efficiency, and user experience. As developers, we play a crucial 111 | role in ensuring that our software makes the best use of the available hardware, 112 | thus delivering a seamless and efficient experience to the user. 113 | 114 | Remember, in the world of software development, efficiency is not just an 115 | option; it's a necessity. Let's embrace the challenges posed by Wirth's Law and 116 | strive to create software that's not only feature-rich but also 117 | performance-oriented. 118 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | interface ImportMetaEnv { 5 | readonly SPOTIFY_REFRESH_TOKEN: string 6 | readonly SPOTIFY_CLIENT_ID: string 7 | readonly SPOTIFY_CLIENT_SECRET: string 8 | } 9 | 10 | interface ImportMeta { 11 | readonly env: ImportMetaEnv 12 | } 13 | -------------------------------------------------------------------------------- /src/layouts/BlogPostLayout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Icon } from 'astro-icon/components' 3 | import { getCollection } from 'astro:content' 4 | import Navbar from '@components/Navbar.astro' 5 | import Footer from '@components/Footer.astro' 6 | import HeadTags from '@components/HeadTags.astro' 7 | import { Head } from 'astro-capo' 8 | import '../styles/posts.css' 9 | import type { ImageMetadata } from 'astro' 10 | import { Image } from 'astro:assets' 11 | 12 | interface BlogPostLayoutProps { 13 | frontmatter: { 14 | title: string 15 | description: string 16 | pubDate: string 17 | cover: ImageMetadata 18 | coverAlt: string 19 | } 20 | minutes: { 21 | minutesRead: string 22 | } 23 | } 24 | 25 | let { frontmatter, minutes } = Astro.props as BlogPostLayoutProps 26 | 27 | const formattedDate = new Date(frontmatter.pubDate).toLocaleDateString( 28 | 'en-us', 29 | { 30 | year: 'numeric', 31 | month: 'short', 32 | day: 'numeric', 33 | }, 34 | ) 35 | 36 | // get all posts 37 | let posts = await getCollection('posts') 38 | .then((posts) => 39 | posts.sort( 40 | (a, b) => 41 | new Date(b.data.pubDate).valueOf() - new Date(a.data.pubDate).valueOf(), 42 | ), 43 | ) 44 | .then((posts) => posts.filter((post) => !post.data.draft)) 45 | 46 | // get the index of the current post 47 | const index = posts.findIndex((post) => { 48 | return Astro.request.url.includes(post.slug ?? '') 49 | }) 50 | 51 | // get the next and previous posts 52 | const nextPost = posts[index + 1] 53 | const prevPost = posts[index - 1] 54 | --- 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 |

    {frontmatter.title}

    65 |
    66 |

    67 | 71 | {formattedDate} 72 |

    73 |

    74 | 78 | {minutes.minutesRead} 79 |

    80 |
    81 |
    82 | {frontmatter.coverAlt} 87 |
    88 |
    89 | 90 |
    91 |
    92 |
    93 |
    94 | { 95 | prevPost && ( 96 | 100 |

    ← Previous post

    101 |

    {prevPost.data.title}

    102 |
    103 | ) 104 | } 105 | { 106 | nextPost && ( 107 | 111 |

    Next post →

    112 |

    {nextPost.data.title}

    113 |
    114 | ) 115 | } 116 |
    117 |
    118 |
    119 |