├── 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 :
}
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 |
87 |
88 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------