Repository: hbollon/portfolio-vuejs
Branch: master
Commit: 174ec803c440
Files: 29
Total size: 40.4 KB
Directory structure:
gitextract_g9zataws/
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── .python-version
├── LICENSE
├── README.md
├── babel.config.js
├── data/
│ └── portfolio-vuejs_import.json
├── package.json
├── public/
│ ├── .htaccess
│ └── index.html
└── src/
├── App.vue
├── components/
│ ├── AnimateOnVisible.vue
│ ├── Description.vue
│ ├── Experience.vue
│ ├── ExperienceColumn.vue
│ ├── Footer.vue
│ ├── LandingPage.vue
│ ├── PersonnalCard.vue
│ ├── Photo.vue
│ ├── Presentation.vue
│ ├── Projects.vue
│ ├── Skills.vue
│ ├── SocialBar.vue
│ └── Title.vue
├── cosmic.js
├── main.js
└── styles/
├── animation.scss
├── constants.scss
└── global.scss
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [hbollon]
ko_fi: hugobollon
custom: ['paypal.me/hugobollon']
================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
================================================
FILE: .python-version
================================================
portfolio-vuejs
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Hugo Bollon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<h1 align="center">VueJS portfolio template</h1>
> Portfolio template using VueJs framework, CosmicJS API and Bootstrap
<p align="center"><strong> Live demo with my personnal data <a href="https://hugobollon.me">here</a> ! </strong></p>
<img align="center" src="doc/portfolio-vuejs_demo.gif" alt="portfolio-vuejs preview"></img>
---
## Table of Contents
- [Features](#features)
- [Setup](#project-setup)
- [Edit Content](#how-to-edit-content)
- [Author](#author)
- [Contributing](#-contributing)
- [License](#-license)
---
## Features
- Builded with VueJs framework ✨
- One page layout ✨
- Material design ✨
- Bootstrap 4.5 & SCSS ✨
- Responsive ✨
- Animated layout ✨
- Content managed with CosmicJS API ✨
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
## How to edit content
This template have a CMS support with CosmicJS. You can easily put your content there by creating and linking your CosmicJS account.
In order to customize your portfolio, you must create a CosmicJS account and a new fresh bucket on it. Once done, you must update credentials of the **.env** file.
To do this, you just have to modify the variables contained in the ENV file (API token, bucket slug and read key).
```env
# CosmicJS
VUE_APP_COSMICJS_TOKEN=" "
VUE_APP_COSMICJS_BUCKET_SLUG=" "
VUE_APP_COSMICJS_BUCKET_READ_KEY=" "
```
After that, you need to create the object type and data we will use. To do that properly and easily, you can import the json file, located [inside the data folder of this repo](https://github.com/hbollon/portfolio-vuejs/blob/master/data/portfolio-vuejs_import.json), by accessing import/export settings inside your bucket settings page.
Now, you can customize it with your data through CosmicJS interface!
## Author
👤 **Hugo Bollon**
* Github: [@hbollon](https://github.com/hbollon)
* LinkedIn: [@Hugo Bollon](https://www.linkedin.com/in/hugobollon/)
* Portfolio: [hugobollon.me](https://www.hugobollon.me)
## 🤝 Contributing
Contributions, issues and feature requests are welcome!<br />Feel free to check [issues page](https://github.com/hbollon/portfolio-vuejs/issues).
## Show your support
Give a ⭐️ if this project helped you!
## 📝 License
This project is under [MIT](https://github.com/hbollon/portfolio-vuesjs/blob/master/LICENSE) license.
================================================
FILE: babel.config.js
================================================
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
================================================
FILE: data/portfolio-vuejs_import.json
================================================
{"bucket":{"_id":"5f00bd777c62a200080e8faf","slug":"hugobollonme","title":"hugobollon.me","object_types":[{"title":"portfolio-contents","slug":"portfolio-contents","singular":"portfolio-content","metafields":[],"options":{"slug_field":1,"content_editor":1},"preview_link":"","priority_locale":null,"extensions":null,"order":2}],"objects":[{"_id":"5f011c5b90e9e00007825ea8","order":5,"slug":"experiences","title":"experiences","content":"","metafields":[{"children":null,"type":"text","title":"description","key":"description","id":"rab2gb3ikerouxtvpewn","value":"Professional and academic"},{"children":null,"type":"text","title":"title","key":"title","id":"drnw6zxqjxpxagtmntqc","value":"Experience"},{"children":null,"type":"json","title":"professional","key":"professional","id":"FtSwl0XI9C","value":[{"year":"01.2020 - 05.2020","title":"Urbalog LAET Project","content":"Digitizing the Urbalog board game on Android with the Nearby Connection API"},{"year":"07.2019","title":"Pfeiffer Vacuum SAS, Annecy","content":"Interim IT mission: implementation of a new Qlickview access management program + Laravel development"},{"year":"07.2018","title":"Pfeiffer Vacuum SAS, Annecy","content":"Interim IT mission: Management of SAP tables and inventory of sub-programs."}],"required":true},{"children":null,"type":"json","title":"academic","key":"academic","id":"b2a969sy7g","value":[{"year":"From September 2020","title":"Computer Science Master at USMB","content":"Specialized in artificial intelligence and collaborative systems"},{"year":"2017 - 2020","title":"IT License","content":"University Savoie Mont-Blanc, France"},{"year":"2013 - 2017","title":"Scientific Baccalaureate","content":"Lycée de l'Albanais at Rumilly, France"}],"required":true}],"bucket":"5f00bd777c62a200080e8faf","type_slug":"portfolio-contents","created_at":"2020-07-05T00:18:35.583Z","created_by":"5f00bd137c62a200080e8fae","modified_at":"2020-07-05T17:32:57.155Z","created":"2020-07-05T00:18:35.583Z","user_id":"5f00bd137c62a200080e8fae","options":{"content_editor":1,"slug_field":1},"status":"published","published_at":"2020-07-05T17:32:57.155Z","modified_by":"5f00bd137c62a200080e8fae","publish_at":null,"unpublish_at":null},{"_id":"5f01fd8350006f00083229f5","order":4,"slug":"description","title":"description","content":"","metafields":[{"children":null,"type":"text","title":"pres_second","key":"pres_second","id":"Igv3UdzOfX","value":"I always liked to get into projects using the skills that I did not have or were even spare pure programming like web design, 3D modeling ect... This allowed me to learn to be autonomous and to learn on my own, certainly not without difficulties far from it, but I always managed to overcome the obstacles and solve the problems I encountered !"},{"children":null,"type":"text","title":"pres_first","key":"pres_first","id":"d6tIa0Bibt","value":"I'm a Full-Stack Developer student. Actually, I have a Licence BAC+3 in IT and I just started a computer science Master specialized in artificial intelligence and collaborative systems. IT is not only my professional background. It is also and above all a passion that has grown since I was 16 years old."},{"children":null,"type":"text","title":"pres_title","key":"pres_title","id":"IqVCE1U7Qb","value":"Who am I ?"},{"children":null,"type":"text","title":"description","key":"description","id":"68UNnxOFq7","value":"Hope to know you after"},{"children":null,"type":"text","title":"title","key":"title","id":"OhyFWRR6Xn","value":"About Me"}],"bucket":"5f00bd777c62a200080e8faf","type_slug":"portfolio-contents","created_at":"2020-07-05T16:19:15.848Z","created_by":"5f00bd137c62a200080e8fae","modified_at":"2020-11-06T14:31:39.945Z","created":"2020-07-05T16:19:15.848Z","user_id":"5f00bd137c62a200080e8fae","options":{"content_editor":1,"slug_field":1},"status":"published","published_at":"2020-11-06T14:31:39.945Z","modified_by":"5f00bd137c62a200080e8fae","publish_at":null,"unpublish_at":null},{"_id":"5f01fee250006f00083229f7","order":3,"slug":"user-data","title":"user-data","content":"","metafields":[{"children":null,"type":"text","title":"name","key":"name","id":"OEkbevb9ch","value":"Hugo Bollon"},{"children":null,"type":"text","title":"status","key":"status","id":"NX8v723S0G","value":"Full-Stack Developer"},{"children":null,"type":"text","title":"email","key":"email","id":"2VhiCIosBh","value":"hugo.bollon@gmail.com"},{"children":null,"type":"text","title":"phone","key":"phone","id":"zSqJFpvOeE","value":"+33 6 51 99 80 78"},{"children":null,"type":"text","title":"city","key":"city","id":"eePnSPzpxY","value":"Chambéry, France"},{"children":null,"type":"text","title":"lang","key":"lang","id":"jh95I8Z7VB","value":"French, English"},{"children":null,"type":"text","title":"photo","key":"photo","id":"D4LfrSKbrc","value":"photo.jpg"}],"bucket":"5f00bd777c62a200080e8faf","type_slug":"portfolio-contents","created_at":"2020-07-05T16:25:06.834Z","created_by":"5f00bd137c62a200080e8fae","modified_at":"2020-07-05T20:33:36.208Z","created":"2020-07-05T16:25:06.834Z","user_id":"5f00bd137c62a200080e8fae","options":{"content_editor":1,"slug_field":1},"status":"published","published_at":"2020-07-05T20:33:36.208Z","modified_by":"5f00bd137c62a200080e8fae","publish_at":null,"unpublish_at":null},{"_id":"5f0204d6ea1bf90007ad680f","order":2,"slug":"links","title":"links","content":"","metafields":[{"children":null,"type":"text","title":"facebook","key":"facebook","id":"gLDrzR50ta","value":"https://www.facebook.com/hugo.bollon"},{"children":null,"type":"text","title":"instagram","key":"instagram","id":"KMi8FlOyrI","value":"https://www.instagram.com/_hbollon"},{"children":null,"type":"text","title":"linkedin","key":"linkedin","id":"qVc8Mh2S5A","value":"https://www.linkedin.com/in/hugobollon"},{"children":null,"type":"text","title":"github","key":"github","id":"NPDNo7uSc9","value":"https://github.com/hbollon"}],"bucket":"5f00bd777c62a200080e8faf","type_slug":"portfolio-contents","created_at":"2020-07-05T16:50:30.087Z","created_by":"5f00bd137c62a200080e8fae","modified_at":"2020-11-06T14:30:59.357Z","created":"2020-07-05T16:50:30.087Z","user_id":"5f00bd137c62a200080e8fae","options":{"content_editor":1,"slug_field":1},"status":"published","published_at":"2020-11-06T14:30:59.357Z","modified_by":"5f00bd137c62a200080e8fae","publish_at":null,"unpublish_at":null},{"_id":"5f02127c1b1b930007b5896c","order":1,"slug":"skills","title":"skills","content":"","metafields":[{"children":null,"type":"json","title":"items","key":"items","id":"IEBchE5swc","value":[{"title":"C","img":"C.png"},{"title":"C++","img":"cplusplus.png"},{"title":"C#","img":"Csharp.png"},{"title":"Java","img":"java.png"},{"title":"Unity","img":"unity.png"},{"title":"HTML","img":"html.png"},{"title":"CSS","img":"css.png"},{"title":"PHP","img":"php.png"},{"title":"Laravel","img":"laravel.png"},{"title":"VueJS","img":"vuejs.png"},{"title":"Android","img":"android.png"},{"title":"Linux","img":"linux.png"},{"title":"Git/Github","img":"Octocat.png"},{"title":"Go","img":"go.png"},{"title":"VPS/Web Hosting","img":"ovh.png"}]},{"children":null,"type":"text","title":"title","key":"title","id":"YtRf42YCsn","value":"Skills"},{"children":null,"type":"text","title":"description","key":"description","id":"QTsdJU1QcF","value":"Here is my armament"}],"bucket":"5f00bd777c62a200080e8faf","type_slug":"portfolio-contents","created_at":"2020-07-05T17:48:44.602Z","created_by":"5f00bd137c62a200080e8fae","modified_at":"2020-11-06T13:55:46.187Z","created":"2020-07-05T17:48:44.602Z","user_id":"5f00bd137c62a200080e8fae","options":{"content_editor":1,"slug_field":1},"status":"published","published_at":"2020-11-06T13:55:46.187Z","modified_by":"5f00bd137c62a200080e8fae","publish_at":null,"unpublish_at":null},{"_id":"5f023635b7e93b000741a246","order":0,"slug":"projects","title":"projects","content":"","metafields":[{"children":null,"type":"json","title":"items","key":"items","id":"nPhh7RXJaN","value":[{"title":"IG_Automation_Bot (Work in progress)","content":"Python script for Instagram automation using Instaloader. Used for auto following and DM. Easily configurable through Yaml config files!<br><br>Link : <a href='https://github.com/hbollon/IG_Automation_Bot'>https://github.com/hbollon/IG_Automation_Bot</a>","link":"https://github.com/hbollon/IG_Automation_Bot","image":"ig.gif","tag":"Software","color":"red","date":"2020-11-02"},{"title":"Go-edlib","content":"Golang string comparison and edit distance algorithms library, featuring : Levenshtein, LCS, Hamming, Damerau levenshtein (OSA and Adjacent transpositions algorithms), Jaro-Winkler, Cosine, etc.<br><br>Link : <a href='https://github.com/hbollon/go-edlib'>https://github.com/hbollon/go-edlib</a>","link":"https://github.com/hbollon/go-edlib","image":"edlib.jpg","tag":"Library","color":"blue","date":"2020-08-25"},{"title":"Gyro'Ball","content":"3D labyrinth game project for Android using Unity. Use device gyroscope.<br><br>Link : <a href='https://play.google.com/store/apps/details?id=com.BitsPlease.GyroBall&hl=fr&fbclid=IwAR0itwUAcbpAl3TgkFGiSrQz_CHTRpWjSXTQZqNGPESikdOJWZ6DIrk5qAA'>Google Play</a>","link":"https://play.google.com/store/apps/details?id=com.BitsPlease.GyroBall&hl=fr&fbclid=IwAR0itwUAcbpAl3TgkFGiSrQz_CHTRpWjSXTQZqNGPESikdOJWZ6DIrk5qAA","image":"gyroball.jpg","tag":"Video Game","color":"purple","date":"2020-07-28"},{"title":"GyroscopeControl","content":"Unity script used for smooth and customizable object rotation with gyroscope. It include initial calibration with offset, rotation speed, smoothing parameter editable in Unity inspector and debug overlay.<br><br>Link : <a href='https://github.com/hbollon/GyroscopeControl'>https://github.com/hbollon/GyroscopeControl</a>","link":"https://github.com/hbollon/GyroscopeControl","image":"","tag":"Script","color":"green","date":"2020-06-17"},{"title":"Urbalog","content":"Android adaptation of board game using Nearby Connections API and Java for the LAET<br><br>Link : <a href='https://github.com/hbollon/Urbalog'>https://github.com/hbollon/Urbalog</a>","link":"https://github.com/hbollon/Urbalog","image":"urba.jpg","tag":"Video Game","color":"purple","date":"2020-05-19"},{"title":"android-sqlite-toolbox","content":"Android java package designed to manage any sqlite database. Includes creation of the db and interactions with it (CRUD), import and export in several file formats (easily adaptable to any other format) and synchronization in http.<br><br>Link : <a href='https://github.com/hbollon/android-sqlite-toolbox'>https://github.com/hbollon/android-sqlite-toolbox</a>","link":"https://github.com/hbollon/android-sqlite-toolbox","image":"","tag":"Library","color":"blue","date":"2020-05-08"},{"title":"Le-Parrainage-Boursorama","content":"Website built with Laravel. Used to promote and manage sponsorships for a online bank. Hosted on personnal vps.<br><br>Link : <a href='https://le-parrainage-boursorama.fr'>https://le-parrainage-boursorama.fr</a>","link":"https://le-parrainage-boursorama.fr","image":"boursoweb.jpg","tag":"Website","color":"orange","date":"2019-09-15"}]},{"children":null,"type":"text","title":"title","key":"title","id":"Di7UyvvoeZ","value":"My projects"},{"children":null,"type":"text","title":"description","key":"description","id":"Cc6brPtb2p","value":"This is my story"}],"bucket":"5f00bd777c62a200080e8faf","type_slug":"portfolio-contents","created_at":"2020-07-05T20:21:09.882Z","created_by":"5f00bd137c62a200080e8fae","modified_at":"2020-11-09T10:16:41.986Z","created":"2020-07-05T20:21:09.882Z","user_id":"5f00bd137c62a200080e8fae","options":{"content_editor":1,"slug_field":1},"status":"published","published_at":"2020-11-09T10:16:41.986Z","modified_by":"5f00bd137c62a200080e8fae","publish_at":null,"unpublish_at":null}],"media":[]}}
================================================
FILE: package.json
================================================
{
"name": "portfolio-vuejs",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@cosmicjs/sdk": "^1.2.0",
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-brands-svg-icons": "^5.15.4",
"@fortawesome/free-solid-svg-icons": "^5.15.4",
"@fortawesome/vue-fontawesome": "^2.0.10",
"@growthbunker/vuetimeline": "^0.1.17",
"bootstrap": "^4.6.2",
"core-js": "^3.38.1",
"css-loader": "^5.2.7",
"vue": "^2.7.16",
"vue-carousel": "^0.18.0",
"vue-style-loader": "^4.1.3",
"vue2-animate": "^2.1.4"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.5.19",
"@vue/cli-plugin-eslint": "^4.5.19",
"@vue/cli-service": "^4.5.19",
"babel-eslint": "^10.1.0",
"bootstrap-vue": "^2.23.1",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^7.20.0",
"node-sass": "^5.0.0",
"sass-loader": "^10.5.2",
"style-loader": "^2.0.0",
"vue-template-compiler": "^2.7.16"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
================================================
FILE: public/.htaccess
================================================
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
================================================
FILE: public/index.html
================================================
<!DOCTYPE html>
<html lang="en" style="height: 100%;">
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-148902588-2"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'UA-148902588-2');
</script>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="description" content="Welcome to my personnal portfolio builded with VueJS !" />
<meta property="og:title" content="Hugo Bollon Portfolio" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://hugobollon.me/" />
<meta property="og:locale" content="en_US" />
<meta property="og:image" content="<%= BASE_URL %>og.png" />
<meta property="og:description" content="Welcome to my personnal portfolio builded with VueJS !" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>Hugo Bollon Portfolio</title>
</head>
<body style="height: 100%;">
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
================================================
FILE: src/App.vue
================================================
<template>
<transition name="fade" tag="div" class="wrapper" mode="out-in">
<div class="wrapper" v-if="isLoaded" id="app">
<LandingPage :user="user" />
<Description :user="user" :content="description" :links="links" />
<Experience :content="experiences" />
<Skills :content="skills" />
<Projects :content="projects" />
<Footer :user="user" :links="links" />
</div>
</transition>
</template>
<script>
import LandingPage from "./components/LandingPage.vue";
import Description from "./components/Description.vue";
import Experience from "./components/Experience.vue";
import Skills from "./components/Skills.vue";
import Projects from "./components/Projects.vue";
import Footer from "./components/Footer.vue";
import { cosmic } from "./cosmic.js";
export default {
name: "App",
components: {
LandingPage,
Description,
Experience,
Skills,
Projects,
Footer,
},
data: () => ({
isLoaded: false,
user: {},
description: {},
links: {},
experiences: {},
skills: {},
projects: {},
}),
methods: {
async fetchObject(slug) {
return await cosmic.objects.findOne({
type: slug,
slug: slug
}).props("slug,title,metadata")
.depth(1)
},
extractFirstObject(objects) {
if(objects.objects == null)
return void 0;
else
return objects.objects[0];
}
},
created() {
document.body.classList.add("loading");
Promise.all([
this.fetchObject('user-data'),
this.fetchObject('description'),
this.fetchObject('links'),
this.fetchObject('experiences'),
this.fetchObject('skills'),
this.fetchObject('projects')
]).then(([
user_data,
description,
links,
experiences,
skills,
projects
]) => {
this.user = {
name: user_data.object.metadata.name,
status: user_data.object.metadata.status,
email: user_data.object.metadata.email,
phone: user_data.object.metadata.phone,
city: user_data.object.metadata.city,
lang: user_data.object.metadata.lang,
photo: user_data.object.metadata.photo,
}
this.description = description
this.links = links
this.experiences = experiences
this.skills = skills
this.projects = projects
this.isLoaded = true;
this.$nextTick(() => document.body.classList.remove("loading"));
});
},
};
</script>
<style scoped lang="scss">
@import "@/styles/constants.scss";
#app {
font-family: Montserrat-Regular, serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
}
.wrapper {
height: 100%;
}
</style>
================================================
FILE: src/components/AnimateOnVisible.vue
================================================
<template>
<div>
<transition :name="name" :appear="appear">
<div v-if="isVisible" :style="{ animationDuration: `${duration}s`, transitionDuration: `${duration}s` }">
<slot></slot>
</div>
</transition>
</div>
</template>
<script>
let isPassiveSupported = false;
try {
const options = Object.defineProperty({}, 'passive', {
get: () => isPassiveSupported = true
});
window.addEventListener('test', null, options);
} catch (err) {
console.log("Passive not supported");
}
export default {
props: {
name: String,
appear: { type: Boolean, default: false },
offsetTop: { type: Number, default: 0 },
duration: { type: Number, default: 1 }
},
data: () => ({
isVisible: false
}),
methods: {
inViewport() {
const rect = this.$el.getBoundingClientRect()
return rect.top <= (window.innerHeight - this.offsetTop) && rect.left <= window.innerWidth
},
detectVisibility() {
this.isVisible = this.inViewport()
}
},
mounted() {
this.$nextTick(this.detectVisibility)
window.addEventListener('scroll', this.detectVisibility, isPassiveSupported ? { passive: true } : false)
window.addEventListener('resize', this.detectVisibility, isPassiveSupported ? { passive: true } : false)
window.addEventListener('orientationchange', this.detectVisibility, isPassiveSupported ? { passive: true } : false)
},
destroyed() {
window.removeEventListener('scroll', this.detectVisibility)
window.removeEventListener('resize', this.detectVisibility)
window.removeEventListener('orientationchange', this.detectVisibility)
}
}
</script>
================================================
FILE: src/components/Description.vue
================================================
<template>
<section id="about">
<AnimateOnVisible name="fadeDown" :duration="1">
<Title :title="content.object.metadata.title" :description="content.object.metadata.description" />
</AnimateOnVisible>
<AnimateOnVisible name="fadeRight" :duration="1">
<div class="section-content">
<div class="container-fluid">
<div class="row justify-content-center">
<Photo :user="user"/>
</div>
<div class="row">
<div class="col-md-7 mr-auto card-mobile">
<Presentation :content="content"/>
</div>
<div class="col-md-4 card-mobile">
<PersonnalCard :user="user" :links="links"/>
</div>
</div>
</div>
</div>
</AnimateOnVisible>
</section>
</template>
<script>
import Title from './Title.vue'
import PersonnalCard from './PersonnalCard.vue'
import Presentation from './Presentation.vue'
import Photo from './Photo.vue'
export default {
name: 'AboutMe',
props: ['user', 'content', 'links'],
components: {
Title,
PersonnalCard,
Presentation,
Photo
},
}
</script>
<style scoped lang="scss">
@import '@/styles/constants.scss';
#about {
background-color: lighten(map-get($colors, dark), 100%);
}
@media(min-width: #{map-get($breakpoints, medium)}) {
.section-content {
width: 80%;
margin: 0 auto;
}
}
@media(max-width: #{map-get($breakpoints, medium)}) {
.card-mobile {
text-align: center !important;
margin-top: 20px;
}
}
</style>
================================================
FILE: src/components/Experience.vue
================================================
<template>
<section id="experience">
<AnimateOnVisible name="fadeDown" :duration="1">
<Title
class="title"
:title="content.object.metadata.title"
:description="content.object.metadata.description"
/>
</AnimateOnVisible>
<AnimateOnVisible name="fadeUp" :duration="1">
<div class="container-fluid">
<div class="row">
<ExperienceColumn
:posts="content.object.metadata.academic"
title="Education"
class="col-12 col-md left"
/>
<ExperienceColumn
:posts="content.object.metadata.professional"
title="Professional"
class="col-12 col-md right"
/>
</div>
</div>
</AnimateOnVisible>
</section>
</template>
<script>
import Title from "./Title.vue";
import ExperienceColumn from "./ExperienceColumn.vue";
export default {
name: "Experience",
props: ["content"],
components: {
Title,
ExperienceColumn
}
};
</script>
<style scoped lang="scss">
@import "@/styles/constants.scss";
$linear: map-get($colors, dark);
#experience {
background-color: lighten(map-get($colors, primary), 5%);
}
.title {
color: map-get($colors, light);
}
.row {
padding-top: 20px;
text-align: center;
}
@media (min-width: #{map-get($breakpoints, small)}) {
.left {
text-align: right;
border-right: 2px solid $linear;
}
.right {
text-align: left;
}
}
@media (max-width: #{map-get($breakpoints, small)}) {
.right {
margin-top: 20px;
}
.left:before {
content : "";
position: absolute;
left : 20%;
bottom : 0;
height : 2px;
width : 60%; /* or 100px */
border-bottom:2px solid $linear;
}
}
/deep/ .text-wrapper {
&:after {
border-bottom: 1px solid map-get($colors, dark);
}
}
</style>
================================================
FILE: src/components/ExperienceColumn.vue
================================================
<template>
<div>
<h3 class="color-light">{{ title }}</h3>
<div class="resume-item" v-for="(post, index) in posts" :key="index">
<div class="year color-darker">{{ post.year }}</div>
<div class="resume-description">
<strong class="color-light" v-html="post.title"></strong>
</div>
<div class="color-darker" v-html="post.content"></div>
</div>
</div>
</template>
<script>
export default {
name: "ExperienceColumn",
props: ["posts", "title"],
};
</script>
<style scoped lang="scss">
@import "@/styles/constants.scss";
.resume-item {
margin-bottom: 25px;
p {
font-size: 1.5rem;
margin-top: 0;
}
.resume-description {
font-size: 1.7rem;
}
.year {
font-weight: 600;
margin-bottom: 5px;
}
}
.color-light {
color: map-get($colors, light);
}
.color-darker {
color: map-get($colors, dark);
}
</style>
================================================
FILE: src/components/Footer.vue
================================================
<template>
<footer class="footer">
<div>
<font-awesome-icon class="copyright-icon" icon="copyright"/>
2020 {{user.name}}
| Get this template <a href="https://github.com/hbollon/portfolio-vuejs" target="_blank">here</a> !
</div>
<SocialBar :links="links"/>
</footer>
</template>
<script>
import SocialBar from './SocialBar'
export default {
name: 'Footer',
props: ['user', 'links'],
components: {
SocialBar,
},
}
</script>
<style scoped lang="scss">
@import '@/styles/constants.scss';
$bg-footer: map-get($colors, primary) !default;
.footer {
padding: 10px 20px 10px 20px;
background-color: $bg-footer;
color: map-get($colors, light);
display: flex;
justify-content: space-between;
align-items: center;
}
.copyright-icon{
font-size: 1.5rem;
vertical-align: middle;
}
/deep/ .social-wrap {
float: right;
.icon {
font-size: 2.8rem;
&:hover {
color: map-get($colors, light);
}
}
}
/deep/ li{
margin-bottom: 0 !important;
}
</style>
================================================
FILE: src/components/LandingPage.vue
================================================
<template>
<header class="header parallax">
<div class="name">
<div class="wrapper-name">
<AnimateOnVisible name="fadeDown" ::duration="1">
<h1>{{ user.name }}</h1>
</AnimateOnVisible>
<hr />
<AnimateOnVisible name="fadeUp" ::duration="1">
<p>{{ user.status }}</p>
</AnimateOnVisible>
</div>
</div>
</header>
</template>
<script>
export default {
name: "LandingPage",
props: ["user"]
};
</script>
<style scoped lang="scss">
@import "@/styles/constants.scss";
.header {
padding: 20px;
background-color: map-get($colors, dark);
}
.parallax {
background-image: url("../assets/img/bg.jpg");
background-attachment: fixed;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
height: 100%;
}
.name {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
margin: 0 auto;
z-index: 400;
.wrapper-name {
width: 250px;
}
h1 {
font-size: 2.3rem;
padding: 2px 10px;
text-align: center;
text-transform: uppercase;
color: whitesmoke;
}
p {
font-size: 1.5rem;
text-align: center;
margin: 5px auto;
color: whitesmoke;
}
hr {
border: 1px solid whitesmoke;
}
}
@media (min-width: #{map-get($breakpoints, small)}) {
.name {
.wrapper-name {
width: 55%;
}
h1 {
font-size: 2.8rem;
padding: 4% 8%;
}
p {
font-size: 2rem;
}
}
}
@media (min-width: #{map-get($breakpoints, medium)}) {
.name {
.wrapper-name {
width: 450px;
}
h1 {
font-size: 4rem;
padding: 4% 10%;
}
p {
font-size: 2.5rem;
}
}
}
@media only screen and (max-device-width: 1024px) {
.parallax {
background-attachment: scroll;
}
}
</style>
================================================
FILE: src/components/PersonnalCard.vue
================================================
<template>
<div>
<h3>{{ user.name }}</h3>
<div class="data"><strong>E-mail:</strong> {{ user.email }}</div>
<div class="data"><strong>Phone:</strong> {{ user.phone }}</div>
<div class="data"><strong>City:</strong> {{ user.city }}</div>
<div class="data"><strong>Languages:</strong> {{ user.lang }}</div>
<SocialBar :links="links"/>
</div>
</template>
<script>
import SocialBar from './SocialBar.vue'
export default {
name: 'PersonnalCard',
props: ['user', 'links'],
components: {
SocialBar,
}
}
</script>
<style scoped lang="scss">
@import '@/styles/constants.scss';
.data {
margin-bottom: 15px;
}
</style>
================================================
FILE: src/components/Photo.vue
================================================
<template>
<div>
<div class="photo">
<a>
<img :src="getImgUrl(user.photo)" alt="photo" />
</a>
</div>
</div>
</template>
<script>
export default {
name: "Photo",
props: ["user"],
methods: {
getImgUrl(img) {
if (img == undefined || img == "") return "";
else if (/^https:\/\/cdn\.cosmicjs\.com\/.+\.(jpg|png|gif)$/.test(img))
return img;
return require("../assets/img/projects/" + img);
},
},
};
</script>
<style scoped lang="scss">
.photo {
a:focus {
outline: none;
}
img {
width: 200px;
height: 200px;
border-radius: 50%;
display: inline-block;
}
}
img {
max-width: 100%;
}
</style>
================================================
FILE: src/components/Presentation.vue
================================================
<template>
<div class="paragraph">
<h3>{{content.object.metadata.pres_title}}</h3>
<div class="begin">{{content.object.metadata.pres_first}}</div>
<p>{{content.object.metadata.pres_second}}</p>
</div>
</template>
<script>
export default {
name: 'Description',
props: ['content']
}
</script>
<style scoped lang="scss">
@import '@/styles/constants.scss';
.paragraph {
color: map-get($colors, primary);
.begin {
color: map-get($colors, secondary);
}
}
</style>
================================================
FILE: src/components/Projects.vue
================================================
<template>
<section id="projects">
<AnimateOnVisible name="fadeDown" :duration="1">
<Title
class="title"
:title="content.object.metadata.title"
:description="content.object.metadata.description"
/>
</AnimateOnVisible>
<div class="container-fluid center-block">
<article class="content text-center">
<AnimateOnVisible class="timeline mx-auto" v-for="(post, index) in content.object.metadata.items" :key="index" name="fadeLeft" :duration="0.5">
<vue-timeline-update
:date="new Date(post.date)"
:title="post.title"
:description="post.content"
:thumbnail="getImgUrl(post.image)"
:color="post.color"
:category="post.tag"
icon="code"
/>
</AnimateOnVisible>
</article>
</div>
</section>
</template>
<script>
import Title from "./Title.vue";
export default {
name: "Projects",
props: ['content'],
components: {
Title
},
methods: {
getImgUrl(img) {
if(img == undefined || img == "")
return ""
else if(/^https:\/\/cdn\.cosmicjs\.com\/.+\.(jpg|png|gif)$/.test(img))
return img
return require('../assets/img/projects/'+img)
},
},
};
</script>
<style scoped lang="scss">
@import "@/styles/constants.scss";
$linear: map-get($colors, dark);
#projects {
background-color: lighten(map-get($colors, primary), 5%);
}
.title {
color: map-get($colors, light);
}
/deep/ .text-wrapper {
&:after {
border-bottom: 1px solid map-get($colors, dark);
}
}
article .inner {
position: relative;
display: inline-block;
vertical-align: middle;
z-index: 1;
}
.content {
color: map-get($colors, light);
margin-top: 30px;
header {
height: 100%;
width: 70%;
}
h1{
font-size: 3rem;
}
}
.vertical-center {
display: flex;
align-items: center;
}
h1 {
margin-top: 10px;
margin-bottom: 20px;
}
</style>
================================================
FILE: src/components/Skills.vue
================================================
<template>
<section id="skills">
<AnimateOnVisible name="fadeDown" :duration="1">
<Title :title="content.object.metadata.title" :description="content.object.metadata.description" />
</AnimateOnVisible>
<div class="section-content">
<div class="container-fluid">
<div class="row d-flex flex-wrap align-items-center">
<div class="col-md-2 m-auto pb-4" v-for="(post, index) in this.content.object.metadata.items" :key="index">
<AnimateOnVisible name="bounce">
<img id="imgLogo" class="img-responsive mx-auto d-block" :src="getImgUrl(post.img)" :alt="post.title"/>
<div id="divAlt" class="altCaption text-center">{{post.title}}</div>
</AnimateOnVisible>
</div>
</div>
</div>
</div>
</section>
</template>
<script>
import Title from './Title.vue'
export default {
name: 'Skills',
components: {
Title,
},
props: ['content'],
methods: {
getImgUrl(img) {
return require('../assets/img/logo/'+img);
},
},
}
</script>
<style scoped lang="scss">
@import '@/styles/constants.scss';
#skills {
background-color: lighten(map-get($colors, dark), 100%);
}
@media(min-width: #{map-get($breakpoints, medium)}) {
.section-content {
width: 80%;
margin: 0 auto;
}
}
img{
max-width: 120px;
}
.altCaption{
color: map-get($colors, secondary);
margin-top: 1rem;
}
</style>
================================================
FILE: src/components/SocialBar.vue
================================================
<template>
<div class="social-wrap">
<ul>
<li>
<a :href="links.object.metadata.facebook" target="_blank">
<font-awesome-icon class="icon" :icon="['fab', 'facebook-square']" />
</a>
</li>
<li>
<a :href="links.object.metadata.instagram" target="_blank">
<font-awesome-icon class="icon" :icon="['fab', 'instagram-square']" />
</a>
</li>
<li>
<a :href="links.object.metadata.linkedin" target="_blank">
<font-awesome-icon class="icon" :icon="['fab', 'linkedin']" />
</a>
</li>
<li>
<a :href="links.object.metadata.github" target="_blank">
<font-awesome-icon class="icon" :icon="['fab', 'github-square']" />
</a>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'SocialBar',
props: ['links'],
}
</script>
<style scoped lang="scss">
@import '@/styles/constants.scss';
.social-wrap {
li {
display: inline-block;
margin-right: 10px;
}
.icon {
font-size: 3rem;
}
}
</style>
================================================
FILE: src/components/Title.vue
================================================
<template>
<div class="title">
<h2>{{ title }}</h2>
<div class="wrapper">
<div class="text-wrapper">{{ description }}</div>
</div>
</div>
</template>
<script>
export default {
name: 'Title',
props: [
'title',
'description'
]
}
</script>
<style scoped lang="scss">
@import '@/styles/constants.scss';
.title {
text-align: center;
color: map-get($colors, primary);
h2 {
font-size: 4rem;
text-transform: uppercase;
}
}
.text-wrapper {
text-transform: uppercase;
&:after {
content: "";
width: 150px;
display: block;
margin: 20px auto;
border-bottom: 1px solid map-get($colors, primary);
}
}
</style>
================================================
FILE: src/cosmic.js
================================================
import { createBucketClient } from '@cosmicjs/sdk';
export const cosmic = createBucketClient({
bucketSlug: process.env.VUE_APP_COSMICJS_BUCKET_SLUG,
readKey: process.env.VUE_APP_COSMICJS_BUCKET_READ_KEY,
});
================================================
FILE: src/main.js
================================================
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue';
import App from './App.vue'
import AnimateOnVisible from "./components/AnimateOnVisible.vue"
Vue.use(BootstrapVue)
import VueTimeline from "@growthbunker/vuetimeline";
Vue.use(VueTimeline);
import { library } from '@fortawesome/fontawesome-svg-core'
import { faCopyright} from '@fortawesome/free-solid-svg-icons'
import { faFacebookSquare, faInstagramSquare, faLinkedin, faGithubSquare } from '@fortawesome/free-brands-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
library.add(faCopyright, faFacebookSquare, faInstagramSquare, faLinkedin, faGithubSquare)
Vue.component('font-awesome-icon', FontAwesomeIcon)
Vue.component('AnimateOnVisible', AnimateOnVisible)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
import './styles/global.scss'
================================================
FILE: src/styles/animation.scss
================================================
$animationDuration: 1s !default;
@import "~vue2-animate/src/sass/vue2-animate.scss";
================================================
FILE: src/styles/constants.scss
================================================
$colors: (
primary: #353535,
secondary: #3C6E71,
complementary: #284B63,
light: whitesmoke,
dark: #D9D9D9,
error: #ff2818,
);
$breakpoints: (
small: 767px,
medium: 992px,
large: 1200px,
);
@font-face {
font-family: "Montserrat-Regular";
src: url("~@/assets/fonts/Montserrat-Medium.ttf");
}
================================================
FILE: src/styles/global.scss
================================================
@import './constants';
@import './animation.scss';
body,
html {
height: 100%;
font-size: 10px;
}
body {
font-size: 14px;
min-width: 320px;
position: relative;
line-height: 1.4;
font-weight: normal;
color: lighten(map-get($colors, primary), 100%);
&.loading {
overflow: scroll;
}
}
section {
padding: 50px 0;
overflow: hidden;
h3 {
text-transform: uppercase;
font-weight: 600;
margin-bottom: 30px;
}
}
ul {
margin: 0;
padding: 0;
list-style-type: none;
}
a {
color: map-get($colors, secondary);
&:hover {
color: map-get($colors, dark);
text-decoration: none;
}
}
li {
margin-bottom: 10px;
}
.timeline {
@media (max-width: #{map-get($breakpoints, medium)}) {
width: 100%;
}
@media (min-width: #{map-get($breakpoints, medium)}) {
width: 60%
}
}
[class^="gb-"]:not(.gb-base-icon) {
font-family: Montserrat-Regular, Helvetica, sans-serif !important;
}
gitextract_g9zataws/
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── .python-version
├── LICENSE
├── README.md
├── babel.config.js
├── data/
│ └── portfolio-vuejs_import.json
├── package.json
├── public/
│ ├── .htaccess
│ └── index.html
└── src/
├── App.vue
├── components/
│ ├── AnimateOnVisible.vue
│ ├── Description.vue
│ ├── Experience.vue
│ ├── ExperienceColumn.vue
│ ├── Footer.vue
│ ├── LandingPage.vue
│ ├── PersonnalCard.vue
│ ├── Photo.vue
│ ├── Presentation.vue
│ ├── Projects.vue
│ ├── Skills.vue
│ ├── SocialBar.vue
│ └── Title.vue
├── cosmic.js
├── main.js
└── styles/
├── animation.scss
├── constants.scss
└── global.scss
Condensed preview — 29 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (46K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 116,
"preview": "# These are supported funding model platforms\n\ngithub: [hbollon]\nko_fi: hugobollon\ncustom: ['paypal.me/hugobollon']\n"
},
{
"path": ".gitignore",
"chars": 230,
"preview": ".DS_Store\nnode_modules\n/dist\n\n# local env files\n.env.local\n.env.*.local\n\n# Log files\nnpm-debug.log*\nyarn-debug.log*\nyarn"
},
{
"path": ".python-version",
"chars": 16,
"preview": "portfolio-vuejs\n"
},
{
"path": "LICENSE",
"chars": 1068,
"preview": "MIT License\n\nCopyright (c) 2020 Hugo Bollon\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
},
{
"path": "README.md",
"chars": 2534,
"preview": "<h1 align=\"center\">VueJS portfolio template</h1>\n\n> Portfolio template using VueJs framework, CosmicJS API and Bootstrap"
},
{
"path": "babel.config.js",
"chars": 73,
"preview": "module.exports = {\n presets: [\n '@vue/cli-plugin-babel/preset'\n ]\n}\n"
},
{
"path": "data/portfolio-vuejs_import.json",
"chars": 11760,
"preview": "{\"bucket\":{\"_id\":\"5f00bd777c62a200080e8faf\",\"slug\":\"hugobollonme\",\"title\":\"hugobollon.me\",\"object_types\":[{\"title\":\"port"
},
{
"path": "package.json",
"chars": 1616,
"preview": "{\n \"name\": \"portfolio-vuejs\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"serve\": \"vue-cli"
},
{
"path": "public/.htaccess",
"chars": 782,
"preview": "<IfModule mod_rewrite.c>\n <IfModule mod_negotiation.c>\n Options -MultiViews -Indexes\n </IfModule>\n\n Rewr"
},
{
"path": "public/index.html",
"chars": 1407,
"preview": "<!DOCTYPE html>\n<html lang=\"en\" style=\"height: 100%;\">\n\n<head>\n\t<!-- Global site tag (gtag.js) - Google Analytics -->\n\t<"
},
{
"path": "src/App.vue",
"chars": 2730,
"preview": "<template>\n <transition name=\"fade\" tag=\"div\" class=\"wrapper\" mode=\"out-in\">\n <div class=\"wrapper\" v-if=\"isLoaded\" i"
},
{
"path": "src/components/AnimateOnVisible.vue",
"chars": 1646,
"preview": "<template>\n <div>\n <transition :name=\"name\" :appear=\"appear\">\n <div v-if=\"isVisible\" :style=\"{ animationDuratio"
},
{
"path": "src/components/Description.vue",
"chars": 1901,
"preview": "<template>\n <section id=\"about\">\n <AnimateOnVisible name=\"fadeDown\" :duration=\"1\">\n <Title :title=\""
},
{
"path": "src/components/Experience.vue",
"chars": 1755,
"preview": "<template>\n <section id=\"experience\">\n <AnimateOnVisible name=\"fadeDown\" :duration=\"1\">\n <Title\n class=\""
},
{
"path": "src/components/ExperienceColumn.vue",
"chars": 883,
"preview": "<template>\n <div>\n <h3 class=\"color-light\">{{ title }}</h3>\n <div class=\"resume-item\" v-for=\"(post, index) in pos"
},
{
"path": "src/components/Footer.vue",
"chars": 1089,
"preview": "<template>\n <footer class=\"footer\">\n <div>\n <font-awesome-icon class=\"copyright-icon\" icon=\"copyright\"/> \n "
},
{
"path": "src/components/LandingPage.vue",
"chars": 1840,
"preview": " <template>\n <header class=\"header parallax\">\n <div class=\"name\">\n <div class=\"wrapper-name\">\n <AnimateO"
},
{
"path": "src/components/PersonnalCard.vue",
"chars": 682,
"preview": "<template>\n <div>\n <h3>{{ user.name }}</h3>\n <div class=\"data\"><strong>E-mail:</strong> {{ user.email }}</div>\n "
},
{
"path": "src/components/Photo.vue",
"chars": 690,
"preview": "<template>\n <div>\n <div class=\"photo\">\n <a>\n <img :src=\"getImgUrl(user.photo)\" alt=\"photo\" />\n </a>"
},
{
"path": "src/components/Presentation.vue",
"chars": 533,
"preview": "<template>\n <div class=\"paragraph\">\n <h3>{{content.object.metadata.pres_title}}</h3>\n <div class=\"begin\">{{conten"
},
{
"path": "src/components/Projects.vue",
"chars": 1935,
"preview": "<template>\n <section id=\"projects\">\n <AnimateOnVisible name=\"fadeDown\" :duration=\"1\">\n\t\t<Title\n\t\tclass=\"title\"\n\t\t:ti"
},
{
"path": "src/components/Skills.vue",
"chars": 1727,
"preview": "<template>\n <section id=\"skills\">\n <AnimateOnVisible name=\"fadeDown\" :duration=\"1\">\n <Title :title="
},
{
"path": "src/components/SocialBar.vue",
"chars": 1108,
"preview": " <template>\n <div class=\"social-wrap\">\n <ul>\n <li>\n <a :href=\"links.object.metadata.facebook\" target=\"_b"
},
{
"path": "src/components/Title.vue",
"chars": 740,
"preview": "<template>\n <div class=\"title\">\n <h2>{{ title }}</h2>\n <div class=\"wrapper\">\n <div class=\"text-wrapp"
},
{
"path": "src/cosmic.js",
"chars": 213,
"preview": "import { createBucketClient } from '@cosmicjs/sdk';\n\nexport const cosmic = createBucketClient({\n bucketSlug: process.en"
},
{
"path": "src/main.js",
"chars": 962,
"preview": "import Vue from 'vue'\nimport BootstrapVue from 'bootstrap-vue';\nimport App from './App.vue'\n\nimport AnimateOnVisible fro"
},
{
"path": "src/styles/animation.scss",
"chars": 85,
"preview": "$animationDuration: 1s !default;\n\n@import \"~vue2-animate/src/sass/vue2-animate.scss\";"
},
{
"path": "src/styles/constants.scss",
"chars": 317,
"preview": "$colors: (\n primary: #353535,\n secondary: #3C6E71,\n complementary: #284B63,\n light: whitesmoke,\n dark: #D9D9D9,\n e"
},
{
"path": "src/styles/global.scss",
"chars": 944,
"preview": "@import './constants';\n@import './animation.scss';\n\nbody,\nhtml {\n height: 100%;\n font-size: 10px;\n}\n\nbody {\n font-siz"
}
]
About this extraction
This page contains the full source code of the hbollon/portfolio-vuejs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 29 files (40.4 KB), approximately 12.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.