├── src ├── vite-env.d.ts ├── interfaces │ ├── profile.tsx │ ├── article.tsx │ ├── github-project.tsx │ └── sanitized-config.tsx ├── constants │ ├── default-custom-theme.tsx │ ├── default-themes.tsx │ ├── errors.tsx │ └── index.tsx ├── main.tsx ├── components │ ├── footer │ │ └── index.tsx │ ├── lazy-image │ │ └── index.tsx │ ├── head-tag-editor │ │ └── index.tsx │ ├── error-page │ │ └── index.tsx │ ├── skill-card │ │ └── index.tsx │ ├── education-card │ │ └── index.tsx │ ├── certification-card │ │ └── index.tsx │ ├── experience-card │ │ └── index.tsx │ ├── avatar-card │ │ └── index.tsx │ ├── theme-changer │ │ └── index.tsx │ ├── publication-card │ │ └── index.tsx │ ├── external-project-card │ │ └── index.tsx │ ├── github-project-card │ │ └── index.tsx │ ├── blog-card │ │ └── index.tsx │ ├── gitprofile.tsx │ └── details-card │ │ └── index.tsx ├── assets │ └── index.css ├── utils │ └── index.tsx └── data │ └── colors.json ├── dist ├── logo.png ├── favicon.ico ├── logo192.png ├── logo512.png ├── favicon-16x16.png ├── favicon-32x32.png ├── robots.txt ├── apple-touch-icon.png ├── registerSW.js ├── manifest.webmanifest ├── sw.js ├── index.html └── workbox-1ab968a5.js ├── public ├── logo.png ├── favicon.ico ├── logo192.png ├── logo512.png ├── favicon-16x16.png ├── favicon-32x32.png ├── robots.txt └── apple-touch-icon.png ├── postcss.config.js ├── .prettierrc ├── tsconfig.node.json ├── .eslintignore ├── .prettierignore ├── .gitignore ├── docker-compose.yml ├── tailwind.config.js ├── .idx └── dev.nix ├── .eslintrc.cjs ├── tsconfig.json ├── vite.config.ts ├── index.html ├── package.json ├── gitprofile.config.ts └── global.d.ts /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /dist/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/dist/logo.png -------------------------------------------------------------------------------- /dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/dist/favicon.ico -------------------------------------------------------------------------------- /dist/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/dist/logo192.png -------------------------------------------------------------------------------- /dist/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/dist/logo512.png -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/public/logo.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/public/logo512.png -------------------------------------------------------------------------------- /dist/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/dist/favicon-16x16.png -------------------------------------------------------------------------------- /dist/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/dist/favicon-32x32.png -------------------------------------------------------------------------------- /dist/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /dist/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/dist/apple-touch-icon.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bots/portfolio/main/public/apple-touch-icon.png -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /dist/registerSW.js: -------------------------------------------------------------------------------- 1 | if('serviceWorker' in navigator) {window.addEventListener('load', () => {navigator.serviceWorker.register('/gitprofile/sw.js', { scope: '/gitprofile/' })})} -------------------------------------------------------------------------------- /src/interfaces/profile.tsx: -------------------------------------------------------------------------------- 1 | export interface Profile { 2 | avatar: string; 3 | name: string; 4 | bio?: string; 5 | location?: string; 6 | company?: string; 7 | } 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "arrowParens": "always", 4 | "bracketSpacing": true, 5 | "printWidth": 80, 6 | "singleQuote": true, 7 | "tabWidth": 2, 8 | "endOfLine": "auto" 9 | } 10 | -------------------------------------------------------------------------------- /src/interfaces/article.tsx: -------------------------------------------------------------------------------- 1 | export interface Article { 2 | title: string; 3 | thumbnail: string; 4 | link: string; 5 | publishedAt: Date; 6 | description: string; 7 | categories: string[]; 8 | } 9 | -------------------------------------------------------------------------------- /src/interfaces/github-project.tsx: -------------------------------------------------------------------------------- 1 | export interface GithubProject { 2 | name: string; 3 | html_url: string; 4 | description: string; 5 | stargazers_count: string; 6 | forks_count: string; 7 | language: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/constants/default-custom-theme.tsx: -------------------------------------------------------------------------------- 1 | export const DEFAULT_CUSTOM_THEME = { 2 | primary: '#fc055b', 3 | secondary: '#219aaf', 4 | accent: '#e8d03a', 5 | neutral: '#2A2730', 6 | 'base-100': '#E3E3ED', 7 | '--rounded-box': '3rem', 8 | '--rounded-btn': '3rem', 9 | }; 10 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["gitprofile.config.ts", "vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /dist/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"Portfolio","short_name":"Portfolio","start_url":"/gitprofile/","display":"standalone","background_color":"#ffffff","lang":"en","scope":"/gitprofile/","description":"Personal Portfolio","icons":[{"src":"logo.png","sizes":"64x64 32x32 24x24 16x16 192x192 512x512","type":"image/png"}]} 2 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import GitProfile from './components/gitprofile.tsx'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root')!).render( 6 | 7 | 8 | , 9 | ); 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | pnpm-debug.log* 7 | lerna-debug.log* 8 | 9 | node_modules 10 | dist 11 | dist-ssr 12 | *.local 13 | 14 | .vscode/* 15 | !.vscode/extensions.json 16 | .idea 17 | .DS_Store 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | pnpm-debug.log* 7 | lerna-debug.log* 8 | 9 | node_modules 10 | dist 11 | dist-ssr 12 | *.local 13 | 14 | .vscode/* 15 | !.vscode/extensions.json 16 | .idea 17 | .DS_Store 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | *.local 12 | 13 | # Editor directories and files 14 | .vscode/* 15 | !.vscode/extensions.json 16 | .idea 17 | .DS_Store 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | app: 4 | build: 5 | context: . 6 | dockerfile: ./node_modules/vail/runtimes/npm/Dockerfile 7 | args: 8 | VAIL_NODE_VERSION: 20 9 | command: npm run dev -- --host 0.0.0.0 10 | ports: 11 | - '5173:5173' 12 | volumes: 13 | - .:/var/www/html 14 | networks: 15 | - vail 16 | networks: 17 | vail: 18 | driver: bridge 19 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | import CONFIG from './gitprofile.config'; 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | export default { 5 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [require('daisyui')], 10 | daisyui: { 11 | logs: false, 12 | themes: [ 13 | ...CONFIG.themeConfig.themes, 14 | { procyon: CONFIG.themeConfig.customTheme }, 15 | ], 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /src/components/footer/index.tsx: -------------------------------------------------------------------------------- 1 | import { skeleton } from '../../utils'; 2 | 3 | const Footer = ({ 4 | content, 5 | loading, 6 | }: { 7 | content: string | null; 8 | loading: boolean; 9 | }) => { 10 | if (!content) return null; 11 | 12 | return ( 13 |
14 | {loading ? ( 15 | skeleton({ widthCls: 'w-52', heightCls: 'h-6' }) 16 | ) : ( 17 |
18 | )} 19 |
20 | ); 21 | }; 22 | 23 | export default Footer; 24 | -------------------------------------------------------------------------------- /.idx/dev.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: { 2 | channel = "stable-23.11"; 3 | packages = [ 4 | pkgs.nodejs_20 5 | ]; 6 | env = {}; 7 | idx = { 8 | extensions = []; 9 | workspace = { 10 | onCreate = { 11 | npm-install = "npm install"; 12 | }; 13 | }; 14 | previews = { 15 | enable = true; 16 | previews = { 17 | web = { 18 | command = ["npm" "run" "dev" "--" "--port" "$PORT" "--host" "0.0.0.0"]; 19 | manager = "web"; 20 | }; 21 | }; 22 | }; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true, node: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | 'plugin:prettier/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parser: '@typescript-eslint/parser', 12 | plugins: ['react-refresh'], 13 | rules: { 14 | 'react-refresh/only-export-components': [ 15 | 'warn', 16 | { allowConstantExport: true }, 17 | ], 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /src/constants/default-themes.tsx: -------------------------------------------------------------------------------- 1 | export const DEFAULT_THEMES = [ 2 | 'light', 3 | 'dark', 4 | 'cupcake', 5 | 'bumblebee', 6 | 'emerald', 7 | 'corporate', 8 | 'synthwave', 9 | 'retro', 10 | 'cyberpunk', 11 | 'valentine', 12 | 'halloween', 13 | 'garden', 14 | 'forest', 15 | 'aqua', 16 | 'lofi', 17 | 'pastel', 18 | 'fantasy', 19 | 'wireframe', 20 | 'black', 21 | 'luxury', 22 | 'dracula', 23 | 'cmyk', 24 | 'autumn', 25 | 'business', 26 | 'acid', 27 | 'lemonade', 28 | 'night', 29 | 'coffee', 30 | 'winter', 31 | 'dim', 32 | 'nord', 33 | 'sunset', 34 | 'procyon', 35 | ]; 36 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src", "global.d.ts"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /dist/sw.js: -------------------------------------------------------------------------------- 1 | if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise((s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()})).then((()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e})));self.define=(n,r)=>{const t=e||("document"in self?document.currentScript.src:"")||location.href;if(s[t])return;let o={};const l=e=>i(e,t),d={module:{uri:t},exports:o,require:l};s[t]=Promise.all(n.map((e=>d[e]||l(e)))).then((e=>(r(...e),o)))}}define(["./workbox-1ab968a5"],(function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"assets/index-Dy_UvuCq.css",revision:null},{url:"assets/index-ZEXjDPVz.js",revision:null},{url:"index.html",revision:"a18b2b78558488d96502132a4497aa15"},{url:"registerSW.js",revision:"10f213b06230203d4df3acd6eb7b6e58"},{url:"logo.png",revision:"81cb75a1fb9a9353c8f406242dd6c00c"},{url:"manifest.webmanifest",revision:"db9c0b8acfa52d3d174518bfb62d02a6"}],{}),e.cleanupOutdatedCaches()})); 2 | -------------------------------------------------------------------------------- /src/components/lazy-image/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, Fragment, useEffect } from 'react'; 2 | 3 | /** 4 | * LazyImage component. 5 | * 6 | * @param {string} placeholder The placeholder image URL. 7 | * @param {string} src The image URL. 8 | * @param {string} alt The alt text for the image. 9 | * @param {object} rest Additional props for the image element. 10 | * 11 | * @returns {ReactElement} The LazyImage component. 12 | */ 13 | const LazyImage: React.FC<{ 14 | placeholder: React.ReactElement; 15 | src: string; 16 | alt: string; 17 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 18 | [key: string]: any; 19 | }> = ({ placeholder, src, alt, ...rest }): React.ReactElement => { 20 | const [loading, setLoading] = useState(true); 21 | 22 | useEffect(() => { 23 | const imageToLoad = new Image(); 24 | imageToLoad.src = src; 25 | 26 | imageToLoad.onload = () => { 27 | setLoading(false); 28 | }; 29 | }, [src]); 30 | 31 | return ( 32 | 33 | {loading ? placeholder : {alt}} 34 | 35 | ); 36 | }; 37 | 38 | export default LazyImage; 39 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | JP Wile
-------------------------------------------------------------------------------- /src/components/head-tag-editor/index.tsx: -------------------------------------------------------------------------------- 1 | import { Helmet } from 'react-helmet-async'; 2 | import { isDarkishTheme } from '../../utils'; 3 | 4 | type HeadTagEditorProps = { 5 | googleAnalyticsId?: string; 6 | appliedTheme: string; 7 | }; 8 | 9 | /** 10 | * Renders the head tag editor component. 11 | * 12 | * @param {HeadTagEditorProps} googleAnalyticsId - The Google Analytics ID. 13 | * @param {HeadTagEditorProps} appliedTheme - The applied theme. 14 | * @return {React.ReactElement} The head tag editor component. 15 | */ 16 | const HeadTagEditor: React.FC = ({ 17 | googleAnalyticsId, 18 | appliedTheme, 19 | }) => { 20 | return ( 21 | 22 | 26 | {googleAnalyticsId && ( 27 | <> 28 | 32 | 41 | 42 | )} 43 | 44 | ); 45 | }; 46 | 47 | export default HeadTagEditor; 48 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react'; 3 | import { VitePWA } from 'vite-plugin-pwa'; 4 | import CONFIG from './gitprofile.config'; 5 | import { createHtmlPlugin } from 'vite-plugin-html'; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | base: CONFIG.base || '/', 10 | plugins: [ 11 | react(), 12 | createHtmlPlugin({ 13 | inject: { 14 | data: { 15 | metaTitle: CONFIG.seo.title, 16 | metaDescription: CONFIG.seo.description, 17 | metaImageURL: CONFIG.seo.imageURL, 18 | }, 19 | }, 20 | }), 21 | ...(CONFIG.enablePWA 22 | ? [ 23 | VitePWA({ 24 | registerType: 'autoUpdate', 25 | workbox: { 26 | navigateFallback: undefined, 27 | }, 28 | includeAssets: ['logo.png'], 29 | manifest: { 30 | name: 'Portfolio', 31 | short_name: 'Portfolio', 32 | description: 'Personal Portfolio', 33 | icons: [ 34 | { 35 | src: 'logo.png', 36 | sizes: '64x64 32x32 24x24 16x16 192x192 512x512', 37 | type: 'image/png', 38 | }, 39 | ], 40 | }, 41 | }), 42 | ] 43 | : []), 44 | ], 45 | define: { 46 | CONFIG: CONFIG, 47 | }, 48 | }); 49 | -------------------------------------------------------------------------------- /src/constants/errors.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react'; 2 | 3 | export interface CustomError { 4 | status: number; 5 | title: string; 6 | subTitle: string | ReactElement; 7 | } 8 | 9 | export const INVALID_CONFIG_ERROR: CustomError = { 10 | status: 500, 11 | title: 'Invalid Config!', 12 | subTitle: ( 13 |

14 | Please provide correct config in gitprofile.config.ts. 15 |

16 | ), 17 | }; 18 | 19 | export const setTooManyRequestError = (resetTime: string): CustomError => { 20 | return { 21 | status: 429, 22 | title: 'Too Many Requests!', 23 | subTitle: ( 24 |

25 | Oh no, you hit the{' '} 26 | 32 | rate limit 33 | 34 | ! Try again later{` ${resetTime}`}. 35 |

36 | ), 37 | }; 38 | }; 39 | 40 | export const INVALID_GITHUB_USERNAME_ERROR: CustomError = { 41 | status: 404, 42 | title: 'Invalid GitHub Username!', 43 | subTitle: ( 44 |

45 | Please provide correct github username in{' '} 46 | gitprofile.config.ts. 47 |

48 | ), 49 | }; 50 | 51 | export const GENERIC_ERROR: CustomError = { 52 | status: 500, 53 | title: 'Oops!!', 54 | subTitle: 'Something went wrong.', 55 | }; 56 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%- metaTitle %> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/components/error-page/index.tsx: -------------------------------------------------------------------------------- 1 | import { CustomError } from '../../constants/errors'; 2 | 3 | /** 4 | * Render the ErrorPage component. 5 | * 6 | * @param props - The props for the ErrorPage component. 7 | * @returns The rendered ErrorPage component. 8 | */ 9 | const ErrorPage: React.FC = (props) => { 10 | return ( 11 |
12 |
13 |
14 |
15 |

16 | {`${props.status}`} 17 |

18 |

{props.title}

19 |
20 | {props.subTitle} 21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | ); 29 | }; 30 | 31 | export default ErrorPage; 32 | -------------------------------------------------------------------------------- /src/components/skill-card/index.tsx: -------------------------------------------------------------------------------- 1 | import { skeleton } from '../../utils'; 2 | 3 | const SkillCard = ({ 4 | loading, 5 | skills, 6 | }: { 7 | loading: boolean; 8 | skills: string[]; 9 | }) => { 10 | const renderSkeleton = () => { 11 | const array = []; 12 | for (let index = 0; index < 12; index++) { 13 | array.push( 14 |
15 | {skeleton({ widthCls: 'w-16', heightCls: 'h-4', className: 'm-1' })} 16 |
, 17 | ); 18 | } 19 | 20 | return array; 21 | }; 22 | 23 | return ( 24 |
25 |
26 |
27 |
28 | {loading ? ( 29 | skeleton({ widthCls: 'w-32', heightCls: 'h-8' }) 30 | ) : ( 31 | Tech Stack 32 | )} 33 |
34 |
35 |
36 |
37 | {loading 38 | ? renderSkeleton() 39 | : skills.map((skill, index) => ( 40 |
44 | {skill} 45 |
46 | ))} 47 |
48 |
49 |
50 |
51 | ); 52 | }; 53 | 54 | export default SkillCard; 55 | -------------------------------------------------------------------------------- /src/assets/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | * { 6 | scrollbar-width: thin; 7 | } 8 | 9 | ::-webkit-scrollbar-track { 10 | box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); 11 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); 12 | -moz-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); 13 | } 14 | 15 | @media screen and (min-width: 966px) { 16 | ::-webkit-scrollbar, 17 | .scroller { 18 | width: 8px; 19 | height: 8px; 20 | background-color: #f1f1f1; 21 | } 22 | } 23 | 24 | ::-webkit-scrollbar-thumb { 25 | background-color: #888; 26 | border-radius: 10px; 27 | } 28 | 29 | body { 30 | margin: 0; 31 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 32 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 33 | sans-serif; 34 | -webkit-font-smoothing: antialiased; 35 | -moz-osx-font-smoothing: grayscale; 36 | } 37 | 38 | code { 39 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 40 | monospace; 41 | } 42 | 43 | .text-base-content-important { 44 | color: hsla(var(--bc) / var(--tw-text-opacity)) !important; 45 | } 46 | 47 | svg { 48 | vertical-align: unset; 49 | } 50 | 51 | .z-hover { 52 | transition: all ease-in-out 0.3s !important; 53 | } 54 | 55 | .z-hover:hover, 56 | .z-hover:focus, 57 | .z-hover:active { 58 | transition: transform 0.3s !important; 59 | -ms-transform: scale(1.01) !important; 60 | -webkit-transform: scale(1.01) !important; 61 | transform: scale(1.01) !important; 62 | } 63 | 64 | .pb-0-important { 65 | padding-bottom: 0 !important; 66 | } 67 | 68 | .fade-in { 69 | opacity: 1; 70 | animation-name: fadeIn; 71 | animation-iteration-count: 1; 72 | animation-timing-function: ease-in; 73 | animation-duration: 1s; 74 | } 75 | 76 | @keyframes fadeIn { 77 | 0% { 78 | opacity: 0; 79 | } 80 | 100% { 81 | opacity: 1; 82 | } 83 | } 84 | 85 | @-webkit-keyframes fadeIn { 86 | from { 87 | opacity: 0; 88 | } 89 | to { 90 | opacity: 1; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bots/portfolio", 3 | "description": "Automatic portfolio based on GitHub profile", 4 | "version": "1.0", 5 | "type": "module", 6 | "license": "MIT", 7 | "author": "bots", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/bots/portfolio.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/bots/portfolio/issues" 14 | }, 15 | "scripts": { 16 | "dev": "vite", 17 | "build": "tsc && vite build", 18 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 19 | "lint:fix": "eslint . --fix --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 20 | "prettier": "prettier --check \"./**/*.{js,jsx,ts,tsx,css,md,json}\"", 21 | "prettier:fix": "prettier --write \"./**/*.{js,jsx,ts,tsx,css,md,json}\"", 22 | "preview": "vite preview" 23 | }, 24 | "dependencies": { 25 | "react": "^18.3.1", 26 | "react-dom": "^18.3.1" 27 | }, 28 | "devDependencies": { 29 | "@arifszn/blog-js": "^2.0.6", 30 | "@types/react": "^18.3.3", 31 | "@types/react-dom": "^18.3.0", 32 | "@typescript-eslint/eslint-plugin": "^7.8.0", 33 | "@typescript-eslint/parser": "^7.3.1", 34 | "@vitejs/plugin-react": "^4.2.1", 35 | "autoprefixer": "^10.4.19", 36 | "axios": "^1.7.2", 37 | "daisyui": "^4.10.5", 38 | "date-fns": "^3.6.0", 39 | "eslint": "^8.57.0", 40 | "eslint-config-prettier": "^9.1.0", 41 | "eslint-plugin-prettier": "^5.1.3", 42 | "eslint-plugin-react-hooks": "^4.6.0", 43 | "eslint-plugin-react-refresh": "^0.4.7", 44 | "postcss": "^8.4.38", 45 | "prettier": "^3.2.5", 46 | "react-helmet-async": "^2.0.5", 47 | "react-hotjar": "^6.3.1", 48 | "react-icons": "^5.2.1", 49 | "tailwindcss": "^3.4.3", 50 | "typescript": "^5.4.5", 51 | "vail": "^1.0.3", 52 | "vite": "^5.2.11", 53 | "vite-plugin-html": "^3.2.2", 54 | "vite-plugin-pwa": "^0.20.0" 55 | }, 56 | "keywords": [ 57 | "git-profile", 58 | "gitprofile", 59 | "gitportfolio", 60 | "personal-site", 61 | "template", 62 | "portfolio", 63 | "resume", 64 | "cv", 65 | "personal-website", 66 | "portfolio-website", 67 | "portfolio-site", 68 | "portfolio-template", 69 | "portfolio-page", 70 | "developer-portfolio", 71 | "portfolio-project", 72 | "github-portfolio", 73 | "tailwind-portfolio", 74 | "vite-portfolio", 75 | "projects", 76 | "open-source", 77 | "git", 78 | "react-portfolio", 79 | "github", 80 | "github-page", 81 | "github-pages", 82 | "github-portfolio", 83 | "vite-portfolio", 84 | "academic-portfolio", 85 | "github-api" 86 | ] 87 | } 88 | -------------------------------------------------------------------------------- /src/components/education-card/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SanitizedEducation } from '../../interfaces/sanitized-config'; 3 | import { skeleton } from '../../utils'; 4 | 5 | const ListItem = ({ 6 | time, 7 | degree, 8 | institution, 9 | }: { 10 | time: React.ReactNode; 11 | degree?: React.ReactNode; 12 | institution?: React.ReactNode; 13 | }) => ( 14 |
  • 15 |
    19 |
    {time}
    20 |

    {degree}

    21 |
    {institution}
    22 |
  • 23 | ); 24 | 25 | const EducationCard = ({ 26 | loading, 27 | educations, 28 | }: { 29 | loading: boolean; 30 | educations: SanitizedEducation[]; 31 | }) => { 32 | const renderSkeleton = () => { 33 | const array = []; 34 | for (let index = 0; index < 2; index++) { 35 | array.push( 36 | , 49 | ); 50 | } 51 | 52 | return array; 53 | }; 54 | 55 | return ( 56 |
    57 |
    58 |
    59 |
    60 | {loading ? ( 61 | skeleton({ widthCls: 'w-32', heightCls: 'h-8' }) 62 | ) : ( 63 | Education 64 | )} 65 |
    66 |
    67 |
    68 |
      69 | {loading ? ( 70 | renderSkeleton() 71 | ) : ( 72 | <> 73 | {educations.map((item, index) => ( 74 | 80 | ))} 81 | 82 | )} 83 |
    84 |
    85 |
    86 |
    87 | ); 88 | }; 89 | 90 | export default EducationCard; 91 | -------------------------------------------------------------------------------- /src/components/certification-card/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SanitizedCertification } from '../../interfaces/sanitized-config'; 3 | import { skeleton } from '../../utils'; 4 | 5 | const ListItem = ({ 6 | year, 7 | name, 8 | body, 9 | link, 10 | }: { 11 | year?: React.ReactNode; 12 | name?: React.ReactNode; 13 | body?: React.ReactNode; 14 | link?: string; 15 | }) => ( 16 |
  • 17 |
    21 |
    {year}
    22 | 27 |

    {body}

    28 |
  • 29 | ); 30 | 31 | const CertificationCard = ({ 32 | certifications, 33 | loading, 34 | }: { 35 | certifications: SanitizedCertification[]; 36 | loading: boolean; 37 | }) => { 38 | const renderSkeleton = () => { 39 | const array = []; 40 | for (let index = 0; index < 2; index++) { 41 | array.push( 42 | , 55 | ); 56 | } 57 | 58 | return array; 59 | }; 60 | 61 | return ( 62 |
    63 |
    64 |
    65 |
    66 | {loading ? ( 67 | skeleton({ widthCls: 'w-32', heightCls: 'h-8' }) 68 | ) : ( 69 | 70 | Certification 71 | 72 | )} 73 |
    74 |
    75 |
    76 |
      77 | {loading ? ( 78 | renderSkeleton() 79 | ) : ( 80 | <> 81 | {certifications.map((certification, index) => ( 82 | 89 | ))} 90 | 91 | )} 92 |
    93 |
    94 |
    95 |
    96 | ); 97 | }; 98 | 99 | export default CertificationCard; 100 | -------------------------------------------------------------------------------- /src/components/experience-card/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { SanitizedExperience } from '../../interfaces/sanitized-config'; 3 | import { skeleton } from '../../utils'; 4 | 5 | const ListItem = ({ 6 | time, 7 | position, 8 | company, 9 | companyLink, 10 | }: { 11 | time: React.ReactNode; 12 | position?: React.ReactNode; 13 | company?: React.ReactNode; 14 | companyLink?: string; 15 | }) => ( 16 |
  • 17 |
    21 |
    {time}
    22 |

    {position}

    23 | 28 |
  • 29 | ); 30 | 31 | const ExperienceCard = ({ 32 | experiences, 33 | loading, 34 | }: { 35 | experiences: SanitizedExperience[]; 36 | loading: boolean; 37 | }) => { 38 | const renderSkeleton = () => { 39 | const array = []; 40 | for (let index = 0; index < 2; index++) { 41 | array.push( 42 | , 55 | ); 56 | } 57 | 58 | return array; 59 | }; 60 | return ( 61 |
    62 |
    63 |
    64 |
    65 | {loading ? ( 66 | skeleton({ widthCls: 'w-32', heightCls: 'h-8' }) 67 | ) : ( 68 | Experience 69 | )} 70 |
    71 |
    72 |
    73 |
      74 | {loading ? ( 75 | renderSkeleton() 76 | ) : ( 77 | 78 | {experiences.map((experience, index) => ( 79 | 90 | ))} 91 | 92 | )} 93 |
    94 |
    95 |
    96 |
    97 | ); 98 | }; 99 | 100 | export default ExperienceCard; 101 | -------------------------------------------------------------------------------- /src/constants/index.tsx: -------------------------------------------------------------------------------- 1 | export const LOCAL_STORAGE_KEY_NAME = 'gitprofile-theme'; 2 | 3 | export const BG_COLOR = 'bg-base-300'; 4 | 5 | export const FALLBACK_IMAGE = 6 | 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=='; 7 | -------------------------------------------------------------------------------- /src/components/avatar-card/index.tsx: -------------------------------------------------------------------------------- 1 | import { FALLBACK_IMAGE } from '../../constants'; 2 | import { Profile } from '../../interfaces/profile'; 3 | import { skeleton } from '../../utils'; 4 | import LazyImage from '../lazy-image'; 5 | 6 | interface AvatarCardProps { 7 | profile: Profile | null; 8 | loading: boolean; 9 | avatarRing: boolean; 10 | resumeFileUrl?: string; 11 | } 12 | 13 | /** 14 | * Renders an AvatarCard component. 15 | * @param profile - The profile object. 16 | * @param loading - A boolean indicating if the profile is loading. 17 | * @param avatarRing - A boolean indicating if the avatar should have a ring. 18 | * @param resumeFileUrl - The URL of the resume file. 19 | * @returns JSX element representing the AvatarCard. 20 | */ 21 | const AvatarCard: React.FC = ({ 22 | profile, 23 | loading, 24 | avatarRing, 25 | resumeFileUrl, 26 | }): JSX.Element => { 27 | return ( 28 |
    29 |
    30 | {loading || !profile ? ( 31 |
    32 |
    33 | {skeleton({ 34 | widthCls: 'w-full', 35 | heightCls: 'h-full', 36 | shape: '', 37 | })} 38 |
    39 |
    40 | ) : ( 41 |
    42 |
    49 | { 50 | 59 | } 60 |
    61 |
    62 | )} 63 |
    64 |
    65 | {loading || !profile ? ( 66 | skeleton({ widthCls: 'w-48', heightCls: 'h-8' }) 67 | ) : ( 68 | 69 | {profile.name} 70 |
    71 | (BotsOne) 72 |
    73 | )} 74 |
    75 |
    76 | {loading || !profile 77 | ? skeleton({ widthCls: 'w-48', heightCls: 'h-5' }) 78 | : profile.bio} 79 |
    80 |
    81 | {resumeFileUrl && 82 | (loading ? ( 83 |
    84 | {skeleton({ widthCls: 'w-40', heightCls: 'h-8' })} 85 |
    86 | ) : ( 87 | 94 | Download Resume 95 | 96 | ))} 97 |
    98 |
    99 | ); 100 | }; 101 | 102 | export default AvatarCard; 103 | -------------------------------------------------------------------------------- /src/interfaces/sanitized-config.tsx: -------------------------------------------------------------------------------- 1 | export interface SanitizedGithub { 2 | username: string; 3 | } 4 | 5 | export interface SanitizedGitHubProjects { 6 | display: boolean; 7 | header: string; 8 | mode: string; 9 | automatic: { 10 | sortBy: string; 11 | limit: number; 12 | exclude: { 13 | forks: boolean; 14 | projects: Array; 15 | }; 16 | }; 17 | manual: { 18 | projects: Array; 19 | }; 20 | } 21 | 22 | export interface SanitizedExternalProject { 23 | title: string; 24 | description?: string; 25 | imageUrl?: string; 26 | link: string; 27 | } 28 | 29 | export interface SanitizedExternalProjects { 30 | header: string; 31 | projects: SanitizedExternalProject[]; 32 | } 33 | 34 | export interface SanitizedProjects { 35 | github: SanitizedGitHubProjects; 36 | external: SanitizedExternalProjects; 37 | } 38 | 39 | export interface SanitizedSEO { 40 | title?: string; 41 | description?: string; 42 | imageURL?: string; 43 | } 44 | 45 | export interface SanitizedSocial { 46 | linkedin?: string; 47 | twitter?: string; 48 | mastodon?: string; 49 | researchGate?: string; 50 | facebook?: string; 51 | instagram?: string; 52 | reddit?: string; 53 | threads?: string; 54 | youtube?: string; 55 | udemy?: string; 56 | dribbble?: string; 57 | behance?: string; 58 | medium?: string; 59 | dev?: string; 60 | stackoverflow?: string; 61 | website?: string; 62 | skype?: string; 63 | telegram?: string; 64 | phone?: string; 65 | email?: string; 66 | } 67 | 68 | export interface SanitizedResume { 69 | fileUrl?: string; 70 | } 71 | 72 | export interface SanitizedExperience { 73 | company?: string; 74 | position?: string; 75 | from: string; 76 | to: string; 77 | companyLink?: string; 78 | } 79 | 80 | export interface SanitizedCertification { 81 | body?: string; 82 | name?: string; 83 | year?: string; 84 | link?: string; 85 | } 86 | 87 | export interface SanitizedEducation { 88 | institution?: string; 89 | degree?: string; 90 | from: string; 91 | to: string; 92 | } 93 | 94 | export interface SanitizedPublication { 95 | title: string; 96 | conferenceName?: string; 97 | journalName?: string; 98 | authors?: string; 99 | link?: string; 100 | description?: string; 101 | } 102 | 103 | export interface SanitizedGoogleAnalytics { 104 | id?: string; 105 | } 106 | 107 | export interface SanitizedHotjar { 108 | id?: string; 109 | snippetVersion: number; 110 | } 111 | 112 | export interface SanitizedBlog { 113 | display: boolean; 114 | source: string; 115 | username: string; 116 | limit: number; 117 | } 118 | 119 | export interface SanitizedCustomTheme { 120 | primary: string; 121 | secondary: string; 122 | accent: string; 123 | neutral: string; 124 | 'base-100': string; 125 | '--rounded-box': string; 126 | '--rounded-btn': string; 127 | } 128 | 129 | export interface SanitizedThemeConfig { 130 | defaultTheme: string; 131 | disableSwitch: boolean; 132 | respectPrefersColorScheme: boolean; 133 | displayAvatarRing: boolean; 134 | themes: Array; 135 | customTheme: SanitizedCustomTheme; 136 | } 137 | 138 | export interface SanitizedConfig { 139 | github: SanitizedGithub; 140 | projects: SanitizedProjects; 141 | seo: SanitizedSEO; 142 | social: SanitizedSocial; 143 | resume: SanitizedResume; 144 | skills: Array; 145 | experiences: Array; 146 | educations: Array; 147 | certifications: Array; 148 | publications: Array; 149 | googleAnalytics: SanitizedGoogleAnalytics; 150 | hotjar: SanitizedHotjar; 151 | blog: SanitizedBlog; 152 | themeConfig: SanitizedThemeConfig; 153 | footer?: string; 154 | enablePWA: boolean; 155 | } 156 | -------------------------------------------------------------------------------- /src/components/theme-changer/index.tsx: -------------------------------------------------------------------------------- 1 | import { AiOutlineControl } from 'react-icons/ai'; 2 | import { SanitizedThemeConfig } from '../../interfaces/sanitized-config'; 3 | import { LOCAL_STORAGE_KEY_NAME } from '../../constants'; 4 | import { skeleton } from '../../utils'; 5 | import { MouseEvent } from 'react'; 6 | 7 | /** 8 | * Renders a theme changer component. 9 | * 10 | * @param {Object} props - The props object. 11 | * @param {string} props.theme - The current theme. 12 | * @param {function} props.setTheme - A function to set the theme. 13 | * @param {boolean} props.loading - Whether the component is in a loading state. 14 | * @param {SanitizedThemeConfig} props.themeConfig - The theme configuration object. 15 | * @return {JSX.Element} The rendered theme changer component. 16 | */ 17 | const ThemeChanger = ({ 18 | theme, 19 | setTheme, 20 | loading, 21 | themeConfig, 22 | }: { 23 | theme: string; 24 | setTheme: (theme: string) => void; 25 | loading: boolean; 26 | themeConfig: SanitizedThemeConfig; 27 | }) => { 28 | const changeTheme = ( 29 | e: MouseEvent, 30 | selectedTheme: string, 31 | ) => { 32 | e.preventDefault(); 33 | 34 | document.querySelector('html')?.setAttribute('data-theme', selectedTheme); 35 | 36 | typeof window !== 'undefined' && 37 | localStorage.setItem(LOCAL_STORAGE_KEY_NAME, selectedTheme); 38 | 39 | setTheme(selectedTheme); 40 | }; 41 | 42 | return ( 43 |
    44 |
    45 |
    46 |
    47 | {loading ? ( 48 | skeleton({ 49 | widthCls: 'w-20', 50 | heightCls: 'h-8', 51 | className: 'mb-1', 52 | }) 53 | ) : ( 54 | Theme 55 | )} 56 |
    57 | 58 | {loading 59 | ? skeleton({ widthCls: 'w-16', heightCls: 'h-5' }) 60 | : theme === themeConfig.defaultTheme 61 | ? 'Default' 62 | : theme} 63 | 64 |
    65 |
    66 | {loading ? ( 67 | skeleton({ 68 | widthCls: 'w-14 md:w-28', 69 | heightCls: 'h-10', 70 | className: 'mr-6', 71 | }) 72 | ) : ( 73 |
    74 |
    78 | 79 | Change Theme 80 | 85 | 86 | 87 |
    88 |
    92 | 112 |
    113 |
    114 | )} 115 |
    116 |
    117 |
    118 | ); 119 | }; 120 | 121 | export default ThemeChanger; 122 | -------------------------------------------------------------------------------- /src/components/publication-card/index.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'react'; 2 | import { SanitizedPublication } from '../../interfaces/sanitized-config'; 3 | import { skeleton } from '../../utils'; 4 | 5 | const PublicationCard = ({ 6 | publications, 7 | loading, 8 | }: { 9 | publications: SanitizedPublication[]; 10 | loading: boolean; 11 | }) => { 12 | const renderSkeleton = () => { 13 | const array = []; 14 | for (let index = 0; index < publications.length; index++) { 15 | array.push( 16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 |

    23 | {skeleton({ 24 | widthCls: 'w-32', 25 | heightCls: 'h-8', 26 | className: 'mb-2 mx-auto', 27 | })} 28 |

    29 |
    30 | {skeleton({ 31 | widthCls: 'w-20', 32 | heightCls: 'h-4', 33 | className: 'mb-2 mx-auto', 34 | })} 35 |
    36 |
    37 | {skeleton({ 38 | widthCls: 'w-20', 39 | heightCls: 'h-4', 40 | className: 'mb-2 mx-auto', 41 | })} 42 |
    43 |
    44 | {skeleton({ 45 | widthCls: 'w-full', 46 | heightCls: 'h-4', 47 | className: 'mb-2 mx-auto', 48 | })} 49 |
    50 |
    51 | {skeleton({ 52 | widthCls: 'w-full', 53 | heightCls: 'h-4', 54 | className: 'mb-2 mx-auto', 55 | })} 56 |
    57 |
    58 | {skeleton({ 59 | widthCls: 'w-full', 60 | heightCls: 'h-4', 61 | className: 'mb-2 mx-auto', 62 | })} 63 |
    64 |
    65 |
    66 |
    67 |
    68 |
    69 |
    , 70 | ); 71 | } 72 | 73 | return array; 74 | }; 75 | 76 | const renderPublications = () => { 77 | return publications.map((item, index) => ( 78 | 85 |
    86 |
    87 |
    88 |
    89 |
    90 |

    {item.title}

    91 | {item.conferenceName && ( 92 |

    93 | {item.conferenceName} 94 |

    95 | )} 96 | {item.journalName && ( 97 |

    98 | {item.journalName} 99 |

    100 | )} 101 | {item.authors && ( 102 |

    103 | Author: {item.authors} 104 |

    105 | )} 106 | {item.description && ( 107 |

    108 | {item.description} 109 |

    110 | )} 111 |
    112 |
    113 |
    114 |
    115 |
    116 |
    117 | )); 118 | }; 119 | 120 | return ( 121 | 122 |
    123 |
    124 |
    125 |
    126 |
    127 |
    128 |
    129 | {loading ? ( 130 | skeleton({ widthCls: 'w-40', heightCls: 'h-8' }) 131 | ) : ( 132 | 133 | Publications 134 | 135 | )} 136 |
    137 |
    138 |
    139 |
    140 | {loading ? renderSkeleton() : renderPublications()} 141 |
    142 |
    143 |
    144 |
    145 |
    146 |
    147 |
    148 |
    149 | ); 150 | }; 151 | 152 | export default PublicationCard; 153 | -------------------------------------------------------------------------------- /src/components/external-project-card/index.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'react'; 2 | import LazyImage from '../lazy-image'; 3 | import { ga, skeleton } from '../../utils'; 4 | import { SanitizedExternalProject } from '../../interfaces/sanitized-config'; 5 | 6 | const ExternalProjectCard = ({ 7 | externalProjects, 8 | header, 9 | loading, 10 | googleAnalyticId, 11 | }: { 12 | externalProjects: SanitizedExternalProject[]; 13 | header: string; 14 | loading: boolean; 15 | googleAnalyticId?: string; 16 | }) => { 17 | const renderSkeleton = () => { 18 | const array = []; 19 | for (let index = 0; index < externalProjects.length; index++) { 20 | array.push( 21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |

    28 | {skeleton({ 29 | widthCls: 'w-32', 30 | heightCls: 'h-8', 31 | className: 'mb-2 mx-auto', 32 | })} 33 |

    34 |
    35 |
    36 | {skeleton({ 37 | widthCls: 'w-full', 38 | heightCls: 'h-full', 39 | shape: '', 40 | })} 41 |
    42 |
    43 |
    44 | {skeleton({ 45 | widthCls: 'w-full', 46 | heightCls: 'h-4', 47 | className: 'mx-auto', 48 | })} 49 |
    50 |
    51 | {skeleton({ 52 | widthCls: 'w-full', 53 | heightCls: 'h-4', 54 | className: 'mx-auto', 55 | })} 56 |
    57 |
    58 |
    59 |
    60 |
    61 |
    62 |
    , 63 | ); 64 | } 65 | 66 | return array; 67 | }; 68 | 69 | const renderExternalProjects = () => { 70 | return externalProjects.map((item, index) => ( 71 | { 76 | e.preventDefault(); 77 | 78 | try { 79 | if (googleAnalyticId) { 80 | ga.event('Click External Project', { 81 | post: item.title, 82 | }); 83 | } 84 | } catch (error) { 85 | console.error(error); 86 | } 87 | 88 | window?.open(item.link, '_blank'); 89 | }} 90 | > 91 |
    92 |
    93 |
    94 |
    95 |
    96 |

    97 | {item.title} 98 |

    99 | {item.imageUrl && ( 100 |
    101 |
    102 | 111 |
    112 |
    113 | )} 114 |

    115 | {item.description} 116 |

    117 |
    118 |
    119 |
    120 |
    121 |
    122 |
    123 | )); 124 | }; 125 | 126 | return ( 127 | 128 |
    129 |
    130 |
    131 |
    132 |
    133 |
    134 |
    135 | {loading ? ( 136 | skeleton({ widthCls: 'w-40', heightCls: 'h-8' }) 137 | ) : ( 138 | 139 | {header} 140 | 141 | )} 142 |
    143 |
    144 |
    145 |
    146 | {loading ? renderSkeleton() : renderExternalProjects()} 147 |
    148 |
    149 |
    150 |
    151 |
    152 |
    153 |
    154 |
    155 | ); 156 | }; 157 | 158 | export default ExternalProjectCard; 159 | -------------------------------------------------------------------------------- /src/components/github-project-card/index.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'react'; 2 | import { AiOutlineFork, AiOutlineStar } from 'react-icons/ai'; 3 | import { MdInsertLink } from 'react-icons/md'; 4 | import { ga, getLanguageColor, skeleton } from '../../utils'; 5 | import { GithubProject } from '../../interfaces/github-project'; 6 | 7 | const GithubProjectCard = ({ 8 | header, 9 | githubProjects, 10 | loading, 11 | limit, 12 | username, 13 | googleAnalyticsId, 14 | }: { 15 | header: string; 16 | githubProjects: GithubProject[]; 17 | loading: boolean; 18 | limit: number; 19 | username: string; 20 | googleAnalyticsId?: string; 21 | }) => { 22 | if (!loading && githubProjects.length === 0) { 23 | return; 24 | } 25 | 26 | const renderSkeleton = () => { 27 | const array = []; 28 | for (let index = 0; index < limit; index++) { 29 | array.push( 30 |
    31 |
    32 |
    33 |
    34 | 35 |
    36 | {skeleton({ 37 | widthCls: 'w-32', 38 | heightCls: 'h-8', 39 | className: 'mb-1', 40 | })} 41 |
    42 |
    43 |
    44 |
    45 | {skeleton({ 46 | widthCls: 'w-full', 47 | heightCls: 'h-4', 48 | className: 'mb-2', 49 | })} 50 | {skeleton({ widthCls: 'w-full', heightCls: 'h-4' })} 51 |
    52 |
    53 |
    54 |
    55 | 56 | {skeleton({ widthCls: 'w-12', heightCls: 'h-4' })} 57 | 58 | 59 | {skeleton({ widthCls: 'w-12', heightCls: 'h-4' })} 60 | 61 |
    62 |
    63 | 64 | {skeleton({ widthCls: 'w-12', heightCls: 'h-4' })} 65 | 66 |
    67 |
    68 |
    69 |
    , 70 | ); 71 | } 72 | 73 | return array; 74 | }; 75 | 76 | const renderProjects = () => { 77 | return githubProjects.map((item, index) => ( 78 | { 83 | e.preventDefault(); 84 | 85 | try { 86 | if (googleAnalyticsId) { 87 | ga.event('Click project', { 88 | project: item.name, 89 | }); 90 | } 91 | } catch (error) { 92 | console.error(error); 93 | } 94 | 95 | window?.open(item.html_url, '_blank'); 96 | }} 97 | > 98 |
    99 |
    100 |
    101 |
    102 | 103 | {item.name} 104 |
    105 |
    106 |

    107 | {item.description} 108 |

    109 |
    110 |
    111 |
    112 | 113 | 114 | {item.stargazers_count} 115 | 116 | 117 | 118 | {item.forks_count} 119 | 120 |
    121 |
    122 | 123 |
    127 | {item.language} 128 | 129 |
    130 |
    131 |
    132 |
    133 | )); 134 | }; 135 | 136 | return ( 137 | 138 |
    139 |
    140 |
    141 |
    142 |
    143 |
    144 |
    145 | {loading ? ( 146 | skeleton({ widthCls: 'w-40', heightCls: 'h-8' }) 147 | ) : ( 148 | 149 | {header} 150 | 151 | )} 152 |
    153 | {loading ? ( 154 | skeleton({ widthCls: 'w-10', heightCls: 'h-5' }) 155 | ) : ( 156 | 162 | See All 163 | 164 | )} 165 |
    166 |
    167 |
    168 | {loading ? renderSkeleton() : renderProjects()} 169 |
    170 |
    171 |
    172 |
    173 |
    174 |
    175 |
    176 |
    177 | ); 178 | }; 179 | 180 | export default GithubProjectCard; 181 | -------------------------------------------------------------------------------- /gitprofile.config.ts: -------------------------------------------------------------------------------- 1 | // gitprofile.config.ts 2 | 3 | const CONFIG = { 4 | github: { 5 | username: 'bots', // Your GitHub org/user name. (This is the only required config) 6 | }, 7 | /** 8 | * If you are deploying to https://.github.io/, for example your repository is at https://github.com/arifszn/arifszn.github.io, set base to '/'. 9 | * If you are deploying to https://.github.io//, 10 | * for example your repository is at https://github.com/arifszn/portfolio, then set base to '/portfolio/'. 11 | */ 12 | base: '/', 13 | projects: { 14 | github: { 15 | display: true, // Display GitHub projects? 16 | header: 'Recent Github Projects', 17 | mode: 'automatic', // Mode can be: 'automatic' or 'manual' 18 | automatic: { 19 | sortBy: 'updated', // Sort projects by 'stars' or 'updated' 20 | limit: 16, // How many projects to display. 21 | exclude: { 22 | forks: true, // Forked projects will not be displayed if set to true. 23 | projects: ['bots/RMSTrial'], // These projects will not be displayed. example: ['arifszn/my-project1', 'arifszn/my-project2'] 24 | }, 25 | }, 26 | manual: { 27 | // Properties for manually specifying projects 28 | projects: [], // List of repository names to display. example: ['arifszn/my-project1', 'arifszn/my-project2'] 29 | }, 30 | }, 31 | external: { 32 | header: 'My Projects', 33 | // To hide the `External Projects` section, keep it empty. 34 | projects: [ 35 | // { 36 | // title: 'Project Name', 37 | // description: 38 | // 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, nunc ut.', 39 | // imageUrl: 40 | // 'https://img.freepik.com/free-vector/illustration-gallery-icon_53876-27002.jpg', 41 | // link: 'https://example.com', 42 | // }, 43 | // { 44 | // title: 'Project Name', 45 | // description: 46 | // 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, nunc ut.', 47 | // imageUrl: 48 | // 'https://img.freepik.com/free-vector/illustration-gallery-icon_53876-27002.jpg', 49 | // link: 'https://example.com', 50 | // }, 51 | ], 52 | }, 53 | }, 54 | seo: { 55 | title: 'BotsOne', 56 | description: "John Paul Wile's Portfolio", 57 | imageURL: '', 58 | }, 59 | social: { 60 | linkedin: 'John Paul Wile', 61 | twitter: '@JohnPaulWile', 62 | mastodon: 'botsone@mastodon.social', 63 | facebook: 'John Paul Wile', 64 | instagram: '@botsone', 65 | reddit: 'JPWile', 66 | threads: '@botsone', 67 | youtube: 'botsone', 68 | medium: 'John Paul Wile', 69 | stackoverflow: 'JP Wile', 70 | website: 'https://www.botsone.net', 71 | email: 'botsone@gmail.com', 72 | }, 73 | resume: { 74 | fileUrl: 75 | 'https://docs.google.com/file/d/19u_Diu16u7I5Of_vlpJMaZ6MSCdjEBKBWmneVCMxEzM/edit?usp=sharing', // Empty fileUrl will hide the `Download Resume` button. 76 | }, 77 | skills: [ 78 | 'JavaScript', 79 | 'TypeScript', 80 | 'Java', 81 | 'ReactJS', 82 | 'NextJS', 83 | 'NodeJS', 84 | 'Vue', 85 | 'MySQL', 86 | 'PostgreSQL', 87 | 'Git', 88 | 'Docker', 89 | 'Shadcn', 90 | 'CSS', 91 | 'Antd', 92 | 'Tailwind', 93 | ], 94 | experiences: [ 95 | { 96 | company: 'Forward Flow', 97 | position: 'Tech Lead', 98 | from: 'January, 2024', 99 | to: 'Present', 100 | companyLink: 'https://forward-flow.com', 101 | }, 102 | { 103 | company: 'Red Mountain Scientific', 104 | position: 'Full Stack Developer', 105 | from: 'July 2019', 106 | to: 'August 2022', 107 | companyLink: 'https://redmtnsci.com', 108 | }, 109 | ], 110 | publications: [ 111 | { 112 | title: 'How to tune your subwoofer and monitors correctly', 113 | conferenceName: '', 114 | journalName: 'SubStack', 115 | authors: 'John Paul Wile', 116 | link: 'https://johnpaulwile.substack.com/p/how-to-tune-your-subwoofer-and-monitors?utm_source=profile&utm_medium=reader2', 117 | description: 118 | 'A short article to help with tuning your studio subwoofer with your studio monitors.', 119 | }, 120 | { 121 | title: "Why are we looking for a 'flat' signal in music production?", 122 | conferenceName: '', 123 | journalName: 'SubStack', 124 | authors: 'John Paul Wile', 125 | link: 'https://johnpaulwile.substack.com/p/why-are-we-looking-for-a-flat-signal?utm_source=profile&utm_medium=reader2', 126 | description: 127 | 'An article explaining why producers and engineers are looking for a flat speaker response curve in their studio.', 128 | }, 129 | ], 130 | // Display articles from your medium or dev account. (Optional) 131 | // blog: { 132 | // source: 'dev', // medium | dev 133 | // username: 'botsone', // to hide blog section, keep it empty 134 | // limit: 2, // How many articles to display. Max is 10. 135 | // }, 136 | googleAnalytics: { 137 | id: '', // GA3 tracking id/GA4 tag id UA-XXXXXXXXX-X | G-XXXXXXXXXX 138 | }, 139 | // Track visitor interaction and behavior. https://www.hotjar.com 140 | hotjar: { 141 | id: '', 142 | snippetVersion: 6, 143 | }, 144 | themeConfig: { 145 | defaultTheme: 'synthwave', 146 | 147 | // Hides the switch in the navbar 148 | // Useful if you want to support a single color mode 149 | disableSwitch: true, 150 | 151 | // Should use the prefers-color-scheme media-query, 152 | // using user system preferences, instead of the hardcoded defaultTheme 153 | respectPrefersColorScheme: false, 154 | 155 | // Display the ring in Profile picture 156 | displayAvatarRing: true, 157 | 158 | // Available themes. To remove any theme, exclude from here. 159 | themes: [ 160 | 'light', 161 | 'dark', 162 | 'cupcake', 163 | 'bumblebee', 164 | 'emerald', 165 | 'corporate', 166 | 'synthwave', 167 | 'retro', 168 | 'cyberpunk', 169 | 'valentine', 170 | 'halloween', 171 | 'garden', 172 | 'forest', 173 | 'aqua', 174 | 'lofi', 175 | 'pastel', 176 | 'fantasy', 177 | 'wireframe', 178 | 'black', 179 | 'luxury', 180 | 'dracula', 181 | 'cmyk', 182 | 'autumn', 183 | 'business', 184 | 'acid', 185 | 'lemonade', 186 | 'night', 187 | 'coffee', 188 | 'winter', 189 | 'dim', 190 | 'nord', 191 | 'sunset', 192 | 'procyon', 193 | ], 194 | 195 | // Custom theme, applied to `procyon` theme 196 | customTheme: { 197 | primary: '#fc055b', 198 | secondary: '#219aaf', 199 | accent: '#e8d03a', 200 | neutral: '#2A2730', 201 | 'base-100': '#E3E3ED', 202 | '--rounded-box': '3rem', 203 | '--rounded-btn': '3rem', 204 | }, 205 | }, 206 | 207 | // Optional Footer. Supports plain text or HTML. 208 | footer: `Made by John Paul Wile with ❤️`, 209 | 210 | enablePWA: true, 211 | }; 212 | 213 | export default CONFIG; 214 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | interface Github { 2 | /** 3 | * GitHub org/user name 4 | */ 5 | username: string; 6 | } 7 | 8 | interface GitHubProjects { 9 | /** 10 | * Display GitHub projects? 11 | */ 12 | display?: boolean; 13 | 14 | /** 15 | * Header 16 | */ 17 | header?: string; 18 | 19 | /** 20 | * 'automatic' | 'manual' 21 | */ 22 | mode?: string; 23 | 24 | /** 25 | * Config of automatic mode 26 | */ 27 | automatic?: { 28 | /** 29 | * 'stars' | 'updated' 30 | */ 31 | sortBy?: string; 32 | 33 | /** 34 | * How many projects to display 35 | */ 36 | limit?: number; 37 | 38 | /** 39 | * Exclude projects option 40 | */ 41 | exclude?: { 42 | /** 43 | * Forked projects will not be displayed if set to true 44 | */ 45 | forks?: boolean; 46 | 47 | /** 48 | * These projects will not be displayed 49 | * 50 | * example: ['my-project1', 'my-project2'] 51 | */ 52 | projects?: Array; 53 | }; 54 | }; 55 | 56 | /** 57 | * Config of manual mode 58 | */ 59 | manual?: { 60 | /** 61 | * These projects will be displayed 62 | * 63 | * example: ['my-project1', 'my-project2'] 64 | */ 65 | projects?: Array; 66 | }; 67 | } 68 | 69 | interface ExternalProjects { 70 | /** 71 | * Header 72 | */ 73 | header?: string; 74 | 75 | /** 76 | * Project list 77 | */ 78 | projects?: { 79 | title: string; 80 | description?: string; 81 | imageUrl?: string; 82 | link: string; 83 | }[]; 84 | } 85 | 86 | interface Projects { 87 | github?: GitHubProjects; 88 | 89 | external?: ExternalProjects; 90 | } 91 | 92 | interface SEO { 93 | /** 94 | * Meta title 95 | */ 96 | title?: string; 97 | 98 | /** 99 | * Meta description 100 | */ 101 | description?: string; 102 | 103 | /** 104 | * Meta image 105 | */ 106 | imageURL?: string; 107 | } 108 | 109 | interface Social { 110 | /** 111 | * LinkedIn 112 | */ 113 | linkedin?: string; 114 | 115 | /** 116 | * Twitter 117 | */ 118 | twitter?: string; 119 | 120 | /** 121 | * Mastodon 122 | */ 123 | mastodon?: string; 124 | 125 | /** 126 | * ResearchGate username 127 | */ 128 | researchGate?: string; 129 | 130 | /** 131 | * Facebook 132 | */ 133 | facebook?: string; 134 | 135 | /** 136 | * Instagram 137 | */ 138 | instagram?: string; 139 | 140 | /** 141 | * Reddit 142 | */ 143 | reddit?: string; 144 | 145 | /** 146 | * Threads 147 | */ 148 | threads?: string; 149 | 150 | /** 151 | * YouTube 152 | */ 153 | youtube?: string; 154 | 155 | /** 156 | * Udemy 157 | */ 158 | udemy?: string; 159 | 160 | /** 161 | * Dribbble 162 | */ 163 | dribbble?: string; 164 | 165 | /** 166 | * Behance 167 | */ 168 | behance?: string; 169 | 170 | /** 171 | * Medium 172 | */ 173 | medium?: string; 174 | 175 | /** 176 | * dev 177 | */ 178 | dev?: string; 179 | 180 | /** 181 | * Stack Overflow 182 | */ 183 | stackoverflow?: string; 184 | 185 | /** 186 | * Website 187 | */ 188 | website?: string; 189 | 190 | /** 191 | * Skype username 192 | */ 193 | skype?: string; 194 | 195 | /** 196 | * Telegram username 197 | */ 198 | telegram?: string; 199 | 200 | /** 201 | * Phone 202 | */ 203 | phone?: string; 204 | 205 | /** 206 | * Email 207 | */ 208 | email?: string; 209 | } 210 | 211 | interface Resume { 212 | /** 213 | * Resume file urlm 214 | */ 215 | fileUrl?: string; 216 | } 217 | 218 | interface Experience { 219 | company?: string; 220 | position?: string; 221 | from: string; 222 | to: string; 223 | companyLink?: string; 224 | } 225 | 226 | interface Certification { 227 | body?: string; 228 | name?: string; 229 | year?: string; 230 | link?: string; 231 | } 232 | 233 | interface Education { 234 | institution?: string; 235 | degree?: string; 236 | from: string; 237 | to: string; 238 | } 239 | 240 | interface Publication { 241 | title: string; 242 | conferenceName?: string; 243 | journalName?: string; 244 | authors?: string; 245 | link?: string; 246 | description?: string; 247 | } 248 | 249 | interface GoogleAnalytics { 250 | /** 251 | * GA3 tracking id/GA4 tag id UA-XXXXXXXXX-X | G-XXXXXXXXXX 252 | */ 253 | id?: string; 254 | } 255 | 256 | interface Hotjar { 257 | /** 258 | * Hotjar id 259 | */ 260 | id?: string; 261 | 262 | /** 263 | * Snippet Version 264 | */ 265 | snippetVersion?: number; 266 | } 267 | 268 | interface Blog { 269 | /** 270 | * medium | dev 271 | */ 272 | source?: string; 273 | 274 | /** 275 | * Username 276 | */ 277 | username?: string; 278 | 279 | /** 280 | * How many articles to display 281 | * 282 | * Max is 10 283 | */ 284 | limit?: number; 285 | } 286 | 287 | interface CustomTheme { 288 | /** 289 | * Primary color 290 | */ 291 | primary?: string; 292 | 293 | /** 294 | * Secondary color 295 | */ 296 | secondary?: string; 297 | 298 | /** 299 | * Accent color 300 | */ 301 | accent?: string; 302 | 303 | /** 304 | * Neutral color 305 | */ 306 | neutral?: string; 307 | 308 | /** 309 | * Base color of page 310 | */ 311 | 'base-100'?: string; 312 | 313 | /** 314 | * Border radius of rounded-box 315 | */ 316 | '--rounded-box'?: string; 317 | 318 | /** 319 | * Border radius of rounded-btn 320 | */ 321 | '--rounded-btn'?: string; 322 | } 323 | 324 | interface ThemeConfig { 325 | /** 326 | * Default theme 327 | */ 328 | defaultTheme?: string; 329 | 330 | /** 331 | * Hides the switch in the navbar 332 | */ 333 | disableSwitch?: boolean; 334 | 335 | /** 336 | * Should use the prefers-color-scheme media-query 337 | */ 338 | respectPrefersColorScheme?: boolean; 339 | 340 | /** 341 | * Hide the ring in Profile picture 342 | */ 343 | displayAvatarRing?: boolean; 344 | 345 | /** 346 | * Available themes 347 | */ 348 | themes?: Array; 349 | 350 | /** 351 | * Custom theme 352 | */ 353 | customTheme?: CustomTheme; 354 | } 355 | 356 | interface Config { 357 | /** 358 | * GitHub config 359 | */ 360 | github: Github; 361 | 362 | /** 363 | * Vite's base url 364 | */ 365 | base?: string; 366 | 367 | /** 368 | * Projects config 369 | */ 370 | projects?: Projects; 371 | 372 | /** 373 | * SEO config 374 | */ 375 | seo?: SEO; 376 | 377 | /** 378 | * Social links 379 | */ 380 | social?: Social; 381 | 382 | /** 383 | * Skill list 384 | */ 385 | skills?: Array; 386 | 387 | /** 388 | * Experience list 389 | */ 390 | experiences?: Array; 391 | 392 | /** 393 | * Certifications list 394 | */ 395 | certifications?: Array; 396 | 397 | /** 398 | * Education list 399 | */ 400 | educations?: Array; 401 | 402 | /** 403 | * Publication list 404 | */ 405 | publications?: Array; 406 | 407 | /** 408 | * Resume 409 | */ 410 | resume?: Resume; 411 | 412 | /** 413 | * Google Analytics config 414 | */ 415 | googleAnalytics?: GoogleAnalytics; 416 | 417 | /** 418 | * Hotjar config 419 | */ 420 | hotjar?: Hotjar; 421 | 422 | /** 423 | * Blog config 424 | */ 425 | blog?: Blog; 426 | 427 | /** 428 | * Theme config 429 | */ 430 | themeConfig?: ThemeConfig; 431 | 432 | /** 433 | * Custom footer 434 | */ 435 | footer?: string; 436 | 437 | /** 438 | * Enable PWA 439 | */ 440 | enablePWA?: boolean; 441 | } 442 | 443 | declare const CONFIG: Config; 444 | -------------------------------------------------------------------------------- /src/components/blog-card/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import LazyImage from '../lazy-image'; 3 | import { AiOutlineContainer } from 'react-icons/ai'; 4 | import { getDevPost, getMediumPost } from '@arifszn/blog-js'; 5 | import { formatDistance } from 'date-fns'; 6 | import { SanitizedBlog } from '../../interfaces/sanitized-config'; 7 | import { ga, skeleton } from '../../utils'; 8 | import { Article } from '../../interfaces/article'; 9 | 10 | const BlogCard = ({ 11 | loading, 12 | blog, 13 | googleAnalyticsId, 14 | }: { 15 | loading: boolean; 16 | blog: SanitizedBlog; 17 | googleAnalyticsId?: string; 18 | }) => { 19 | const [articles, setArticles] = useState([]); 20 | 21 | useEffect(() => { 22 | if (blog.source === 'medium') { 23 | getMediumPost({ 24 | user: blog.username, 25 | }).then((res) => { 26 | setArticles(res); 27 | }); 28 | } else if (blog.source === 'dev') { 29 | getDevPost({ 30 | user: blog.username, 31 | }).then((res) => { 32 | setArticles(res); 33 | }); 34 | } 35 | }, [blog.source, blog.username]); 36 | 37 | const renderSkeleton = () => { 38 | const array = []; 39 | for (let index = 0; index < blog.limit; index++) { 40 | array.push( 41 |
    42 |
    43 |
    44 |
    45 |
    46 | {skeleton({ 47 | widthCls: 'w-full', 48 | heightCls: 'h-full', 49 | shape: '', 50 | })} 51 |
    52 |
    53 |
    54 |
    55 |
    56 |

    57 | {skeleton({ 58 | widthCls: 'w-full', 59 | heightCls: 'h-8', 60 | className: 'mb-2 mx-auto md:mx-0', 61 | })} 62 |

    63 | {skeleton({ 64 | widthCls: 'w-24', 65 | heightCls: 'h-3', 66 | className: 'mx-auto md:mx-0', 67 | })} 68 |
    69 | {skeleton({ 70 | widthCls: 'w-full', 71 | heightCls: 'h-4', 72 | className: 'mx-auto md:mx-0', 73 | })} 74 |
    75 |
    76 | {skeleton({ 77 | widthCls: 'w-32', 78 | heightCls: 'h-4', 79 | className: 'md:mr-2 mx-auto md:mx-0', 80 | })} 81 |
    82 |
    83 |
    84 |
    85 |
    86 |
    87 |
    , 88 | ); 89 | } 90 | 91 | return array; 92 | }; 93 | 94 | const renderArticles = () => { 95 | return articles && articles.length ? ( 96 | articles.slice(0, blog.limit).map((article, index) => ( 97 | { 102 | e.preventDefault(); 103 | 104 | try { 105 | if (googleAnalyticsId) { 106 | ga.event('Click Blog Post', { 107 | post: article.title, 108 | }); 109 | } 110 | } catch (error) { 111 | console.error(error); 112 | } 113 | 114 | window?.open(article.link, '_blank'); 115 | }} 116 | > 117 |
    118 |
    119 |
    120 |
    121 | 130 |
    131 |
    132 |
    133 |
    134 |
    135 |

    136 | {article.title} 137 |

    138 |

    139 | {formatDistance(article.publishedAt, new Date(), { 140 | addSuffix: true, 141 | })} 142 |

    143 |

    144 | {article.description} 145 |

    146 |
    147 | {article.categories.map((category, index2) => ( 148 |
    152 | #{category} 153 |
    154 | ))} 155 |
    156 |
    157 |
    158 |
    159 |
    160 |
    161 |
    162 | )) 163 | ) : ( 164 |
    165 | 166 |

    167 | No recent post 168 |

    169 |
    170 | ); 171 | }; 172 | 173 | return ( 174 |
    175 |
    176 |
    177 |
    184 |
    185 |
    186 |
    187 | {loading ? ( 188 | skeleton({ widthCls: 'w-28', heightCls: 'h-8' }) 189 | ) : ( 190 | 191 | My Articles 192 | 193 | )} 194 |
    195 |
    196 |
    197 |
    198 | {loading || !articles ? renderSkeleton() : renderArticles()} 199 |
    200 |
    201 |
    202 |
    203 |
    204 |
    205 |
    206 | ); 207 | }; 208 | 209 | export default BlogCard; 210 | -------------------------------------------------------------------------------- /src/utils/index.tsx: -------------------------------------------------------------------------------- 1 | import { hotjar } from 'react-hotjar'; 2 | import { LOCAL_STORAGE_KEY_NAME } from '../constants'; 3 | import { DEFAULT_CUSTOM_THEME } from '../constants/default-custom-theme'; 4 | import { DEFAULT_THEMES } from '../constants/default-themes'; 5 | import colors from '../data/colors.json'; 6 | import { 7 | SanitizedConfig, 8 | SanitizedHotjar, 9 | SanitizedThemeConfig, 10 | } from '../interfaces/sanitized-config'; 11 | 12 | export const isDarkishTheme = (appliedTheme: string): boolean => { 13 | return ['dark', 'halloween', 'forest', 'black', 'luxury', 'dracula'].includes( 14 | appliedTheme, 15 | ); 16 | }; 17 | 18 | type EventParams = { 19 | [key: string]: string; 20 | }; 21 | 22 | type Colors = { 23 | [key: string]: { color: string | null; url: string }; 24 | }; 25 | 26 | export const getSanitizedConfig = ( 27 | config: Config, 28 | ): SanitizedConfig | Record => { 29 | try { 30 | return { 31 | github: { 32 | username: config.github.username, 33 | }, 34 | projects: { 35 | github: { 36 | display: config?.projects?.github?.display ?? true, 37 | header: config?.projects?.github?.header || 'Github Projects', 38 | mode: config?.projects?.github?.mode || 'automatic', 39 | automatic: { 40 | sortBy: config?.projects?.github?.automatic?.sortBy || 'stars', 41 | limit: config?.projects?.github?.automatic?.limit || 8, 42 | exclude: { 43 | forks: 44 | config?.projects?.github?.automatic?.exclude?.forks || false, 45 | projects: 46 | config?.projects?.github?.automatic?.exclude?.projects || [], 47 | }, 48 | }, 49 | manual: { 50 | projects: config?.projects?.github?.manual?.projects || [], 51 | }, 52 | }, 53 | external: { 54 | header: config?.projects?.external?.header || 'My Projects', 55 | projects: config?.projects?.external?.projects || [], 56 | }, 57 | }, 58 | seo: { 59 | title: config?.seo?.title, 60 | description: config?.seo?.description, 61 | imageURL: config?.seo?.imageURL, 62 | }, 63 | social: { 64 | linkedin: config?.social?.linkedin, 65 | twitter: config?.social?.twitter, 66 | mastodon: config?.social?.mastodon, 67 | facebook: config?.social?.facebook, 68 | instagram: config?.social?.instagram, 69 | reddit: config?.social?.reddit, 70 | threads: config?.social?.threads, 71 | youtube: config?.social?.youtube, 72 | udemy: config?.social?.udemy, 73 | dribbble: config?.social?.dribbble, 74 | behance: config?.social?.behance, 75 | medium: config?.social?.medium, 76 | dev: config?.social?.dev, 77 | stackoverflow: config?.social?.stackoverflow, 78 | website: config?.social?.website, 79 | phone: config?.social?.phone, 80 | email: config?.social?.email, 81 | skype: config?.social?.skype, 82 | telegram: config?.social?.telegram, 83 | researchGate: config?.social?.researchGate, 84 | }, 85 | resume: { 86 | fileUrl: config?.resume?.fileUrl || '', 87 | }, 88 | skills: config?.skills || [], 89 | experiences: 90 | config?.experiences?.filter( 91 | (experience) => 92 | experience.company || 93 | experience.position || 94 | experience.from || 95 | experience.to, 96 | ) || [], 97 | certifications: 98 | config?.certifications?.filter( 99 | (certification) => 100 | certification.year || certification.name || certification.body, 101 | ) || [], 102 | educations: 103 | config?.educations?.filter( 104 | (item) => item.institution || item.degree || item.from || item.to, 105 | ) || [], 106 | publications: config?.publications?.filter((item) => item.title) || [], 107 | googleAnalytics: { 108 | id: config?.googleAnalytics?.id, 109 | }, 110 | hotjar: { 111 | id: config?.hotjar?.id, 112 | snippetVersion: config?.hotjar?.snippetVersion || 6, 113 | }, 114 | blog: { 115 | username: config?.blog?.username || '', 116 | source: config?.blog?.source || 'dev', 117 | limit: config?.blog?.limit || 5, 118 | display: !!config?.blog?.username && !!config?.blog?.source, 119 | }, 120 | themeConfig: { 121 | defaultTheme: config?.themeConfig?.defaultTheme || DEFAULT_THEMES[0], 122 | disableSwitch: config?.themeConfig?.disableSwitch || false, 123 | respectPrefersColorScheme: 124 | config?.themeConfig?.respectPrefersColorScheme || false, 125 | displayAvatarRing: config?.themeConfig?.displayAvatarRing ?? true, 126 | themes: config?.themeConfig?.themes || DEFAULT_THEMES, 127 | customTheme: { 128 | primary: 129 | config?.themeConfig?.customTheme?.primary || 130 | DEFAULT_CUSTOM_THEME.primary, 131 | secondary: 132 | config?.themeConfig?.customTheme?.secondary || 133 | DEFAULT_CUSTOM_THEME.secondary, 134 | accent: 135 | config?.themeConfig?.customTheme?.accent || 136 | DEFAULT_CUSTOM_THEME.accent, 137 | neutral: 138 | config?.themeConfig?.customTheme?.neutral || 139 | DEFAULT_CUSTOM_THEME.neutral, 140 | 'base-100': 141 | config?.themeConfig?.customTheme?.['base-100'] || 142 | DEFAULT_CUSTOM_THEME['base-100'], 143 | '--rounded-box': 144 | config?.themeConfig?.customTheme?.['--rounded-box'] || 145 | DEFAULT_CUSTOM_THEME['--rounded-box'], 146 | '--rounded-btn': 147 | config?.themeConfig?.customTheme?.['--rounded-btn'] || 148 | DEFAULT_CUSTOM_THEME['--rounded-btn'], 149 | }, 150 | }, 151 | footer: config?.footer, 152 | enablePWA: config?.enablePWA ?? true, 153 | }; 154 | } catch (error) { 155 | return {}; 156 | } 157 | }; 158 | 159 | export const getInitialTheme = (themeConfig: SanitizedThemeConfig): string => { 160 | if (themeConfig.disableSwitch) { 161 | return themeConfig.defaultTheme; 162 | } 163 | 164 | if ( 165 | typeof window !== 'undefined' && 166 | !(localStorage.getItem(LOCAL_STORAGE_KEY_NAME) === null) 167 | ) { 168 | const savedTheme = localStorage.getItem(LOCAL_STORAGE_KEY_NAME); 169 | 170 | if (savedTheme && themeConfig.themes.includes(savedTheme)) { 171 | return savedTheme; 172 | } 173 | } 174 | 175 | if (themeConfig.respectPrefersColorScheme && !themeConfig.disableSwitch) { 176 | return typeof window !== 'undefined' && 177 | window.matchMedia('(prefers-color-scheme: dark)').matches 178 | ? 'dark' 179 | : themeConfig.defaultTheme; 180 | } 181 | 182 | return themeConfig.defaultTheme; 183 | }; 184 | 185 | export const skeleton = ({ 186 | widthCls = null, 187 | heightCls = null, 188 | style = {} as React.CSSProperties, 189 | shape = 'rounded-full', 190 | className = null, 191 | }: { 192 | widthCls?: string | null; 193 | heightCls?: string | null; 194 | style?: React.CSSProperties; 195 | shape?: string; 196 | className?: string | null; 197 | }): JSX.Element => { 198 | const classNames = ['bg-base-300', 'animate-pulse', shape]; 199 | if (className) { 200 | classNames.push(className); 201 | } 202 | if (widthCls) { 203 | classNames.push(widthCls); 204 | } 205 | if (heightCls) { 206 | classNames.push(heightCls); 207 | } 208 | 209 | return
    ; 210 | }; 211 | 212 | export const setupHotjar = (hotjarConfig: SanitizedHotjar): void => { 213 | if (hotjarConfig?.id) { 214 | const snippetVersion = hotjarConfig?.snippetVersion || 6; 215 | hotjar.initialize({ id: parseInt(hotjarConfig.id), sv: snippetVersion }); 216 | } 217 | }; 218 | 219 | export const ga = { 220 | event(action: string, params: EventParams): void { 221 | try { 222 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 223 | (window as any)?.gtag('event', action, params); 224 | } catch (error) { 225 | console.error(error); 226 | } 227 | }, 228 | }; 229 | 230 | export const getLanguageColor = (language: string): string => { 231 | const languageColors: Colors = colors; 232 | if (typeof languageColors[language] !== 'undefined') { 233 | return languageColors[language].color || 'gray'; 234 | } else { 235 | return 'gray'; 236 | } 237 | }; 238 | -------------------------------------------------------------------------------- /src/components/gitprofile.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import axios, { AxiosError } from 'axios'; 3 | import { formatDistance } from 'date-fns'; 4 | import { 5 | CustomError, 6 | GENERIC_ERROR, 7 | INVALID_CONFIG_ERROR, 8 | INVALID_GITHUB_USERNAME_ERROR, 9 | setTooManyRequestError, 10 | } from '../constants/errors'; 11 | import { HelmetProvider } from 'react-helmet-async'; 12 | import '../assets/index.css'; 13 | import { getInitialTheme, getSanitizedConfig, setupHotjar } from '../utils'; 14 | import { SanitizedConfig } from '../interfaces/sanitized-config'; 15 | import ErrorPage from './error-page'; 16 | import HeadTagEditor from './head-tag-editor'; 17 | import { DEFAULT_THEMES } from '../constants/default-themes'; 18 | import ThemeChanger from './theme-changer'; 19 | import { BG_COLOR } from '../constants'; 20 | import AvatarCard from './avatar-card'; 21 | import { Profile } from '../interfaces/profile'; 22 | import DetailsCard from './details-card'; 23 | import SkillCard from './skill-card'; 24 | import ExperienceCard from './experience-card'; 25 | import EducationCard from './education-card'; 26 | import CertificationCard from './certification-card'; 27 | import { GithubProject } from '../interfaces/github-project'; 28 | import GithubProjectCard from './github-project-card'; 29 | import ExternalProjectCard from './external-project-card'; 30 | import BlogCard from './blog-card'; 31 | import Footer from './footer'; 32 | import PublicationCard from './publication-card'; 33 | 34 | /** 35 | * Renders the GitProfile component. 36 | * 37 | * @param {Object} config - the configuration object 38 | * @return {JSX.Element} the rendered GitProfile component 39 | */ 40 | const GitProfile = ({ config }: { config: Config }) => { 41 | const [sanitizedConfig] = useState>( 42 | getSanitizedConfig(config), 43 | ); 44 | const [theme, setTheme] = useState(DEFAULT_THEMES[0]); 45 | const [error, setError] = useState(null); 46 | const [loading, setLoading] = useState(false); 47 | const [profile, setProfile] = useState(null); 48 | const [githubProjects, setGithubProjects] = useState([]); 49 | 50 | const getGithubProjects = useCallback( 51 | async (publicRepoCount: number): Promise => { 52 | if (sanitizedConfig.projects.github.mode === 'automatic') { 53 | if (publicRepoCount === 0) { 54 | return []; 55 | } 56 | 57 | const excludeRepo = 58 | sanitizedConfig.projects.github.automatic.exclude.projects 59 | .map((project) => `+-repo:${project}`) 60 | .join(''); 61 | 62 | const query = `user:${sanitizedConfig.github.username}+fork:${!sanitizedConfig.projects.github.automatic.exclude.forks}${excludeRepo}`; 63 | const url = `https://api.github.com/search/repositories?q=${query}&sort=${sanitizedConfig.projects.github.automatic.sortBy}&per_page=${sanitizedConfig.projects.github.automatic.limit}&type=Repositories`; 64 | 65 | const repoResponse = await axios.get(url, { 66 | headers: { 'Content-Type': 'application/vnd.github.v3+json' }, 67 | }); 68 | const repoData = repoResponse.data; 69 | 70 | return repoData.items; 71 | } else { 72 | if (sanitizedConfig.projects.github.manual.projects.length === 0) { 73 | return []; 74 | } 75 | const repos = sanitizedConfig.projects.github.manual.projects 76 | .map((project) => `+repo:${project}`) 77 | .join(''); 78 | 79 | const url = `https://api.github.com/search/repositories?q=${repos}&type=Repositories`; 80 | 81 | const repoResponse = await axios.get(url, { 82 | headers: { 'Content-Type': 'application/vnd.github.v3+json' }, 83 | }); 84 | const repoData = repoResponse.data; 85 | 86 | return repoData.items; 87 | } 88 | }, 89 | [ 90 | sanitizedConfig.github.username, 91 | sanitizedConfig.projects.github.mode, 92 | sanitizedConfig.projects.github.manual.projects, 93 | sanitizedConfig.projects.github.automatic.sortBy, 94 | sanitizedConfig.projects.github.automatic.limit, 95 | sanitizedConfig.projects.github.automatic.exclude.forks, 96 | sanitizedConfig.projects.github.automatic.exclude.projects, 97 | ], 98 | ); 99 | 100 | const loadData = useCallback(async () => { 101 | try { 102 | setLoading(true); 103 | 104 | const response = await axios.get( 105 | `https://api.github.com/users/${sanitizedConfig.github.username}`, 106 | ); 107 | const data = response.data; 108 | 109 | setProfile({ 110 | avatar: data.avatar_url, 111 | name: data.name || ' ', 112 | bio: data.bio || '', 113 | location: data.location || '', 114 | company: data.company || '', 115 | }); 116 | 117 | if (!sanitizedConfig.projects.github.display) { 118 | return; 119 | } 120 | 121 | setGithubProjects(await getGithubProjects(data.public_repos)); 122 | } catch (error) { 123 | handleError(error as AxiosError | Error); 124 | } finally { 125 | setLoading(false); 126 | } 127 | }, [ 128 | sanitizedConfig.github.username, 129 | sanitizedConfig.projects.github.display, 130 | getGithubProjects, 131 | ]); 132 | 133 | useEffect(() => { 134 | if (Object.keys(sanitizedConfig).length === 0) { 135 | setError(INVALID_CONFIG_ERROR); 136 | } else { 137 | setError(null); 138 | setTheme(getInitialTheme(sanitizedConfig.themeConfig)); 139 | setupHotjar(sanitizedConfig.hotjar); 140 | loadData(); 141 | } 142 | }, [sanitizedConfig, loadData]); 143 | 144 | useEffect(() => { 145 | theme && document.documentElement.setAttribute('data-theme', theme); 146 | }, [theme]); 147 | 148 | const handleError = (error: AxiosError | Error): void => { 149 | console.error('Error:', error); 150 | 151 | if (error instanceof AxiosError) { 152 | try { 153 | const reset = formatDistance( 154 | new Date(error.response?.headers?.['x-ratelimit-reset'] * 1000), 155 | new Date(), 156 | { addSuffix: true }, 157 | ); 158 | 159 | if (typeof error.response?.status === 'number') { 160 | switch (error.response.status) { 161 | case 403: 162 | setError(setTooManyRequestError(reset)); 163 | break; 164 | case 404: 165 | setError(INVALID_GITHUB_USERNAME_ERROR); 166 | break; 167 | default: 168 | setError(GENERIC_ERROR); 169 | break; 170 | } 171 | } else { 172 | setError(GENERIC_ERROR); 173 | } 174 | } catch (innerError) { 175 | setError(GENERIC_ERROR); 176 | } 177 | } else { 178 | setError(GENERIC_ERROR); 179 | } 180 | }; 181 | 182 | return ( 183 | 184 |
    185 | {error ? ( 186 | 191 | ) : ( 192 | <> 193 | 197 |
    198 |
    199 |
    200 |
    201 | {!sanitizedConfig.themeConfig.disableSwitch && ( 202 | 208 | )} 209 | 215 | 221 | {sanitizedConfig.skills.length !== 0 && ( 222 | 226 | )} 227 | {sanitizedConfig.experiences.length !== 0 && ( 228 | 232 | )} 233 | {sanitizedConfig.certifications.length !== 0 && ( 234 | 238 | )} 239 | {sanitizedConfig.educations.length !== 0 && ( 240 | 244 | )} 245 |
    246 |
    247 |
    248 |
    249 | {sanitizedConfig.projects.github.display && ( 250 | 258 | )} 259 | {sanitizedConfig.publications.length !== 0 && ( 260 | 264 | )} 265 | {sanitizedConfig.projects.external.projects.length !== 266 | 0 && ( 267 | 275 | )} 276 | {sanitizedConfig.blog.display && ( 277 | 282 | )} 283 |
    284 |
    285 |
    286 |
    287 | {sanitizedConfig.footer && ( 288 |
    291 |
    292 |
    293 |
    294 |
    295 | )} 296 | 297 | )} 298 |
    299 |
    300 | ); 301 | }; 302 | 303 | export default GitProfile; 304 | -------------------------------------------------------------------------------- /src/components/details-card/index.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'react'; 2 | import { 3 | AiFillGithub, 4 | AiFillInstagram, 5 | AiFillMediumSquare, 6 | } from 'react-icons/ai'; 7 | import { CgDribbble } from 'react-icons/cg'; 8 | import { 9 | FaBehanceSquare, 10 | FaBuilding, 11 | FaDev, 12 | FaFacebook, 13 | FaGlobe, 14 | FaLinkedin, 15 | FaMastodon, 16 | FaReddit, 17 | FaSkype, 18 | FaStackOverflow, 19 | FaTelegram, 20 | FaYoutube, 21 | } from 'react-icons/fa'; 22 | import { FaSquareThreads } from 'react-icons/fa6'; 23 | import { MdLocationOn } from 'react-icons/md'; 24 | import { RiMailFill, RiPhoneFill } from 'react-icons/ri'; 25 | import { SiResearchgate, SiTwitter, SiUdemy } from 'react-icons/si'; 26 | import { Profile } from '../../interfaces/profile'; 27 | import { 28 | SanitizedGithub, 29 | SanitizedSocial, 30 | } from '../../interfaces/sanitized-config'; 31 | import { skeleton } from '../../utils'; 32 | 33 | type Props = { 34 | profile: Profile | null; 35 | loading: boolean; 36 | social: SanitizedSocial; 37 | github: SanitizedGithub; 38 | }; 39 | 40 | const isCompanyMention = (company: string): boolean => { 41 | return company.startsWith('@') && !company.includes(' '); 42 | }; 43 | 44 | const companyLink = (company: string): string => { 45 | return `https://github.com/${company.substring(1)}`; 46 | }; 47 | 48 | const getFormattedMastodonValue = ( 49 | mastodonValue: string, 50 | isLink: boolean, 51 | ): string => { 52 | const [username, server] = mastodonValue.split('@'); 53 | 54 | if (isLink) { 55 | return `https://${server}/@${username}`; 56 | } else { 57 | return `${username}@${server}`; 58 | } 59 | }; 60 | 61 | const ListItem: React.FC<{ 62 | icon: React.ReactNode; 63 | title: React.ReactNode; 64 | value: React.ReactNode; 65 | link?: string; 66 | skeleton?: boolean; 67 | }> = ({ icon, title, value, link, skeleton = false }) => { 68 | return ( 69 |
    70 |
    71 | {icon} {title} 72 |
    73 | 90 |
    91 | ); 92 | }; 93 | 94 | const OrganizationItem: React.FC<{ 95 | icon: React.ReactNode; 96 | title: React.ReactNode; 97 | value: React.ReactNode | string; 98 | link?: string; 99 | skeleton?: boolean; 100 | }> = ({ icon, title, value, link, skeleton = false }) => { 101 | const renderValue = () => { 102 | if (typeof value === 'string') { 103 | return value.split(' ').map((company) => { 104 | company = company.trim(); 105 | if (!company) return null; 106 | 107 | if (isCompanyMention(company)) { 108 | return ( 109 | 115 | {company} 116 | 117 | ); 118 | } else { 119 | return {company}; 120 | } 121 | }); 122 | } 123 | return value; 124 | }; 125 | 126 | return ( 127 |
    128 |
    129 | {icon} {title} 130 |
    131 |
    139 | {renderValue()} 140 |
    141 |
    142 | ); 143 | }; 144 | 145 | /** 146 | * Renders the details card component. 147 | * 148 | * @param {Object} profile - The profile object. 149 | * @param {boolean} loading - Indicates whether the data is loading. 150 | * @param {Object} social - The social object. 151 | * @param {Object} github - The GitHub object. 152 | * @return {JSX.Element} The details card component. 153 | */ 154 | const DetailsCard = ({ profile, loading, social, github }: Props) => { 155 | const renderSkeleton = () => { 156 | const array = []; 157 | for (let index = 0; index < 4; index++) { 158 | array.push( 159 | , 166 | ); 167 | } 168 | 169 | return array; 170 | }; 171 | 172 | return ( 173 |
    174 |
    175 |
    176 | {loading || !profile ? ( 177 | renderSkeleton() 178 | ) : ( 179 | 180 | {profile.location && ( 181 | } 183 | title="Based in:" 184 | value={profile.location} 185 | /> 186 | )} 187 | {profile.company && ( 188 | } 190 | title="Organization:" 191 | value={profile.company} 192 | link={ 193 | isCompanyMention(profile.company.trim()) 194 | ? companyLink(profile.company.trim()) 195 | : undefined 196 | } 197 | /> 198 | )} 199 | } 201 | title="GitHub:" 202 | value={github.username} 203 | link={`https://github.com/${github.username}`} 204 | /> 205 | {social?.researchGate && ( 206 | } 208 | title="ResearchGate:" 209 | value={social.researchGate} 210 | link={`https://www.researchgate.net/profile/${social.researchGate}`} 211 | /> 212 | )} 213 | {social?.twitter && ( 214 | } 216 | title="Twitter:" 217 | value={social.twitter} 218 | link={`https://twitter.com/${social.twitter}`} 219 | /> 220 | )} 221 | {social?.mastodon && ( 222 | } 224 | title="Mastodon:" 225 | value={getFormattedMastodonValue(social.mastodon, false)} 226 | link={getFormattedMastodonValue(social.mastodon, true)} 227 | /> 228 | )} 229 | {social?.linkedin && ( 230 | } 232 | title="LinkedIn:" 233 | value={social.linkedin} 234 | link={`https://www.linkedin.com/in/${social.linkedin}`} 235 | /> 236 | )} 237 | {social?.dribbble && ( 238 | } 240 | title="Dribbble:" 241 | value={social.dribbble} 242 | link={`https://dribbble.com/${social.dribbble}`} 243 | /> 244 | )} 245 | {social?.behance && ( 246 | } 248 | title="Behance:" 249 | value={social.behance} 250 | link={`https://www.behance.net/${social.behance}`} 251 | /> 252 | )} 253 | {social?.facebook && ( 254 | } 256 | title="Facebook:" 257 | value={social.facebook} 258 | link={`https://www.facebook.com/${social.facebook}`} 259 | /> 260 | )} 261 | {social?.instagram && ( 262 | } 264 | title="Instagram:" 265 | value={social.instagram} 266 | link={`https://www.instagram.com/${social.instagram}`} 267 | /> 268 | )} 269 | {social?.reddit && ( 270 | } 272 | title="Reddit:" 273 | value={social.reddit} 274 | link={`https://www.reddit.com/user/${social.reddit}`} 275 | /> 276 | )} 277 | {social?.threads && ( 278 | } 280 | title="Threads:" 281 | value={social.threads} 282 | link={`https://www.threads.net/@${social.threads.replace('@', '')}`} 283 | /> 284 | )} 285 | {social?.youtube && ( 286 | } 288 | title="YouTube:" 289 | value={`@${social.youtube}`} 290 | link={`https://www.youtube.com/@${social.youtube}`} 291 | /> 292 | )} 293 | {social?.udemy && ( 294 | } 296 | title="Udemy:" 297 | value={social.udemy} 298 | link={`https://www.udemy.com/user/${social.udemy}`} 299 | /> 300 | )} 301 | {social?.medium && ( 302 | } 304 | title="Medium:" 305 | value={social.medium} 306 | link={`https://medium.com/@${social.medium}`} 307 | /> 308 | )} 309 | {social?.dev && ( 310 | } 312 | title="Dev:" 313 | value={social.dev} 314 | link={`https://dev.to/${social.dev}`} 315 | /> 316 | )} 317 | {social?.stackoverflow && ( 318 | } 320 | title="Stack Overflow:" 321 | value={social.stackoverflow.split('/').slice(-1)} 322 | link={`https://stackoverflow.com/users/${social.stackoverflow}`} 323 | /> 324 | )} 325 | {social?.website && ( 326 | } 328 | title="Website:" 329 | value={social.website 330 | .replace('https://', '') 331 | .replace('http://', '')} 332 | link={ 333 | !social.website.startsWith('http') 334 | ? `http://${social.website}` 335 | : social.website 336 | } 337 | /> 338 | )} 339 | {social?.skype && ( 340 | } 342 | title="Skype" 343 | value={social.skype} 344 | link={`skype:${social.skype}?chat`} 345 | /> 346 | )} 347 | {social?.telegram && ( 348 | } 350 | title="Telegram" 351 | value={social.telegram} 352 | link={`https://t.me/${social.telegram}`} 353 | /> 354 | )} 355 | {social?.phone && ( 356 | } 358 | title="Phone:" 359 | value={social.phone} 360 | link={`tel:${social.phone}`} 361 | /> 362 | )} 363 | {social?.email && ( 364 | } 366 | title="Email:" 367 | value={social.email} 368 | link={`mailto:${social.email}`} 369 | /> 370 | )} 371 | 372 | )} 373 |
    374 |
    375 |
    376 | ); 377 | }; 378 | 379 | export default DetailsCard; 380 | -------------------------------------------------------------------------------- /dist/workbox-1ab968a5.js: -------------------------------------------------------------------------------- 1 | define(["exports"],(function(t){"use strict";try{self["workbox:core:7.0.0"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:7.0.0"]&&_()}catch(t){}const n=t=>t&&"object"==typeof t?t:{handle:t};class i{constructor(t,e,s="GET"){this.handler=n(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=n(t)}}class r extends i{constructor(t,e,s){super((({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)}),e,s)}}class o{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",(t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map((e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})})));t.waitUntil(s),t.ports&&t.ports[0]&&s.then((()=>t.ports[0].postMessage(!0)))}}))}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:i,route:r}=this.findMatchingRoute({event:e,request:t,sameOrigin:n,url:s});let o=r&&r.handler;const c=t.method;if(!o&&this.i.has(c)&&(o=this.i.get(c)),!o)return;let a;try{a=o.handle({url:s,request:t,event:e,params:i})}catch(t){a=Promise.reject(t)}const h=r&&r.catchHandler;return a instanceof Promise&&(this.o||h)&&(a=a.catch((async n=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:i})}catch(t){t instanceof Error&&(n=t)}if(this.o)return this.o.handle({url:s,request:t,event:e});throw n}))),a}findMatchingRoute({url:t,sameOrigin:e,request:s,event:n}){const i=this.t.get(s.method)||[];for(const r of i){let i;const o=r.match({url:t,sameOrigin:e,request:s,event:n});if(o)return i=o,(Array.isArray(i)&&0===i.length||o.constructor===Object&&0===Object.keys(o).length||"boolean"==typeof o)&&(i=void 0),{route:r,params:i}}return{}}setDefaultHandler(t,e="GET"){this.i.set(e,n(t))}setCatchHandler(t){this.o=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let c;const a=()=>(c||(c=new o,c.addFetchListener(),c.addCacheListener()),c);const h={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},u=t=>[h.prefix,t,h.suffix].filter((t=>t&&t.length>0)).join("-"),l=t=>t||u(h.precache),f=t=>t||u(h.runtime);function w(t,e){const s=e();return t.waitUntil(s),s}try{self["workbox:precaching:7.0.0"]&&_()}catch(t){}function d(t){if(!t)throw new s("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const i=new URL(n,location.href),r=new URL(n,location.href);return i.searchParams.set("__WB_REVISION__",e),{cacheKey:i.href,url:r.href}}class p{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:t,state:e})=>{e&&(e.originalRequest=t)},this.cachedResponseWillBeUsed=async({event:t,state:e,cachedResponse:s})=>{if("install"===t.type&&e&&e.originalRequest&&e.originalRequest instanceof Request){const t=e.originalRequest.url;s?this.notUpdatedURLs.push(t):this.updatedURLs.push(t)}return s}}}class y{constructor({precacheController:t}){this.cacheKeyWillBeUsed=async({request:t,params:e})=>{const s=(null==e?void 0:e.cacheKey)||this.h.getCacheKeyForURL(t.url);return s?new Request(s,{headers:t.headers}):t},this.h=t}}let g;async function R(t,e){let n=null;if(t.url){n=new URL(t.url).origin}if(n!==self.location.origin)throw new s("cross-origin-copy-response",{origin:n});const i=t.clone(),r={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=e?e(r):r,c=function(){if(void 0===g){const t=new Response("");if("body"in t)try{new Response(t.body),g=!0}catch(t){g=!1}g=!1}return g}()?i.body:await i.blob();return new Response(c,o)}function m(t,e){const s=new URL(t);for(const t of e)s.searchParams.delete(t);return s.href}class v{constructor(){this.promise=new Promise(((t,e)=>{this.resolve=t,this.reject=e}))}}const q=new Set;try{self["workbox:strategies:7.0.0"]&&_()}catch(t){}function U(t){return"string"==typeof t?new Request(t):t}class L{constructor(t,e){this.u={},Object.assign(this,e),this.event=e.event,this.l=t,this.p=new v,this.R=[],this.m=[...t.plugins],this.v=new Map;for(const t of this.m)this.v.set(t,{});this.event.waitUntil(this.p.promise)}async fetch(t){const{event:e}=this;let n=U(t);if("navigate"===n.mode&&e instanceof FetchEvent&&e.preloadResponse){const t=await e.preloadResponse;if(t)return t}const i=this.hasCallback("fetchDidFail")?n.clone():null;try{for(const t of this.iterateCallbacks("requestWillFetch"))n=await t({request:n.clone(),event:e})}catch(t){if(t instanceof Error)throw new s("plugin-error-request-will-fetch",{thrownErrorMessage:t.message})}const r=n.clone();try{let t;t=await fetch(n,"navigate"===n.mode?void 0:this.l.fetchOptions);for(const s of this.iterateCallbacks("fetchDidSucceed"))t=await s({event:e,request:r,response:t});return t}catch(t){throw i&&await this.runCallbacks("fetchDidFail",{error:t,event:e,originalRequest:i.clone(),request:r.clone()}),t}}async fetchAndCachePut(t){const e=await this.fetch(t),s=e.clone();return this.waitUntil(this.cachePut(t,s)),e}async cacheMatch(t){const e=U(t);let s;const{cacheName:n,matchOptions:i}=this.l,r=await this.getCacheKey(e,"read"),o=Object.assign(Object.assign({},i),{cacheName:n});s=await caches.match(r,o);for(const t of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await t({cacheName:n,matchOptions:i,cachedResponse:s,request:r,event:this.event})||void 0;return s}async cachePut(t,e){const n=U(t);var i;await(i=0,new Promise((t=>setTimeout(t,i))));const r=await this.getCacheKey(n,"write");if(!e)throw new s("cache-put-with-no-response",{url:(o=r.url,new URL(String(o),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var o;const c=await this.q(e);if(!c)return!1;const{cacheName:a,matchOptions:h}=this.l,u=await self.caches.open(a),l=this.hasCallback("cacheDidUpdate"),f=l?await async function(t,e,s,n){const i=m(e.url,s);if(e.url===i)return t.match(e,n);const r=Object.assign(Object.assign({},n),{ignoreSearch:!0}),o=await t.keys(e,r);for(const e of o)if(i===m(e.url,s))return t.match(e,n)}(u,r.clone(),["__WB_REVISION__"],h):null;try{await u.put(r,l?c.clone():c)}catch(t){if(t instanceof Error)throw"QuotaExceededError"===t.name&&await async function(){for(const t of q)await t()}(),t}for(const t of this.iterateCallbacks("cacheDidUpdate"))await t({cacheName:a,oldResponse:f,newResponse:c.clone(),request:r,event:this.event});return!0}async getCacheKey(t,e){const s=`${t.url} | ${e}`;if(!this.u[s]){let n=t;for(const t of this.iterateCallbacks("cacheKeyWillBeUsed"))n=U(await t({mode:e,request:n,event:this.event,params:this.params}));this.u[s]=n}return this.u[s]}hasCallback(t){for(const e of this.l.plugins)if(t in e)return!0;return!1}async runCallbacks(t,e){for(const s of this.iterateCallbacks(t))await s(e)}*iterateCallbacks(t){for(const e of this.l.plugins)if("function"==typeof e[t]){const s=this.v.get(e),n=n=>{const i=Object.assign(Object.assign({},n),{state:s});return e[t](i)};yield n}}waitUntil(t){return this.R.push(t),t}async doneWaiting(){let t;for(;t=this.R.shift();)await t}destroy(){this.p.resolve(null)}async q(t){let e=t,s=!1;for(const t of this.iterateCallbacks("cacheWillUpdate"))if(e=await t({request:this.request,response:e,event:this.event})||void 0,s=!0,!e)break;return s||e&&200!==e.status&&(e=void 0),e}}class b{constructor(t={}){this.cacheName=f(t.cacheName),this.plugins=t.plugins||[],this.fetchOptions=t.fetchOptions,this.matchOptions=t.matchOptions}handle(t){const[e]=this.handleAll(t);return e}handleAll(t){t instanceof FetchEvent&&(t={event:t,request:t.request});const e=t.event,s="string"==typeof t.request?new Request(t.request):t.request,n="params"in t?t.params:void 0,i=new L(this,{event:e,request:s,params:n}),r=this.U(i,s,e);return[r,this.L(r,i,s,e)]}async U(t,e,n){let i;await t.runCallbacks("handlerWillStart",{event:n,request:e});try{if(i=await this._(e,t),!i||"error"===i.type)throw new s("no-response",{url:e.url})}catch(s){if(s instanceof Error)for(const r of t.iterateCallbacks("handlerDidError"))if(i=await r({error:s,event:n,request:e}),i)break;if(!i)throw s}for(const s of t.iterateCallbacks("handlerWillRespond"))i=await s({event:n,request:e,response:i});return i}async L(t,e,s,n){let i,r;try{i=await t}catch(r){}try{await e.runCallbacks("handlerDidRespond",{event:n,request:s,response:i}),await e.doneWaiting()}catch(t){t instanceof Error&&(r=t)}if(await e.runCallbacks("handlerDidComplete",{event:n,request:s,response:i,error:r}),e.destroy(),r)throw r}}class C extends b{constructor(t={}){t.cacheName=l(t.cacheName),super(t),this.C=!1!==t.fallbackToNetwork,this.plugins.push(C.copyRedirectedCacheableResponsesPlugin)}async _(t,e){const s=await e.cacheMatch(t);return s||(e.event&&"install"===e.event.type?await this.O(t,e):await this.N(t,e))}async N(t,e){let n;const i=e.params||{};if(!this.C)throw new s("missing-precache-entry",{cacheName:this.cacheName,url:t.url});{const s=i.integrity,r=t.integrity,o=!r||r===s;n=await e.fetch(new Request(t,{integrity:"no-cors"!==t.mode?r||s:void 0})),s&&o&&"no-cors"!==t.mode&&(this.k(),await e.cachePut(t,n.clone()))}return n}async O(t,e){this.k();const n=await e.fetch(t);if(!await e.cachePut(t,n.clone()))throw new s("bad-precaching-response",{url:t.url,status:n.status});return n}k(){let t=null,e=0;for(const[s,n]of this.plugins.entries())n!==C.copyRedirectedCacheableResponsesPlugin&&(n===C.defaultPrecacheCacheabilityPlugin&&(t=s),n.cacheWillUpdate&&e++);0===e?this.plugins.push(C.defaultPrecacheCacheabilityPlugin):e>1&&null!==t&&this.plugins.splice(t,1)}}C.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:t})=>!t||t.status>=400?null:t},C.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:t})=>t.redirected?await R(t):t};class E{constructor({cacheName:t,plugins:e=[],fallbackToNetwork:s=!0}={}){this.K=new Map,this.P=new Map,this.T=new Map,this.l=new C({cacheName:l(t),plugins:[...e,new y({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this.l}precache(t){this.addToCacheList(t),this.W||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this.W=!0)}addToCacheList(t){const e=[];for(const n of t){"string"==typeof n?e.push(n):n&&void 0===n.revision&&e.push(n.url);const{cacheKey:t,url:i}=d(n),r="string"!=typeof n&&n.revision?"reload":"default";if(this.K.has(i)&&this.K.get(i)!==t)throw new s("add-to-cache-list-conflicting-entries",{firstEntry:this.K.get(i),secondEntry:t});if("string"!=typeof n&&n.integrity){if(this.T.has(t)&&this.T.get(t)!==n.integrity)throw new s("add-to-cache-list-conflicting-integrities",{url:i});this.T.set(t,n.integrity)}if(this.K.set(i,t),this.P.set(i,r),e.length>0){const t=`Workbox is precaching URLs without revision info: ${e.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(t)}}}install(t){return w(t,(async()=>{const e=new p;this.strategy.plugins.push(e);for(const[e,s]of this.K){const n=this.T.get(s),i=this.P.get(e),r=new Request(e,{integrity:n,cache:i,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:r,event:t}))}const{updatedURLs:s,notUpdatedURLs:n}=e;return{updatedURLs:s,notUpdatedURLs:n}}))}activate(t){return w(t,(async()=>{const t=await self.caches.open(this.strategy.cacheName),e=await t.keys(),s=new Set(this.K.values()),n=[];for(const i of e)s.has(i.url)||(await t.delete(i),n.push(i.url));return{deletedURLs:n}}))}getURLsToCacheKeys(){return this.K}getCachedURLs(){return[...this.K.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.K.get(e.href)}getIntegrityForCacheKey(t){return this.T.get(t)}async matchPrecache(t){const e=t instanceof Request?t.url:t,s=this.getCacheKeyForURL(e);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(t){const e=this.getCacheKeyForURL(t);if(!e)throw new s("non-precached-url",{url:t});return s=>(s.request=new Request(t),s.params=Object.assign({cacheKey:e},s.params),this.strategy.handle(s))}}let O;const x=()=>(O||(O=new E),O);class N extends i{constructor(t,e){super((({request:s})=>{const n=t.getURLsToCacheKeys();for(const i of function*(t,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:n=!0,urlManipulation:i}={}){const r=new URL(t,location.href);r.hash="",yield r.href;const o=function(t,e=[]){for(const s of[...t.searchParams.keys()])e.some((t=>t.test(s)))&&t.searchParams.delete(s);return t}(r,e);if(yield o.href,s&&o.pathname.endsWith("/")){const t=new URL(o.href);t.pathname+=s,yield t.href}if(n){const t=new URL(o.href);t.pathname+=".html",yield t.href}if(i){const t=i({url:r});for(const e of t)yield e.href}}(s.url,e)){const e=n.get(i);if(e){return{cacheKey:e,integrity:t.getIntegrityForCacheKey(e)}}}}),t.strategy)}}function k(t){const e=x();!function(t,e,n){let o;if("string"==typeof t){const s=new URL(t,location.href);o=new i((({url:t})=>t.href===s.href),e,n)}else if(t instanceof RegExp)o=new r(t,e,n);else if("function"==typeof t)o=new i(t,e,n);else{if(!(t instanceof i))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});o=t}a().registerRoute(o)}(new N(e,t))}t.cleanupOutdatedCaches=function(){self.addEventListener("activate",(t=>{const e=l();t.waitUntil((async(t,e="-precache-")=>{const s=(await self.caches.keys()).filter((s=>s.includes(e)&&s.includes(self.registration.scope)&&s!==t));return await Promise.all(s.map((t=>self.caches.delete(t)))),s})(e).then((t=>{})))}))},t.clientsClaim=function(){self.addEventListener("activate",(()=>self.clients.claim()))},t.precacheAndRoute=function(t,e){!function(t){x().precache(t)}(t),k(e)}})); 2 | -------------------------------------------------------------------------------- /src/data/colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "1C Enterprise": { 3 | "color": "#814CCC", 4 | "url": "https://github.com/trending?l=1C-Enterprise" 5 | }, 6 | "ABAP": { 7 | "color": "#E8274B", 8 | "url": "https://github.com/trending?l=ABAP" 9 | }, 10 | "ActionScript": { 11 | "color": "#882B0F", 12 | "url": "https://github.com/trending?l=ActionScript" 13 | }, 14 | "Ada": { 15 | "color": "#02f88c", 16 | "url": "https://github.com/trending?l=Ada" 17 | }, 18 | "Agda": { 19 | "color": "#315665", 20 | "url": "https://github.com/trending?l=Agda" 21 | }, 22 | "AGS Script": { 23 | "color": "#B9D9FF", 24 | "url": "https://github.com/trending?l=AGS-Script" 25 | }, 26 | "Alloy": { 27 | "color": "#64C800", 28 | "url": "https://github.com/trending?l=Alloy" 29 | }, 30 | "Alpine Abuild": { 31 | "color": null, 32 | "url": "https://github.com/trending?l=Alpine-Abuild" 33 | }, 34 | "AMPL": { 35 | "color": "#E6EFBB", 36 | "url": "https://github.com/trending?l=AMPL" 37 | }, 38 | "AngelScript": { 39 | "color": "#C7D7DC", 40 | "url": "https://github.com/trending?l=AngelScript" 41 | }, 42 | "ANTLR": { 43 | "color": "#9DC3FF", 44 | "url": "https://github.com/trending?l=ANTLR" 45 | }, 46 | "Apex": { 47 | "color": null, 48 | "url": "https://github.com/trending?l=Apex" 49 | }, 50 | "API Blueprint": { 51 | "color": "#2ACCA8", 52 | "url": "https://github.com/trending?l=API-Blueprint" 53 | }, 54 | "APL": { 55 | "color": "#5A8164", 56 | "url": "https://github.com/trending?l=APL" 57 | }, 58 | "Apollo Guidance Computer": { 59 | "color": null, 60 | "url": "https://github.com/trending?l=Apollo-Guidance-Computer" 61 | }, 62 | "AppleScript": { 63 | "color": "#101F1F", 64 | "url": "https://github.com/trending?l=AppleScript" 65 | }, 66 | "Arc": { 67 | "color": "#aa2afe", 68 | "url": "https://github.com/trending?l=Arc" 69 | }, 70 | "ASP": { 71 | "color": "#6a40fd", 72 | "url": "https://github.com/trending?l=ASP" 73 | }, 74 | "AspectJ": { 75 | "color": "#a957b0", 76 | "url": "https://github.com/trending?l=AspectJ" 77 | }, 78 | "Assembly": { 79 | "color": "#6E4C13", 80 | "url": "https://github.com/trending?l=Assembly" 81 | }, 82 | "ATS": { 83 | "color": "#1ac620", 84 | "url": "https://github.com/trending?l=ATS" 85 | }, 86 | "Augeas": { 87 | "color": null, 88 | "url": "https://github.com/trending?l=Augeas" 89 | }, 90 | "AutoHotkey": { 91 | "color": "#6594b9", 92 | "url": "https://github.com/trending?l=AutoHotkey" 93 | }, 94 | "AutoIt": { 95 | "color": "#1C3552", 96 | "url": "https://github.com/trending?l=AutoIt" 97 | }, 98 | "Awk": { 99 | "color": null, 100 | "url": "https://github.com/trending?l=Awk" 101 | }, 102 | "Ballerina": { 103 | "color": "#FF5000", 104 | "url": "https://github.com/trending?l=Ballerina" 105 | }, 106 | "Batchfile": { 107 | "color": "#C1F12E", 108 | "url": "https://github.com/trending?l=Batchfile" 109 | }, 110 | "Befunge": { 111 | "color": null, 112 | "url": "https://github.com/trending?l=Befunge" 113 | }, 114 | "Bison": { 115 | "color": null, 116 | "url": "https://github.com/trending?l=Bison" 117 | }, 118 | "BitBake": { 119 | "color": null, 120 | "url": "https://github.com/trending?l=BitBake" 121 | }, 122 | "BlitzBasic": { 123 | "color": null, 124 | "url": "https://github.com/trending?l=BlitzBasic" 125 | }, 126 | "BlitzMax": { 127 | "color": "#cd6400", 128 | "url": "https://github.com/trending?l=BlitzMax" 129 | }, 130 | "Bluespec": { 131 | "color": null, 132 | "url": "https://github.com/trending?l=Bluespec" 133 | }, 134 | "Boo": { 135 | "color": "#d4bec1", 136 | "url": "https://github.com/trending?l=Boo" 137 | }, 138 | "Brainfuck": { 139 | "color": "#2F2530", 140 | "url": "https://github.com/trending?l=Brainfuck" 141 | }, 142 | "Brightscript": { 143 | "color": null, 144 | "url": "https://github.com/trending?l=Brightscript" 145 | }, 146 | "Bro": { 147 | "color": null, 148 | "url": "https://github.com/trending?l=Bro" 149 | }, 150 | "C": { 151 | "color": "#555555", 152 | "url": "https://github.com/trending?l=C" 153 | }, 154 | "C#": { 155 | "color": "#178600", 156 | "url": "https://github.com/trending?l=Csharp" 157 | }, 158 | "C++": { 159 | "color": "#f34b7d", 160 | "url": "https://github.com/trending?l=C++" 161 | }, 162 | "C2hs Haskell": { 163 | "color": null, 164 | "url": "https://github.com/trending?l=C2hs-Haskell" 165 | }, 166 | "Cap'n Proto": { 167 | "color": null, 168 | "url": "https://github.com/trending?l=Cap'n-Proto" 169 | }, 170 | "CartoCSS": { 171 | "color": null, 172 | "url": "https://github.com/trending?l=CartoCSS" 173 | }, 174 | "Ceylon": { 175 | "color": "#dfa535", 176 | "url": "https://github.com/trending?l=Ceylon" 177 | }, 178 | "Chapel": { 179 | "color": "#8dc63f", 180 | "url": "https://github.com/trending?l=Chapel" 181 | }, 182 | "Charity": { 183 | "color": null, 184 | "url": "https://github.com/trending?l=Charity" 185 | }, 186 | "ChucK": { 187 | "color": null, 188 | "url": "https://github.com/trending?l=ChucK" 189 | }, 190 | "Cirru": { 191 | "color": "#ccccff", 192 | "url": "https://github.com/trending?l=Cirru" 193 | }, 194 | "Clarion": { 195 | "color": "#db901e", 196 | "url": "https://github.com/trending?l=Clarion" 197 | }, 198 | "Clean": { 199 | "color": "#3F85AF", 200 | "url": "https://github.com/trending?l=Clean" 201 | }, 202 | "Click": { 203 | "color": "#E4E6F3", 204 | "url": "https://github.com/trending?l=Click" 205 | }, 206 | "CLIPS": { 207 | "color": null, 208 | "url": "https://github.com/trending?l=CLIPS" 209 | }, 210 | "Clojure": { 211 | "color": "#db5855", 212 | "url": "https://github.com/trending?l=Clojure" 213 | }, 214 | "CMake": { 215 | "color": null, 216 | "url": "https://github.com/trending?l=CMake" 217 | }, 218 | "COBOL": { 219 | "color": null, 220 | "url": "https://github.com/trending?l=COBOL" 221 | }, 222 | "CoffeeScript": { 223 | "color": "#244776", 224 | "url": "https://github.com/trending?l=CoffeeScript" 225 | }, 226 | "ColdFusion": { 227 | "color": "#ed2cd6", 228 | "url": "https://github.com/trending?l=ColdFusion" 229 | }, 230 | "ColdFusion CFC": { 231 | "color": null, 232 | "url": "https://github.com/trending?l=ColdFusion-CFC" 233 | }, 234 | "Common Lisp": { 235 | "color": "#3fb68b", 236 | "url": "https://github.com/trending?l=Common-Lisp" 237 | }, 238 | "Common Workflow Language": { 239 | "color": "#B5314C", 240 | "url": "https://github.com/trending?l=Common-Workflow-Language" 241 | }, 242 | "Component Pascal": { 243 | "color": "#B0CE4E", 244 | "url": "https://github.com/trending?l=Component-Pascal" 245 | }, 246 | "Cool": { 247 | "color": null, 248 | "url": "https://github.com/trending?l=Cool" 249 | }, 250 | "Coq": { 251 | "color": null, 252 | "url": "https://github.com/trending?l=Coq" 253 | }, 254 | "Crystal": { 255 | "color": "#000100", 256 | "url": "https://github.com/trending?l=Crystal" 257 | }, 258 | "Csound": { 259 | "color": null, 260 | "url": "https://github.com/trending?l=Csound" 261 | }, 262 | "Csound Document": { 263 | "color": null, 264 | "url": "https://github.com/trending?l=Csound-Document" 265 | }, 266 | "Csound Score": { 267 | "color": null, 268 | "url": "https://github.com/trending?l=Csound-Score" 269 | }, 270 | "CSS": { 271 | "color": "#563d7c", 272 | "url": "https://github.com/trending?l=CSS" 273 | }, 274 | "Cuda": { 275 | "color": "#3A4E3A", 276 | "url": "https://github.com/trending?l=Cuda" 277 | }, 278 | "CWeb": { 279 | "color": null, 280 | "url": "https://github.com/trending?l=CWeb" 281 | }, 282 | "Cycript": { 283 | "color": null, 284 | "url": "https://github.com/trending?l=Cycript" 285 | }, 286 | "Cython": { 287 | "color": null, 288 | "url": "https://github.com/trending?l=Cython" 289 | }, 290 | "D": { 291 | "color": "#ba595e", 292 | "url": "https://github.com/trending?l=D" 293 | }, 294 | "Dart": { 295 | "color": "#00B4AB", 296 | "url": "https://github.com/trending?l=Dart" 297 | }, 298 | "DataWeave": { 299 | "color": "#003a52", 300 | "url": "https://github.com/trending?l=DataWeave" 301 | }, 302 | "DIGITAL Command Language": { 303 | "color": null, 304 | "url": "https://github.com/trending?l=DIGITAL-Command-Language" 305 | }, 306 | "DM": { 307 | "color": "#447265", 308 | "url": "https://github.com/trending?l=DM" 309 | }, 310 | "Dockerfile": { 311 | "color": "#384d54", 312 | "url": "https://github.com/trending?l=Dockerfile" 313 | }, 314 | "Dogescript": { 315 | "color": "#cca760", 316 | "url": "https://github.com/trending?l=Dogescript" 317 | }, 318 | "DTrace": { 319 | "color": null, 320 | "url": "https://github.com/trending?l=DTrace" 321 | }, 322 | "Dylan": { 323 | "color": "#6c616e", 324 | "url": "https://github.com/trending?l=Dylan" 325 | }, 326 | "E": { 327 | "color": "#ccce35", 328 | "url": "https://github.com/trending?l=E" 329 | }, 330 | "eC": { 331 | "color": "#913960", 332 | "url": "https://github.com/trending?l=eC" 333 | }, 334 | "ECL": { 335 | "color": "#8a1267", 336 | "url": "https://github.com/trending?l=ECL" 337 | }, 338 | "ECLiPSe": { 339 | "color": null, 340 | "url": "https://github.com/trending?l=ECLiPSe" 341 | }, 342 | "Eiffel": { 343 | "color": "#946d57", 344 | "url": "https://github.com/trending?l=Eiffel" 345 | }, 346 | "Elixir": { 347 | "color": "#6e4a7e", 348 | "url": "https://github.com/trending?l=Elixir" 349 | }, 350 | "Elm": { 351 | "color": "#60B5CC", 352 | "url": "https://github.com/trending?l=Elm" 353 | }, 354 | "Emacs Lisp": { 355 | "color": "#c065db", 356 | "url": "https://github.com/trending?l=Emacs-Lisp" 357 | }, 358 | "EmberScript": { 359 | "color": "#FFF4F3", 360 | "url": "https://github.com/trending?l=EmberScript" 361 | }, 362 | "EQ": { 363 | "color": "#a78649", 364 | "url": "https://github.com/trending?l=EQ" 365 | }, 366 | "Erlang": { 367 | "color": "#B83998", 368 | "url": "https://github.com/trending?l=Erlang" 369 | }, 370 | "F#": { 371 | "color": "#b845fc", 372 | "url": "https://github.com/trending?l=Fsharp" 373 | }, 374 | "Factor": { 375 | "color": "#636746", 376 | "url": "https://github.com/trending?l=Factor" 377 | }, 378 | "Fancy": { 379 | "color": "#7b9db4", 380 | "url": "https://github.com/trending?l=Fancy" 381 | }, 382 | "Fantom": { 383 | "color": "#14253c", 384 | "url": "https://github.com/trending?l=Fantom" 385 | }, 386 | "Filebench WML": { 387 | "color": null, 388 | "url": "https://github.com/trending?l=Filebench-WML" 389 | }, 390 | "Filterscript": { 391 | "color": null, 392 | "url": "https://github.com/trending?l=Filterscript" 393 | }, 394 | "fish": { 395 | "color": null, 396 | "url": "https://github.com/trending?l=fish" 397 | }, 398 | "FLUX": { 399 | "color": "#88ccff", 400 | "url": "https://github.com/trending?l=FLUX" 401 | }, 402 | "Forth": { 403 | "color": "#341708", 404 | "url": "https://github.com/trending?l=Forth" 405 | }, 406 | "Fortran": { 407 | "color": "#4d41b1", 408 | "url": "https://github.com/trending?l=Fortran" 409 | }, 410 | "FreeMarker": { 411 | "color": "#0050b2", 412 | "url": "https://github.com/trending?l=FreeMarker" 413 | }, 414 | "Frege": { 415 | "color": "#00cafe", 416 | "url": "https://github.com/trending?l=Frege" 417 | }, 418 | "Game Maker Language": { 419 | "color": "#71b417", 420 | "url": "https://github.com/trending?l=Game-Maker-Language" 421 | }, 422 | "GAMS": { 423 | "color": null, 424 | "url": "https://github.com/trending?l=GAMS" 425 | }, 426 | "GAP": { 427 | "color": null, 428 | "url": "https://github.com/trending?l=GAP" 429 | }, 430 | "GCC Machine Description": { 431 | "color": null, 432 | "url": "https://github.com/trending?l=GCC-Machine-Description" 433 | }, 434 | "GDB": { 435 | "color": null, 436 | "url": "https://github.com/trending?l=GDB" 437 | }, 438 | "GDScript": { 439 | "color": "#355570", 440 | "url": "https://github.com/trending?l=GDScript" 441 | }, 442 | "Genie": { 443 | "color": "#fb855d", 444 | "url": "https://github.com/trending?l=Genie" 445 | }, 446 | "Genshi": { 447 | "color": null, 448 | "url": "https://github.com/trending?l=Genshi" 449 | }, 450 | "Gentoo Ebuild": { 451 | "color": null, 452 | "url": "https://github.com/trending?l=Gentoo-Ebuild" 453 | }, 454 | "Gentoo Eclass": { 455 | "color": null, 456 | "url": "https://github.com/trending?l=Gentoo-Eclass" 457 | }, 458 | "Gherkin": { 459 | "color": "#5B2063", 460 | "url": "https://github.com/trending?l=Gherkin" 461 | }, 462 | "GLSL": { 463 | "color": null, 464 | "url": "https://github.com/trending?l=GLSL" 465 | }, 466 | "Glyph": { 467 | "color": "#c1ac7f", 468 | "url": "https://github.com/trending?l=Glyph" 469 | }, 470 | "Gnuplot": { 471 | "color": "#f0a9f0", 472 | "url": "https://github.com/trending?l=Gnuplot" 473 | }, 474 | "Go": { 475 | "color": "#00ADD8", 476 | "url": "https://github.com/trending?l=Go" 477 | }, 478 | "Golo": { 479 | "color": "#88562A", 480 | "url": "https://github.com/trending?l=Golo" 481 | }, 482 | "Gosu": { 483 | "color": "#82937f", 484 | "url": "https://github.com/trending?l=Gosu" 485 | }, 486 | "Grace": { 487 | "color": null, 488 | "url": "https://github.com/trending?l=Grace" 489 | }, 490 | "Grammatical Framework": { 491 | "color": "#79aa7a", 492 | "url": "https://github.com/trending?l=Grammatical-Framework" 493 | }, 494 | "Groovy": { 495 | "color": "#e69f56", 496 | "url": "https://github.com/trending?l=Groovy" 497 | }, 498 | "Groovy Server Pages": { 499 | "color": null, 500 | "url": "https://github.com/trending?l=Groovy-Server-Pages" 501 | }, 502 | "Hack": { 503 | "color": "#878787", 504 | "url": "https://github.com/trending?l=Hack" 505 | }, 506 | "Harbour": { 507 | "color": "#0e60e3", 508 | "url": "https://github.com/trending?l=Harbour" 509 | }, 510 | "Haskell": { 511 | "color": "#5e5086", 512 | "url": "https://github.com/trending?l=Haskell" 513 | }, 514 | "Haxe": { 515 | "color": "#df7900", 516 | "url": "https://github.com/trending?l=Haxe" 517 | }, 518 | "HCL": { 519 | "color": null, 520 | "url": "https://github.com/trending?l=HCL" 521 | }, 522 | "HiveQL": { 523 | "color": "#dce200", 524 | "url": "https://github.com/trending?l=HiveQL" 525 | }, 526 | "HLSL": { 527 | "color": null, 528 | "url": "https://github.com/trending?l=HLSL" 529 | }, 530 | "HTML": { 531 | "color": "#e34c26", 532 | "url": "https://github.com/trending?l=HTML" 533 | }, 534 | "Hy": { 535 | "color": "#7790B2", 536 | "url": "https://github.com/trending?l=Hy" 537 | }, 538 | "HyPhy": { 539 | "color": null, 540 | "url": "https://github.com/trending?l=HyPhy" 541 | }, 542 | "IDL": { 543 | "color": "#a3522f", 544 | "url": "https://github.com/trending?l=IDL" 545 | }, 546 | "Idris": { 547 | "color": "#b30000", 548 | "url": "https://github.com/trending?l=Idris" 549 | }, 550 | "IGOR Pro": { 551 | "color": null, 552 | "url": "https://github.com/trending?l=IGOR-Pro" 553 | }, 554 | "Inform 7": { 555 | "color": null, 556 | "url": "https://github.com/trending?l=Inform-7" 557 | }, 558 | "Inno Setup": { 559 | "color": null, 560 | "url": "https://github.com/trending?l=Inno-Setup" 561 | }, 562 | "Io": { 563 | "color": "#a9188d", 564 | "url": "https://github.com/trending?l=Io" 565 | }, 566 | "Ioke": { 567 | "color": "#078193", 568 | "url": "https://github.com/trending?l=Ioke" 569 | }, 570 | "Isabelle": { 571 | "color": "#FEFE00", 572 | "url": "https://github.com/trending?l=Isabelle" 573 | }, 574 | "Isabelle ROOT": { 575 | "color": null, 576 | "url": "https://github.com/trending?l=Isabelle-ROOT" 577 | }, 578 | "J": { 579 | "color": "#9EEDFF", 580 | "url": "https://github.com/trending?l=J" 581 | }, 582 | "Jasmin": { 583 | "color": null, 584 | "url": "https://github.com/trending?l=Jasmin" 585 | }, 586 | "Java": { 587 | "color": "#b07219", 588 | "url": "https://github.com/trending?l=Java" 589 | }, 590 | "Java Server Pages": { 591 | "color": null, 592 | "url": "https://github.com/trending?l=Java-Server-Pages" 593 | }, 594 | "JavaScript": { 595 | "color": "#f1e05a", 596 | "url": "https://github.com/trending?l=JavaScript" 597 | }, 598 | "JFlex": { 599 | "color": null, 600 | "url": "https://github.com/trending?l=JFlex" 601 | }, 602 | "Jison": { 603 | "color": null, 604 | "url": "https://github.com/trending?l=Jison" 605 | }, 606 | "Jison Lex": { 607 | "color": null, 608 | "url": "https://github.com/trending?l=Jison-Lex" 609 | }, 610 | "Jolie": { 611 | "color": "#843179", 612 | "url": "https://github.com/trending?l=Jolie" 613 | }, 614 | "JSONiq": { 615 | "color": "#40d47e", 616 | "url": "https://github.com/trending?l=JSONiq" 617 | }, 618 | "JSX": { 619 | "color": null, 620 | "url": "https://github.com/trending?l=JSX" 621 | }, 622 | "Julia": { 623 | "color": "#a270ba", 624 | "url": "https://github.com/trending?l=Julia" 625 | }, 626 | "Jupyter Notebook": { 627 | "color": "#DA5B0B", 628 | "url": "https://github.com/trending?l=Jupyter-Notebook" 629 | }, 630 | "Kotlin": { 631 | "color": "#F18E33", 632 | "url": "https://github.com/trending?l=Kotlin" 633 | }, 634 | "KRL": { 635 | "color": "#28430A", 636 | "url": "https://github.com/trending?l=KRL" 637 | }, 638 | "LabVIEW": { 639 | "color": null, 640 | "url": "https://github.com/trending?l=LabVIEW" 641 | }, 642 | "Lasso": { 643 | "color": "#999999", 644 | "url": "https://github.com/trending?l=Lasso" 645 | }, 646 | "Lean": { 647 | "color": null, 648 | "url": "https://github.com/trending?l=Lean" 649 | }, 650 | "Lex": { 651 | "color": "#DBCA00", 652 | "url": "https://github.com/trending?l=Lex" 653 | }, 654 | "LFE": { 655 | "color": "#4C3023", 656 | "url": "https://github.com/trending?l=LFE" 657 | }, 658 | "LilyPond": { 659 | "color": null, 660 | "url": "https://github.com/trending?l=LilyPond" 661 | }, 662 | "Limbo": { 663 | "color": null, 664 | "url": "https://github.com/trending?l=Limbo" 665 | }, 666 | "Literate Agda": { 667 | "color": null, 668 | "url": "https://github.com/trending?l=Literate-Agda" 669 | }, 670 | "Literate CoffeeScript": { 671 | "color": null, 672 | "url": "https://github.com/trending?l=Literate-CoffeeScript" 673 | }, 674 | "Literate Haskell": { 675 | "color": null, 676 | "url": "https://github.com/trending?l=Literate-Haskell" 677 | }, 678 | "LiveScript": { 679 | "color": "#499886", 680 | "url": "https://github.com/trending?l=LiveScript" 681 | }, 682 | "LLVM": { 683 | "color": "#185619", 684 | "url": "https://github.com/trending?l=LLVM" 685 | }, 686 | "Logos": { 687 | "color": null, 688 | "url": "https://github.com/trending?l=Logos" 689 | }, 690 | "Logtalk": { 691 | "color": null, 692 | "url": "https://github.com/trending?l=Logtalk" 693 | }, 694 | "LOLCODE": { 695 | "color": "#cc9900", 696 | "url": "https://github.com/trending?l=LOLCODE" 697 | }, 698 | "LookML": { 699 | "color": "#652B81", 700 | "url": "https://github.com/trending?l=LookML" 701 | }, 702 | "LoomScript": { 703 | "color": null, 704 | "url": "https://github.com/trending?l=LoomScript" 705 | }, 706 | "LSL": { 707 | "color": "#3d9970", 708 | "url": "https://github.com/trending?l=LSL" 709 | }, 710 | "Lua": { 711 | "color": "#000080", 712 | "url": "https://github.com/trending?l=Lua" 713 | }, 714 | "M": { 715 | "color": null, 716 | "url": "https://github.com/trending?l=M" 717 | }, 718 | "M4": { 719 | "color": null, 720 | "url": "https://github.com/trending?l=M4" 721 | }, 722 | "M4Sugar": { 723 | "color": null, 724 | "url": "https://github.com/trending?l=M4Sugar" 725 | }, 726 | "Makefile": { 727 | "color": "#427819", 728 | "url": "https://github.com/trending?l=Makefile" 729 | }, 730 | "Mako": { 731 | "color": null, 732 | "url": "https://github.com/trending?l=Mako" 733 | }, 734 | "Mask": { 735 | "color": "#f97732", 736 | "url": "https://github.com/trending?l=Mask" 737 | }, 738 | "Mathematica": { 739 | "color": null, 740 | "url": "https://github.com/trending?l=Mathematica" 741 | }, 742 | "Matlab": { 743 | "color": "#e16737", 744 | "url": "https://github.com/trending?l=Matlab" 745 | }, 746 | "Max": { 747 | "color": "#c4a79c", 748 | "url": "https://github.com/trending?l=Max" 749 | }, 750 | "MAXScript": { 751 | "color": "#00a6a6", 752 | "url": "https://github.com/trending?l=MAXScript" 753 | }, 754 | "Mercury": { 755 | "color": "#ff2b2b", 756 | "url": "https://github.com/trending?l=Mercury" 757 | }, 758 | "Meson": { 759 | "color": "#007800", 760 | "url": "https://github.com/trending?l=Meson" 761 | }, 762 | "Metal": { 763 | "color": "#8f14e9", 764 | "url": "https://github.com/trending?l=Metal" 765 | }, 766 | "MiniD": { 767 | "color": null, 768 | "url": "https://github.com/trending?l=MiniD" 769 | }, 770 | "Mirah": { 771 | "color": "#c7a938", 772 | "url": "https://github.com/trending?l=Mirah" 773 | }, 774 | "Modelica": { 775 | "color": null, 776 | "url": "https://github.com/trending?l=Modelica" 777 | }, 778 | "Modula-2": { 779 | "color": null, 780 | "url": "https://github.com/trending?l=Modula-2" 781 | }, 782 | "Module Management System": { 783 | "color": null, 784 | "url": "https://github.com/trending?l=Module-Management-System" 785 | }, 786 | "Monkey": { 787 | "color": null, 788 | "url": "https://github.com/trending?l=Monkey" 789 | }, 790 | "Moocode": { 791 | "color": null, 792 | "url": "https://github.com/trending?l=Moocode" 793 | }, 794 | "MoonScript": { 795 | "color": null, 796 | "url": "https://github.com/trending?l=MoonScript" 797 | }, 798 | "MQL4": { 799 | "color": "#62A8D6", 800 | "url": "https://github.com/trending?l=MQL4" 801 | }, 802 | "MQL5": { 803 | "color": "#4A76B8", 804 | "url": "https://github.com/trending?l=MQL5" 805 | }, 806 | "MTML": { 807 | "color": "#b7e1f4", 808 | "url": "https://github.com/trending?l=MTML" 809 | }, 810 | "MUF": { 811 | "color": null, 812 | "url": "https://github.com/trending?l=MUF" 813 | }, 814 | "mupad": { 815 | "color": null, 816 | "url": "https://github.com/trending?l=mupad" 817 | }, 818 | "Myghty": { 819 | "color": null, 820 | "url": "https://github.com/trending?l=Myghty" 821 | }, 822 | "NCL": { 823 | "color": "#28431f", 824 | "url": "https://github.com/trending?l=NCL" 825 | }, 826 | "Nearley": { 827 | "color": "#990000", 828 | "url": "https://github.com/trending?l=Nearley" 829 | }, 830 | "Nemerle": { 831 | "color": "#3d3c6e", 832 | "url": "https://github.com/trending?l=Nemerle" 833 | }, 834 | "nesC": { 835 | "color": "#94B0C7", 836 | "url": "https://github.com/trending?l=nesC" 837 | }, 838 | "NetLinx": { 839 | "color": "#0aa0ff", 840 | "url": "https://github.com/trending?l=NetLinx" 841 | }, 842 | "NetLinx+ERB": { 843 | "color": "#747faa", 844 | "url": "https://github.com/trending?l=NetLinx+ERB" 845 | }, 846 | "NetLogo": { 847 | "color": "#ff6375", 848 | "url": "https://github.com/trending?l=NetLogo" 849 | }, 850 | "NewLisp": { 851 | "color": "#87AED7", 852 | "url": "https://github.com/trending?l=NewLisp" 853 | }, 854 | "Nextflow": { 855 | "color": "#3ac486", 856 | "url": "https://github.com/trending?l=Nextflow" 857 | }, 858 | "Nim": { 859 | "color": "#37775b", 860 | "url": "https://github.com/trending?l=Nim" 861 | }, 862 | "Nit": { 863 | "color": "#009917", 864 | "url": "https://github.com/trending?l=Nit" 865 | }, 866 | "Nix": { 867 | "color": "#7e7eff", 868 | "url": "https://github.com/trending?l=Nix" 869 | }, 870 | "NSIS": { 871 | "color": null, 872 | "url": "https://github.com/trending?l=NSIS" 873 | }, 874 | "Nu": { 875 | "color": "#c9df40", 876 | "url": "https://github.com/trending?l=Nu" 877 | }, 878 | "NumPy": { 879 | "color": null, 880 | "url": "https://github.com/trending?l=NumPy" 881 | }, 882 | "Objective-C": { 883 | "color": "#438eff", 884 | "url": "https://github.com/trending?l=Objective-C" 885 | }, 886 | "Objective-C++": { 887 | "color": "#6866fb", 888 | "url": "https://github.com/trending?l=Objective-C++" 889 | }, 890 | "Objective-J": { 891 | "color": "#ff0c5a", 892 | "url": "https://github.com/trending?l=Objective-J" 893 | }, 894 | "OCaml": { 895 | "color": "#3be133", 896 | "url": "https://github.com/trending?l=OCaml" 897 | }, 898 | "Omgrofl": { 899 | "color": "#cabbff", 900 | "url": "https://github.com/trending?l=Omgrofl" 901 | }, 902 | "ooc": { 903 | "color": "#b0b77e", 904 | "url": "https://github.com/trending?l=ooc" 905 | }, 906 | "Opa": { 907 | "color": null, 908 | "url": "https://github.com/trending?l=Opa" 909 | }, 910 | "Opal": { 911 | "color": "#f7ede0", 912 | "url": "https://github.com/trending?l=Opal" 913 | }, 914 | "OpenCL": { 915 | "color": null, 916 | "url": "https://github.com/trending?l=OpenCL" 917 | }, 918 | "OpenEdge ABL": { 919 | "color": null, 920 | "url": "https://github.com/trending?l=OpenEdge-ABL" 921 | }, 922 | "OpenRC runscript": { 923 | "color": null, 924 | "url": "https://github.com/trending?l=OpenRC-runscript" 925 | }, 926 | "OpenSCAD": { 927 | "color": null, 928 | "url": "https://github.com/trending?l=OpenSCAD" 929 | }, 930 | "Ox": { 931 | "color": null, 932 | "url": "https://github.com/trending?l=Ox" 933 | }, 934 | "Oxygene": { 935 | "color": "#cdd0e3", 936 | "url": "https://github.com/trending?l=Oxygene" 937 | }, 938 | "Oz": { 939 | "color": "#fab738", 940 | "url": "https://github.com/trending?l=Oz" 941 | }, 942 | "P4": { 943 | "color": "#7055b5", 944 | "url": "https://github.com/trending?l=P4" 945 | }, 946 | "Pan": { 947 | "color": "#cc0000", 948 | "url": "https://github.com/trending?l=Pan" 949 | }, 950 | "Papyrus": { 951 | "color": "#6600cc", 952 | "url": "https://github.com/trending?l=Papyrus" 953 | }, 954 | "Parrot": { 955 | "color": "#f3ca0a", 956 | "url": "https://github.com/trending?l=Parrot" 957 | }, 958 | "Parrot Assembly": { 959 | "color": null, 960 | "url": "https://github.com/trending?l=Parrot-Assembly" 961 | }, 962 | "Parrot Internal Representation": { 963 | "color": null, 964 | "url": "https://github.com/trending?l=Parrot-Internal-Representation" 965 | }, 966 | "Pascal": { 967 | "color": "#E3F171", 968 | "url": "https://github.com/trending?l=Pascal" 969 | }, 970 | "PAWN": { 971 | "color": "#dbb284", 972 | "url": "https://github.com/trending?l=PAWN" 973 | }, 974 | "Pep8": { 975 | "color": "#C76F5B", 976 | "url": "https://github.com/trending?l=Pep8" 977 | }, 978 | "Perl": { 979 | "color": "#0298c3", 980 | "url": "https://github.com/trending?l=Perl" 981 | }, 982 | "Perl 6": { 983 | "color": "#0000fb", 984 | "url": "https://github.com/trending?l=Perl-6" 985 | }, 986 | "PHP": { 987 | "color": "#4F5D95", 988 | "url": "https://github.com/trending?l=PHP" 989 | }, 990 | "PicoLisp": { 991 | "color": null, 992 | "url": "https://github.com/trending?l=PicoLisp" 993 | }, 994 | "PigLatin": { 995 | "color": "#fcd7de", 996 | "url": "https://github.com/trending?l=PigLatin" 997 | }, 998 | "Pike": { 999 | "color": "#005390", 1000 | "url": "https://github.com/trending?l=Pike" 1001 | }, 1002 | "PLpgSQL": { 1003 | "color": null, 1004 | "url": "https://github.com/trending?l=PLpgSQL" 1005 | }, 1006 | "PLSQL": { 1007 | "color": "#dad8d8", 1008 | "url": "https://github.com/trending?l=PLSQL" 1009 | }, 1010 | "PogoScript": { 1011 | "color": "#d80074", 1012 | "url": "https://github.com/trending?l=PogoScript" 1013 | }, 1014 | "Pony": { 1015 | "color": null, 1016 | "url": "https://github.com/trending?l=Pony" 1017 | }, 1018 | "PostScript": { 1019 | "color": "#da291c", 1020 | "url": "https://github.com/trending?l=PostScript" 1021 | }, 1022 | "POV-Ray SDL": { 1023 | "color": null, 1024 | "url": "https://github.com/trending?l=POV-Ray-SDL" 1025 | }, 1026 | "PowerBuilder": { 1027 | "color": "#8f0f8d", 1028 | "url": "https://github.com/trending?l=PowerBuilder" 1029 | }, 1030 | "PowerShell": { 1031 | "color": "#012456", 1032 | "url": "https://github.com/trending?l=PowerShell" 1033 | }, 1034 | "Processing": { 1035 | "color": "#0096D8", 1036 | "url": "https://github.com/trending?l=Processing" 1037 | }, 1038 | "Prolog": { 1039 | "color": "#74283c", 1040 | "url": "https://github.com/trending?l=Prolog" 1041 | }, 1042 | "Propeller Spin": { 1043 | "color": "#7fa2a7", 1044 | "url": "https://github.com/trending?l=Propeller-Spin" 1045 | }, 1046 | "Puppet": { 1047 | "color": "#302B6D", 1048 | "url": "https://github.com/trending?l=Puppet" 1049 | }, 1050 | "PureBasic": { 1051 | "color": "#5a6986", 1052 | "url": "https://github.com/trending?l=PureBasic" 1053 | }, 1054 | "PureScript": { 1055 | "color": "#1D222D", 1056 | "url": "https://github.com/trending?l=PureScript" 1057 | }, 1058 | "Python": { 1059 | "color": "#3572A5", 1060 | "url": "https://github.com/trending?l=Python" 1061 | }, 1062 | "Python console": { 1063 | "color": null, 1064 | "url": "https://github.com/trending?l=Python-console" 1065 | }, 1066 | "q": { 1067 | "color": "#0040cd", 1068 | "url": "https://github.com/trending?l=q" 1069 | }, 1070 | "QMake": { 1071 | "color": null, 1072 | "url": "https://github.com/trending?l=QMake" 1073 | }, 1074 | "QML": { 1075 | "color": "#44a51c", 1076 | "url": "https://github.com/trending?l=QML" 1077 | }, 1078 | "R": { 1079 | "color": "#198CE7", 1080 | "url": "https://github.com/trending?l=R" 1081 | }, 1082 | "Racket": { 1083 | "color": "#3c5caa", 1084 | "url": "https://github.com/trending?l=Racket" 1085 | }, 1086 | "Ragel": { 1087 | "color": "#9d5200", 1088 | "url": "https://github.com/trending?l=Ragel" 1089 | }, 1090 | "RAML": { 1091 | "color": "#77d9fb", 1092 | "url": "https://github.com/trending?l=RAML" 1093 | }, 1094 | "Rascal": { 1095 | "color": "#fffaa0", 1096 | "url": "https://github.com/trending?l=Rascal" 1097 | }, 1098 | "REALbasic": { 1099 | "color": null, 1100 | "url": "https://github.com/trending?l=REALbasic" 1101 | }, 1102 | "Reason": { 1103 | "color": null, 1104 | "url": "https://github.com/trending?l=Reason" 1105 | }, 1106 | "Rebol": { 1107 | "color": "#358a5b", 1108 | "url": "https://github.com/trending?l=Rebol" 1109 | }, 1110 | "Red": { 1111 | "color": "#f50000", 1112 | "url": "https://github.com/trending?l=Red" 1113 | }, 1114 | "Redcode": { 1115 | "color": null, 1116 | "url": "https://github.com/trending?l=Redcode" 1117 | }, 1118 | "Ren'Py": { 1119 | "color": "#ff7f7f", 1120 | "url": "https://github.com/trending?l=Ren'Py" 1121 | }, 1122 | "RenderScript": { 1123 | "color": null, 1124 | "url": "https://github.com/trending?l=RenderScript" 1125 | }, 1126 | "REXX": { 1127 | "color": null, 1128 | "url": "https://github.com/trending?l=REXX" 1129 | }, 1130 | "Ring": { 1131 | "color": "#2D54CB", 1132 | "url": "https://github.com/trending?l=Ring" 1133 | }, 1134 | "RobotFramework": { 1135 | "color": null, 1136 | "url": "https://github.com/trending?l=RobotFramework" 1137 | }, 1138 | "Roff": { 1139 | "color": "#ecdebe", 1140 | "url": "https://github.com/trending?l=Roff" 1141 | }, 1142 | "Rouge": { 1143 | "color": "#cc0088", 1144 | "url": "https://github.com/trending?l=Rouge" 1145 | }, 1146 | "RPC": { 1147 | "color": null, 1148 | "url": "https://github.com/trending?l=RPC" 1149 | }, 1150 | "Ruby": { 1151 | "color": "#701516", 1152 | "url": "https://github.com/trending?l=Ruby" 1153 | }, 1154 | "RUNOFF": { 1155 | "color": "#665a4e", 1156 | "url": "https://github.com/trending?l=RUNOFF" 1157 | }, 1158 | "Rust": { 1159 | "color": "#dea584", 1160 | "url": "https://github.com/trending?l=Rust" 1161 | }, 1162 | "Sage": { 1163 | "color": null, 1164 | "url": "https://github.com/trending?l=Sage" 1165 | }, 1166 | "SaltStack": { 1167 | "color": "#646464", 1168 | "url": "https://github.com/trending?l=SaltStack" 1169 | }, 1170 | "SAS": { 1171 | "color": "#B34936", 1172 | "url": "https://github.com/trending?l=SAS" 1173 | }, 1174 | "Scala": { 1175 | "color": "#c22d40", 1176 | "url": "https://github.com/trending?l=Scala" 1177 | }, 1178 | "Scheme": { 1179 | "color": "#1e4aec", 1180 | "url": "https://github.com/trending?l=Scheme" 1181 | }, 1182 | "Scilab": { 1183 | "color": null, 1184 | "url": "https://github.com/trending?l=Scilab" 1185 | }, 1186 | "sed": { 1187 | "color": "#64b970", 1188 | "url": "https://github.com/trending?l=sed" 1189 | }, 1190 | "Self": { 1191 | "color": "#0579aa", 1192 | "url": "https://github.com/trending?l=Self" 1193 | }, 1194 | "ShaderLab": { 1195 | "color": null, 1196 | "url": "https://github.com/trending?l=ShaderLab" 1197 | }, 1198 | "Shell": { 1199 | "color": "#89e051", 1200 | "url": "https://github.com/trending?l=Shell" 1201 | }, 1202 | "ShellSession": { 1203 | "color": null, 1204 | "url": "https://github.com/trending?l=ShellSession" 1205 | }, 1206 | "Shen": { 1207 | "color": "#120F14", 1208 | "url": "https://github.com/trending?l=Shen" 1209 | }, 1210 | "Slash": { 1211 | "color": "#007eff", 1212 | "url": "https://github.com/trending?l=Slash" 1213 | }, 1214 | "Smali": { 1215 | "color": null, 1216 | "url": "https://github.com/trending?l=Smali" 1217 | }, 1218 | "Smalltalk": { 1219 | "color": "#596706", 1220 | "url": "https://github.com/trending?l=Smalltalk" 1221 | }, 1222 | "Smarty": { 1223 | "color": null, 1224 | "url": "https://github.com/trending?l=Smarty" 1225 | }, 1226 | "SMT": { 1227 | "color": null, 1228 | "url": "https://github.com/trending?l=SMT" 1229 | }, 1230 | "Solidity": { 1231 | "color": "#AA6746", 1232 | "url": "https://github.com/trending?l=Solidity" 1233 | }, 1234 | "SourcePawn": { 1235 | "color": "#5c7611", 1236 | "url": "https://github.com/trending?l=SourcePawn" 1237 | }, 1238 | "SQF": { 1239 | "color": "#3F3F3F", 1240 | "url": "https://github.com/trending?l=SQF" 1241 | }, 1242 | "SQLPL": { 1243 | "color": null, 1244 | "url": "https://github.com/trending?l=SQLPL" 1245 | }, 1246 | "Squirrel": { 1247 | "color": "#800000", 1248 | "url": "https://github.com/trending?l=Squirrel" 1249 | }, 1250 | "SRecode Template": { 1251 | "color": "#348a34", 1252 | "url": "https://github.com/trending?l=SRecode-Template" 1253 | }, 1254 | "Stan": { 1255 | "color": "#b2011d", 1256 | "url": "https://github.com/trending?l=Stan" 1257 | }, 1258 | "Standard ML": { 1259 | "color": "#dc566d", 1260 | "url": "https://github.com/trending?l=Standard-ML" 1261 | }, 1262 | "Stata": { 1263 | "color": null, 1264 | "url": "https://github.com/trending?l=Stata" 1265 | }, 1266 | "SuperCollider": { 1267 | "color": "#46390b", 1268 | "url": "https://github.com/trending?l=SuperCollider" 1269 | }, 1270 | "Swift": { 1271 | "color": "#ffac45", 1272 | "url": "https://github.com/trending?l=Swift" 1273 | }, 1274 | "SystemVerilog": { 1275 | "color": "#DAE1C2", 1276 | "url": "https://github.com/trending?l=SystemVerilog" 1277 | }, 1278 | "Tcl": { 1279 | "color": "#e4cc98", 1280 | "url": "https://github.com/trending?l=Tcl" 1281 | }, 1282 | "Tcsh": { 1283 | "color": null, 1284 | "url": "https://github.com/trending?l=Tcsh" 1285 | }, 1286 | "Terra": { 1287 | "color": "#00004c", 1288 | "url": "https://github.com/trending?l=Terra" 1289 | }, 1290 | "TeX": { 1291 | "color": "#3D6117", 1292 | "url": "https://github.com/trending?l=TeX" 1293 | }, 1294 | "Thrift": { 1295 | "color": null, 1296 | "url": "https://github.com/trending?l=Thrift" 1297 | }, 1298 | "TI Program": { 1299 | "color": "#A0AA87", 1300 | "url": "https://github.com/trending?l=TI-Program" 1301 | }, 1302 | "TLA": { 1303 | "color": null, 1304 | "url": "https://github.com/trending?l=TLA" 1305 | }, 1306 | "Turing": { 1307 | "color": "#cf142b", 1308 | "url": "https://github.com/trending?l=Turing" 1309 | }, 1310 | "TXL": { 1311 | "color": null, 1312 | "url": "https://github.com/trending?l=TXL" 1313 | }, 1314 | "TypeScript": { 1315 | "color": "#2b7489", 1316 | "url": "https://github.com/trending?l=TypeScript" 1317 | }, 1318 | "Unified Parallel C": { 1319 | "color": null, 1320 | "url": "https://github.com/trending?l=Unified-Parallel-C" 1321 | }, 1322 | "Unix Assembly": { 1323 | "color": null, 1324 | "url": "https://github.com/trending?l=Unix-Assembly" 1325 | }, 1326 | "Uno": { 1327 | "color": null, 1328 | "url": "https://github.com/trending?l=Uno" 1329 | }, 1330 | "UnrealScript": { 1331 | "color": "#a54c4d", 1332 | "url": "https://github.com/trending?l=UnrealScript" 1333 | }, 1334 | "UrWeb": { 1335 | "color": null, 1336 | "url": "https://github.com/trending?l=UrWeb" 1337 | }, 1338 | "Vala": { 1339 | "color": "#fbe5cd", 1340 | "url": "https://github.com/trending?l=Vala" 1341 | }, 1342 | "VCL": { 1343 | "color": "#148AA8", 1344 | "url": "https://github.com/trending?l=VCL" 1345 | }, 1346 | "Verilog": { 1347 | "color": "#b2b7f8", 1348 | "url": "https://github.com/trending?l=Verilog" 1349 | }, 1350 | "VHDL": { 1351 | "color": "#adb2cb", 1352 | "url": "https://github.com/trending?l=VHDL" 1353 | }, 1354 | "Vim script": { 1355 | "color": "#199f4b", 1356 | "url": "https://github.com/trending?l=Vim-script" 1357 | }, 1358 | "Visual Basic": { 1359 | "color": "#945db7", 1360 | "url": "https://github.com/trending?l=Visual-Basic" 1361 | }, 1362 | "Volt": { 1363 | "color": "#1F1F1F", 1364 | "url": "https://github.com/trending?l=Volt" 1365 | }, 1366 | "Vue": { 1367 | "color": "#2c3e50", 1368 | "url": "https://github.com/trending?l=Vue" 1369 | }, 1370 | "wdl": { 1371 | "color": "#42f1f4", 1372 | "url": "https://github.com/trending?l=wdl" 1373 | }, 1374 | "WebAssembly": { 1375 | "color": "#04133b", 1376 | "url": "https://github.com/trending?l=WebAssembly" 1377 | }, 1378 | "WebIDL": { 1379 | "color": null, 1380 | "url": "https://github.com/trending?l=WebIDL" 1381 | }, 1382 | "wisp": { 1383 | "color": "#7582D1", 1384 | "url": "https://github.com/trending?l=wisp" 1385 | }, 1386 | "X10": { 1387 | "color": "#4B6BEF", 1388 | "url": "https://github.com/trending?l=X10" 1389 | }, 1390 | "xBase": { 1391 | "color": "#403a40", 1392 | "url": "https://github.com/trending?l=xBase" 1393 | }, 1394 | "XC": { 1395 | "color": "#99DA07", 1396 | "url": "https://github.com/trending?l=XC" 1397 | }, 1398 | "Xojo": { 1399 | "color": null, 1400 | "url": "https://github.com/trending?l=Xojo" 1401 | }, 1402 | "XProc": { 1403 | "color": null, 1404 | "url": "https://github.com/trending?l=XProc" 1405 | }, 1406 | "XQuery": { 1407 | "color": "#5232e7", 1408 | "url": "https://github.com/trending?l=XQuery" 1409 | }, 1410 | "XS": { 1411 | "color": null, 1412 | "url": "https://github.com/trending?l=XS" 1413 | }, 1414 | "XSLT": { 1415 | "color": "#EB8CEB", 1416 | "url": "https://github.com/trending?l=XSLT" 1417 | }, 1418 | "Xtend": { 1419 | "color": null, 1420 | "url": "https://github.com/trending?l=Xtend" 1421 | }, 1422 | "Yacc": { 1423 | "color": "#4B6C4B", 1424 | "url": "https://github.com/trending?l=Yacc" 1425 | }, 1426 | "Zephir": { 1427 | "color": "#118f9e", 1428 | "url": "https://github.com/trending?l=Zephir" 1429 | }, 1430 | "Zimpl": { 1431 | "color": null, 1432 | "url": "https://github.com/trending?l=Zimpl" 1433 | } 1434 | } 1435 | --------------------------------------------------------------------------------