diff --git a/.changes/v2.0.0.md b/.changes/v2.0.0.md new file mode 100644 index 00000000..b4f13025 --- /dev/null +++ b/.changes/v2.0.0.md @@ -0,0 +1,13 @@ +## v2.0.0 (2023-02-26) + +### Features + +- TailwindCSS for UI. +- Multiple themes. +- SEO. +- Analytics: Google Analytics, Swetrix Analytics, Counter Analytics. +- Sitemap. +- RSS. +- Posts by tags, difficulty. + +Full Changelog: [v2.0.0](https://github.com/ansidev/astro-basic-template/commits/v2.0.0) diff --git a/.github/workflows/deploy_to_netlify.yaml b/.github/workflows/deploy_to_netlify.yaml index 7f825de0..632d45ba 100644 --- a/.github/workflows/deploy_to_netlify.yaml +++ b/.github/workflows/deploy_to_netlify.yaml @@ -172,7 +172,7 @@ jobs: steps: - name: Delete the PR branch after merged into develop env: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} GIT_REF_PATH: /repos/${{ github.repository }}/git/refs/heads/${{ github.head_ref }} run: | STATUS_CODE=$(gh api -i -H "Accept: application/vnd.github+json" $GIT_REF_PATH | head -1) diff --git a/.gitignore b/.gitignore index 08879b96..2eac41de 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ pnpm-debug.log* # VSCode Workspace astro-basic-template.code-workspace + +# Astro directories +.astro diff --git a/.taskfiles/task_site.yaml b/.taskfiles/task_site.yaml index ef3f9240..882d708b 100644 --- a/.taskfiles/task_site.yaml +++ b/.taskfiles/task_site.yaml @@ -23,6 +23,6 @@ tasks: clean: desc: Clean the build output cmds: - - rm -r {{.OUTPUT_DIR}} + - rm -r ./{{.OUTPUT_DIR}} silent: true ignore_error: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..7e9a7b9b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org). + +## v2.0.0 (2023-02-26) + +### Features + +- TailwindCSS for UI. +- Multiple themes. +- SEO. +- Analytics: Google Analytics, Swetrix Analytics, Counter Analytics. +- Sitemap. +- RSS. +- Posts by tags, difficulty. + +Full Changelog: [v2.0.0](https://github.com/ansidev/astro-basic-template/commits/v2.0.0) diff --git a/README.md b/README.md index ac7d8148..ddfe7cac 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # LeetCode Blog [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) +[![Netlify Status](https://api.netlify.com/api/v1/badges/278e1add-72b0-4ffe-b932-03b27a72c8b5/deploy-status)](https://app.netlify.com/sites/leetcode-blog/deploys) Solutions for LeetCode problems - Written by [ansidev](https://github.com/ansidev). diff --git a/astro.config.mjs b/astro.config.mjs index 09d43efd..b28656cd 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,3 +1,6 @@ +import partytown from '@astrojs/partytown' +import sitemap from '@astrojs/sitemap' +import tailwind from '@astrojs/tailwind' import { defineConfig } from 'astro/config' import compress from 'astro-compress' import purgecss from 'astro-purgecss' @@ -17,6 +20,13 @@ if (baseURL.length === 0) { export default defineConfig({ site: baseURL, integrations: [ + tailwind(), + partytown({ + config: { + forward: ['dataLayer.push'], + }, + }), + sitemap(), purgecss(), compress(), ] diff --git a/package.json b/package.json index e69c2ef6..f5c8fd38 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "leetcode-blog", "description": "Solutions for LeetCode problems - Written by ansidev", "type": "module", - "version": "1.0.1", + "version": "2.0.0", "license": "MIT", "scripts": { "dev": "astro dev", @@ -15,14 +15,23 @@ "lint": "eslint --ext .cjs,.mjs,.ts,.astro --ignore-path .gitignore ." }, "dependencies": { - "astro": "^2.0.14", - "astro-compress": "^1.1.33", - "astro-purgecss": "^2.0.0" + "@astrojs/partytown": "^1.0.3", + "@astrojs/rss": "^2.1.1", + "@astrojs/sitemap": "^1.1.0", + "@astrojs/tailwind": "^3.0.1", + "astro": "^2.0.15", + "astro-compress": "^1.1.34", + "astro-purgecss": "^2.0.0", + "tailwindcss": "^3.2.7" }, "devDependencies": { "@commitlint/cli": "^17.4.4", "@commitlint/config-conventional": "^17.4.4", - "@types/node": "^18.14.0", + "@iconify-json/bi": "^1.1.15", + "@tailwindcss/typography": "^0.5.9", + "@types/lodash.get": "^4.4.7", + "@types/lodash.kebabcase": "^4.1.7", + "@types/node": "^18.14.1", "@typescript-eslint/eslint-plugin": "^5.53.0", "@typescript-eslint/parser": "^5.53.0", "commitizen": "^4.3.0", @@ -31,6 +40,10 @@ "eslint-plugin-astro": "^0.23.0", "eslint-plugin-simple-import-sort": "^10.0.0", "husky": "^8.0.3", + "lodash.get": "^4.4.2", + "lodash.kebabcase": "^4.1.1", + "sass": "^1.58.3", + "tailwindcss-themer": "^3.0.1", "typescript": "^4.9.5" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1aa31af6..e9054bf9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,13 +1,21 @@ lockfileVersion: 5.4 specifiers: + '@astrojs/partytown': ^1.0.3 + '@astrojs/rss': ^2.1.1 + '@astrojs/sitemap': ^1.1.0 + '@astrojs/tailwind': ^3.0.1 '@commitlint/cli': ^17.4.4 '@commitlint/config-conventional': ^17.4.4 - '@types/node': ^18.14.0 + '@iconify-json/bi': ^1.1.15 + '@tailwindcss/typography': ^0.5.9 + '@types/lodash.get': ^4.4.7 + '@types/lodash.kebabcase': ^4.1.7 + '@types/node': ^18.14.1 '@typescript-eslint/eslint-plugin': ^5.53.0 '@typescript-eslint/parser': ^5.53.0 - astro: ^2.0.14 - astro-compress: ^1.1.33 + astro: ^2.0.15 + astro-compress: ^1.1.34 astro-purgecss: ^2.0.0 commitizen: ^4.3.0 dotenv: ^16.0.3 @@ -15,17 +23,31 @@ specifiers: eslint-plugin-astro: ^0.23.0 eslint-plugin-simple-import-sort: ^10.0.0 husky: ^8.0.3 + lodash.get: ^4.4.2 + lodash.kebabcase: ^4.1.1 + sass: ^1.58.3 + tailwindcss: ^3.2.7 + tailwindcss-themer: ^3.0.1 typescript: ^4.9.5 dependencies: - astro: 2.0.14_@types+node@18.14.0 - astro-compress: 1.1.33 - astro-purgecss: 2.0.0_astro@2.0.14 + '@astrojs/partytown': 1.0.3 + '@astrojs/rss': 2.1.1 + '@astrojs/sitemap': 1.1.0 + '@astrojs/tailwind': 3.0.1_enl5tymeuxlslzmmdrwlr7zkvy + astro: 2.0.15_435aevtanapkguv7m72cl6trbi + astro-compress: 1.1.34 + astro-purgecss: 2.0.0_astro@2.0.15 + tailwindcss: 3.2.7 devDependencies: '@commitlint/cli': 17.4.4 '@commitlint/config-conventional': 17.4.4 - '@types/node': 18.14.0 + '@iconify-json/bi': 1.1.15 + '@tailwindcss/typography': 0.5.9_tailwindcss@3.2.7 + '@types/lodash.get': 4.4.7 + '@types/lodash.kebabcase': 4.1.7 + '@types/node': 18.14.1 '@typescript-eslint/eslint-plugin': 5.53.0_ny4s7qc6yg74faf3d6xty2ofzy '@typescript-eslint/parser': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm commitizen: 4.3.0 @@ -34,6 +56,10 @@ devDependencies: eslint-plugin-astro: 0.23.0_eslint@8.34.0 eslint-plugin-simple-import-sort: 10.0.0_eslint@8.34.0 husky: 8.0.3 + lodash.get: 4.4.2 + lodash.kebabcase: 4.1.1 + sass: 1.58.3 + tailwindcss-themer: 3.0.1_tailwindcss@3.2.7 typescript: 4.9.5 packages: @@ -71,13 +97,13 @@ packages: vscode-uri: 3.0.7 dev: false - /@astrojs/markdown-remark/2.0.1_astro@2.0.14: + /@astrojs/markdown-remark/2.0.1_astro@2.0.15: resolution: {integrity: sha512-xQF1rXGJN18m+zZucwRRtmNehuhPMMhZhi6HWKrtpEAKnHSPk8lqf1GXgKH7/Sypglu8ivdECZ+EGs6kOYVasQ==} peerDependencies: astro: ^2.0.2 dependencies: '@astrojs/prism': 2.0.0 - astro: 2.0.14_@types+node@18.14.0 + astro: 2.0.15_435aevtanapkguv7m72cl6trbi github-slugger: 1.5.0 import-meta-resolve: 2.2.0 rehype-raw: 6.1.1 @@ -94,6 +120,13 @@ packages: - supports-color dev: false + /@astrojs/partytown/1.0.3: + resolution: {integrity: sha512-u2/pG9owRipICz/PksLUUqUat98sm0/YODvv3/IKKJ0JCwF7+0olbMvyyvoj7kJooIXh0XowcOjEs5vhCYFo5g==} + dependencies: + '@builder.io/partytown': 0.7.5 + mrmime: 1.0.1 + dev: false + /@astrojs/prism/2.0.0: resolution: {integrity: sha512-YgeoeEPqsxaEpg0rwe/bUq3653LqSQnMjrLlpYwrbQQMQQqz6Y5yXN+RX3SfLJ6ppNb4+Fu2+Z49EXjk48Ihjw==} engines: {node: '>=16.12.0'} @@ -101,6 +134,36 @@ packages: prismjs: 1.29.0 dev: false + /@astrojs/rss/2.1.1: + resolution: {integrity: sha512-3BpCqyCmVWfrGqyc1YXUQf7O3QjORYNmvxzeKvp0E+7KGiKj8ckRzwj90xGYjbKcb/BwKfzYgek4+BrZ1aqQtA==} + dependencies: + fast-xml-parser: 4.1.2 + kleur: 4.1.5 + dev: false + + /@astrojs/sitemap/1.1.0: + resolution: {integrity: sha512-JnKZcbD0WdFxw2VAj3qXb0cYKuvk6nwBYPfUy+plSa9TC9hikQrDFpd7yapOPTRf3AdmUittzFckoTw/nUilCw==} + dependencies: + sitemap: 7.1.1 + zod: 3.20.2 + dev: false + + /@astrojs/tailwind/3.0.1_enl5tymeuxlslzmmdrwlr7zkvy: + resolution: {integrity: sha512-QSYh/xmz454j1yZU9rjw2J24PpH7j3h2ClesqMaAniOtcuL8RfP7KYCnCrk01xvjwqqO+QBpZNDD/SUhHNtFFg==} + peerDependencies: + astro: ^2.0.4 + tailwindcss: ^3.0.24 + dependencies: + '@proload/core': 0.3.3 + astro: 2.0.15_435aevtanapkguv7m72cl6trbi + autoprefixer: 10.4.13_postcss@8.4.21 + postcss: 8.4.21 + postcss-load-config: 4.0.1_postcss@8.4.21 + tailwindcss: 3.2.7 + transitivePeerDependencies: + - ts-node + dev: false + /@astrojs/telemetry/2.0.0: resolution: {integrity: sha512-RnWojVMIsql3GGWDP5pNWmhmBQVkCpxGNZ8yPr2cbmUqsUYGSvErhqfkLfro9j2/STi5UDmSpNgjPkQmXpgnKw==} engines: {node: '>=16.12.0'} @@ -350,6 +413,11 @@ packages: to-fast-properties: 2.0.0 dev: false + /@builder.io/partytown/0.7.5: + resolution: {integrity: sha512-Zbr2Eo0AQ4yzmQr/36/h+6LKjmdVBB3Q5cGzO6rtlIKB/IOpbQVUZW+XAnhpJmJr9sIF97OZjgbhG9k7Sjn4yw==} + hasBin: true + dev: false + /@commitlint/cli/17.4.4: resolution: {integrity: sha512-HwKlD7CPVMVGTAeFZylVNy14Vm5POVY0WxPkZr7EXLC/os0LH/obs6z4HRvJtH/nHCMYBvUBQhGwnufKfTjd5g==} engines: {node: '>=v14'} @@ -436,15 +504,15 @@ packages: '@commitlint/execute-rule': 17.4.0 '@commitlint/resolve-extends': 17.4.4 '@commitlint/types': 17.4.4 - '@types/node': 18.14.0 + '@types/node': 18.14.1 chalk: 4.1.2 cosmiconfig: 8.0.0 - cosmiconfig-typescript-loader: 4.3.0_ipkhww4xc5z2tt2x53vp2mt2be + cosmiconfig-typescript-loader: 4.3.0_s4dpre5ezutgdzsn47klmddvia lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 resolve-from: 5.0.0 - ts-node: 10.9.1_tncu2ai53lzgmizdedur7lbibe + ts-node: 10.9.1_uayvamxqnl5yeiojjysxwopmsy typescript: 4.9.5 transitivePeerDependencies: - '@swc/core' @@ -776,6 +844,16 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true + /@iconify-json/bi/1.1.15: + resolution: {integrity: sha512-NsbDeTVIYwgPLyAT0UgzuaCl79V65Z+M319BjbdEwdnbDGuaYgmAMBJUIWrnBEVoEg7rtouAuvaZRb3nIYIRcA==} + dependencies: + '@iconify/types': 2.0.0 + dev: true + + /@iconify/types/2.0.0: + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + dev: true + /@jridgewell/gen-mapping/0.1.1: resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} engines: {node: '>=6.0.0'} @@ -859,6 +937,25 @@ packages: tiny-glob: 0.2.9 tslib: 2.4.1 + /@proload/core/0.3.3: + resolution: {integrity: sha512-7dAFWsIK84C90AMl24+N/ProHKm4iw0akcnoKjRvbfHifJZBLhaDsDus1QJmhG12lXj4e/uB/8mB/0aduCW+NQ==} + dependencies: + deepmerge: 4.3.0 + escalade: 3.1.1 + dev: false + + /@tailwindcss/typography/0.5.9_tailwindcss@3.2.7: + resolution: {integrity: sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.2.7 + dev: true + /@trysound/sax/0.2.0: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} @@ -943,6 +1040,22 @@ packages: resolution: {integrity: sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==} dev: false + /@types/lodash.get/4.4.7: + resolution: {integrity: sha512-af34Mj+KdDeuzsJBxc/XeTtOx0SZHZNLd+hdrn+PcKGQs0EG2TJTzQAOTCZTgDJCArahlCzLWSy8c2w59JRz7Q==} + dependencies: + '@types/lodash': 4.14.191 + dev: true + + /@types/lodash.kebabcase/4.1.7: + resolution: {integrity: sha512-qzrcpK5uiADZ9OyZaegalM0b9Y3WetoBQ04RAtP3xZFGC5ul1UxmbjZ3j6suCh0BDkvgQmoMh8t5e9cVrdJYMw==} + dependencies: + '@types/lodash': 4.14.191 + dev: true + + /@types/lodash/4.14.191: + resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==} + dev: true + /@types/mdast/3.0.10: resolution: {integrity: sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==} dependencies: @@ -963,8 +1076,12 @@ packages: '@types/unist': 2.0.6 dev: false - /@types/node/18.14.0: - resolution: {integrity: sha512-5EWrvLmglK+imbCJY0+INViFWUHg1AHel1sq4ZVSfdcNqGy9Edv3UB9IIzzg+xPaUcAgZYcfVs2fBcwDeZzU0A==} + /@types/node/17.0.45: + resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + dev: false + + /@types/node/18.14.1: + resolution: {integrity: sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ==} /@types/normalize-package-data/2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -978,6 +1095,12 @@ packages: resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} dev: false + /@types/sax/1.2.4: + resolution: {integrity: sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==} + dependencies: + '@types/node': 18.14.1 + dev: false + /@types/semver/7.3.13: resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} dev: true @@ -985,7 +1108,7 @@ packages: /@types/sharp/0.31.1: resolution: {integrity: sha512-5nWwamN9ZFHXaYEincMSuza8nNfOof8nmO+mcI+Agx1uMUk4/pQnNIcix+9rLPXzKrm1pS34+6WRDbDV0Jn7ag==} dependencies: - '@types/node': 18.14.0 + '@types/node': 18.14.1 dev: false /@types/unist/2.0.6: @@ -1161,11 +1284,27 @@ packages: acorn: 8.8.1 dev: true + /acorn-node/1.8.2: + resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==} + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + xtend: 4.0.2 + + /acorn-walk/7.2.0: + resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} + engines: {node: '>=0.4.0'} + /acorn-walk/8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} dev: true + /acorn/7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + /acorn/8.8.1: resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} engines: {node: '>=0.4.0'} @@ -1228,10 +1367,20 @@ packages: engines: {node: '>=12'} dev: false + /anymatch/3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + /arg/4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true + /arg/5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + /argparse/1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: @@ -1260,18 +1409,18 @@ packages: engines: {node: '>=0.10.0'} dev: true - /astro-compress/1.1.33: - resolution: {integrity: sha512-i9LuEzQAeYPs9Vn+FbN9bdPDa/g2PYddT1GOkoUacFpAZCl9qIYFFvacULjeZIMMKhrRkPZ5f9lZVC3RJP7Qpg==} + /astro-compress/1.1.34: + resolution: {integrity: sha512-To7O9z+vhllvP/qSWszJBjVYZgf8g3omkUq8IOroPjElqur1W+bPTq01RNO/aLp9bQ6YqBpwa2j+cSwa/j3FxQ==} dependencies: '@types/csso': 5.0.0 '@types/html-minifier-terser': 7.0.0 '@types/sharp': 0.31.1 csso: 5.0.5 - files-pipeline: 0.0.3 + files-pipeline: 0.0.4 html-minifier-terser: 7.1.0 sharp: 0.31.3 svgo: 3.0.2 - terser: 5.16.3 + terser: 5.16.5 dev: false /astro-eslint-parser/0.11.0: @@ -1289,23 +1438,23 @@ packages: - supports-color dev: true - /astro-purgecss/2.0.0_astro@2.0.14: + /astro-purgecss/2.0.0_astro@2.0.15: resolution: {integrity: sha512-xt8Wvhkgp3Q3qgwGxtNPZ+KmnO29Rs7K2g2e4imIaWkNnXUMoCG7S0n8/m2ZwXRF9snI5h1DV8N80zZHj7Jx3A==} peerDependencies: astro: ^2.0.0 dependencies: - astro: 2.0.14_@types+node@18.14.0 + astro: 2.0.15_435aevtanapkguv7m72cl6trbi purgecss: 5.0.0 dev: false - /astro/2.0.14_@types+node@18.14.0: - resolution: {integrity: sha512-BiXnHyK3rj5Uz45V5p9jRi0xtJc/zxhCxnXYAekHHF1bVvvoa3aXMwl0GZ3Bc0mxP6vPLmbRcjNKdqfyZn1B3Q==} + /astro/2.0.15_435aevtanapkguv7m72cl6trbi: + resolution: {integrity: sha512-CUJNrcdgcSjASValRNEesTgXSefgQ8Z9GHrZXckmTivatoBlVkYb8G8tmk7dgRt6JF+bwfOsA6cGdv7+G7hCbA==} engines: {node: '>=16.12.0', npm: '>=6.14.0'} hasBin: true dependencies: '@astrojs/compiler': 1.1.0 '@astrojs/language-server': 0.28.3 - '@astrojs/markdown-remark': 2.0.1_astro@2.0.14 + '@astrojs/markdown-remark': 2.0.1_astro@2.0.15 '@astrojs/telemetry': 2.0.0 '@astrojs/webapi': 2.0.1 '@babel/core': 7.20.7 @@ -1351,7 +1500,7 @@ packages: typescript: 4.9.5 unist-util-visit: 4.1.1 vfile: 5.3.6 - vite: 4.1.2_@types+node@18.14.0 + vite: 4.1.2_435aevtanapkguv7m72cl6trbi vitefu: 0.2.4_vite@4.1.2 yargs-parser: 21.1.1 zod: 3.20.2 @@ -1380,6 +1529,22 @@ packages: engines: {node: '>= 4.0.0'} dev: true + /autoprefixer/10.4.13_postcss@8.4.21: + resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.4 + caniuse-lite: 1.0.30001441 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: false + /bail/2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} dev: false @@ -1390,6 +1555,10 @@ packages: /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + /binary-extensions/2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + /bl/4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: @@ -1494,6 +1663,10 @@ packages: tslib: 2.4.1 dev: false + /camelcase-css/2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + /camelcase-keys/6.2.2: resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} engines: {node: '>=8'} @@ -1557,6 +1730,20 @@ packages: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true + /chokidar/3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + /chownr/1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} dev: false @@ -1636,7 +1823,6 @@ packages: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - dev: false /color/4.2.3: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} @@ -1644,7 +1830,6 @@ packages: dependencies: color-convert: 2.0.1 color-string: 1.9.1 - dev: false /comma-separated-tokens/2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -1746,7 +1931,7 @@ packages: engines: {node: '>= 0.6'} dev: false - /cosmiconfig-typescript-loader/4.3.0_ipkhww4xc5z2tt2x53vp2mt2be: + /cosmiconfig-typescript-loader/4.3.0_s4dpre5ezutgdzsn47klmddvia: resolution: {integrity: sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==} engines: {node: '>=12', npm: '>=6'} peerDependencies: @@ -1755,9 +1940,9 @@ packages: ts-node: '>=10' typescript: '>=3' dependencies: - '@types/node': 18.14.0 + '@types/node': 18.14.1 cosmiconfig: 8.0.0 - ts-node: 10.9.1_tncu2ai53lzgmizdedur7lbibe + ts-node: 10.9.1_uayvamxqnl5yeiojjysxwopmsy typescript: 4.9.5 dev: true @@ -1903,6 +2088,16 @@ packages: engines: {node: '>=12.4.0'} dev: false + /deepmerge-ts/4.3.0: + resolution: {integrity: sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw==} + engines: {node: '>=12.4.0'} + dev: false + + /deepmerge/4.3.0: + resolution: {integrity: sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==} + engines: {node: '>=0.10.0'} + dev: false + /defaults/1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: @@ -1912,6 +2107,9 @@ packages: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} + /defined/1.0.1: + resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==} + /dequal/2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1932,10 +2130,22 @@ packages: engines: {node: '>=8'} dev: false + /detective/5.2.1: + resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==} + engines: {node: '>=0.8.0'} + hasBin: true + dependencies: + acorn-node: 1.8.2 + defined: 1.0.1 + minimist: 1.2.7 + /devalue/4.2.0: resolution: {integrity: sha512-mbjoAaCL2qogBKgeFxFPOXAUsZchircF+B/79LD4sHH0+NHfYm8gZpQrskKDn5gENGt35+5OI1GUF7hLVnkPDw==} dev: false + /didyoumean/1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + /diff/4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -1955,7 +2165,6 @@ packages: /dlv/1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - dev: false /doctrine/3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} @@ -2352,6 +2561,13 @@ packages: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true + /fast-xml-parser/4.1.2: + resolution: {integrity: sha512-CDYeykkle1LiA/uqQyNwYpFbyF6Axec6YapmpUP+/RHWIoR1zKjocdvNaTsxCxZzQ6v9MLXaSYm9Qq0thv0DHg==} + hasBin: true + dependencies: + strnum: 1.0.5 + dev: false + /fastq/1.14.0: resolution: {integrity: sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==} dependencies: @@ -2371,10 +2587,10 @@ packages: flat-cache: 3.0.4 dev: true - /files-pipeline/0.0.3: - resolution: {integrity: sha512-dtHt96Rs0qsNBnIlPuHUP8vd5xPRGbMYgywMfA2Qy83QPz8feoWsoXSuxxLsWYv4vag+vAhNZ4YOHL0pdyGKFA==} + /files-pipeline/0.0.4: + resolution: {integrity: sha512-eHbXa8YGUui/qGnxES+SwwQVpQpi4xz2VR1VrJz1HFflnRWo8+zCQCseRqDAzMinH1B6iI9ckVWpFgoWnTH9nA==} dependencies: - deepmerge-ts: 4.2.2 + deepmerge-ts: 4.3.0 fast-glob: 3.2.12 dev: false @@ -2438,6 +2654,10 @@ packages: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true + /fraction.js/4.2.0: + resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + dev: false + /fs-constants/1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} dev: false @@ -2469,7 +2689,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: false optional: true /function-bind/1.1.1: @@ -2524,7 +2743,6 @@ packages: engines: {node: '>=10.13.0'} dependencies: is-glob: 4.0.3 - dev: true /glob/7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} @@ -2774,7 +2992,7 @@ packages: entities: 4.4.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.16.3 + terser: 5.16.5 dev: false /html-void-elements/2.0.1: @@ -2812,6 +3030,9 @@ packages: engines: {node: '>= 4'} dev: true + /immutable/4.2.4: + resolution: {integrity: sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==} + /import-fresh/3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -2877,7 +3098,12 @@ packages: /is-arrayish/0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: false + + /is-binary-path/2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 /is-buffer/2.0.5: resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} @@ -3069,6 +3295,10 @@ packages: engines: {'0': node >= 0.2.0} dev: true + /just-unique/4.2.0: + resolution: {integrity: sha512-cxQGGUiit6CGUpuuiezY8N4m1wgF4o7127rXEXDFcxeDUFfdV7gSkwA26Fe2wWBiNQq2SZOgN4gSmMxB/StA8Q==} + dev: true + /kind-of/6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -3091,6 +3321,10 @@ packages: type-check: 0.4.0 dev: true + /lilconfig/2.0.6: + resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} + engines: {node: '>=10'} + /lines-and-columns/1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true @@ -3121,6 +3355,14 @@ packages: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} dev: true + /lodash.castarray/4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + dev: true + + /lodash.get/4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + dev: true + /lodash.isfunction/3.0.9: resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} dev: true @@ -3705,6 +3947,11 @@ packages: engines: {node: '>=4'} dev: false + /mrmime/1.0.1: + resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + engines: {node: '>=10'} + dev: false + /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -3776,6 +4023,15 @@ packages: validate-npm-package-license: 3.0.4 dev: true + /normalize-path/3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + /normalize-range/0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: false + /npm-run-path/4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -3796,6 +4052,10 @@ packages: boolbase: 1.0.0 dev: false + /object-hash/3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + /once/1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -3982,6 +4242,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /pify/2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + /pify/4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -3994,6 +4258,76 @@ packages: find-up: 4.1.0 dev: false + /postcss-import/14.1.0_postcss@8.4.21: + resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.1 + + /postcss-js/4.0.1_postcss@8.4.21: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.21 + + /postcss-load-config/3.1.4_postcss@8.4.21: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.0.6 + postcss: 8.4.21 + yaml: 1.10.2 + + /postcss-load-config/4.0.1_postcss@8.4.21: + resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.0.6 + postcss: 8.4.21 + yaml: 2.2.1 + dev: false + + /postcss-nested/6.0.0_postcss@8.4.21: + resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + + /postcss-selector-parser/6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + /postcss-selector-parser/6.0.11: resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==} engines: {node: '>=4'} @@ -4001,6 +4335,9 @@ packages: cssesc: 3.0.0 util-deprecate: 1.0.2 + /postcss-value-parser/4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + /postcss/8.4.20: resolution: {integrity: sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==} engines: {node: ^10 || ^12 || >=14} @@ -4016,7 +4353,6 @@ packages: nanoid: 3.3.4 picocolors: 1.0.0 source-map-js: 1.0.2 - dev: false /prebuild-install/7.1.1: resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} @@ -4120,6 +4456,10 @@ packages: engines: {node: '>=8'} dev: true + /quick-lru/5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + /rc/1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -4130,6 +4470,11 @@ packages: strip-json-comments: 2.0.1 dev: false + /read-cache/1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + /read-pkg-up/7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -4157,6 +4502,12 @@ packages: string_decoder: 1.3.0 util-deprecate: 1.0.2 + /readdirp/3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + /redent/3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -4401,6 +4752,19 @@ packages: suf-log: 2.5.3 dev: false + /sass/1.58.3: + resolution: {integrity: sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==} + engines: {node: '>=12.0.0'} + hasBin: true + dependencies: + chokidar: 3.5.3 + immutable: 4.2.4 + source-map-js: 1.0.2 + + /sax/1.2.4: + resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + dev: false + /section-matter/1.0.0: resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} engines: {node: '>=4'} @@ -4482,12 +4846,22 @@ packages: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} dependencies: is-arrayish: 0.3.2 - dev: false /sisteransi/1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: false + /sitemap/7.1.1: + resolution: {integrity: sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==} + engines: {node: '>=12.0.0', npm: '>=5.6.0'} + hasBin: true + dependencies: + '@types/node': 17.0.45 + '@types/sax': 1.2.4 + arg: 5.0.2 + sax: 1.2.4 + dev: false + /slash/3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -4643,6 +5017,10 @@ packages: engines: {node: '>=8'} dev: true + /strnum/1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + dev: false + /style-to-object/0.3.0: resolution: {integrity: sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==} dependencies: @@ -4697,6 +5075,49 @@ packages: '@pkgr/utils': 2.3.1 tslib: 2.4.1 + /tailwindcss-themer/3.0.1_tailwindcss@3.2.7: + resolution: {integrity: sha512-fYbrcm9/WrLepfFg51PcbfQuEBv0jFeMYlU/emCuvTt6NOvzks9Dlp2Ji+EqM6K7KHA6KpZ2NJWbZLmewpHNwA==} + peerDependencies: + tailwindcss: ^3.1.0 + dependencies: + color: 4.2.3 + just-unique: 4.2.0 + lodash.merge: 4.6.2 + lodash.mergewith: 4.6.2 + tailwindcss: 3.2.7 + dev: true + + /tailwindcss/3.2.7: + resolution: {integrity: sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==} + engines: {node: '>=12.13.0'} + hasBin: true + dependencies: + arg: 5.0.2 + chokidar: 3.5.3 + color-name: 1.1.4 + detective: 5.2.1 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.2.12 + glob-parent: 6.0.2 + is-glob: 4.0.3 + lilconfig: 2.0.6 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.21 + postcss-import: 14.1.0_postcss@8.4.21 + postcss-js: 4.0.1_postcss@8.4.21 + postcss-load-config: 3.1.4_postcss@8.4.21 + postcss-nested: 6.0.0_postcss@8.4.21 + postcss-selector-parser: 6.0.11 + postcss-value-parser: 4.2.0 + quick-lru: 5.1.1 + resolve: 1.22.1 + transitivePeerDependencies: + - ts-node + /tar-fs/2.1.1: resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} dependencies: @@ -4717,8 +5138,8 @@ packages: readable-stream: 3.6.0 dev: false - /terser/5.16.3: - resolution: {integrity: sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q==} + /terser/5.16.5: + resolution: {integrity: sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==} engines: {node: '>=10'} hasBin: true dependencies: @@ -4784,7 +5205,7 @@ packages: resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==} dev: false - /ts-node/10.9.1_tncu2ai53lzgmizdedur7lbibe: + /ts-node/10.9.1_uayvamxqnl5yeiojjysxwopmsy: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -4803,7 +5224,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 - '@types/node': 18.14.0 + '@types/node': 18.14.1 acorn: 8.8.1 acorn-walk: 8.2.0 arg: 4.1.3 @@ -5043,7 +5464,7 @@ packages: vfile-message: 3.1.3 dev: false - /vite/4.1.2_@types+node@18.14.0: + /vite/4.1.2_435aevtanapkguv7m72cl6trbi: resolution: {integrity: sha512-MWDb9Rfy3DI8omDQySbMK93nQqStwbsQWejXRY2EBzEWKmLAXWb1mkI9Yw2IJrc+oCvPCI1Os5xSSIBYY6DEAw==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -5068,11 +5489,12 @@ packages: terser: optional: true dependencies: - '@types/node': 18.14.0 + '@types/node': 18.14.1 esbuild: 0.16.17 postcss: 8.4.21 resolve: 1.22.1 rollup: 3.14.0 + sass: 1.58.3 optionalDependencies: fsevents: 2.3.2 dev: false @@ -5085,7 +5507,7 @@ packages: vite: optional: true dependencies: - vite: 4.1.2_@types+node@18.14.0 + vite: 4.1.2_435aevtanapkguv7m72cl6trbi dev: false /vscode-css-languageservice/6.2.1: @@ -5218,6 +5640,10 @@ packages: /wrappy/1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + /xtend/4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + /y18n/5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -5230,6 +5656,15 @@ packages: /yallist/4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + /yaml/1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + /yaml/2.2.1: + resolution: {integrity: sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==} + engines: {node: '>= 14'} + dev: false + /yargs-parser/20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} diff --git a/public/ansidev.png b/public/ansidev.png new file mode 100644 index 00000000..b22cfb94 Binary files /dev/null and b/public/ansidev.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 00000000..f9e7a94c Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg deleted file mode 100644 index 0f390629..00000000 --- a/public/favicon.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/src/assets/scss/_base.scss b/src/assets/scss/_base.scss new file mode 100644 index 00000000..b5c61c95 --- /dev/null +++ b/src/assets/scss/_base.scss @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/src/assets/scss/_styles.scss b/src/assets/scss/_styles.scss new file mode 100644 index 00000000..c30a105e --- /dev/null +++ b/src/assets/scss/_styles.scss @@ -0,0 +1,65 @@ +%link { + @apply no-underline; + @apply text-style-link hover:text-style-link-hover; +} + +a { + @extend %link; +} + +.header-icon { + @apply text-site-header-text hover:text-site-header-text-hover; +} + +/* Badge CSS */ +%badge { + @apply px-3 py-2 border rounded-full border-style-primary; +} + +.badge-default { + @apply text-style-secondary border-b-2 border-dotted border-b-style-primary; +} + +.badge-outline { + @extend %badge; + @apply bg-transparent text-style-secondary hover:bg-style-primary hover:text-style-primary-inverted; +} + +/* Prose CSS */ +.prose { + @apply transition duration-150 ease-in w-full max-w-5xl; + @apply prose-a:no-underline prose-a:break-words; + & :is(:where(a):not(:where([class~='not-prose'] *))) { + @extend %link; + } + + li:has(> a.new) { + list-style-type: '\2610'; + padding-inline-start: 1ch; + + &::marker { + @apply pt-1 text-gray-500; + } + } + + li:has(> a.internal:not(.new)) { + list-style-type: '\2611'; + padding-inline-start: 1ch; + + &::marker { + @apply pt-1 text-green-500; + } + } + + a.new { + @apply text-gray-500; + } + + a.internal:not(.new) { + @apply text-green-500 hover:text-style-secondary; + + &::after { + content: ' \2192'; + } + } +} diff --git a/src/assets/scss/app.scss b/src/assets/scss/app.scss new file mode 100644 index 00000000..5cb25987 --- /dev/null +++ b/src/assets/scss/app.scss @@ -0,0 +1,2 @@ +@import 'base'; +@import 'styles'; diff --git a/src/components/AppFooter.astro b/src/components/AppFooter.astro new file mode 100644 index 00000000..924607fe --- /dev/null +++ b/src/components/AppFooter.astro @@ -0,0 +1,64 @@ +--- +import CounterAnalytics from '@/components/analytics/CounterAnalytics.astro' +import GoogleAnalytics from '@/components/analytics/GoogleAnalytics.astro' +import Swetrix from '@/components/analytics/Swetrix.astro' +import Icon from '@/components/Icon.astro' +import siteConfig from '@/configs/site' +import { getPluginConfig, isPluginEnabled } from '@/utils/plugin' + +const { author } = siteConfig +const { name, nickname, email, homepage, github, twitter, telegram, reddit } = + author +const socialNetworks: Record = { + github, + twitter, + telegram, + reddit, +} +--- + + +{ + isPluginEnabled('googleAnalytics') ? ( + + ) : ( + '' + ) +} +{isPluginEnabled('swetrix') ? : ''} +{ + isPluginEnabled('counterAnalytics') ? ( + + ) : ( + '' + ) +} + + diff --git a/src/components/AppHeader.astro b/src/components/AppHeader.astro new file mode 100644 index 00000000..6dc0fa9b --- /dev/null +++ b/src/components/AppHeader.astro @@ -0,0 +1,69 @@ +--- +import type { HTMLAttributes } from 'astro/types' + +import Icon from '@/components/Icon.astro' +import ThemeSelector from '@/components/ThemeSelector.astro' +import siteConfig from '@/configs/site' + +export interface Props extends HTMLAttributes<'svg'> {} + +const { ...props } = Astro.props +const { title, description, author, themes } = siteConfig +--- + +
+
+
+ +
+

+ {title} +

+

{description}

+
+
+
+ + + + +
+
+
+ + diff --git a/src/components/Breadcrumb.astro b/src/components/Breadcrumb.astro new file mode 100644 index 00000000..576feaa3 --- /dev/null +++ b/src/components/Breadcrumb.astro @@ -0,0 +1,61 @@ +--- +import Icon from '@/components/Icon.astro' + +interface BreadcrumbItem { + icon: string + title: string + url: string +} + +interface Props { + items: BreadcrumbItem[] +} + +const { items } = Astro.props +const hasIcon = (item: BreadcrumbItem) => item.icon.length > 0 +--- + + diff --git a/src/components/Card.astro b/src/components/Card.astro deleted file mode 100644 index e36b8f09..00000000 --- a/src/components/Card.astro +++ /dev/null @@ -1,63 +0,0 @@ ---- -export interface Props { - title: string - body: string - href: string -} - -const { href, title, body } = Astro.props ---- - - - diff --git a/src/components/Icon.astro b/src/components/Icon.astro new file mode 100644 index 00000000..7eb44d7f --- /dev/null +++ b/src/components/Icon.astro @@ -0,0 +1,45 @@ +--- +import bi from '@iconify-json/bi/icons.json' +import type { HTMLAttributes } from 'astro/types' +import _get from 'lodash.get' + +export interface Props extends HTMLAttributes<'svg'> { + size?: number +} + +const { size = 16, ...props } = Astro.props + +const getIcon = (icon: string) => { + if (typeof icon !== 'string' || icon.length === 0) { + return '' + } + + const s = icon.split(':') + + if (s.length < 2) { + return '' + } + + const preset = s[0] + const iconName = s[1] + + if (preset !== 'bi') { + return '' + } + + return _get(bi.icons, `${iconName}.body`) +} + +const svg = getIcon(props.name || '') +--- + + diff --git a/src/components/LeetCodeDifficulty.astro b/src/components/LeetCodeDifficulty.astro new file mode 100644 index 00000000..71a71287 --- /dev/null +++ b/src/components/LeetCodeDifficulty.astro @@ -0,0 +1,62 @@ +--- +import type { HTMLAttributes } from 'astro/types' + +export interface Props extends HTMLAttributes<'a' | 'span'> { + tag?: string + href?: string + difficulty: string +} + +const { difficulty, tag = 'a', ...props } = Astro.props + +const Tag = tag + +const tagProps: Record = {} +if (tag === 'a') { + tagProps.href = props.href! +} + +const difficultyStyleMap: Record = { + Easy: 'difficulty-easy', + Medium: 'difficulty-medium', + Hard: 'difficulty-hard', +} + +const getDifficultyCssClass: (difficulty: string) => string = ( + difficulty: string +) => { + return Object.keys(difficultyStyleMap).includes(difficulty) + ? difficultyStyleMap[difficulty] || '' + : '' +} +--- + + + + + + diff --git a/src/components/SEOMeta.astro b/src/components/SEOMeta.astro new file mode 100644 index 00000000..3fd181b2 --- /dev/null +++ b/src/components/SEOMeta.astro @@ -0,0 +1,37 @@ +--- +interface Props { + title: string + description?: string + favicon?: string + faviconMimeType?: string + keywords?: string + author?: string + robots?: string +} + +const { + title, + description, + favicon, + faviconMimeType, + keywords, + author, + robots, +} = Astro.props +--- + + + + + + + +{title} + +{description && } + + +{keywords && } + +{robots && } + diff --git a/src/components/ThemeSelector.astro b/src/components/ThemeSelector.astro new file mode 100644 index 00000000..411c8234 --- /dev/null +++ b/src/components/ThemeSelector.astro @@ -0,0 +1,162 @@ +--- +import bi from '@iconify-json/bi/icons.json' + +import Icon from '@/components/Icon.astro' + +interface ThemeMeta { + name: string + icon: string +} + +export interface Props { + themes: Record + fallbackLightTheme: string + fallbackDarkTheme: string + iconClass: string +} + +const STORAGE_KEY_THEME: string = 'theme' + +const { themes, fallbackLightTheme, fallbackDarkTheme, iconClass } = Astro.props +const defaultTheme = fallbackDarkTheme +const icons = bi.icons +--- + +
+ + +
+ + + + diff --git a/src/components/analytics/CounterAnalytics.astro b/src/components/analytics/CounterAnalytics.astro new file mode 100644 index 00000000..a9d20727 --- /dev/null +++ b/src/components/analytics/CounterAnalytics.astro @@ -0,0 +1,15 @@ +--- +interface Props { + projectId: string + utcOffset: number +} + +const { projectId, utcOffset } = Astro.props +--- + + diff --git a/src/components/analytics/GoogleAnalytics.astro b/src/components/analytics/GoogleAnalytics.astro new file mode 100644 index 00000000..8b8b0bed --- /dev/null +++ b/src/components/analytics/GoogleAnalytics.astro @@ -0,0 +1,24 @@ +--- +interface Props { + projectId: string +} + +const { projectId } = Astro.props +--- + + + + diff --git a/src/components/analytics/Swetrix.astro b/src/components/analytics/Swetrix.astro new file mode 100644 index 00000000..74d0b1c0 --- /dev/null +++ b/src/components/analytics/Swetrix.astro @@ -0,0 +1,15 @@ +--- +interface Props { + projectId: string +} + +const { projectId } = Astro.props +--- + + + diff --git a/src/components/post/PostHeader.astro b/src/components/post/PostHeader.astro new file mode 100644 index 00000000..dad33c76 --- /dev/null +++ b/src/components/post/PostHeader.astro @@ -0,0 +1,54 @@ +--- +import type { CollectionEntry } from 'astro:content' +import kebabCase from 'lodash.kebabcase' + +import LeetCodeDifficulty from '@/components/LeetCodeDifficulty.astro' +import siteConfig from '@/configs/site' +import { formatDate } from '@/utils/date' + +type Props = CollectionEntry<'leetcode-solutions'>['data'] + +const { author: siteAuthor } = siteConfig +const { title, author, pubDate, tags = [], difficulty } = Astro.props + +const formattedDate = formatDate(pubDate) +--- + +
+

{title}

+
+ + {difficulty} + +
+
+ { + tags.map((tag) => ( + + + {tag} + + + )) + } +
+
+ Author's avatar +
+ {author} + Posted on {formattedDate} +
+
+
+ + diff --git a/src/components/post/PostItem.astro b/src/components/post/PostItem.astro new file mode 100644 index 00000000..24088da8 --- /dev/null +++ b/src/components/post/PostItem.astro @@ -0,0 +1,36 @@ +--- +import Icon from '@/components/Icon.astro' +import LeetCodeDifficulty from '@/components/LeetCodeDifficulty.astro' + +export interface Props { + title: string + href: string + difficulty: string +} + +const { title, href, difficulty } = Astro.props +--- + + +
+
+

{title}

+
+ +
+
+ + diff --git a/src/components/post/PostList.astro b/src/components/post/PostList.astro new file mode 100644 index 00000000..b235f03b --- /dev/null +++ b/src/components/post/PostList.astro @@ -0,0 +1,23 @@ +--- +import type { CollectionEntry } from 'astro:content' + +import PostItem from '@/components/post/PostItem.astro' + +export interface Props { + posts: CollectionEntry[] +} + +const { posts = [] } = Astro.props +--- + +
+ { + posts.map((p) => ( + + )) + } +
diff --git a/src/configs/site.ts b/src/configs/site.ts new file mode 100644 index 00000000..efc33aa2 --- /dev/null +++ b/src/configs/site.ts @@ -0,0 +1,53 @@ +const env = process.env + +export default { + title: 'LeetCode Blog', + description: 'Solutions for LeetCode problems - Written by ansidev', + author: { + name: 'Le Minh Tri', + nickname: 'ansidev', + email: 'ansidev@gmail.com', + avatar: '/ansidev.png', + homepage: 'https://ansidev.xyz', + github: 'https://github.com/ansidev', + twitter: 'https://twitter.com/ansidev', + telegram: 'https://t.me/ansidev', + reddit: 'https://reddit.com/u/ansidev', + }, + favicon: '/favicon.ico', + faviconMimeType: 'image/x-icon', + themes: { + 'leetcode-light': { + name: 'LeetCode Light', + icon: 'bi:sun-fill', + }, + 'leetcode-dark': { + name: 'LeetCode Dark', + icon: 'bi:moon-fill', + }, + google: { + name: 'Google', + icon: 'bi:sun-fill', + }, + microsoft: { + name: 'Microsoft', + icon: 'bi:sun-fill', + }, + twitter: { + name: 'Twitter', + icon: 'bi:sun-fill', + }, + }, + plugins: { + counterAnalytics: { + projectId: env.COUNTER_ANALYTICS_ID, + utcOffset: 7, + }, + googleAnalytics: { + projectId: env.GA_ID, + }, + swetrix: { + projectId: env.SWETRIX_ID, + }, + } +} diff --git a/src/content/config.ts b/src/content/config.ts new file mode 100644 index 00000000..71663c22 --- /dev/null +++ b/src/content/config.ts @@ -0,0 +1,16 @@ +import { defineCollection, z } from 'astro:content' + +const leetcodeSolutionCollection = defineCollection({ + schema: z.object({ + title: z.string(), + keywords: z.array(z.string()), + author: z.string(), + pubDate: z.string(), + difficulty: z.string(), + tags: z.array(z.string()), + }), +}) + +export const collections = { + 'leetcode-solutions': leetcodeSolutionCollection, +} diff --git a/src/content/leetcode-solutions/0003-longest-substring-without-repeating-characters.md b/src/content/leetcode-solutions/0003-longest-substring-without-repeating-characters.md new file mode 100644 index 00000000..62b442b5 --- /dev/null +++ b/src/content/leetcode-solutions/0003-longest-substring-without-repeating-characters.md @@ -0,0 +1,120 @@ +--- +title: "3. Longest Substring Without Repeating Characters" +slug: "0003-longest-substring-without-repeating-characters" +keywords: +- "longest" +- "substring" +- "without" +- "repeating" +- "characters" +author: "ansidev" +pubDate: "2022-10-31T19:46:49+07:00" +difficulty: "Medium" +tags: +- "Hash Table" +- "String" +- "Sliding Window" +--- +## Problem + +Given a string `s`, find the length of the **longest substring** without repeating characters. + +**Example 1:** + +``` +Input: s = "abcabcbb" +Output: 3 +Explanation: The answer is "abc", with the length of 3. +``` + +**Example 2:** + +``` +Input: s = "bbbbb" +Output: 1 +Explanation: The answer is "b", with the length of 1. +``` + +**Example 3:** + +``` +Input: s = "pwwkew" +Output: 3 +Explanation: The answer is "wke", with the length of 3. +Notice that the answer must be a substring, "pwke" is a subsequence and not a substring. +``` + +**Constraints:** + +- `0 <= s.length <= 5 * 104` +- `s` consists of English letters, digits, symbols, and spaces. + +## Analysis + +| Abbreviation | Stands for | +| ------------ | ------------------------------------------------ | +| `CSWRC` | `current substring without repeating characters` | +| `LSWRC` | `longest substring without repeating characters` | + +- If `s` is an empty string, the LSWRC is `0`. +- If the length of string `s` is 1, the LSWRC is `1`. + +## Approaches + +### Approach 1 + +#### Approach + +- If the length of string `s` is greater than 1, because we need to determine the LSWRC, while iterating over the string, we have to check whether the character at a specific index is repeating or not. We can consider using a hashmap. + + - Assume the `LSWRC` is `s[0]`. + + | Initial value | Note | + | --------------------- | ---------------------------------------------------------------------- | + | `left = 0` | Left index of the LSWRC | + | `result = 1` | Length of the LSWRC | + | `lastIndex = { a=0 }` | This hashmap saves the last index of a specific character of the array | + + - Start traversing the array from index `1`. For each step: + - Check whether the current character is repeating: `lastIndex` has the key `s[right]`. + - If yes, update `left = lastIndex[s[right]] + 1` if `left < lastIndex[s[right]] + 1`. + - Update the last index of `s[right]` to the current index. + - Calculate the new length of the `CSWRC`. (`right - left + 1`). + - Update `result` if it is less than the new length of the `CSWRC`. + - Finally, return `result`. It is the `LSWRC`. + +#### Solutions + +```go +func lengthOfLongestSubstring(s string) int { + l := len(s) + if l == 0 || l == 1 { + return l + } + + result, left := 1, 0 + lastIndex := map[byte]int{} + lastIndex[s[0]] = 0 + for right := 1; right < l; right++ { + if v, ok := lastIndex[s[right]]; ok { + if left < v+1 { + left = v + 1 + } + } + lastIndex[s[right]] = right + if result < right-left+1 { + result = right - left + 1 + } + } + + return result +} +``` + +#### Complexity + +- Time complexity: `O(n)` because we just traverse the string once. +- Space complexity: + - We use four extra variables `l`, `result`, `left`, `right`, no matter what value will, they will take fixed bytes. So the space complexity is `O(1)`. + - The space complexity of the map `lastIndex` is `O(n)`. + - So the final space complexity is `O(n)`. diff --git a/src/content/leetcode-solutions/0008-string-to-integer-atoi.md b/src/content/leetcode-solutions/0008-string-to-integer-atoi.md new file mode 100644 index 00000000..f850c048 --- /dev/null +++ b/src/content/leetcode-solutions/0008-string-to-integer-atoi.md @@ -0,0 +1,179 @@ +--- +title: "8. String to Integer (atoi)" +slug: "0008-string-to-integer-atoi" +keywords: +- "string" +- "to" +- "integer" +- "atoi" +author: "ansidev" +pubDate: "2022-10-26T13:11:00+07:00" +difficulty: "Medium" +tags: +- "String" +--- +## Problem + +Implement the `myAtoi(string s)` function, which converts a string to a 32-bit signed integer (similar to C/C++'s `atoi` function). + +The algorithm for `myAtoi(string s)` is as follows: + +1. Read in and ignore any leading whitespace. +2. Check if the next character (if not already at the end of the string) is `'-'` or `'+'`. Read this character in if it is either. This determines if the final result is negative or positive respectively. Assume the result is positive if neither is present. +3. Read in next the characters until the next non-digit character or the end of the input is reached. The rest of the string is ignored. +4. Convert these digits into an integer (i.e. `"123" -> 123`, `"0032" -> 32`). If no digits were read, then the integer is 0. Change the sign as necessary (from step 2). +5. If the integer is out of the 32-bit signed integer range [-231, 231 - 1], then clamp the integer so that it remains in the range. Specifically, integers less than -231 should be clamped to -231, and integers greater than 231-1 should be clamped to 231-1. +Return the integer as the final result. + +**Note:** + +- Only the space character `' '` is considered a whitespace character. +- **Do not ignore** any characters other than the leading whitespace or the rest of the string after the digits. + +**Example 1:** + +``` +Input: s = "42" +Output: 42 +Explanation: The underlined characters are what is read in, the caret is the current reader position. +Step 1: "42" (no characters read because there is no leading whitespace) + ^ +Step 2: "42" (no characters read because there is neither a `'-'` nor `'+'`) + ^ +Step 3: "42" ("42" is read in) + ^ +The parsed integer is 42. +Since 42 is in the range [-2^31, 2^31 - 1], the final result is 42. +``` + +**Example 2:** + +``` +Input: s = " -42" +Output: -42 +Explanation: +Step 1: " -42" (leading whitespace is read and ignored) + ^ +Step 2: " -42" (`'-'` is read, so the result should be negative) + ^ +Step 3: " -42" ("42" is read in) + ^ +The parsed integer is -42. +Since -42 is in the range [-2^31, 2^31 - 1], the final result is -42. +``` + +**Example 3:** + +``` +Input: s = "4193 with words" +Output: 4193 +Explanation: +Step 1: "4193 with words" (no characters read because there is no leading whitespace) + ^ +Step 2: "4193 with words" (no characters read because there is neither a `'-'` nor `'+'`) + ^ +Step 3: "4193 with words" ("4193" is read in; reading stops because the next character is a non-digit) + ^ +The parsed integer is 4193. +Since 4193 is in the range [-2^31, 2^31 - 1], the final result is 4193. +``` + +**Constraints:** + +- `0 <= s.length <= 200`. +- `s` consists of English letters (lower-case and upper-case), digits (`0-9`), `' '`, `'+'`, `'-'`, and `'.'`. + +## Analysis + +I don't have any special analysis since the problem is so clearly. + +## Approaches + +### Approach 1 + +#### Approach + +1. Check whether the input is null (depends on programming language) or empty. If it is, return `0`. +2. Use a pointer `i` to traverse the input string, always remember checking whether i less than length of `s`. + - While `s[i]` is a whitespace, keep increasing i by 1. + - Check whether the next character (`s[i]`) is one of `-`, `+`, or digit (`0-9`): + - If not, return `0`. + - Otherwise, check whether `s[i]` is one of `-` or `+`, save the result to a boolean variable and increase i by 1. + - Note that if `s[i]` is not one of `-` or `+`, this means that it is a digit, and `i` will not be increased. + - Check whether the current character is a sign, if it is, return `0` because it is an invalid input. + - Create new 64 bit float number `n` to save the result. + - While `s[i]` is a digit, `n = n x 10 + integer value of s[i]`, then increasing `i` by `1`. + - If the sign is `-`, `n = -n`. + - Check the range of `n`: + - If n < -231, return -231. + - If n > 231-1, return 231-1. + - Otherwise, convert n to integer and return. + +- Notes: `MinInt32 = -1 << 31` (-231) and `MaxInt32 = 1<<31 - 1` (231-1). + +#### Solutions + +```go +func myAtoi(s string) int { + l := len(s) + if l == 0 { + return 0 + } + + i := 0 + for i < l && s[i] == ' ' { + i++ + } + + if i < l && s[i] != '+' && + s[i] != '-' && + s[i] != '0' && + s[i] != '1' && + s[i] != '2' && + s[i] != '3' && + s[i] != '4' && + s[i] != '5' && + s[i] != '6' && + s[i] != '7' && + s[i] != '8' && + s[i] != '9' { + return 0 + } + + isNegative := false + if i < l && s[i] == '-' { + isNegative = true + i++ + } else if i < l && s[i] == '+' { + i++ + } + + if i < l && (s[i] == '+' || s[i] == '-') { + return 0 + } + + var n float64 = 0 + for i < l && s[i] >= '0' && s[i] <= '9' { + n = n*10 + float64(s[i]-'0') + i++ + } + + if isNegative { + n = -n + } + + if n < math.MinInt32 { + return math.MinInt32 + } + if n > math.MaxInt32 { + return math.MaxInt32 + } + + return int(n) +} +``` + +#### Complexity + +- Time complexity: `O(n)` because we just traverse the string once. +- Space complexity: We use three extra variable `l`, `isNegative`, `n`, no matter what value will, they will take a fixed bytes. So the space complexity is `O(1)`. diff --git a/src/content/leetcode-solutions/0053-maximum-subarray.md b/src/content/leetcode-solutions/0053-maximum-subarray.md new file mode 100644 index 00000000..df43a86b --- /dev/null +++ b/src/content/leetcode-solutions/0053-maximum-subarray.md @@ -0,0 +1,106 @@ +--- +title: "53. Maximum Subarray" +slug: "0053-maximum-subarray" +keywords: +- "maximum" +- "subarray" +- "kadane" +author: "ansidev" +pubDate: "2022-10-26T13:11:00+07:00" +difficulty: "Medium" +tags: +- "Array" +- "Divide and Conquer" +- "Dynamic Programming" +--- +## Problem + +Given an integer array `nums`, find the contiguous subarray (containing at least one number) which has the largest sum and return `its sum`. + +A `subarray` is a `contiguous` part of an array. + +**Example 1:** + +``` +Input: nums = [-2,1,-3,4,-1,2,1,-5,4] +Output: 6 +Explanation: [4,-1,2,1] has the largest sum = 6. +``` + +**Example 2:** + +``` +Input: nums = [1] +Output: 1 +``` + +**Example 3:** + +``` +Input: nums = [5,4,-1,7,8] +Output: 23 +``` + +**Constraints:** + +- 1 <= nums.length <= 105 +- -104 <= nums[i] <= 104 + +**Follow up:** If you have figured out the `O(n)` solution, try coding another solution using the **divide and conquer** approach, which is more subtle. + +## Analysis + +## Approaches + +### Approach 1 + +#### Approach + +Technique: Dynamic Programming + +Original author: [Kadane](https://www.cmu.edu/dietrich/statistics-datascience/people/faculty/joseph-kadane.html). + +Assume we have `dp[i]`: maximum sum of subarray that ends at index `i`. + +`dp[i] = max(dp[i - 1] + nums[i], nums[i])` + +Initial state `dp[0] = nums[0]`. + +From the above formula, we just need to access its previous element at each step, so we can use 2 variables: + +- `currentMaxSum`: maximum sum of subarray that ends at the current index `i`. + - `currentMaxSum = max(currentMaxSum + nums[i], nums[i]`. +- `globalSum`: global maximum subarray sum + - `globalSum = max(currentMaxSum, globalSum)`. + +#### Solutions + +```go +func maxSubArray(nums []int) int { + currentMaxSum := nums[0] + globalSum := nums[0] + + for _, x := range nums[1:] { + if currentMaxSum+x > x { + currentMaxSum += x + } else { + currentMaxSum = x + } + + if globalSum < currentMaxSum { + globalSum = currentMaxSum + } + } + + return globalSum +} +``` + +#### Complexity + +- **Time Complexity**: `O(n)` because we just iterate over the array once. +- **Space Complexity**: `O(1)`. We just use 2 integer variables for extra spaces. + +## References + +- https://en.wikipedia.org/wiki/Maximum_subarray_problem diff --git a/src/content/leetcode-solutions/0231-power-of-two.md b/src/content/leetcode-solutions/0231-power-of-two.md new file mode 100644 index 00000000..f178f245 --- /dev/null +++ b/src/content/leetcode-solutions/0231-power-of-two.md @@ -0,0 +1,152 @@ +--- +title: "231. Power of Two" +slug: "0231-power-of-two" +keywords: +- "power of two" +author: "ansidev" +pubDate: "2022-10-27T15:23:12+07:00" +difficulty: "Easy" +tags: +- "Math" +- "Bit Manipulation" +- "Recursion" +--- +## Problem + +Given an integer `n`, return `true` *if it is a power of two. Otherwise, return* `false`. + +An integer `n` is a power of two, if there exists an integer `x` such that n == 2x. + +**Example 1:** + +``` +Input: n = 1 +Output: true +Explanation: 20 = 1 +``` +**Example 2:** + +``` +Input: n = 16 +Output: true +Explanation: 24 = 16 +``` +**Example 3:** + +``` +Input: n = 3 +Output: false +``` + +**Constraints:** + +- -231 <= n <= 231 - 1. + +**Follow up:** Could you solve it without loops/recursion? + +## Analysis + +1. If `n` is a power of two: + +
+n = 2i (`i ≥ 0`)
+  ≥ 20 = 1
+
+ +`=>` **If `n < 0`, `n` is not a power of two**. + +2. If `n` is a power of two: + +The binary form of every 2i (`i` is a non-negative number): + +
+2010 = 110 = 12
+
+2110 = 210 = 102
+
+2210 = 410 = 1002
+
+2310 = 810 = 10002
+
+...
+
+2i10 = 100...002 (only one 1-digit at the first position and followed by total `i` 0-digit numbers)
+       |__i__|
+
+ +2.1. If `i = 0`: +
+n = 20 = 1 > 0
+n & (n-1) = 1 & 0 = 0
+
+ + +2.2. If `i > 0`: + +The binary form of every 2i - 1: + +
+2110 - 1 = 110 = 012
+
+2210 - 1 = 310 = 0112
+
+2310 - 1 = 710 = 01112
+
+...
+
+2i10 - 1 = 011...112 (total `i` 1-digit numbers)
+           |__i__|
+
+ +For `i > 0`, we have: + +
+n - 1 = 2i10 - 1 ≥ 0
+n & (n-1) = 2i10 & 2i10 - 1 = 100...002 & 011...112 = 0
+                 |__i__|     |__i__|
+
+ +``` +If n is a power of two: n & (n-1) = 0. +``` + +3. If `n` is not an power of two, we have: +
+             n10 = [0....0][1....1][0....0]2 (i ≥ 0, j ≥ 2, k ≥ 1)
+                   |__i__| |__j__| |__k__|
+
+         (n-1)10 = [0....0][1....1]0[1....1]2
+                   |__i__| |_j-1_|  |__k__|
+
+x = n10 & (n-1)10 = [0....0][1.....][0....0]2
+                   |__i__| |_j-1_| |_k+1_|
+
+ +- `j ≥ 2` => `j-1 ≥ 1`. So `x` has at least one 1-digit number => `x > 0`. + +``` +If n is not a power of two: n & (n-1) > 0. +``` + +``` +As result, n is a power of two if and only if n > 0 and n & (n-1) = 0. +``` +## Approaches + +### Approach 1 + +#### Approach + +- `n` is a power of two if and only if `n > 0` and `n & (n-1) = 0`. + +#### Solutions + +```go +func isPowerOfTwo(n int) bool { + return n > 0 && n&(n-1) == 0 +} +``` + +#### Complexity + +- **Time Complexity**: `O(1)`. diff --git a/src/content/leetcode-solutions/0628-maximum-product-of-three-numbers.md b/src/content/leetcode-solutions/0628-maximum-product-of-three-numbers.md new file mode 100644 index 00000000..12bb03c0 --- /dev/null +++ b/src/content/leetcode-solutions/0628-maximum-product-of-three-numbers.md @@ -0,0 +1,142 @@ +--- +title: "628. Maximum Product of Three Numbers" +slug: "0628-maximum-product-of-three-numbers" +keywords: +- "maximum" +- "product" +- "of" +- "three" +- "numbers" +author: "ansidev" +pubDate: "2022-11-18T08:58:46+07:00" +difficulty: "Easy" +tags: +- "Array" +- "Math" +- "Sorting" +--- +## Problem + +Given an integer array `nums`, *find three numbers whose product is maximum and return the maximum product*. + +**Example 1:** + +``` +Input: nums = [1,2,3] +Output: 6 +``` + +**Example 2:** + +``` +Input: nums = [1,2,3,4] +Output: 24 +``` + +**Example 3:** + +``` +Input: nums = [-1,-2,-3] +Output: -6 + ``` + +**Constraints:** + +- `3 <= nums.length <= 104`. +- `-1000 <= nums[i] <= 1000`. + +## Analysis + +If length of `nums` is 3, maximum product is `nums[0] x nums[1] x nums[2]`. + +## Approaches + +### Approach 1 + +``` +Notes: +- l: length of nums. +- nums[i:j]: sub array of nums from index i to j (includes nums[i], nums[j]). +``` +#### Approach + +- If length of `nums` is 3, maximum product is `nums[0] x nums[1] x nums[2]`. +- If length of `nums` is greater than 3: + - Step 1: Sort the input array (ascending). + - For the sorted array, there are following possible cases: + - Case 1: All element are positive numbers. + - `maxProduct = nums[l-3] x nums[l-2] x nums[l-1]`. + - Case 2: All element are negative numbers. + - Product of two random elements will be a positive number. + - Product of three random elements will be a negative number. + - If `n1 > 0`, `n2 < n3 < 0`: `n1 x n2 < n1 x n3 < 0`. + - If `m1 < 0`, `0 < m2 < n3`: `m1 x m3 < m1 x m2 < 0`. + - So to find the maximum product of three numbers in this case, we need to find two negative numbers `a`, `b` that `a x b` has max value (note that `a x b > 0`), then find the third number which is the maximum value of remaining ones. + - `nums[0]` & `nums[1]` are two smallest negative numbers. So their product will be the maximum product of two numbers. `nums[l-1]` is the maximum one of [`nums[2]`, `nums[3]`,..., `nums[l-1]`]. + - Finally, the maximum product of three numbers in this case is 👉 `nums[0] x nums[1] x nums[l-1]`. + - Other cases. + - `nums` has at least 4 elements. + - If `nums` contains zero: + - If `nums[0] = 0`, all remaining elements are positive numbers (similar to case 1) + - 👉 `maxProduct = nums[l-3] x nums[l-2] x nums[l-1]`. + - If `nums[l-1] = 0`, all remaining elements are negative numbers (similar to case 2) + - `maxProduct` of three numbers of sub array `nums[0:l-2]` is a negative number, it less than product of `nums[l-1]` (`= 0`) and two random numbers of sub array `nums[0:l-2]`. So in this case, we can pick three numbers `nums[l-3]`, `nums[l-2]`, `nums[l-1]`. + - 👉 `maxProduct = nums[l-3] x nums[l-2] x nums[l-1] = 0`. + - If `nums[i] = 0` (`0 < i < l-1`), because `nums` has at least 4 elements, so there are possible cases: + - `nums` has at least there positive numbers, similar to case 1. + - `nums` has two positive numbers: + - If `l = 4`: + - `nums` = [`nums[0]`, `0`, `num[2]`, `nums[3]`]. + - `nums[0] x nums[2] x nums[3] < 0 x num[2] x nums[3] = 0`. + - 👉 `maxProduct` must contains zero so `maxProduct = 0 = nums[l-3] x nums[l-2] x nums[l-1]` + - If `l > 4`: + - `nums` = [`nums[0]`, `nums[1]`, ..., `nums[l-4]` ,`0`, `num[l-2]`, `nums[l-1]`]. + - If one of three numbers of `maxProduct` is zero, `maxProduct = 0`. + - Otherwise, if two of three numbers of `maxProduct` is `num[l-2]`, `nums[l-1]`, `maxProduct < 0`. + - Otherwise, two of three numbers of `maxProduct` is negative numbers, max product of two negative numbers of sub array `nums[0:l-4]` is `nums[0] x nums[1] > 0`. + - `maxProduct = nums[0] x nums[1] x nums[l-1] > 0` + - 👉 `maxProduct = nums[0] x nums[1] x nums[l-1] > 0` + - 👉 `maxProduct = nums[0] x nums[1] x nums[l-1]` + - `nums` has one positive numbers: + - `nums` = [`nums[0]`, `nums[1]`, ..., `nums[l-3]` ,`0`, `nums[l-1]`]. + - We have cases: + - `maxProduct` contains zero: `0 x nums[i] x nums[j] = 0` + - `maxProduct` does not contain zero: + - `nums[i] x nums[j] x nums[k] < 0` (`i < j < k < 0`) + - `nums[i] x nums[j] x nums[l-1] > 0` (`i < j < 0`) + - So `maxProduct` is product of two negative numbers and one positive number (`nums[l-1]`) + - 👉 `maxProduct = max(nums[j] x num[j]) x nums[l-1] = nums[0] x nums[1] x nums[l-1]`. + - The maximum product of three numbers in every cases (`l > 3`) is one of two: + - `nums[0] x nums[1] x nums[l-1]` + - `nums[l-3] x nums[l-2] x nums[l-1]` + - Conclusion, 👉 `maxProduct = max(nums[0] x nums[1] x nums[l-1], nums[l-3] x nums[l-2] x nums[l-1])`. + +#### Solutions + +```go +import "sort" + +func maximumProduct(nums []int) int { + l := len(nums) + + if l == 3 { + return nums[0] * nums[1] * nums[2] + } + + sort.Ints(nums) + + return max(nums[0]*nums[1]*nums[l-1], nums[l-3]*nums[l-2]*nums[l-1]) +} + +func max(x int, y int) int { + if x > y { + return x + } + + return y +} +``` + +#### Complexity + +- **Time Complexity**: Time complexity of the sort algorithm. diff --git a/src/content/leetcode-solutions/2400-number-of-ways-to-reach-a-position-after-exactly-k-steps.md b/src/content/leetcode-solutions/2400-number-of-ways-to-reach-a-position-after-exactly-k-steps.md new file mode 100644 index 00000000..70efb523 --- /dev/null +++ b/src/content/leetcode-solutions/2400-number-of-ways-to-reach-a-position-after-exactly-k-steps.md @@ -0,0 +1,119 @@ +--- +title: "2400. Number of Ways to Reach a Position After Exactly k Steps" +slug: "2400-number-of-ways-to-reach-a-position-after-exactly-k-steps" +keywords: +- "number" +- "of" +- "ways" +- "to" +- "reach" +- "a" +- "position" +- "after" +- "exactly" +- "k" +- "steps" +author: "ansidev" +pubDate: "2022-10-24T23:49:00+07:00" +difficulty: "Medium" +tags: +- "Math" +- "Dynamic Programming" +- "Combinatorics" +--- +## Problem + +You are given two **positive** integers `startPos` and `endPos`. Initially, you are standing at position startPos on an **infinite** number line. With one step, you can move either one position to the left, or one position to the right. + +Given a positive integer `k`, return the number of **different** ways to reach the position `endPos` starting from `startPos`, such that you perform **exactly** `k` steps. Since the answer may be very large, return it **modulo** 109 + 7. + +Two ways are considered different if the order of the steps made is not exactly the same. + +**Note** that the number line includes negative integers. + +**Example 1:** + +``` +Input: startPos = 1, endPos = 2, k = 3 +Output: 3 +Explanation: We can reach position 2 from 1 in exactly 3 steps in three ways: +- 1 -> 2 -> 3 -> 2. +- 1 -> 2 -> 1 -> 2. +- 1 -> 0 -> 1 -> 2. +It can be proven that no other way is possible, so we return 3. +``` +**Example 2:** + +``` +Input: startPos = 2, endPos = 5, k = 10 +Output: 0 +Explanation: It is impossible to reach position 5 from position 2 in exactly 10 steps. +``` + +**Constraints:** + +- `1 <= startPos, endPos, k <= 1000` + +## Analysis + +Assuming `d` is the **distance** between `startPos` and `endPos` => `d = abs(startPos - endPos)`. + +- `1 <= startPos <= 1000` +- `-1000 <= -endPos <= -1` + +=> `-999 <= startPos - endPos <= 999` + +=> `0 <= d = abs(startPos - endPos) <= 999` + +For `k` is the **numbers of steps** and `d` is the **distance** between `startPos` and `endPos`, the number of ways is: +- `dfs(k, d) = dfs(k-1, abs(d-1)) + dfs(k-1, d+1)`. + +For k steps, the maximum distance is k. +- **d > k**: `dfs(k, d) = 0`. +- **d = k**: `dfs(k, d) = dfs(k, k) = 1`. +- **d = 0**: `dfs(k, 0) = dfs(k-1, 1) + dfs(k-1, 1) = 2 x dfs(k-1, 1)`. + + +Example values: +- `dfs(0,0) = 1`. +- `dfs(1, 0) = 2 x dfs(0, 1) = 0`. +- `dfs(1, 1) = 1`. +- `dfs(2, 0) = 2 x dfs(1, 1) = 2 x 1 = 2`. +- `dfs(2, 1) = dfs(1, 0) + dfs(1, 2) = 0 + 0 = 0`. +- `dfs(2, 2) = 1`. + +## Approaches + +### Approach 1 + +#### Approach + +- From the above analysis, we can use the bottom-up approach to calculate the next values of the `dfs` function from each prior values. Then, we can get the number of ways. + +#### Solutions + +```go +func numberOfWays(startPos int, endPos int, k int) int { + const mod = 1e9 + 7 + dp := [1001][1001]int{} + for i := 1; i <= 1000; i++ { + dp[i][i] = 1 + for j := 0; j < i; j++ { + dp[i][j] = (dp[i-1][abs(j-1)] + dp[i-1][j+1]) % mod + } + } + + return dp[k][abs(startPos-endPos)] +} + +func abs(x int) int { + if x >= 0 { + return x + } + + return -x +} +``` + +## References +- dfs: [[deep-first-search|Deep First Search]] diff --git a/src/env.d.ts b/src/env.d.ts index f964fe0c..6811d7c2 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -1 +1,3 @@ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// /// diff --git a/src/layouts/AppLayout.astro b/src/layouts/AppLayout.astro new file mode 100644 index 00000000..6a7d42ab --- /dev/null +++ b/src/layouts/AppLayout.astro @@ -0,0 +1,39 @@ +--- +import '@/assets/scss/app.scss' + +import AppFooter from '@/components/AppFooter.astro' +import AppHeader from '@/components/AppHeader.astro' +import SEOMeta from '@/components/SEOMeta.astro' +import siteConfig from '@/configs/site' + +export interface Props { + title: string + description: string + author: string + headerCssClasses?: string + keywords?: string[] +} + +const { title, description = '', author, headerCssClasses = '' } = Astro.props +const { favicon, faviconMimeType } = siteConfig +const pageDescription = + description.length > 0 ? description : `${title} - ${siteConfig.description}` +--- + + + + + + + + + + + + diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro deleted file mode 100644 index 575e9caa..00000000 --- a/src/layouts/Layout.astro +++ /dev/null @@ -1,40 +0,0 @@ ---- -export interface Props { - title: string -} - -const { title } = Astro.props ---- - - - - - - - - - {title} - - - - - - diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro new file mode 100644 index 00000000..88821a18 --- /dev/null +++ b/src/layouts/PostLayout.astro @@ -0,0 +1,40 @@ +--- +import type { CollectionEntry } from 'astro:content' + +import Breadcrumb from '@/components/Breadcrumb.astro' +import PostHeader from '@/components/post/PostHeader.astro' +import siteConfig from '@/configs/site' +import AppLayout from '@/layouts/AppLayout.astro' + +interface Props { + frontmatter: CollectionEntry<'leetcode-solutions'>['data'] +} + +const { frontmatter } = Astro.props +const { author } = siteConfig +--- + + +
+ + +
+ +
+
+
+ + diff --git a/src/pages/404.astro b/src/pages/404.astro new file mode 100644 index 00000000..92eff695 --- /dev/null +++ b/src/pages/404.astro @@ -0,0 +1,20 @@ +--- +import siteConfig from '@/configs/site' +import AppLayout from '@/layouts/AppLayout.astro' + +const { author } = siteConfig + +const title = '404 Not Found' +const description = '404 Error, probably not found...' +--- + + +
+

{title}

+

{description}

+ Go Home +
+
diff --git a/src/pages/[slug].astro b/src/pages/[slug].astro new file mode 100644 index 00000000..fc2934ac --- /dev/null +++ b/src/pages/[slug].astro @@ -0,0 +1,26 @@ +--- +import { CollectionEntry, getCollection } from 'astro:content' + +import PostLayout from '@/layouts/PostLayout.astro' + +export async function getStaticPaths() { + const allPosts = await getCollection('leetcode-solutions') + return allPosts.map((entry) => ({ + params: { + slug: entry.slug, + }, + props: { + entry, + }, + })) +} + +type Props = CollectionEntry<'leetcode-solutions'> + +const { entry } = Astro.props +const { Content } = await entry.render() +--- + + + + diff --git a/src/pages/difficulties/[slug].astro b/src/pages/difficulties/[slug].astro new file mode 100644 index 00000000..97087162 --- /dev/null +++ b/src/pages/difficulties/[slug].astro @@ -0,0 +1,54 @@ +--- +import { getCollection } from 'astro:content' +import kebabCase from 'lodash.kebabcase' + +import LeetCodeDifficulty from '@/components/LeetCodeDifficulty.astro' +import PostList from '@/components/post/PostList.astro' +import siteConfig from '@/configs/site' +import AppLayout from '@/layouts/AppLayout.astro' + +export async function getStaticPaths() { + const allDifficulties = new Set() + const allPosts = await getCollection( + 'leetcode-solutions', + ({ data }) => data.difficulty?.length > 0 + ) + allPosts.forEach((post) => allDifficulties.add(post.data.difficulty)) + + return Array.from(allDifficulties).map((difficulty) => { + const slug = kebabCase(difficulty) + const filteredPosts = allPosts.filter( + (post) => post.data.difficulty === difficulty + ) + return { + params: { slug }, + props: { + posts: filteredPosts, + difficulty, + }, + } + }) +} + +const { difficulty, posts } = Astro.props +const { title, description, author } = siteConfig +--- + + +
+
+

+ Posts by difficulty: + + {difficulty} + +

+
+ +
+
diff --git a/src/pages/index.astro b/src/pages/index.astro index c66ff95b..70dd89d0 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,82 +1,34 @@ --- -import Card from '@/components/Card.astro' -import Layout from '@/layouts/Layout.astro' +import { getCollection } from 'astro:content' + +import LeetCodeDifficulty from '@/components/LeetCodeDifficulty.astro' +import PostList from '@/components/post/PostList.astro' +import siteConfig from '@/configs/site' +import AppLayout from '@/layouts/AppLayout.astro' + +const { title, description, author } = siteConfig + +const posts = await getCollection('leetcode-solutions') --- - -
-

Welcome to Astro

-

- To get started, open the directory src/pages in your project. -
- Code Challenge: Tweak the "Welcome to Astro" message above. -

- + +
+
+ + Easy + + + Medium + + + Hard + +
+
- - - +
diff --git a/src/pages/rss.xml.ts b/src/pages/rss.xml.ts new file mode 100644 index 00000000..2592b977 --- /dev/null +++ b/src/pages/rss.xml.ts @@ -0,0 +1,30 @@ +import rss from '@astrojs/rss' +import type { APIContext } from 'astro' +import { getCollection } from 'astro:content' + +import siteConfig from '@/configs/site' + +const { title, description } = siteConfig + +export const get = async ({ site }: APIContext) => { + if (site === undefined || site.toString().length === 0) { + return { + body: '' + } + } + + const allPosts = await getCollection('leetcode-solutions') + + return rss({ + title, + description, + site: site.toString(), + items: allPosts.map((post) => ({ + title: post.data.title, + description: `LeetCode Problem #${post.data.title}`, + pubDate: new Date(post.data.pubDate), + link: `/${post.slug}`, + })), + customData: 'en-US', + }) +} diff --git a/src/pages/tags/[slug].astro b/src/pages/tags/[slug].astro new file mode 100644 index 00000000..3a0c0fba --- /dev/null +++ b/src/pages/tags/[slug].astro @@ -0,0 +1,54 @@ +--- +import { getCollection } from 'astro:content' +import kebabCase from 'lodash.kebabcase' + +import PostList from '@/components/post/PostList.astro' +import siteConfig from '@/configs/site' +import AppLayout from '@/layouts/AppLayout.astro' + +export async function getStaticPaths() { + const allTags = new Set() + const allPosts = await getCollection( + 'leetcode-solutions', + ({ data }) => data.tags?.length > 0 + ) + allPosts.forEach((post) => + post.data.tags?.forEach((t: string) => allTags.add(t)) + ) + + return Array.from(allTags).map((tag) => { + const slug = kebabCase(tag) + const filteredPosts = allPosts.filter((post) => + post.data.tags?.includes(tag) + ) + + return { + params: { slug }, + props: { + posts: filteredPosts, + tag, + }, + } + }) +} + +const { tag, posts } = Astro.props +const { title, description, author } = siteConfig +--- + + +
+
+

+ Posts by tag: + {tag} +

+
+ +
+
diff --git a/src/utils/date.ts b/src/utils/date.ts new file mode 100644 index 00000000..5548f013 --- /dev/null +++ b/src/utils/date.ts @@ -0,0 +1 @@ +export const formatDate = (date: string | Date) => (date instanceof Date ? date : new Date(date)).toLocaleString('en-US', { day: 'numeric', month: 'long', year: 'numeric' }) diff --git a/src/utils/plugin.ts b/src/utils/plugin.ts new file mode 100644 index 00000000..84332939 --- /dev/null +++ b/src/utils/plugin.ts @@ -0,0 +1,8 @@ +import _get from 'lodash.get' + +import siteConfig from '@/configs/site' + +const { plugins } = siteConfig + +export const isPluginEnabled = (pluginName: string) => typeof _get(plugins, pluginName) === 'object' +export const getPluginConfig = (key: string) => _get(plugins, key) diff --git a/tailwind.config.cjs b/tailwind.config.cjs new file mode 100644 index 00000000..0fd2f7e3 --- /dev/null +++ b/tailwind.config.cjs @@ -0,0 +1,45 @@ +const tailwindCssTheme = require('tailwindcss-themer') +const themeConfig = require('./theme.config.cjs') +const typography = require('@tailwindcss/typography') + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], + theme: { + extend: { + colors: { + transparent: 'transparent', + }, + typography: theme => ({ + DEFAULT: { + css: { + color: theme('textColor.style.primary'), + '--tw-prose-headings': theme('textColor.style.primary'), + '--tw-prose-bold': theme('textColor.style.primary'), + '--tw-prose-quotes': theme('textColor.style.primary'), + '--tw-prose-code': theme('textColor.style.primary-inverted'), + '--tw-prose-pre-bg': theme('textColor.style.primary'), + code: { + backgroundColor: theme('backgroundColor.style.primary'), + paddingTop: '2px', + paddingBottom: '5px', + paddingLeft: '5px', + paddingRight: '5px', + borderRadius: '3px', + }, + 'code::before': { + content: 'none', + }, + 'code::after': { + content: 'none', + }, + } + }, + }), + }, + }, + plugins: [ + tailwindCssTheme(themeConfig), + typography(), + ], +} diff --git a/theme.config.cjs b/theme.config.cjs new file mode 100644 index 00000000..1e7b713c --- /dev/null +++ b/theme.config.cjs @@ -0,0 +1,224 @@ +const colors = require('tailwindcss/colors') + +const themeColors = { + leetcode: { + easy: '#00af9b', + medium: '#ffb800', + hard: '#ff2d55', + }, + light: { + secondary: colors.gray['900'], + tertiary: colors.gray['50'], + }, + dark: { + secondary: colors.zinc['100'], + tertiary: colors.zinc['900'], + }, +} + +const darkTheme = ({ primary, secondary, tertiary }) => { + const baseColors = { + fill: primary, + text: secondary, + 'text-inverted': tertiary, + } + + const textDifficultyColors = { + easy: baseColors['text-inverted'], + medium: baseColors['text-inverted'], + hard: baseColors['text-inverted'], + 'easy-hover': themeColors.leetcode.easy, + 'medium-hover': themeColors.leetcode.medium, + 'hard-hover': themeColors.leetcode.hard, + } + + return { + textColor: { + style: { + primary: baseColors.text, + 'primary-inverted': baseColors['text-inverted'], + secondary: baseColors.fill, + link: colors.blue['500'], + 'link-hover': colors.pink['500'], + }, + site: { + 'title': baseColors.text, + 'header-text': baseColors.text, + 'header-text-hover': baseColors.fill, + }, + 'theme-menu': { + 'text-hover': baseColors.fill, + }, + difficulty: { + ...textDifficultyColors, + }, + }, + backgroundColor: { + style: { + primary: baseColors.fill, + secondary: baseColors.text, + 'secondary-inverted': baseColors['text-inverted'], + }, + site: { + 'bg': '#1a1a1a', + 'header-bg': '#282828', + }, + 'theme-menu': { + 'bg': colors.zinc['800'], + 'bg-hover': tertiary, + }, + difficulty: { + easy: themeColors.leetcode.easy, + 'easy-hover': baseColors.text, + medium: themeColors.leetcode.medium, + 'medium-hover': baseColors.text, + hard: themeColors.leetcode.hard, + 'hard-hover': baseColors.text, + }, + }, + borderColor: { + style: { + primary: baseColors.fill, + secondary: baseColors.text, + 'secondary-inverted': baseColors['text-inverted'], + }, + site: { + 'header-border': colors.zinc['600'], + }, + difficulty: { + ...themeColors.leetcode, + 'easy-hover': themeColors.leetcode.easy, + 'medium-hover': themeColors.leetcode.medium, + 'hard-hover': themeColors.leetcode.hard, + }, + }, + } +} + +const lightTheme = ({ primary, secondary, tertiary }) => { + const baseColors = { + fill: primary, + text: secondary, + 'text-inverted': tertiary, + } + + const textDifficultyColors = { + easy: baseColors.text, + medium: baseColors.text, + hard: baseColors.text, + 'easy-hover': themeColors.leetcode.easy, + 'medium-hover': themeColors.leetcode.medium, + 'hard-hover': themeColors.leetcode.hard, + } + + return { + textColor: { + style: { + primary: baseColors.text, + 'primary-inverted': baseColors['text-inverted'], + secondary: baseColors.fill, + link: colors.blue['500'], + 'link-hover': colors.pink['500'], + }, + site: { + 'title': baseColors['text-inverted'], + 'header-text': baseColors['text-inverted'], + 'header-text-hover': colors.gray['300'], + }, + 'theme-menu': { + 'text-hover': baseColors.fill, + }, + difficulty: { + ...textDifficultyColors, + }, + }, + backgroundColor: { + style: { + primary: baseColors.fill, + secondary: baseColors.text, + 'secondary-inverted': baseColors['text-inverted'], + }, + site: { + 'bg': colors.white, + 'header-bg': primary, + }, + 'theme-menu': { + 'bg': baseColors['text-inverted'], + 'bg-hover': colors.gray['100'], + }, + difficulty: { + easy: themeColors.leetcode.easy, + 'easy-hover': baseColors.text, + medium: themeColors.leetcode.medium, + 'medium-hover': baseColors.text, + hard: themeColors.leetcode.hard, + 'hard-hover': baseColors.text, + }, + }, + borderColor: { + style: { + primary: baseColors.fill, + secondary: baseColors.text, + 'secondary-inverted': baseColors['text-inverted'], + }, + site: { + 'header-border': primary, + } + }, + difficulty: { + ...themeColors.leetcode, + 'easy-hover': themeColors.leetcode.easy, + 'medium-hover': themeColors.leetcode.medium, + 'hard-hover': themeColors.leetcode.hard, + }, + } +} + +const siteTheme = ({ primary, secondary, tertiary }, isDark = true) => + isDark + ? darkTheme({ primary, secondary, tertiary }) + : lightTheme({ primary, secondary, tertiary }) + +module.exports = { + defaultTheme: { + extend: siteTheme({ + primary: '#ffa116', + secondary: themeColors.dark['secondary'], + tertiary: themeColors.dark['tertiary'], + }, true) + }, + themes: [ + { + name: 'leetcode-light', + extend: siteTheme({ + primary: '#ffa116', + secondary: themeColors.light['secondary'], + tertiary: themeColors.light['tertiary'], + }, false) + }, + { + name: 'google', + extend: siteTheme({ + primary: '#db2028', + secondary: themeColors.light['secondary'], + tertiary: themeColors.light['tertiary'], + }, false) + }, + { + name: 'microsoft', + extend: siteTheme({ + primary: '#0067b8', + secondary: themeColors.light['secondary'], + tertiary: themeColors.light['tertiary'], + }, false) + }, + { + name: 'twitter', + extend: siteTheme({ + primary: '#1d9bf0', + secondary: themeColors.light['secondary'], + tertiary: themeColors.light['tertiary'], + }, false) + }, + ] +}