mirror of
https://github.com/morten-olsen/morten-olsen.github.io.git
synced 2026-02-08 01:46:28 +01:00
Compare commits
8 Commits
v1.1175332
...
v1.1180082
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
695b69c225 | ||
|
|
5467979563 | ||
|
|
42952604e3 | ||
|
|
4606cbec9c | ||
|
|
52b2572949 | ||
|
|
5f3017c7cf | ||
|
|
84f4de421f | ||
|
|
337cc4e71b |
2
.github/workflows/build-latex.yml
vendored
2
.github/workflows/build-latex.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
|||||||
node-version: '12'
|
node-version: '12'
|
||||||
|
|
||||||
- name: Build READM.md
|
- name: Build READM.md
|
||||||
run: node readme/index.js > README.md
|
run: node scripts/update-readme.js > README.md
|
||||||
|
|
||||||
- uses: EndBug/add-and-commit@v7
|
- uses: EndBug/add-and-commit@v7
|
||||||
with:
|
with:
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -2,3 +2,8 @@
|
|||||||
/*.log
|
/*.log
|
||||||
/out
|
/out
|
||||||
/.next
|
/.next
|
||||||
|
/latex/**/*.aux
|
||||||
|
/latex/**/*.log
|
||||||
|
/latex/**/*.in
|
||||||
|
/latex/**/*.pdf
|
||||||
|
/latex/_markdown_*
|
||||||
|
|||||||
@@ -1,131 +1,103 @@
|
|||||||
// https://redstapler.co/cool-nebula-background-effect-three-js/
|
// https://redstapler.co/cool-nebula-background-effect-three-js/
|
||||||
|
|
||||||
|
// https://redstapler.co/cool-nebula-background-effect-three-js/
|
||||||
|
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { Canvas, useFrame, useThree } from '@react-three/fiber';
|
import React, { useEffect } from 'react';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
interface CloudProps {
|
const setup = () => {
|
||||||
texture: THREE.Texture;
|
let scene, camera, renderer;
|
||||||
position: [number, number, number];
|
let cloudParticles = [];
|
||||||
rotation: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
z: number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialClouds = new Array(30).fill(undefined).map((_, i) => ({
|
function init() {
|
||||||
name: `id_${i}`,
|
scene = new THREE.Scene();
|
||||||
position: [
|
camera = new THREE.PerspectiveCamera(60,window.innerWidth / window.innerHeight,1,1000);
|
||||||
Math.random() * 800 - 400,
|
camera.position.z = 1;
|
||||||
|
camera.rotation.x = 1.16;
|
||||||
|
camera.rotation.y = -0.12;
|
||||||
|
camera.rotation.z = 0.27;
|
||||||
|
let ambient = new THREE.AmbientLight(0x555555);
|
||||||
|
scene.add(ambient);
|
||||||
|
renderer = new THREE.WebGLRenderer();
|
||||||
|
renderer.setSize(window.innerWidth,window.innerHeight);
|
||||||
|
scene.fog = new THREE.FogExp2(0x03544e, 0.001);
|
||||||
|
renderer.setClearColor(scene.fog.color);
|
||||||
|
renderer.domElement.style.position = 'fixed';
|
||||||
|
renderer.domElement.style.top = '0';
|
||||||
|
renderer.domElement.style.left = '0';
|
||||||
|
renderer.domElement.style.width = '100%';
|
||||||
|
renderer.domElement.style.height = '100%';
|
||||||
|
renderer.domElement.style.zIndex = -1;
|
||||||
|
renderer.domElement.style.animationName = 'fadein';
|
||||||
|
renderer.domElement.style.animationDuration = '2s';
|
||||||
|
|
||||||
|
document.body.appendChild(renderer.domElement);
|
||||||
|
addParticles();
|
||||||
|
addLights();
|
||||||
|
render();
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
//renderer.domElement.style.opacity = 1;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addParticles = () => {
|
||||||
|
let loader = new THREE.TextureLoader();
|
||||||
|
loader.load("/images/smoke.png", (texture) => {
|
||||||
|
const cloudGeo = new THREE.PlaneBufferGeometry(500,500);
|
||||||
|
const cloudMaterial = new THREE.MeshLambertMaterial({
|
||||||
|
map:texture,
|
||||||
|
transparent: true
|
||||||
|
});
|
||||||
|
for(let p=0; p<50; p++) {
|
||||||
|
let cloud = new THREE.Mesh(cloudGeo, cloudMaterial);
|
||||||
|
cloud.position.set(
|
||||||
|
Math.random()*800 -400,
|
||||||
500,
|
500,
|
||||||
Math.random() * 500 - 500,
|
Math.random()*500-500
|
||||||
] as [number, number, number],
|
|
||||||
rotation: {
|
|
||||||
x: 1.16,
|
|
||||||
y: -0.12,
|
|
||||||
z: Math.random() * 2 * Math.PI,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const Cloud: React.FC<CloudProps> = ({ texture, position, rotation }) => {
|
|
||||||
return (
|
|
||||||
<mesh position={position} rotation={[ rotation.x, rotation.y, rotation.z ]}>
|
|
||||||
<planeBufferGeometry args={[500, 500]} />
|
|
||||||
<meshLambertMaterial opacity={.5} map={texture} transparent={true} />
|
|
||||||
</mesh>
|
|
||||||
);
|
);
|
||||||
|
cloud.rotation.x = 1.16;
|
||||||
|
cloud.rotation.y = -0.12;
|
||||||
|
cloud.rotation.z = Math.random()*2*Math.PI;
|
||||||
|
cloud.material.opacity = 0.55;
|
||||||
|
cloudParticles.push(cloud);
|
||||||
|
scene.add(cloud);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const addLights = () => {
|
||||||
|
let directionalLight = new THREE.DirectionalLight(0xff8c19);
|
||||||
|
directionalLight.position.set(0,0,1);
|
||||||
|
scene.add(directionalLight);
|
||||||
|
|
||||||
|
let orangeLight = new THREE.PointLight(0xcc6600,50,450,1.7);
|
||||||
|
orangeLight.position.set(200,300,100);
|
||||||
|
scene.add(orangeLight);
|
||||||
|
let redLight = new THREE.PointLight(0xd8547e,50,450,1.7);
|
||||||
|
redLight.position.set(100,300,100);
|
||||||
|
scene.add(redLight);
|
||||||
|
let blueLight = new THREE.PointLight(0x3677ac,50,450,1.7);
|
||||||
|
blueLight.position.set(300,300,200);
|
||||||
|
scene.add(blueLight);
|
||||||
|
};
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
cloudParticles.forEach(p => {
|
||||||
|
p.rotation.z -=0.001;
|
||||||
|
});
|
||||||
|
renderer.render(scene,camera);
|
||||||
|
requestAnimationFrame(render);
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
};
|
};
|
||||||
|
|
||||||
const Clouds: React.FC<{}> = () => {
|
|
||||||
const { camera } = useThree();
|
|
||||||
const loader = useMemo(() => new THREE.TextureLoader(), []);
|
|
||||||
const [texture, setTexture] = useState<THREE.Texture | undefined>();
|
|
||||||
const [clouds, setClouds] = useState(initialClouds);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log(camera);
|
|
||||||
camera.rotateX(1.16);
|
|
||||||
camera.rotateY(-0.12);
|
|
||||||
camera.rotateZ(0.27);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loader.load('images/smoke.png', (loadedTexture) => {
|
|
||||||
setTexture(loadedTexture);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useFrame(() => {
|
|
||||||
setClouds(current => current.map(c => ({
|
|
||||||
...c,
|
|
||||||
rotation: {
|
|
||||||
...c.rotation,
|
|
||||||
z: c.rotation.z -= 0.001,
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!texture) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{clouds.map((cloud) => (
|
|
||||||
<Cloud
|
|
||||||
key={cloud.name}
|
|
||||||
texture={texture}
|
|
||||||
position={cloud.position}
|
|
||||||
rotation={cloud.rotation}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
position: 'fixed' as any,
|
|
||||||
zIndex: -1,
|
|
||||||
}
|
|
||||||
|
|
||||||
const Background: React.FC<{}> = () => {
|
const Background: React.FC<{}> = () => {
|
||||||
const fog = useMemo(() => new THREE.FogExp2(0x03544e, 0.001), []);
|
|
||||||
const [aspect, setAspect] = useState(global.window ? window.innerWidth / window.innerHeight : 1);
|
|
||||||
if (!global.window) {
|
|
||||||
return <Canvas style={style}><></></Canvas>
|
|
||||||
}
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const update = () => {
|
setup();
|
||||||
setAspect(window.innerWidth / window.innerHeight);
|
|
||||||
};
|
|
||||||
window.addEventListener('resize', update);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('resize', update);
|
|
||||||
}
|
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return <></>
|
||||||
<Canvas style={style}>
|
|
||||||
<color attach="background" args={[0x03544e]} />
|
|
||||||
<scene fog={fog}>
|
|
||||||
<perspectiveCamera
|
|
||||||
args={[60, aspect, 1, 1000]}
|
|
||||||
aspect={aspect}
|
|
||||||
position={[0, 0, 0]}
|
|
||||||
/>
|
|
||||||
<ambientLight args={[0x555555]} />
|
|
||||||
<directionalLight args={[0xff8c19]} position={[0,0,1]} />
|
|
||||||
<pointLight args={[0xcc6600, 50, 450, 1.7]} position={[200,300,100]} />
|
|
||||||
<pointLight args={[0xd8547e, 50, 450, 1.7]} position={[100,300,100]} />
|
|
||||||
<pointLight args={[0x3677ac, 50, 450, 1.7]} position={[300,300,200]} />
|
|
||||||
<Clouds />
|
|
||||||
</scene>
|
|
||||||
</Canvas>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Background;
|
export default Background;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useRef, useState, useEffect } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Image from 'next/image'
|
|
||||||
import image from '../public/images/me.jpg';
|
import image from '../public/images/me.jpg';
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
@@ -11,14 +10,16 @@ const Wrapper = styled.div`
|
|||||||
padding: 80px;
|
padding: 80px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ImageWrapper = styled.div`
|
const ImageWrapper = styled.div<{loaded: boolean}>`
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: solid 30px rgba(255, 255, 255, .5);
|
border: solid 10px rgba(255, 255, 255, .5);
|
||||||
box-shadow: 0 0 35px rgba(255, 255, 255, .5);
|
box-shadow: 0 0 35px rgba(0, 0, 0, .5);
|
||||||
overflow: hidden;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 360px;
|
max-width: 300px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
transition: all 1.2s;
|
||||||
|
opacity: ${({ loaded }) => loaded ? '1' : '0'};
|
||||||
|
transform: rotateY(180deg) translateZ(0);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Spacer = styled.div`
|
const Spacer = styled.div`
|
||||||
@@ -35,6 +36,12 @@ const Title = styled.h1`
|
|||||||
0 0 5px rgba(255, 255, 255, .5);
|
0 0 5px rgba(255, 255, 255, .5);
|
||||||
0 0 10px rgba(0, 0, 0, .5);
|
0 0 10px rgba(0, 0, 0, .5);
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
letter-spacing: 7px;
|
||||||
|
|
||||||
|
&::first-letter {
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const SubTitle = styled.h2`
|
const SubTitle = styled.h2`
|
||||||
@@ -48,25 +55,40 @@ const SubTitle = styled.h2`
|
|||||||
0 0 10px rgba(0, 0, 0, .5);
|
0 0 10px rgba(0, 0, 0, .5);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Divider = styled.div`
|
const Image = styled.img<{blurDataURL: string}>`
|
||||||
margin-top: 70px;
|
background: url("${({ blurDataURL }) => blurDataURL}");
|
||||||
|
position: absolute;
|
||||||
|
background-size: cover;
|
||||||
|
border-radius: 50%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 800px;
|
height: 100%;
|
||||||
height: 1px;
|
`
|
||||||
background: rgba(255, 255, 255, .5);
|
|
||||||
box-shadow: 0 0 30px rgba(255, 255, 255, .7);
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Me: React.FC<{}> = () => (
|
const Me: React.FC<{}> = () => {
|
||||||
|
const imgRef = useRef<HTMLImageElement>();
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (imgRef.current && imgRef.current.complete) {
|
||||||
|
setLoaded(true);
|
||||||
|
}
|
||||||
|
}, [imgRef]);
|
||||||
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<ImageWrapper>
|
<ImageWrapper loaded={loaded}>
|
||||||
<Image layout="fill" {...image} />
|
<Image
|
||||||
|
ref={imgRef}
|
||||||
|
src={image.src}
|
||||||
|
blurDataURL={image.blurDataURL}
|
||||||
|
onLoad={() => setLoaded(true)}
|
||||||
|
/>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
</ImageWrapper>
|
</ImageWrapper>
|
||||||
<Title>Morten Olsen</Title>
|
<Title>Morten Olsen</Title>
|
||||||
<SubTitle>“...One part genius, one part crazy”</SubTitle>
|
<SubTitle>“...One part genius, one part crazy”</SubTitle>
|
||||||
<Divider />
|
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Me;
|
export default Me;
|
||||||
|
|||||||
@@ -15,11 +15,9 @@ const Outer = styled.div`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Image = styled.div<{ src: string }>`
|
const Image = styled.img<{ src: string }>`
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
background: url('${({ src }) => src}');
|
|
||||||
background-size: cover;
|
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
transition: all .8s;
|
transition: all .8s;
|
||||||
filter: grayscale(100%) invert();
|
filter: grayscale(100%) invert();
|
||||||
@@ -31,7 +29,7 @@ const Wrapper = styled.div`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
max-width: 1000px;
|
max-width: 1600px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ItemWrapper = styled.a`
|
const ItemWrapper = styled.a`
|
||||||
@@ -53,7 +51,7 @@ const ItemWrapper = styled.a`
|
|||||||
color: #000;
|
color: #000;
|
||||||
box-shadow: 0 0 35px rgba(0, 0, 0, .3);
|
box-shadow: 0 0 35px rgba(0, 0, 0, .3);
|
||||||
|
|
||||||
&> div {
|
&> img {
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
data.json
26
data.json
@@ -2,7 +2,7 @@
|
|||||||
"type": "info",
|
"type": "info",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "Morten Olsen",
|
"name": "Morten Olsen",
|
||||||
"image": "assets/me.jpg",
|
"image": "public/images/me.jpg",
|
||||||
"info": [{
|
"info": [{
|
||||||
"name": "E-mail",
|
"name": "E-mail",
|
||||||
"value": "hello@buy-me.coffee"
|
"value": "hello@buy-me.coffee"
|
||||||
@@ -12,6 +12,9 @@
|
|||||||
},{
|
},{
|
||||||
"name": "Github",
|
"name": "Github",
|
||||||
"value": "https://github.com/morten-olsen"
|
"value": "https://github.com/morten-olsen"
|
||||||
|
}, {
|
||||||
|
"name": "Homepage",
|
||||||
|
"value": "https://mortenolsen.pro"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
@@ -158,20 +161,25 @@
|
|||||||
"title": "Projects of interest",
|
"title": "Projects of interest",
|
||||||
"description": "A list of projects I have worked with or am working on, for which the source is publicly available. Keep in mind that some of these projects are in early stages, and some are merely created for the training, and may not represent the actual way I think production code should be structured",
|
"description": "A list of projects I have worked with or am working on, for which the source is publicly available. Keep in mind that some of these projects are in early stages, and some are merely created for the training, and may not represent the actual way I think production code should be structured",
|
||||||
"projects": [{
|
"projects": [{
|
||||||
"name": "Clash of Robots",
|
"name": "Todoma",
|
||||||
"tagline": "AI vs AI game enginge and IDE",
|
"tagline": "GTD meets kanban in this mobile and web task manager",
|
||||||
"url": "github.com/clash-of-robots/core",
|
"url": "github.com/morten-olsen/todoma",
|
||||||
"description": "A game engine for creating AI vs. AI turn-based games, including an IDE (still work in progress). The project is built to be the base for a space drone vs. space drone AI game. Players are given control over a set of drones and have to traverse the solar system, fighting to prosper as selles bots, pirate bots, communication relays or whatever the player decides to do within the confines of the sandbox."
|
"description": "A task manager that merges GTD and Kanban into a system that I use for organising my day. The app is written in React Native and runs both on iOS, Android and web, all with the same codebase"
|
||||||
}, {
|
}, {
|
||||||
"name": "Curriculum Vitae",
|
"name": "Curriculum Vitae",
|
||||||
"tagline": "Automated CV building",
|
"tagline": "Automated CV building",
|
||||||
"url": "github.com/morten-olsen/curriculum-vitae",
|
"url": "github.com/morten-olsen/curriculum-vitae",
|
||||||
"description": " Another small fun project is this resume, as it is created in \\LaTeX, versioned using _Git_ on _Github_ and set to automatically build and create multiple release versions using _Travis CI_ and _Docker "
|
"description": " Another small fun project is this resume, as it is created in \\LaTeX, versioned using _Git_ on _Github_ and set to automatically build and create multiple release versions using _Travis CI_ and _Docker "
|
||||||
}, {
|
}, {
|
||||||
"name": "LifeFlow Mind",
|
"name": "React Native Debug Console",
|
||||||
"tagline": "Evernote meets \\LaTeX meets Jupyter",
|
"tagline": "Easily debug in-the-wild React Native apps",
|
||||||
"url": "github.com/lifeflow-mind",
|
"url": "github.com/morten-olsen/react-native-debug-console",
|
||||||
"description": "I love Evernote simple digital note storages, but as a developer and general nerd, its features often come up short. Which is why I am working on a project named Mind, which attempts to bridge the best aspect of Evernote, with a dynamic view system, so new views (for instance a calendar or a contact view) can be introduced. Also, it can render mathematical formulas using \\LaTeX and execute code snippets, to test and present code directly inside documents. A somewhat working demo can be found at https://morten-olsen.github.io/mind"
|
"description": "I needed a tool that allowed me to easily debug the JavaScript and network asspect of a React Native app after it has been push to testers. This tool adds a development menu inspired by Chrome Dev Tools to a React Native app, allowing for an easier debugging experience"
|
||||||
|
}, {
|
||||||
|
"name": "IoT (Name TBD)",
|
||||||
|
"tagline": "Developer focused IoT platform",
|
||||||
|
"url": "https://iot.mortenolsen.pro",
|
||||||
|
"description": "A lot of IoT platforms tries to be user friendly, sacrificing customizability and still staying to complicated for the general user. This project creates a developer focus that does not trade control for user friendliness, retaining focus on 'coding' your own home setup"
|
||||||
}, {
|
}, {
|
||||||
"name": "Redux App State",
|
"name": "Redux App State",
|
||||||
"tagline": "Advanced app like navigation on the web",
|
"tagline": "Advanced app like navigation on the web",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { createGlobalStyle } from 'styled-components';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Background from '../components/Background';
|
import Background from '../components/Background';
|
||||||
import Me from '../components/Me';
|
import Me from '../components/Me';
|
||||||
@@ -7,16 +9,42 @@ import htbLogo from '../public/images/logos/htb.svg';
|
|||||||
import githubLogo from '../public/images/logos/github.svg';
|
import githubLogo from '../public/images/logos/github.svg';
|
||||||
import linkedinLogo from '../public/images/logos/linkedin.svg';
|
import linkedinLogo from '../public/images/logos/linkedin.svg';
|
||||||
import stackOverflowLogo from '../public/images/logos/stackoverflow.svg';
|
import stackOverflowLogo from '../public/images/logos/stackoverflow.svg';
|
||||||
|
import codinGameLogo from '../public/images/logos/codingame.svg';
|
||||||
|
import resumeLogo from '../public/images/logos/resume.svg';
|
||||||
|
|
||||||
|
const Globals = createGlobalStyle`
|
||||||
|
html, body, #__next {
|
||||||
|
height: 90%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
background: #03544e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadein {
|
||||||
|
from {opacity: 0}
|
||||||
|
to {opacity: 1}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
min-height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
`
|
||||||
|
|
||||||
const Frontpage: React.FC<{}> = () => {
|
const Frontpage: React.FC<{}> = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<Wrapper>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Morten Olsen</title>
|
<title>Morten Olsen</title>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@300&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@300&display=swap" rel="stylesheet" />
|
||||||
</Head>
|
</Head>
|
||||||
|
<Globals />
|
||||||
<Background />
|
<Background />
|
||||||
<Me />
|
<Me />
|
||||||
<Social
|
<Social
|
||||||
@@ -32,13 +60,21 @@ const Frontpage: React.FC<{}> = () => {
|
|||||||
title: 'Stack Overflow',
|
title: 'Stack Overflow',
|
||||||
link: 'https://stackoverflow.com/users/1689055/morten-olsen',
|
link: 'https://stackoverflow.com/users/1689055/morten-olsen',
|
||||||
logo: stackOverflowLogo,
|
logo: stackOverflowLogo,
|
||||||
|
}, {
|
||||||
|
title: 'Codingame',
|
||||||
|
link: 'https://www.codingame.com/profile/8b34b1812baa75715c972bfe190c0aed4552622',
|
||||||
|
logo: codinGameLogo,
|
||||||
|
}, {
|
||||||
|
title: 'Resumè',
|
||||||
|
link: 'https://github.com/morten-olsen/morten-olsen.github.io/releases/',
|
||||||
|
logo: resumeLogo,
|
||||||
}, {
|
}, {
|
||||||
title: 'Linkedin',
|
title: 'Linkedin',
|
||||||
link: 'https://www.linkedin.com/in/mortenolsendk',
|
link: 'https://www.linkedin.com/in/mortenolsendk',
|
||||||
logo: linkedinLogo,
|
logo: linkedinLogo,
|
||||||
}]}
|
}]}
|
||||||
/>
|
/>
|
||||||
</>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
1
public/images/logos/codingame.svg
Normal file
1
public/images/logos/codingame.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 2083 2083" xmlns="http://www.w3.org/2000/svg" width="2500" height="2500"><path d="M0 0h2083v2083H0z" fill="transparent"/><g fill="#1f2528"><path d="M0 1636c411.554-97.771 692.897-283.177 724-637 10.714-131.212 67.364-243.777 216-319 207.675-80.617 305.728-52.164 390-10 92.668 81.722 119.468 199.94 50 371.5-31.914 100.266-291.001 223.867-410 258.5-392.539 175.893-595.504 430.282-561 783H0z"/><ellipse cx="1666.5" cy="408" rx="196.5" ry="192"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 560 B |
@@ -1,22 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 176 33" style="enable-background:new 0 0 176 33;" xml:space="preserve">
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 33 33" style="enable-background:new 0 0 176 33;" xml:space="preserve">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.st0{fill:#FFFFFF;}
|
.st0{fill:#FFFFFF;}
|
||||||
.st1{fill:#000;}
|
.st1{fill:#000;}
|
||||||
</style>
|
</style>
|
||||||
<g>
|
|
||||||
<path class="st0" d="M48.2,22.4v-4.6c0-0.2-0.1-0.3-0.3-0.3h-4c-0.2,0-0.3,0.1-0.3,0.3v4.6c0,0.2-0.1,0.3-0.3,0.3h-2.4 c-0.2,0-0.3-0.1-0.3-0.3V10.6c0-0.2,0.1-0.3,0.3-0.3h2.4c0.2,0,0.3,0.1,0.3,0.3v4.1c0,0.2,0.1,0.3,0.3,0.3h4c0.2,0,0.3-0.1,0.3-0.3 v-4.1c0-0.2,0.1-0.3,0.3-0.3H51c0.2,0,0.3,0.1,0.3,0.3v11.9c0,0.2-0.1,0.3-0.3,0.3h-2.4C48.4,22.7,48.2,22.6,48.2,22.4z"/>
|
|
||||||
<path class="st0" d="M57.9,20.3l-0.7,2.2c0,0.1-0.2,0.2-0.3,0.2h-2.6c-0.2,0-0.3-0.2-0.3-0.4l4-11.9c0-0.1,0.2-0.2,0.3-0.2H62 c0.1,0,0.2,0.1,0.3,0.2l4,11.9c0.1,0.2-0.1,0.4-0.3,0.4h-2.6c-0.1,0-0.2-0.1-0.3-0.2l-0.7-2.2c0-0.1-0.2-0.2-0.3-0.2h-4.1 C58.1,20.1,58,20.2,57.9,20.3z M61.5,16.8c-0.5-1.5-0.9-3-1.3-4.5h0c-0.4,1.5-0.8,3-1.2,4.5l-0.1,0.5c-0.1,0.2,0.1,0.4,0.3,0.4h2.2 c0.2,0,0.3-0.2,0.3-0.4L61.5,16.8z"/>
|
|
||||||
<path class="st0" d="M79.2,14.8h-2.6c-0.1,0-0.3-0.1-0.3-0.2c-0.2-1.2-1-1.9-2.3-1.9c-1.6,0-2.7,1.5-2.7,3.9s1,3.9,2.6,3.9 c1.2,0,2.1-0.7,2.3-2c0-0.1,0.1-0.2,0.3-0.2h2.6c0.2,0,0.3,0.2,0.3,0.3c-0.4,2.9-2.4,4.5-5.5,4.5c-3.6,0-5.8-2.5-5.8-6.5 s2.2-6.5,5.8-6.5c3.1,0,5.1,1.7,5.4,4.4C79.5,14.6,79.4,14.8,79.2,14.8z"/>
|
|
||||||
<path class="st0" d="M93.4,22.7h-3.1c-0.1,0-0.2,0-0.2-0.1L86.3,17h0v5.4c0,0.2-0.1,0.3-0.3,0.3h-2.4c-0.2,0-0.3-0.1-0.3-0.3V10.6 c0-0.2,0.1-0.3,0.3-0.3H86c0.2,0,0.3,0.1,0.3,0.3v5.1h0l3.6-5.2c0.1-0.1,0.1-0.1,0.2-0.1H93c0.2,0,0.4,0.3,0.2,0.5l-4,5.2 c-0.1,0.1-0.1,0.2,0,0.4l4.4,5.9C93.7,22.4,93.6,22.7,93.4,22.7z"/>
|
|
||||||
<path class="st0" d="M101.8,12v10.4c0,0.2-0.1,0.3-0.3,0.3h-1c-0.2,0-0.3-0.1-0.3-0.3V12c0-0.2-0.1-0.3-0.3-0.3h-3.7 c-0.2,0-0.3-0.1-0.3-0.3v-0.9c0-0.2,0.1-0.3,0.3-0.3h9.5c0.2,0,0.3,0.1,0.3,0.3v0.9c0,0.2-0.1,0.3-0.3,0.3h-3.7 C101.9,11.7,101.8,11.8,101.8,12z"/>
|
|
||||||
<path class="st0" d="M117.3,22.4v-5.2c0-0.2-0.1-0.3-0.3-0.3h-5.8c-0.2,0-0.3,0.1-0.3,0.3v5.2c0,0.2-0.1,0.3-0.3,0.3h-1 c-0.2,0-0.3-0.1-0.3-0.3V10.6c0-0.2,0.1-0.3,0.3-0.3h1c0.2,0,0.3,0.1,0.3,0.3v4.7c0,0.2,0.1,0.3,0.3,0.3h5.8c0.2,0,0.3-0.1,0.3-0.3 v-4.7c0-0.2,0.1-0.3,0.3-0.3h1c0.2,0,0.3,0.1,0.3,0.3v11.9c0,0.2-0.1,0.3-0.3,0.3h-1C117.4,22.7,117.3,22.6,117.3,22.4z"/>
|
|
||||||
<path class="st0" d="M125.5,12v3.3c0,0.2,0.1,0.3,0.3,0.3h5.5c0.2,0,0.3,0.1,0.3,0.3v0.8c0,0.2-0.1,0.3-0.3,0.3h-5.5 c-0.2,0-0.3,0.1-0.3,0.3V21c0,0.2,0.1,0.3,0.3,0.3h6c0.2,0,0.3,0.1,0.3,0.3v0.9c0,0.2-0.1,0.3-0.3,0.3h-7.6c-0.2,0-0.3-0.1-0.3-0.3 V10.6c0-0.2,0.1-0.3,0.3-0.3h7.4c0.2,0,0.3,0.1,0.3,0.3v0.9c0,0.2-0.1,0.3-0.3,0.3h-5.8C125.7,11.7,125.5,11.8,125.5,12z"/>
|
|
||||||
<path class="st0" d="M141,10.3c3.1,0,4.5,1.3,4.5,3.2c0,1.5-0.7,2.3-2.1,2.8v0c1.6,0.4,2.4,1.3,2.4,2.9c0,2.1-1.7,3.5-4.5,3.5h-4.9 c-0.2,0-0.3-0.1-0.3-0.3V10.6c0-0.2,0.1-0.3,0.3-0.3H141z M139.1,15c0,0.2,0.1,0.3,0.3,0.3h1.3c1.3,0,1.9-0.4,1.9-1.4 s-0.6-1.3-1.8-1.3h-1.4c-0.2,0-0.3,0.1-0.3,0.3V15z M139.1,20.1c0,0.2,0.1,0.3,0.3,0.3h1.4c1.4,0,2-0.4,2-1.4c0-1-0.6-1.5-2-1.5 h-1.3c-0.2,0-0.3,0.1-0.3,0.3V20.1z"/>
|
|
||||||
<path class="st0" d="M160.8,16.5c0,4-2.3,6.5-6,6.5c-3.7,0-6-2.5-6-6.5s2.3-6.5,6-6.5C158.4,10,160.8,12.5,160.8,16.5z M151.9,16.5 c0,2.4,1,3.9,2.9,3.9c1.8,0,2.8-1.4,2.8-3.9s-1-3.9-2.8-3.9C152.9,12.6,151.9,14.1,151.9,16.5z"/>
|
|
||||||
<path class="st0" d="M173.4,22.7h-2.9c-0.1,0-0.2-0.1-0.3-0.2l-2.2-4.1h0l-2.2,4.1c-0.1,0.1-0.1,0.2-0.3,0.2h-2.9 c-0.2,0-0.4-0.2-0.2-0.4l3.5-5.8c0.1-0.1,0.1-0.2,0-0.3l-3.3-5.5c-0.1-0.2,0-0.4,0.2-0.4h2.8c0.1,0,0.2,0.1,0.3,0.2l2,4h0l2-4 c0-0.1,0.2-0.2,0.3-0.2h2.8c0.2,0,0.4,0.2,0.2,0.4l-3.3,5.5c-0.1,0.1-0.1,0.2,0,0.3l3.5,5.8C173.8,22.5,173.7,22.7,173.4,22.7z"/>
|
|
||||||
</g>
|
|
||||||
<desc>Created with Sketch.</desc>
|
|
||||||
<path class="st1" d="M28.6,9.3C28.6,9.3,28.6,9.3,28.6,9.3c0-0.3-0.1-0.6-0.4-0.8c0,0,0,0,0,0c0,0,0,0-0.1-0.1c0,0-0.1,0-0.1-0.1 c0,0,0,0,0,0L15.6,1.2c0,0-0.1,0-0.1,0C15.3,1,15.1,1,14.9,1c-0.1,0-0.2,0-0.3,0.1c-0.1,0-0.1,0.1-0.2,0.1L2,8.3c0,0,0,0,0,0 c0,0,0,0,0,0c0,0,0,0,0,0C1.8,8.4,1.7,8.5,1.7,8.6c0,0,0,0,0,0C1.5,8.8,1.4,9,1.4,9.3c0,0,0,0,0,0c0,0,0,0,0,0v14.3 c0,0.4,0.2,0.8,0.6,1l12.4,7.2c0,0,0,0,0.1,0c0,0,0,0,0,0c0.1,0,0.1,0.1,0.2,0.1c0,0,0,0,0,0c0.1,0,0.2,0,0.2,0s0.2,0,0.2,0 c0,0,0,0,0,0c0.1,0,0.1,0,0.2-0.1c0,0,0,0,0,0c0,0,0,0,0.1,0l12.4-7.2c0.4-0.2,0.6-0.6,0.6-1L28.6,9.3C28.6,9.4,28.6,9.3,28.6,9.3z M6.3,8.9L14.7,4c0.2-0.1,0.4-0.1,0.5,0l8.4,4.9c0.4,0.2,0.4,0.7,0,0.9l-8.4,4.9c-0.2,0.1-0.4,0.1-0.5,0L6.3,9.8 C5.9,9.6,5.9,9.1,6.3,8.9z M13.5,27.4c0,0.4-0.4,0.7-0.8,0.5L4.3,23C4.1,22.9,4,22.7,4,22.5v-9.7c0-0.4,0.4-0.7,0.8-0.5l8.4,4.9 c0.2,0.1,0.3,0.3,0.3,0.5V27.4z M25.9,22.5c0,0.2-0.1,0.4-0.3,0.5l-8.4,4.9c-0.4,0.2-0.8-0.1-0.8-0.5v-9.7c0-0.2,0.1-0.4,0.3-0.5 l8.4-4.9c0.4-0.2,0.8,0.1,0.8,0.5V22.5z"/>
|
<path class="st1" d="M28.6,9.3C28.6,9.3,28.6,9.3,28.6,9.3c0-0.3-0.1-0.6-0.4-0.8c0,0,0,0,0,0c0,0,0,0-0.1-0.1c0,0-0.1,0-0.1-0.1 c0,0,0,0,0,0L15.6,1.2c0,0-0.1,0-0.1,0C15.3,1,15.1,1,14.9,1c-0.1,0-0.2,0-0.3,0.1c-0.1,0-0.1,0.1-0.2,0.1L2,8.3c0,0,0,0,0,0 c0,0,0,0,0,0c0,0,0,0,0,0C1.8,8.4,1.7,8.5,1.7,8.6c0,0,0,0,0,0C1.5,8.8,1.4,9,1.4,9.3c0,0,0,0,0,0c0,0,0,0,0,0v14.3 c0,0.4,0.2,0.8,0.6,1l12.4,7.2c0,0,0,0,0.1,0c0,0,0,0,0,0c0.1,0,0.1,0.1,0.2,0.1c0,0,0,0,0,0c0.1,0,0.2,0,0.2,0s0.2,0,0.2,0 c0,0,0,0,0,0c0.1,0,0.1,0,0.2-0.1c0,0,0,0,0,0c0,0,0,0,0.1,0l12.4-7.2c0.4-0.2,0.6-0.6,0.6-1L28.6,9.3C28.6,9.4,28.6,9.3,28.6,9.3z M6.3,8.9L14.7,4c0.2-0.1,0.4-0.1,0.5,0l8.4,4.9c0.4,0.2,0.4,0.7,0,0.9l-8.4,4.9c-0.2,0.1-0.4,0.1-0.5,0L6.3,9.8 C5.9,9.6,5.9,9.1,6.3,8.9z M13.5,27.4c0,0.4-0.4,0.7-0.8,0.5L4.3,23C4.1,22.9,4,22.7,4,22.5v-9.7c0-0.4,0.4-0.7,0.8-0.5l8.4,4.9 c0.2,0.1,0.3,0.3,0.3,0.5V27.4z M25.9,22.5c0,0.2-0.1,0.4-0.3,0.5l-8.4,4.9c-0.4,0.2-0.8-0.1-0.8-0.5v-9.7c0-0.2,0.1-0.4,0.3-0.5 l8.4-4.9c0.4-0.2,0.8,0.1,0.8,0.5V22.5z"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 1.4 KiB |
43
public/images/logos/resume.svg
Normal file
43
public/images/logos/resume.svg
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 370.123 370.123" style="enable-background:new 0 0 370.123 370.123;" xml:space="preserve">
|
||||||
|
<path d="M321.321,18.74H310.78c0-10.352-8.39-18.74-18.74-18.74H78.083c-10.352,0-18.74,8.389-18.74,18.74H48.802
|
||||||
|
c-10.351,0-18.74,8.389-18.74,18.74c0,10.351,8.39,18.74,18.74,18.74h12.279l22.132,296.557c0.73,9.781,8.879,17.346,18.688,17.346
|
||||||
|
h166.322c9.809,0,17.957-7.564,18.686-17.346l22.133-296.557h12.279c10.351,0,18.74-8.39,18.74-18.74
|
||||||
|
C340.061,27.129,331.672,18.74,321.321,18.74z M119.297,332.643l-7.47-100.061h34.125c8.242,17.324,22.939,28.11,39.109,28.11
|
||||||
|
c16.17,0,30.867-10.786,39.109-28.11h34.125l-7.471,100.061H119.297z M263.007,169.469h-38.836
|
||||||
|
c-8.242-17.324-22.939-28.114-39.109-28.114c-16.17,0-30.867,10.79-39.109,28.114h-38.836L98.665,56.221h172.793L263.007,169.469z"
|
||||||
|
/>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -3,8 +3,9 @@
|
|||||||
FORMAT=$1; shift
|
FORMAT=$1; shift
|
||||||
|
|
||||||
docker run \
|
docker run \
|
||||||
|
--workdir "/var/texlive/latex" \
|
||||||
--user "$UID:$GID" \
|
--user "$UID:$GID" \
|
||||||
--net=none \
|
--net=none \
|
||||||
-v $(pwd):/var/texlive \
|
-v $(pwd):/var/texlive \
|
||||||
blang/latex:ubuntu
|
blang/latex:ubuntu \
|
||||||
lualatex "latex/$FORMAT.tex"
|
lualatex "$FORMAT.tex"
|
||||||
|
|||||||
Reference in New Issue
Block a user