mirror of
https://github.com/LucasVbr/LucasVbr.git
synced 2026-05-13 17:11:52 +00:00
+4
-1
@@ -1 +1,4 @@
|
|||||||
.idea/
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|||||||
@@ -1,49 +1,56 @@
|
|||||||

|
<header>
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
## 🚀 About Me
|
## 🚀 About Me
|
||||||
I'm a passionate developer from 🇫🇷 **Albi, France**.
|
|
||||||
|
|
||||||
## 🔗 Links
|
I'm a passionate developer from 🇫🇷 **Pau, France**.
|
||||||
[](https://lucasvbr.github.io/links/?portfolio)
|
|
||||||
[](https://www.linkedin.com/in/lucasvbr)
|
|
||||||
[](https://www.freecodecamp.org/LucasVbr)
|
|
||||||
[](https://openclassrooms.com/fr/members/97j9zltv6225)
|
|
||||||
[](https://exercism.org/profiles/LucasVbr)
|
|
||||||
|
|
||||||
|
## 🔗 Social Links
|
||||||
|
|
||||||
|
[](https://www.linkedin.com/in/lucasvbr)
|
||||||
|
[](https://www.freecodecamp.org/LucasVbr)
|
||||||
|
[](https://openclassrooms.com/fr/members/97j9zltv6225)
|
||||||
|
[](https://exercism.org/profiles/LucasVbr)
|
||||||
|
|
||||||
## 🛠 Skills
|
## 🛠 Skills
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<footer>
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||

|

|
||||||
@@ -52,5 +59,5 @@ I'm a passionate developer from 🇫🇷 **Albi, France**.
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</footer>
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 1000 400" width="1000" height="400">
|
||||||
|
<foreignObject width="100%" height="100%">
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 400px;
|
||||||
|
/* background: linear-gradient(-45deg, #fc5c7d, #6a82fb, #05dfd7); */
|
||||||
|
background: #333;
|
||||||
|
background-size: 600% 400%;
|
||||||
|
border-radius: 10px;
|
||||||
|
/* color: white; */
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 5em;
|
||||||
|
letter-spacing: 8px;
|
||||||
|
font-family: "Lucida Console", Monaco, monospace;
|
||||||
|
font-weight: 400;
|
||||||
|
/*Create overlap*/
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
line-height: 0;
|
||||||
|
/*Animation*/
|
||||||
|
|
||||||
|
animation: glitch1 2.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1:nth-child(2) {
|
||||||
|
color: #67f3da;
|
||||||
|
animation: glitch2 2.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1:nth-child(3) {
|
||||||
|
color: #f16f6f;
|
||||||
|
animation: glitch3 2.5s infinite;
|
||||||
|
}
|
||||||
|
/*Keyframes*/
|
||||||
|
|
||||||
|
@keyframes glitch1 {
|
||||||
|
0% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
7% {
|
||||||
|
transform: skew(-0.5deg, -0.9deg);
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
10% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
27% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
transform: skew(0.8deg, -0.1deg);
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
35% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
52% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
55% {
|
||||||
|
transform: skew(-1deg, 0.2deg);
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
72% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: skew(0.4deg, 1deg);
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes glitch2 {
|
||||||
|
0% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
7% {
|
||||||
|
transform: translate(-2px, -3px);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
10% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
27% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
transform: translate(-5px, -2px);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
35% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
52% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
55% {
|
||||||
|
transform: translate(-5px, -1px);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
72% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: translate(-2px, -6px);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes glitch3 {
|
||||||
|
0% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
7% {
|
||||||
|
transform: translate(2px, 3px);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
10% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
27% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
transform: translate(5px, 2px);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
35% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
52% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
55% {
|
||||||
|
transform: translate(5px, 1px);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
72% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: translate(2px, 6px);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Hi I'm Lucàs👋</h1>
|
||||||
|
<h1>Hi I'm Lucàs👋</h1>
|
||||||
|
<h1>Hi I'm Lucàs👋</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</foreignObject>
|
||||||
|
<script xmlns=""/></svg>
|
||||||
|
After Width: | Height: | Size: 7.2 KiB |
@@ -1,3 +0,0 @@
|
|||||||
const ImageComponent = (label: string, url: URL) => ``;
|
|
||||||
|
|
||||||
export default ImageComponent
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
const LinkComponent = (label: string, url: string) => `[${label}](${url})`;
|
|
||||||
|
|
||||||
export default LinkComponent;
|
|
||||||
+39
-123
@@ -1,128 +1,44 @@
|
|||||||
file:
|
user: "LucasVbr"
|
||||||
template: "template.njk"
|
|
||||||
output: "README.md"
|
|
||||||
|
|
||||||
view:
|
socials:
|
||||||
userName: "LucasVbr"
|
- name: "Linkedin"
|
||||||
|
url: "https://www.linkedin.com/in/lucasvbr"
|
||||||
|
|
||||||
banner:
|
- name: "FreeCodeCamp"
|
||||||
type: "glitch"
|
url: "https://www.freecodecamp.org/LucasVbr"
|
||||||
text1: "Hi I'm Lucàs👋"
|
|
||||||
width: 1000
|
|
||||||
height: 400
|
|
||||||
|
|
||||||
about: "I'm a passionate developer from 🇫🇷 **Albi, France**."
|
- name: "OpenClassRooms"
|
||||||
|
url: "https://openclassrooms.com/fr/members/97j9zltv6225"
|
||||||
|
|
||||||
links:
|
- name: "Exercism"
|
||||||
- label:
|
url: "https://exercism.org/profiles/LucasVbr"
|
||||||
message: "My Portfolio"
|
|
||||||
logo: "ko-fi"
|
|
||||||
url: "https://lucasvbr.github.io/links/?portfolio"
|
|
||||||
|
|
||||||
- label:
|
skills:
|
||||||
message: "Linkedin"
|
- "Android"
|
||||||
color: "0e76a8"
|
- "Angular"
|
||||||
url: "https://www.linkedin.com/in/lucasvbr"
|
- "Bootstrap"
|
||||||
|
- "Bulma"
|
||||||
- label:
|
- "C"
|
||||||
message: "FreeCodeCamp"
|
- "CSS3"
|
||||||
color: "0a0a23"
|
- "Deno"
|
||||||
url: "https://www.freecodecamp.org/LucasVbr"
|
- "Docker"
|
||||||
|
- "Express"
|
||||||
- label:
|
- "Figma"
|
||||||
message: "OpenClassRooms"
|
- "Git"
|
||||||
color: "7451eb"
|
- "GNU Bash"
|
||||||
url: "https://openclassrooms.com/fr/members/97j9zltv6225"
|
- "HTML5"
|
||||||
|
- "JavaScript"
|
||||||
- label:
|
- "MariaDB"
|
||||||
message: "Exercism"
|
- "MongoDB"
|
||||||
color: "2e57e8"
|
- "MySQL"
|
||||||
url: "https://exercism.org/profiles/LucasVbr"
|
- "Node.js"
|
||||||
|
- "Nunjucks"
|
||||||
skills:
|
- "OCaml"
|
||||||
- message: "Android"
|
- "PHP"
|
||||||
color: "3DDC84"
|
- "PostgreSQL"
|
||||||
|
- "Pug"
|
||||||
- message: "Angular"
|
- "Python"
|
||||||
color: "DD0031"
|
- "React"
|
||||||
|
- "SQLite"
|
||||||
- message: "Bootstrap"
|
- "Symfony"
|
||||||
color: "7952B3"
|
- "TypeScript"
|
||||||
|
|
||||||
- message: "Bulma"
|
|
||||||
color: "00D1B2"
|
|
||||||
|
|
||||||
- message: "C"
|
|
||||||
color: "A8B9CC"
|
|
||||||
|
|
||||||
- message: "CSS3"
|
|
||||||
color: "1572B6"
|
|
||||||
|
|
||||||
- message: "Deno"
|
|
||||||
|
|
||||||
- message: "Docker"
|
|
||||||
color: "2496ED"
|
|
||||||
|
|
||||||
- message: "Express"
|
|
||||||
|
|
||||||
- message: "Figma"
|
|
||||||
color: "F24E1E"
|
|
||||||
|
|
||||||
- message: "Git"
|
|
||||||
color: "F05032"
|
|
||||||
|
|
||||||
- message: "GNU Bash"
|
|
||||||
color: "4EAA25"
|
|
||||||
|
|
||||||
- message: "HTML5"
|
|
||||||
color: "E34F26"
|
|
||||||
|
|
||||||
- message: "JavaScript"
|
|
||||||
color: "F7DF1E"
|
|
||||||
|
|
||||||
- message: "MariaDB"
|
|
||||||
color: "003545"
|
|
||||||
|
|
||||||
- message: "MongoDB"
|
|
||||||
color: "47A248"
|
|
||||||
|
|
||||||
- message: "MySQL"
|
|
||||||
color: "4479A1"
|
|
||||||
|
|
||||||
- message: "Node.js"
|
|
||||||
color: "339933"
|
|
||||||
logo: "nodedotjs"
|
|
||||||
|
|
||||||
- message: "Nunjucks"
|
|
||||||
color: "1C4913"
|
|
||||||
|
|
||||||
- message: "OCaml"
|
|
||||||
color: "EC6813"
|
|
||||||
|
|
||||||
- message: "PHP"
|
|
||||||
color: "777BB4"
|
|
||||||
|
|
||||||
- message: "PostgreSQL"
|
|
||||||
color: "4169E1"
|
|
||||||
|
|
||||||
- message: "Pug"
|
|
||||||
color: "A86454"
|
|
||||||
|
|
||||||
- message: "Python"
|
|
||||||
color: "3776AB"
|
|
||||||
|
|
||||||
- message: "React"
|
|
||||||
color: "61DAFB"
|
|
||||||
|
|
||||||
- message: "SQLite"
|
|
||||||
color: "003B57"
|
|
||||||
|
|
||||||
- message: "Symfony"
|
|
||||||
|
|
||||||
- message: "TypeScript"
|
|
||||||
color: "3178C6"
|
|
||||||
|
|
||||||
footer:
|
|
||||||
- ""
|
|
||||||
- ""
|
|
||||||
- ""
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
from src.config import Config
|
||||||
|
from src.template import Template
|
||||||
|
|
||||||
|
CONFIG_FILE = 'config.yaml'
|
||||||
|
TEMPLATE_FILE = 'template.md'
|
||||||
|
OUTPUT_FILE = 'README.md'
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
template = Template(TEMPLATE_FILE)
|
||||||
|
data = Config(CONFIG_FILE).get_data()
|
||||||
|
|
||||||
|
# Generate README file
|
||||||
|
render = template.render(**data)
|
||||||
|
with open(OUTPUT_FILE, 'w') as f:
|
||||||
|
f.write(render)
|
||||||
|
|
||||||
|
print(f"{OUTPUT_FILE} generated successfully! 🎉")
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import {parse} from 'https://deno.land/std@0.82.0/encoding/yaml.ts';
|
|
||||||
import nunjucks from 'https://deno.land/x/nunjucks@3.2.3-2/mod.js';
|
|
||||||
import makeContext from './src/Context.ts';
|
|
||||||
import type ConfigModel from './models/ConfigModel.ts';
|
|
||||||
|
|
||||||
// Load Config file
|
|
||||||
const config: ConfigModel = parse(await Deno.readTextFile('config.yaml')) as ConfigModel;
|
|
||||||
const context = makeContext(config.view)
|
|
||||||
|
|
||||||
// Render
|
|
||||||
nunjucks.configure('views/', {autoescape: true});
|
|
||||||
nunjucks.render(config.file.template, context, (err, res) => {
|
|
||||||
if (err) return console.error(err);
|
|
||||||
|
|
||||||
Deno.writeTextFile(config.file.output, res ?? '')
|
|
||||||
.then(() => console.info(`[INFO] ${config.file.output} successfully generated`))
|
|
||||||
.catch(err => console.error(err));
|
|
||||||
});
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
type BadgeModel = {
|
|
||||||
label?: string,
|
|
||||||
message?: string,
|
|
||||||
color?: string,
|
|
||||||
logo?: string,
|
|
||||||
style: "plastic" | "flat" | "flat-square" | "for-the-badge" | "social",
|
|
||||||
logoColor?: string,
|
|
||||||
logoWidth?: number,
|
|
||||||
link?: string,
|
|
||||||
labelColor?: string,
|
|
||||||
cacheSeconds?: number,
|
|
||||||
}
|
|
||||||
export default BadgeModel;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
type BannerModel = {
|
|
||||||
text1: string,
|
|
||||||
text2?: string,
|
|
||||||
type: "origin" | "textBox" | "glitch" | "luminance" | "typeWriter" | "rainbow",
|
|
||||||
width: number,
|
|
||||||
height: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BannerModel;
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import type BannerModel from './BannerModel.ts';
|
|
||||||
import type LinkModel from './LinkModel.ts';
|
|
||||||
import type BadgeModel from './BadgeModel.ts';
|
|
||||||
|
|
||||||
type ConfigModel = {
|
|
||||||
file: {
|
|
||||||
template: string,
|
|
||||||
output: string
|
|
||||||
},
|
|
||||||
view: {
|
|
||||||
userName: string,
|
|
||||||
banner: BannerModel,
|
|
||||||
about: string,
|
|
||||||
links: LinkModel[],
|
|
||||||
skills: BadgeModel[],
|
|
||||||
footer: string[]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ConfigModel;
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import BadgeModel from './BadgeModel.ts';
|
|
||||||
|
|
||||||
type LinkModel = {
|
|
||||||
label: BadgeModel,
|
|
||||||
url: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LinkModel
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
pyyaml
|
||||||
|
json
|
||||||
|
requests
|
||||||
|
simpleicons
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import ImageComponent from '../components/ImageComponent.ts';
|
|
||||||
import type BannerModel from '../models/BannerModel.ts';
|
|
||||||
import BannerUrlBuilder from './url_builder/BannerUrlBuilder.ts';
|
|
||||||
|
|
||||||
export default class Banner {
|
|
||||||
|
|
||||||
constructor(private model: BannerModel) {
|
|
||||||
};
|
|
||||||
|
|
||||||
toString(): string {
|
|
||||||
const url = new BannerUrlBuilder()
|
|
||||||
.setParameters({...this.model})
|
|
||||||
.build()
|
|
||||||
;
|
|
||||||
|
|
||||||
return ImageComponent(this.model.text1, url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import Skill from './Skill.ts';
|
|
||||||
import Banner from './Banner.ts';
|
|
||||||
import Link from './Link.ts';
|
|
||||||
|
|
||||||
export default function makeContext(viewConfig) {
|
|
||||||
return {
|
|
||||||
...viewConfig,
|
|
||||||
banner: new Banner(viewConfig.banner).toString(),
|
|
||||||
links: viewConfig.links.map(link => (new Link(link)).toString()),
|
|
||||||
skills: viewConfig.skills.map(skill => (new Skill(skill)).toString()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
import BadgeUrlBuilder from './url_builder/BadgeUrlBuilder.ts';
|
|
||||||
import ImageComponent from '../components/ImageComponent.ts';
|
|
||||||
import LinkModel from '../models/LinkModel.ts';
|
|
||||||
import LinkComponent from '../components/LinkComponent.ts';
|
|
||||||
|
|
||||||
export default class Link {
|
|
||||||
|
|
||||||
constructor(private model: LinkModel) {}
|
|
||||||
|
|
||||||
handleLabel() {
|
|
||||||
const {label: badge} = this.model;
|
|
||||||
const defaultLogo = badge.message.toLowerCase().replace(/\s+/g, '')
|
|
||||||
|
|
||||||
const url = new BadgeUrlBuilder()
|
|
||||||
.setParameters({
|
|
||||||
...badge,
|
|
||||||
style: "for-the-badge",
|
|
||||||
logo: badge.logo ?? defaultLogo,
|
|
||||||
color: badge.color ?? '000',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
;
|
|
||||||
|
|
||||||
return ImageComponent(badge.message, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
toString(): string {
|
|
||||||
const label = this.handleLabel().toString()
|
|
||||||
return LinkComponent(label, this.model.url).toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import BadgeUrlBuilder from './url_builder/BadgeUrlBuilder.ts';
|
|
||||||
import ImageComponent from '../components/ImageComponent.ts';
|
|
||||||
import type SkillModel from '../models/SkillModel.ts';
|
|
||||||
|
|
||||||
export default class Skill {
|
|
||||||
|
|
||||||
constructor(private model: SkillModel) {};
|
|
||||||
|
|
||||||
toString(): string {
|
|
||||||
const defaultLogo = this.model.message.toLowerCase().replace(/\s+/g, '');
|
|
||||||
|
|
||||||
const url = new BadgeUrlBuilder()
|
|
||||||
.setParameters({
|
|
||||||
...this.model,
|
|
||||||
color: this.model.color ?? '000',
|
|
||||||
logo: this.model.logo ?? defaultLogo
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
;
|
|
||||||
|
|
||||||
return ImageComponent(this.model.message, url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import yaml
|
||||||
|
from src.model.skill_list import SkillList
|
||||||
|
import requests
|
||||||
|
from src.model.social_list import SocialList
|
||||||
|
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
config_file_path: str
|
||||||
|
config_data: dict[str, any] = None
|
||||||
|
|
||||||
|
def __init__(self, config_file_path: str):
|
||||||
|
self.config_file_path = config_file_path
|
||||||
|
|
||||||
|
def load_config_file(self):
|
||||||
|
with open(self.config_file_path, 'r') as config_file:
|
||||||
|
self.config_data = yaml.safe_load(config_file)
|
||||||
|
|
||||||
|
def handle_user_info(self):
|
||||||
|
user = self.config_data["user"]
|
||||||
|
response = requests.get(f"https://api.github.com/users/{user}")
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise Exception("User not found")
|
||||||
|
self.config_data["user"] = response.json()
|
||||||
|
|
||||||
|
def handle_skill_section(self):
|
||||||
|
self.config_data["skills"] = str(SkillList(self.config_data["skills"]))
|
||||||
|
|
||||||
|
def handle_social_section(self):
|
||||||
|
self.config_data["socials"] = str(SocialList(self.config_data["socials"]))
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
self.load_config_file()
|
||||||
|
self.handle_user_info()
|
||||||
|
self.handle_skill_section()
|
||||||
|
self.handle_social_section()
|
||||||
|
|
||||||
|
return self.config_data
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from src.shield.skill_shield import SkillShield
|
||||||
|
|
||||||
|
|
||||||
|
class Skill:
|
||||||
|
alt: str
|
||||||
|
src: str
|
||||||
|
|
||||||
|
def __init__(self, name: str):
|
||||||
|
self.alt = name
|
||||||
|
self.src = SkillShield(name).__str__()
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f""
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
from src.model.skill import Skill
|
||||||
|
|
||||||
|
|
||||||
|
class SkillList:
|
||||||
|
skills: list[Skill]
|
||||||
|
|
||||||
|
def __init__(self, skills: list[str]):
|
||||||
|
# Sort and remove duplicates
|
||||||
|
skills = list(set(skills))
|
||||||
|
skills.sort()
|
||||||
|
|
||||||
|
self.skills = [Skill(skill) for skill in skills]
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return "\n".join([str(skill) for skill in self.skills])
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from src.shield.social_shield import SocialShield
|
||||||
|
|
||||||
|
|
||||||
|
class Social:
|
||||||
|
name: str
|
||||||
|
img: str
|
||||||
|
|
||||||
|
def __init__(self, name: str, url: str):
|
||||||
|
self.name = name
|
||||||
|
self.img = SocialShield(name, url).__str__()
|
||||||
|
self.url = url
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"[]({self.url})"
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
from src.model.social import Social
|
||||||
|
|
||||||
|
|
||||||
|
class SocialList:
|
||||||
|
socials: list[Social]
|
||||||
|
|
||||||
|
def __init__(self, socials: list):
|
||||||
|
self.socials = [Social(social.get("name"), social.get("url")) for social in socials]
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return "\n".join([str(social) for social in self.socials])
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
from urllib.parse import urlunsplit, urlencode
|
||||||
|
from simpleicons.all import icons
|
||||||
|
|
||||||
|
class ShieldBuilder:
|
||||||
|
BASE_URL = "https://img.shields.io/static/v1"
|
||||||
|
|
||||||
|
message: str = None
|
||||||
|
style: str = None
|
||||||
|
logo: str = None
|
||||||
|
logo_color: str = None
|
||||||
|
label: str = None
|
||||||
|
label_color: str = None
|
||||||
|
color: str = None
|
||||||
|
cache_seconds: int = None
|
||||||
|
link: str = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.logo_color = "white"
|
||||||
|
self.label = " "
|
||||||
|
self.color = "black"
|
||||||
|
|
||||||
|
def set_message(self, message: str):
|
||||||
|
self.message = (
|
||||||
|
message
|
||||||
|
.replace("_", "__")
|
||||||
|
.replace("-", "--")
|
||||||
|
.replace(" ", "_")
|
||||||
|
)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_style(self, style: str):
|
||||||
|
if not style in ["flat", "flat-square", "plastic", "for-the-badge", "social"]:
|
||||||
|
raise Exception("Invalid style")
|
||||||
|
|
||||||
|
self.style = style
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_logo(self, logo: str):
|
||||||
|
self.logo = icons.get(logo).slug if logo in icons else None
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_logo_color(self, logo_color: str):
|
||||||
|
self.logo_color = logo_color
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_label(self, label: str):
|
||||||
|
self.label = label
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_label_color(self, label_color: str):
|
||||||
|
self.label_color = label_color
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_color(self, color: str):
|
||||||
|
self.color = color
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_cache_seconds(self, cache_seconds: int):
|
||||||
|
self.cache_seconds = cache_seconds
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_link(self, link: str):
|
||||||
|
self.link = link
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get_query(self):
|
||||||
|
query = {
|
||||||
|
"message": self.message,
|
||||||
|
"style": self.style,
|
||||||
|
"logo": self.logo,
|
||||||
|
"logoColor": self.logo_color,
|
||||||
|
"label": self.label,
|
||||||
|
"labelColor": self.label_color,
|
||||||
|
"color": self.color,
|
||||||
|
"cacheSeconds": self.cache_seconds,
|
||||||
|
"link": self.link
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove None values
|
||||||
|
return {k: v for k, v in query.items() if v is not None}
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
query = urlencode(self.get_query())
|
||||||
|
return urlunsplit(("", "", self.BASE_URL, query, ""))
|
||||||
|
|
||||||
|
|
||||||
|
if "__main__" == __name__:
|
||||||
|
shield_builder = (
|
||||||
|
ShieldBuilder()
|
||||||
|
.set_logo("HTML5")
|
||||||
|
.set_message("HTML5")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
print(shield_builder)
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
from simpleicons.all import icons
|
||||||
|
|
||||||
|
from src.shield.shield_builder import ShieldBuilder
|
||||||
|
|
||||||
|
|
||||||
|
class SkillShield:
|
||||||
|
|
||||||
|
def __init__(self, name: str = None):
|
||||||
|
self.builder = ShieldBuilder()
|
||||||
|
self.skill = self.set_skill(name) if name is not None else None
|
||||||
|
|
||||||
|
def get_skill(self):
|
||||||
|
return self.skill
|
||||||
|
|
||||||
|
def set_skill(self, name: str):
|
||||||
|
self.skill = icons.get(name)
|
||||||
|
|
||||||
|
if self.skill is not None:
|
||||||
|
(self.builder.set_message(self.skill.title)
|
||||||
|
.set_logo(self.skill.slug)
|
||||||
|
.set_color(self.skill.hex)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.builder.set_message(name)
|
||||||
|
|
||||||
|
def get_builder(self):
|
||||||
|
return self.builder
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
self.__str__()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.builder.build()
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
from simpleicons.all import icons
|
||||||
|
|
||||||
|
from src.shield.shield_builder import ShieldBuilder
|
||||||
|
|
||||||
|
|
||||||
|
class SocialShield:
|
||||||
|
|
||||||
|
def __init__(self, name: str = None, url: str = None):
|
||||||
|
self.builder = ShieldBuilder().set_style("for-the-badge")
|
||||||
|
self.name = name
|
||||||
|
self.url = url
|
||||||
|
self.social = self.set_social(name, url) if name is not None and url is not None else None
|
||||||
|
|
||||||
|
def get_social(self):
|
||||||
|
return self.social
|
||||||
|
|
||||||
|
def set_social(self, name: str, url: str):
|
||||||
|
self.social = icons.get(name)
|
||||||
|
|
||||||
|
if self.social is not None:
|
||||||
|
(self.builder.set_message(self.social.title)
|
||||||
|
.set_logo(self.social.slug)
|
||||||
|
.set_color(self.social.hex)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.builder.set_message(name)
|
||||||
|
|
||||||
|
self.builder.set_link(url)
|
||||||
|
|
||||||
|
def get_builder(self):
|
||||||
|
return self.builder
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
self.__str__()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.builder.build()
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
class Template:
|
||||||
|
|
||||||
|
def __init__(self, template_path: str):
|
||||||
|
self.template_path = template_path
|
||||||
|
|
||||||
|
def render(self, **kwargs) -> str:
|
||||||
|
with open(self.template_path, 'r') as f:
|
||||||
|
template = f.read()
|
||||||
|
|
||||||
|
return template.format(**kwargs)
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import type BadgeModel from '../../models/BadgeModel.ts';
|
|
||||||
import UrlBuilder from './UrlBuilder.ts';
|
|
||||||
|
|
||||||
export default class BadgeUrlBuilder extends UrlBuilder {
|
|
||||||
static BASE_URL = 'https://img.shields.io/static/v1';
|
|
||||||
static DEFAULT_PARAMETERS: BadgeModel = {style: 'flat', label: " ", logoColor: "white"};
|
|
||||||
|
|
||||||
constructor(parameters?: BadgeModel) {
|
|
||||||
super(BadgeUrlBuilder.BASE_URL,
|
|
||||||
parameters,
|
|
||||||
BadgeUrlBuilder.DEFAULT_PARAMETERS,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
public setParameters(parameters: BadgeModel): this {
|
|
||||||
return super.setParameters(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import UrlBuilder from './UrlBuilder.ts';
|
|
||||||
import type BannerModel from '../../models/BannerModel.ts';
|
|
||||||
|
|
||||||
export default class BannerUrlBuilder extends UrlBuilder {
|
|
||||||
static BASE_URL = 'https://svg-banners.vercel.app/api';
|
|
||||||
|
|
||||||
constructor(parameters: BannerModel) {
|
|
||||||
super(BannerUrlBuilder.BASE_URL, parameters, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
public setParameters(parameters: BannerModel): this {
|
|
||||||
return super.setParameters(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
export default abstract class UrlBuilder {
|
|
||||||
|
|
||||||
private parameters;
|
|
||||||
|
|
||||||
protected constructor(
|
|
||||||
private baseUrl: string,
|
|
||||||
parameters,
|
|
||||||
private defaultParameters,
|
|
||||||
) {
|
|
||||||
this.parameters = {...defaultParameters, ...parameters};
|
|
||||||
};
|
|
||||||
|
|
||||||
setParameters(parameters) {
|
|
||||||
this.parameters = {...this.parameters, ...parameters};
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
build(): URL {
|
|
||||||
const url = new URL(this.baseUrl);
|
|
||||||
for (const [key, value] of Object.entries(this.parameters)) {
|
|
||||||
if (value) url.searchParams.set(key, value);
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
<header>
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
## 🚀 About Me
|
||||||
|
|
||||||
|
I'm a passionate developer from 🇫🇷 **{user[location]}**.
|
||||||
|
|
||||||
|
## 🔗 Social Links
|
||||||
|
|
||||||
|
{socials}
|
||||||
|
|
||||||
|
## 🛠 Skills
|
||||||
|
|
||||||
|
{skills}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
{{banner}}
|
|
||||||
|
|
||||||
## 🚀 About Me
|
|
||||||
{{about}}
|
|
||||||
|
|
||||||
## 🔗 Links
|
|
||||||
{% for link in links -%}
|
|
||||||
{{link}}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
## 🛠 Skills
|
|
||||||
{% for skill in skills -%}
|
|
||||||
{{skill}}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
{% for badge in footer -%}
|
|
||||||
{{badge}}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
Reference in New Issue
Block a user