Initial commit
This commit is contained in:
commit
0ed88f38a4
27 changed files with 3345 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
**/cache
|
||||
37
docs/.vitepress/config.mts
Normal file
37
docs/.vitepress/config.mts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { defineConfig, DefaultTheme } from "vitepress";
|
||||
|
||||
interface CustomThemeConfig extends DefaultTheme.Config {
|
||||
socialLinks?: Array<{
|
||||
label: string;
|
||||
link: string;
|
||||
icon: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig<CustomThemeConfig>({
|
||||
title: "Simon Einzinger",
|
||||
description: "Cybersecurity Student",
|
||||
markdown: {
|
||||
theme: "github-light",
|
||||
},
|
||||
themeConfig: {
|
||||
socialLinks: [
|
||||
{
|
||||
label: "GitHub",
|
||||
icon: "/icons/github.svg",
|
||||
link: "https://github.com/einCyberSimon",
|
||||
},
|
||||
{
|
||||
label: "LinkedIn",
|
||||
icon: "/icons/linkedin.svg",
|
||||
link: "https://www.linkedin.com/in/simon-einzinger",
|
||||
},
|
||||
{
|
||||
label: "Email",
|
||||
icon: "/icons/email.svg",
|
||||
link: "mailto:info@simon-einzinger.de",
|
||||
},
|
||||
],
|
||||
},
|
||||
} satisfies { themeConfig: CustomThemeConfig });
|
||||
35
docs/.vitepress/theme/components/BlogIndex.vue
Normal file
35
docs/.vitepress/theme/components/BlogIndex.vue
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { data as posts } from "../composables/posts.data";
|
||||
import BlogItem from "../layout/BlogItem.vue";
|
||||
|
||||
const sortedPosts = computed(() => {
|
||||
return [...posts].sort((a, b) => b.date.time - a.date.time);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="blog-index">
|
||||
<h1>All Posts</h1>
|
||||
<ul class="blog-list">
|
||||
<li v-for="post in sortedPosts" :key="post.url">
|
||||
<BlogItem
|
||||
:url="post.url"
|
||||
:title="post.title"
|
||||
:date="post.date.string"
|
||||
:excerpt="post.excerpt"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.blog-index {
|
||||
}
|
||||
.blog-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 1em 0;
|
||||
}
|
||||
</style>
|
||||
21
docs/.vitepress/theme/composables/index.data.ts
Normal file
21
docs/.vitepress/theme/composables/index.data.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { createContentLoader } from "vitepress";
|
||||
|
||||
interface Index {
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
declare const data: Index[];
|
||||
|
||||
export { data };
|
||||
|
||||
export default createContentLoader("*.md", {
|
||||
transform(raw): Index[] {
|
||||
return raw
|
||||
.map(({ url, frontmatter, excerpt }) => ({
|
||||
title: frontmatter.title,
|
||||
url,
|
||||
}))
|
||||
.sort((a, b) => a.title.localeCompare(b.title));
|
||||
},
|
||||
});
|
||||
42
docs/.vitepress/theme/composables/posts.data.ts
Normal file
42
docs/.vitepress/theme/composables/posts.data.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { createContentLoader } from "vitepress";
|
||||
|
||||
interface Post {
|
||||
title: string;
|
||||
url: string;
|
||||
date: {
|
||||
time: number;
|
||||
string: string;
|
||||
};
|
||||
excerpt: string | undefined;
|
||||
}
|
||||
|
||||
declare const data: Post[];
|
||||
|
||||
export { data };
|
||||
|
||||
export default createContentLoader("posts/*.md", {
|
||||
excerpt: true,
|
||||
transform(raw): Post[] {
|
||||
return raw
|
||||
.map(({ url, frontmatter, excerpt }) => ({
|
||||
title: frontmatter.title,
|
||||
url,
|
||||
excerpt,
|
||||
date: formatDate(frontmatter.date),
|
||||
}))
|
||||
.sort((a, b) => a.title.localeCompare(b.title));
|
||||
},
|
||||
});
|
||||
|
||||
function formatDate(raw: string): Post["date"] {
|
||||
const date = new Date(raw);
|
||||
date.setUTCHours(12);
|
||||
return {
|
||||
time: +date,
|
||||
string: date.toLocaleDateString("de-DE", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
}),
|
||||
};
|
||||
}
|
||||
33
docs/.vitepress/theme/composables/useTheme.ts
Normal file
33
docs/.vitepress/theme/composables/useTheme.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { ref, onMounted } from "vue";
|
||||
|
||||
export function useTheme() {
|
||||
const isDarkMode = ref(false);
|
||||
|
||||
function setTheme(dark: boolean) {
|
||||
isDarkMode.value = dark;
|
||||
document.documentElement.setAttribute(
|
||||
"data-theme",
|
||||
dark ? "dark" : "light",
|
||||
);
|
||||
localStorage.setItem("theme", dark ? "dark" : "light");
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
setTheme(!isDarkMode.value);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const savedTheme = localStorage.getItem("theme");
|
||||
if (savedTheme) {
|
||||
setTheme(savedTheme === "dark");
|
||||
} else {
|
||||
setTheme(false);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
isDarkMode,
|
||||
setTheme,
|
||||
toggleTheme,
|
||||
};
|
||||
}
|
||||
23
docs/.vitepress/theme/index.ts
Normal file
23
docs/.vitepress/theme/index.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// https://vitepress.dev/guide/custom-theme
|
||||
import { Theme } from "vitepress";
|
||||
import DefaultLayout from "./layout/Layout.vue";
|
||||
import BlogLayout from "./layout/BlogLayout.vue";
|
||||
import type { Theme } from "vitepress";
|
||||
import "./style.css";
|
||||
|
||||
import BlogIndex from "./components/BlogIndex.vue";
|
||||
|
||||
// @ts-ignore
|
||||
const components = [BlogIndex];
|
||||
|
||||
const theme: Theme = {
|
||||
Layout: DefaultLayout,
|
||||
enhanceApp({ app, router, siteData }) {
|
||||
app.component("BlogIndex", BlogIndex);
|
||||
},
|
||||
layouts: {
|
||||
blog: BlogLayout,
|
||||
},
|
||||
};
|
||||
|
||||
export default theme;
|
||||
100
docs/.vitepress/theme/layout/BlogItem.vue
Normal file
100
docs/.vitepress/theme/layout/BlogItem.vue
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<script setup lang="ts">
|
||||
interface BlogItemProps {
|
||||
url: string;
|
||||
title: string;
|
||||
date: string;
|
||||
excerpt: string;
|
||||
}
|
||||
|
||||
const props = defineProps<BlogItemProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="blog-card">
|
||||
<div class="blog-header">
|
||||
<h2 class="blog-title">
|
||||
<a :href="props.url">
|
||||
{{ props.title }}
|
||||
</a>
|
||||
</h2>
|
||||
<span class="blog-date">{{ props.date }}</span>
|
||||
</div>
|
||||
<div v-if="props.excerpt" v-html="props.excerpt"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.blog-card {
|
||||
position: relative;
|
||||
margin-bottom: 1.5em;
|
||||
padding: 1.5em;
|
||||
border: 1px solid var(--outline-color);
|
||||
border-radius: 8px;
|
||||
background-color: transparent;
|
||||
transition:
|
||||
transform 0.3s,
|
||||
box-shadow 0.3s,
|
||||
opacity 0.3s;
|
||||
}
|
||||
|
||||
.blog-card::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
var(--primary-color),
|
||||
var(--highlight-color)
|
||||
);
|
||||
-webkit-mask:
|
||||
linear-gradient(#fff 0 0) content-box,
|
||||
linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.blog-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.blog-card:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.blog-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.blog-title {
|
||||
margin: 0;
|
||||
font-size: 1.2em;
|
||||
color: var(--secondary-text-color);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.blog-title a {
|
||||
text-decoration: none;
|
||||
color: inherit; /* Inherit the .blog-title color */
|
||||
}
|
||||
|
||||
.blog-date {
|
||||
font-size: 0.9em;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.blog-excerpt {
|
||||
margin: 0;
|
||||
margin-top: 0.5em;
|
||||
color: var(--text-color);
|
||||
font-weight: var(--font-weight-regular);
|
||||
}
|
||||
</style>
|
||||
68
docs/.vitepress/theme/layout/BlogLayout.vue
Normal file
68
docs/.vitepress/theme/layout/BlogLayout.vue
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<script setup lang="ts">
|
||||
import { useData } from "vitepress";
|
||||
import { computed } from "vue";
|
||||
import { data as posts } from "../composables/posts.data";
|
||||
import TopBar from "./TopBar.vue";
|
||||
|
||||
import { useRoute } from "vitepress";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const currentIndex = computed(() =>
|
||||
posts.findIndex((p) => p.url === route.path),
|
||||
);
|
||||
const currentPost = computed(() =>
|
||||
currentIndex.value > -1 ? posts[currentIndex.value] : null,
|
||||
);
|
||||
|
||||
const nextPost = computed(() => {
|
||||
return currentIndex.value < posts.length - 1
|
||||
? posts[currentIndex.value + 1]
|
||||
: null;
|
||||
});
|
||||
|
||||
const prevPost = computed(() => {
|
||||
return currentIndex.value > 0 ? posts[currentIndex.value - 1] : null;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="blog-layout">
|
||||
<TopBar />
|
||||
|
||||
<!-- The main post content: the markdown for this page -->
|
||||
<article class="blog-post">
|
||||
<Content />
|
||||
</article>
|
||||
|
||||
<hr />
|
||||
|
||||
<nav class="post-navigation">
|
||||
<!-- Previous post link -->
|
||||
<div v-if="prevPost" class="prev-post">
|
||||
<a :href="prevPost.url">← {{ prevPost.title }}</a>
|
||||
</div>
|
||||
<!-- Next post link -->
|
||||
<div v-if="nextPost" class="next-post">
|
||||
<a :href="nextPost.url">{{ nextPost.title }} →</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.blog-layout {
|
||||
/* your layout styling */
|
||||
}
|
||||
.blog-post {
|
||||
max-width: 700px;
|
||||
margin: 2rem auto;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
.post-navigation {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 2rem auto;
|
||||
max-width: 700px;
|
||||
}
|
||||
</style>
|
||||
34
docs/.vitepress/theme/layout/Home.vue
Normal file
34
docs/.vitepress/theme/layout/Home.vue
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { data as posts } from "../composables/posts.data.js";
|
||||
|
||||
import BlogItem from "./BlogItem.vue";
|
||||
|
||||
const latestPosts = computed(() => {
|
||||
return posts.slice(0, 3);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Content />
|
||||
<div>
|
||||
<h1>Latest Blog Posts</h1>
|
||||
<ul class="blog-list">
|
||||
<li v-for="{ title, url, date, excerpt } of posts" :key="url">
|
||||
<BlogItem
|
||||
:url="url"
|
||||
:title="title"
|
||||
:date="date.string"
|
||||
:excerpt="excerpt"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.blog-list {
|
||||
list-style: none;
|
||||
margin: 1em;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
17
docs/.vitepress/theme/layout/Layout.vue
Normal file
17
docs/.vitepress/theme/layout/Layout.vue
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
import { useData } from "vitepress";
|
||||
|
||||
import TopBar from "./TopBar.vue";
|
||||
import Home from "./Home.vue";
|
||||
import NotFound from "./NotFound.vue";
|
||||
|
||||
// https://vitepress.dev/reference/runtime-api#usedata
|
||||
const { frontmatter, page } = useData();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TopBar />
|
||||
<Home v-if="frontmatter.home" />
|
||||
<NotFound v-else-if="page.isNotFound" />
|
||||
<Content v-else />
|
||||
</template>
|
||||
9
docs/.vitepress/theme/layout/NotFound.vue
Normal file
9
docs/.vitepress/theme/layout/NotFound.vue
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<h1>404 Not found</h1>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
68
docs/.vitepress/theme/layout/ThemeToggle.vue
Normal file
68
docs/.vitepress/theme/layout/ThemeToggle.vue
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<script setup lang="ts">
|
||||
import { useTheme } from "../composables/useTheme";
|
||||
|
||||
const { isDarkMode, toggleTheme } = useTheme();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ThemeToggle">
|
||||
<button
|
||||
class="theme-toggle"
|
||||
:class="{ 'theme-toggle--toggled': isDarkMode }"
|
||||
type="button"
|
||||
title="Toggle theme"
|
||||
aria-label="Toggle theme"
|
||||
@click="toggleTheme"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
width="2.5em"
|
||||
height="2.5em"
|
||||
fill="currentColor"
|
||||
class="theme-toggle__around"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<clipPath id="theme-toggle__around__cutout">
|
||||
<path d="M0 0h42v30a1 1 0 00-16 13H0Z" />
|
||||
</clipPath>
|
||||
<g clip-path="url(#theme-toggle__around__cutout)">
|
||||
<circle cx="16" cy="16" r="8.4" />
|
||||
<g>
|
||||
<circle cx="16" cy="3.3" r="2.3" />
|
||||
<circle cx="27" cy="9.7" r="2.3" />
|
||||
<circle cx="27" cy="22.3" r="2.3" />
|
||||
<circle cx="16" cy="28.7" r="2.3" />
|
||||
<circle cx="5" cy="22.3" r="2.3" />
|
||||
<circle cx="5" cy="9.7" r="2.3" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@import "theme-toggles/css/around.css";
|
||||
|
||||
.ThemeToggle {
|
||||
margin: 1em 1em;
|
||||
transition:
|
||||
transform 0.3s,
|
||||
opacity 0.3s;
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
</style>
|
||||
145
docs/.vitepress/theme/layout/TopBar.vue
Normal file
145
docs/.vitepress/theme/layout/TopBar.vue
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
<script setup lang="ts">
|
||||
import { useData } from "vitepress";
|
||||
import { computed } from "vue";
|
||||
|
||||
import { data as index } from "../composables/index.data.js";
|
||||
|
||||
import ThemeToggle from "./ThemeToggle.vue";
|
||||
|
||||
const { site, theme } = useData();
|
||||
|
||||
const socialLinks = computed(() => {
|
||||
return theme.value.socialLinks ?? [];
|
||||
});
|
||||
|
||||
const indexList = computed(() => {
|
||||
return index;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="TopBar">
|
||||
<div class="ProfileImageContainer">
|
||||
<img src="/images/me.jpeg" alt="Simon Einzinger" class="ProfileImage" />
|
||||
</div>
|
||||
|
||||
<h1 class="title">{{ site.title }}</h1>
|
||||
<p class="subtitle">{{ site.description }}</p>
|
||||
|
||||
<div class="RightSideTop">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
|
||||
<div class="RightSideBottom">
|
||||
<div class="social-icons">
|
||||
<a
|
||||
v-for="(icon, index) in socialLinks"
|
||||
:key="index"
|
||||
:href="icon.link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<img :src="icon.icon" :alt="icon.label" class="social-icon" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="Separator"></div>
|
||||
<ul class="IndexList">
|
||||
<li v-for="{ title, url } of indexList" :key="title">
|
||||
<a :href="url">#{{ title }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.TopBar {
|
||||
display: grid;
|
||||
grid-template-columns: 100px 1fr auto;
|
||||
grid-template-rows: auto auto;
|
||||
gap: 0 2em;
|
||||
align-items: center;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.ProfileImageContainer {
|
||||
grid-row: 1 / 3;
|
||||
grid-column: 1;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
border: 2px solid var(--outline-color);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.ProfileImage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.title {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
margin: 0.75em 0 0;
|
||||
font-size: 2.5em;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
margin: 0.25em 0 0;
|
||||
font-size: 1.5em;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.RightSideTop {
|
||||
grid-column: 3;
|
||||
grid-row: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.RightSideBottom {
|
||||
grid-column: 3;
|
||||
grid-row: 2;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.social-icons {
|
||||
display: flex;
|
||||
margin-left: 1em;
|
||||
filter: var(--icon-filter);
|
||||
}
|
||||
|
||||
.social-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 0 0.4em;
|
||||
}
|
||||
|
||||
.Separator {
|
||||
border-bottom: 1px solid var(--outline-color);
|
||||
margin: 0.5em 1em;
|
||||
}
|
||||
|
||||
.IndexList {
|
||||
list-style: none;
|
||||
margin: 1em 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1.5em;
|
||||
}
|
||||
|
||||
.IndexList li a {
|
||||
text-decoration: none;
|
||||
color: var(--text-color);
|
||||
padding: 0 0.75em;
|
||||
}
|
||||
</style>
|
||||
48
docs/.vitepress/theme/style.css
Normal file
48
docs/.vitepress/theme/style.css
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
:root {
|
||||
--background-color: #ffffff;
|
||||
--primary-color: #0066cc;
|
||||
--text-color: #333333;
|
||||
--secondary-text-color: #666666;
|
||||
--highlight-color: #3399ff;
|
||||
--outline-color: rgba(0, 0, 0, 0.2);
|
||||
|
||||
--icon-filter: invert(0);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--background-color: #1a1a1a;
|
||||
--primary-color: #a594f9;
|
||||
--text-color: #e0e0e0;
|
||||
--secondary-text-color: #cdc1ff;
|
||||
--highlight-color: #e5d9f2;
|
||||
--outline-color: rgba(255, 255, 255, 0.2);
|
||||
|
||||
--icon-filter: invert(1);
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: "VT323", monospace;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
color: var(--secondary-text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -2px;
|
||||
width: 0%;
|
||||
height: 1px;
|
||||
background-color: var(--primary-color);
|
||||
transition: width 0.3s;
|
||||
}
|
||||
|
||||
a:hover::after {
|
||||
width: 100%;
|
||||
}
|
||||
5
docs/blog.md
Normal file
5
docs/blog.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Blog
|
||||
---
|
||||
|
||||
<BlogIndex />
|
||||
3
docs/cv.md
Normal file
3
docs/cv.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
title: CV
|
||||
---
|
||||
3
docs/impressum.md
Normal file
3
docs/impressum.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
title: Impressum
|
||||
---
|
||||
9
docs/index.md
Normal file
9
docs/index.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
home: true
|
||||
title: About
|
||||
---
|
||||
|
||||
Hello there, I am Simon, a 23 year-old cybersecurity student at Saarland University.
|
||||
Currently, I am in my last bachelor semester writing my thesis on [Side-channel attacks](https://en.wikipedia.org/wiki/Side-channel_attack).
|
||||
Parts of my freetime are dedicated to playing Capture-The-Flag competitions (usually with my team [saarsec](https://saarsec.rocks)).
|
||||
Thus, I will be looking forward to showing some cool challenge solutions in the [Blogs](/blog) section.
|
||||
40
docs/posts/hello.md
Normal file
40
docs/posts/hello.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
title: Hello
|
||||
date: 2025-01-07
|
||||
---
|
||||
|
||||
Some excerpt for the blog post teasing what it is _about_
|
||||
|
||||
---
|
||||
|
||||
[[TOC]]
|
||||
|
||||
## Some test blog post
|
||||
|
||||
with some lorem ipsum text
|
||||
|
||||
```python
|
||||
def hello_world() -> str:
|
||||
return "Hello, World!"
|
||||
|
||||
if __name__ == "__main__":
|
||||
string = hello_world()
|
||||
print(string)
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Highlights information that users should take into account, even when skimming.
|
||||
|
||||
```js
|
||||
export default {
|
||||
name: "MyComponent",
|
||||
};
|
||||
```
|
||||
|
||||
## Hello 2
|
||||
|
||||
some more `text` with _awesome_ **formatting**
|
||||
|
||||
---
|
||||
|
||||
some more text
|
||||
33
docs/posts/hello2.md
Normal file
33
docs/posts/hello2.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
layout: blog
|
||||
title: Another Hello
|
||||
date: 2025-01-07
|
||||
---
|
||||
|
||||
Some excerpt for the blog post teasing what it is _about_
|
||||
|
||||
---
|
||||
|
||||
[[TOC]]
|
||||
|
||||
## Some test blog post
|
||||
|
||||
with some lorem ipsum text
|
||||
|
||||
```py
|
||||
def hello_world() -> str:
|
||||
return "Hello, World!"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
string = hello_world()
|
||||
print(string)
|
||||
```
|
||||
|
||||
## Hello 2
|
||||
|
||||
some more `text` with _awesome_ _formatting_
|
||||
|
||||
---
|
||||
|
||||
some more text
|
||||
19
docs/public/icons/email.svg
Normal file
19
docs/public/icons/email.svg
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg height="800px" width="800px" version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512" xml:space="preserve" fill="currentColor">
|
||||
<g>
|
||||
<path d="M510.746,110.361c-2.128-10.754-6.926-20.918-13.926-29.463c-1.422-1.794-2.909-3.39-4.535-5.009
|
||||
c-12.454-12.52-29.778-19.701-47.531-19.701H67.244c-17.951,0-34.834,7-47.539,19.708c-1.608,1.604-3.099,3.216-4.575,5.067
|
||||
c-6.97,8.509-11.747,18.659-13.824,29.428C0.438,114.62,0,119.002,0,123.435v265.137c0,9.224,1.874,18.206,5.589,26.745
|
||||
c3.215,7.583,8.093,14.772,14.112,20.788c1.516,1.509,3.022,2.901,4.63,4.258c12.034,9.966,27.272,15.45,42.913,15.45h377.51
|
||||
c15.742,0,30.965-5.505,42.967-15.56c1.604-1.298,3.091-2.661,4.578-4.148c5.818-5.812,10.442-12.49,13.766-19.854l0.438-1.05
|
||||
c3.646-8.377,5.497-17.33,5.497-26.628V123.435C512,119.06,511.578,114.649,510.746,110.361z M34.823,99.104
|
||||
c0.951-1.392,2.165-2.821,3.714-4.382c7.689-7.685,17.886-11.914,28.706-11.914h377.51c10.915,0,21.115,4.236,28.719,11.929
|
||||
c1.313,1.327,2.567,2.8,3.661,4.272l2.887,3.88l-201.5,175.616c-6.212,5.446-14.21,8.443-22.523,8.443
|
||||
c-8.231,0-16.222-2.99-22.508-8.436L32.19,102.939L34.823,99.104z M26.755,390.913c-0.109-0.722-0.134-1.524-0.134-2.341V128.925
|
||||
l156.37,136.411L28.199,400.297L26.755,390.913z M464.899,423.84c-6.052,3.492-13.022,5.344-20.145,5.344H67.244
|
||||
c-7.127,0-14.094-1.852-20.142-5.344l-6.328-3.668l159.936-139.379l17.528,15.246c10.514,9.128,23.922,14.16,37.761,14.16
|
||||
c13.89,0,27.32-5.032,37.827-14.16l17.521-15.253L471.228,420.18L464.899,423.84z M485.372,388.572
|
||||
c0,0.803-0.015,1.597-0.116,2.304l-1.386,9.472L329.012,265.409l156.36-136.418V388.572z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
3
docs/public/icons/github.svg
Normal file
3
docs/public/icons/github.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg" fill="currentColor">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 986 B |
3
docs/public/icons/linkedin.svg
Normal file
3
docs/public/icons/linkedin.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-linkedin" viewBox="0 0 16 16">
|
||||
<path d="M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854zm4.943 12.248V6.169H2.542v7.225zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248S2.4 3.226 2.4 3.934c0 .694.521 1.248 1.327 1.248zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016l.016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 666 B |
BIN
docs/public/images/me.jpeg
Normal file
BIN
docs/public/images/me.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 236 KiB |
2528
package-lock.json
generated
Normal file
2528
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
17
package.json
Normal file
17
package.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"vitepress": "^1.5.0",
|
||||
"@types/markdown-it": "^12.2.3",
|
||||
"@types/node": "^20.11.27",
|
||||
"vue": "^3.5.0-beta.3"
|
||||
},
|
||||
"scripts": {
|
||||
"docs:dev": "vitepress dev docs",
|
||||
"docs:build": "vitepress build docs",
|
||||
"docs:preview": "vitepress preview docs"
|
||||
},
|
||||
"dependencies": {
|
||||
"theme-toggles": "^4.10.1"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue