Compare commits

...

106 Commits

Author SHA1 Message Date
xdrm 71ff315897
feat: SEO, accessibility 2024-09-28 10:27:55 +02:00
xdrm e1aa5b948a
feat: preload google fonts 2024-09-24 22:58:48 +02:00
xdrm e5856c39ec
feat: update google fonts 2024-09-24 22:50:26 +02:00
xdrm fa71f5c909
feat: set initial scale to 1
- otherwise 300ms delay for being zoomable on smartphones
2024-09-24 22:42:49 +02:00
xdrm ccf518f30e
chore: yarn upgrade 2024-09-23 19:02:55 +02:00
xdrm 784f8ec021
fix: remove global.scss 2024-03-22 14:05:48 +01:00
xdrm fc83a0049e
feat: add r-smart project and its skills 2024-03-21 18:22:04 +01:00
Adrien Marquès 72c30e9a40
feat: file missing skill descriptions 2022-11-22 14:05:08 +01:00
Adrien Marquès e52d2770f1
feat: tweak links style 2022-11-22 13:32:39 +01:00
Adrien Marquès 81a1b9a823
feat: translate interest and mastery levels 2022-11-22 13:26:23 +01:00
Adrien Marquès d493cf0f78
feat: skills are ordered by interest level, then by mastery level 2022-11-22 13:21:22 +01:00
Adrien Marquès d4e9d1449a
feat: add interest and mastery levels to skills 2022-11-22 13:16:31 +01:00
Adrien Marquès 318ce57b39
feat: create the level 1-5 representation component 2022-11-22 12:06:22 +01:00
Adrien Marquès ecadd07e61
feat: add the api-mixer project 2022-10-26 10:55:02 +02:00
Adrien Marquès cbb021696e
feat: add lebonprix.apk project 2022-10-26 10:50:00 +02:00
Adrien Marquès 127f7788a5
feat: remove duplicate project's skills and reword the neural net (php) with a more optimistic tone 2022-10-26 10:26:29 +02:00
Adrien Marquès 9db21a214c
fix: invalid characters in links 2022-10-26 10:20:27 +02:00
Adrien Marquès 3016851831
fix: projects wording 2022-10-26 09:57:31 +02:00
Adrien Marquès 0c8428f11d
feat: polish timeline colors 2022-10-25 16:31:27 +02:00
Adrien Marquès b42ae74f3d
fix: project dates 2022-10-25 16:20:36 +02:00
Adrien Marquès 67a7dc0576
feat: add nuit de l'info and xbee projects 2022-10-25 16:10:37 +02:00
Adrien Marquès 0e44110c6a
feat: remove timeline user icon 2022-10-25 15:28:28 +02:00
Adrien Marquès 3a80ab0a60
chore: lint 2022-10-25 15:08:53 +02:00
Adrien Marquès 5021b1ea3b
feat: default to languages tag in the skill picker 2022-10-25 12:21:41 +02:00
Adrien Marquès d7dcff6523
feat: add needs analysis skill 2022-10-25 12:21:31 +02:00
Adrien Marquès 6490946300
feat: add small projects 2022-10-25 11:16:17 +02:00
Adrien Marquès 4ea8b60cc2
feat: add some IoT and personal projects 2022-10-24 16:14:36 +02:00
Adrien Marquès 055cc6d28f
feat: add marlink and nxtic projects 2022-10-24 15:06:54 +02:00
Adrien Marquès 74524cb2ea
fix: add denoising and douscord projects 2022-10-22 17:24:26 +02:00
Adrien Marquès d7f0ef05ee
feat: make project descriptions more readable 2022-10-22 16:19:09 +02:00
Adrien Marquès 4ce8ad30b6
feat: update favicon 2022-10-19 18:36:00 +02:00
Adrien Marquès e5fc1dd0e1
feat: create the skill picker guide 2022-10-19 18:00:22 +02:00
Adrien Marquès d65c8df1f0
fix: do not select by default 2022-10-19 18:00:13 +02:00
Adrien Marquès 534780f59c
feat: add projects 2022-10-19 17:42:50 +02:00
Adrien Marquès a30645d23b
feat: make the timeline header dark 2022-10-19 17:42:44 +02:00
Adrien Marquès 01fdd1abd0
feat: timeline add project count 2022-10-19 17:29:15 +02:00
Adrien Marquès c6217babc4
feat: display all projects when no skill is selected 2022-10-19 17:24:58 +02:00
Adrien Marquès d2f9e5632f
feat: timeline minor tweaks 2022-10-19 17:15:51 +02:00
Adrien Marquès 6b060f38f5
feat: homepage terminal has now 5 lines 2022-10-19 11:43:10 +02:00
Adrien Marquès 89de987569
feat: timeline, add username 2022-10-19 11:42:58 +02:00
Adrien Marquès 8c83e0b84c
fix: multiple project sources key conflicts 2022-10-19 11:42:44 +02:00
Adrien Marquès 54c70ce873
chore: lint 2022-10-18 18:25:03 +02:00
Adrien Marquès b97ccb53f9
feat: add projects and skills 2022-10-18 18:24:11 +02:00
Adrien Marquès d904de66a8
feat: localize short date format, project can have multiple sources with independant commit counts 2022-10-18 18:03:27 +02:00
Adrien Marquès 46ac058209
fix: make french translations exhaustive 2022-10-18 17:50:24 +02:00
Adrien Marquès a9022a02dd
feat: sort projects by end date
priority (first to last)
 - still active with last start date
 - still active
 - ended last
2022-10-18 17:01:05 +02:00
Adrien Marquès fe4af9ea62
fix: remove contact from the home page 2022-10-18 16:58:24 +02:00
Adrien Marquès 53d16159b9
feat: dynamic language switch for the skill picker 2022-10-18 15:31:55 +02:00
Adrien Marquès 90d13d31f5
feat: create language picker 2022-10-18 12:35:50 +02:00
Adrien Marquès ae0a9622c1
feat: create a basic page footer 2022-10-18 11:52:14 +02:00
Adrien Marquès 5ec2a8fb09
feat: write skills 2022-10-17 11:45:14 +02:00
Adrien Marquès a8d98999e6
feat: add some skills and projects 2022-10-12 19:00:42 +02:00
Adrien Marquès 043a36d15f
feat: add duration in the timeline 2022-10-12 19:00:34 +02:00
Adrien Marquès 0098c0708f
fix: translate skill picker details 2022-10-12 14:52:01 +02:00
Adrien Marquès 879bcab044
fix: translate skill picker description 2022-10-12 11:47:46 +02:00
Adrien Marquès c7e7bc2ce4
feat: reduce timeline skill size 2022-10-12 11:41:54 +02:00
Adrien Marquès c6c5339a89
feat: translate project descriptions 2022-10-12 11:41:46 +02:00
Adrien Marquès 2fccc09b3f
feat: translate the timeline fixed elements 2022-10-12 11:18:19 +02:00
Adrien Marquès 8257b30109
feat: translate dates and elapsed time 2022-10-12 11:12:29 +02:00
Adrien Marquès 965574d112
feat: translate skills 2022-10-12 10:47:42 +02:00
Adrien Marquès 6f292a02e9
feat: translate skill tags 2022-10-12 10:33:17 +02:00
Adrien Marquès ef4bd2fe46
chore: fix typescript upgrade 2022-10-11 16:13:35 +02:00
Adrien Marquès d0648bea84
feat: create timelint header 2022-10-11 16:09:24 +02:00
Adrien Marquès fc02045783
fix: remove timeline layout debug 2022-10-11 15:29:49 +02:00
Adrien Marquès 674a23215b
feat: brighten timeline lines and joints 2022-10-11 13:10:59 +02:00
Adrien Marquès 0cebaf2e62
feat: i18n for the home page 2022-10-11 13:08:52 +02:00
Adrien Marquès f074720d0f
feat: add i18n capabilities 2022-10-11 12:39:40 +02:00
Adrien Marquès 48da61765a
feat: add a final spacer to the timeline 2022-10-11 12:01:04 +02:00
Adrien Marquès 56eef15dc2
chore: lint 2022-10-11 11:33:12 +02:00
Adrien Marquès 97c2c312d0
feat: create timeline continuity line and re-layout with grid 2022-10-11 11:26:20 +02:00
Adrien Marquès 525b451b6a
fix: line svg uses rem for scaling 2022-10-11 10:28:07 +02:00
Adrien Marquès 8801b8c020
feat: skip type writing animation before 10min has passed 2022-10-10 16:29:42 +02:00
Adrien Marquès 74a0771f01
feat: responsive homepage width 2022-10-10 16:16:33 +02:00
Adrien Marquès de0034ed5b
feat: typewriter animation for the homepage readme 2022-10-10 16:12:19 +02:00
Adrien Marquès 67e136b500
feat: update google fonts 2022-10-10 15:55:04 +02:00
Adrien Marquès fd78df33e4
feat: new home page 2022-10-10 15:53:52 +02:00
Adrien Marquès 8f156dd4d1
feat: set the mobile ratio 2022-10-10 14:20:54 +02:00
Adrien Marquès 408f6ebe87
feat: use a white header for the timeline 2022-10-10 14:20:46 +02:00
Adrien Marquès f2eccc9f55
feat: add diagonal transition after the skill picker 2022-10-06 17:19:00 +02:00
Adrien Marquès adf87b62ed
feat: create scroll button on the home page 2022-10-05 17:36:23 +02:00
Adrien Marquès b0bd060033
fix: timeline color coherence 2022-10-05 16:57:39 +02:00
Adrien Marquès cdb6708a57
feat: moving header smoother style 2022-10-05 16:51:10 +02:00
Adrien Marquès 013380545a
feat: improve link styles 2022-10-05 16:50:49 +02:00
Adrien Marquès 28aebf9abc
feat: webkit scrollbar style 2022-10-05 16:50:39 +02:00
Adrien Marquès eb01da57c3
fix: skill picker media queries centering 2022-10-05 16:40:22 +02:00
Adrien Marquès e289eec8dd
feat: browse button uses smooth scroll 2022-10-05 16:25:15 +02:00
Adrien Marquès e6179034e9
feat: add skill card title and skill picker media queries 2022-10-05 16:21:50 +02:00
Adrien Marquès f8f3b0e282
feat: add smooth scrolling capabilities 2022-10-05 16:07:36 +02:00
Adrien Marquès c4a0587c8c
fix: link underline size diff 2022-10-05 16:06:52 +02:00
Adrien Marquès a027a47544
feat: tag has a default and cannot be deselected, timeline allows picking a skill and vice-versa 2022-10-05 15:16:06 +02:00
Adrien Marquès 9b02041ca8
feat: adjust timeline 2022-10-05 14:36:24 +02:00
Adrien Marquès df8f61d209
fix: skill card folder icon 2022-10-05 14:31:48 +02:00
Adrien Marquès 3cdf4aa6aa
feat: filter the timeline according to the picker 2022-10-05 14:26:50 +02:00
Adrien Marquès 9179c7637d
feat: use skill cards in the timeline 2022-10-05 14:26:30 +02:00
Adrien Marquès 65b6c89357
feat: update timeline icon and colors 2022-10-05 14:25:46 +02:00
Adrien Marquès 60345a5f26
feat: create the skill details section 2022-10-05 11:39:39 +02:00
Adrien Marquès e9fdc3a3d4
feat: create services to use models 2022-10-05 11:39:27 +02:00
Adrien Marquès 6b9fb14293
refactor: global link style 2022-10-05 11:25:22 +02:00
Adrien Marquès d154d27c37
feat: create the barebone following header 2022-10-04 19:08:36 +02:00
Adrien Marquès 0b3c4654ba
feat: migrate to purple theme for waves 2022-10-04 18:34:57 +02:00
Adrien Marquès d136b9d1e2
feat: create the skill picker page 2022-10-04 18:29:27 +02:00
Adrien Marquès d6ab16026d
feat: fix the skill card and add all icons 2022-10-04 18:29:15 +02:00
Adrien Marquès 890271086a
feat: create the skill card component 2022-10-04 16:41:50 +02:00
Adrien Marquès 52b45583c6
feat: search engine timeline view 2022-10-04 13:06:09 +02:00
Adrien Marquès 4dddc7973e
feat: create the base blue banner 2022-08-31 16:46:18 +02:00
Adrien Marquès 24baa3c5c4
feat: create the multiple banner/theme homepage 2022-08-31 15:19:07 +02:00
94 changed files with 7939 additions and 2449 deletions

View File

@ -10,8 +10,9 @@
"dependencies": { "dependencies": {
"core-js": "^2.6.5", "core-js": "^2.6.5",
"vanilla-tilt": "^1.7.2", "vanilla-tilt": "^1.7.2",
"vue": "^2.6.10", "vue": "^2.6.14",
"vue-class-component": "^7.0.2", "vue-class-component": "^7.0.2",
"vue-i18n": "8.27.1",
"vue-property-decorator": "^8.1.0" "vue-property-decorator": "^8.1.0"
}, },
"devDependencies": { "devDependencies": {
@ -20,7 +21,7 @@
"@vue/cli-service": "^3.7.0", "@vue/cli-service": "^3.7.0",
"sass": "^1.18.0", "sass": "^1.18.0",
"sass-loader": "^7.1.0", "sass-loader": "^7.1.0",
"typescript": "^3.4.3", "typescript": "^4.8.4",
"vue-template-compiler": "^2.5.21" "vue-template-compiler": "^2.5.21"
}, },
"postcss": { "postcss": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -3,11 +3,18 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel='stylesheet' href='styles.css'> <link rel='stylesheet' href='styles.css'>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" href="https://fonts.googleapis.com/css?family=Source%20Sans%20Pro|Fira%20Code&display=swap" as="style">
<link rel="stylesheet"href="https://fonts.googleapis.com/css?family=Source%20Sans%20Pro|Fira%20Code&display=swap" media="all">
<title>xdrm()</title> <title>xdrm()</title>
<meta name="description" content="Portfolio d'Adrien Marquès, développeur freelance spécialisé en Go et IoT">
<meta name="keywords" content="développeur freelance, Go, IoT, web development">
</head> </head>
<body> <body>
<noscript> <noscript>

View File

@ -15,6 +15,10 @@ html, body{
overflow: hidden; overflow: hidden;
} }
button {
border: none;
}
body { body {
display: block; display: block;
position: absolute; position: absolute;
@ -32,3 +36,19 @@ body {
overflow-y: auto; overflow-y: auto;
} }
::-webkit-scrollbar {
display: block;
width: .3rem;
background-color: #202228;
overflow: hidden;
}
::-webkit-scrollbar-thumb {
border-radius: .3rem / .3rem;
background-color: #6d7076;
transition: background-color .1s ease-in-out;
}
::-webkit-scrollbar-thumb:hover {
background-color: #a9acb2;
}

View File

@ -1,25 +1,56 @@
<template> <template>
<div id="app"> <div id="app">
<Banner/> <Home/>
<Skills/> <SkillPicker ref='picker' @pick='onPick($event)'/>
<Experiences/> <Timeline ref='timeline' @pick='onPicked($event)'/>
<Footer/>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'; import { Component, Vue } from 'vue-property-decorator';
import Banner from './components/Banner.vue'; import { tID } from '@/model/skills';
import Skills from './components/Skills.vue'; import Home from './components/Home.vue';
import Experiences from './components/Experiences.vue'; import Timeline from './components/Timeline.vue';
import SkillPicker from './components/SkillPicker.vue';
import Footer from './components/Footer.vue';
@Component({ @Component({
components: { components: {
Banner, Home,
Skills, Timeline,
Experiences, SkillPicker,
Footer,
}, },
}) })
export default class App extends Vue { export default class App extends Vue {
// skill picker selection -> filters the timeline
protected onPick(id: tID|null) {
const timeline = this.$refs.timeline as Timeline;
if ( timeline == null ) {
return;
}
timeline.filter(id);
}
// skill picked from the timeline -> select on the skill picker
protected onPicked(id: tID) {
const picker = this.$refs.picker as SkillPicker;
if ( picker == null ) {
return;
}
picker.select(id, false);
}
private mounted() {
const picker = this.$refs.picker as SkillPicker;
if ( picker == null ) {
return;
}
picker.select(0, true);
}
} }
</script> </script>
@ -33,12 +64,47 @@ export default class App extends Vue {
min-height: 100%; min-height: 100%;
height: auto; height: auto;
padding-bottom: 4vw;
font-size: 1rem;
font-family: 'Source Sans Pro', sans-serif;
flex-flow: column nowrap; flex-flow: column nowrap;
overflow: hidden; overflow: hidden;
background: #1a1a1a; background: #fff;
}
a, a:visited {
display: inline-block;
position: relative;
color: #cbcbcb;
cursor: pointer;
transition: color .2s ease-in-out;
&::after {
content: '';
display: block;
position: absolute;
margin-left: 0%;
width: 100%;
height: .1em;
background: #5f50bf;
transition: width .2s ease-in-out, margin-left .2s ease-in-out;
transform-style: preserve-3d;
}
&:hover{
color: #fff;
&::after {
width: 104%;
}
}
} }
</style> </style>

6
src/assets/home/line.svg Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="41.72mm" height="1e3mm" version="1.1" viewBox="0 0 41.72 1e3" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-97.866 -2.5065)">
<path d="m118.71 2.5069 0.0164 20.649c0.0165 9.7177-20.331 9.1678-20.331 20.348v48.444c0 11.18 20.348 10.63 20.331 20.015l-0.0165 890.54" fill="none" stroke="#fff" style='stroke-width: .1rem'/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 402 B

17
src/assets/home/logo.svg Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="4.2333mm" height="4.2333mm" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a" x1="122.52" x2="130.49" y1="39.199" y2="31.302" gradientTransform="translate(.09658)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
<linearGradient id="b">
<stop stop-color="#745cfc" offset="0"/>
<stop stop-color="#564ba4" offset="1"/>
</linearGradient>
</defs>
<g transform="matrix(.10155 0 0 .10155 -90.232 -10.197)">
<path d="m930.25 121.26a20.844 20.844 0 0 1-20.844 20.844 20.844 20.844 0 0 1-20.844-20.844 20.844 20.844 0 0 1 20.844-20.844 20.844 20.844 0 0 1 20.844 20.844z" fill="#fff" style="paint-order:fill markers stroke"/>
<g transform="matrix(0 2.2967 -2.2967 0 990.37 -169.5)" fill="url(#a)">
<path d="m122.72 31.302 2.3682 3.7201a0.21308 0.21308 89.753 0 1 9.8e-4 0.2273l-2.4668 3.9498h1.5785a0.73492 0.73492 149.45 0 0 0.64338-0.37973l1.4732-2.6685a0.27716 0.27716 104.45 0 0 0.0345-0.13395v-1.7817a0.28946 0.28946 76.14 0 0-0.0332-0.13465l-1.2688-2.4147a0.71792 0.71792 31.14 0 0-0.63551-0.38397z"/>
<path d="m130.48 39.199-2.4404-3.8335 2.538-4.0637h-2.0122l-1.7173 3.1108v1.9246l1.5038 2.8618z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="4.2333mm" height="2.8687mm" version="1.1" viewBox="0 0 4.2333 2.8687" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-129.22 -199.04)"><g transform="matrix(.22857 0 0 .22857 99.684 154.49)" stroke-linejoin="round"><path d="m130.59 194.88c-0.75842 0-1.3689 0.61052-1.3689 1.3689v9.8128c0 0.75839 0.61048 1.3689 1.3689 1.3689h4.8044v-12.551z" fill="#4758a9" stroke-width=".8" style="paint-order:fill markers stroke"/><path d="m141.57 194.88v12.551h4.8044c0.75843 0 1.3694-0.61048 1.3694-1.3689v-9.8128c0-0.75843-0.611-1.3689-1.3694-1.3689z" fill="#ed5565" stroke-width=".8" style="paint-order:fill markers stroke"/><rect x="135.39" y="194.88" width="6.1737" height="12.551" fill="#fff" stroke-width=".81063" style="paint-order:fill markers stroke"/></g></g></svg>

After

Width:  |  Height:  |  Size: 846 B

706
src/assets/laptop.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="4.2333mm" height="4.2333mm" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-89.762 -60.858)">
<path d="m91.879 62.037a0.13229 0.13229 0 0 0-0.13281 0.13281v1.2891l-0.55078-0.55078a0.13229 0.13229 0 0 0-0.18555 0 0.13229 0.13229 0 0 0 0 0.1875l0.77539 0.77539a0.13229 0.13229 0 0 0 0.1875 0l0.77539-0.77539a0.13229 0.13229 0 0 0 0-0.1875 0.13229 0.13229 0 0 0-0.1875 0l-0.55078 0.55273v-1.291a0.13229 0.13229 0 0 0-0.13086-0.13281z" color="#000000" stroke-linecap="round" stroke-linejoin="bevel" style="-inkscape-stroke:none"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 649 B

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<title>Foundation/Logo/Qt-logo</title>
<title>wiki:Behnam N</title>
<g id="a" transform="matrix(.014701 0 0 .014701 3.0939 3.2778)">
<path d="m216.88 268.43h78.241l-39.12-94.054zm39.121-242.42-213.36 76.394 32.652 283.08 180.71 100.52 180.71-100.52 32.653-283.08zm133.69 350.96h-49.904l-26.9-67.153h-113.56l-26.903 67.153h-50.003l133.58-300.13z" fill="#dd0031"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 538 B

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<title>Foundation/Logo/Qt-logo</title>
<title>wiki:Behnam N</title>
<path d="m8.9519 5.0074c-0.3845 0.0027763-0.76852 0.10877-1.1037 0.33574-0.42666 0.27074-0.72833 0.63217-0.98178 1.0029-0.38887-0.55865-0.90509-1.088-1.6572-1.2589h-2.43e-5c-0.17889-0.040647-0.35714-0.055483-0.52906-0.055223l-0.030002 4.42e-5 -0.00286 5.128e-4c-1.3845-0.070163-2.4692 1.4787-1.9035 2.7541 0.55622 1.3685 2.5236 1.7213 3.5159 0.62746 0.23078-0.22812 0.41646-0.47911 0.58352-0.73238 0.37988 0.61244 0.94347 1.1493 1.708 1.3222 3.705e-4 8.5e-5 6.993e-4 2.143e-4 0.00104 2.993e-4 1.3616 0.39056 2.7751-0.89233 2.5473-2.2861-0.16819-1.0442-1.1592-1.7178-2.1478-1.7106zm-0.084756 0.71736c0.68248-0.026722 1.4361 0.43077 1.5444 1.1045l9.4e-5 4.659e-4 7.1e-5 5.128e-4c0.13909 0.8497-0.85595 1.7493-1.6821 1.5003l-0.011834-0.00356-0.012042-0.00269c-0.63121-0.14241-1.1311-0.69109-1.4379-1.3184 0.27965-0.44311 0.55994-0.82548 0.95667-1.0762l0.00521-0.0033 0.00512-0.00347c0.18513-0.12643 0.40471-0.18914 0.63221-0.19805zm-4.3017 0.00191c0.02595-1.206e-4 0.052013 8.39e-4 0.078128 0.00286l0.044118 0.00356 0.017057-0.00295c0.12204 0.00113 0.24068 0.011947 0.35 0.036778 0.58924 0.13392 1.0335 0.64324 1.4009 1.2237-0.21908 0.35205-0.43246 0.67976-0.69341 0.93556l-0.00781 0.00763-0.00737 0.00816c-0.61475 0.68346-2.0154 0.43292-2.3594-0.42163l-0.00252-0.00625-0.00278-0.00616c-0.33147-0.73944 0.37847-1.7776 1.183-1.7813zm4.1818 0.65972v0.40169h-0.38988v0.37806h0.38988v0.40169h0.37806v-0.40169h0.41351v-0.37806h-0.41351v-0.40169zm-4.7376 0.44304v0.29536h1.4177v-0.29536z" color="#000000" fill="#00979c" stroke-width=".011814" style="-inkscape-stroke:none"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="a" x1="28.54" x2="11.41" y1="69.49" y2="99.17" gradientTransform="matrix(.14152 0 0 .14152 118.11 154.62)" gradientUnits="userSpaceOnUse">
<stop stop-color="#0052cc" offset="0"/>
<stop stop-color="#2684ff" offset=".92"/>
</linearGradient>
</defs>
<g transform="matrix(.81724 0 0 .81724 -93.495 -127.08)" stroke-width=".14152">
<path class="cls-1" d="m120.89 163.75a0.26606 0.26606 0 0 0-0.45287 0.0495l-2.2969 4.5938a0.27455 0.27455 0 0 0 0.24483 0.39767h3.1984a0.26465 0.26465 0 0 0 0.24625-0.15567c0.68921-1.4152 0.27172-3.5904-0.93971-4.8853z" fill="url(#a)"/>
<path class="cls-2" d="m122.58 159.57a6.0586 6.0586 0 0 0-0.35381 5.9821l1.5497 3.0753a0.27455 0.27455 0 0 0 0.24483 0.15285h3.1984a0.28304 0.28304 0 0 0 0.23634-0.39485l-4.4084-8.8154a0.25899 0.25899 0 0 0-0.46702 0z" fill="#2684ff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1014 B

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<g transform="matrix(.11199 0 0 .11199 88.969 73.863)">
<g transform="matrix(.99033 0 0 .99033 53.488 2.2884)">
<path d="m2.889 195.94c0.11963-2.1532 1.1757-4.0526 2.7663-5.3989 1.5629-1.3227 3.6635-2.1298 5.9574-2.1299 2.2928 1.5e-4 4.3934 0.80727 5.9564 2.1299 1.5911 1.3463 2.6471 3.2454 2.768 5.3982 0.1217 2.2151-0.76742 4.2733-2.3293 5.7971-1.5915 1.5522-3.8586 2.5268-6.3947 2.5268-2.5368 0-4.8054-0.97453-6.3969-2.5268-1.5618-1.5233-2.4501-3.5817-2.3271-5.7964" enable-background="new" fill="#0d528b"/>
<path d="m-11.007 200.3c0.01433 0.8411 0.2858 2.4784 0.68802 3.7619 0.84825 2.7071 2.2862 5.2147 4.2897 7.4249 2.0546 2.2675 4.5809 4.0924 7.5026 5.3862 3.0729 1.36 6.4006 2.0556 9.853 2.0492 3.4532-6e-3 6.7804-0.71297 9.8523-2.0831 2.9192-1.3062 5.4457-3.1354 7.4989-5.4098 1.9994-2.2182 3.4353-4.7299 4.2845-7.4392 0.42882-1.3699 0.69619-2.7574 0.80614-4.1523 0.10709-1.3723 0.06133-2.7495-0.13328-4.1229-0.37988-2.6788-1.3076-5.1919-2.7333-7.4844-1.308-2.1047-2.9897-3.9477-4.9892-5.4998l0.0028-3e-3 -20.186-15.502c-0.01751-0.0139-0.03391-0.0281-0.05155-0.0416-1.3277-1.0154-3.5546-1.0122-5.01 7e-3 -1.4747 1.0304-1.6401 2.7329-0.33129 3.8104l-0.0041 3e-3 8.4197 6.848-25.666 0.0274c-0.0124 0-0.02329-3.4e-4 -0.03501-3.4e-4 -2.1207 1e-3 -4.1585 1.3957-4.5642 3.153-0.41136 1.7925 1.0296 3.2785 3.2333 3.2869l-0.0012 7e-3 13.007-0.0249-23.213 17.817c-0.02936 0.0224-0.05995 0.0435-0.08917 0.0663-2.1867 1.6765-2.8959 4.4658-1.5181 6.2308 1.4013 1.7952 4.3762 1.7963 6.5902 8e-3l12.668-10.366s-0.18361 1.4001-0.16977 2.2414zm32.553 4.6839c-2.6105 2.6626-6.2621 4.1696-10.219 4.176-3.9587 8e-3 -7.6122-1.4874-10.223-4.1428-1.2761-1.2951-2.2135-2.781-2.7919-4.369-0.56616-1.5578-0.78616-3.2095-0.64162-4.878 0.14126-1.6331 0.62325-3.1871 1.4007-4.596 0.76204-1.3837 1.8109-2.6337 3.1057-3.6922 2.5373-2.0691 5.7656-3.1871 9.1469-3.1915 3.3813-5e-3 6.6098 1.1045 9.1469 3.1655 1.2938 1.0527 2.3417 2.2986 3.1044 3.6799 0.77691 1.4066 1.2594 2.9585 1.4015 4.592 0.14428 1.667-0.07567 3.3199-0.64176 4.879-0.57785 1.5889-1.5144 3.0774-2.7888 4.377" enable-background="new" fill="#f5792a"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

11
src/assets/skills/c.svg Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<title>Foundation/Logo/Qt-logo</title>
<g transform="matrix(.026144 0 0 .026144 3.5164 3.2794)">
<path d="m255.57 84.72c-2e-3 -4.83-1.035-9.098-3.124-12.761-2.052-3.602-5.125-6.621-9.247-9.008-34.025-19.619-68.083-39.178-102.1-58.817-9.17-5.294-18.061-5.101-27.163 0.269-13.543 7.987-81.348 46.834-101.55 58.537-8.321 4.817-12.37 12.189-12.372 21.771-0.013 39.455 0 78.909-0.013 118.36 2e-3 4.724 0.991 8.909 2.988 12.517 2.053 3.711 5.169 6.813 9.386 9.254 20.206 11.703 88.02 50.547 101.56 58.536 9.106 5.373 17.997 5.565 27.17 0.269 34.015-19.64 68.075-39.198 102.1-58.817 4.217-2.44 7.333-5.544 9.386-9.252 1.994-3.608 2.985-7.793 2.987-12.518 0 0 0-78.889-0.013-118.34" fill="#5c8dbc"/>
<path d="m128.18 143.51-125.19 72.084c2.053 3.711 5.169 6.813 9.386 9.254 20.206 11.703 88.02 50.547 101.56 58.536 9.106 5.373 17.997 5.565 27.17 0.269 34.015-19.64 68.075-39.198 102.1-58.817 4.217-2.44 7.333-5.544 9.386-9.252l-124.41-72.074" fill="#1a4674"/>
<path d="m91.101 164.86c7.285 12.718 20.98 21.296 36.69 21.296 15.807 0 29.58-8.687 36.828-21.541l-36.437-21.107-37.081 21.352" fill="#1a4674"/>
<path d="m255.57 84.72c-2e-3 -4.83-1.035-9.098-3.124-12.761l-124.26 71.55 124.41 72.074c1.994-3.608 2.985-7.793 2.987-12.518 0 0 0-78.889-0.013-118.34" fill="#1b598e"/>
<path d="m164.62 164.62c-7.248 12.854-21.021 21.541-36.828 21.541-15.71 0-29.405-8.578-36.69-21.296-3.539-6.18-5.574-13.335-5.574-20.968 0-23.341 18.923-42.263 42.264-42.263 15.609 0 29.232 8.471 36.553 21.059l36.941-21.272c-14.683-25.346-42.096-42.398-73.494-42.398-46.876 0-84.875 38-84.875 84.874 0 15.378 4.091 29.799 11.241 42.238 14.646 25.48 42.137 42.637 73.634 42.637 31.555 0 59.089-17.226 73.714-42.781l-36.886-21.371" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(.85198 0 0 -.85198 -40.527 45.883)">
<path d="m56.199 42.616c-0.10023-0.10023-0.26329-0.10023-0.36349 0-0.10016 0.1002-0.10016 0.26317 0 0.3634 0.1002 0.1002 0.26326 0.1002 0.36349 0 0.10017-0.10023 0.10017-0.2632 0-0.3634zm0.01612 2.8978c0.10016 0.1002 0.26322 0.1002 0.36342 0 0.10023-0.10023 0.10023-0.26326 0-0.3634-0.1002-0.10022-0.26326-0.10022-0.36342 0-0.1002 0.10014-0.1002 0.26317 0 0.3634zm2.7998 0.76425c-0.08527 0.85172-0.77907 1.5593-1.6433 1.6696-0.41541 0.64577-1.1332 1.0414-1.9064 1.0414-0.60523 0-1.1742-0.23568-1.6022-0.66366-0.42794-0.42797-0.66359-0.9969-0.66359-1.6021 0-0.02605-0.0017-0.06874-0.0035-0.11723-2.19e-4 -0.0053-3.99e-4 -0.01064-6.41e-4 -0.01593-0.55502 0.01684-1.0977-0.19444-1.4965-0.5933-0.791-0.79094-0.791-2.078 0-2.869 0.79097-0.791 2.078-0.791 2.869 0 0.01266 0.01269 0.02373 0.02629 0.0333 0.04066 0.01437 0.0096 0.02801 0.02064 0.04069 0.03333l1.5279 1.5279c0.22947-0.08613 0.49842-0.0376 0.68276 0.1467 0.25122 0.25125 0.25128 0.66005 0 0.91126-0.25124 0.25128-0.66008 0.25128-0.91129 0-0.1843-0.18431-0.23287-0.45323-0.14673-0.68279l-1.5279-1.5279c-0.01266-0.0126-0.02376-0.0263-0.0333-0.04067-0.01437-0.0095-0.02801-0.02055-0.04066-0.03324-0.58413-0.58409-1.5345-0.58409-2.1186-9.4e-5 -0.5841 0.58416-0.5841 1.5346 2.3e-5 2.1186 0.34976 0.34976 0.84928 0.50417 1.3363 0.41308 0.08335-0.01559 0.16924 0.0094 0.23094 0.06766 0.06167 0.05825 0.09183 0.14227 0.08106 0.22645-0.0043 0.03883 0.0012 0.18648 0.0038 0.25742 0.0021 0.05568 0.0039 0.10378 0.0039 0.13701 0 0.46347 0.18046 0.89918 0.50821 1.2269 0.32768 0.32771 0.76345 0.50824 1.2269 0.50824 0.62883 0 1.2099-0.3419 1.5166-0.89218 0.04458-0.08014 0.12734-0.13145 0.2189-0.13588 0.70056-0.03391 1.2924-0.64246 1.2925-1.3291-3.2e-5 -0.11298 0.07142-0.21229 0.17569-0.24956 0.56287-0.22384 0.92607-0.7589 0.92615-1.3654 0-0.38771-0.15154-0.74722-0.42675-1.0125-0.2749-0.26494-0.64497-0.41085-1.0421-0.41085h-1.5226c-0.03106 0.06894-0.0742 0.13377-0.13077 0.19033-0.25122 0.25125-0.66001 0.25125-0.91126 0-0.25124-0.25127-0.25124-0.66008 0-0.91132 0.25125-0.25118 0.66004-0.25118 0.91126 0 0.05657 0.05657 0.09971 0.12139 0.13077 0.19034h1.5226c0.53511 0 1.036 0.19865 1.4103 0.55945 0.37987 0.36615 0.5891 0.86133 0.5891 1.3945 0 0.77027-0.43059 1.4561-1.1107 1.7918m-1.5462 2.2197c0.54013-0.04146 1.0492-0.27595 1.4335-0.66026 0.38428-0.38424 0.61879-0.89337 0.66028-1.4336 0.0051-0.06571 0.03366-0.12399 0.07687-0.16719 0.05284-0.05287 0.12759-0.08317 0.20798-0.07702 0.14614 0.01122 0.25549 0.13875 0.24422 0.28487-0.05111 0.66641-0.34034 1.2944-0.81414 1.7682-0.47377 0.47377-1.1017 0.76294-1.7681 0.81412-0.14612 0.01125-0.27366-0.09812-0.28487-0.24422-0.01119-0.14606 0.09812-0.27362 0.24424-0.2849m0.21614-2.8699c0.14652 0 0.26531 0.11879 0.26531 0.26531 0 0.26503-0.1032 0.51423-0.29063 0.70166-0.18744 0.18737-0.4366 0.29063-0.70163 0.29063-0.14655 0-0.26534-0.11879-0.26534-0.26534 2.4e-5 -0.14652 0.11878-0.2653 0.26537-0.26534 0.12325 0 0.23917-0.04801 0.32637-0.13521 0.08721-0.0872 0.13521-0.20308 0.13521-0.32634 0-0.07332 0.02969-0.13967 0.07769-0.18768 0.04801-0.04801 0.11439-0.0777 0.18765-0.0777" fill="#f9f9f9"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<title>Foundation/Logo/Qt-logo</title>
<title>wiki:Behnam N</title>
<g transform="matrix(.029587 0 0 .029587 3.7138 4.0383)" fill="#5e6b8c">
<path class="st0" d="m193.2 203h-34.4l2.4-29h-16.9l-2.4 29h-71.3l-2.4-29h-16.9l2.4 29h-34.4l-19.3-125.7 48.3-77.3h115.9l48.3 77.3zm-38.7-164.3h-31l2.1 14.5h-38.7l2.1-14.5h-31l-19.4 38.6 9.7 77.3h115.9l9.7-77.3zm-16.9 90.3c-2.7 0-4.8-8-4.8-18s2.2-18 4.8-18c2.7 0 4.8 8 4.8 18s-2.1 18-4.8 18zm-60.9 1.5c-2.7 0-4.8-8-4.8-18s2.2-18 4.8-18c2.7 0 4.8 8 4.8 18s-2.2 18-4.8 18z" fill="#5e6b8c"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 704 B

15
src/assets/skills/cpp.svg Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<title>Foundation/Logo/Qt-logo</title>
<g transform="matrix(.026144 0 0 .026144 3.5164 3.2794)">
<path d="m255.57 84.72c-2e-3 -4.83-1.035-9.098-3.124-12.761-2.052-3.602-5.125-6.621-9.247-9.008-34.025-19.619-68.083-39.178-102.1-58.817-9.17-5.294-18.061-5.101-27.163 0.269-13.543 7.987-81.348 46.834-101.55 58.537-8.321 4.817-12.37 12.189-12.372 21.771-0.013 39.455 0 78.909-0.013 118.36 2e-3 4.724 0.991 8.909 2.988 12.517 2.053 3.711 5.169 6.813 9.386 9.254 20.206 11.703 88.02 50.547 101.56 58.536 9.106 5.373 17.997 5.565 27.17 0.269 34.015-19.64 68.075-39.198 102.1-58.817 4.217-2.44 7.333-5.544 9.386-9.252 1.994-3.608 2.985-7.793 2.987-12.518 0 0 0-78.889-0.013-118.34" fill="#5c8dbc"/>
<path d="m128.18 143.51-125.19 72.084c2.053 3.711 5.169 6.813 9.386 9.254 20.206 11.703 88.02 50.547 101.56 58.536 9.106 5.373 17.997 5.565 27.17 0.269 34.015-19.64 68.075-39.198 102.1-58.817 4.217-2.44 7.333-5.544 9.386-9.252l-124.41-72.074" fill="#1a4674"/>
<path d="m91.101 164.86c7.285 12.718 20.98 21.296 36.69 21.296 15.807 0 29.58-8.687 36.828-21.541l-36.437-21.107-37.081 21.352" fill="#1a4674"/>
<path d="m255.57 84.72c-2e-3 -4.83-1.035-9.098-3.124-12.761l-124.26 71.55 124.41 72.074c1.994-3.608 2.985-7.793 2.987-12.518 0 0 0-78.889-0.013-118.34" fill="#1b598e"/>
<g fill="#fff">
<path d="m248.73 148.66h-9.722v9.724h-9.724v-9.724h-9.721v-9.721h9.721v-9.722h9.724v9.722h9.722v9.721"/>
<path d="m213.25 148.66h-9.721v9.724h-9.722v-9.724h-9.722v-9.721h9.722v-9.722h9.722v9.722h9.721v9.721"/>
<path d="m164.62 164.62c-7.248 12.854-21.021 21.541-36.828 21.541-15.71 0-29.405-8.578-36.69-21.296-3.539-6.18-5.574-13.335-5.574-20.968 0-23.341 18.923-42.263 42.264-42.263 15.609 0 29.232 8.471 36.553 21.059l36.941-21.272c-14.683-25.346-42.096-42.398-73.494-42.398-46.876 0-84.875 38-84.875 84.874 0 15.378 4.091 29.799 11.241 42.238 14.646 25.48 42.137 42.637 73.634 42.637 31.555 0 59.089-17.226 73.714-42.781l-36.886-21.371"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<title>Foundation/Logo/Qt-logo</title>
<g transform="matrix(.20209 0 0 .20209 3.6237 3.8078)">
<polygon points="27.377 28.889 16.001 32 4.625 28.889 2 0 30.002 0" fill="#1f62ae"/>
<polygon points="16 29.75 25.232 27.008 27.688 2 16 2" fill="#347dc6"/>
<polygon points="8.619 17 19.502 17 19.158 21 16 21.99 12.861 20.984 12.533 19 8.803 19 9.262 23.987 16 25.99 22.728 23.986 23.719 12.99 16.026 12.99 24 10 24.363 6 7.607 6 8 10 16 10 8.25 12.99" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 640 B

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<path d="m98.99 95.188a1.6801 1.6801 0 0 0-0.79082-0.25267 0.88598 0.88598 0 0 0-0.14438 0 1.3913 1.3913 0 0 0-0.53816-0.7416 0.16407 0.16407 0 0 0-0.21001 0.02297 1.3323 1.3323 0 0 0-0.23626 1.0763 0.17063 0.17063 0 0 1-0.04266 0.13454 0.14766 0.14766 0 0 1-0.11157 0.04922h-0.14766v-0.80723a0.16407 0.16407 0 0 0-0.16407-0.16407h-0.82035v-1.8048a0.16407 0.16407 0 0 0-0.16407-0.16407h-0.98442a0.16407 0.16407 0 0 0-0.16407 0.16407v0.82035h-1.8048a0.16407 0.16407 0 0 0-0.16407 0.16407v0.82035h-0.82035a0.16407 0.16407 0 0 0-0.16407 0.16407v0.82035h-0.16407a0.16407 0.16407 0 0 0-0.16407 0.16407c0 2.5168 1.8343 2.7892 2.6251 2.7892a4.6891 4.6891 0 0 0 4.0624-2.3232 1.6834 1.6834 0 0 0 1.1649-0.71206 0.16407 0.16407 0 0 0-0.0525-0.21985zm-2.5497 0.30189h-0.65628v-0.65628h0.65628zm-3.6095 0v-0.65628h0.65628v0.65628zm0.98442 0v-0.65628h0.65628v0.65628zm1.6407-1.6407v0.65628h-0.65628v-0.65628zm-0.65628 1.6407v-0.65628h0.65628v0.65628zm0-2.6251h0.65628v0.65628h-0.65628zm-0.32814 1.6407h-0.65628v-0.65628h0.65628zm-1.6407-0.65628h0.65628v0.65628h-0.65628zm-0.98442 0.98442h0.65628v0.65628h-0.65628zm0.98442 2.297a0.32814 0.32814 0 1 1 0.32814-0.32814 0.32814 0.32814 0 0 1-0.32814 0.32814z" fill="#03a8e7" stroke-width=".32814"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<g transform="matrix(3.7826 0 0 3.7826 -421.05 -680.81)" shape-rendering="auto">
<path d="m136.17 205.72v0.49266h0.0898v-0.49266zm0.48271 0v0.49266h0.0898v-0.49266z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#717ea0" image-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
<path d="m136.21 204.24c-0.0675 0-0.12576 0.022-0.16586 0.0617s-0.0604 0.0952-0.0604 0.15426v0.75433h0.0449c0.0512 0 0.0918 0.0406 0.0918 0.0918s-0.0406 0.0918-0.0918 0.0918h-0.0449v0.32013h0.96239v-0.32013h-0.0449c-0.0512 0-0.0918-0.0406-0.0918-0.0918s0.0406-0.0918 0.0918-0.0918h0.0449v-0.75433c0-0.059-0.0203-0.11456-0.0604-0.15426s-0.0983-0.0617-0.16586-0.0617z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#ed9310" image-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<path d="m94.495 92.965h-1.8937c-0.3486 0-0.62808 0.28256-0.62808 0.63124l-0.0024 3.7874c0 0.3486 0.28256 0.63124 0.63124 0.63124h5.0499c0.3486 0 0.63124-0.28256 0.63124-0.63124v-3.1562c0-0.3486-0.28256-0.63124-0.63124-0.63124h-2.5249z" fill="#5e6b8c" stroke-width=".15781"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 491 B

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<path d="m96.546 93.126c-0.52166 0-0.9455 0.42364-0.9455 0.94554 0 0.349 0.19131 0.65049 0.47259 0.81459v0.13088s0 0.94554-0.94554 0.94554c-0.39222 0-0.69924 0.08365-0.94558 0.21292v-2.2348c0.28176-0.16409 0.47259-0.46615 0.47259-0.81467 0-0.52194-0.42368-0.94554-0.94554-0.94554-0.52194 0-0.94558 0.42368-0.94558 0.94554 0 0.349 0.19131 0.65049 0.47259 0.81467v3.0987c-0.28176 0.16369-0.47259 0.46559-0.47259 0.81439 0 0.52174 0.42368 0.9455 0.94558 0.9455s0.94554-0.42368 0.94554-0.9455c0-0.24734-0.09726-0.47107-0.25294-0.63989 0.13768-0.1685 0.36141-0.30578 0.72582-0.30578 1.8763 0 1.891-1.8911 1.891-1.8911v-0.13088c0.28136-0.16409 0.47259-0.46607 0.47259-0.81463 0-0.52198-0.42368-0.9455-0.94558-0.9455zm-2.8366-0.47259c0.26135 0 0.47255 0.21092 0.47255 0.47259 0 0.26135-0.21092 0.47255-0.47255 0.47255-0.26135 0-0.47259-0.21092-0.47259-0.47255 0-0.26135 0.21092-0.47259 0.47259-0.47259zm0 5.6732c-0.26135 0-0.47259-0.21172-0.47259-0.47255 0-0.26055 0.21092-0.47255 0.47259-0.47255 0.26135 0 0.47255 0.21172 0.47255 0.47255 0 0.26135-0.21092 0.47255-0.47255 0.47255zm2.8366-3.7821c-0.26135 0-0.47255-0.21132-0.47255-0.47255 0-0.26135 0.21172-0.47263 0.47255-0.47263 0.26135 0 0.47259 0.21092 0.47259 0.47263 0 0.26135-0.21172 0.47255-0.47259 0.47255z" fill="#d85f44" stroke-width=".007387"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

6
src/assets/skills/go.svg Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<path d="m96.162 94.816c-0.14048 0.036-0.25655 0.0556-0.37542 0.0981-0.09405 0.024-0.18971 0.0504-0.29937 0.0789l-8e-3 1e-3c-0.05403 0.016-0.05923 0.016-0.10966-0.0412-0.06083-0.0684-0.10446-0.11207-0.18851-0.15329-0.25415-0.12407-0.49925-0.0876-0.72758 0.0608-0.27336 0.1765-0.41412 0.43861-0.41028 0.76372 4e-3 0.32259 0.22453 0.58698 0.5315 0.63084 0.28376 0.036 0.51346-0.0592 0.69404-0.26455 0.02802-0.032 0.05163-0.0684 0.07885-0.10686 8e-3 -0.012 0.02001-0.028 0.02802-0.0428h-0.77532c-0.08405 0-0.10446-0.0516-0.07604-0.12006 0.05163-0.12528 0.14848-0.3342 0.20492-0.43862 0.01201-0.024 0.04002-0.0748 0.08885-0.0748h1.3042c0.05803-0.1729 0.15209-0.347 0.27856-0.51213 0.29297-0.38582 0.636-0.58702 1.1249-0.67087 0.41024-0.07204 0.79598-0.03202 1.1469 0.20492 0.31738 0.21692 0.51345 0.51085 0.56632 0.89655 0.0684 0.54312-0.0888 0.98433-0.47343 1.3623-0.25414 0.26975-0.57925 0.43861-0.95335 0.51473-0.072 0.016-0.14328 0.02-0.21252 0.024-0.03602 2e-3 -0.07364 8e-3 -0.10966 8e-3 -0.36501-8e-3 -0.6992-0.11206-0.98044-0.3534-0.19731-0.1713-0.33419-0.38182-0.40119-0.62564-0.04763 0.0941-0.10326 0.1857-0.1805 0.27215-0.27856 0.38182-0.65662 0.6192-1.1339 0.68244-0.39463 0.0528-0.75984-0.024-1.0823-0.26456-0.29657-0.22573-0.46571-0.52246-0.50957-0.89271-0.05283-0.43861 0.07604-0.84368 0.3406-1.1778 0.28656-0.37422 0.66438-0.61152 1.1262-0.69536 0.37782-0.08005 0.73918-0.02401 1.0656 0.19731 0.21292 0.14048 0.36501 0.33299 0.46571 0.56637 0.02401 0.036 8e-3 0.0568-0.04002 0.0684zm-4.5384 0.072c-0.01601 0-0.02001-8e-3 -0.01201-0.02l0.08445-0.10807c8e-3 -0.012 0.02802-0.02 0.04443-0.02h1.4353c0.01601 0 0.02001 0.012 0.01201 0.024l-0.06844 0.10446c-8e-3 0.016-0.02802 0.024-0.04002 0.024zm-0.60703 0.37021c-0.01601 0-0.02001-8e-3 -0.01201-0.02l0.08445-0.10846c8e-3 -0.012 0.02802-0.02 0.04443-0.02h1.8334c0.01601 0 0.02401 0.016 0.02001 0.024l-0.03202 0.0969c-4e-3 0.016-0.02001 0.024-0.03602 0.024zm0.96071 0.3458c-8e-3 0.012-4e-3 0.024 0.01201 0.024l0.87662 4e-3c0.01201 0 0.02802-0.012 0.02802-0.028l8e-3 -0.0969c0-0.016-8e-3 -0.028-0.02401-0.028h-0.8037c-0.01601 0-0.03202 0.012-0.04042 0.024zm6.4694-0.23213c-2e-3 -0.032-4e-3 -0.062-8e-3 -0.0901-0.072-0.39742-0.43861-0.62311-0.82047-0.5341-0.37421 0.0841-0.61535 0.32138-0.70308 0.69924-0.07204 0.31338 0.08005 0.6308 0.36901 0.75983 0.22173 0.0969 0.44254 0.0853 0.65538-0.024 0.31738-0.1757 0.4902-0.42184 0.51085-0.7676-1e-3 -0.016-1e-3 -0.028-2e-3 -0.0424z" fill="#7889b6" stroke-width=".0129"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<title>Foundation/Logo/Qt-logo</title>
<g transform="matrix(.20209 0 0 .20209 3.6237 3.8078)">
<polygon points="4.625 28.889 2 0 30.002 0 27.377 28.889 16.001 32" fill="#e44d26"/>
<polygon points="27.688 2 16 2 16 29.75 25.232 27.008" fill="#ff6c39"/>
<polygon points="12.857 20.984 12.648 19 8.803 19 9.262 23.987 16 25.99 22.728 23.986 23.718 13 12.252 13 12.002 10 24 10 24.363 6 7.607 6 8 10 8.619 17 19.503 17 19.148 20.984 16 21.99" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 631 B

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(.26458 0 0 .26458 3.6824 -268.36)">
<path d="m23 1048.4c0 1.1-0.895 2-2 2h-18c-1.1046 0-2-0.9-2-2v-14c0-1.1 0.8954-2 2-2h18c1.105 0 2 0.9 2 2v4z" fill="#95a5a6"/>
<path d="m1 1047.4c0 1.1 0.8954 2 2 2h18c1.105 0 2-0.9 2-2v-14c0-1.1-0.895-2-2-2h-18c-1.1046 0-2 0.9-2 2v4z" fill="#bdc3c7"/>
<rect transform="translate(0,1028.4)" x="3" y="5" width="18" height="14" fill="#f39c12"/>
<path transform="translate(0,1028.4)" d="M 16.625,8.625 6.281,19 H 21 v -6 z" fill="#e67e22"/>
<path transform="matrix(1.75,0,0,1.75,-3,1024.4)" d="m8 8a2 2 0 1 1-4 0 2 2 0 1 1 4 0z" fill="#f1c40f"/>
<path transform="translate(0,1028.4)" d="M 8.0938,11.094 3,16.188 V 19 h 13 z" fill="#d35400"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 879 B

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<path d="m6.8233 3.9168c-0.19711-0.00291-0.38735 0.073554-0.52731 0.21272l-2.3349 2.3913c-0.8845 0.88371 0.57256 0.80989 1.0991 1.1443 0.24395 0.15928-0.78184 0.36274-0.57256 0.57256 0.20399 0.20982 1.2303 0.40243 1.4346 0.60669 0.20399 0.20928-0.41381 0.43048-0.20981 0.63976 0.19844 0.20982 0.67442 0.01138 0.75935 0.48736 0.062177 0.35137 0.87312 0.17621 1.2361-0.12462 0.22595-0.19288-0.39132-0.19288-0.18732-0.40243 0.51038-0.51567 0.96361-0.23151 1.1507-0.70802 0.10239-0.25479-0.77073-0.43656-0.53816-0.60087 0.55589-0.39079 2.5961-0.58923 1.655-1.5298l-2.4201-2.4762c-0.14817-0.13785-0.34264-0.21352-0.54451-0.21272zm0.019844 0.22728c0.13996 7.937e-4 0.28046 0.053181 0.38206 0.15452l0.92392 0.94112c0.08546 0.084931 0.08546 0.26088 0.03413 0.31168l-0.45879-0.37359-0.090752 0.54954-0.37968-0.20426-0.61807 0.39105-0.20399-0.82153-0.32861 0.71385-0.82206-0.0058209c-0.15875 0-0.136-0.16431 0.02831-0.32861 0.32306-0.35719 0.9525-0.96308 1.1509-1.1729 0.10186-0.10478 0.24209-0.15584 0.38232-0.15452zm2.581 4.3064c-0.19579 0.0066-0.39582 0.10451-0.44688 0.28575 0 0.1188 0.92392 0.18706 0.87339-0.02831-0.03731-0.1815-0.23019-0.26511-0.42624-0.25797v5.29e-4zm-4.0603 0.51462c-0.2667 0.01561-0.55245 0.20928-0.32597 0.40005 0.20929 0.1815 0.52705-0.03969 0.62891-0.29448-0.0635-0.08361-0.1815-0.11271-0.30242-0.1053h-5.292e-4zm3.5002 0.02037c-0.26088 0.23812 0.04498 0.48736 0.30004 0.32306 0.06826-0.04577-0.0056-0.26591-0.30004-0.32306z" fill="#5e6b8c" stroke-width=".26458"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

5
src/assets/skills/js.svg Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<title>Foundation/Logo/Qt-logo</title>
<path d="m3.6239 3.8078v6.467h6.467v-6.467zm3.4645 5.0235c0 0.62952-0.37185 0.93642-0.91014 0.93642-0.48618 0-0.76839-0.25175-0.91159-0.55561l0.49484-0.29953c0.09543 0.16933 0.1679 0.31252 0.37618 0.31252 0.17323 0 0.31557-0.078096 0.31557-0.38196v-2.0328h0.63515zm1.4341 0.92198c-0.56428 0-0.92891-0.25464-1.1069-0.60628l0.49556-0.28683c0.12992 0.21278 0.29954 0.35453 0.59906 0.35453 0.25176 0 0.39798-0.11144 0.39798-0.2851 0-0.2083-0.15057-0.28207-0.42844-0.40419l-0.15187-0.065247c-0.43853-0.1865-0.72926-0.42093-0.72926-0.91592 0-0.45572 0.34717-0.7886 0.88979-0.7886 0.38643 0 0.66402 0.1201 0.86395 0.47174l-0.47406 0.30473c-0.10422-0.18665-0.21655-0.25983-0.39062-0.25983-0.178 0-0.29088 0.11288-0.29088 0.25983 0 0.18232 0.11289 0.25608 0.3733 0.36897l0.15187 0.065104c0.51664 0.22144 0.80751 0.44749 0.80751 0.95504 1.62e-4 0.5471-0.4296 0.83205-1.007 0.83205z" fill="#e7d34b" stroke-width=".014435"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<title>Foundation/Logo/Qt-logo</title>
<title>wiki:Behnam N</title>
<g id="a" transform="matrix(.27612 0 0 .27612 3.5487 3.7277)">
<path d="m20.93 19.07a3.72 3.72 0 0 1-0.57-0.43c-0.09-0.09 0-0.5 0-0.72a1.45 1.45 0 0 0-0.2-1.17 0.91 0.91 0 0 0-0.16-0.12v-0.64a9.84 9.84 0 0 0-2.7-6.62 4.42 4.42 0 0 1-1.3-2.37v-0.23c0-1.38 0-4.26-1.5-5.77a3.39 3.39 0 0 0-2.5-1c-2.1 0-3.5 1-3.5 6a8 8 0 0 1-1.19 3.68c-0.27 0.52-0.54 1.06-0.78 1.63a24.32 24.32 0 0 0-1.53 4.79 1.59 1.59 0 0 0-0.78 1.2l-0.08 0.33a2.78 2.78 0 0 1-0.54 0.14c-0.6 0.12-1.6 0.32-1.6 1.23a3.82 3.82 0 0 0 0.08 0.73 3.21 3.21 0 0 1-0.08 1.65 0.49 0.49 0 0 0 0 0.13c0 1.19 1.87 1.35 3 1.49h0.44a4.52 4.52 0 0 1 0.79 0.32 5.82 5.82 0 0 0 2.27 0.68 1.17 1.17 0 0 0 0.86-0.39h0.05a5.94 5.94 0 0 1 2.59-0.61h3.06a1.33 1.33 0 0 0 0.29 0.55 1.48 1.48 0 0 0 1.15 0.45 3.2 3.2 0 0 0 2.5-1.32l0.32-0.33a3.54 3.54 0 0 1 1.19-0.59c0.72-0.26 1.46-0.53 1.46-1.26s-0.57-1.11-1.04-1.43zm-7.93-16.07c0.58 0 1 0.62 1 1.48s-0.33 1.34-0.81 1.45a0.82 0.82 0 0 0 0.29-0.68c0-0.41-0.22-0.75-0.48-0.75s-0.48 0.33-0.48 0.75a0.82 0.82 0 0 0 0.29 0.68c-0.48-0.12-0.81-0.7-0.81-1.45s0.42-1.48 1-1.48zm0.44 3.88a5.82 5.82 0 0 1-2.44 1.12 3.74 3.74 0 0 1-1.5-1 3.07 3.07 0 0 1 2-1 3.91 3.91 0 0 1 1.94 0.88zm-3.44-3.88c0.58 0 1 0.62 1 1.48s-0.33 1.33-0.81 1.45a0.82 0.82 0 0 0 0.29-0.68c0-0.41-0.22-0.75-0.48-0.75s-0.48 0.33-0.48 0.75a0.82 0.82 0 0 0 0.29 0.68c-0.48-0.12-0.81-0.69-0.81-1.45s0.42-1.48 1-1.48zm-1.23 19.71c-0.06 0.19-0.15 0.29-0.27 0.29a5.14 5.14 0 0 1-1.87-0.6 4.44 4.44 0 0 0-1-0.4h-0.56c-1.49-0.12-1.95-0.3-2.05-0.44a4.14 4.14 0 0 0 0.05-2 3.36 3.36 0 0 1-0.07-0.56 2.24 2.24 0 0 1 0.83-0.26 2 2 0 0 0 1-0.39 1.66 1.66 0 0 0 0.37-0.82c0.13-0.53 0.18-0.53 0.25-0.53a3.92 3.92 0 0 1 0.7 1.18 5.6 5.6 0 0 0 1.54 2.24 2.27 2.27 0 0 1 1.08 2.29zm6.37-3c-1.47 1.48-4.41 1.38-5.71 1.18a3.64 3.64 0 0 0-1.15-1.29 5 5 0 0 1-1.21-1.85 4 4 0 0 0-1-1.57 23.88 23.88 0 0 1 1.43-4.48c0.21-0.53 0.48-1 0.74-1.55a15.43 15.43 0 0 0 0.94-2.09 3.86 3.86 0 0 0 1.82 0.94 10.05 10.05 0 0 0 2.83-1.2 18.65 18.65 0 0 1 2.17 8.95 6.06 6.06 0 0 0-0.86 2.95zm5.06 1.12a4.19 4.19 0 0 0-1.55 0.83l-0.34 0.35a2.27 2.27 0 0 1-1.81 1 0.55 0.55 0 0 1-0.41-0.12c-0.15-0.17-0.11-0.56-0.1-0.72s0.01-0.17 0.01-0.17c0-0.3 0.28-3.13 0.37-3.58a1.43 1.43 0 0 0 0.13 0.17 1.34 1.34 0 0 0 1 0.42h0.35a2 2 0 0 0 1.47-0.45 1.24 1.24 0 0 0 0.33 0.8 4.53 4.53 0 0 0 0.73 0.56c0.38 0.25 0.62 0.42 0.64 0.53a2.89 2.89 0 0 1-0.82 0.37z" fill="#5e6b8c"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(.033326 0 0 .033326 2.6962 4.2782)">
<g fill="#f9f9f9">
<polyline class="st0" points="0 110.9 0 35.7 18.8 35.7 18.8 95.1 48.2 95.1 48.2 110.9 0 110.9"/>
<path class="st0" d="m143.8 49.6v20.3h17.2c6.7 0 10.1-3.4 10.1-10.1 0-6.8-3.3-10.1-10.1-10.1h-17.2zm-18.8 61.3v-75.2h37.6c18.2 0 27.3 8.2 27.3 24.7 0 4.9-1.4 9.1-4.2 12.7s-6.6 6.2-11.3 7.6l17.2 30.2h-21l-14.8-27.1h-12v27.1h-18.8"/>
<path class="st0" d="m232.4 87.5v-3.9l-11.1 3.2c-5.3 1.5-7.9 3.9-7.9 7.3 0 4.2 2.6 6.3 7.7 6.3 3.4 0 6.1-1.1 8.2-3.3 2-2.3 3.1-5.5 3.1-9.6m15.8-18v29.7c0 5.3 0.5 9.2 1.5 11.7h-15.2c-0.6-1.8-0.9-3.7-1-5.8h-0.2c-1.3 2.1-3.8 3.9-7.3 5.3-3.2 1.3-6.4 2-9.6 2-5.6 0-10-1.4-13.4-4.3-3.6-3-5.4-7.2-5.4-12.4 0-6.4 2.6-11.3 7.7-14.6 4.4-2.8 11.4-4.9 21.1-6.2l6-0.8v-4.7c0-4-2.6-6-7.8-6-2.7 0-4.7 0.6-6 1.7s-2.2 3.1-2.6 5.8h-16c0.3-11.8 8.1-17.6 23.2-17.6 8.2 0 14.3 1.1 18.3 3.4 4.5 2.5 6.7 6.8 6.7 12.8"/>
<path class="st0" d="m84 65.8c-4.5 0-7.9 1.6-10.2 4.9-2.1 3-3.2 7-3.2 12.1s1.1 9.1 3.2 12.1c2.3 3.3 5.7 4.9 10.2 4.9 9 0 13.4-5.9 13.4-17.7 0-4.8-1.1-8.7-3.2-11.6-2.3-3.1-5.7-4.7-10.2-4.7m0-12.6c9 0 16.2 2.6 21.7 7.8s8.2 12.5 8.2 21.8-2.7 16.6-8.2 21.8-12.7 7.8-21.7 7.8-16.2-2.6-21.7-7.8-8.2-12.5-8.2-21.8 2.7-16.6 8.2-21.8c5.6-5.2 12.8-7.8 21.7-7.8"/>
</g>
<g fill="#009fe3">
<path class="st0" d="m83.5 42c8.2 0 15.9 2.4 22.3 6.6l3.2-4.6c-7.4-4.8-16.1-7.6-25.5-7.6-9 0-17.4 2.6-24.6 7l3.2 4.6c6.3-3.9 13.6-6 21.4-6"/>
<path class="st0" d="m48.9 29 4.3 6.1c8.8-5.6 19.2-8.8 30.3-8.8 11.6 0 22.3 3.5 31.3 9.4l4.3-6.1c-10.2-6.8-22.5-10.8-35.6-10.8-12.7 0-24.6 3.8-34.6 10.2"/>
<path class="st0" d="m83.5 10.3c15 0 28.9 4.5 40.6 12.3l5.4-7.7c-13.1-8.9-29-14-46-14-16.5 0-32 4.9-44.9 13.3l5.4 7.7c11.4-7.3 24.9-11.6 39.5-11.6"/>
<path class="st0" d="m84.6 123.8c-8.2 0-15.9-2.4-22.3-6.6l-3.2 4.6c7.4 4.8 16.1 7.6 25.6 7.6 9 0 17.4-2.6 24.6-7l-3.2-4.6c-6.4 3.9-13.7 6-21.5 6"/>
<path class="st0" d="m119.2 136.8-4.3-6.1c-8.8 5.6-19.2 8.8-30.3 8.8-11.6 0-22.3-3.5-31.3-9.4l-4.3 6.1c10.2 6.8 22.4 10.8 35.6 10.8 12.7 0 24.6-3.8 34.6-10.2"/>
<path class="st0" d="m84.6 155.5c-15 0-28.9-4.5-40.6-12.3l-5.4 7.7c13.1 8.8 29 14 46 14 16.5 0 32-4.9 44.9-13.3l-5.4-7.7c-11.4 7.3-25 11.6-39.5 11.6"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<path d="m97.136 93.387h-4.0165c-0.05243 0-0.09525 0.04282-0.09525 0.09565v0.57373c0 0.05283 0.04282 0.09526 0.09525 0.09526h0.66914c-0.0031 0.87918-0.07524 2.9303-0.36141 3.2873-0.02401 0.02802-0.02802 0.06804-0.01201 0.10126 0.01601 0.03202 0.04923 0.05403 0.08605 0.05403h0.57381c0.02802 0 0.05643-0.01201 0.07484-0.03602 0.36501-0.45654 0.40023-2.8345 0.40315-3.4068h1.1479v2.7732c0 0.36901 0.30017 0.66942 0.66942 0.66942h0.57377c0.02802 0 0.05963-0.01601 0.07764-0.04002 0.02001-0.02401 0.02401-0.05723 0.01201-0.08605l-0.19131-0.57385c-0.01201-0.04002-0.04963-0.06564-0.09045-0.06564l-0.19091 1.6e-4h-1.19e-4c-0.05243 0-0.09526-0.04282-0.09526-0.09525l-1.6e-4 -2.5822h0.66942c0.05243 0 0.09565-0.04283 0.09565-0.09526v-0.57373c0-0.05243-0.04283-0.09565-0.09565-0.09565z" fill="#e03400" stroke-width=".19126"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<path d="m95.126 94.139c-0.74631 0-1.3519 0.60555-1.3519 1.3524 0 0.74603 0.60555 1.3528 1.3519 1.3528 0.74663 0 1.3477-0.60679 1.3477-1.3528 0-0.74687-0.60106-1.3524-1.3477-1.3524zm2.7105 2.1293-0.24414 0.58702 0.43449 0.85349 0.0576 0.11286-0.60262 0.60263-0.98869-0.46667-0.58698 0.24094-0.29777 0.912-0.03602 0.12087h-0.85173l-0.36861-1.0294-0.58694-0.24174-0.85493 0.43349-0.11246 0.0564-0.60234-0.60206 0.46667-0.98929-0.24174-0.58638-0.91052-0.29737-0.12087-0.04162v-0.85129l1.0302-0.36901 0.24174-0.5859-0.43341-0.85537-0.05723-0.11206 0.60159-0.60158 0.99001 0.46615 0.58614-0.24254 0.29737-0.91148 0.03602-0.12087h0.85149l0.36861 1.0307 0.58542 0.24254 0.85513-0.43393 0.11366-0.05723 0.60207 0.6015-0.46615 0.98849 0.24134 0.58722 0.913 0.29737 0.12007 0.03602v0.85097z" fill="#3d809c" stroke-width=".0083479"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<g transform="matrix(.2443 0 0 .2443 91.218 91.582)">
<path d="m23.75 15.75c0 6.4893-4.7124 9.5-7.75 9.5s-7.75-3.0107-7.75-9.5 7.75-14 7.75-14 7.75 7.5107 7.75 14z" fill="#88b03c"/>
<path d="m15.55 5.953a0.50005 0.50005 0 0 0-0.49219 0.50782v5.0352l-1.8906-1.8906a0.50005 0.50005 0 0 0-0.35938-0.15235 0.50005 0.50005 0 0 0-0.34765 0.85938l2.5976 2.5976v2.8359l-2.3906-2.3906a0.50005 0.50005 0 0 0-0.35938-0.15235 0.50005 0.50005 0 0 0-0.34765 0.85938l3.0976 3.0976v2.8359l-2.6406-2.6406a0.50005 0.50005 0 0 0-0.35938-0.15235 0.50005 0.50005 0 0 0-0.34765 0.85938l3.3476 3.3477v3.7422c0.32495 0.05865 0.64277 0.09766 0.94141 0.09766 0.0189 0 0.0395-0.0037 0.0586-0.0039v-3.8359l3.3496-3.3477a0.50005 0.50005 0 1 0-0.70703-0.70703l-2.6426 2.6406v-2.8359l3.0996-3.0976a0.50005 0.50005 0 1 0-0.70703-0.70703l-2.3926 2.3906v-2.8359l2.5996-2.5976a0.50005 0.50005 0 1 0-0.70703-0.70703l-1.8926 1.8906v-5.0352a0.50005 0.50005 0 0 0-0.50781-0.50782z" color="#000000" color-rendering="auto" fill="#81a53c" fill-rule="evenodd" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="isolation:auto;mix-blend-mode:normal;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-transform:none;white-space:normal"/>
<path d="m15.992 5.748a0.50005 0.50005 0 0 0-0.49219 0.50781v5.0352l-1.8906-1.8906a0.50005 0.50005 0 0 0-0.35938-0.15234 0.50005 0.50005 0 0 0-0.34766 0.85938l2.5977 2.5977v2.8359l-2.3906-2.3906a0.50005 0.50005 0 0 0-0.35938-0.15234 0.50005 0.50005 0 0 0-0.34766 0.85938l3.0977 3.0977v2.8359l-2.6406-2.6406a0.50005 0.50005 0 0 0-0.35938-0.15234 0.50005 0.50005 0 0 0-0.34766 0.85938l3.3477 3.3477v8.5371a0.50005 0.50005 0 1 0 1 0v-8.5371l3.3496-3.3477a0.50005 0.50005 0 1 0-0.70703-0.70703l-2.6426 2.6406v-2.8359l3.0996-3.0977a0.50005 0.50005 0 1 0-0.70703-0.70703l-2.3926 2.3906v-2.8359l2.5996-2.5977a0.50005 0.50005 0 1 0-0.70703-0.70703l-1.8926 1.8906v-5.0352a0.50005 0.50005 0 0 0-0.50781-0.50781z" color="#000000" color-rendering="auto" fill="#94b84f" fill-rule="evenodd" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="isolation:auto;mix-blend-mode:normal;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-transform:none;white-space:normal"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="b" x1="18.894" x2="18.894" y1="15.287" y2="22.614" gradientUnits="userSpaceOnUse">
<stop stop-color="#5d5c66" offset="0"/>
<stop stop-color="#48474f" offset="1"/>
</linearGradient>
<linearGradient id="a" x1="9.998" x2="9.998" y1="1.149" y2="18.652" gradientUnits="userSpaceOnUse">
<stop stop-color="#a6f9ff" offset="0"/>
<stop stop-color="#3ed0f7" offset="1"/>
</linearGradient>
</defs>
<g transform="matrix(.26458 0 0 .26458 3.6839 3.8673)">
<path class="cls-1" d="m17.607 14.79 4.8 4.8a1.994 1.994 0 0 1-2.82 2.82l-4.8-4.8z" fill="url(#b)"/>
<circle class="cls-2" cx="9.998" cy="10" r="9" fill="url(#a)"/>
<path class="cls-3" d="m5 11a1 1 0 0 1-1-1 6.006 6.006 0 0 1 6-6 1 1 0 0 1 0 2 4 4 0 0 0-4 4 1 1 0 0 1-1 1z" fill="#cffeff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 960 B

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<g transform="matrix(.11078 0 0 .11078 89.589 89.95)">
<path d="m19.985 35 30.001-14.986 30.001 14.986-0.017 1.981-29.984 15.004-29.989-14.98z" fill="#efc75e"/>
<path d="m19.986 35 29.977 14.999 30.023-14.999v30l-30.109 15-29.891-15z" fill="#e7bf55"/>
<path d="m49.986 79.945v-29.958l-0.023 0.012-29.977-14.999v30l29.891 15z" fill="#dbb551"/>
<path d="m61.99 43.991v8.792l2.678 0.216 1.707-2.033 1.607 0.253v-10.222z" fill="#f1d999"/>
<path d="m31.967 29.015 30.51 15.279 5.502-3.26-30-15.023z" fill="#f7e3af"/>
<path d="m43.979 63.122-9.984-5.01c-0.551-0.277-0.998 0.06-0.998 0.751v6.261c0 0.692 0.447 1.478 0.998 1.754l9.984 5.01c0.551 0.276 0.998-0.061 0.998-0.752v-6.261c0-0.691-0.448-1.477-0.998-1.753zm-2.497 5.009-5.99-3.007c-0.275-0.138-0.499-0.53-0.499-0.876s0.224-0.514 0.499-0.376l5.99 3.006c0.275 0.139 0.499 0.531 0.499 0.877s-0.223 0.514-0.499 0.376zm0.998-2.004-6.988-3.507c-0.275-0.139-0.499-0.531-0.499-0.877s0.224-0.514 0.499-0.376l6.988 3.508c0.276 0.138 0.499 0.531 0.499 0.876 0 0.346-0.222 0.515-0.499 0.376z" fill="#f2f2f2"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<title>Foundation/Logo/Qt-logo</title>
<path id="a" d="m6.8574 4.0411a3.0002 3.0002 0 0 0-3.0001 3.0001 3.0002 3.0002 0 0 0 3.0001 3.0004 3.0002 3.0002 0 0 0 3.0001-3.0004 3.0002 3.0002 0 0 0-3.0001-3.0001zm-0.31759 2.105h0.30578l-0.072564 0.37598h0.27169c0.1711 0 0.28977 0.030683 0.3554 0.090452 0.065629 0.059769 0.084172 0.15699 0.058389 0.29059l-0.12758 0.65848h-0.31051l0.12184-0.62574c0.014063-0.071489 0.0082511-0.11968-0.015188-0.14547-0.024611-0.025783-0.075046-0.038476-0.15357-0.038476h-0.24503l-0.15694 0.80968h-0.30612zm-1.2846 0.37598h0.593c0.17814 0 0.30812 0.048157 0.39016 0.14074 0.082036 0.092584 0.10673 0.22392 0.073914 0.39151-0.012891 0.069145-0.036196 0.13225-0.067839 0.18968-0.031643 0.057425-0.073987 0.10889-0.12555 0.15694-0.060941 0.057425-0.12986 0.098579-0.20487 0.12319-0.075005 0.024611-0.1712 0.036451-0.28722 0.036451h-0.26359l-0.072902 0.37598h-0.30814zm2.4047 0h0.593c0.17814 0 0.30846 0.046985 0.3905 0.14074 0.082036 0.092584 0.10673 0.22392 0.073914 0.39151-0.012892 0.069145-0.036535 0.13225-0.068176 0.18968-0.031643 0.057425-0.073649 0.10889-0.12522 0.15694-0.062111 0.057425-0.1302 0.098579-0.2052 0.12319-0.075005 0.024611-0.1712 0.036451-0.28722 0.036451h-0.26359l-0.072564 0.37598h-0.30848zm-2.1374 0.22275-0.11475 0.59199h0.18732c0.12423 0 0.21683-0.023661 0.27777-0.070539 0.060941-0.046878 0.1021-0.12524 0.12319-0.23423 0.019923-0.10548 0.010324-0.17939-0.028351-0.22275-0.039846-0.043362-0.11704-0.064464-0.23423-0.064464zm2.4047 0-0.11475 0.59199h0.18732c0.12423 0 0.21683-0.023661 0.27777-0.070539 0.060942-0.046877 0.1021-0.12524 0.12319-0.23423 0.01992-0.10548 0.010659-0.17939-0.028012-0.22275-0.039847-0.043362-0.11737-0.064464-0.23457-0.064464z" fill="#8892be" stroke-width=".01172"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<g transform="matrix(3.7826 0 0 3.7826 -393 -511.06)" stroke-width=".013704">
<path d="m129.04 159.58c-0.0628 2.9e-4 -0.1228 6e-3 -0.17558 0.015-0.15548 0.0275-0.18371 0.085-0.18371 0.191v0.14003h0.36743v0.0467h-0.50533c-0.10678 0-0.20028 0.0642-0.22953 0.18628-0.0337 0.13996-0.0352 0.22729 0 0.37343 0.0261 0.10878 0.0885 0.18628 0.19527 0.18628h0.12634v-0.16787c0-0.12127 0.10493-0.22825 0.22953-0.22825h0.367c0.10216 0 0.18372-0.0841 0.18372-0.18671v-0.34987c0-0.0996-0.084-0.17438-0.18372-0.191-0.0631-0.0105-0.12861-0.0153-0.19142-0.015zm-0.1987 0.11262c0.0379 0 0.0689 0.0315 0.0689 0.0702 0 0.0386-0.031 0.0698-0.0689 0.0698-0.0381 0-0.069-0.0312-0.069-0.0698 0-0.0387 0.0309-0.0702 0.069-0.0702z" fill="#498abc"/>
<path d="m129.46 159.98v0.16316c0 0.12649-0.10725 0.23296-0.22954 0.23296h-0.367c-0.10053 0-0.18371 0.086-0.18371 0.18671v0.34987c0 0.0996 0.0866 0.15815 0.18371 0.18672 0.11631 0.0342 0.22784 0.0404 0.367 0 0.0925-0.0268 0.18372-0.0807 0.18372-0.18672v-0.14003h-0.367v-0.0467h0.55071c0.10678 0 0.14658-0.0745 0.18371-0.18628 0.0384-0.1151 0.0367-0.22578 0-0.37343-0.0264-0.1063-0.0768-0.18628-0.18371-0.18628zm-0.20641 0.88603c0.0381 0 0.0689 0.0312 0.0689 0.0698 0 0.0387-0.0309 0.0702-0.0689 0.0702s-0.069-0.0315-0.069-0.0702c0-0.0386 0.031-0.0698 0.069-0.0698z" fill="#ffd63f"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

12
src/assets/skills/qt.svg Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<title>Foundation/Logo/Qt-logo</title>
<g id="a" transform="matrix(.025787 0 0 .025787 2.9856 4.1403)" fill="none" fill-rule="evenodd">
<g id="b" transform="translate(-177,-251)" fill="#41cd52">
<g id="c" transform="translate(177,251)">
<path d="m111.88 57.447c-11.797 0-19.933 4.0086-24.408 12.131-4.373 8.1227-6.6104 20.992-6.6104 38.82 0 17.722 2.1357 30.487 6.407 38.082s12.509 11.498 24.611 11.498c12.102 0 20.238-3.7976 24.509-11.287 4.1696-7.4898 6.3053-20.254 6.3053-38.082 0-17.933-2.1357-30.909-6.5087-39.031-4.373-8.1227-12.509-12.131-24.306-12.131"/>
<path d="m219.23 173.77c-10.746 0-18.115-2.4989-22.108-7.4966-3.9905-4.9977-6.0374-14.474-6.0374-28.424v-44.979h-14.533v-17.908h14.533v-28.112h20.572v28.112h26.201v18.012h-26.201v42.586c0 7.912 0.61408 13.118 1.7399 15.618 1.1248 2.603 4.0939 3.8524 8.6994 3.8524l15.557-0.62576 0.9201 16.868c-8.3914 1.6649-14.84 2.4989-19.342 2.4989zm-59.259 20.198-19.548 9.2676-16.887-28.217c-2.4563 0.72883-6.2431 1.1464-11.565 1.1464-19.753 0-33.57-5.4142-41.348-16.244-7.7783-10.827-11.667-28.007-11.667-51.331 0-23.427 3.9915-40.814 11.872-52.163 7.8807-11.35 21.595-17.077 41.143-17.077 19.548 0 33.263 5.6235 41.041 16.972 7.8807 11.245 11.77 28.633 11.77 52.164 0 15.513-1.6375 28.007-4.8103 37.483-3.2761 9.5789-8.4948 16.867-15.864 21.865zm111.46-193.97h-229.77l-41.655 42.376v182.62h258.63l41.655-42.375v-182.62z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<g transform="matrix(.057303 .057303 .057303 -.057303 88.824 95.155)">
<path d="m75 23-25.208 25.208c-2.124-0.768-4.404-1.208-6.792-1.208-11.046 0-20 8.954-20 20s8.954 20 20 20 20-8.954 20-20c0-2.388-0.44-4.668-1.208-6.792l1.208-1.208v-8h8v-8h8v-8h8v-12zm-27.405 53.204c-0.2 0.955-0.869 1.744-1.777 2.1-3.631 1.422-7.918 0.666-10.853-2.269-2.935-2.934-3.691-7.223-2.268-10.855 0.356-0.909 1.146-1.577 2.102-1.776s1.946 0.098 2.636 0.788l9.375 9.375c0.69 0.69 0.985 1.682 0.785 2.637z" fill="#facb1b"/>
<path d="m87 23h-12l-25.208 25.208c-2.124-0.768-4.404-1.208-6.792-1.208-11.046 0-20 8.954-20 20 0 5.701 2.397 10.832 6.224 14.475l5.602-5.602c-2.809-2.929-3.526-7.124-2.128-10.693 0.356-0.909 1.146-1.577 2.102-1.776s1.946 0.098 2.636 0.788l4.536 4.536 45.028-45.03z" fill="#fbe158"/>
<path d="m55 49c-0.552-0.552-0.552-1.448 0-2l22-22h4l-24 24c-0.552 0.552-1.448 0.552-2 0z" fill="#f3b607"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="c">
<rect x="79.457" width="155.7" height="155.7" rx="77.852" fill="#fff"/>
</clipPath>
<linearGradient id="b" x1="147.45" x2="134.89" y1="57.646" y2="57.459" gradientUnits="userSpaceOnUse">
<stop stop-color="#F5FF80" stop-opacity="0" offset="0"/>
<stop stop-color="#F5FF80" offset="1"/>
</linearGradient>
<linearGradient id="a" x1="123.31" x2="107.66" y1="98.025" y2="98.025" gradientUnits="userSpaceOnUse">
<stop stop-color="#F5FF80" stop-opacity="0" offset="0"/>
<stop stop-color="#F5FF80" offset=".96"/>
</linearGradient>
</defs>
<g transform="matrix(.050694 0 0 .050694 -1.1176 3.0968)" clip-path="url(#c)">
<path d="m147.26 70.076-8e-3 -12.83c0-0.7636-0.303-1.4962-0.843-2.0366l-11.107-11.128-7.749 8.5489 18.109 18.108c0.59 0.5901 1.599 0.1713 1.599-0.663z" fill="url(#b)"/>
<path d="m122.73 101.01-21.827-21.78-7.8007 8.4737 28.875 28.846c0.59 0.589 1.598 0.172 1.598-0.663v-12.839c0-0.765-0.304-1.498-0.845-2.038z" fill="url(#a)"/>
<path d="m151.8 0.47409c-39.176 2.7063-70.485 35.133-71.96 74.374-1.4399 38.314 24.907 70.732 60.479 78.684l-47.424-47.431c-0.6796-0.68-1.0608-1.602-1.0586-2.564l0.0354-15.323c0.0011-0.6399 0.7757-0.9603 1.2277-0.5073l7.8004-8.4736c-0.489-0.4896-0.489-1.2841 0-1.7726l7.504-7.4857c0.489-0.4884 1.282-0.4873 1.77 0l25.35 25.35c1.078 1.0774 1.682 2.5383 1.682 4.0611v26.449c0 3.811 1.514 7.465 4.208 10.159l19.329 19.329c4.91-0.21 9.696-0.882 14.324-1.967l-24.486-24.494c-1.211-1.211-1.892-2.854-1.892-4.567v-25.56c0-4.1904-1.664-8.2095-4.628-11.172l-25.729-25.729c-0.49-0.4896-0.49-1.2841 1e-3 -1.7737l7.451-7.4283c0.49-0.4884 1.282-0.4873 1.771 0.0012l7.748-8.5489c-0.144-0.1657-0.282-0.347-0.419-0.5348-2.212-2.9859-3.557-7.9101-1.989-13.463 0.94-3.555 2.89-6.2359 6.486-9.4936 0.316-0.2862 0.814-0.3006 1.136-0.0221l9.314 8.0692 1.834 1.5924c0.705 0.6111 1.547 1.0443 2.454 1.2598l29.038 6.9022c0.339 0.0796 0.651 0.2531 0.896 0.4918l29.827 29.755c0.521 0.4984 0.817 1.1935 0.817 1.9162v6.3265c0 0.9725-0.245 1.9305-0.714 2.7825l-14.76 26.893c-0.643 1.178-2.083 1.67-3.312 1.135l-6.804-2.965h-24.866c-1.598 0-2.892 1.295-2.892 2.892v11.087c0 1.526 0.603 2.991 1.678 4.074l25.755 25.952c27.123-12.076 46.038-39.278 46.038-70.898 0-44.657-37.722-80.495-83.025-77.366z" fill="#f5ff80"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

5
src/assets/skills/ts.svg Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<title>Foundation/Logo/Qt-logo</title>
<path d="m3.624 3.808h6.4666v6.4666h-6.4666zm4.3111 2.5867c-0.47619 0-0.86222 0.38603-0.86222 0.86222 0 0.47619 0.38603 0.86222 0.86222 0.86222h0.43111c0.2381 0 0.43111 0.19301 0.43111 0.43111 0 0.2381-0.19301 0.43111-0.43111 0.43111h-0.43111c-0.23809 0-0.43111-0.19301-0.43111-0.43111h-0.43111c0 0.4762 0.38603 0.86222 0.86222 0.86222h0.43111c0.4762 0 0.86222-0.38602 0.86222-0.86222 0-0.47619-0.38602-0.86222-0.86222-0.86222h-0.43111c-0.23809 0-0.43111-0.19302-0.43111-0.43111 0-0.23809 0.19302-0.43111 0.43111-0.43111h0.50298c0.1984 0 0.35924 0.16085 0.35924 0.35926v0.071853h0.43111v-0.071853c0-0.43651-0.35385-0.79037-0.79035-0.79037zm-3.0178 0h2.1556v0.43111h-0.86222v2.5867h-0.43111v-2.5867h-0.86222z" clip-rule="evenodd" fill="#0077c8" fill-rule="evenodd" stroke-width=".43111"/>
</svg>

After

Width:  |  Height:  |  Size: 993 B

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<circle cx="95.127" cy="95.49" r="1.8937" fill="#5e6b8c" style="paint-order:stroke fill markers"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 314 B

10
src/assets/skills/vue.svg Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<g transform="matrix(.027999 0 0 .027999 91.543 92.399)">
<path d="M 204.8,0 H 256 L 128,220.8 0,0 H 97.92 L 128,51.2 157.44,0 Z" fill="#41b883"/>
<path d="M 0,0 128,220.8 256,0 H 204.8 L 128,132.48 50.56,0 Z" fill="#41b883"/>
<path d="M 50.56,0 128,133.12 204.8,0 H 157.44 L 128,51.2 97.92,0 Z" fill="#35495e"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 544 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="13.715mm" height="14.082mm" version="1.1" viewBox="0 0 13.715 14.082" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-88.27 -88.449)">
<g transform="matrix(.0067198 0 0 .0067198 91.095 91.458)">
<path d="m600 0 530.3 300v600l-530.3 300-530.3-300v-600z" fill="#fff"/>
<path d="m1035.6 879.3-418.1 236.5v-184.2l260.5-143.3zm28.6-25.9v-494.6l-153 88.3v317.9zm-901.5 25.9 418.1 236.5v-184.2l-260.5-143.3zm-28.6-25.9v-494.6l153 88.3v317.9zm17.9-526.6 428.8-242.6v178.1l-274.7 151.1-2.1 1.2zm894.3 0-428.8-242.6v178.1l274.7 151.1 2.1 1.2z" fill="#8ed6fb"/>
<path d="m580.8 889.7-257-141.3v-280l257 148.4v272.9m36.7 0 257-141.3v-280l-257 148.4v272.9m-276.3-453.7 258-141.9 258 141.9-258 149-258-149" fill="#1c78c0"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 806 B

5
src/assets/tick.svg Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="4.2333mm" height="4.2333mm" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg">
<path id="a" d="m2.1173 0c-1.1611 0-2.1173 0.95625-2.1173 2.1173 0 1.1611 0.95626 2.116 2.1173 2.116s2.116-0.95493 2.116-2.116c0-1.1611-0.95493-2.1173-2.116-2.1173zm-6.4e-4 0.67548a1.4418 1.4418 0 0 1 0.0445 6.38e-4 1.4418 1.4418 0 0 1 0.0445 0.0019 1.4418 1.4418 0 0 1 0.0442 0.0034 1.4418 1.4418 0 0 1 0.0442 5e-3 1.4418 1.4418 0 0 1 0.0442 0.0062 1.4418 1.4418 0 0 1 0.0438 0.0072 1.4418 1.4418 0 0 1 0.0435 9e-3 1.4418 1.4418 0 0 1 0.0432 0.01024 1.4418 1.4418 0 0 1 0.0429 0.01152 1.4418 1.4418 0 0 1 0.0426 0.0128 1.4418 1.4418 0 0 1 0.0421 0.01408 1.4418 1.4418 0 0 1 0.0418 0.0152 1.4418 1.4418 0 0 1 0.0411 0.0168 1.4418 1.4418 0 0 1 0.0405 0.01779 1.4418 1.4418 0 0 1 0.0402 0.01944 1.4418 1.4418 0 0 1 0.0392 0.02042 1.4418 1.4418 0 0 1 0.0389 0.02141 1.4418 1.4418 0 0 1 0.0382 0.02272 1.4418 1.4418 0 0 1 0.0373 0.02403 1.4418 1.4418 0 0 1 0.037 0.02504 1.4418 1.4418 0 0 1 0.0355 0.02635 1.4418 1.4418 0 0 1 0.0352 0.02733 1.4418 1.4418 0 0 1 0.0339 0.02832 1.4418 1.4418 0 0 1 0.0336 0.02931 1.4418 1.4418 0 0 1 0.0323 0.03062 1.4418 1.4418 0 0 1 0.0314 0.03128 1.4418 1.4418 0 0 1 0.0302 0.03227 1.4418 1.4418 0 0 1 0.0296 0.03326 1.4418 1.4418 0 0 1 0.0283 0.03426 1.4418 1.4418 0 0 1 0.0274 0.03491 1.4418 1.4418 0 0 1 0.0261 0.0359 1.4418 1.4418 0 0 1 0.0254 0.03656 1.4418 1.4418 0 0 1 0.0237 0.03755 1.4418 1.4418 0 0 1 0.0227 0.03821 1.4418 1.4418 0 0 1 0.0218 0.03853 1.4418 1.4418 0 0 1 0.0203 0.03952 1.4418 1.4418 0 0 1 0.0192 0.04018 1.4418 1.4418 0 0 1 0.0181 0.04051 1.4418 1.4418 0 0 1 0.0165 0.04117 1.4418 1.4418 0 0 1 0.016 0.0415 1.4418 1.4418 0 0 1 0.0144 0.04216 1.4418 1.4418 0 0 1 0.0128 0.04248 1.4418 1.4418 0 0 1 0.0112 0.04315 1.4418 1.4418 0 0 1 0.01 0.04315 1.4418 1.4418 0 0 1 8e-3 0.04347 1.4418 1.4418 0 0 1 8e-3 0.04381 1.4418 1.4418 0 0 1 6e-3 0.04381 1.4418 1.4418 0 0 1 5e-3 0.04446 1.4418 1.4418 0 0 1 3e-3 0.04413 1.4418 1.4418 0 0 1 2e-3 0.04446 1.4418 1.4418 0 0 1 6.4e-4 0.04413 1.4418 1.4418 0 0 1-6e-3 0.13306 1.4418 1.4418 0 0 1-0.0184 0.13206 1.4418 1.4418 0 0 1-0.0304 0.12942 1.4418 1.4418 0 0 1-0.0426 0.12646 1.4418 1.4418 0 0 1-0.0536 0.12186 1.4418 1.4418 0 0 1-0.065 0.11626 1.4418 1.4418 0 0 1-0.075 0.11 1.4418 1.4418 0 0 1-0.0853 0.10243 1.4418 1.4418 0 0 1-0.0939 0.09419 1.4418 1.4418 0 0 1-0.10272 0.08498 1.4418 1.4418 0 0 1-0.1096 0.07509 1.4418 1.4418 0 0 1-0.11632 0.06488 1.4418 1.4418 0 0 1-0.12176 0.05402 1.4418 1.4418 0 0 1-0.1264 0.04216 1.4418 1.4418 0 0 1-0.12944 0.03062 1.4418 1.4418 0 0 1-0.132 0.01845 1.4418 1.4418 0 0 1-0.13312 0.0059 1.4418 1.4418 0 0 1-0.13312-0.0059 1.4418 1.4418 0 0 1-0.13168-0.01845 1.4418 1.4418 0 0 1-0.12976-0.03062 1.4418 1.4418 0 0 1-0.12608-0.04216 1.4418 1.4418 0 0 1-0.12192-0.05402 1.4418 1.4418 0 0 1-0.11632-0.06488 1.4418 1.4418 0 0 1-0.10992-0.07509 1.4418 1.4418 0 0 1-0.1024-0.08498 1.4418 1.4418 0 0 1-0.0942-0.09419 1.4418 1.4418 0 0 1-0.085-0.10243 1.4418 1.4418 0 0 1-0.0754-0.11 1.4418 1.4418 0 0 1-0.0648-0.11626 1.4418 1.4418 0 0 1-0.0536-0.12186 1.4418 1.4418 0 0 1-0.0422-0.12646 1.4418 1.4418 0 0 1-0.0307-0.12942 1.4418 1.4418 0 0 1-0.0184-0.13206 1.4418 1.4418 0 0 1-6e-3 -0.13306 1.4418 1.4418 0 0 1 6e-3 -0.13272 1.4418 1.4418 0 0 1 0.0184-0.13206 1.4418 1.4418 0 0 1 0.0307-0.12976 1.4418 1.4418 0 0 1 0.0422-0.12614 1.4418 1.4418 0 0 1 0.0536-0.12186 1.4418 1.4418 0 0 1 0.0648-0.11626 1.4418 1.4418 0 0 1 0.0754-0.11 1.4418 1.4418 0 0 1 0.085-0.10243 1.4418 1.4418 0 0 1 0.0942-0.09419 1.4418 1.4418 0 0 1 0.1024-0.08498 1.4418 1.4418 0 0 1 0.10992-0.07542 1.4418 1.4418 0 0 1 0.11632-0.06454 1.4418 1.4418 0 0 1 0.12192-0.05402 1.4418 1.4418 0 0 1 0.12608-0.04216 1.4418 1.4418 0 0 1 0.12976-0.03063 1.4418 1.4418 0 0 1 0.13168-0.01845 1.4418 1.4418 0 0 1 0.13312-0.0059z" color="#000000" fill="#f8f8f8" stroke-linecap="round" stroke-width=".63731" style="paint-order:stroke fill markers"/>
<circle id="b" cx="2.1167" cy="2.1171" r=".73799" fill="#f8f8f8" style="paint-order:stroke fill markers"/>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="4.2333mm"
height="4.2333mm"
version="1.1"
viewBox="0 0 4.2333 4.2333"
id="svg25433"
sodipodi:docname="activity.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs25437" />
<sodipodi:namedview
id="namedview25435"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="51.1254"
inkscape:cx="8.0097173"
inkscape:cy="7.9999374"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg25433" />
<g
transform="translate(-139.71 -124.82)"
id="g25431">
<g
transform="matrix(.49451 0 0 .49451 38.616 -321.26)"
id="g25429">
<g
transform="matrix(.64049 0 0 .64049 75.033 338.39)"
id="g25427">
<g
transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"
id="g25425">
<circle
cx="208.71"
cy="865.03"
r="4.9253"
fill="#292a2e"
style="paint-order:stroke fill markers;fill:#2a2e36;fill-opacity:1.0"
id="circle25421" />
<path
d="m207.85 862.56a0.33661 0.33661 0 0 0-0.31641 0.18946l-0.96289 1.9766h-0.59961a0.33657 0.33657 0 0 0-0.33789 0.33594 0.33657 0.33657 0 0 0 0.33789 0.33789h0.81055a0.33661 0.33661 0 0 0 0.30273-0.18946l0.71875-1.4746 1.3867 3.543a0.33661 0.33661 0 0 0 0.61133 0.0332l0.99804-1.9121h0.64649a0.33657 0.33657 0 0 0 0.33594-0.33789 0.33657 0.33657 0 0 0-0.33594-0.33594h-0.84961a0.33661 0.33661 0 0 0-0.29883 0.18164l-0.75 1.4375-1.3984-3.5703a0.33661 0.33661 0 0 0-0.29882-0.21485z"
color="#000000"
fill="#9b9ea1"
style="paint-order:stroke fill markers"
id="path25423" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="4.2333mm"
height="4.2333mm"
version="1.1"
viewBox="0 0 4.2333 4.2333"
id="svg25568"
sodipodi:docname="doc.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs25572" />
<sodipodi:namedview
id="namedview25570"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="51.1254"
inkscape:cx="8.0097173"
inkscape:cy="7.9999374"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg25568" />
<g
transform="translate(-139.71 -124.82)"
id="g25566">
<g
transform="matrix(.49451 0 0 .49451 38.616 -321.26)"
id="g25564">
<g
transform="matrix(.64049 0 0 .64049 75.033 338.39)"
id="g25560">
<g
transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"
id="g25558">
<circle
cx="208.71"
cy="865.03"
r="4.9253"
fill="#292a2e"
style="paint-order:stroke fill markers;fill:#2a2e36;fill-opacity:1.0"
id="circle25556" />
</g>
</g>
<path
d="m208.16 907.55-0.20732 0.20589c-0.17972 0.17813-0.47201 0.17829-0.65163 0-0.0864-0.0857-0.13378-0.19948-0.13378-0.32046 0-0.12097 0.0474-0.23483 0.13373-0.32051l0.76312-0.75713c0.15816-0.15683 0.45567-0.38776 0.67252-0.1727 0.0995 0.0988 0.26023 0.0982 0.35903-2e-3 0.0988-0.0995 0.0982-0.26023-2e-3 -0.35897-0.3686-0.36579-0.91339-0.29819-1.3879 0.17259l-0.76317 0.75718c-0.1831 0.18177-0.28389 0.42351-0.28389 0.68082 0 0.25732 0.10079 0.49901 0.28394 0.68077 0.18843 0.18694 0.43585 0.28036 0.68338 0.28036 0.24758 0 0.4951-0.0934 0.68363-0.28046l0.20748-0.20594c0.0995-0.0987 0.10003-0.25946 8.7e-4 -0.35898-0.0987-0.0994-0.25956-0.1-0.35907-8.8e-4zm2.3127-2.9412c-0.39591-0.39282-0.94946-0.41413-1.316-0.0505l-0.25844 0.25654c-0.0995 0.0988-0.10012 0.25946-2e-3 0.35897 0.0989 0.0995 0.25957 0.10008 0.35909 2e-3l0.25833-0.25638c0.18985-0.18853 0.43846-0.11038 0.60061 0.0505 0.0865 0.0857 0.13393 0.19954 0.13393 0.32051 0 0.12103-0.0475 0.23488-0.13383 0.32056l-0.81418 0.80769c-0.37229 0.36937-0.54694 0.196-0.62145 0.12205-0.0995-0.0988-0.26019-0.0982-0.35904 2e-3 -0.0988 0.0995-0.0982 0.26023 2e-3 0.35897 0.17091 0.16963 0.36599 0.25372 0.5705 0.25372 0.2504 0 0.51488-0.12609 0.76636-0.37576l0.81418-0.80769c0.18305-0.18177 0.28394-0.42355 0.28394-0.68087 0-0.25721-0.10089-0.499-0.28404-0.68087z"
fill="#9b9ea1"
id="path25562" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="4.2333mm"
height="4.2333mm"
version="1.1"
viewBox="0 0 4.2333 4.2333"
id="svg25838"
sodipodi:docname="info.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs25842" />
<sodipodi:namedview
id="namedview25840"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="51.1254"
inkscape:cx="8.0097173"
inkscape:cy="7.9999374"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg25838" />
<g
transform="translate(-139.71 -124.82)"
id="g25836">
<g
transform="matrix(.49451 0 0 .49451 38.616 -321.26)"
id="g25834">
<g
transform="matrix(.64049 0 0 .64049 75.033 338.39)"
id="g25830">
<g
transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"
id="g25828">
<circle
cx="208.71"
cy="865.03"
r="4.9253"
fill="#292a2e"
style="paint-order:stroke fill markers;fill:#2a2e36;fill-opacity:1.0"
id="circle25826" />
</g>
</g>
<path
d="m210.17 904.55h-2.9336c-0.40336 0-0.73339 0.33003-0.73339 0.7334v1.8335c0 0.40337 0.33003 0.73339 0.73339 0.73339v0.55005c0 0.16502 0.20169 0.23836 0.3117 0.12835l0.67839-0.6784h1.9435c0.40336 0 0.73339-0.33002 0.73339-0.73339v-1.8335c0-0.40337-0.33003-0.7334-0.73339-0.7334zm-1.1001 2.2002h-1.1001c-0.11001 0-0.18336-0.0733-0.18336-0.18335 0-0.11001 0.0734-0.18334 0.18336-0.18334h1.1001c0.11001 0 0.18335 0.0733 0.18335 0.18334 0 0.11001-0.0733 0.18335-0.18335 0.18335zm0.36669-0.7334h-1.4668c-0.11001 0-0.18336-0.0733-0.18336-0.18334 0-0.11001 0.0734-0.18336 0.18336-0.18336h1.4668c0.11001 0 0.18336 0.0734 0.18336 0.18336 0 0.11001-0.0734 0.18334-0.18336 0.18334z"
fill="#9b9ea1"
id="path25832" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24.399mm" height="7.6165mm" version="1.1" viewBox="0 0 24.399 7.6165" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-115.97 -188.99)">
<path d="m122.35 188.99c0 4.3726 11.131 3.333 11.131 7.6165" fill="none" stroke="#44444a" stroke-width=".8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 326 B

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24.399mm" height="7.6165mm" version="1.1" viewBox="0 0 24.399 7.6165" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-115.97 -188.99)">
<path d="m133.54 188.99c0 4.3726-10.693 3.333-10.693 7.6165" fill="none" stroke="#44444a" stroke-width=".8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 326 B

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1mm" height="4.2333mm" version="1.1" viewBox="0 0 1 4.2333" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-124.76,-160.47)">
<path d="m125.26 160.47v4.2333" fill="none" stroke="#44444a"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 269 B

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="4.233336mm"
height="4.233336mm"
viewBox="0 0 4.233336 4.233336"
version="1.1"
id="svg2475"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="list-icon.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview2477"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="23.318967"
inkscape:cx="5.2103509"
inkscape:cy="9.5630308"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="36"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2472" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-152.75743,-23.863736)">
<circle
style="fill:none;fill-opacity:1;stroke:#202228;stroke-width:0.639316;stroke-linejoin:round;stroke-opacity:1;paint-order:fill markers stroke"
id="path1850"
cx="154.40691"
cy="26.429564"
r="0.92050833" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="4.2333mm" height="4.2333mm" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a" x1="122.52" x2="130.49" y1="39.199" y2="31.302" gradientTransform="translate(.09658)" gradientUnits="userSpaceOnUse">
<stop stop-color="#745cfc" offset="0"/>
<stop stop-color="#564ba4" offset="1"/>
</linearGradient>
</defs>
<g transform="matrix(.10155 0 0 .10155 -90.232 -10.197)">
<g transform="matrix(0 2.2967 -2.2967 0 990.37 -169.5)" fill="url(#a)">
<path d="m122.72 31.302 2.3682 3.7201a0.21308 0.21308 89.753 0 1 9.8e-4 0.2273l-2.4668 3.9498h1.5785a0.73492 0.73492 149.45 0 0 0.64338-0.37973l1.4732-2.6685a0.27716 0.27716 104.45 0 0 0.0345-0.13395v-1.7817a0.28946 0.28946 76.14 0 0-0.0332-0.13465l-1.2688-2.4147a0.71792 0.71792 31.14 0 0-0.63551-0.38397z"/>
<path d="m130.48 39.199-2.4404-3.8335 2.538-4.0637h-2.0122l-1.7173 3.1108v1.9246l1.5038 2.8618z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="4.2333mm"
height="4.2333mm"
version="1.1"
viewBox="0 0 4.2333 4.2333"
id="svg26102"
sodipodi:docname="project.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs26106" />
<sodipodi:namedview
id="namedview26104"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="51.1254"
inkscape:cx="8.0097173"
inkscape:cy="7.9999374"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg26102" />
<g
transform="translate(-55.204 -97.322)"
id="g26100">
<g
transform="matrix(.42975 0 0 .42975 -32.37 -272.31)"
id="g26098">
<circle
cx="208.71"
cy="865.03"
r="4.9253"
fill="#2c55cf"
style="paint-order:stroke fill markers;fill:#5f50bf;fill-opacity:1"
id="circle26094" />
<path
d="m209.6 862.95c0-0.0945-0.0733-0.29631-0.29592-0.29631-0.12969 0-0.24927 0.0842-0.28656 0.21504l-1.1874 4.156c-8e-3 0.0272-0.0115 0.0547-0.0115 0.0816-8e-3 0.0957 0.0745 0.29704 0.29715 0.29704 0.12913 0 0.24797-0.0849 0.28526-0.21541l1.1874-4.156c8e-3 -0.0276 0.0116-0.055 0.0116-0.082zm-2.2264 1.0312c0-0.16958-0.13869-0.29686-0.29686-0.29686-0.076 0-0.15195 0.029-0.20984 0.087l-1.039 1.039c-0.058 0.0661-0.087 0.14216-0.087 0.20987 0 0.0677 0.029 0.16049 0.087 0.21801l1.039 1.039c0.0577 0.0584 0.13382 0.0789 0.20988 0.0789 0.15817 0 0.29686-0.12737 0.29686-0.29686 0-0.076-0.029-0.15195-0.087-0.20993l-0.82911-0.82907 0.82916-0.82915c0.0582-0.0503 0.0869-0.12542 0.0869-0.20984zm4.3044 1.039c0-0.076-0.029-0.15195-0.087-0.20993l-1.039-1.039c-0.0577-0.0498-0.13382-0.0869-0.20989-0.0869-0.15816 0-0.29685 0.12737-0.29685 0.29686 0 0.076 0.029 0.15195 0.087 0.20993l0.82915 0.82916-0.82915 0.82916c-0.0582 0.066-0.087 0.14202-0.087 0.20974 0 0.16958 0.13869 0.29686 0.29685 0.29686 0.076 0 0.15196-0.029 0.20985-0.087l1.039-1.039c0.0583-0.0494 0.087-0.12546 0.087-0.20988z"
fill="#fff"
id="path26096" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="4.2333mm"
height="4.2333mm"
version="1.1"
viewBox="0 0 4.2333 4.2333"
id="svg25971"
sodipodi:docname="skills.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs25975" />
<sodipodi:namedview
id="namedview25973"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="51.1254"
inkscape:cx="8.0097173"
inkscape:cy="7.9999374"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg25971" />
<g
transform="translate(-139.71 -124.82)"
id="g25969">
<g
transform="matrix(.31672 0 0 .31672 75.72 -153.92)"
id="g25967">
<g
transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"
id="g25963">
<circle
cx="208.71"
cy="865.03"
r="4.9253"
fill="#292a2e"
style="paint-order:stroke fill markers;fill:#2a2e36;fill-opacity:1.0"
id="circle25961" />
</g>
<path
d="m211.76 887.29-0.13917-0.0804c-0.16563-0.0955-0.26458-0.26696-0.26458-0.45826 0-0.19129 0.099-0.36274 0.26458-0.45826l0.13917-0.0804c0.37994-0.2196 0.50985-0.70432 0.29051-1.0843l-0.26458-0.45826c-0.21881-0.37915-0.70538-0.50932-1.0843-0.29051l-0.13917 0.0802c-0.16563 0.0958-0.36381 0.0958-0.52917 0-0.16563-0.0958-0.26458-0.26697-0.26458-0.45826v-0.1606c0-0.43762-0.35613-0.79375-0.79375-0.79375h-0.52917c-0.43762 0-0.79375 0.35613-0.79375 0.79375v0.16086c0 0.1913-0.099 0.36248-0.26458 0.45826-0.16563 0.0955-0.36354 0.0958-0.52917 0l-0.13917-0.0804c-0.37888-0.21881-0.86545-0.0886-1.0845 0.29051l-0.26458 0.45826c-0.21934 0.37994-0.0894 0.86492 0.29051 1.0843l0.13944 0.0804c0.16563 0.0955 0.26458 0.26697 0.26458 0.45826 0 0.1913-0.099 0.36275-0.26458 0.45826l-0.13917 0.0804c-0.37995 0.21934-0.50986 0.70433-0.29052 1.0843l0.26459 0.45826c0.21907 0.37914 0.70564 0.50932 1.0843 0.29051l0.13917-0.0802c0.16563-0.096 0.36354-0.0955 0.52917 0 0.16563 0.0958 0.26458 0.26696 0.26458 0.45826v0.1606c0 0.43762 0.35613 0.79375 0.79375 0.79375h0.52917c0.43762 0 0.79375-0.35613 0.79375-0.79375v-0.16087c0-0.19129 0.099-0.36248 0.26458-0.45826 0.16536-0.0955 0.36354-0.0958 0.52917 0l0.13917 0.0804c0.37888 0.21854 0.86545 0.0884 1.0843-0.29051l0.26458-0.45826c0.21934-0.37994 0.0894-0.86493-0.29051-1.0843zm-3.0496 0.78423c-0.72945 0-1.3229-0.59346-1.3229-1.3229 0-0.72945 0.59346-1.3229 1.3229-1.3229 0.72946 0 1.3229 0.59346 1.3229 1.3229 0 0.72946-0.59346 1.3229-1.3229 1.3229z"
fill="#9b9ea1"
id="path25965" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="4.2333mm"
height="4.2333mm"
version="1.1"
viewBox="0 0 4.2333 4.2333"
id="svg25703"
sodipodi:docname="src.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs25707" />
<sodipodi:namedview
id="namedview25705"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="51.1254"
inkscape:cx="8.0097173"
inkscape:cy="7.9999374"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg25703" />
<g
transform="translate(-139.71 -124.82)"
id="g25701">
<g
transform="matrix(.49451 0 0 .49451 38.616 -321.26)"
id="g25699">
<g
transform="matrix(.64049 0 0 .64049 75.033 338.39)"
id="g25695">
<g
transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"
id="g25693">
<circle
cx="208.71"
cy="865.03"
r="4.9253"
fill="#292a2e"
style="paint-order:stroke fill markers;fill:#2a2e36;fill-opacity:1.0"
id="circle25691" />
</g>
</g>
<path
d="m210.25 905.42a0.61713 0.61713 0 1 0-0.92831 0.53266c-0.0221 0.28041-0.19362 0.35678-0.7232 0.4669-0.17126 0.0355-0.34646 0.0718-0.50827 0.13432v-1.2169a0.61713 0.61713 0 1 0-0.61713 0v2.0174a0.61713 0.61713 0 1 0 0.62118 2e-3c0.023-0.17357 0.15429-0.23461 0.62929-0.33287 0.2645-0.0547 0.53787-0.11128 0.76929-0.25987 0.27964-0.17916 0.42939-0.45109 0.44704-0.80892a0.61713 0.61713 0 0 0 0.31011-0.53508zm-2.4685-0.9257a0.30857 0.30857 0 1 1-0.30856 0.30857 0.30857 0.30857 0 0 1 0.30856-0.30857zm0 3.7028a0.30857 0.30857 0 1 1 0.30857-0.30856 0.30857 0.30857 0 0 1-0.30857 0.30856zm1.8514-2.4685a0.30857 0.30857 0 1 1 0.30857-0.30857 0.30857 0.30857 0 0 1-0.30857 0.30857z"
fill="#9b9ea1"
id="path25697" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

72
src/assets/wave.1.svg Normal file
View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="420mm"
height="20.822mm"
version="1.1"
viewBox="0 0 420 20.822"
id="svg4984"
sodipodi:docname="wave.invert.1.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview4986"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.79815972"
inkscape:cx="793.70079"
inkscape:cy="38.839344"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg4984" />
<defs
id="defs4978">
<linearGradient
id="a"
x1="787.57"
x2="787.57"
y1="4.8222"
y2="78.695"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#fff"
stop-opacity=".97647"
offset="0"
id="stop4971"
style="stop-color:#745cfc;stop-opacity:0.98000002;" />
<stop
stop-color="#fff"
stop-opacity=".89804"
offset="0.59202999"
id="stop4973"
style="stop-color:#745cfc;stop-opacity:0.89999998;" />
<stop
stop-color="#fff"
offset="1"
id="stop4975"
style="stop-color:#745cfc;stop-opacity:1;" />
</linearGradient>
</defs>
<g
transform="translate(-.83328 -158.95)"
id="g4982">
<path
transform="matrix(.26458 0 0 .26458 .83328 158.95)"
d="m606.63 54.502c-83.352 2.1157-145.7-25.555-224.09-24.496s-178.77 28.151-242.71 33.189c-63.941 5.0388-69.397 2.5932-139.83 2.9902v12.51h1587.4v-12.51l-43.672-0.2875c-118.61-0.78082-508.71-61.141-665.74-55.086-157.03 6.0549-188 41.574-271.35 43.69z"
fill="url(#a)"
stroke-width=".56894"
id="path4980" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

73
src/assets/wave.2.svg Normal file
View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="420mm"
height="20.822mm"
version="1.1"
viewBox="0 0 420 20.822"
id="svg7400"
sodipodi:docname="wave.invert.2.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7402"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.86745536"
inkscape:cx="659.97633"
inkscape:cy="39.195101"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg7400" />
<defs
id="defs7394">
<linearGradient
id="a"
x1="775"
x2="775"
y1="78.695"
y2="-.00018898"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#fff"
stop-opacity=".25098"
offset="0"
id="stop7387"
style="stop-color:#745cfc;stop-opacity:0.25;" />
<stop
stop-color="#fff"
stop-opacity=".14902"
offset="0.22879"
id="stop7389"
style="stop-color:#745cfc;stop-opacity:0.15000001;" />
<stop
stop-color="#fff"
stop-opacity=".05098"
offset="1"
id="stop7391"
style="stop-color:#745cfc;stop-opacity:0.05;" />
</linearGradient>
</defs>
<g
transform="translate(-.83328 -158.95)"
id="g7398">
<path
transform="matrix(.26458 0 0 .26458 .83328 158.95)"
d="m606.63 28.502c-75.125 1.5646-148.96-25.511-224.09-24.496-81.65 1.1031-161.31 26.774-242.71 33.189-46.477 3.6626-69.397 2.5932-139.83 2.9902v38.51h1587.4v-38.51c-9.6396 0.39215-29.126 0.28262-43.672-0.2875-85.048-3.3332-169.58-19.372-254.68-18.308-79.795 0.99823-158.54 26.167-238.28 23.133-86.633-3.2952-169.52-43.758-256.21-44.724-63.351-0.70603-124.57 27.183-187.92 28.502z"
fill="url(#a)"
stroke-width=".56894"
id="path7396" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<svg width="420mm" height="20.822mm" version="1.1" viewBox="0 0 420 20.822" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <svg width="420.23mm" height="14.465mm" version="1.1" viewBox="0 0 420.23 14.465" xmlns="http://www.w3.org/2000/svg">
<defs> <defs>
<linearGradient id="a" x1="787.57" x2="787.57" y1="4.8222" y2="78.695" gradientUnits="userSpaceOnUse"> <linearGradient id="a" x1="787.57" x2="787.57" y1="4.8222" y2="78.695" gradientTransform="matrix(.26458 0 0 .11755 .83328 170.52)" gradientUnits="userSpaceOnUse">
<stop stop-color="#1a1a1a" stop-opacity=".97647" offset="0"/> <stop stop-color="#202228" stop-opacity=".98039" offset="0"/>
<stop stop-color="#1a1a1a" stop-opacity=".89804" offset=".59203"/> <stop stop-color="#202228" stop-opacity=".9" offset=".59203"/>
<stop stop-color="#1a1a1a" offset="1"/> <stop stop-color="#202228" offset="1"/>
</linearGradient> </linearGradient>
</defs> </defs>
<g transform="translate(-.83328 -158.95)"> <g transform="matrix(1 0 0 -1 -.83328 179.77)">
<path transform="matrix(.26458 0 0 .26458 .83328 158.95)" d="m606.63 54.502c-83.352 2.1157-145.7-25.555-224.09-24.496s-178.77 28.151-242.71 33.189c-63.941 5.0388-69.397 2.5932-139.83 2.9902v12.51h1587.4v-12.51l-43.672-0.2875c-118.61-0.78082-508.71-61.141-665.74-55.086-157.03 6.0549-188 41.574-271.35 43.69z" fill="url(#a)" stroke-width=".56894"/> <path d="m161.34 176.93c-22.053 0.24871-38.549-3.004-59.29-2.8795-20.74 0.12449-47.299 3.3092-64.216 3.9014-16.918 0.59231-18.361 0.30483-36.996 0.3515v1.4706h419.99v-1.4706l-11.555-0.0338c-31.382-0.0918-134.59-7.1872-176.14-6.4754-41.547 0.71177-49.741 4.8871-71.794 5.1358z" fill="url(#a)" stroke-width=".10034"/>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 921 B

After

Width:  |  Height:  |  Size: 913 B

View File

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<svg width="420mm" height="20.822mm" version="1.1" viewBox="0 0 420 20.822" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <svg width="420.23mm" height="14.465mm" version="1.1" viewBox="0 0 420.23 14.465" xmlns="http://www.w3.org/2000/svg">
<defs> <defs>
<linearGradient id="a" x1="775" x2="775" y1="78.695" y2="-.00018898" gradientUnits="userSpaceOnUse"> <linearGradient id="a" x1="775" x2="775" y1="78.695" y2="-.00018898" gradientUnits="userSpaceOnUse">
<stop stop-color="#1a1a1a" stop-opacity=".25098" offset="0"/> <stop stop-color="#202228" stop-opacity=".25098" offset="0"/>
<stop stop-color="#1a1a1a" stop-opacity=".14902" offset=".22879"/> <stop stop-color="#202228" stop-opacity=".14902" offset=".22879"/>
<stop stop-color="#1a1a1a" stop-opacity=".05098" offset="1"/> <stop stop-color="#202228" stop-opacity=".05098" offset="1"/>
</linearGradient> </linearGradient>
</defs> </defs>
<g transform="translate(-.83328 -158.95)"> <g transform="matrix(1 0 0 -.41891 -.71728 75.423)">
<path transform="matrix(.26458 0 0 .26458 .83328 158.95)" d="m606.63 28.502c-75.125 1.5646-148.96-25.511-224.09-24.496-81.65 1.1031-161.31 26.774-242.71 33.189-46.477 3.6626-69.397 2.5932-139.83 2.9902v38.51h1587.4v-38.51c-9.6396 0.39215-29.126 0.28262-43.672-0.2875-85.048-3.3332-169.58-19.372-254.68-18.308-79.795 0.99823-158.54 26.167-238.28 23.133-86.633-3.2952-169.52-43.758-256.21-44.724-63.351-0.70603-124.57 27.183-187.92 28.502z" fill="url(#a)" stroke-width=".56894"/> <path transform="matrix(.26458 0 0 .26458 .83328 158.95)" d="m606.63 28.502c-75.125 1.5646-148.96-25.511-224.09-24.496-81.65 1.1031-161.31 26.774-242.71 33.189-46.477 3.6626-69.397 2.5932-139.83 2.9902v38.51h1587.4v-38.51c-9.6396 0.39215-29.126 0.28262-43.672-0.2875-85.048-3.3332-169.58-19.372-254.68-18.308-79.795 0.99823-158.54 26.167-238.28 23.133-86.633-3.2952-169.52-43.758-256.21-44.724-63.351-0.70603-124.57 27.183-187.92 28.502z" fill="url(#a)" stroke-width=".56894"/>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,310 +0,0 @@
<template>
<div id='banner'>
<div class='wave'></div>
<div class='wave w2'></div>
<div class='border-card'></div>
<div class='content'>
<h3>Hello, I'm</h3>
<h1 class='name'>
{{ model.firstname.toUpperCase() }}
{{ model.lastname.toUpperCase() }}
</h1>
<h2 class='headline'>{{ model.headline.toUpperCase() }}</h2>
<form class='themes'>
<input type='radio' name='theme' id='bw' checked/>
<label for='bw'>
<div class='spacer'>
<div class='tick'></div>
</div>
</label>
<input type='radio' name='theme' id='gooey'/>
<label for='gooey'>
<div class='spacer'>
<div class='tick'></div>
</div>
</label>
<input type='radio' name='theme' id='flat'/>
<label for='flat'>
<div class='spacer'>
<div class='tick'></div>
</div>
</label>
<input type='radio' name='theme' id='glass'/>
<label for='glass'>
<div class='spacer'>
<div class='tick'></div>
</div>
</label>
</form>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import BannerModel from '../model/banner';
@Component({})
export default class Banner extends Vue {
private model: any = BannerModel;
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
@import '../global.scss';
$banner-size: 100vh;
$wave-height: 6.3rem;
$bg-color: #f8f8f8;
$themes-spacing: 2rem;
// horizontal spacing between theme chips
$theme-chip-spacing: 1rem;
// full size of a theme chip
$theme-chip-size: 1.8rem;
// empty space inside the chip (relative to the chip size)
$theme-chip-void: 1.2rem;
// size of the inner (checked) circle (relative to the chip void)
$theme-chip-inner: .6rem;
// MEDIA QUERIES
// optimize vertical space
$mq-vert: 590px;
#banner {
flex: #{$banner-size * 0.999} 0 0;
display: flex;
position: relative;
top: 0;
left: 0;
width: 100%;
flex-flow: row wrap;
justify-content: flex-start;
align-items: center;
color: #3d3d3d;
font-size: 1rem;
font-family: 'Source Sans Pro';
font-weight: 400;
white-space: nowrap;
background: $bg-color;
}
@keyframes wave-x {
from{ background-position-x: 1px; }
to{ background-position-x: 100vw; }
}
.wave {
display: block;
position: absolute;
top: calc( #{$banner-size} - #{$wave-height} );
left: 0;
width: 100vw;
height: $wave-height;
background-image: url('../assets/wave.invert.1.svg');
background-repeat: repeat-x;
background-position-y: 101%;
background-size: auto 100%;
animation: 18s ease-in-out wave-x infinite alternate;
&.w2{
background-image: url('../assets/wave.invert.2.svg');
animation: 20s ease-in-out wave-x infinite alternate-reverse;
}
}
.border-card {
display: block;
position: absolute;
top: -20vw;
left: 70vw;
width: 60vw;
height: 60vw;
background: #1a1a1a;
border-radius: 3rem;
transform: rotate(35deg);
}
.content {
flex: auto 0 1;
display: flex;
position: relative;
flex-flow: column nowrap;
margin: 0 10rem;
margin-bottom: $wave-height;
h1 {
font-size: 4em;
font-weight: 700;
}
h2 {
font-size: 2.2em;
font-weight: 400;
}
h3 {
font-size: 2em;
font-weight: 400;
}
.name {
display: inline-block;
}
}
.themes {
display: flex;
position: relative;
flex-flow: row wrap;
justify-content: flex-start;
align-items: center;
margin-top: 1em;
input[type=radio]{
display: none;
}
& > label {
display: flex;
position: relative;
width: $theme-chip-size;
height: $theme-chip-size;
justify-content: center;
align-items: center;
margin: 0 #{$theme-chip-spacing};
background: transparent;
border-radius: 50% / 50%;
cursor: pointer;
transition: transform .2s ease-in;
// hide the center to make a ring
.spacer {
display: flex;
position: relative;
width: $theme-chip-void;
height: $theme-chip-void;
justify-content: center;
align-items: center;
background: $bg-color;
border-radius: 50% / 50%;
}
.tick {
opacity: 0;
display: block;
position: relative;
width: $theme-chip-inner;
height: $theme-chip-inner;
background: transparent;
border-radius: 50% / 50%;
transition: opacity .1s ease-in-out;
}
}
label:nth-child(2) { margin-left: 0; }
label:last-child { margin-right: 0; }
input:checked + label .tick {
opacity: 1;
}
input#bw + label {
background: #3d3d3d;
.tick{
background: #3d3d3d;
}
}
input#gooey + label {
background: linear-gradient(#7936dc, #bd8eff);
.tick{
background: linear-gradient(#7936dc, #bd8eff);
}
}
input#flat + label {
background: #3d3d3d;
.tick{
background: #3d3d3d;
}
}
input#glass + label {
background: #3d3d3d;
.tick{
background: #3d3d3d;
}
}
}
// not enough vertical space
@media screen and (max-height: 615px){
#banner {
align-items: flex-start;
.content {
margin-top: 2rem;
}
}
}
// not enough horizontal space
@media screen and (max-width: 740px){
#banner {
font-size: 2vw;
align-items: center;
justify-content: center;
.content {
margin: 0 2rem;
}
}
}
// not enough vertical space
@media screen and (max-height: 615px){
#banner {
font-size: 1vw;
}
}
// border-card overlaps with the text
@media screen and (max-width: 1010px){
.border-card {
left: 80vw;
}
}
</style>

View File

@ -1,87 +0,0 @@
<template>
<div id='experiences'>
<div class='experience' v-for='(experience, si) of experiences' :key='si'>
<h2>{{ experience.title.toUpperCase() }}</h2>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import ExperiencesModel from '../model/experiences';
@Component
export default class Experiences extends Vue {
private experiences = ExperiencesModel;
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
@import '../global';
$timeline-width: .25rem;
$timeline-dot-size: 1rem;
$timeline-spacing: 1rem;
#experiences {
flex: auto 0 1;
display: flex;
position: relative;
margin: 0 5%;
margin-top: 3em;
flex-flow: row wrap;
justify-content: space-between;
align-items: flex-start;
// timeline
&:before {
content: '';
position: absolute;
display: block;
width: $timeline-width;
height: 100%;
border-radius: #{$timeline-width} / #{$timeline-width};
background: #07142d;
}
.experience {
display: inline-block;
position: relative;
width: 100%;
margin-left: $timeline-spacing;
border: 1px solid red;
h2 {
font-size: 1.3em;
font-weight: normal;
&:before {
content: '';
position: absolute;
display: block;
top: calc( 1em - #{$timeline-dot-size});
left: calc( #{-$timeline-spacing} - #{$timeline-dot-size} / 2 + #{$timeline-width} / 5 );
width: $timeline-dot-size;
height: $timeline-dot-size;
border-radius: 50% / 50%;
border: 2px solid white;
background: #07142d;
}
}
}
}
</style>

130
src/components/Footer.vue Normal file
View File

@ -0,0 +1,130 @@
<template>
<footer id='footer'>
<div class='wave' aria-hidden="true"></div>
<div class='wave w2' aria-hidden="true"></div>
<div class="footer-content">
<div class='copyright'>
<p>&copy; {{ currentYear }} xdrm-brackets</p>
<p>Adrien Marquès</p>
</div>
<img class='logo' src='../assets/home/logo.svg' alt='Logo Adrien Marquès' width="64" height="64"/>
<address class='contact'>
<p>xdrm.io</p>
<p><a href="tel:+33669051910">(+33) 06 69 05 19 10</a></p>
<p><a href="mailto:xdrm.dev@gmail.com">xdrm.dev@gmail.com</a></p>
<p>Montauban, 82000</p>
</address>
</div>
</footer>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component({
components: {},
})
export default class Footer extends Vue {
get currentYear() {
return new Date().getFullYear();
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
$width: 100vw;
$wave-height: 3rem;
$logo-size: 4em;
#footer {
display: block;
position: relative;
min-height: $wave-height;
background: lighten(#202228, 15%);
}
a {
color: inherit;
text-decoration: none;
&:hover, &:focus {
text-decoration: underline;
}
&::after {
display: none;
}
}
.footer-content {
display: flex;
position: relative;
margin-top: $wave-height;
min-height: 20vh;
color: #bcc6ce;
flex-flow: row wrap;
justify-content: space-around;
align-items: center;
font-family: 'Fira Code';
}
.copyright, .contact {
font-size: 1.3em;
text-align: center;
p {
margin: 0.5em 0;
}
}
.contact a {
color: inherit;
text-decoration: none;
&:hover, &:focus {
text-decoration: underline;
}
}
.copyright {
user-select: none;
}
img {
width: $logo-size;
height: $logo-size;
}
@keyframes wave-x {
from{ background-position-x: 1px; }
to{ background-position-x: 100vw; }
}
.wave {
display: block;
position: absolute;
top: -1px;
left: 0;
width: 100vw;
height: $wave-height;
background-image: url('../assets/wave.invert.1.svg');
background-repeat: repeat-x;
background-position-y: 0;
background-size: auto 100%;
animation: 23s ease-in-out wave-x infinite alternate;
&.w2{
background-image: url('../assets/wave.invert.2.svg');
animation: 25s ease-in-out wave-x infinite alternate-reverse;
}
z-index: 200;
}
</style>

367
src/components/Home.vue Normal file
View File

@ -0,0 +1,367 @@
<template>
<main id='home'>
<LangPicker/>
<img class='logo' src='../assets/home/logo.svg' alt="Logo Adrien Marquès" width="128" height="128"/>
<header class='name'>
<h1>Adrien<br>Marquès</h1>
<p class="job-title">{{ $t('home.title') }}</p>
</header>
<div class='separator' aria-hidden="true"></div>
<section class='readme' ref='test'>
<h2 class='header'>README.md</h2>
<div class="readme-content">
<p ref='text1'>{{ $t('home.line1') }}</p>
<p ref='text2'>{{ $t('home.line2-1') }} <span class="highlight">{{ $t('home.line2-2') }}</span> {{$t('home.line2-3') }}</p>
<p ref='text3'>{{ $t('home.line3') }}</p>
<p ref='text4'>{{ $t('home.line4') }}</p>
<p ref='text5'>{{ $t('home.line5-1') }} <span class="highlight">Go</span> {{ $t('home.line5-2') }} <span class="highlight">IoT</span> {{ $t('home.line5-3') }}</p>
</div>
</section>
<button class='scroll-arrow' @click='scrollNext()' aria-label="Défiler vers la section suivante">
<img src='@/assets/scroll_arrow.svg' alt="" aria-hidden="true" width="40" height="40"/>
</button>
<div class='wave' aria-hidden="true"></div>
<div class='wave w2' aria-hidden="true"></div>
</main>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { go } from '@/service/scroller';
import { TypeWriter } from '@/service/typewriter';
import LangPicker from './LangPicker.vue';
@Component({
components: { LangPicker },
})
export default class Home extends Vue {
protected scrollNext() {
go('skill-picker', 0);
}
protected mounted() {
let last = 0;
// check if already animated in the last 10 min
const typed = localStorage.getItem('typed');
const parsed = parseInt(typed!) as number;
if ( !isNaN(parsed) ) {
last = parsed;
}
const now = new Date().getTime();
const diff_sec = (now - last) / 1e3;
const threshold = 10 * 60;
// skip animation if already done less than 10min ago
if ( diff_sec < threshold ) {
(this.$refs.text1 as HTMLElement).style.display = 'block';
(this.$refs.text2 as HTMLElement).style.display = 'block';
(this.$refs.text3 as HTMLElement).style.display = 'block';
(this.$refs.text4 as HTMLElement).style.display = 'block';
(this.$refs.text5 as HTMLElement).style.display = 'block';
return;
}
const t1 = new TypeWriter( this.$refs.text1 as HTMLElement );
const t2 = new TypeWriter( this.$refs.text2 as HTMLElement );
const t3 = new TypeWriter( this.$refs.text3 as HTMLElement );
const t4 = new TypeWriter( this.$refs.text4 as HTMLElement );
const t5 = new TypeWriter( this.$refs.text5 as HTMLElement );
// wait for i18n to change text
setTimeout(() => {
t1.init(false);
t2.init();
t3.init();
t4.init();
t5.init();
// wait 1s, anim 1.5s
setTimeout( () => t1.animate(1500), 1000 );
// wait 3s, anim 2s
setTimeout( () => t2.animate(2000), 3000 );
// wait 6s, anim 4s
setTimeout( () => t3.animate(4000), 6000 );
// wait 10s, anim 3s
setTimeout( () => t4.animate(3000), 10000 );
// wait 13s, anim 3s
setTimeout( () => t5.animate(3000), 13000 );
// local storage: store that we animated once
setTimeout( () => {
localStorage.setItem('typed', `${new Date().getTime()}`);
}, 16000 );
}, 100);
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
$width: 100vw;
$height: 100vh;
$logo-size: 8rem;
$scroll-btn-top: 10rem;
$scroll-btn-size: 5rem;
$wave-height: 6.3rem;
$primary-color: #6553d0;
#home {
display: grid;
position: relative;
top: 0;
left: 0;
width: $width;
min-height: $height;
background: #202228;
grid-template-columns: 1fr #{$logo-size} 1fr;
grid-template-rows: min-content $logo-size auto $scroll-btn-top;
align-items: center;
}
.lang-picker {
grid-column: 3;
grid-row: 1;
justify-self: end;
display: block;
position: relative;
top: 0;
right: 0;
margin-top: .5em;
margin-right: .5em;
font-size: 1.2em;
}
.logo {
grid-column: 2;
grid-row: 2;
height: $logo-size;
}
.name {
grid-column: 1;
grid-row: 3;
display: block;
position: relative;
margin-top: 3em;
margin-right: 5em;
margin-left: auto;
margin-bottom: auto;
color: #fff;
text-align: right;
h1 { font-size: 7em; font-weight: 800; line-height: 1em; }
p { font-size: 2.5em; font-weight: 400; white-space: nowrap; }
}
.separator {
grid-column: 2;
grid-row: 3;
width: $logo-size;
height: 100%;
background: url('../assets/home/line.svg');
background-repeat: no-repeat;
background-position: 0 0;
background-size: 100% auto;
}
.readme {
grid-column: 3;
grid-row: 3;
display: block;
position: relative;
width: auto;
max-width: 80rem;
height: auto;
min-height: 6rem;
margin: 0 5em;
margin-left: calc( 5em - #{$logo-size}/2 );
margin-top: 3em;
margin-bottom: auto;
font-size: 1.5em;
font-family: 'Fira Code';
font-weight: 400;
background: #202228;
border: .15rem solid #323841;
border-radius: .4rem / .4rem;
.header {
font-size: 1em;
text-align: center;
padding: .3em;
color: #b5bac4ff;
background: #2a2e36;
border-bottom: .15rem solid #323841;
}
h2 {
font-size: 1em;
// ... autres styles existants ...
}
.readme-content {
p {
// typewriter animation changes it
display: none;
color: #fff;
margin: 1em;
line-height: 1.5em;
// offset to the right and inverse indent the first line for the
// ::before (> arrow) to always be on the left
margin-left: calc( 1em + 1.2em );
text-indent: -1.2em;
.highlight {
display: inline;
position: relative;
text-decoration: none;
&::after {
content: '';
display: inline-block;
position: absolute;
top: 60%;
left: -.2em;
width: calc( 100% + 2*.2em );
height: 30%;
background: darken($primary-color, 10%);
z-index: -1;
}
z-index: 1;
}
&::before {
content: '> ';
color: $primary-color;
font-weight: 700;
}
}
}
}
@keyframes wave-x {
from{ background-position-x: 1px; }
to{ background-position-x: 100vw; }
}
@keyframes fadeLoop {
from { opacity: 1.0; transform: translateY(-2%); }
to { opacity: 0.8; transform: translateY(2%); }
}
.wave {
display: block;
position: absolute;
top: calc( 100% * 1.005 - #{$wave-height} );
left: 0;
width: 100vw;
height: $wave-height;
background-image: url('../assets/wave.1.svg');
background-repeat: repeat-x;
background-position-y: 101%;
background-size: auto 100%;
animation: 18s ease-in-out wave-x infinite alternate;
&.w2{
background-image: url('../assets/wave.2.svg');
animation: 20s ease-in-out wave-x infinite alternate-reverse;
}
z-index: 200;
}
.scroll-arrow {
grid-column: 2;
grid-row: 4;
align-self: start;
display: flex;
position: relative;
width: $scroll-btn-size;
height: $scroll-btn-size;
margin: 0 auto;
opacity: .9;
background: #fff;
border-radius: 50% / 50%;
z-index: 1000;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
transition: opacity .2s ease-in-out;
cursor: pointer;
img {
width: #{.8*$scroll-btn-size};
height: #{.8*$scroll-btn-size};
animation: 1s ease-in-out fadeLoop infinite alternate-reverse;
}
&:hover {
opacity: 1;
}
}
@media (max-width: 1600px) {
.readme {
align-self: center;
margin-bottom: 0;
font-size: 1vw;
}
}
@media (max-width: 1200px) {
.name {
font-size: 1.2vw;
align-self: center;
margin-bottom: 0;
}
}
</style>

View File

@ -0,0 +1,117 @@
<template>
<div class='lang-picker' role="region" aria-label="fr-FR: Sélection de la langue, en-US: Language selection">
<button @click='setLanguage(locales[0])' :aria-pressed='current === locales[0]'>
<img src='../assets/lang/en-US.svg' alt="English"/>
<span class="visually-hidden">English</span>
</button>
<button @click='setLanguage(locales[1])' :aria-pressed='current === locales[1]'>
<img src='../assets/lang/fr-FR.svg' alt="Français"/>
<span class="visually-hidden">Français</span>
</button>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { Locales } from '@/locales';
@Component({})
export default class LangPicker extends Vue {
private locales: Locales[] = [Locales.EN, Locales.FR];
get current(): string {
return this.$i18n.locale;
}
private setLanguage(lang: string) {
if (this.current !== lang) {
localStorage.setItem('lang', lang);
this.$i18n.locale = lang;
this.$nextTick(() => {
this.announceLanguageChange(lang);
});
}
}
private announceLanguageChange(lang: string) {
const announcement = document.createElement('div');
announcement.setAttribute('aria-live', 'polite');
if (lang === Locales.EN) {
announcement.textContent = `Language changed to ${lang === Locales.EN ? 'English' : 'French'}`;
} else {
announcement.textContent = `La langue a été changée en ${lang === Locales.EN ? 'anglais' : 'français'}`;
}
document.body.appendChild(announcement);
setTimeout(() => {
document.body.removeChild(announcement);
}, 1000);
}
private mounted() {
const ls = localStorage.getItem('lang') as Locales|null;
if (ls != null && this.locales.indexOf(ls) >= 0) {
this.setLanguage(ls);
} else if (navigator.language.indexOf('fr') > -1) {
this.setLanguage(Locales.FR);
} else {
this.setLanguage(Locales.EN);
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
div.lang-picker {
display: flex;
position: relative;
flex-flow: row nowrap;
button {
flex: 2em;
display: block;
padding: .25em;
cursor: pointer;
background: none;
transform-style: preserve-3d;
transition: filter .1s ease-in-out;
;
filter: grayscale(70%);
&[data-active=true]{
filter: grayscale(0);
}
&:not([data-active=true]):hover {
filter: grayscale(40%);
}
&:focus {
filter: grayscale(0);
}
}
img {
width: 2em;
}
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>

View File

@ -0,0 +1,83 @@
<template>
<div class='level' :aria-label="$t('skills.level-display', { level: level })">
<div ref='l1' class='l1' :aria-hidden="true"/>
<div ref='l2' class='l2' :aria-hidden="true"/>
<div ref='l3' class='l3' :aria-hidden="true"/>
<div ref='l4' class='l4' :aria-hidden="true"/>
<div ref='l5' class='l5' :aria-hidden="true"/>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
@Component({})
export default class LevelDisplay extends Vue {
// id of the skill to display (number representation)
@Prop(Number) private level: number|undefined;
protected mounted(){
this.update(this.level);
}
@Watch('level')
protected onLevelChanged(val: number|undefined, oldVal: number|undefined) {
this.update(val);
}
private update(level: number|undefined) {
if( level == undefined ){
return;
}
const els : HTMLElement[] = [
this.$refs.l1 as HTMLElement,
this.$refs.l2 as HTMLElement,
this.$refs.l3 as HTMLElement,
this.$refs.l4 as HTMLElement,
this.$refs.l5 as HTMLElement,
];
for( let i = 0 ; i < els.length ; i++ ){
if( els[i] == undefined ){
continue;
}
els[i].classList.remove("active");
if( i < level ){
els[i].classList.add("active");
}
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.level {
display: flex;
flex-flow: row nowrap;
div {
display: block;
position: relative;
width: 1.2em;
height: 1.2em;
margin: 0 .2em;
border-radius: 50%;
border: .2rem solid #141619;
background: #141619;
}
.l1.active { background: darken(#6252c6, 8%); }
.l2.active { background: darken(#6252c6, 6%); }
.l3.active { background: darken(#6252c6, 4%); }
.l4.active { background: darken(#6252c6, 2%); }
.l5.active { background: darken(#6252c6, 0%); }
.l1 { margin-left: 0; }
.l5 { margin-right: 0; }
}
</style>

View File

@ -0,0 +1,163 @@
<template>
<div
class='skill-card'
ref='root'
:data-active='active ? "1" : "0"'
@click='onClick()'
:title='name()'
role="button"
:aria-pressed='active'
:tabindex="0"
@keydown.enter='onClick()'
@keydown.space='onClick()'
>
<img class='icon' :src='icon()' :alt="$t('skills.icon-alt', { skill: name() })"/>
<span class='name'>
<span v-html='name()'></span>
</span>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { skillLabel, tID } from '@/model/skills';
import * as skills from '@/service/skills';
@Component({})
export default class SkillCard extends Vue {
// id of the skill to display (string representation)
@Prop(Number) public readonly id: tID|undefined;
// whether it has the active style
@Prop(Boolean) public active: boolean|undefined;
// folder name to display instead of a skill to fetch
// not empty -> used as displayed folder name
// empty -> this.id is used to fetch the skill
@Prop(String) public folder: string|undefined;
protected mounted() {}
protected icon(): string {
const unknown = () => require('../assets/skills/unknown.svg');
if ( this.folder != undefined ) {
return require('../assets/skills/folder.svg');
}
if ( this.id == undefined ) {
return unknown();
}
const skill = skills.get(this.id);
if ( skill == null ) {
return unknown();
}
if ( skill.icon == null ) {
return unknown();
}
return require(`../assets/${skill.icon}`);
}
protected name(): string {
if ( this.folder != undefined ) {
return this.folder;
}
if ( this.id == undefined ) {
return '?';
}
const skill = skills.get(this.id);
if ( skill == null ) {
return '';
}
return this.$t(skillLabel(this.id)).toString();
}
private onClick(e: MouseEvent) {
this.$emit('pick', !this.active);
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
$height: 2em;
$active-border: .2em;
.skill-card {
display: inline-flex;
position: relative;
width: auto;
height: $height;
font-size: 1.4rem;
background: #2a2e36;
border-radius: .3rem / .3rem;
transition: background .1s ease-in-out;
flex-flow: row nowrap;
align-items: center;
user-select: none;
cursor: pointer;
.icon {
display: block;
position: relative;
height: $height;
width: $height;
background: #323945;
border-radius: .3rem / .3rem;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
transition: background .1s ease-in-out;
}
.name {
display: flex;
position: relative;
height: $height;
color: #fff;
font-weight: 400;
white-space: nowrap;
padding: 0 .6em;
span {
text-overflow: ellipsis;
overflow: hidden;
}
overflow: hidden;
flex-flow: row nowrap;
align-items: center;
border-left: $active-border solid transparent;
transition: border-color .1s ease-in-out;
}
&:hover{
background: #383d45;
.icon {
background: #3c4150;
}
}
&[data-active="1"]{
background: #444952;
.icon {
background: #3c4150;
}
.name {
border-color: #41b883;
}
}
}
</style>

View File

@ -0,0 +1,514 @@
<template>
<section id='skill-picker' aria-labelledby="skill-picker-title">
<h2 id="skill-picker-title" class="visually-hidden">{{ $t('skills.title') }}</h2>
<div class='wrapper'>
<div class='container'>
<nav class='categories' aria-label="Catégories de compétences">
<SkillCard v-for='(t) of tags'
:key='t'
:active='t == tag'
:folder='$t(tagLabel(t))'
@pick='onTag(t, $event)'/>
</nav>
<div class='skills' role="list" aria-label="Liste des compétences">
<SkillCard v-for='(id) of ids'
:key='id'
v-show='filtered.indexOf(id) >= 0'
:id='id'
:active='id == sel'
@pick='onPick(id, $event)'
role="listitem"/>
</div>
<section class='details' v-if='details != null' aria-live="polite">
<header>
<span class='interest'>{{ $t('skills.interest') }}<LevelDisplay :level='details.interest'/></span>
<img :src='details.icon' :alt="$t('skills.icon-alt', { skill: details.title })"/>
<span class='mastery'>{{ $t('skills.mastery') }}<LevelDisplay :level='details.mastery'/></span>
</header>
<h3>{{ details.title }}</h3>
<h3>{{ $t('skills.featured-before') }} <b class="primary-color">{{ details.projects.length }}</b> {{ details.projects.length > 1 ? $t('skills.featured-after-n') : $t('skills.featured-after-1') }}</h3>
<p class="project-links">
<template v-for='(proj, index) of details.projects'>
<a :key='"pick-" + proj.name' :href='"#" + sanitize(proj.name)' @click.prevent='scroll(sanitize(proj.name))'>
{{ proj.name }}
</a><span v-if="index < details.projects.length - 1" :key='"separator-" + index'>, </span>
</template>
</p>
<p v-html='details.text[$i18n.locale]'></p>
</section>
<section class='guide' v-else aria-live="polite">
<p v-html='$t("skills.guide")'></p>
</section>
</div>
<button v-if='this.sel != null' @click='browse()'>{{ $t('skills.browse') }}</button>
<button v-else @click='browse()'>{{ $t('skills.browse-all') }}</button>
</div>
<div class='spacer' aria-hidden="true"></div>
</section>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import SkillCard from './SkillCard.vue';
import LevelDisplay from './LevelDisplay.vue';
import { skillLabel, tagLabel, tID, tTag } from '@/model/skills';
import { Project } from '@/model/projects';
import * as skills from '@/service/skills';
import * as projects from '@/service/projects';
import { go } from '@/service/scroller';
import * as url from '@/service/url';
import { Locales } from '@/locales';
import { Level } from '@/model/level';
interface Details {
icon: string|null;
title: string;
projects: Project[];
text: { [key in Locales]: string };
interest: Level,
mastery: Level,
}
const DEFAULT_TAG = tTag.All;
@Component({
components: {
SkillCard,
LevelDisplay,
},
})
export default class SkillPicker extends Vue {
// list of available skills
public readonly ids: tID[] = skills.available();
// details section when a skill is selected
protected details: Details|null = null;
// currently selected skill
private sel: tID|null = null;
// list of ids to display according to the current tag
private filtered: tID[] = [];
// available categories (tags)
private tags: tTag[] = [DEFAULT_TAG, ...skills.tags()];
// currently selected tag
private tag: tTag = tTag.Language;
// selects or deselects a skill. If the skill is not in the current
// folder, it navigates to the DEFAULT_TAG folder beforehand. Scrolls to
// the selected skill when selected.
public select(id: tID, deselect: boolean) {
const skill = skills.get(id);
if ( deselect || skill == null ) {
this.sel = null;
this.details = null;
this.$emit('pick', null);
return;
}
// not available in current tag filter (folder)
// -> navigate to default tag
if ( skill.tags.indexOf(this.tag) < 0 ) {
this.onTag(DEFAULT_TAG, true);
}
this.sel = id;
this.loadDetails(id);
this.$emit('pick', id);
}
protected sanitize(raw: string): string {
return url.sanitize(raw);
}
// returns the label for a tag
protected tagLabel(t: tTag): string {
return tagLabel(t);
}
protected onPick(id: tID, picked: boolean) {
this.select(id, !picked);
}
protected onTag(t: tTag, picked: boolean) {
if ( picked ) { // select
this.tag = t;
this.filterByTag();
return;
}
}
protected browse() {
go(`search-header`, 0);
}
protected mounted() {
this.filterByTag();
}
protected scroll(proj_name: string) {
const head = document.querySelector(`#search-header`) as HTMLElement;
if ( head == null ) {
return;
}
go(`project-${proj_name}`, -1.2 * head.offsetHeight);
}
// apply filter by tag
private filterByTag() {
if ( this.tag == DEFAULT_TAG ) {
this.filtered = this.ids;
} else {
this.filtered = skills.filtered(this.tag);
}
// maintain selection behavior:
// - deselect if current is no more present
// - keep if still listed
if ( this.sel != null && this.filtered.indexOf(this.sel) < 0 ) {
this.select(0, true);
}
}
// loads details about a skill
private loadDetails(id: tID) {
const skill = skills.get(id);
if ( skill == null ) {
this.details = null;
return;
}
let icon: string|null = null;
if ( skill.icon != null ) {
icon = require(`../assets/${skill.icon}`);
}
this.details = {
icon,
title: this.$t(skillLabel(id)).toString(),
projects: projects.bySkill(id),
text: skill.info,
interest: skill.interest,
mastery: skill.mastery,
};
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
$page-margin: 3rem;
$bottom-spacer: 15vh;
$bottom-margin: 10vh;
$primary-color: #745cfc;
#skill-picker {
display: block;
position: relative;
top: 0;
left: 0;
width: 100vw;
height: #{100vh + $bottom-spacer};
font-size: 1rem;
background: #202228;
.wrapper {
display: block;
position: relative;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: linear-gradient(0, #564ba4, #745cfc);
padding: $page-margin;
.container {
display: flex;
position: relative;
width: calc( 100vw - #{2*$page-margin} );
height: calc( 100vh - #{2*$page-margin} );
background: #202228;
border-radius: 1em / 1em;
padding: 2em;
flex-flow: row nowrap;
}
}
.spacer {
display: block;
position: relative;
width: 100vw;
height: $bottom-margin;
background: linear-gradient(to bottom left, #564ba4 0%, #564ba4 49.5%, transparent 50%, transparent 100%);
}
}
.categories {
flex: 20em 0 0;
display: flex;
height: 100%;
flex-flow: column nowrap;
align-items: flex-start;
justify-content: flex-start;
margin-right: 1em;
overflow: hidden;
overflow-y: visible;
.skill-card {
margin: .3em 0;
width: 13.4em;
}
}
.skills {
flex: 20em 0 0;
display: flex;
height: 100%;
flex-flow: column nowrap;
align-items: flex-start;
justify-content: flex-start;
overflow: hidden;
overflow-y: visible;
.skill-card {
margin: .3em 0;
width: 13.4em;
}
}
.details {
flex: auto;
display: flex;
height: 100%;
margin: auto;
flex-flow: column nowrap;
align-items: center;
justify-content: flex-start;
header {
flex: auto 0 1;
width: calc( 100% - 2*3em );
margin: 0 auto;
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: flex-start;
span {
display: flex;
position: relative;
flex-flow: column nowrap;
}
.interest { align-items: flex-start; }
.mastery { align-items: flex-end; }
img {
display: block;
flex: 8em 0 0;
}
}
h1 {
font-size: 4em;
color: #cacaca;
i { display: none; }
}
h2 {
margin-top: .3em;
font-size: 1.8em;
color: #616c7c;
font-weight: 400;
b { color: #fff; }
}
h3 {
margin: 0 3em;
margin-top: .8em;
font-size: 1.3em;
font-weight: 400;
color: #616c7c;
& > span:last-child {
display: none;
}
}
p {
flex: auto 0 1;
display: block;
position: relative;
margin: 0 2em;
margin-top: 2em;
margin-bottom: 4rem;
font-size: 1.5em;
font-weight: 500;
text-align: justify;
line-height: 1.5;
color: #c1c1c1;
padding: 1em;
background: #323841;
border: .1rem solid #45454b;
border-radius: .4em / .4em;
overflow: hidden;
overflow-y: auto;
}
.project-links {
display: inline;
font-size: 1.3em;
color: #616c7c;
background: none;
border: none;
padding: 1em;
margin: 0 2em;
}
.primary-color {
color: $primary-color;
}
}
.guide {
flex: auto;
display: flex;
height: 100%;
margin: auto;
flex-flow: column nowrap;
align-items: center;
justify-content: center;
p {
flex: auto 0 1;
display: block;
position: relative;
margin: 0 3em;
margin-top: 2em;
margin-bottom: 4rem;
font-size: 1.8em;
font-weight: 500;
text-align: justify;
color: #667;
padding: 1em;
overflow: hidden;
user-select: none;
}
}
input, button {
display: block;
position: absolute;
top: calc( 100% - #{$page-margin} );
left: 50%;
padding: .6em 2em;
color: #202228;
font-size: 1.4rem;
font-weight: 600;
background: #fff;
border: none;
border-radius: 2em;
box-shadow: 0 0 .5em transparent;
transform-style: preserve-3d;
transform: translateX(-50%) translateY(-50%);
cursor: pointer;
transition: box-shadow .2s ease-in-out,
transform .2s ease-in-out,
color .2s ease-in-out;
z-index: 10;
&:hover {
color: #000;
box-shadow: 0 0 .3em #191b1f;
transform: translateX(-50%) translateY(-50%) scale(1.05);
}
}
// width > 1800px : center the container
@media (min-width: 1800px) {
.container {
left: calc( 100vw/2 - 1800px/2 - #{$page-margin} );
max-width: 1800px;
}
}
// screen less than 1400 p
@media (max-width: 1400px) {
#skill-picker {
font-size: 1vw;
.skill-card {
width: 18vw;
}
}
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>

View File

@ -1,183 +0,0 @@
<template>
<div id='skills'>
<div class='skillset' v-for='(skillset, si) of skills' :key='si'>
<div class='title'>
{{ skillset.title.toUpperCase() }}
</div>
<div class='subtitle'>
<span v-for='keyword of skillset.keywords' :key='keyword'>{{ keyword }}</span>
</div>
<div class='stack'>
<template v-for='(skill,i) of skillset.skills'>
<a class='label' :key='i' :href='skill.link' v-html='skill.label'></a>
<span class='level' :key='i'>
<span :style='{ width: (skill.level*100)+"%" }'></span>
</span>
</template>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import SkillsModel from '../model/skills';
@Component
export default class Skills extends Vue {
private skills = SkillsModel;
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
@import '../global';
#skills {
flex: auto 0 1;
display: flex;
position: relative;
margin: 0 5%;
margin-top: 3em;
flex-flow: row wrap;
justify-content: space-between;
align-items: flex-start;
.skillset {
flex: 30%-(2*3%);
display: flex;
position: relative;
margin: 0 3%;
flex-flow: column nowrap;
justify-content: flex-start;
align-items: center;
.title {
flex: auto 0 1;
display: block;
position: relative;
padding: .2em 1em;
margin-bottom: .2em;
font-size: 1.5rem;
font-weight: normal;
border-bottom: 2px solid #ccc;
}
.subtitle {
flex: auto 0 1;
display: flex;
position: relative;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
color: #ccc;
font-size: 1rem;
font-weight: normal;
span{
padding-left: .5em;
&:not(:last-child):after{
content: ' / ';
color: #aaa;
}
}
}
.stack {
flex: auto;
display: flex;
position: relative;
margin: 1rem;
min-width: 15rem;
flex-flow: row wrap;
justify-content: space-between;
align-items: center;
.label{
flex: 60%;
display: inline-block;
position: relative;
padding: .2rem 0;
font-size: 1.1rem;
font-weight: normal;
}
.level{
flex: 40%;
display: block;
position: relative;
padding: .2rem 0;
height: .5rem;
border-radius: .25rem / .25rem;
overflow: hidden;
background-color: #e6e6e6;
@keyframes fillIn {
from { width: 50%; }
}
span {
display: block;
position: absolute;
top: 0;
left: 0;
width: 0;
height: 100%;
background-size: 100% 100%;
animation: 1s ease fillIn;
}
}
}
}
.skillset:nth-of-type(3n+1) .stack .level span{
@include gradient1;
}
// .skillset:nth-of-type(3n+1),
// .skillset:nth-of-type(3n+3) {
// margin-top: 5rem;
// }
.skillset:nth-of-type(3n+2) .stack .level span{
@include gradient2;
}
.skillset:nth-of-type(3n+3) .stack .level span{
@include gradient3;
}
}
</style>
<style lang="scss">
#skills .skillset .stack .label i {
font-style: normal;
opacity: 0.3;
}
</style>

641
src/components/Timeline.vue Normal file
View File

@ -0,0 +1,641 @@
<template>
<div id='timeline'>
<div id='search-header' ref='header'>
<img src='../assets/timeline/logo.svg'/>
<span>
<h6>{{ projects.length }}</h6>
<h3>{{ $t( (skill == null) ? 'timeline.title-all' : 'timeline.title') }}</h3>
<SkillCard v-show='skill != null' :id='skill' :active='true'/>
</span>
<input type='button' :value="$t('timeline.back')" @click='$event.preventDefault(); scrollBack()'/>
</div>
<template class='project' v-for='(proj) of projects'>
<div :key="'spacer-'+proj.name" class='spacer' />
<!-- id is used for navigation -->
<div :key="'start-'+proj.name" class='start' :id='"project-" + sanitize(proj.name)'>
{{ short_date(proj.started_at) }}
</div>
<div :key="'name-icon-'+proj.name" class='name-icon'>
<img src='../assets/timeline/project.svg' />
</div>
<div :key="'name-'+proj.name" class='name'>
{{ $t('project.created') }} <b>{{ proj.name }}</b> <span>{{ elapsed(proj.started_at) }}</span>
</div>
<div :key="'joint-start-'+proj.name" class='joint-start' />
<div :key="'skill-icon-'+proj.name" class='skill-icon'>
<img src='../assets/timeline/skills.svg' />
</div>
<div :key="'skillset-'+proj.name" class='skillset'>
<SkillCard v-for='(id) of proj.skills' :key='"timeline-" + proj.name + "-" + id' :id='id' :active='id == skill' @pick='$emit("pick", id)'/>
</div>
<div :key="'desc-icon-'+proj.name" class='desc-icon'>
<img src='../assets/timeline/info.svg' />
</div>
<div :key="'desc-'+proj.name" class='desc'>
<!-- <div class='user'>{{ $t('project.username') }}</div> -->
<p v-html='proj.info[$i18n.locale]'/>
</div>
<template v-if='proj.source != null'>
<template v-for='(src, i) in proj.source'>
<div :key="'src-icon-'+proj.name+'-'+i" class='src-icon'>
<img src='../assets/timeline/src.svg' />
</div>
<div :key="'src-'+proj.name+'-'+i" class='src'>
{{ $t('project.sources') }} <a :href='src.link'>{{ src.name }}</a> <span>({{ proj.commits || src.commits }} commits)</span>
</div>
</template>
</template>
<template v-if='proj.doc != null'>
<div :key="'doc-icon-'+proj.name" class='doc-icon'>
<img src='../assets/timeline/doc.svg' />
</div>
<div :key="'doc-'+proj.name" class='doc'>
{{ $t('project.doc') }} <a :href='proj.doc.link'>{{ proj.doc.name }}</a>
</div>
</template>
<div :key="'end-icon-'+proj.name" class='end-icon'>
<img src='../assets/timeline/activity.svg' />
</div>
<div :key="'end-'+proj.name" class='end'>
<template v-if='proj.stopped_at != null'>
{{ $t('project.end') }} {{ short_date(proj.stopped_at) }} <span>{{ duration(proj.started_at, proj.stopped_at, true) }}</span>
</template>
<template v-else>
{{ $t('project.still') }} <span>{{ duration(proj.started_at, new Date(), false) }}</span>
</template>
</div>
<div :key="'joint-stop-'+proj.name" class='joint-stop' />
</template>
<div class='spacer' />
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import SkillCard from './SkillCard.vue';
import { Project } from '../model/projects';
import { tID } from '../model/skills';
import * as projects from '../service/projects';
import * as scroller from '../service/scroller';
import * as url from '../service/url';
interface TimeDiff {
diff: number;
one: string; // singular translation label
plural: string; // plural translation label
}
// returns a TimeDiff from a date. Holds the time elapsed until @stop in the most
// broad unit that is greater than 0.
function getTimeDiff(start: Date, stop: Date): TimeDiff {
const minute = 60 * 1000;
const hour = 60 * minute;
const day = 24 * hour;
const month = 30 * day;
const year = 365 * day;
const diff = stop.getTime() - start.getTime();
if ( diff < 0 ) {
return {diff: 0, one: 'time.some', plural: 'time.some'};
}
if ( diff < minute ) {
return {diff, one: 'time.second', plural: 'time.seconds'};
}
if ( diff < hour ) {
return {diff: diff / minute, one: 'time.minute', plural: 'time.minutes'};
}
if ( diff < day ) {
return {diff: diff / hour, one: 'time.hour', plural: 'time.hours'};
}
if ( diff < month ) {
return {diff: diff / day, one: 'time.day', plural: 'time.days'};
}
if ( diff < year ) {
return {diff: diff / month, one: 'time.month', plural: 'time.months'};
}
return {diff: diff / year, one: 'time.year', plural: 'time.years'};
}
@Component({
components: {
SkillCard,
},
})
export default class Timeline extends Vue {
private skill: tID|null = null;
private projects: Project[] = [];
public filter(skill: tID|null) {
this.skill = skill;
if ( skill == null ) {
this.projects = projects.all().sort(this.sort_projects);
return;
}
this.projects = projects.bySkill(skill).sort(this.sort_projects);
}
protected short_date(date: Date): string {
return date.toLocaleDateString(this.$i18n.locale, { month: 'short', year: 'numeric' });
}
protected elapsed(date: Date): string {
const fmt = 'time.diff-format';
const td = getTimeDiff(date, new Date());
const diff = Math.floor(td.diff);
if ( diff == 0 ) {
return this.$t(fmt, { elapsed: this.$t(td.one) }).toString();
}
if ( diff > 1 ) { // plural
return this.$t(fmt, { elapsed: this.$t(td.plural, { n: diff }) }).toString();
}
// singular
return this.$t(fmt, { elapsed: this.$t(td.one, { n: diff }) }).toString();
}
protected duration(start: Date, stop: Date, ended: boolean): string {
const fmt = ended ? 'time.dur-format' : 'time.cur-format';
const td = getTimeDiff(start, stop);
const diff = Math.floor(td.diff);
if ( diff == 0 ) {
return this.$t(fmt, { duration: this.$t(td.one) }).toString();
}
if ( diff > 1 ) { // plural
return this.$t(fmt, { duration: this.$t(td.plural, { n: diff }) }).toString();
}
// singular
return this.$t(fmt, { duration: this.$t(td.one, { n: diff }) }).toString();
}
protected scrollBack() {
scroller.go('skill-picker', 0);
}
private sort_projects(a: Project, b: Project): number {
if ( b.stopped_at == null && a.stopped_at == null ) {
return b.started_at.getTime() - a.started_at.getTime();
}
if ( a.stopped_at != null && b.stopped_at == null ) {
return 1;
}
if ( b.stopped_at != null && a.stopped_at == null ) {
return -1;
}
return b.started_at.getTime() - a.started_at.getTime();
}
private mounted() {
document.body.addEventListener('scroll', this.onScroll, { passive: true });
}
private onScroll(e: Event) {
const target = e.target as HTMLElement;
const header = this.$refs.header as HTMLElement;
if ( target == undefined || header == undefined ) {
return;
}
const el = this.$el as HTMLElement;
if ( el == null ) {
return;
}
// when scrolling after this limit, make the header fixed
const limit = el.offsetTop;
if ( target.scrollTop >= limit ) {
header.classList.add('fixed');
} else {
header.classList.remove('fixed');
}
}
protected sanitize(raw: string): string {
return url.sanitize(raw);
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
$icon-size: 2.7rem;
$space-width: 1rem;
$bg-color: #202228;
$header-height: 6rem;
$joint-height: 2rem;
$logo-size: 5rem;
#search-header {
display: grid;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: $header-height;
font-size: 1em;
background: #202228;
box-shadow: 0 0 1em transparent;
z-index: 500;
grid-template-columns: $logo-size auto min-content;
align-items: center;
transition: background .5s ease-in-out,
margin .2s ease-in-out,
left .2s ease-in-out,
width .2s ease-in-out,
border-radius .2s ease-in-out,
box-shadow .1s ease-in-out;
overflow: hidden;
img {
width: $logo-size;
height: $logo-size;
opacity: 0;
margin-left: .5rem;
transition: opacity .2s ease-in-out;
}
span {
display: flex;
margin: auto;
flex-flow: row nowrap;
justify-content: space-around;
align-items: center;
h3 {
color: #fff;
font-size: 1.3em;
transition: color .2s ease-in-out;
}
.skill-card {
margin: 0 .5em;
cursor: auto;
&:hover {
background: #444952;
}
}
}
h6 {
display: block;
margin-right: .5em;
padding: .2em .5em;
font-size: 1em;
color: #fff;
background: #444952;
border-radius: .1em / .1em;
}
input {
padding: .4em 1em;
margin-right: 1.5em;
color: #fff;
font-size: 1.4rem;
background: linear-gradient(to top right, #564ba4, #745cfc);
border: none;
border-radius: .5rem / .5rem;
opacity: 0;
transition: opacity .2s ease-in-out;
cursor: pointer;
}
&.fixed {
position: fixed;
left: 1rem;
width: calc( 100% - #{2*1rem} );
opacity: .95;
background: #2b2d37;
// background: linear-gradient(to top right, #564ba4, #745cfc);
border-radius: 0 0 1rem 1rem;
box-shadow: 0 0 1em .5em darken(#202228, 5%);
img {
opacity: 1;
}
h3 {
color: #fff;
}
input {
opacity: 1;
}
}
}
#timeline {
display: grid;
position: relative;
width: 100vw;
// min-height: 100vh;
grid-template-columns: auto $icon-size $icon-size $space-width auto;
grid-gap: 0;
color: #fff;
font-size: 1.6rem;
font-weight: 400;
background: $bg-color;
padding: 0 5rem;
padding-top: $header-height;
// line 1
.spacer { grid-column: 2; };
// line 2
.start { grid-column: 1; }
.name-icon { grid-column: 2; }
.name { grid-column: 3 / 6; }
// line 3 (line junction)
.joint-start { grid-column: 2 / 4; }
// line 4
.skill-icon { grid-column: 3; }
.skillset { grid-column: 5; margin-bottom: 2rem; }
// line 5
.desc-icon { grid-column: 3; }
.desc { grid-column: 5; margin-bottom: 2rem; }
// line 6
.src-icon { grid-column: 3; }
.src { grid-column: 5; margin-bottom: 2rem; }
// line 7
.doc-icon { grid-column: 3; }
.doc { grid-column: 5; margin-bottom: 2rem; }
// line 8
.end-icon { grid-column: 3; }
.end { grid-column: 5; }
// line 9 (junction stop)
.joint-stop { grid-column: 2 / 4; }
.skill-icon, .name-icon, .desc-icon, .src-icon, .doc-icon, .end-icon, .src-icon, .doc-icon, .end-icon {
justify-self: center;
display: flex;
position: relative;
width: $icon-size;
background: url('../assets/timeline/line.svg');
background-repeat: repeat-y;
background-position: center;
background-size: .2rem auto;
flex-flow: row nowrap;
justify-content: center;
align-items: flex-start;
img {
width: #{$icon-size - 2*.1rem};
height: #{$icon-size - 2*.1rem};
padding: .1rem;
background: $bg-color;
}
}
// line 1
.spacer {
height: 5rem;
background: url('../assets/timeline/line.svg');
background-repeat: repeat-y;
background-position: center;
background-size: .2rem auto;
}
// line 2
.start {
align-self: center;
justify-self: end;
font-size: .8em;
color: #434d5d;
margin-right: 1rem;
}
.name {
align-self: center;
margin-left: 1rem;
color: #89919f;
}
// line 3
.joint-start {
height: $joint-height;
background: url('../assets/timeline/joint-start.svg');
background-repeat: no-repeat;
background-position: center;
background-size: auto;
}
.joint-stop {
height: $joint-height;
background: url('../assets/timeline/joint-stop.svg');
background-repeat: no-repeat;
background-position: center;
background-size: auto;
}
.skillset {
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
align-items: flex-start;
width: 50vw;
.skill-card {
margin-right: .4em;
margin-bottom: .4em;
user-select: none;
font-size: .7em;
}
}
.desc {
display: block;
position: relative;
width: 50vw;
font-size: .8em;
letter-spacing: .01rem;
// text-align: justify;
padding: 2em;
margin-bottom: 1.5em;
color: #eee;
background: #323945;
border: .1rem solid #45454b;
border-radius: .8em;
z-index: 100;
transform-style: preserve-3d;
&:before, &:after {
// content: '';
display: block;
position: absolute;
top: .6em;
left: .6em;
width: 100%;
height: 100%;
background: darken(#323841, 4%);
border: .1rem solid darken(#45454b, 4%);
border-radius: .8em;
transform: translateZ(-1px);
}
&:before {
top: 1.2em;
left: 1.2em;
}
.user {
margin-bottom: 1em;
width: 100%;
height: 2em;
background: url('../assets/home/logo.svg');
background-repeat: no-repeat;
background-size: auto 90%;
background-position: 0 center;
text-align: left;
text-indent: 2.5em;
line-height: 2em;
color: #fff;
}
}
.src, .doc, .end {
color: #89919f;
}
.name b {
color: #745cfc;
font-weight: 500;
}
.name span,
.src span,
.end span {
position: relative;
font-size: .8em;
color: #434d5d;
margin-left: .5em;
&:before {
content: '';
display: inline-block;
position: relative;
margin-bottom: .15em;
margin-right: .5em;
width: .3em;
height: .3em;
background: #4e596c;
border-radius: 50% / 50%;
}
}
}
</style>
<style lang="scss">
// for translated styles
.desc {
b {
color: #fff;
}
i {
font-size: .8em;
font-style: normal;
color: #9d9d9d;
}
li {
list-style-type: none;
list-style-image: url('../assets/timeline/list-icon.svg');
margin-left: 1.5em;
}
}
</style>

View File

@ -1,17 +0,0 @@
@mixin gradient1{
background-image: linear-gradient(to right, #7d82ff, #51a8fc);
}
@mixin gradient2{
background-image: linear-gradient(to right, #51bafc, #00dfa3);
}
@mixin gradient3{
background-image: linear-gradient(to right, #8f93ff, #ec7fef);
}
@mixin radial-gradient($innerColor: #555, $outerColor: #333) {
background-color: $outerColor;
background-image: -webkit-gradient(radial, center center, 0, center center, 460, from($innerColor), to($outerColor));
background-image: -webkit-radial-gradient(circle, $innerColor, $outerColor);
background-image: -moz-radial-gradient(circle, $innerColor, $outerColor);
background-image: -o-radial-gradient(circle, $innerColor, $outerColor);
background-repeat: no-repeat;
}

117
src/locales/en.ts Normal file
View File

@ -0,0 +1,117 @@
export default {
'home.title': 'FREELANCE DEVELOPER',
'home.line1': 'Hi, I am another freelance developer !',
'home.line2-1': 'I\'ve created this website so you can',
'home.line2-2': 'browse',
'home.line2-3': 'my skills and projects.',
'home.line3': 'I started dev back in 2010. I now have worked on more than 50 projects.',
'home.line4': 'I work hard to provide my clients with maintainable projects so that they can evolve at low cost (economic and ecological).',
'home.line5-1': 'My top technologies at the moment are',
'home.line5-2': 'and',
'home.line5-3': '(Arduino, Raspberry).',
'timeline.title': 'Timeline of projects featuring',
'timeline.title-all': 'Timeline of all projects',
'timeline.back': 'Change skill',
'skills.featured-before': 'Featured in',
'skills.featured-after-1': 'project',
'skills.featured-after-n': 'projects',
'skills.browse': 'Browse projects',
'skills.browse-all': 'Browse all projects',
'skills.guide': 'You can select a skill to browse related projects. You can browse all projects by not selecting or deselecting the active skill.',
'skills.interest': 'interest level',
'skills.mastery': 'mastery level',
'tag.all': 'All',
'tag.web': 'Web',
'tag.storage': 'Storage',
'tag.ui': 'UI/UX',
'tag.system': 'System',
'tag.mobile': 'Mobile',
'tag.network': 'Network',
'tag.iot': 'IoT',
'tag.organization': 'Organization',
'tag.language': 'Language',
'tag.human': 'Human',
'tag.other': 'Other',
'skill.mariadb': 'MariaDB',
'skill.postgres': 'PostgreSQL',
'skill.mongo': 'MongoDB',
'skill.vue': 'Vue <i>(.js)</i>',
'skill.angular': 'Angular <i>(7+)</i>',
'skill.parcel': 'Parcel',
'skill.cordova': 'Apache Cordova',
'skill.webpack': 'Webpack',
'skill.webgl': 'WebGL',
'skill.audioapi': 'Audio API',
'skill.websocket': 'Websocket',
'skill.docker': 'Docker',
'skill.bash': 'bash',
'skill.linux': 'GNU/Linux',
'skill.systemd': 'systemd',
'skill.git': 'Git',
'skill.rpm': 'RPM packaging',
'skill.raspberry': 'Raspberry',
'skill.arduino': 'Arduino',
'skill.php': 'PHP',
'skill.html': 'HTML5',
'skill.css': 'CSS3',
'skill.js': 'Javascript',
'skill.ajax': 'AJAX',
'skill.ts': 'Typescript',
'skill.c': 'C (lang)',
'skill.cpp': 'C++',
'skill.python': 'Python',
'skill.go': 'Go (lang)',
'skill.qt': 'Qt',
'skill.opensource': 'Open-source',
'skill.electronics': 'Electronics',
'skill.web': 'Web',
'skill.rest': 'REST',
'skill.crypto': 'Security/crypto',
'skill.imageprocessing': 'Image processing',
'skill.ai': 'Artificial Intelligence',
'skill.deeplearning': 'Deep Learning',
'skill.neuralnetwork': 'Neural Networks',
'skill.opti': 'Program optimization',
'skill.sockets': 'Sockets',
'skill.concurrency': 'Concurrency',
'skill.uiux': 'UI/UX',
'skill.inkscape': 'Inkscape',
'skill.rnd': 'R&D',
'skill.teamlead': 'Team Lead',
'skill.needsanalysis': 'Needs analysis',
'skill.grpc': 'gRPC',
'skill.microservices': 'Micro services',
'skill.kubernetes': 'Kubernetes',
'skill.scrum': 'SCRUM',
'skill.lora': 'LoRa',
'skill.architecture': 'Architecture',
'skill.chirpstack': 'ChirpStack',
'skill.refactor': 'Refactoring',
'skill.timescale': 'Timescale',
'skill.atlassian': 'Atlassian',
'time.dur-format': 'took {duration}',
'time.cur-format': 'since {duration}',
'time.diff-format': '{elapsed} ago',
'time.some': 'sometime',
'time.second': '{n} second', 'time.seconds': '{n} seconds',
'time.minute': '{n} minute', 'time.minutes': '{n} minutes',
'time.hour': '{n} hour', 'time.hours': '{n} hours',
'time.day': '{n} day', 'time.days': '{n} days',
'time.month': '{n} month', 'time.months': '{n} months',
'time.year': '{n} year', 'time.years': '{n} years',
'project.created': 'Created',
'project.sources': 'Hosted at',
'project.doc': 'Documentation at',
'project.end': 'Project stopped in',
'project.still': 'Project still active',
'project.username': 'Adrien Marquès',
'end': ''
}

118
src/locales/fr.ts Normal file
View File

@ -0,0 +1,118 @@
export default {
'home.title': 'DÉVELOPPEUR FREELANCE',
'home.line1': 'Hello, je suis développeur freelance !',
'home.line2-1': 'J\'ai développé ce site afin de vous permettre de',
'home.line2-2': 'parcourir',
'home.line2-3': 'mes compétences et projets.',
'home.line3': 'J\'ai démarré le dev en 2010. Aujourd\'hui je compte plus de 50 projets à mon actif.',
'home.line4': 'Je suis très attaché à fournir à mes clients des projets maintenables afin qu\'ils puissent évoluer à moindre coût (économique et écologique).',
'home.line5-1': 'Les technologies qui m\'intéressent actuellement sont le',
'home.line5-2': 'et l\'',
'home.line5-3': '(Arduino, Raspberry).',
'timeline.title': 'Chronologie des projets avec',
'timeline.title-all': 'Chronologie de tous les projets',
'timeline.back': 'Changer de compétence',
'skills.featured-before': 'Apparaît dans',
'skills.featured-after-1': 'projet',
'skills.featured-after-n': 'projets',
'skills.browse': 'Parcourir les projets',
'skills.browse-all': 'Parcourir tous les projets',
'skills.guide': 'Vous pouvez sélectionner une compétence afin de parcourir les projets liés. Vous pouvez aussi parcourir tous les projets en ne sélectionnant pas de compétence.',
'skills.interest': 'intérêt',
'skills.mastery': 'maîtrise',
'tag.all': 'Tout',
'tag.web': 'Web',
'tag.storage': 'Stockage',
'tag.ui': 'UI/UX',
'tag.system': 'Système',
'tag.mobile': 'Mobile',
'tag.network': 'Réseau',
'tag.iot': 'IoT',
'tag.organization': 'Organisation',
'tag.language': 'Langage',
'tag.human': 'Humain',
'tag.other': 'Autre',
'skill.mariadb': 'MariaDB',
'skill.postgres': 'PostgreSQL',
'skill.mongo': 'MongoDB',
'skill.vue': 'Vue <i>(.js)</i>',
'skill.angular': 'Angular <i>(7+)</i>',
'skill.parcel': 'Parcel',
'skill.cordova': 'Apache Cordova',
'skill.webpack': 'Webpack',
'skill.webgl': 'WebGL',
'skill.audioapi': 'Audio API',
'skill.websocket': 'Websocket',
'skill.docker': 'Docker',
'skill.bash': 'bash',
'skill.linux': 'GNU/Linux',
'skill.systemd': 'systemd',
'skill.git': 'Git',
'skill.rpm': 'RPM packaging',
'skill.raspberry': 'Raspberry',
'skill.arduino': 'Arduino',
'skill.php': 'PHP',
'skill.html': 'HTML5',
'skill.css': 'CSS3',
'skill.js': 'Javascript',
'skill.ajax': 'AJAX',
'skill.ts': 'Typescript',
'skill.c': 'C (lang)',
'skill.cpp': 'C++',
'skill.python': 'Python',
'skill.go': 'Go (lang)',
'skill.qt': 'Qt',
'skill.opensource': 'Open-source',
'skill.electronics': 'Electronique',
'skill.web': 'Web',
'skill.rest': 'REST',
'skill.crypto': 'Securité/crypto',
'skill.imageprocessing': 'Traitement d\'image',
'skill.ai': 'Intelligence Artificielle',
'skill.deeplearning': 'Deep Learning',
'skill.neuralnetwork': 'Réseaux de neuronnes',
'skill.opti': 'Optimization',
'skill.sockets': 'Sockets',
'skill.concurrency': 'Programmation Concurrente',
'skill.uiux': 'UI/UX',
'skill.inkscape': 'Inkscape',
'skill.rnd': 'R&D',
'skill.teamlead': 'Chef d\'équipe',
'skill.needsanalysis': 'Analyse besoin client',
'skill.grpc': 'gRPC',
'skill.microservices': 'Micro services',
'skill.kubernetes': 'Kubernetes',
'skill.scrum': 'SCRUM',
'skill.lora': 'LoRa',
'skill.architecture': 'Architecture',
'skill.chirpstack': 'ChirpStack',
'skill.refactor': 'Refactor',
'skill.timescale': 'Timescale',
'skill.atlassian': 'Atlassian',
'time.dur-format': 'a duré {duration}',
'time.cur-format': 'depuis {duration}',
'time.diff-format': 'il y a {elapsed}',
'time.some': 'un moment',
'time.second': '{n} seconde', 'time.seconds': '{n} secondes',
'time.minute': '{n} minute', 'time.minutes': '{n} minutes',
'time.hour': '{n} heure', 'time.hours': '{n} heures',
'time.day': '{n} jour', 'time.days': '{n} jours',
'time.month': '{n} mois', 'time.months': '{n} mois',
'time.year': '{n} an', 'time.years': '{n} ans',
'project.created': 'Création de',
'project.sources': 'Sources:',
'project.doc': 'Documentation:',
'project.end': 'Projet terminé en',
'project.still': 'Projet toujours actif',
'project.username': 'Adrien Marquès',
'end': ''
}

20
src/locales/index.ts Normal file
View File

@ -0,0 +1,20 @@
export enum Locales {
EN = 'en-US',
FR = 'fr-FR',
}
export const LOCALES = [
{ value: Locales.EN, caption: 'English' },
{ value: Locales.FR, caption: 'Français' },
];
import en from './en';
import fr from './fr';
export const messages = {
[Locales.EN]: en,
[Locales.FR]: fr,
};
export const defaultLocale = Locales.EN;

View File

@ -1,8 +1,19 @@
import Vue from 'vue'; import Vue from 'vue';
import App from './App.vue'; import App from './App.vue';
import VueI18n from 'vue-i18n';
import { messages, defaultLocale } from '@/locales';
Vue.config.productionTip = false; Vue.config.productionTip = false;
Vue.use(VueI18n);
const i18n = new VueI18n({
messages,
locale: defaultLocale,
fallbackLocale: defaultLocale,
});
new Vue({ new Vue({
i18n,
render: (h) => h(App), render: (h) => h(App),
}).$mount('#app'); }).$mount('#app');

View File

@ -1,16 +0,0 @@
export default [
{
title: 'LogAuth',
link: `https://git.xdrm.io/logauth`,
when: [ '2014', '2018' ],
description: `Système de régulation évolutif pour système embarqués avec plateforme de gestion`,
tags: [ `php`, `js`, `python`, `raspberry`, `rest`, `electronics`, `systemd`, `linux` ],
},
{
title: 'LogAuth 2',
link: `https://git.xdrm.io/logauth`,
when: [ '2015', '2018' ],
description: `Système de régulation évolutif pour système embarqués avec plateforme de gestion`,
tags: [ `php`, `js`, `python`, `raspberry`, `rest`, `electronics`, `systemd`, `linux` ],
},
];

8
src/model/level.ts Normal file
View File

@ -0,0 +1,8 @@
export enum Level {
L0 = 0,
L1 = 1,
L2 = 2,
L3 = 3,
L4 = 4,
L5 = 5,
}

532
src/model/projects.ts Normal file
View File

@ -0,0 +1,532 @@
import { Locales } from '@/locales';
import { tID as s } from './skills';
export interface Link {
name: string;
link: string;
}
export interface SrcLink extends Link {
commits: number;
}
export interface Project {
name: string;
skills: s[];
client: string|null;
started_at: Date;
stopped_at: Date|null;
info: { [loc in Locales]: string };
source: SrcLink[]|null;
doc: Link|null;
commits: number;
}
export const Projects: Project[] = [
{
name: 'R-Smart',
client: 'NGEConnect',
skills: [s.Go, s.Web, s.Rest, s.Linux, s.Git, s.RnD, s.Grpc, s.Microservices, s.Kubernetes, s.Scrum, s.Docker, s.LoRa, s.Mongo, s.Architecture, s.Chirpstack, s.Refactor, s.Timescale, s.Atlassian],
started_at: new Date(2023, 4, 1),
stopped_at: new Date(2023, 11, 31),
info: {
[Locales.EN]: 'Micro-services application enabling the collection of information and the control of various LoRa devices. It allows for the supervision of Smart Cities and other connected environments.<br><br>Design, study, and development of GO micro-services enabling incoming and outgoing communication with LoRa devices (R-Card). Integration into the existing system.<br><br>In detail:<ul><li>A lot of refactoring and architecture work</li><li>Refactoring of the permission system</li><li>Migration of JS micro-services to TS</li><li>Involvement in CI/CD and DevOps issues; k8s context</li><li>Introduction of GRPC in micro-services</li><li>Migration of micro-services to GRPC, reusability and deployment issues</li><li>Design and implementation of the overall system for controlling devices</li><li>Creation of the minikube development environment</li><li>Creation of the GO library for error handling</li><li>Creation of the GO library for implementing business transactions with different databases; CI setup</li><li>Creation of the E2E test suite</li><li>Creation of 4 new micro-services</li></ul><br><br>I was able to complete the initial mission before the planned 4 months. The solution was made available to the relevant clients. The mission was extended to continue working on the initiated issues: DevOps, refactoring, control system, etc., as well as deployment to production. During this period, we were able to replace the old unstable system with the new one, which did not encounter any issues in the last 3 months of my mission.<br><br>Participation:<ul><li>14 repositories</li><li>976 commits</li><li>222,000 additions</li><li>165,680 deletions</li></ul>',
[Locales.FR]: 'Application micro-services permettant la collecte d\'informations et le pilotage de divers équipements LoRa sur le terrain. Permet entre autres l\'hypervision de Smart Cities et autres environnements connectés.<br><br>Conception, étude et développement de micro-services GO permettant la communication entrante et sortante avec des équipements LoRA sur le terrain (R-Card). Intégration dans l\'existant.<br><br>En détail :<ul> <li>beaucoup de refactor et d\'architecture</li><li>refactor du système de droits</li><li>migration de micro-services JS en TS</li><li>implication dans les problématiques CI/CD et DevOps ; contexte k8s</li><li>apport du GRPC dans les micro-services</li><li>migration de micro-services en grpc, problématiques de réutilisabilité et déploiement</li><li>conception et implémentation du système global de pilotage des équipements</li><li>création de l\'environnement de dev minikube</li><li>création de la librairie GO pour la gestion des erreurs</li><li>création de la librairie GO pour la mise en place des transactions métier vis à vis des différentes bases de données ; mise en place de la CI</li><li>création de la suite de tests E2E</li><li>création de 4 nouveaux micro-services</li></ul><br><br>J\'ai pu terminer la mission initiale avant les 4 mois prévus. La solution a pu être mise à disposition des clients concernés. Prolongation pour continuer sur les problématiques que j\'avais engagées : DevOps, refactor, système de pilotage, etc ; ainsi que la mise en prod. On a pu dans cette période remplacer l\'ancien système bancal par le nouveau avec lequel on n\'a pas vu de problème pendant les 3 derniers mois de ma mission.<br><br>Participation :<ul><li>14 repos</li><li>976 commits</li><li>222000 additions</li><li>165680 deletions</li></ul>',
},
source: null, doc: null,
commits: 976,
},
{
name: 'Modular box',
client: 'Marlink',
skills: [s.Angular, s.RnD, s.Concurrency, s.Html, s.Css, s.Inkscape, s.UIUX, s.Ts, s.Js, s.Ajax, s.Cordova, s.Bash, s.Git, s.Rest, s.Rpm, s.Vue, s.Web, s.NeedsAnalysis, s.Docker],
started_at: new Date(2019, 0, 1),
stopped_at: new Date(2019, 6, 15),
info: {
[Locales.EN]: 'Master\'s degree internship. Internal R&D for a client that sells internet boxes with satellite communications for boats.<br><br>The projet was to create the prototype of a modular system. It allows users to only have the parts of the website and features they have installed. It also allows for independent tiny development without having to rebuild the whole projet for every new feature.<br><br>I conducted works on the Angular frontend. I developed an app store to manage module installation and updates. Furthermore, I created a minimal Android version of the existing website as a proof-of-concept for mobile porting. By the end of the internship I was able to propose a prototype that allowed :<ul><li>to change navigation elements (menus)</li><li>to inject components (pages, graphical elements)</li><li>to inject services (business logic)</li></ul><br>At the same time I developed a set of tools for compiling Angular components and services into an installable RPM package on the box.<br><br>The prototype allowed the client to envision an industrial modular solution.',
[Locales.FR]: 'Stage de fin de Master. R&D interne pour un client proposant des box internet satellite pour bateaux.<br><br>Le projet consiste en la création d\'un prototype de système de module. Il permet aux utilisateurs d\'avoir uniquement les parties du site web et les fonctionnalités qu\'ils souhaitent installer. Cela permet aussi d\'avoir des développements indépendants sans avoir à reconstruire tout le produit à chaque nouvelle fonctionnalité.<br><br>J\'ai conduit les travaux sur la partie frontend avec Angular 7. J\'ai développé un app store pour gérer les modules installés et leur mise à jour ; ainsi qu\'une version Android minimaliste du site existant comme proof-of-concept du portage vers les plateformes mobiles. Au terme du stage j\'ai pu proposer un prototype qui permettait :<ul><li>de modifier la navigation (menus)</li><li>d\'injecter des composants graphiques (pages ou éléments graphiques)</li><li>d\'injecter des services (logique métier)</li></ul><br>En parallèle j\'ai aussi développé un ensemble d\'outils permettant la compilation des différents composants et services Angular en un paquet RPM installable sur la box.<br><br>Le prototype a permis au client de se projeter vers une solution modulaire industrielle.',
},
source: null, doc: null,
commits: 0, // ???
},
{
name: 'EarthMap',
client: 'Collins Aerospace',
skills: [s.Qt, s.RnD, s.Cpp, s.Concurrency, s.Git, s.Css, s.NeedsAnalysis, s.Inkscape, s.UIUX],
started_at: new Date(2019, 9, 26),
stopped_at: new Date(2021, 3, 11),
info: {
[Locales.EN]: 'Multi-platform (Surface, iOS) flight assistance application for pilots. Research and development branch of an international aeronautical company (US). It performs data retrieval and visualization of standardized aeronautical data such as flight information, airport information, meteorological information, etc. The application is embedded in an existing software that interfaces with aircraft computers.<br><br>Project existing for about 10 years, I first rewrote and refactored critical parts in order to soften the blocking points. In a second time, I added new functionalities that were impossible or too expensive until then. I was also able to sensitize the team and the client to transition from SVN to Git. Some of my work:<ul><li>redesign of the network data fetching core</li><li>redesign of the asynchronous weather image download manager</li><li>redesign of the color management</li><li>support for the Arinc 424 standard</li><li>redesign of the popup system and click management</li><li>integration of new data sources</li></ul><br>The project ended with a stable version that is still in use today.',
[Locales.FR]: 'Application multi-plateforme (Surface, iOS) d\'assistance de vol pour pilotes de ligne. Branche recherche et développement d\'une entreprise aéronautique internationale (US). Elle effectue la récupération et la représentation graphique de données standardisées aéronautiques d\'informations de vol, d\'aéroports, météorologiques, etc. L\'application est intégrée dans un logiciel existant qui s\'interface avec les calculateurs avioniques.<br><br>Projet ayant environ 10 ans d\'existence, j\'ai tout d\'abord ré-écris et refactorisé des parties critiques afin d\'assouplir les points bloquants. Dans un second temps il a été question d\'ajouter de nouvelles fonctionnalités jusque là impossibles ou trop coûteuses. J\'ai aussi pu sensibiliser l\'équipe et le client à une transition de SVN vers Git. Quelques-uns de mes travaux:<ul><li>refonte du moteur de récupération de données réseau</li><li>refonte du gestionnaire de téléchargement asynchrone d\'images météorologiques</li><li>refonte de la gestion des couleurs</li><li>support du standard Arinc 424</li><li>refonte du système de popups et gestion des clicks</li><li>ajout de nouvelles sources de données</li></ul><br>Le projet s\'est terminé sur une version stable qui est encore aujourd\'hui utilisée.',
},
source: null, doc: null,
commits: 840,
},
{
name: 'ADSBOnPED',
client: 'Collins Aerospace',
skills: [s.Qt, s.RnD, s.Cpp, s.Concurrency, s.Git, s.NeedsAnalysis, s.Css, s.Bash, s.Linux, s.Inkscape, s.UIUX, s.TeamLead],
started_at: new Date(2020, 6, 16),
stopped_at: new Date(2023, 3, 16),
info: {
[Locales.EN]: 'Multi-platform (linux, windows and iOS) flight assistance application for pilots. Research and development branch of an international aeronautical company (US).<br><br>I am the project architect and have been in charge of the technical direction since its creation. I lead a rotating team of 2 to 3 people, ensure the follow-up and the analysis of the customer needs up to task extraction through a simplified Agile organization.<br><br>The project is developed in C++ with the <b>Qt</b> framework. It features:<ul><li>an asynchronous event-driven message bus</li><li>a simplified <a href="https://sass-lang.com/documentation/syntax#scss">SCSS</a> engine</li><li>a dynamic settings system</li><li>a generic iOS windowing system</li><li>a unit test and integration framework</li><li>a generic interface for aircraft on-board computers</li></ul><br>The first iteration of the project has now been accepted for the industrialization process. I now work on the next R&D phases.',
[Locales.FR]: 'Application multi-plateforme (linux, windows et iOS) d\'assistance de vol pour les pilotes de ligne. Branche recherche et développement d\'une entreprise aéronautique internationale (US).<br><br>Je suis architecte du projet et assure la direction technique depuis sa création. Je pilote une équipe tournante de 2 à 3 personnes, assure le suivi et l\'analyse des besoins client jusqu\'à l\'extraction de tâches à travers une organisation Agile simplifiée.<br><br>Le projet est développé en C++ avec le framework <b>Qt</b>. Il comporte notamment :<ul><li>un bus de messages évènementiel asynchrone</li><li>un moteur <a href="https://sass-lang.com/documentation/syntax#scss">SCSS</a> simplifié</li><li>un système de paramètres dynamique</li><li>un système de fenêtrage générique iOS</li><li>un système de tests unitaires et tests d\'intégration</li><li>une interface générique pour différents calculateurs avioniques</li></ul><br>La première itération du projet est passée en industrialisation après tests par les pilotes. Je travaille depuis sur les prochaines phases R&D.',
},
source: null, doc: null,
commits: 1897 + 86,
},
{
name: 'EasyCom',
client: 'Medwin | Vygon',
skills: [s.Go, s.Rest, s.Concurrency, s.Git, s.NeedsAnalysis, s.Bash, s.Linux, s.Docker, s.Crypto, s.Postgres, s.Opti, s.Websocket, s.Inkscape, s.UIUX],
started_at: new Date(2021, 0, 6),
stopped_at: new Date(2022, 10, 1),
info: {
[Locales.EN]: 'Medical project for a French company with international reach. The project consists in a modern web platform for the management of nutrient pumps via Internet. I developed the backend of the system as well as the production environment.<br><br>The backend is written in Go and uses docker. It exposes a REST API for the frontend and a websocket API to communicate with the pumps. A PostgreSQL database allows managing the different resources. We have implemented several systems to ensure medical grade security at each level, such as the use of certificate chains, argon2, mTLS, jwt. The backend is fully covered by both unit and integration tests (dockerized) in order to reach a code coverage greater than 80%. I have set up integration and continuous deployment mechanisms to provide an up-to-date version to the client throughout the development.<br><br>The REST API is based on the use of the open-source library <a href="#project-aicra">aicra</a> that I have been developing for several years.<br><br>My work consists of:<ul><li><b>backend</b> : the backend with its 2 APIs. <i>(1493 commits)</i></li><li><b>cert</b> : command line tool to manage certificate chains, used as a library by the backend. <i>(47 commits)</i></li><li><b>package</b> : simplified execution environment for team members. <i>(13 commits)</i></li><li><b>setup</b> : production environment. <i>(14 commits)</i></li></ul>',
[Locales.FR]: 'Projet médical pour une entreprise française à portée internationale. Le projet consiste en un système web moderne permettant la gestion de parcs de pompes à nutriments depuis Internet. J\'ai réalisé le backend du système ainsi que l\'élaboration de l\'environnement de production.<br><br>Le backend est développé en Go et utilise docker. Il expose une API REST pour le frontend et une API websocket pour communiquer avec les pompes. Une base de données postgreSQL permet la gestion des différentes ressources. Plusieurs systèmes ont été mis en place afin d\'assurer une sécurité de grade médical aux différents niveaux, notamment avec l\'utilisation de chaînes de certificats, argon2, mTLS, jwt. Le backend est entièrement couvert par des tests unitaires et d\'intégration (dockerisés) afin d\'atteindre une couverture de code supérieure à 80%. J\'ai mis en palce des mécanismes d\'intégration et de déploiement continu afin de fournir un version à jour au client tout au long du développement.<br><br>L\'API REST repose sur l\'utilisation de la librairie open-source <a href="#project-aicra">aicra</a> que je développe depuis plusieurs années.<br><br>Mon travail consiste en:<ul><li><b>backend</b> : le backend avec ses 2 APIs. <i>(1493 commits)</i></li><li><b>cert</b> : outil en ligne de commande permettant la gestion des chaînes de certificats, utilisé comme librairie par le backend. <i>(47 commits)</i></li><li><b>package</b> : environnement simplifié d\'exécution pour les membres de l\'équipe. <i>(13 commits)</i></li><li><b>setup</b> : environnement de production. <i>(14 commits)</i></li>',
},
source: null, doc: null,
commits: 1493 + 47 + 13 + 14,
},
{
name: 'aicra',
client: null,
skills: [s.Go, s.Web, s.Rest, s.OpenSource, s.Git, s.Inkscape],
started_at: new Date(2018, 4, 19),
stopped_at: null, // still active
info: {
[Locales.EN]: 'Started as a personal go library to ease REST API development. The main goal is to greatly enhance maintainability by providing a single configuration file that describes every endpoint of the API. Everything that can be automated is while staying idiomatic to go. Aicra automates routing, parameter validation and extraction, permission management, response output formatting.<br><br>I then used it for a professional project in the medical field after corporate evaluation and audit. At the end of the project, results in production are promising : development and testing speed and ease, overall performances, configuration readability for other team members provides a real benefit, they can know the whole api at any time.',
[Locales.FR]: 'Démarré en tant que projet personnel afin de simplifier mes développements d\'API REST. L\'objectif principal est d\'améliorer significativement la maintenabilité via un fichier de configuration unique qui décrit/définit tous les endpoints de l\'API. Tout ce qui peut être automatisé l\'est, tout en restant idiomatique à Go. Aicra automatise le routage, la validation et l\'extraction des paramètres, les classes de permissions, le formattage des réponses, etc.<br><br>Ce projet a été utilisé pour un projet industriel dans le domaine médical après audit et évaluation de l\'entreprise. Au terme du projet, les résultats sont plutôt encourageants :<ul><li>rapidité de développement</li><li>rapidité d\'écriture des tests</li><li>performances</li><li>lisibilité du fichier de configuration pour les membres de l\'équipe (permet de connaître toute l\'API à tout moment du développement)</li></ul>',
},
source: [{ name: 'github.com/xdrm-io/aicra', link: 'https://github.com/xdrm-io/aicra', commits: 535 }],
doc: { name: 'pkg.go.dev/github.com/xdrm-io/aicra', link: 'https://pkg.go.dev/github.com/xdrm-io/aicra' },
commits: 0,
},
{
name: 'go-ws',
client: null,
skills: [s.Go, s.Websocket, s.OpenSource, s.Opti, s.Git],
started_at: new Date(2018, 3, 24),
stopped_at: new Date(2021, 5, 21),
info: {
[Locales.EN]: 'Research project to improve my understanding of websockets. It consists of a client and a server implementation, based on the protocol\'s RFC.<br><br>I conducted an optimization pass to achieve an efficient concurrent solution solution while remaining idiomatic to Go (channels, select).',
[Locales.FR]: 'Projet de recherche pour étendre ma compréhension des websockets. Le projet consiste en la création d\'un client et d\'un serveur websocket, basés exclusivement sur la RFC du protocole.<br><br>Une passe d\'optimisation a été réalisée afin d\'avoir une solution performante en terme de concurrence, tout en étant idiomatique à Go (channels, select).',
},
source: [{ name: 'git.xdrm.io/go/ws', link: 'https://git.xdrm.io/go/ws', commits: 114 }],
doc: { name: 'pkg.go.dev/git.xdrm.io/go/ws', link: 'https://pkg.go.dev/git.xdrm.io/go/ws' },
commits: 0,
},
{
name: 'SMMP (LogAuth)',
client: null,
skills: [s.Php, s.UIUX, s.Css, s.Js, s.Ajax, s.Web, s.NeedsAnalysis, s.MariaDB, s.Git, s.Inkscape],
started_at: new Date(2017, 0, 18),
stopped_at: new Date(2018, 10, 5),
info: {
[Locales.EN]: 'I conducted the LogAuth project back in 2017 for a French branch of an international carrier and logistics company. The project aims to provide a solution to track their warehouse\'s users and vehicles.<br>The project consists of:<ul><li><b>SMMP</b> : website accessible on the Internet for warehouse management.</li><li><b>SATS</b> : hardware and software for a Raspberry Pi that is embedded on each machine. It provides RFID authentication to users with history capability.</li><li><b>setup</b> : tool to write a functional microSD card for each new Raspberry to add to the system (custom credentials, id, features, plugins).</li></ul><br>The SMMP platform is a website developed with a PHP backend and a vanilla frontend (no library). It allows managing groups of users and groups of machines. Actions can be configured for groups of users on groups of machines. A page allows to view real-time access history.<br><br>The strength of the project is that it allows the addition of new features without any changes, thanks to a generic database model, which allows the direct definition of inputs/outputs on the Raspberry Pi. The state machine is also defined in a generic database model.',
[Locales.FR]: 'J\'ai réalisé le projet <b>LogAuth</b> en 2017 pour la branche française d\'une entreprise internationale de transport et logistique. Le projet vise à fournir une solution de suivi et gestion des accès utilisateurs aux machines d\'un entrepôt.<br><br>Le projet consiste en:<ul><li><b>SMMP</b> : site web accessible sur Internet pour la gestion par entrepôt.</li><li><b>SATS</b> : hardware et logiciel pour un Raspberry Pi qui est embarqué sur chaque machine. Il fournit une authentification RFID aux utilisateurs avec historique.</li><li><b>setup</b> : outil permettant d\'écrire un carte microSD fonctionnelle pour chaque nouveau Raspberry à ajouter au système (authentification sur-mesure, id, fonctionnalités, plugins).</li></ul><br>La plateforme SMMP est un site web développé avec un backend PHP et un frontend vanilla (pas de librairie). Elle permet la gestion de groupes d\'utilisateurs et de groupes de machines. Des actions peuvent être configurées pour des groupes d\'utilisateurs sur des groupes de machines. Une page permet de parcourir l\'historique des accès en temps réel.<br><br>Le point fort du projet est qu\'il permet l\'ajout de nouvelles fonctionnalités en l\'état, grâce à un modèle de base de données générique, qui permet directement de définir des entrées/sorties du Raspberry Pi. La machine à état est aussi définie dans le modèle générique en base.',
},
source: [{ name: 'git.xdrm.io/logauth/smmp', link: 'https://git.xdrm.io/logauth/smmp', commits: 567 }],
doc: null,
commits: 0,
},
{
name: 'SATS (LogAuth)',
client: null,
skills: [s.RaspBerry, s.Electronics, s.Systemd, s.NeedsAnalysis, s.Python, s.Php, s.Linux, s.Bash, s.Crypto, s.Git, s.Arduino],
started_at: new Date(2017, 0, 18),
stopped_at: new Date(2018, 10, 5),
info: {
[Locales.EN]: 'I conducted the LogAuth project back in 2017 for a French branch of an international carrier and logistics company. The project aims to provide a solution to track their warehouse\'s users and vehicles. The project consists of:<ul><li><b>SMMP</b> : website accessible on the Internet for warehouse management.</li><li><b>SATS</b> : hardware and software for a Raspberry Pi that is embedded on each machine. It provides RFID authentication to users with history capability.</li><li><b>setup</b> : tool to write a functional microSD card for each new Raspberry to add to the system (custom credentials, id, features, plugins).</li></ul><br>The SATS system consists of a shield (electronic board) that attaches to the Raspberry Pi and a firmware. The firmware is a collection of programs that are executed through a complex <b>systemd</b> state machine. It schedules the different phases: boot, network connection, update check, update, I/O initialization, the daemon (main loop).<br><br>It grants or denies access when a RFID card is detected, the access list is updated periodically from the SMMP API. The SATS firmware is capable of updating itself and loading new features from the network. It records all accesses (or attempts), manages and monitors the status of the machine in real time.<br><br>I made several PCBs so that SATS can interface correctly with the Arduino, and in later versions with a Raspberry Pi.<br><br>Through this project, I have designed a customized authentification system, which protects communications between SATS and the SMMP. It avoids key theft, request hijacking, and allows a secure resynchronization protocol after an attack or theft. Source: <a href="https://git.xdrm.io/logauth/schastsp/src/branch/master/PROTOCOL.md">Draft protocol</a>',
[Locales.FR]: 'J\'ai réalisé le projet <b>LogAuth</b> en 2017 pour la branche française d\'une entreprise internationale de transport et logistique. Le projet vise à fournir une solution de suivi et gestion des accès utilisateurs aux machines d\'un entrepôt.<br><br>Le projet consiste en:<ul><li><b>SMMP</b> : site web accessible sur Internet pour la gestion par entrepôt.</li><li><b>SATS</b> : hardware et logiciel pour un Raspberry Pi qui est embarqué sur chaque machine. Il fournit une authentification RFID aux utilisateurs avec historique.</li><li><b>setup</b> : outil permettant d\'écrire un carte microSD fonctionnelle pour chaque nouveau Raspberry à ajouter au système (authentification sur-mesure, id, fonctionnalités, plugins).</li></ul><br>Le système SATS consiste en un shield (carte éléctronique) qui se branche au Raspberry Pi ainsi qu\'un firmware. Le firmware est composé de plusieurs programmes lancés à travers une machine à état complexe basée sur <b>systemd</b>. Elle ordonnance les différentes phases : boot, connexion réseau, vérification de mise à jour, mise à jour, initialisation entrées/sorties, le démon (boucle principale).<br><br>Il autorise ou non les accès quand une carte RFID est détectée, la liste des accès est mise à jour périodiquement depuis l\'api SMMP. Il est capable de se mettre à jour et de charger de nouvelles fonctionnalités depuis le réseau. Il enregistre tous les accès (ou tentatives), gère et monitore l\'état de la machine en temps réel.<br><br>J\'ai réalisé plusieurs PCBs afin que le SATS s\'interface correctement avec l\'Arduino, puis dans les dernières versions avec un Raspberry Pi.<br><br>A travers ce projet, j\'ai pu concevoir mon système d\'authentication personnalisé, qui protège les communications entre les SATS et le SMMP. Il évite notamment le vol de clé, le détournement de requêtes, et permet une resynchronisation sécurisée après une attaque ou un vol. Source: <a href="https://git.xdrm.io/logauth/schastsp/src/branch/master/PROTOCOL.md">Définition du protocole</a>',
},
source: [
{ name: 'git.xdrm.io/logauth/sats', link: 'https://git.xdrm.io/logauth/sats', commits: 214 },
{ name: 'git.xdrm.io/logauth/bash-com', link: 'https://git.xdrm.io/logauth/bash-com', commits: 32 },
{ name: 'git.xdrm.io/logauth/ioemu', link: 'https://git.xdrm.io/logauth/ioemu', commits: 47 },
{ name: 'git.xdrm.io/logauth/schastsp', link: 'https://git.xdrm.io/logauth/schastsp', commits: 92 },
],
doc: null,
commits: 0,
},
{
name: 'SID (IUT)',
client: null,
skills: [s.Git, s.TeamLead, s.Rest, s.Php, s.NeedsAnalysis, s.UIUX, s.Inkscape, s.Ajax, s.Html, s.Css, s.Js, s.MariaDB, s.Web],
started_at: new Date(2015, 9, 20),
stopped_at: new Date(2016, 0, 12),
info: {
[Locales.EN]: 'Tutored project for my DUT degree (BTEC Higher National Dimploma equivalent). The subject was to provide a web-based solution for the management of the IT department (information system): teachers, students, exams, grades, timetables, course units and subunits, etc.<br><br>It was my first experience in analyzing customer needs, the head of the department in this case. I learned to adopt the vision of the client in order to provide a result as close as possible to his expectations.<br><br>The project is mainly done in PHP with a draft of a 2-level web API (not knowing REST yet). It was a complete professional web experience: UI/UX, html/css, js, php, sql, needs analysis, deadlines, etc. The quality of the project is based on skills acquired during my personal web projects. The client appreciated my investment and availability.',
[Locales.FR]: 'Projet tutoré de fin de cycle dans le cadre de l\'IUT informatique. Le sujet était de proposer une solution web pour la gestion du département informatique (système d\'information complet) : enseignants, élèves, examens, notes, emplois du temps, gestion des UE, modules, etc.<br><br>C\'est ma première expérience d\'analyse des besoins clients, le chef du département dans ce cas. J\'ai appris à adopter la vision du client afin de fournir un résultat au plus proche de ses attentes.<br><br>Le projet est principalement fait en PHP avec une ébauche d\'API web à 2 niveaux (ne connaissant pas encore REST). Ce fut une expérience professionnelle web complète : ui/ux, html/css, js, php, sql, analyse besoin client, délais, etc. La qualité du projet repose sur les compétences acquises lors de mes projets web perso. Le client a apprécié mon investissement et ma disponibilité.',
},
source: [{ name: 'git.xdrm.io/dut/sid', link: 'https://git.xdrm.io/dut/sid', commits: 401 }],
doc: null,
commits: 0,
},
{
name: 'PTUT (Master)',
client: null,
skills: [s.Git, s.TeamLead, s.Rest, s.Php, s.UIUX, s.NeedsAnalysis, s.Inkscape, s.Ajax, s.Vue, s.Js, s.MariaDB, s.Docker, s.Web],
started_at: new Date(2018, 1, 16),
stopped_at: new Date(2018, 5, 13),
info: {
[Locales.EN]: 'Tutored project for my Master\'s degree. The subject was to provide a web-based solution for the management of the IT department (information system): teachers, classes, rooms, grades, timetables, course units and subunits, etc.<br><br>We had to replace a set of excel files used by the department chiefs to manage resources and generate schedules. The unavailability of the client was sometimes a hindrance to progress. <br><br>The project has a PHP backend, and a Vue frontend. We used docker-compose with a continuous integration and deployment system. 300 hours of work were invested in a little over 3 months. At the end of the collaboration, the project was adopted.',
[Locales.FR]: 'Projet tutoré de fin de cycle dans le cadre du Master Informatique. Le sujet était de proposer une solution web pour la gestion du département informatique (système d\'information complet) : enseignants, classes, salles, notes, emplois du temps, gestion des UE, modules, etc.<br><br>Nous devions remplacer un ensemble de fichiers excel utilisés par les chefs de département pour gérer les ressources et générer les plannings. L\'indisponibilité du client a parfois été un frein à l\'avancement. <br><br>Le projet a un backend PHP, et un frontend en Vue. Nous avons utilisé docker-compose avec un système d\'intégration et de déploiement continu. 300 heures de travail ont été investies en un peu plus de 3 mois. Au terme de la collaboration, le projet a été adopté.',
},
source: [
{ name: 'git.xdrm.io/mti/ptut-virtenv', link: 'https://git.xdrm.io/mti/ptut-virtenv', commits: 103 },
{ name: 'git.xdrm.io/mti/ptut-vhost', link: 'https://git.xdrm.io/mti/ptut-vhost', commits: 397 },
],
doc: null,
commits: 0,
},
{
name: 'Denoising.py',
client: null,
skills: [s.RnD, s.Python, s.ImageProcessing, s.OpenSource, s.Git],
started_at: new Date(2015, 8, 6),
stopped_at: new Date(2015, 10, 13),
info: {
[Locales.EN]: 'Research project for my DUT degree (BTEC Higher National Dimploma equivalent). I had to propose denoising functions in order to restore image quality after several noise levels.<br><br>Manual encoding and decoding of BMP files (raw image format). It features noising and denoising functions for : salt and pepper, Berouilli\'s additive noise, gaussian additive noise, etc. It also uses convolution product in the later phase. Surprising results for salt and pepper and Berouilli noises.<br><br><a href="https://git.xdrm.io/dut/denoising.py/raw/branch/master/dossier/dossier.pdf">PDF report (fr)</a>',
[Locales.FR]: 'Projet de recherche dans le cadre du DUT informatique. Il fallait proposer des fonctions de débuitage d\'images afin de restaurer la qualité après différents niveaux de bruit.<br><br>Encodage et décodage manuel des fichiers BMP (fichier d\'image brut). Implémentation de fonctions de bruitage et débruitage pour les bruits : poivre et sel, bruit additif de Bernouilli, bruit additif gaussien, etc. Utilisation de produit de convolution dans les dernières phases. Résultats étonnament bons pour le poivre et sel et le bruit additif de Bernouilli.<br><br><a href="https://git.xdrm.io/dut/denoising.py/raw/branch/master/dossier/dossier.pdf">Dossier pdf</a>',
},
source: [
{ name: 'git.xdrm.io/dut/denoising.py', link: 'https://git.xdrm.io/dut/denoising.py', commits: 97 },
],
doc: null,
commits: 0,
},
{
name: 'Douscord',
client: null,
skills: [s.RnD, s.Vue, s.AudioAPI, s.Parcel, s.Docker, s.Web, s.Rest, s.Ajax, s.Cordova, s.Html, s.Css, s.Js, s.Git],
started_at: new Date(2018, 2, 21),
stopped_at: new Date(2018, 3, 24),
info: {
[Locales.EN]: 'Advanced web development project for my Master\'s degree. We created in duo a Discord clone (chat and audio/video streaming platform). The challenge was not to look at the original source code.<br><br>I did the frontend part with Vue as a single-page application (SPA). We have deployed the web as well as a desktop version using Electron. We managed to achieve in the 1 month:<ul><li>a visual clone of the Discord user interface</li><li>multi-channel management for chat and audio</li><li>real-time chat capability</li><li>audio full-duplex streaming communication</li></ul>',
[Locales.FR]: 'Projet de développement web avancé dans le cadre de mon Master. Nous avons en binôme, créé un clone de Discord (plateforme de chat et streaming audio/vidéo). Le défi étant de ne pas regarder le code source de l\'application officielle.<br><br>J\'ai développé la partie frontend avec Vue sous forme de single-page application (SPA). Nous avons pu déployer la version web ainsi qu\'une version bureau en utilisant Electron. Nous avons réussi à réaliser en 1 mois:<ul><li>une copie visuelle de l\'interface de Discord</li><li>la gestion de plusieurs "channels" de chat et audio</li><li>le chat temps réel</li><li>La communication audio full-duplex en streaming</li></ul>',
},
source: [
{ name: 'git.xdrm.io/mti/discord-client', link: 'https://git.xdrm.io/mti/discord-client', commits: 176 },
{ name: 'git.xdrm.io/mti/discord-server', link: 'https://git.xdrm.io/mti/discord-server', commits: 91 },
],
doc: null,
commits: 0,
},
{
name: 'NxTIC',
client: 'CNRS & IRIT',
skills: [s.Php, s.Web, s.Js, s.Css, s.Html, s.NeedsAnalysis, s.Git],
started_at: new Date(2016, 3, 4),
stopped_at: new Date(2016, 11, 20),
info: {
[Locales.EN]: 'For the degree termination internship of my DUT (BTEC Higher National Dimploma equivalent). I worked in collaboration with the IRIT and the CNRS for a study in sociology. The study focused on the interpersonal networks induced by the use of new communication means: smartphones, calls, sms, social networks.<br><br>The project consists of a full-stack website allowing the subjects of the study to fill in an interactive questionnaire with specific sociological tools that are difficult to represent on a traditional printed medium.<br><br>With a PHP backend and a vanilla frontend (without library), I was able, during the 3 months of internship, to analyze the needs of the customers and provide a suitable solution in the allotted time.<br><br>At the end of the internship, I was hired for a 3 months contract (in parallel of my studies) to continue the development of new features.',
[Locales.FR]: 'Stage de fin de DUT. J\'ai travaillé en collaboration avec l\'IRIT et le CNRS dans le cadre d\'une étude en sociologie. L\'étude visait à étudier les réseaux interpersonnels induits par l\'utilisation des nouveaux moyens de communication : smartphones, appels, sms, réseaux sociaux.<br><br>Le projet consiste en un site web fullstack permettant aux sujet de l\'étude la saisie d\'un questionnaire interactif comportant des outils sociologies spécifiques difficilement représentables sur un support papier classique.<br><br>Backend PHP et frontend vanilla (sans librairie), j\'ai pu, pendant les 3 mois de stage, analyser les besoin des clients afin de fournir une solution adaptée dans le temps imparti.<br><br>A l\'issue du stage, j\'ai décroché un CDD de trois mois supplémentaires (en parallèle de mon cursus) pour poursuivre le développement de nouvelles fonctionnalités.',
},
source: [
{ name: 'git.xdrm.io/xdrm-brackets/NxTIC', link: 'https://git.xdrm.io/xdrm-brackets/NxTIC', commits: 582 },
],
doc: null,
commits: 0,
},
{
name: 'Enigmail.py',
client: null,
skills: [s.Python, s.Crypto, s.Bash, s.Linux, s.OpenSource, s.Git],
started_at: new Date(2015, 4, 21),
stopped_at: new Date(2015, 5, 1),
info: {
[Locales.EN]: 'Personal research project. Reinterpretation of the encryption and decryption principles used by ENIGMA. With automated sending and receiving of emails.',
[Locales.FR]: 'Projet personnel de recherche. Réinterprétation des principes de cryptage et décryptage utilisés par <a href="https://en.wikipedia.org/wiki/Enigma_machine">ENIGMA</a>. Ajout de l\'envoi et la réception automatisée d\'emails.',
},
source: [
{ name: 'git.xdrm.io/xdrm-brackets/enigmail.py', link: 'https://git.xdrm.io/xdrm-brackets/enigmail.py', commits: 89 },
],
doc: null,
commits: 0,
},
{
name: 'univ-pau-ics',
client: null,
skills: [s.Php, s.Css, s.Bash, s.Web, s.OpenSource, s.Git],
started_at: new Date(2017, 8, 12),
stopped_at: new Date(2017, 8, 17),
info: {
[Locales.EN]: 'Personal project to extract the university schedule into an format that is compatible with most agenda apps. The schedule was provided as an image. I used an OCR (extracts text from images) and text positions to generate course titles, classrooms, teachers, days, hours. I created a web service that exposes an up-to-date calendar file. That way, agenda applications can update themselves to the latest schedule provided.<br><br>Used by university students until the schedule software has been changed.',
[Locales.FR]: 'Projet personnel pour extraire l\'emploi du temps de l\'université dans un format compatible avec les applications d\'agenda. L\'emploi du temps était distribué sous forme d\'image. Utilisation d\'un OCR (extraction de texte depuis une image) et des positionnements pour générer les intitulés des cours, les salles, enseignants et horaires. Création d\'un service web qui expose le fichier calendrier à jour. Ainsi, les applications d\'agenda récupèrent les dernières informations.<br><br>Service utilisé par les étudiants jusqu\'au changement du logiciel.',
},
source: [
{ name: 'git.xdrm.io/xdrm-brackets/univ-pau-ics', link: 'https://git.xdrm.io/xdrm-brackets/univ-pau-ics', commits: 83 },
],
doc: null,
commits: 0,
},
{
name: 'neural network (php)',
client: null,
skills: [s.Php, s.NeuralNetwork, s.DeepLearning, s.AI, s.OpenSource, s.Git],
started_at: new Date(2017, 8, 12),
stopped_at: new Date(2017, 8, 17),
info: {
[Locales.EN]: 'Personal project to better understand how neural networks work. Implementation of a genetic algorithm with a fitness function, anti-regression filter, crossover, and mutations. I did not implement back propagation because I did not master the theoretical details at the time ; I later made a valid implementation in the <a href="#project-neuralnet__go_">neuralnet (go)</a> project.',
[Locales.FR]: 'Projet personnel pour mieux comprendre le fonctionnement des réseaux de neuronnes. Implémentation d\'un algorithme génétique avec fonction de fitness, anti-régression, crossover, et mutations. Je n\'ai pas implémenté la back propagation standard aux réseaux de neuronnes car je ne maîtrisait pas les détails théoriques à l\'époque ; j\'ai plus tard fait une implémentation correcte dans le projet <a href="#project-neuralnet__go_">neuralnet (go)</a>.',
},
source: [
{ name: 'git.xdrm.io/xdrm-brackets/neuralnet.php', link: 'https://git.xdrm.io/xdrm-brackets/neuralnet.php', commits: 16 },
],
doc: null,
commits: 0,
},
{
name: 'neuralnet (go)',
client: null,
skills: [s.Go, s.NeuralNetwork, s.DeepLearning, s.AI, s.OpenSource, s.Git],
started_at: new Date(2018, 9, 10),
stopped_at: new Date(2018, 9, 15),
info: {
[Locales.EN]: 'Personal project to better understand neural networks in practice. The project is inspired by a series of videos on this subject by <a href="https://www.youtube.com/c/3blue1brown">3Blue1Brown</a>. Implementation of propagation and back propagation. Optimization with the use of matrix product. Project finished after a functional implementation and a clean API.',
[Locales.FR]: 'Projet personnel pour mieux comprendre les réseaux de neuronnes en pratique. Inspiré par la série de vidéos sur le sujet de <a href="https://www.youtube.com/c/3blue1brown">3Blue1Brown</a>. Implémentation de la propagation et backpropagation. Optimisation avec l\'utilisation de produit matriciel. Projet terminé après une implémentation fonctionnelle et une API propre.',
},
source: [
{ name: 'git.xdrm.io/go/neuralnet', link: 'https://git.xdrm.io/go/neuralnet', commits: 5 },
],
doc: { name: 'pkg.go.dev/git.xdrm.io/go/neuralnet', link: 'https://pkg.go.dev/git.xdrm.io/go/neuralnet' },
commits: 0,
},
{
name: 'air traffic control',
client: null,
skills: [s.C, s.Concurrency, s.Sockets, s.Bash, s.Git],
started_at: new Date(2017, 2, 22),
stopped_at: new Date(2017, 4, 19),
info: {
[Locales.EN]: 'Distributed systems project in the framework of my Master\'s degree. It consists of a multicast server and several multicast clients:<ul><li>one for planes that send data periodically</li><li>one for traffic visualization in C</li><li>one for visualization in Java</li></ul><br>I realized the C part featuring : multi-threading, low-level sockets management, protocols design, multicast management.',
[Locales.FR]: 'Projet de systèmes distribués dans le cadre du Master. Il s\'agit d\'un serveur multicast et de plusieurs clients multicast:<ul><li>un pour les avions qui envoient leurs données</li><li>un pour la de visualisation du traffic en C</li><li>un pour la visualisation en Java</li></ul><br>J\'ai réalisé la partie C comportant du multithreading, la gestion de sockets bas-niveau, la conception des protocoles, et la gestion multicast.',
},
source: [
{ name: 'git.xdrm.io/xdrm-brackets/sysdis-project', link: 'https://git.xdrm.io/xdrm-brackets/sysdis-project', commits: 101 },
],
doc: null,
commits: 0,
},
{
name: 'carb-sync',
client: null,
skills: [s.Arduino, s.Cpp, s.C, s.Electronics, s.OpenSource, s.Git],
started_at: new Date(2020, 3, 30),
stopped_at: new Date(2020, 5, 1),
info: {
[Locales.EN]: 'Arduino project allowing the adjustment of motorcycle carburetors. The project uses 2 high-resolution pressure sensors to display a graphical differential on a small OLED screen. Knobs allow to adjust the buffer size and the sampling. This small project allows to synchronize its carburetors on battery in a portable way and at low cost.',
[Locales.FR]: 'Projet Arduino permettant le réglage de carburateurs de moto. Le projet emploie 2 capteurs de pression haute résolution pour présenter un différentiel graphique sur un petit écran OLED. Des potentiomètres permettent de régler la taille du tampon et l\'échantillonnage. Ce petit projet permet de synchroniser ses carburateurs sur batterie de manière portable et à moindre coût.',
},
source: [
{ name: 'git.xdrm.io/iot/carb-sync', link: 'https://git.xdrm.io/iot/carb-sync', commits: 16 },
],
doc: null,
commits: 0,
},
{
name: 'evw',
client: null,
skills: [s.Arduino, s.Cpp, s.C, s.Electronics, s.OpenSource, s.Git],
started_at: new Date(2019, 5, 10),
stopped_at: new Date(2019, 6, 17),
info: {
[Locales.EN]: 'Arduino project to remotely control a solenoid valve. The project was ordered to simplify the control of an agricultural irrigation valve. There are 2 components:<ul><li>a very low power controller (battery operated) that sends a command. The controller issues a visual signal when the command is accepted.</li><li>the solenoid valve that waits for orders and is activated when valid and authenticated orders are received.</li></ul><br>The system uses multicast UDP to avoid the need to identify the different elements on the network. The final solution enables several independent solenoid valves and controllers on the same network.',
[Locales.FR]: 'Projet Arduino permettant de commander une électrovanne à distance. Le projet a été commandé pour simplifier le contrôle d\'une vanne d\'irrigation agricole. Il y a 2 composants:<ul><li>une commande très basse consommation (sur pile) qui envoie des ordres. La commande émet un signal visuel quand l\'ordre est accepté.</li><li>l\'électrovanne attends des ordres, elle s\'actionne à la réception d\'un ordre valide et authentifié.</li></ul><br>Le système utilise le multicast UDP afin de ne pas avoir besoin d\'identifier les différents éléments sur le réseau. La solution finale permet d\'avoir plusieurs électrovannes et commandes indépendantes sur le même réseau.',
},
source: [
{ name: 'git.xdrm.io/iot/evw', link: 'https://git.xdrm.io/iot/evw', commits: 13 },
],
doc: null,
commits: 0,
},
{
name: 'clifmt',
client: null,
skills: [s.Go, s.Bash, s.OpenSource, s.Git],
started_at: new Date(2019, 0, 24),
stopped_at: new Date(2019, 10, 18),
info: {
[Locales.EN]: 'Personal project in Go providing layout and color support in the terminal. Implementation of the vt-100 standard used in most Linux terminals. Use of a markdown-inspired syntax to automate bold, italic, underline, links, etc. It allows to use background and font colors with hexadecimal code or color name. It also enables proper alignment, and supports Go channels to have multiple lines that update themselves.',
[Locales.FR]: 'Projet personnel en Go permettant la mise en page et le support des couleurs pour le terminal. Mise en pratique du standard vt-100 utilisé dans la plupart des terminaux linux. Le projet définit une syntaxe inspirée du markdown pour automatiser la mise en forme : gras, italique, souligné, liens, etc. Support des couleurs de fond et de texte avec leur code hexa ou le nom de la couleur. Permet une vraie gestion de l\'alignement, et support des channels Go afin de modifier plusieurs lignes de texte sur place.',
},
source: [
{ name: 'git.xdrm.io/go/clifmt', link: 'https://git.xdrm.io/go/clifmt', commits: 27 },
],
doc: null,
commits: 0,
},
{
name: 'pingu',
client: null,
skills: [s.Go, s.Systemd, s.Linux, s.Bash, s.OpenSource, s.Git],
started_at: new Date(2018, 10, 16),
stopped_at: new Date(2018, 10, 18),
info: {
[Locales.EN]: 'Small project in Go that watches services for Linux servers. When a service fails, it sends you an email with a one-time use link. Opening the link restarts the service. I\'ve used it for a couple years for my VPS.',
[Locales.FR]: 'Projet personnel en Go qui surveille les services pour des serveurs linux. Quand un service échoue ou s\'arrête, un email est envoyé avec un lien à usage unique. Ouvrir le lien permet de redémarrer le service. Utilisé depuis plusieurs années sur mon VPS.',
},
source: [
{ name: 'git.xdrm.io/go/pingu', link: 'https://git.xdrm.io/go/pingu', commits: 32 },
],
doc: null,
commits: 0,
},
{
name: '*nix-amer',
client: null,
skills: [s.Go, s.Linux, s.Systemd, s.Bash, s.OpenSource, s.Git],
started_at: new Date(2018, 10, 6),
stopped_at: new Date(2019, 10, 18),
info: {
[Locales.EN]: 'Personal project in Go providing a universal format to automate Linux configuration.<br><br>The project provides a syntax to manipulate several configuration files, run commands, copy files, install programs, etc. It provides an abstraction of the most used configuration formats : nginx, yaml, ini, bash, json. Running *nix-amer on a new Linux server, installs and configures the whole server. The project supports heavy task parallelization.',
[Locales.FR]: 'Projet personnel en Go proposant un format universel afin d\'automatiser la configuration de machines Linux.<br><br>Le projet fournit une syntaxe permettant d\'éditer différents fichiers de configuration, de lancer des commandes, copier des fichiers, installer des programmes, etc. Il fournit une abstraction des formats de configuration nginx, yaml, ini, bash, json. L\'idée étant qu\'en lançant *nix-amer sur un nouveau serveur Linux, il installe et configure le serveur. Le projet supporte la parallélisation des tâches.',
},
source: [
{ name: 'git.xdrm.io/go/nix-amer', link: 'https://git.xdrm.io/go/nix-amer', commits: 108 },
],
doc: null,
commits: 0,
},
{
name: 'cliverage',
client: null,
skills: [s.Go, s.Bash, s.OpenSource, s.Git],
started_at: new Date(2019, 10, 22),
stopped_at: new Date(2019, 10, 23),
info: {
[Locales.EN]: 'Personal project in Go allowing the visualization of code coverage in a terminal. The library exploits the vt-100 standard which allows using colors.',
[Locales.FR]: 'Projet personnel en Go permettant la visualisation de couverture de code dans un terminal. La librairie exploite le standard vt-100 qui permet notamment l\'utilisation de couleurs.',
},
source: [
{ name: 'git.xdrm.io/go/cliverage', link: 'https://git.xdrm.io/go/cliverage', commits: 11 },
],
doc: null,
commits: 0,
},
{
name: 'xlock',
client: null,
skills: [s.Linux, s.Bash, s.Crypto, s.OpenSource, s.Git],
started_at: new Date(2019, 2, 2),
stopped_at: new Date(2020, 1, 20),
info: {
[Locales.EN]: 'Utility using i3lock to lock your Linux PC without looking like it: the screen remains visible and the mouse works, but that\'s all. You just have to type your password then ENTER to unlock it.<br><br>I added a security layer because it is still possible to kill the i3lock process from another administrator account. When the process starts, a file with a random name is created in /tmp. Deleting this file locks the screen, so it is difficult to unlock it, because it would require recreating the file whose name is not known.',
[Locales.FR]: 'Utilitaire exploitant i3lock pour verrouiller son pc Linux sans en avoir l\'air : l\'écran reste affiché et la souris fonctionne, mais c\'est tout. Il suffit de taper son mot de passe puis ENTRER pour le déverrouiller.<br><br>Ajout d\'une couche de sécurité car il est possible de tuer le processus i3lock depuis un autre compte administrateur. Au verrouillage, un fichier au nom aléatoire est créé dans /tmp. La suppression de ce fichier permet de verrouiller l\'écran, il est donc difficile de le déverrouiller, car il faudrait recréer le fichier dont le nom n\'est pas connu.',
},
source: [
{ name: 'git.xdrm.io/xdrm-brackets/xlock', link: 'https://git.xdrm.io/xdrm-brackets/xlock', commits: 9 },
],
doc: null,
commits: 0,
},
{
name: 'proxy ftp',
client: null,
skills: [s.Sockets, s.C, s.Concurrency, s.Git],
started_at: new Date(2015, 11, 14),
stopped_at: new Date(2016, 2, 1),
info: {
[Locales.EN]: 'Network project developed in C for my DUT (BTEC Higher National Dimploma equivalent). It is a FTP proxy that transforms passive FTP connections into active connections.',
[Locales.FR]: 'Projet réseau développé en C dans le cadre de mon DUT. Il s\'agit d\'un proxy FTP qui transforme les connexions FTP passives en connexions actives.',
},
source: [
{ name: 'git.xdrm.io/dut/proxy-ftp', link: 'https://git.xdrm.io/dut/proxy-ftp', commits: 23 },
],
doc: null,
commits: 0,
},
{
name: 'face recognition',
client: null,
skills: [s.ImageProcessing, s.Js, s.Opti, s.DeepLearning, s.AI, s.Web, s.OpenSource, s.Git],
started_at: new Date(2016, 9, 5),
stopped_at: new Date(2016, 10, 16),
info: {
[Locales.EN]: 'Javascript project in the context of my license. The project consists in performing treatments on an image in order to optimize the recognition of facial features by Tracking.js and jsfeat.<br><br>We used back-buffers and implemented the following operations:<ul><li>brightness adjustments</li><li>contour recognition</li><li>face extrusion</li><li>face contour detection</li><li>background removal</li><li>recognition of facial features</li><li>use of subtractive filters (soft deep learning)</li></ul>',
[Locales.FR]: 'Projet javascript dans le cadre de la L3 informatique. Le projet consiste à effectuer des traitements sur une image afin d\'optimiser la reconnaissance des éléments faciaux par Tracking.js et jsfeat.<br><br>Utilisation de back-buffers et implémentation des traitements suivants:<ul><li>ajustement de luminosité</li><li>détection de contours</li><li>extrusion de visage</li><li>détection des contours du visage</li><li>retrait de l\'arrière plan</li><li>reconnaissance des éléments de visage</li><li>utilisation de filtres soustractifs (soft deep learning)</li></ul>',
},
source: [
{ name: 'git.xdrm.io/l3/face-recognition.js', link: 'https://git.xdrm.io/l3/face-recognition.js', commits: 32 },
],
doc: null,
commits: 0,
},
{
name: 'opportunistic XBee',
client: null,
skills: [s.Arduino, s.Sockets, s.Cpp, s.Concurrency, s.Git],
started_at: new Date(2018, 11, 1),
stopped_at: new Date(2018, 11, 13),
info: {
[Locales.EN]: 'Arduino network project in the context of my Master\'s degree. The goal is to design a protocol for a network of mobile nodes that interconnect to communicate with a central node. The communications are made only from neighbour to neighbour through XBee modules. The protocol must be robust to the nodes\' movements.<br><br>After designing the protocol, I proposed an implementation for the nodes as well as for the central one. Tested in the real world with several spatially distributed Arduino\'s, the protocol is consistent and shows resilience.',
[Locales.FR]: 'Projet Arduino de réseau dans le cadre du Master. Il s\'agit de créer un protocole pour un réseau de nœuds mobiles qui s\'interconnectent afin de communiquer avec un nœud central. Les communications se font uniquement de proche en proche grâce à des modules XBee. Le protocole doit être robuste aux mouvements des nœuds.<br><br>Après la conception du protocole, j\'ai proposé une implémentation pour les nœuds ainsi que pour le nœud central. Testé sur le terrain avec plusieurs Arduino répartis dans l\'espace, le protocole est consistant et fait preuve de résilience.',
},
source: [
{ name: 'git.xdrm.io/mti/opportunistic-xbee', link: 'https://git.xdrm.io/mti/opportunistic-xbee', commits: 27 },
],
doc: null,
commits: 0,
},
{
name: 'Nuit de l\'info (2017)',
client: null,
skills: [s.Web, s.Vue, s.Websocket, s.Ajax, s.Html, s.Css, s.Js, s.Bash, s.Php, s.UIUX, s.Inkscape, s.Git],
started_at: new Date(2017, 11, 9),
stopped_at: new Date(2017, 11, 10),
info: {
[Locales.EN]: 'Project achieved for the <a href="https://nuitdelinfo.com/">Nuit de l\'info</a>, 2017 edition. The Nuit de l\'info is a national event that brings together thousands of students around the development of a project with a common topic. Many sponsors offer challenges and prizes for the winners.<br><br>We built a website with a PHP and Kotlin backend, with a Vue frontend. I developed the PHP part and the front-end.<br><br>At the end of the night, the project won 2 silver medals: <a href="https://www.nuitdelinfo.com/nuitinfo/defis2017:archives">"Into The While" team</a>',
[Locales.FR]: 'Projet réalisé pour la <a href="https://nuitdelinfo.com/">Nuit de l\'info</a>, édition 2017. La nuit de l\'info est un évènement national qui rassemble des milliers d\'étudiants autour du développement d\'un projet avec un sujet commun. De nombreux sponsors proposent des défis et des prix pour les gagnants.<br><br>Nous avons développé, en binôme, un site web full-stack avec un backend PHP et Kotlin, nous avons utilisé Vue pour le frontend. J\'ai développé la partie PHP et le front-end.<br><br>A l\'issue de la nuit, le projet a remporté 2 médailles d\'argent : <a href="https://www.nuitdelinfo.com/nuitinfo/defis2017:archives">équipe "Into The While"</a>',
},
source: [
{ name: 'git.xdrm.io/ndi-2018/main', link: 'https://git.xdrm.io/ndi-2018/main', commits: 229 },
],
doc: null,
commits: 0,
},
{
name: 'lebonprix.apk',
client: null,
skills: [s.UIUX, s.Rest, s.Inkscape, s.OpenSource, s.Git],
started_at: new Date(2018, 10, 5),
stopped_at: new Date(2018, 11, 15),
info: {
[Locales.EN]: 'Android application developed in Kotlin during the Master. The application serves as an interface to an API providing the prices of various goods (amazon, leboncoin, etc). Its purpose is to present a search engine that allows to visualize the price distribution of goods by categories. It\'s my only project in Kotlin, I was mostly in charge of the visual part and the animations.<br><br>At the end of the project, the teacher contacted us to get our agreement to publish the application on the Play Store. He finally abandoned the project because his API was illegal.',
[Locales.FR]: 'Application Android développée en Kotlin pendant le Master. L\'application sert d\'interface à une API donnant les prix de certains biens (amazon, leboncoin, etc). Elle a pour but de présenter un moteur de recherche qui permet de visualiser les distributions de prix des biens par catégories. C\'est mon seul projet en Kotlin, je me suis surtout occupé de la partie visuelle et des animations.<br><br>A l\'issue du projet, l\'enseignant nous a contacté pour avoir notre accord afin de publier l\'application sur le Play Store. Il a finalement abandonné le projet car son API était illégale.',
},
source: [
{ name: 'git.xdrm.io/mti/lebonprix.apk', link: 'https://git.xdrm.io/mti/lebonprix.apk', commits: 3 },
],
doc: null,
commits: 0,
},
{
name: 'api-mixer',
client: null,
skills: [s.Ts, s.Rest, s.OpenSource, s.Git],
started_at: new Date(2018, 9, 17),
stopped_at: new Date(2018, 10, 30),
info: {
[Locales.EN]: 'Node projet for my Master\'s degree. Web service allowing to multiplex several goods selling APIs (supermarkets, amazon, etc.). The service allows you to search for a product and retrieve its details by combining several data sources.<br>The project is developed in typescript (node).',
[Locales.FR]: 'Projet node dans le cadre du Master. Service web permettant de multiplexer plusieurs API de vente de biens (supermarchés, amazon, etc.). Le service permet de faire une recherche sur un produit et de récupérer ses détails en combinant plusieurs sources de données.<br>Le projet est développé en typescript (node).',
},
source: [
{ name: 'git.xdrm.io/mti/api-mixer', link: 'https://git.xdrm.io/mti/api-mixer', commits: 20 },
],
doc: null,
commits: 0,
},
];

View File

@ -1,41 +1,809 @@
export default [ import { Locales } from '@/locales';
{ import { Level } from './level';
title: 'Web',
keywords: [ 'languages', 'technologies' ], export enum tID {
skills: [ MariaDB,
{ label: 'MariaDB', link: 'https://mariadb.org/', level: 0.6 }, Postgres,
{ label: 'Postgres', link: 'https://postgresql.org/', level: 0.8 }, Mongo,
{ label: 'MongoDB', link: 'https://mongodb.com/', level: 0.5 }, Vue,
{ label: 'Vue <i>(.js)</i>', link: 'https://vuejs.org/', level: 0.8 }, Angular,
{ label: 'Angular <i>(7+)</i>', link: 'https://angular.io/', level: 0.3 }, Parcel,
{ label: 'WebGL', link: 'https://www.khronos.org/webgl/', level: 0.3 }, Cordova,
{ label: 'Audio API', link: 'https://webaudio.github.io/web-audio-api/', level: 0.5 }, Webpack,
{ label: 'Websocket', link: 'https://tools.ietf.org/html/rfc6455', level: 1 }, WebGL,
], AudioAPI,
Websocket,
Docker,
Bash,
Linux,
Systemd,
Git,
Rpm,
RaspBerry,
Arduino,
Php,
Html,
Css,
Js,
Ajax,
Ts,
C,
Cpp,
Python,
Go,
Qt,
OpenSource,
Electronics,
Web,
Rest,
Crypto,
ImageProcessing,
AI,
DeepLearning,
NeuralNetwork,
Opti,
Sockets,
Concurrency,
UIUX,
Inkscape,
RnD,
TeamLead,
NeedsAnalysis,
Grpc,
Microservices,
Kubernetes,
Scrum,
LoRa,
Architecture,
Chirpstack,
Refactor,
Timescale,
Atlassian,
}
export enum tTag {
All,
Web,
Storage,
UI,
System,
Mobile,
Network,
IoT,
Organization,
Language,
Human,
Other,
}
export function tagLabel(t: tTag): string {
switch (t) {
case tTag.All: return 'tag.all';
case tTag.Web: return 'tag.web';
case tTag.Storage: return 'tag.storage';
case tTag.UI: return 'tag.ui';
case tTag.System: return 'tag.system';
case tTag.Mobile: return 'tag.mobile';
case tTag.Network: return 'tag.network';
case tTag.IoT: return 'tag.iot';
case tTag.Organization: return 'tag.organization';
case tTag.Language: return 'tag.language';
case tTag.Human: return 'tag.human';
case tTag.Other: return 'tag.other';
}
}
export function skillLabel(s: tID): string {
switch (s) {
case tID.MariaDB: return 'skill.mariadb';
case tID.Postgres: return 'skill.postgres';
case tID.Mongo: return 'skill.mongo';
case tID.Vue: return 'skill.vue';
case tID.Angular: return 'skill.angular';
case tID.Parcel: return 'skill.parcel';
case tID.Cordova: return 'skill.cordova';
case tID.Webpack: return 'skill.webpack';
case tID.WebGL: return 'skill.webgl';
case tID.AudioAPI: return 'skill.audioapi';
case tID.Websocket: return 'skill.websocket';
case tID.Docker: return 'skill.docker';
case tID.Bash: return 'skill.bash';
case tID.Linux: return 'skill.linux';
case tID.Systemd: return 'skill.systemd';
case tID.Git: return 'skill.git';
case tID.Rpm: return 'skill.rpm';
case tID.RaspBerry: return 'skill.raspberry';
case tID.Arduino: return 'skill.arduino';
case tID.Php: return 'skill.php';
case tID.Html: return 'skill.html';
case tID.Css: return 'skill.css';
case tID.Js: return 'skill.js';
case tID.Ajax: return 'skill.ajax';
case tID.Ts: return 'skill.ts';
case tID.C: return 'skill.c';
case tID.Cpp: return 'skill.cpp';
case tID.Python: return 'skill.python';
case tID.Go: return 'skill.go';
case tID.Qt: return 'skill.qt';
case tID.OpenSource: return 'skill.opensource';
case tID.Electronics: return 'skill.electronics';
case tID.Web: return 'skill.web';
case tID.Rest: return 'skill.rest';
case tID.Crypto: return 'skill.crypto';
case tID.ImageProcessing: return 'skill.imageprocessing';
case tID.AI: return 'skill.ai';
case tID.DeepLearning: return 'skill.deeplearning';
case tID.NeuralNetwork: return 'skill.neuralnetwork';
case tID.Opti: return 'skill.opti';
case tID.Sockets: return 'skill.sockets';
case tID.Concurrency: return 'skill.concurrency';
case tID.UIUX: return 'skill.uiux';
case tID.Inkscape: return 'skill.inkscape';
case tID.RnD: return 'skill.rnd';
case tID.TeamLead: return 'skill.teamlead';
case tID.NeedsAnalysis: return 'skill.needsanalysis';
case tID.Grpc: return 'skill.grpc';
case tID.Microservices: return 'skill.microservices';
case tID.Kubernetes: return 'skill.kubernetes';
case tID.Scrum: return 'skill.scrum';
case tID.LoRa: return 'skill.lora';
case tID.Architecture: return 'skill.architecture';
case tID.Chirpstack: return 'skill.chirpstack';
case tID.Refactor: return 'skill.refactor';
case tID.Timescale: return 'skill.timescale';
case tID.Atlassian: return 'skill.atlassian';
}
}
export interface tSkill {
icon: string|null;
link: string;
tags: tTag[];
info: { [loc in Locales]: string };
interest: Level;
mastery: Level;
}
export type tSkills = { [id in tID]: tSkill };
export const Skills: tSkills = {
[tID.MariaDB]: {
link: 'https://mariadb.org',
icon: 'skills/mariadb.svg',
tags: [tTag.Web, tTag.Storage],
interest: Level.L1,
mastery: Level.L4,
info: {
[Locales.EN]: 'MySQL (now MariaDB) is the most known database management system to introduce SQL. I started using mysql then MariaDB as my first introduction to SQL, mostly when I started learning web with PHP. I now prefer using postgreSQL instead as it tends to be more robust and performant overall.',
[Locales.FR]: 'MySQL (maintenant MariaDB) est le système de base de données le plus répendu pour une introduction au SQL. J\'ai commencé à utiliser MySQL puis MariaDB en introduction à SQL, principalement quand j\'ai commencé à apprendre le développement web avec PHP. J\'utilise maintenant plutôt PostgreSQL car je le trouve plus robuste et performant.',
},
}, },
{ [tID.Postgres]: {
title: 'Sys', link: 'https://postgresql.org',
keywords: ['languages', 'technologies'], icon: 'skills/postgres.svg',
skills: [ tags: [tTag.Web, tTag.Storage],
{ label: 'Linux/bash', link: 'https://www.gnu.org/software/bash/', level: 1 }, interest: Level.L2,
{ label: 'docker', link: 'https://www.docker.com/', level: 0.8 }, mastery: Level.L3,
{ label: 'systemd', link: 'https://freedesktop.org/wiki/Software/systemd/', level: 1 }, info: {
{ label: 'git', link: 'https://git-scm.com/', level: 0.8 }, [Locales.EN]: 'A DBMS like MariaDB with more performance and consistency. My main choice of dbms when using sql orfor relational databases. Had some experience with it mainly with go services with docker.',
{ label: 'raspberry', link: 'https://www.raspberrypi.org/', level: 0.8 }, [Locales.FR]: 'Un DBMS comme MariaDB mais avec la performance et robustesse en plus. Mon choix de prédilection quand il s\'agit de DBMS sql ou de base relationnelle. Je l\'ai pas mal utilisé principalement pour des services go avec docker et docker-compose.',
{ label: 'arduino', link: 'https://www.arduino.cc/', level: 0.5 }, },
{ label: 'RPM packaging', link: 'https://rpm.org/', level: 0.7 },
],
}, },
{ [tID.Mongo]: {
title: 'Misc', link: 'https://mongodb.com',
keywords: ['frameworks', 'api', 'dependencies'], icon: 'skills/mongo.svg',
skills: [ tags: [tTag.Web, tTag.Storage],
{ label: 'PHP', link: 'https://www.php.net/', level: 0.9 }, interest: Level.L2,
{ label: 'HTML<i>/</i>CSS', link: 'https://www.w3.org/standards/webdesign/htmlcss', level: 1 }, mastery: Level.L1,
{ label: 'Typescript', link: 'https://www.typescriptlang.org/', level: 0.6 }, info: {
{ label: 'C<i>/</i>C++', link: 'https://isocpp.org/', level: 0.8 }, [Locales.EN]: 'I learned it during my Master\'s degree. I used advanced Mongo with schema validation and complex filter requests. I find this technology really interesting over SQL, but I find classical relational databases are easier to comprehend and I am more used to it.',
{ label: 'Python', link: 'https://www.python.org/', level: 0.3 }, [Locales.FR]: 'J\'ai appris et utilisé Mongo pendant mon Master, je n\'ai pas eu de réelle occasion de l\'utiliser depuis. J\'ai pu utiliser la version "robuste" de Mongo : validation de schéma and requêtes avec filtres complexes.<br><br>Je trouve que cette technologie est une intéressante alternative au SQL "classique", mais les bases relationnelles sont à mon avis plus facile à apréhender, puis j\'y suis habitué.',
{ label: 'go', link: 'https://golang.org/', level: 1 }, },
],
}, },
];
[tID.Vue]: {
link: 'https://vuejs.org',
icon: 'skills/vue.svg',
tags: [tTag.Web, tTag.UI],
interest: Level.L4,
mastery: Level.L3,
info: {
[Locales.EN]: 'Web front-end framework that displays components directly from your data, with an extremely low cost. I started learning Vue (.js) back in 2016, and never stopped practicing since then for personal and professional projects. I view it as a better alternative than angular which provides you with a strict framework that can lack flexibility among teams and projects.<br><br>Vue makes it your responsability to properly structure your project which I like to take care of myself, as it tends to provide a better workflow adjusted for every project.',
[Locales.FR]: 'Framework pour front-end web qui affiche des composants graphiques directement depuis les données à un coup relativement bas. J\'ai découvert Vue en 2016, et n\'ai jamais arrêté de l\'utiliser depuis tant pour mes projets perso que professionnels. Je vois Vue comme une meilleure alternative qu\'Angular qui, lui, fournit un cadre de travail strict qui manque de flexibilité aux équipes et projets.<br><br>Vue vous rend la responsabilité de structurer correctement vos projets, ce que j\'apprécie faire moi-même, je trouve que ça pousse à un meilleur workflow (ajusté à chaque projet).',
},
},
[tID.Angular]: {
link: 'https://angular.io',
icon: 'skills/angular.svg',
tags: [tTag.Web, tTag.UI],
interest: Level.L0,
mastery: Level.L1,
info: {
[Locales.EN]: 'I used it only once to break it apart in a R&D project. I had to "hack" it in order to inject services and components on-the-fly.<br><br>You had a kind of App store where you could install modules. The Angular-powered website would inject new navigation menus, pages and services according to the modules you had installed.<br><br>I do not like Angular, as I think Vue.js does a better job achieving the same goal with a cleaner API, and does not force you to embrace the Angular/Google way of coding and organizing things.',
[Locales.FR]: 'Je l\'ai découvert de la manière forte pour un projet de R&D projet. J\'ai du le "hacker" afin de pouvoir y injecter des services et composants au runtime.<br><br>J\'ai du faire un genre d\'App store où il était possible d\'installer des modules. Le site web Angular se voyait ensuite injecté les éléments de navigation, les pages et services en fonction des modules installés.<br><br>Je n\'apprécie pas Angular, je pense que Vue fait un meilleur travail pour achever le même but avec une interface plus propre. Je trouve qu\'Angular force les devs à se plier à la manière Angular/Google de programmer et d\'organiser les choses.',
},
},
[tID.Parcel]: {
link: 'https://parceljs.org/',
icon: 'skills/parcel.svg',
tags: [tTag.Web],
interest: Level.L2,
mastery: Level.L2,
info: {
[Locales.EN]: 'Package bundler for the web, that aims to require no configuration. I used it often back in my Master\'s Degree period along with PHP backends. It allows to get away from webpack which I find overly complicated and a major point of failure for most frontends.<br><br>I haven\'t used it in a while but would highly consider it as it has no real prerequisite.',
[Locales.FR]: 'Bundler (c.f. webpack) pour le web, qui vise à ne demander aucune configuration. Beaucoup utilisé pendant le Master, souvent pour des projets web avec un backend PHP. Permet de s\'affranchir de webpack, ce que je trouve plutôt bénéfique car trop complexe et souvent un point-of-failure majeur.<br><br>Je ne l\'ai pas utilisé depuis un moment mais je le garde en tête comme alternative sans prérequis pour les projets web.',
},
},
[tID.Cordova]: {
link: 'https://cordova.apache.org/',
icon: 'skills/cordova.svg',
tags: [tTag.System, tTag.Web, tTag.Mobile],
interest: Level.L1,
mastery: Level.L1,
info: {
[Locales.EN]: 'Library that transforms a single page application into a mobile or desktop application with no further programming. I\'ve used it often to transform SPA into android, iOS or desktop apps at low cost. I haven\'t needed this kind of software for a while, it might now be favorable to use Capacitor or other "alternatives".',
[Locales.FR]: 'Programme permettant de transformer des sites web SPA (Single Page Application) en applications mobiles ou bureau sans avoir à toucher le code. Je l\'ai beaucoup utilisé pour transformer mes applis SPA en applications Android, iOS ou de bureau a moindre coût. Je n\'en ai pas eu besoin depuis un moment, il serait sûrement judicieux de m\'intéresser aux nouvelles alternatives plus à jour telles que Capacitor.',
},
},
[tID.Webpack]: {
link: 'https://webpack.js.org/',
icon: 'skills/webpack.svg',
tags: [tTag.Web],
interest: Level.L0,
mastery: Level.L2,
info: {
[Locales.EN]: 'The most known/used package bundler for the web ... I used it a lot because it is often required. But I hate that it is so complex that most users use it without understanding what is really does. I avoid it whenever possible, the issue can be transposed to node_modules and the whole node ecosystem.',
[Locales.FR]: 'Le bundler pour du web le plus connu/utilisé ... J\'ai été forcé de beaucoup l\'utiliser mais jamais par choix ! C\'est un outil très puissant, mais je n\'aime pas que ce soit aussi complexe et que la plupart des utilisateurs (moi inclus) l\'utilisent sans vraiment comprendre ce que ça fait réellement. Je l\'évite dès que possible, cette problématique peut être transposée à node_modules et l\'écosystème node en général.',
},
},
[tID.WebGL]: {
link: 'https://www.khronos.org/webgl/',
icon: null,
tags: [tTag.Web],
interest: Level.L2,
mastery: Level.L1,
info: {
[Locales.EN]: 'OpenGL API for the web ! I used it during my graduation years, the potential is great but I need to find a time to explore what\'s new.',
[Locales.FR]: 'API OpenGL pour le web ! Utilisé à plusieurs reprises pendant mes études, il y a un gros potentiel mais il me faudrait trouver le temps d\'explorer les nouveautés et utilisation.',
},
},
[tID.AudioAPI]: {
link: 'https://webaudio.github.io/web-audio-api/',
icon: null,
tags: [tTag.Web],
interest: Level.L2,
mastery: Level.L3,
info: {
[Locales.EN]: 'Audio API for the web ! I used it mainly to build a discord clone (audio and video full-duplex streaming) as a school project. I also tried the Fourier transform to build nice graphics (c.f. Soundcloud) but have not succeeded at the time.',
[Locales.FR]: 'API Audio pour le web ! Je l\'ai principalement utilisée pour un projet clone de Discord (audio et video en streaming full-duplex) pendant mes études. J\'ai aussi beaucoup joué avec pour essayer d\'implémenter la transformée de Fourier afin de produire un visuel de lecture audio (c.f. Soundcloud) mais je n\'ai pas réussi à l\'époque.',
},
},
[tID.Websocket]: {
link: 'https://tools.ietf.org/html/rfc6455',
icon: null,
tags: [tTag.Web, tTag.Network, tTag.IoT],
interest: Level.L3,
mastery: Level.L5,
info: {
[Locales.EN]: 'Communication protocol for the web that is full-duplex (both sides can send and receive simultaneously). I use them whenever possible to avoid the classical expensive polling.<br><br>I also created my own websocket client & server implementation in Go following nothing but the RFC for learning purposes.<br><br>I\'ve used, designed and implemented websocket communications among personal and professional projects.',
[Locales.FR]: 'Protocole de communication pour le web full-duplex (les 2 côtés peuvent envoyer et recevoir en même temps). Je les utilise dès que possible plutôt que faire du polling coûteux pour rien.<br><br>J\'ai aussi créé ma propre implémentation de client et serveur en Go en suivant juste la RFC afin d\'avoir une meilleure appréhension du fonctionnement et des limites.<br><br>J\'ai eu plusieurs l\'occasion de concevoir et implémenter des systèmes de communications basés sur les websocket que ce soit pour des projets perso ou pro.',
},
},
[tID.Docker]: {
link: 'https://docker.com',
icon: 'skills/docker.svg',
tags: [tTag.Web, tTag.System],
interest: Level.L3,
mastery: Level.L4,
info: {
[Locales.EN]: 'Container system and ecosystem. I am using it for a while now. Most of my go projects use it as it costs no memory for your executable (multi-stage build from scratch) and allows for isolation and a better control over the running environment.<br><br>I am learning docker a bit more every time I use it. It is often coupled with docker-compose in my projects.',
[Locales.FR]: 'Système et écosystème de containerisation. J\'utilise docker assez régulièrement depuis des années. La plupart de mes projets en go en tirent parti car cela permet de construire des containers "multi-plateforme" sans que ça pèse plus lourd que l\'exécutable (multi-stage build from scratch). Docker aussi permet à mes devs en go de l\'isolation et un meilleur contrôle de l\'environnement.<br><br>J\'étends mes connaissances à chaque nouveau projet, je l\'utilise aussi beaucoup via docker-compose.',
},
},
[tID.Bash]: {
link: 'https://www.gnu.org/software/bash/',
icon: null,
tags: [tTag.System],
interest: Level.L3,
mastery: Level.L5,
info: {
[Locales.EN]: 'GNU sh-compatible shell, featured in GNU/Linux distros. Felt in love with GNU/linux and its ecosystem a while back (was 14yo back then) and I\'ve never stopped. I\'m often the linux/bash guy of the team.<br><br>I use it extensively to automate anything a human can do as it avoids a lot of mistakes in most workflows.',
[Locales.FR]: 'Shell compatible <i>sh</i> fourni dans les OS GNU/Linux. Je suis tombé amoureux de GNU/Linux et son écosystème depuis un moment (à 14 ans) et je n\'ai pas déchanté depuis. Je suis souvent le "linux/bash guy" de l\'équipe.<br><br>J\'utilise le bash de manière déraisonnable pour automatiser tout ce que les humains peuvent faire, mais sans les erreurs, dans la plupart de mes workflows.',
},
},
[tID.Linux]: {
link: 'https://www.linux.org',
icon: 'skills/linux.svg',
tags: [tTag.System],
interest: Level.L3,
mastery: Level.L5,
info: {
[Locales.EN]: 'Felt in love with GNU/linux and its ecosystem a while back (was 14yo back then) and I\'ve never stopped. <br><br>Linux has been my main OS choice since then. I stepped through Ubuntu, Debian, Manjaro, Elementary, Tails (live), and I am now on Solus for a few years.',
[Locales.FR]: 'Je suis tombé amoureux de GNU/Linux et son écosystème depuis un moment (à 14 ans) et je n\'ai pas déchanté depuis. <br><br>Linux est mon OS depuis. Je suis passé par Ubuntu, Debian, Manjaro, Elementary, Tails (live), je suis maintenant sur Solus depuis quelques années.',
},
},
[tID.Systemd]: {
link: 'https://freedesktop.org/wiki/Software/systemd/',
icon: null,
tags: [tTag.System],
interest: Level.L4,
mastery: Level.L3,
info: {
[Locales.EN]: 'Linux most advanced system and service manager, it coordinates all programs run in a Linux system. I had the chance to dive deep into systemd for the LogAuth project. I had to define a complex structure for some Raspberry Pi to be independent, featuring services, paths, sockets, dependencies, requirements, etc.<br><br>Often used but rarely understood, I like its flexibility and have used it in many projects upon the production deployment.',
[Locales.FR]: 'Le gestionnaire de système et services le plus avancé pour Linux, il coordonne tous les programmes d\'un système Linux. J\'ai eu l\'occasion de plonger dans les tréfonds de systemd pour le projet LogAuth. J\'ai du concevoir et implémenter une structure complexe pour des Raspberry Pi "embarqués" en utilisant les services, paths, sockets, dépendences, ordres d\'éxécution, etc.<br><br>Souvent utilisé mais rarement compris, j\'apprécie sa puissance et sa flexibilité, je l\'ai souvent utilisé dans mes projets web pour le déploiement sur l\'environnement de production.',
},
},
[tID.Git]: {
link: 'https://git-scm.com/',
icon: 'skills/git.svg',
interest: Level.L3,
mastery: Level.L5,
tags: [tTag.System, tTag.Organization],
info: {
[Locales.EN]: 'The standard distributed version control system for code management. Used for all projects.',
[Locales.FR]: 'Le système distribué de gestion de version par excellence. Utilisé pour chaque projet.',
},
},
[tID.Rpm]: {
link: 'https://rpm.org/',
icon: null,
tags: [tTag.System],
interest: Level.L1,
mastery: Level.L3,
info: {
[Locales.EN]: 'The CentOS package manager. I used it once; I had to create a tool that generates RPM packages to provide installable modules for an embedded web project : the package contains web components (code, assets, navigation, pages) and backend services.',
[Locales.FR]: 'Le gestionnaire de paquets de CentOS. J\'ai pu m\'y intéresser lors d\'un stage, j\'ai du créer un outil qui génère des paquets RPM afin de fournir des modules installables pour un projet web embarqué : les paquets contienent des composants web (du code, images, la navigation, des pages) et des services backend.',
},
},
[tID.RaspBerry]: {
link: 'https://raspberrypi.org',
icon: 'skills/raspberry.svg',
tags: [tTag.System, tTag.IoT],
interest: Level.L5,
mastery: Level.L4,
info: {
[Locales.EN]: 'Raspberry Pi is a set of low cost credit-card sized hardware that allows to run small linux. It features GPIO and bus interfacing with the outer world, widely used in IoT.<br><br>I\'ve been playing with Raspberries for a while, my first experience was for the <b>LogAuth</b> project. I built an embedded system that controls and monitors forklift trucks to manage access with RFID cards. Other than that I always have one somewhere close to built useless automation projects.',
[Locales.FR]: 'Raspberry Pi désigne un ensemble de cartes électroniques bon marché de la taille d\'une carte bancaire. Elles peuvent faire tourner des petits OS basés sur linux. Raspberry Pi fournit des interfaces d\'entrée/sortie et des bus de données qui permettent de s\'interfacer avec le "monde réel".<br><br>Je joue avec des Raspberry depuis un moment, ma première expérience fut pour le projet <b>LogAuth</b>. J\'ai construit un système embarqué qui contrôle et traque des transpalettes, et gère les accès par carte RFID. J\'ai toujours un Raspberry à proximité pour jouer avec et faire des automatisations inutiles.',
},
},
[tID.Arduino]: {
link: 'https://arduino.cc',
icon: 'skills/arduino.svg',
tags: [tTag.System, tTag.IoT],
interest: Level.L5,
mastery: Level.L4,
info: {
[Locales.EN]: 'Arduino is a set of low cost small hardware. It features GPIO and buses and is a minimal affordable choice to create small IoT devices.<br><br>I\'ve been playing with Arduinos for a while, my first experience was for the early stages (v1) of the <b>LogAuth</b> project. I built an embedded system that manages RFID card access and stores an access history. I always have an Arduino nearby to built small projects with WiFi or for outside automation.',
[Locales.FR]: 'Arduino désigne un ensemble de petites cartes électroniques bon marché. Les Arduinos fournissent des entrées/sorties et des bus, c\'est un choix abordable pour les objets connectés simples.<br><br>Je joue avec des Arduinos depuis un moment, ma première expérience fut dans les premières version du projet <b>LogAuth</b>. J\'ai construit un système embarqué qui gère des accès RFID (carte à puce) et enregistre l\'historique des accès. J\'en ai toujours un pas loin pour faire de petits projets connectés en WiFi ou en extérieur.',
},
},
[tID.Php]: {
link: 'https://www.php.net',
icon: 'skills/php.svg',
tags: [tTag.Language, tTag.Web, tTag.IoT],
interest: Level.L1,
mastery: Level.L4,
info: {
[Locales.EN]: 'Programming language mostly used for web servers. I started backend programming with it, I am now using Go as it is more maintainable and performant.',
[Locales.FR]: 'Langage de programmation souvent utilisé pour les serveur web. J\'ai commencé le backend avec le PHP et me suis redirigé vers le Go par souci de maintainabilité et de performances.',
},
},
[tID.Html]: {
link: 'https://www.w3.org/standards/webdesign/htmlcss',
icon: 'skills/html.svg',
tags: [tTag.Language, tTag.Web, tTag.UI],
interest: Level.L2,
mastery: Level.L4,
info: {
[Locales.EN]: 'Markup language to define web page structure.',
[Locales.FR]: 'Langage de markup permettant de définir la structure des pages web.',
},
},
[tID.Css]: {
link: 'https://www.w3.org/standards/webdesign/htmlcss',
icon: 'skills/css.svg',
tags: [tTag.Language, tTag.Web, tTag.UI],
interest: Level.L3,
mastery: Level.L5,
info: {
[Locales.EN]: 'Stylesheet language to define visuals and animation for the web.',
[Locales.FR]: 'Langage de feuilles de style permettant de définir les visuels et animations de sites web.',
},
},
[tID.Js]: {
link: 'http://www.ecma-international.org/publications-and-standards/standards/ecma-262/',
icon: 'skills/js.svg',
tags: [tTag.Language, tTag.Web, tTag.UI],
interest: Level.L2,
mastery: Level.L4,
info: {
[Locales.EN]: 'Programming language by default for web browsers. I used it a lot and replaced it with typescript which brings robustness to the base language.',
[Locales.FR]: 'Langage de programmation par défaut pour les navigateurs. Je l\'ai beaucoup utilisé et utilise maintenant le typescript qui apporte de la robustesse au langage de base.',
},
},
[tID.Ajax]: {
link: 'https://www.w3schools.com/xml/ajax_intro.asp',
icon: null,
tags: [tTag.Web, tTag.Network],
interest: Level.L2,
mastery: Level.L5,
info: {
[Locales.EN]: 'Web API that allows to download resources from a web page without reloading. It is now used almost everywhere, but at my "old days" it was more rare and we used to have reload between each page and static resources. Back then I created my well named "page-manager.js" library, it allows to load javascript, html, css, php, or even image files asynchronously and integrate them into your web page automatically. Now I still use it in web projects, but mainly through libraries.',
[Locales.FR]: 'API web qui permet le téléchargement HTTP depuis une page sans avoir à la recharger. C\'est maintenant monnaie courante, mais dans mes "premiers temps" dans le web c\'était plus rare, il était plus courant de changer souvent de page et d\'avoir des ressources fixes. A cette époque j\'ai créé ma librairie magnifiquement nommée "page-manager.js". Elle permettait de charger des fichiers javascript, html, css, php ou même des images de manière asynchrone et les intégrait automatiquement dans la page web. Maintenant j\'utilise toujours Ajax, mais souvent à travers des librairies qui font le travail à ma place.',
},
},
[tID.Ts]: {
link: 'https://www.typescriptlang.org/',
icon: 'skills/ts.svg',
tags: [tTag.Language, tTag.Web, tTag.UI],
interest: Level.L3,
mastery: Level.L4,
info: {
[Locales.EN]: 'Strongly typed language that (pre)compiles into javascript (javascript with types). It allows for your javascript codebase to be more consistent and maintainable. As these are 2 qualities that I strongly work to ensure, I use it for every javascript project and I\'ve never wrote "raw" javascript since then.',
[Locales.FR]: 'Langage à typage fort qui se (pré)compile en javascript (javascript avec des types). Il permet d\'avoir un code plus robuste et maintenable. Ces 2 points sont parmi ceux que je trouve les plus importants dans mon travail, c\'est pourquoi je n\'ai plus écris de javascript "brut" (sans typescript) depuis que typescript existe.',
},
},
[tID.C]: {
link: 'https://www.open-std.org/jtc1/sc22/wg14/',
icon: 'skills/c.svg',
tags: [tTag.Language, tTag.System],
interest: Level.L1,
mastery: Level.L4,
info: {
[Locales.EN]: 'The ultimate system\'s language. Used it a lot during my studies, I replaced it with Go.',
[Locales.FR]: 'Le langage système par excellence. Beaucoup utilisé pendant mes études, je l\'ai remplacé par le Go.',
},
},
[tID.Cpp]: {
link: 'https://isocpp.org/',
icon: 'skills/cpp.svg',
tags: [tTag.Language, tTag.System],
interest: Level.L1,
mastery: Level.L5,
info: {
[Locales.EN]: 'System programming language allowsing to avoid the quite raw C. Used for years, to write Qt applications.',
[Locales.FR]: 'Language de programmation système permettant d\'éviter le C parfois brut. Utilisé pendant plusieurs années, notamment avec Qt. ',
},
},
[tID.Python]: {
link: 'https://python.org/',
icon: 'skills/python.svg',
tags: [tTag.Language, tTag.System],
interest: Level.L1,
mastery: Level.L2,
info: {
[Locales.EN]: 'Python is a high level programming language often used as an introduction to programming because it is quite easy to learn. I didn\'t use python much except during my studies. I prefer Go because with a little experience it is as easy to write but it is lighter and more powerful.',
[Locales.FR]: 'Python est une langage de programmation haut niveau souvent utilisé comme introduction à la programmation car assez facile d\'accès. J\'ai peu utilisé le python à part pendant mes études. Je préfère le Go car avec un peu d\'expérience on a le même niveau de facilité à l\'écrire mais il est plus léger et performant.',
},
},
[tID.Go]: {
link: 'https://go.dev',
icon: 'skills/go.svg',
tags: [tTag.Language, tTag.System, tTag.IoT, tTag.Network],
interest: Level.L5,
mastery: Level.L5,
info: {
[Locales.EN]: 'Modern programming language. Offers an alternative to more traditional system languages such as C or C++. Allows rapid development, test automation, and provides good performance. Much used for web backend or microservices architectures.',
[Locales.FR]: 'Langage de programmation moderne. Offre une alternative aux langages systèmes plus classiques comme le C ou C++. Permet des développements rapides, l\'automatisation des tests, et ce, en fournissant de bonnes performances. Beaucoup utilisé pour des backend web ou des architectures micro services.',
},
},
[tID.Qt]: {
link: 'https://qt.io',
icon: 'skills/qt.svg',
interest: Level.L1,
mastery: Level.L4,
tags: [tTag.Language, tTag.System, tTag.IoT, tTag.Network],
info: {
[Locales.EN]: 'Multi-platform graphics framework. Allows to create applications compatible with Windows, Linux, iOS, macOS, Surface, etc.',
[Locales.FR]: 'Framework graphique multi-plateforme. Permet de créer des applications compatibles windows, Linux, iOS, macOS, Surface, etc.',
},
},
[tID.OpenSource]: {
link: 'https://opensource.org/',
icon: null,
tags: [tTag.Human],
interest: Level.L4,
mastery: Level.L3,
info: {
[Locales.EN]: 'The acronym Open Source refers to software whose code is made public and open to modification in an open and participatory manner. I develop most of my personal projects in Open Source if it can help other developers. I\'m convinced that Open Source allows to have better tools and libraries to make the computer science evolve globally.',
[Locales.FR]: 'Le sigle Open source désigne les logiciels dont le code est rendu public et ouvert à la modification de manière ouverte et participative. Je développe la plupart de mes projets personnels en Open Source si jamais ça peut aider d\'autres développeurs. Je suis convaincu que l\'Open Source permet d\'avoir de meilleurs outils et librairies pour globalement faire avancer l\'informatique.',
},
},
[tID.Electronics]: {
link: 'https://en.wikipedia.org/wiki/Electronics',
icon: 'skills/electronics.svg',
tags: [tTag.Other, tTag.IoT],
interest: Level.L3,
mastery: Level.L1,
info: {
[Locales.EN]: 'Knowledge and practices needed to understand and create electronic circuits. Often required for Arduino or Raspberry Pi projects.',
[Locales.FR]: 'Ensemble des connaissances et pratiques permettant la compréhension et création de circuits électroniques. Souvent requis pour les projets Arduino ou Raspberry Pi.',
},
},
[tID.Web]: {
link: 'https://en.wikipedia.org/wiki/World_Wide_Web',
icon: null,
tags: [tTag.Web],
interest: Level.L4,
mastery: Level.L5,
info: {
[Locales.EN]: 'Set of technologies around the Internet and websites: frontend and backend. From design to production.',
[Locales.FR]: 'Ensemble de technologies autour d\'Internet et des sites web: frontend et backend. De la conception jusqu\'à la mise en production.',
},
},
[tID.Rest]: {
link: 'https://en.wikipedia.org/wiki/Representational_state_transfer',
icon: null,
tags: [tTag.Web, tTag.Network],
interest: Level.L4,
mastery: Level.L5,
info: {
[Locales.EN]: 'The REST (REpresentational State Transfer) protocol is an architectural style and a communication mode frequently used in the development of Web services. It is known to be lightweight and easily understandable. I often use it for web projects.',
[Locales.FR]: 'Le protocole REST (REpresentational State Transfer) constitue un style architectural et un mode de communication fréquemment utilisé dans le développement de services Web. Il est notamment connu pour être léger et facilement compréhensible. Je l\'utilise souvent pour les projets web.',
},
},
[tID.Crypto]: {
link: 'https://en.wikipedia.org/wiki/Cryptography',
icon: 'skills/security.svg',
tags: [tTag.System, tTag.Network],
interest: Level.L3,
mastery: Level.L4,
info: {
[Locales.EN]: 'Cryptography designates a set of technologies allowing to secure (to make unreadable by an undesired third party) data, particularly targets network communications. The notion of certificate is also an important component that provides a means to ensure the confidentiality, authenticity and integrity of encrypted data.',
[Locales.FR]: 'La cryptographie désigne un ensemble de technologies permettant de sécuriser (rendre illisible par un tiers non souhaité) des données, concerne notamment les communications faites sur le réseau. La notion de certificat est aussi une composante importante qui fournit un moyen d\'assurer la confidentialité, l\'authenticité et l\'intégrité des données cryptées.',
},
},
[tID.ImageProcessing]: {
link: 'https://en.wikipedia.org/wiki/Digital_image_processing',
icon: 'skills/image-processing.svg',
tags: [tTag.System],
interest: Level.L4,
mastery: Level.L3,
info: {
[Locales.EN]: 'Set of technologies allowing the modification and the extraction of data from images. Whether it is a black & white filter or facial recognition.',
[Locales.FR]: 'Ensemble de technologies permettant la modification et l\'extraction de données des images. Que ce soit un filtre noir & blanc ou de la reconnaissance faciale.',
},
},
[tID.AI]: {
link: 'https://en.wikipedia.org/wiki/Artificial_intelligence',
icon: null,
tags: [tTag.Other],
interest: Level.L3,
mastery: Level.L1,
info: {
[Locales.EN]: 'Set of technologies for solving problems that are too complex for conventional programs. Includes soft intelligence technologies or deep learning (neural networks).',
[Locales.FR]: 'Ensemble de technologies permettant de résoudre des problèmes trop complexes pour les programmes classiques. Comprends les technologies à intelligence faibles ou le deep learning (réseaux de neurones).',
},
},
[tID.DeepLearning]: {
link: 'https://en.wikipedia.org/wiki/Deep_learning',
icon: null,
tags: [tTag.Other],
interest: Level.L3,
mastery: Level.L1,
info: {
[Locales.EN]: 'Strong artificial intelligence technologies, often via the use of neural networks.',
[Locales.FR]: 'Technologies d\'intelligence artificiell forte, souvent via l\'utilisation de réseaux de neurones et leurs dérivés.',
},
},
[tID.NeuralNetwork]: {
link: 'https://en.wikipedia.org/wiki/Artificial_neural_network',
icon: null,
tags: [tTag.Other],
interest: Level.L4,
mastery: Level.L1,
info: {
[Locales.EN]: 'Neural networks are a strong artificial intelligence technique. It consists in solving problems by learning, modelled on the functioning of neurons.',
[Locales.FR]: 'Les réseaux de neurones sont une technique d\'intelligence artificielle forte. Cela consiste à résoudre des problèmes par apprentissage, calqué sur le fonctionnement des neurones.',
},
},
[tID.Opti]: {
link: 'https://en.wikipedia.org/wiki/Program_optimization',
icon: 'skills/opti.svg',
tags: [tTag.System, tTag.Network],
interest: Level.L3,
mastery: Level.L4,
info: {
[Locales.EN]: 'Work of modifying a program to limit its CPU usage and memory footprint. Often aims to make the execution faster.',
[Locales.FR]: 'Travail de modification d\'un programme pour limiter son utilisation du processeur et son empreinte mémoire. A souvent pour but de rendre l\'exécution plus rapide.',
},
},
[tID.Sockets]: {
link: 'https://en.wikipedia.org/wiki/Computer_network_programming',
icon: null,
tags: [tTag.System, tTag.Network, tTag.IoT, tTag.Web],
interest: Level.L2,
mastery: Level.L4,
info: {
[Locales.EN]: 'Representation of network connections at the system level.',
[Locales.FR]: 'Représentation des connexions réseau au niveau système.',
},
},
[tID.Concurrency]: {
link: 'https://en.wikipedia.org/wiki/Concurrent_computing',
icon: null,
tags: [tTag.System, tTag.Network],
interest: Level.L2,
mastery: Level.L3,
info: {
[Locales.EN]: 'Consists in creating programs despite processes running simultaneously or that are partially synchronized.',
[Locales.FR]: 'Consiste à créer des programmes déterministe malgré l\'exécution de processus en simultané ou partiellement synchronisés.',
},
},
[tID.UIUX]: {
link: 'https://en.wikipedia.org/wiki/UX',
icon: null,
tags: [tTag.System, tTag.Web, tTag.IoT],
interest: Level.L2,
mastery: Level.L3,
info: {
[Locales.EN]: 'Design of graphics, visual identity, solution adapted to the users in order to propose intuitive interfaces.',
[Locales.FR]: 'Conception de graphismes, d\'identité visuelle, de solution adaptée aux utilisateurs afin de proposer des interfaces intuitives.',
},
},
[tID.Inkscape]: {
link: 'https://inkscape.org/',
icon: 'skills/inkscape.svg',
tags: [tTag.UI, tTag.Web, tTag.Organization],
interest: Level.L3,
mastery: Level.L5,
info: {
[Locales.EN]: 'Free open-source software to create and edit vector files, a must-have for templating and creating good visual assets. I\'ve used it for a several years. The numerous shortcuts and hidden features are no more a secret to me ;) I use it nearly every day be it for templating, or creating visual assets (logos, icons, images, backgrounds, animations, etc).',
[Locales.FR]: 'Logiciel open-source de création et d\'édition de fichiers vectoriels, un indispensable pour la création de maquettes et d\'assets graphiques. Je l\'utilise maintenant depuis plusieurs années. Les nombreux raccourcis et fonctionnalités cachées ne sont plus un secret pour moi ;) Je l\'utilise quasiment tout les jours que ce soit pour les maquettages, ou encore la création des ressources graphiques (images, logos, animations, fonds, etc).',
},
},
[tID.RnD]: {
link: 'https://en.wikipedia.org/wiki/R&D',
icon: null,
tags: [tTag.Organization, tTag.Other],
interest: Level.L5,
mastery: Level.L4,
info: {
[Locales.EN]: 'Research and Development. Design and development of prototypes or software before the industrialization phase.',
[Locales.FR]: 'Recherche et Développement. Conception et élaboration de prototypes ou de logiciel avant la phase d\'industrialisation.',
},
},
[tID.TeamLead]: {
link: 'https://en.wikipedia.org/wiki/Team_leader',
icon: null,
tags: [tTag.Organization, tTag.Human],
interest: Level.L1,
mastery: Level.L3,
info: {
[Locales.EN]: 'Management of a development team. Translate client needs into technical tasks for developers. Estimating and monitoring progress.',
[Locales.FR]: 'Gestion d\'une équipe de développement. Traduire les besoins client en tâches techniques pour les développeurs. Estimations, et suivi de l\'avancement.',
},
},
[tID.NeedsAnalysis]: {
link: '',
icon: null,
tags: [tTag.Organization, tTag.Human],
interest: Level.L2,
mastery: Level.L4,
info: {
[Locales.EN]: 'Analysis of the client\'s needs in order to provide a solution and a technical agenda',
[Locales.FR]: 'Analyse du besoin client afin de fournir une solution et un agenda technique.',
},
},
[tID.Grpc]: {
link: 'https://grpc.io/',
icon: 'skills/grpc.svg',
tags: [tTag.Network, tTag.System, tTag.Web],
interest: Level.L5,
mastery: Level.L4,
info: {
[Locales.EN]: 'gRPC is a high-performance, open-source universal RPC framework. I use it often for microservice architectures. It is a great alternative to REST for internal communications.',
[Locales.FR]: 'gRPC est un framework RPC universel open-source et performant. Je l\'utilise souvent pour des architectures microservices. C\'est une bonne alternative à REST pour les communications internes.',
},
},
[tID.Microservices]: {
link: 'https://en.wikipedia.org/wiki/Microservices',
icon: null,
tags: [tTag.System, tTag.Web, tTag.Organization],
interest: Level.L4,
mastery: Level.L3,
info: {
[Locales.EN]: 'Microservices is an architectural style that structures an application as a collection of services. I use it often for web projects. It allows to have a more maintainable and scalable architecture.',
[Locales.FR]: 'Microservices est un style architectural qui structure une application comme une collection de services. Je l\'utilise souvent pour les projets web. Cela permet d\'avoir une architecture plus maintenable et scalable.',
},
},
[tID.Kubernetes]: {
link: 'https://kubernetes.io/',
icon: 'skills/kubernetes.svg',
tags: [tTag.System, tTag.Web, tTag.Network, tTag.Organization],
interest: Level.L4,
mastery: Level.L3,
info: {
[Locales.EN]: 'Kubernetes is an open-source container-orchestration system for automating application deployment, scaling, and management. I use it often in microservices environments. It allows to have a more maintainable and scalable architecture.',
[Locales.FR]: 'Kubernetes est un système d\'orchestration de conteneurs open-source pour automatiser le déploiement, le dimensionnement et la gestion des applications. Je l\'utilise souvent dans les environnements de microservices. Cela permet d\'avoir une architecture plus maintenable et scalable.',
},
},
[tID.Scrum]: {
link: 'https://en.wikipedia.org/wiki/Scrum_(software_development)',
icon: 'skills/scrum.svg',
tags: [tTag.Organization, tTag.Human],
interest: Level.L2,
mastery: Level.L2,
info: {
[Locales.EN]: 'Scrum is a framework for agile project management. It allows to have a more maintainable and scalable architecture.',
[Locales.FR]: 'Scrum est un cadre de gestion de projet agile. Cela permet d\'avoir une architecture plus maintenable et scalable.',
},
},
[tID.LoRa]: {
link: 'https://lora-alliance.org/',
icon: 'skills/lora.svg',
tags: [tTag.Network, tTag.IoT],
interest: Level.L5,
mastery: Level.L2,
info: {
[Locales.EN]: 'LoRa is a long-range, low-power wireless protocol that is the de facto technology for Internet of Things (IoT) networks worldwide. It provides low-consumption communications for long distances.',
[Locales.FR]: 'LoRa est un protocole sans fil longue portée et basse consommation qui est la technologie de facto pour les réseaux Internet des objets (IoT) dans le monde entier. Il fournit des communications à faible consommation pour de longues distances.',
},
},
[tID.Architecture]: {
link: 'https://en.wikipedia.org/wiki/Software_architecture',
icon: 'skills/architecture.svg',
tags: [tTag.System, tTag.Organization],
interest: Level.L5,
mastery: Level.L4,
info: {
[Locales.EN]: 'Software architecture is the structure of a software system. It is a set of structures needed to reason about the software system, which comprises software elements, relations among them, and properties of both.',
[Locales.FR]: 'L\'architecture logicielle est la structure d\'un système logiciel. C\'est un ensemble de structures nécessaires pour raisonner sur le système logiciel, qui comprend des éléments logiciels, des relations entre eux et les propriétés des deux.',
},
},
[tID.Chirpstack]: {
link: 'https://www.chirpstack.io/',
icon: 'skills/chirpstack.svg',
tags: [tTag.Network, tTag.IoT],
interest: Level.L5,
mastery: Level.L3,
info: {
[Locales.EN]: 'ChirpStack is an open-source LoRaWAN Network Server stack, the most used in the world. It provides a complete LoRaWAN solution.',
[Locales.FR]: 'ChirpStack est une pile de serveurs de réseau LoRaWAN open-source, la plus utilisée au monde. Il fournit une solution LoRaWAN complète.',
},
},
[tID.Refactor]: {
link: 'https://en.wikipedia.org/wiki/Code_refactoring',
icon: 'skills/refactor.svg',
tags: [tTag.System, tTag.Organization],
interest: Level.L5,
mastery: Level.L5,
info: {
[Locales.EN]: 'Code refactoring is the process of restructuring existing computer code without changing its external behavior. Refactoring improves nonfunctional attributes of the software.',
[Locales.FR]: 'Le refactoring de code est le processus de restructuration du code informatique existant sans changer son comportement externe. Le refactoring améliore les attributs non fonctionnels du logiciel.',
},
},
[tID.Timescale]: {
link: 'https://www.timescale.com/',
icon: 'skills/timescale.svg',
tags: [tTag.System, tTag.Storage],
interest: Level.L4,
mastery: Level.L3,
info: {
[Locales.EN]: 'TimescaleDB is a time-series database built on top of PostgreSQL. It provides a complete time-series solution.',
[Locales.FR]: 'TimescaleDB est une base de données de séries temporelles construite sur PostgreSQL. Il fournit une solution de séries temporelles complète.',
},
},
[tID.Atlassian]: {
link: 'https://www.atlassian.com/',
icon: 'skills/atlassian.svg',
tags: [tTag.Organization],
interest: Level.L4,
mastery: Level.L4,
info: {
[Locales.EN]: 'Atlassian is a software company that develops products for software developers, project managers, and content management. I have used Jira and Confluence for project management.',
[Locales.FR]: 'Atlassian est une entreprise de logiciels qui développe des produits pour les développeurs de logiciels, les chefs de projet et la gestion de contenu. J\'ai utilisé Jira et Confluence pour la gestion de projet.',
},
},
};

20
src/model/themes.ts Normal file
View File

@ -0,0 +1,20 @@
export interface Theme {
name: string;
default: boolean;
}
export const Themes: Theme[] = [
{
name: 'black_white',
default: true,
}, {
name: 'blue',
default: false,
}, {
name: 'forest',
default: false,
}, {
name: 'glass',
default: false,
},
];

19
src/service/projects.ts Normal file
View File

@ -0,0 +1,19 @@
import { Projects, Project } from '@/model/projects';
import { tID } from '@/model/skills';
export function all(): Project[] {
return Projects;
}
// returns all projects featuring a specified skill. Keeping the order of the
// projects model
export function bySkill(skill: tID): Project[] {
const filtered: Project[] = [];
for ( const proj of Projects ) {
if ( proj.skills.indexOf(skill) >= 0 ) {
filtered.push(proj);
}
}
return filtered;
}

57
src/service/scroller.ts Normal file
View File

@ -0,0 +1,57 @@
let scroll_interval: number;
// scroll time in milliseconds
const SCROLL_MS = 500;
const SCROLL_MS_STEP = 5;
// go() scrolls to the element with the specified id with an optional offset.
// It achieves a smooth scrolling compared to the direct default one.
export function go(id: string, offset: number|undefined): void {
// find item with id
const item = document.querySelector(`#${id}`) as HTMLElement;
if ( item == null ) {
console.warn(`item with id '${id}' not found`);
return;
}
// get item absolute y coordinates
let targety = absY(item, 0);
if ( offset != null ) {
targety += offset;
}
clearInterval(scroll_interval);
const step = (targety - document.body.scrollTop) / (SCROLL_MS / SCROLL_MS_STEP);
scroll_interval = setInterval(
() => smooth_scroll(targety, step),
SCROLL_MS_STEP,
);
}
function absY(item: HTMLElement|null, children_y: number): number {
if ( item == null || item == document.body ) {
return children_y;
}
return absY(item.parentElement, children_y + item.offsetTop);
}
// incremental smooth scroll
function smooth_scroll(target: number, step: number) {
const cur = document.body.scrollTop;
const max = (document.body as any).scrollTopMax;
// moving up & reached top or target -> stop
const upLimit = ( step < 0 && (cur <= 0 || cur <= target) );
// moving down & reached bottom or target -> stop
const downLimit = ( step > 0 && (cur >= max || cur >= target) );
if ( upLimit || downLimit ) {
clearInterval(scroll_interval);
document.body.scroll(0, target);
return;
}
document.body.scroll(0, cur + step);
}

82
src/service/skills.ts Normal file
View File

@ -0,0 +1,82 @@
import { tID, Skills, tSkill, tTag, skillLabel } from '@/model/skills';
let availCache: tID[]|null = null;
let tagsCache: tTag[]|null = null;
// returns available skill ids.
export function available(): tID[] {
if ( availCache != null ) {
return availCache;
}
availCache = [];
for ( const sid in Skills ) {
const id = str2ID(sid);
if ( id == null ) {
continue;
}
availCache.push(id);
}
availCache.sort( (a: tID, b: tID) => {
const alevel = Skills[a].interest * 1.1 + Skills[a].mastery;
const blevel = Skills[b].interest * 1.1 + Skills[b].mastery;
return blevel - alevel;
});
return availCache;
}
// returns the list of ids filtered by a tag. Skills NOT featuring the provided
// tag are filtered out of the list.
export function filtered(tag: tTag): tID[] {
if ( availCache == null ) {
available();
}
const remain = [];
for ( const id of availCache! ) {
const skill = get(id);
if ( skill == null ) {
continue;
}
if ( skill.tags.indexOf(tag) >= 0 ) {
remain.push(id);
continue;
}
}
return remain;
}
// returns available tags used among skills
export function tags(): tTag[] {
if ( tagsCache != null ) {
return tagsCache;
}
tagsCache = [];
for ( const skill of Object.values(Skills) ) {
for ( const tag of skill.tags ) {
if ( tagsCache.indexOf(tag) < 0 ) {
tagsCache.push(tag);
}
}
}
return tagsCache;
}
// returns a skill from its id
export function get(id: tID): tSkill|null {
if ( Skills[id] === undefined ) {
return null;
}
return Skills[id];
}
// converts a string to a skill id
export function str2ID(str: string): tID|null {
const id = parseInt(str, 10) as tID;
if ( Skills[id] === undefined ) {
return null;
}
return id;
}

43
src/service/typewriter.ts Normal file
View File

@ -0,0 +1,43 @@
export class TypeWriter {
private target: HTMLElement;
private html: string|null = null;
constructor(target: HTMLElement) {
this.target = target;
}
/* initialize with copying and clearing the original text */
public init(hide: boolean = true) {
this.html = this.target.innerHTML;
this.target.innerHTML = '';
if ( hide ) {
this.target.style.display = 'none';
}
}
public animate(msDuration: number) {
if ( this.html == null ) {
console.warn('[typewriter] init must be called first ; doing it anyway');
this.init();
}
const size = this.html!.length;
const msLetter = msDuration / size;
this.target.style.display = 'block';
for ( let i = 0, l = this.html!.length ; i < l ; i++ ) {
setTimeout(
() => {
((x: number) => {
this.target.innerHTML = this.html!.substring(0, x + 1);
})(i);
},
i * msLetter);
}
}
}

5
src/service/url.ts Normal file
View File

@ -0,0 +1,5 @@
// sanitizes an url
export function sanitize(raw: string): string {
const invalid = /[\*\s\(\)]/g;
return encodeURIComponent(raw.replaceAll(invalid, '_'));
}

15
src/shims-tsx.d.ts vendored
View File

@ -1,13 +1,10 @@
import Vue, { VNode } from 'vue'; import Vue, { VNode } from 'vue';
declare global { declare global {
namespace JSX { namespace JSX {
// tslint:disable no-empty-interface // tslint:disable no-empty-interface
interface Element extends VNode {} interface Element extends VNode {}
// tslint:disable no-empty-interface // tslint:disable no-empty-interface
interface ElementClass extends Vue {} interface ElementClass extends Vue {}
interface IntrinsicElements { }
[elem: string]: any;
}
}
} }

4
src/shims-vue.d.ts vendored
View File

@ -1,4 +1,4 @@
declare module '*.vue' { declare module '*.vue' {
import Vue from 'vue'; import Vue from 'vue';
export default Vue; export default Vue;
} }

View File

@ -9,6 +9,7 @@
"experimentalDecorators": true, "experimentalDecorators": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"sourceMap": true, "sourceMap": true,
"baseUrl": ".", "baseUrl": ".",
"types": [ "types": [

3992
yarn.lock

File diff suppressed because it is too large Load Diff