mirror of
https://github.com/ProjectSegfault/website.git
synced 2024-11-23 00:22:59 +05:30
use unocss, add blog, ui changes
This commit is contained in:
parent
a6c4014327
commit
ec15fac578
@ -1,4 +1,3 @@
|
|||||||
version: "3.9"
|
|
||||||
services:
|
services:
|
||||||
website:
|
website:
|
||||||
container_name: website
|
container_name: website
|
||||||
@ -6,6 +5,4 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
#build: .
|
#build: .
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
volumes:
|
|
||||||
- "./data:/usr/src/app/data"
|
|
@ -4,12 +4,10 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"build:docker": "docker build . -t website",
|
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"preview:docker": "pnpm build:docker && docker run --rm --name=website -p 80:80 website",
|
|
||||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ."
|
"format": "prettier -w --plugin-search-dir=. ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/fa6-solid": "^1.1.9",
|
"@iconify-json/fa6-solid": "^1.1.9",
|
||||||
|
@ -3,22 +3,26 @@
|
|||||||
export let position: any;
|
export let position: any;
|
||||||
export let description: any;
|
export let description: any;
|
||||||
export let icon: any;
|
export let icon: any;
|
||||||
export let positionStyles: any;
|
export let positionStyles: any;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="card-inner">
|
<div class="bg-secondary rounded-2 p-4 w-[18rem] sm:w-md flex flex-col">
|
||||||
<div class="main">
|
<div class="flex-1 flex flex-row gap-4">
|
||||||
{#if icon}
|
{#if icon}
|
||||||
<div>
|
<div>
|
||||||
<img src={icon} alt="{title} icon" />
|
<img
|
||||||
|
src={icon}
|
||||||
|
class="h-20 rounded-2"
|
||||||
|
alt="{title} icon"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span class="text-2xl font-bold">
|
||||||
{title}
|
{title}
|
||||||
|
|
||||||
{#if position}
|
{#if position}
|
||||||
<span>- </span>
|
<span>- </span>
|
||||||
<span style={positionStyles}>{position}</span>
|
<span style={positionStyles}>{position}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
@ -30,37 +34,3 @@
|
|||||||
</div>
|
</div>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
.card-inner {
|
|
||||||
background-color: var(--secondary);
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 1rem;
|
|
||||||
width: 30em;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 555px) {
|
|
||||||
.card-inner {
|
|
||||||
width: 18em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
height: 5rem;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 25px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<div class="card-outer">
|
<div class="flex gap-8 flex-row flex-wrap">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
div {
|
|
||||||
display: flex;
|
|
||||||
gap: 2rem;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -4,7 +4,10 @@
|
|||||||
export { classes as class };
|
export { classes as class };
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a href={url} class={classes}>
|
<a
|
||||||
|
href={url}
|
||||||
|
class="border-none rounded-2 p-2 cursor-pointer font-primary text-secondary decoration-none w-fit text-xl flex items-center {classes}"
|
||||||
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@ -14,50 +17,29 @@
|
|||||||
.picture,
|
.picture,
|
||||||
.pgp,
|
.pgp,
|
||||||
.link {
|
.link {
|
||||||
background-color: var(--alt);
|
@apply bg-alt text-alt-text transition-all duration-250;
|
||||||
color: var(--alt-text);
|
|
||||||
transition: all 0.25s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.web:hover,
|
.web:hover,
|
||||||
.email:hover,
|
.email:hover,
|
||||||
.picture:hover,
|
.picture:hover,
|
||||||
.pgp:hover {
|
.pgp:hover {
|
||||||
background-color: var(--accent);
|
@apply bg-accent text-alt;
|
||||||
color: var(--alt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.matrixcolored {
|
.matrixcolored {
|
||||||
background-color: var(--alt);
|
@apply bg-alt text-alt-text;
|
||||||
color: var(--alt-text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.discordcolored {
|
.discordcolored {
|
||||||
background-color: #5865f2;
|
@apply bg-[#5865f2] text-white;
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gitcolored {
|
.gitcolored {
|
||||||
background-color: #f05032;
|
@apply bg-[#f05032] text-white;
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.githubcolored {
|
.githubcolored {
|
||||||
background-color: var(--alt);
|
@apply bg-alt text-alt-text;
|
||||||
color: var(--alt-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
border: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 0.5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
font-family: var(--font-primary);
|
|
||||||
color: var(--secondary);
|
|
||||||
text-decoration: none;
|
|
||||||
width: fit-content;
|
|
||||||
font-size: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
<div>
|
<div class="flex flex-row gap-2">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
div {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
<script>
|
<footer class="flex flex-col text-xl sticky top-full">
|
||||||
import { page } from "$app/stores";
|
<div
|
||||||
</script>
|
class="flex items-center flex-col border-t border-grey p-2 children:(text-text text-sm font-500 text-center)"
|
||||||
|
>
|
||||||
<footer>
|
|
||||||
<div class="content">
|
|
||||||
<span>© 2021 - present, Project Segfault <a href="/team">team</a></span>
|
<span>© 2021 - present, Project Segfault <a href="/team">team</a></span>
|
||||||
<span
|
<span
|
||||||
>Made with <a href="https://kit.svelte.dev/">SvelteKit</a> and
|
>Made with <a href="https://kit.svelte.dev/">SvelteKit</a> and
|
||||||
@ -11,30 +9,9 @@
|
|||||||
<a href="https://opensource.org/licenses/MIT/">MIT license</a
|
<a href="https://opensource.org/licenses/MIT/">MIT license</a
|
||||||
>.</span
|
>.</span
|
||||||
>
|
>
|
||||||
|
<span
|
||||||
|
><a href="https://github.com/ProjectSegfault/website">Source code</a
|
||||||
|
></span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<style>
|
|
||||||
footer {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
font-size: 20px;
|
|
||||||
position: sticky;
|
|
||||||
top: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
border-top: 1px solid var(--grey);
|
|
||||||
padding: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.content > * {
|
|
||||||
color: var(--text);
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
let submit = false;
|
let submit = false;
|
||||||
|
|
||||||
let showSubmitButton = () => {
|
const showSubmitButton = () => {
|
||||||
submit = !submit;
|
submit = !submit;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -18,5 +18,9 @@
|
|||||||
on:success={showSubmitButton}
|
on:success={showSubmitButton}
|
||||||
/>
|
/>
|
||||||
{#if submit}
|
{#if submit}
|
||||||
<input type="submit" value="Submit" class="form-button" />
|
<input
|
||||||
|
type="submit"
|
||||||
|
value="Submit"
|
||||||
|
class="form-button"
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -4,43 +4,25 @@
|
|||||||
export let id: string;
|
export let id: string;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form {action} {method} {id}>
|
<form
|
||||||
|
{action}
|
||||||
|
{method}
|
||||||
|
{id}
|
||||||
|
class="flex flex-col gap-4 w-fit"
|
||||||
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.form-button) {
|
:global(.form-button) {
|
||||||
background-color: var(--secondary);
|
@apply bg-secondary border-none rounded-2 p-2 cursor-pointer text-text font-primary decoration-none;
|
||||||
border: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 0.5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--text);
|
|
||||||
font-family: var(--font-primary);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.form-button:not(select):hover) {
|
:global(.form-button:not(select):hover) {
|
||||||
background-color: var(--accent);
|
@apply bg-accent decoration-none transition-all duration-500 text-secondary;
|
||||||
text-decoration: none;
|
|
||||||
transition: all 0.5s;
|
|
||||||
color: var(--secondary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.form-textbox) {
|
:global(.form-textbox) {
|
||||||
background-color: var(--secondary);
|
@apply bg-secondary text-text rounded-2 border-none p-2 font-primary outline-none;
|
||||||
color: var(--text);
|
|
||||||
border-radius: 10px;
|
|
||||||
border: none;
|
|
||||||
padding: 0.5rem;
|
|
||||||
font-family: var(--font-primary);
|
|
||||||
outline: none;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
export let selectType: string;
|
export let selectType: string;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="meta">
|
<div
|
||||||
|
class="flex items-center flex-row gap-4 children:w-[50%] lt-sm:(flex-col items-start justify-center children:w-[calc(100%-1rem)])"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type={inputType}
|
type={inputType}
|
||||||
name={inputType}
|
name={inputType}
|
||||||
@ -12,35 +14,18 @@
|
|||||||
placeholder={inputPlaceholder}
|
placeholder={inputPlaceholder}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<select name={selectType} required class="form-button">
|
<select
|
||||||
|
name={selectType}
|
||||||
|
required
|
||||||
|
class="form-button"
|
||||||
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.meta {
|
@media screen and (max-width: 640px) {
|
||||||
display: flex;
|
div > :nth-child(2) {
|
||||||
align-items: center;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meta > * {
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 450px) {
|
|
||||||
.meta {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meta > * {
|
|
||||||
width: calc(100% - 1rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.meta > *:nth-child(2) {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,9 @@
|
|||||||
export let icon: string;
|
export let icon: string;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="note">
|
<div class="flex items-center gap-2">
|
||||||
{#if icon}
|
{#if icon}
|
||||||
<div class={icon} />
|
<div class={icon} />
|
||||||
{/if}
|
{/if}
|
||||||
<b>{content}</b>
|
<b>{content}</b>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
.note {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -10,12 +10,6 @@
|
|||||||
rows="4"
|
rows="4"
|
||||||
cols="25"
|
cols="25"
|
||||||
required
|
required
|
||||||
class="form-textbox"
|
class="form-textbox resize-y"
|
||||||
{placeholder}
|
{placeholder}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<style>
|
|
||||||
textarea {
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -6,38 +6,15 @@
|
|||||||
export { styles as style };
|
export { styles as style };
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="hero" style="margin-top: {marginTop}%; {styles}">
|
<div
|
||||||
|
class="flex flex-col items-center justify-center children:(m-4 p-0 text-center)"
|
||||||
|
style="margin-top: {marginTop}%; {styles}"
|
||||||
|
>
|
||||||
{#if title}
|
{#if title}
|
||||||
<h1>{title}</h1>
|
<h1 class="text-5xl font-800 text-accent">{title}</h1>
|
||||||
{/if}
|
{/if}
|
||||||
{#if description}
|
{#if description}
|
||||||
<p>{description}</p>
|
<p class="text-3xl text-text">{description}</p>
|
||||||
{/if}
|
{/if}
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
.hero {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero > * {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 30px;
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 50px;
|
|
||||||
font-weight: 800;
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -4,30 +4,16 @@
|
|||||||
export let title: string;
|
export let title: string;
|
||||||
export let bg: string;
|
export let bg: string;
|
||||||
export let color: string;
|
export let color: string;
|
||||||
|
export let styles: string;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a href={url} style="background-color: {bg}; color: {color};">
|
<a
|
||||||
|
href={url}
|
||||||
|
class="decoration-none bg-accent px-3 py-2 text-primary rounded-2 transition-filter hover:brightness-125 flex items-center w-fit gap-2"
|
||||||
|
style="background-color: {bg}; color: {color}; {styles}"
|
||||||
|
>
|
||||||
{#if icon}
|
{#if icon}
|
||||||
<div class={icon} />
|
<div class={icon} />
|
||||||
{/if}
|
{/if}
|
||||||
{title}
|
{title}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<style>
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
background-color: var(--accent);
|
|
||||||
padding: 8px 1em 8px 1em;
|
|
||||||
color: var(--primary);
|
|
||||||
border-radius: 10px;
|
|
||||||
transition: filter 0.25s;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
width: fit-content;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
filter: brightness(125%);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script>
|
||||||
|
//@ts-ignoreS
|
||||||
export let title;
|
export let title;
|
||||||
export let description;
|
export let description;
|
||||||
export let separator;
|
export let separator;
|
||||||
|
@ -4,186 +4,150 @@
|
|||||||
|
|
||||||
$: currentPage = $page.url.pathname;
|
$: currentPage = $page.url.pathname;
|
||||||
|
|
||||||
|
let showMenuButton = false;
|
||||||
|
|
||||||
|
let listStyles = "";
|
||||||
|
|
||||||
|
let innerWidth: number = 0;
|
||||||
|
|
||||||
|
$: showMenuButton = innerWidth < 1030;
|
||||||
|
|
||||||
|
let menuOpen = false;
|
||||||
|
|
||||||
|
$: menuOpen = innerWidth > 1030;
|
||||||
|
|
||||||
|
let showThemeToggle: boolean = true;
|
||||||
|
|
||||||
|
const toggleMenu = () => {
|
||||||
|
menuOpen = !menuOpen;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNavigation = () => {
|
||||||
|
if (showMenuButton) {
|
||||||
|
menuOpen = false;
|
||||||
|
} else {
|
||||||
|
menuOpen = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const menus = [
|
const menus = [
|
||||||
{ name: "Instances", url: "/instances" },
|
{ name: "Instances", url: "/instances" },
|
||||||
{ name: "Donate", url: "/donate" },
|
{ name: "Donate", url: "/donate" },
|
||||||
{ name: "FAQ", url: "/faq" },
|
|
||||||
{ name: "Contact us", url: "/contact" },
|
{ name: "Contact us", url: "/contact" },
|
||||||
{ name: "Our team", url: "/team" },
|
{ name: "Our team", url: "/team" },
|
||||||
{ name: "Timeline", url: "/timeline" },
|
{ name: "Timeline", url: "/timeline" },
|
||||||
{ name: "Blog", url: "https://blog.projectsegfau.lt/" },
|
{ name: "Blog", url: "/blog" },
|
||||||
{ name: "Legal", url: "/legal" },
|
{ name: "Legal", url: "/legal" },
|
||||||
{ name: "Status", url: "https://status.projectsegfau.lt/" }
|
{
|
||||||
|
name: "Status",
|
||||||
|
url: "https://status.projectsegfau.lt/",
|
||||||
|
external: true
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$: if (typeof Window === "undefined") {
|
||||||
|
menuOpen = true;
|
||||||
|
showMenuButton = false;
|
||||||
|
showThemeToggle = false;
|
||||||
|
listStyles = "list-styles";
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav>
|
<svelte:window bind:innerWidth />
|
||||||
<a class="brand" href="/">
|
|
||||||
<img src="/logo.png" alt="Project Segfault logo" />
|
|
||||||
<span>Project Segfault</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<input type="checkbox" id="toggle-menu" />
|
<nav
|
||||||
<label class="menu-icon" for="toggle-menu">
|
class="bg-primary border-b-1 border-b-grey flex p-2 flex-col justify-between nav:(flex-row items-center)"
|
||||||
<div id="menu-icon" class="i-fa6-solid:bars" />
|
>
|
||||||
</label>
|
<div class="flex flex-row items-center justify-between">
|
||||||
|
|
||||||
<div class="links">
|
|
||||||
{#each menus as { url, name }}
|
|
||||||
<a
|
|
||||||
data-sveltekit-preload-data
|
|
||||||
class:active={url !== "/"
|
|
||||||
? currentPage.match(url)
|
|
||||||
: url === currentPage}
|
|
||||||
href={url}>{name}</a
|
|
||||||
>
|
|
||||||
{/each}
|
|
||||||
<a
|
<a
|
||||||
href="https://matrix.to/#/#project-segfault:projectsegfau.lt/"
|
class="flex items-center decoration-none text-text gap-2 transition-opacity duration-250 hover:opacity-80"
|
||||||
class="icon"
|
href="/"
|
||||||
>
|
>
|
||||||
<div class="i-simple-icons:matrix" />
|
<img
|
||||||
<span>Matrix</span>
|
src="/logo.png"
|
||||||
|
alt="Project Segfault logo"
|
||||||
|
class="h-7"
|
||||||
|
/>
|
||||||
|
<span>Project Segfault</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/ProjectSegfault/" class="icon">
|
|
||||||
<div class="i-simple-icons:github" />
|
{#if showMenuButton}
|
||||||
<span>GitHub</span>
|
<button
|
||||||
</a>
|
on:click={toggleMenu}
|
||||||
<div class="theme-toggle icon">
|
class="i-fa6-solid:bars cursor-pointer mr-2"
|
||||||
<ThemeToggle />
|
/>
|
||||||
</div>
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if menuOpen}
|
||||||
|
<div
|
||||||
|
class="{listStyles} flex flex-col pt-2 nav:(flex-row pt-0) gap-2 links"
|
||||||
|
>
|
||||||
|
{#each menus as { url, name, external }}
|
||||||
|
<a
|
||||||
|
data-sveltekit-preload-data
|
||||||
|
class:active={url !== "/"
|
||||||
|
? currentPage.match(url)
|
||||||
|
: url === currentPage}
|
||||||
|
href={url}
|
||||||
|
on:click={handleNavigation}
|
||||||
|
>{#if external}
|
||||||
|
<div
|
||||||
|
class="i-fa6-solid:arrow-up-right-from-square mr-2"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{name}
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
<a
|
||||||
|
href="https://matrix.to/#/#project-segfault:projectsegfau.lt/"
|
||||||
|
class="icon"
|
||||||
|
>
|
||||||
|
<div class="i-simple-icons:matrix" />
|
||||||
|
<span>Matrix</span>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://github.com/ProjectSegfault/"
|
||||||
|
class="icon"
|
||||||
|
>
|
||||||
|
<div class="i-simple-icons:github" />
|
||||||
|
<span>GitHub</span>
|
||||||
|
</a>
|
||||||
|
{#if showThemeToggle}
|
||||||
|
<div class="icon">
|
||||||
|
<ThemeToggle />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<noscript>
|
|
||||||
<style>
|
|
||||||
.theme-toggle {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</noscript>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
nav {
|
|
||||||
background-color: var(--primary);
|
|
||||||
border-bottom: 1px solid var(--grey);
|
|
||||||
display: flex;
|
|
||||||
padding: 0.5rem;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
a.brand {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--text);
|
|
||||||
gap: 8px;
|
|
||||||
transition: opacity 0.25s;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.active {
|
a.active {
|
||||||
color: var(--accent);
|
@apply text-accent;
|
||||||
}
|
|
||||||
|
|
||||||
a.brand:hover {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.links {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.links > * {
|
.links > * {
|
||||||
padding: 0.5rem;
|
@apply p-2 cursor-pointer text-text decoration-none transition-color duration-25 text-sm font-500 flex items-center hover\:text-accent;
|
||||||
cursor: pointer;
|
|
||||||
color: var(--text);
|
|
||||||
text-decoration: none;
|
|
||||||
transition: color 0.25s;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.links > *:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#toggle-menu {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon > span {
|
.icon > span {
|
||||||
display: none;
|
@apply flex nav\:hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 900px) {
|
.icon {
|
||||||
.links {
|
@apply flex flex-row items-center gap-2;
|
||||||
display: none;
|
}
|
||||||
width: 100%;
|
|
||||||
padding-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
:global(.list-styles) {
|
||||||
|
@apply grid grid-cols-2 gap-2 w-fit links;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1030px) {
|
||||||
|
:global(.list-styles) {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.links a {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-icon {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 1;
|
|
||||||
position: absolute;
|
|
||||||
right: 1rem;
|
|
||||||
top: 0.5rem;
|
|
||||||
border: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
height: 30px;
|
|
||||||
width: 30px;
|
|
||||||
cursor: pointer;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#menu-icon {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
#toggle-menu:checked ~ .links {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon > span {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
display: flex !important;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 0.5rem;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"name": "akisblack",
|
||||||
|
"description": "I am a soydev maintaining the website and api.",
|
||||||
|
"position": "Web dev",
|
||||||
|
"website": "https://akisblack.github.io/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Midou",
|
"name": "Midou",
|
||||||
|
"description": "A random person with an obsession to online cat pics, also happens to be the sysadmin of the project",
|
||||||
"matrix": "https://matrix.to/#/@midou:projectsegfau.lt/",
|
"matrix": "https://matrix.to/#/@midou:projectsegfau.lt/",
|
||||||
"position": "Sysadmin",
|
"position": "Sysadmin",
|
||||||
"description": "I don't need to describe myself.",
|
|
||||||
"git": "https://github.com/Midou36O/",
|
"git": "https://github.com/Midou36O/",
|
||||||
"website": "https://midou36o.github.io/",
|
"website": "https://midou36o.github.io/",
|
||||||
"email": "midou@projectsegfau.lt",
|
"email": "midou@projectsegfau.lt",
|
||||||
@ -22,12 +28,5 @@
|
|||||||
"position": "Sysadmin",
|
"position": "Sysadmin",
|
||||||
"description": "\"I am openssl_rand, a system administrator of the project Segfau.lt.\" - GitHub Copilot",
|
"description": "\"I am openssl_rand, a system administrator of the project Segfau.lt.\" - GitHub Copilot",
|
||||||
"email": "mailto:openssl_rand@projectsegfau.lt"
|
"email": "mailto:openssl_rand@projectsegfau.lt"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "stephenvk",
|
|
||||||
"matrix": "https://matrix.to/#/@stephenvk:projectsegfau.lt",
|
|
||||||
"position": "Backup host",
|
|
||||||
"description": "I am Stephenvk and I host a server that takes our backups.",
|
|
||||||
"website": "https://stephenvk.xyz"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -17,17 +17,8 @@
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
on:click={toggle}
|
on:click={toggle}
|
||||||
class="cursor-pointer flex items-center py-1 px-0 bg-transparent border-0 font-[var(--font-primary)] color-[var(--text)]"
|
class="cursor-pointer flex items-center py-1 px-0 bg-transparent border-0 font-primary color-text"
|
||||||
>
|
>
|
||||||
<div class="i-fa6-solid:{theme === 'dark' ? 'sun' : 'moon'}" />
|
<div class="i-fa6-solid:{theme === 'dark' ? 'sun' : 'moon'}" />
|
||||||
<span class="ml-2">Toggle theme</span>
|
<span class="ml-2 nav:(hidden ml-1)">Toggle theme</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<style>
|
|
||||||
@media screen and (min-width: 900px) {
|
|
||||||
span {
|
|
||||||
display: none;
|
|
||||||
margin-left: 0.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: Raleway;
|
font-family: "JetBrains Mono";
|
||||||
src: url("/Raleway.ttf");
|
src: url("/JetBrainsMono.ttf");
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
--accent: #00a584;
|
--accent: #00a584;
|
||||||
--accent-translucent: #00a58498;
|
--accent-translucent: #00a58498;
|
||||||
--font-primary: Raleway;
|
--font-primary: "JetBrains Mono", monospace;
|
||||||
--font-header: Raleway;
|
|
||||||
--primary: #151515;
|
--primary: #151515;
|
||||||
--secondary: #252525;
|
--secondary: #252525;
|
||||||
--tertiary: #353535;
|
--tertiary: #353535;
|
||||||
@ -20,44 +19,32 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
html.light {
|
html.light {
|
||||||
--primary: #dddddd;
|
--primary: #ffffff;
|
||||||
--secondary: #f9f3f3;
|
--secondary: #eeeeee;
|
||||||
--tertiary: #939393;
|
--tertiary: #939393;
|
||||||
--text: #444444;
|
--text: #444444;
|
||||||
--grey: #444444;
|
--grey: #cecece;
|
||||||
--alt: #ddd;
|
--alt: #ddd;
|
||||||
--alt-text: #333;
|
--alt-text: #333;
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: var(--font-primary);
|
@apply font-primary bg-primary text-text m-0 flex flex-col relative min-h-screen leading-relaxed transition-all duration-250;
|
||||||
background-color: var(--primary);
|
|
||||||
color: var(--text);
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
position: relative;
|
|
||||||
min-height: 100vh;
|
|
||||||
line-height: 1.625;
|
|
||||||
transition: all 0.25s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
background-color: var(--accent-translucent);
|
@apply bg-accentTranslucent;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
padding: 1rem;
|
@apply p-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: underline;
|
@apply underline text-accent underline-offset-5 transition-filter duration-250;
|
||||||
color: var(--accent);
|
|
||||||
text-underline-offset: 5px;
|
|
||||||
transition: filter 0.25s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
filter: brightness(125%);
|
@apply brightness-125;
|
||||||
}
|
}
|
||||||
|
6
src/routes/+error.svelte
Normal file
6
src/routes/+error.svelte
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1>{$page.status}: {$page.error?.message}</h1>
|
||||||
|
<span class="italic">The fetch request to the API probably failed, please contact us on Matrix.</span>
|
@ -30,7 +30,8 @@
|
|||||||
<script
|
<script
|
||||||
defer
|
defer
|
||||||
data-domain="projectsegfau.lt"
|
data-domain="projectsegfau.lt"
|
||||||
src="https://analytics.projectsegfau.lt/js/plausible.js"></script>
|
src="https://analytics.projectsegfau.lt/js/plausible.js"
|
||||||
|
></script>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Nav />
|
<Nav />
|
||||||
|
@ -3,10 +3,16 @@ import { compile } from "mdsvex";
|
|||||||
import { env } from "$env/dynamic/private";
|
import { env } from "$env/dynamic/private";
|
||||||
|
|
||||||
export const load: PageServerLoad = async () => {
|
export const load: PageServerLoad = async () => {
|
||||||
return {
|
return {
|
||||||
state: await fetch(env.VITE_API_URL + "/api/v1/state/announcements").then((res) => res.json()),
|
state: await fetch(
|
||||||
announcements: await fetch(env.VITE_API_URL + "/api/v1/announcements").then((res) => res.json()),
|
env.VITE_API_URL + "/api/v1/state/announcements"
|
||||||
content: await fetch(env.VITE_API_URL + "/api/v1/announcements").then((res) => res.json()).then((res) => compile(res.title)).then((res) => res.code)
|
).then((res) => res.json()).catch(() => ({})),
|
||||||
}
|
announcements: await fetch(
|
||||||
}
|
env.VITE_API_URL + "/api/v1/announcements"
|
||||||
|
).then((res) => res.json()).catch(() => ({})),
|
||||||
|
content: await fetch(env.VITE_API_URL + "/api/v1/announcements")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((res) => compile(res.title))
|
||||||
|
.then((res) => res?.code).catch(() => ({})),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -2,25 +2,27 @@
|
|||||||
import SvelteSeo from "svelte-seo";
|
import SvelteSeo from "svelte-seo";
|
||||||
import Hero from "$lib/Hero.svelte";
|
import Hero from "$lib/Hero.svelte";
|
||||||
import LinkButton from "$lib/LinkButton.svelte";
|
import LinkButton from "$lib/LinkButton.svelte";
|
||||||
import dayjs from "dayjs";
|
import Announcements from "./Announcements.svelte";
|
||||||
import type { PageData } from "./$types";
|
import type { PageData } from "./$types";
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
let announcements = data.announcements;
|
|
||||||
|
|
||||||
let description: string = "Open source development and hosted services.";
|
let description: string = "Open source development and hosted services.";
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SvelteSeo title="Home | Project Segfault" {description} />
|
<SvelteSeo
|
||||||
|
title="Home | Project Segfault"
|
||||||
|
{description}
|
||||||
|
/>
|
||||||
|
|
||||||
<Hero
|
<Hero
|
||||||
title="Project Segfault"
|
title="Project Segfault"
|
||||||
{description}
|
{description}
|
||||||
marginTop=7
|
marginTop="4"
|
||||||
>
|
>
|
||||||
<div class="buttons">
|
<div
|
||||||
|
class="flex flex-col sm:flex-row justify-center items-center gap-4 m-4"
|
||||||
|
>
|
||||||
<LinkButton
|
<LinkButton
|
||||||
url="/instances"
|
url="/instances"
|
||||||
title="Explore our instances"
|
title="Explore our instances"
|
||||||
@ -36,117 +38,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</Hero>
|
</Hero>
|
||||||
|
|
||||||
{#if data.state.enabled}
|
<Announcements {data} />
|
||||||
{#if !announcements.error}
|
|
||||||
<div class="announcements">
|
|
||||||
<div class="announcement-container">
|
|
||||||
<div class="announcement">
|
|
||||||
<div class="flex gap-4 flex-col sm:flex-row border-b-2 p-2 pt-0">
|
|
||||||
{#if announcements.severity === "info"}
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<div class="i-fa6-solid:circle-info" />
|
|
||||||
<span>Info</span>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<div class="i-fa6-solid:triangle-exclamation" />
|
|
||||||
<span>Attention</span>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<span class="flex items-center gap-2">
|
|
||||||
<div class="i-fa6-solid:user" />
|
|
||||||
{announcements.author}
|
|
||||||
</span>
|
|
||||||
<span class="flex items-center gap-2">
|
|
||||||
<div class="i-fa6-solid:calendar" />
|
|
||||||
{dayjs
|
|
||||||
.unix(announcements.created)
|
|
||||||
.format("DD/MM/YYYY HH:mm")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="title">
|
|
||||||
<div class="text-xl font-semibold font-[var(--font-primary)]">{@html data.content}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if announcements.link}
|
|
||||||
<div class="read-more">
|
|
||||||
<a href={announcements.link}>Read more...</a>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if announcements.severity === "info"}
|
|
||||||
<style>
|
|
||||||
.announcement {
|
|
||||||
background-color: #8caaee;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{:else if announcements.severity === "low"}
|
|
||||||
<style>
|
|
||||||
.announcement {
|
|
||||||
background-color: #a6d189;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{:else if announcements.severity === "medium"}
|
|
||||||
<style>
|
|
||||||
.announcement {
|
|
||||||
background-color: #e5c890;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{:else if announcements.severity === "high"}
|
|
||||||
<style>
|
|
||||||
.announcement {
|
|
||||||
background-color: #e78284;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.announcement-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.announcement {
|
|
||||||
color: #252525 !important;
|
|
||||||
padding: 1.5rem;
|
|
||||||
border-radius: 10px;
|
|
||||||
width: fit-content;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.announcement a {
|
|
||||||
color: #252525;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<div class="flex items-center gap-1 text-center justify-center mt-16">
|
|
||||||
<div class="i-fa6-solid:circle-info" />
|
|
||||||
<span>Announcements are currently disabled.</span>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.buttons {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
gap: 1rem;
|
|
||||||
margin: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 452px) {
|
|
||||||
.buttons {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
102
src/routes/Announcements.svelte
Normal file
102
src/routes/Announcements.svelte
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
|
||||||
|
export let data: PageData;
|
||||||
|
|
||||||
|
let announcements = data.announcements;
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if data.state.enabled}
|
||||||
|
{#if !announcements.error}
|
||||||
|
<div class="announcements">
|
||||||
|
<div class="flex justify-center mt-16">
|
||||||
|
<div
|
||||||
|
class="announcement !text-[#252525] p-6 rounded-2 w-fit flex flex-col gap-4"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex gap-4 flex-col sm:flex-row border-b-2 p-2 pt-0"
|
||||||
|
>
|
||||||
|
{#if announcements.severity === "info"}
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<div class="i-fa6-solid:circle-info" />
|
||||||
|
<span>Info</span>
|
||||||
|
</div>
|
||||||
|
{:else if announcements.severity === "low"}
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<div class="i-fa6-solid:check" />
|
||||||
|
<span>Resolved</span>
|
||||||
|
</div>
|
||||||
|
{:else if announcements.severity === "medium"}
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<div class="i-fa6-solid:triangle-exclamation" />
|
||||||
|
<span>Attention</span>
|
||||||
|
</div>
|
||||||
|
{:else if announcements.severity === "high"}
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<div class="i-fa6-solid:ban" />
|
||||||
|
<span>Attention</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<span class="flex items-center gap-2">
|
||||||
|
<div class="i-fa6-solid:user" />
|
||||||
|
{announcements.author}
|
||||||
|
</span>
|
||||||
|
<span class="flex items-center gap-2">
|
||||||
|
<div class="i-fa6-solid:calendar" />
|
||||||
|
{dayjs
|
||||||
|
.unix(announcements.created)
|
||||||
|
.format("DD/MM/YYYY HH:mm")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="title">
|
||||||
|
<div class="text-xl font-semibold font-primary">
|
||||||
|
{@html data.content}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if announcements.link}
|
||||||
|
<div class="read-more">
|
||||||
|
<a
|
||||||
|
href={announcements.link}
|
||||||
|
class="!text-[#252525]">Read more...</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if announcements.severity === "info"}
|
||||||
|
<style>
|
||||||
|
.announcement {
|
||||||
|
background-color: #8caaee;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{:else if announcements.severity === "low"}
|
||||||
|
<style>
|
||||||
|
.announcement {
|
||||||
|
background-color: #a6d189;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{:else if announcements.severity === "medium"}
|
||||||
|
<style>
|
||||||
|
.announcement {
|
||||||
|
background-color: #e5c890;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{:else if announcements.severity === "high"}
|
||||||
|
<style>
|
||||||
|
.announcement {
|
||||||
|
background-color: #e78284;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<div class="flex items-center gap-2 text-center justify-center mt-16">
|
||||||
|
<div class="i-fa6-solid:circle-info" />
|
||||||
|
<span>Announcements are currently disabled.</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
10
src/routes/blog/+layout.server.ts
Normal file
10
src/routes/blog/+layout.server.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import { env } from "$env/dynamic/private";
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async () => {
|
||||||
|
return {
|
||||||
|
state: await fetch(env.VITE_API_URL + "/api/v1/state/blog").then(
|
||||||
|
(res) => res.json()
|
||||||
|
).catch(() => ({}))
|
||||||
|
};
|
||||||
|
};
|
14
src/routes/blog/+layout.svelte
Normal file
14
src/routes/blog/+layout.svelte
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
|
||||||
|
export let data: PageData
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if data.state.enabled}
|
||||||
|
<slot />
|
||||||
|
{:else}
|
||||||
|
<div class="flex items-center gap-2 text-center justify-center mt-16">
|
||||||
|
<div class="i-fa6-solid:circle-info" />
|
||||||
|
<span>The blog is currently disabled.</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
10
src/routes/blog/+page.server.ts
Normal file
10
src/routes/blog/+page.server.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import { env } from "$env/dynamic/private";
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async () => {
|
||||||
|
return {
|
||||||
|
posts: await fetch(env.VITE_API_URL + "/api/v1/blog").then(
|
||||||
|
(res) => res.json()
|
||||||
|
).catch(() => ({}))
|
||||||
|
};
|
||||||
|
};
|
61
src/routes/blog/+page.svelte
Normal file
61
src/routes/blog/+page.svelte
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import Hero from "$lib/Hero.svelte";
|
||||||
|
import LinkButton from "$lib/LinkButton.svelte";
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
export let data: PageData;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Timeline | Project Segfault</title>
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Timeline of Project Segfault's history."
|
||||||
|
/>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<Hero marginTop="4">
|
||||||
|
<h1 class="text-5xl font-800">
|
||||||
|
<span class="text-accent">Project Segfault</span> blog
|
||||||
|
</h1>
|
||||||
|
<div
|
||||||
|
class="flex flex-col sm:flex-row justify-center items-center gap-4 m-4"
|
||||||
|
>
|
||||||
|
<LinkButton
|
||||||
|
url="/blog/tags"
|
||||||
|
title="Tags"
|
||||||
|
icon="i-fa6-solid:tags"
|
||||||
|
/>
|
||||||
|
<LinkButton
|
||||||
|
url="/blog/authors"
|
||||||
|
title="Authors"
|
||||||
|
icon="i-fa6-solid:user"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Hero>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-10 mt-16">
|
||||||
|
{#each data.posts as post}
|
||||||
|
<div class="flex flex-col gap-4 bg-secondary p-4 rounded-2">
|
||||||
|
<span class="text-xl font-bold">{post.title}</span>
|
||||||
|
<div class="flex flex-col md:(flex-row gap-4) gap-2">
|
||||||
|
{#if post.tags.length > 0}
|
||||||
|
<div class="flex flex-row items-center gap-2">
|
||||||
|
<div class="i-fa6-solid:tags" />
|
||||||
|
{#each post.tags as tag}
|
||||||
|
<a href="/blog/tags/{tag}" class="no-underline">{tag}</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<a href="/blog/authors/{post.author}" class="flex items-center gap-2 no-underline"><div class="i-fa6-solid:user" />{post.author}</a>
|
||||||
|
<span class="flex items-center gap-2"><div class="i-fa6-solid:calendar" /> {dayjs
|
||||||
|
.unix(post.created)
|
||||||
|
.format("ddd, DD MMM YYYY HH:mm")}</span>
|
||||||
|
<span class="flex items-center gap-2"><div class="i-fa6-solid:pencil" /> {post.words} words</span>
|
||||||
|
<span class="flex items-center gap-2"><div class="i-fa6-solid:book-open-reader" /> {post.readingTime} minute read</span>
|
||||||
|
</div>
|
||||||
|
<span>{post.content.split(" ").slice(0, 20).join(" ") + "..."}</span>
|
||||||
|
<a href="/blog/{post.title}">Read more...</a>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
16
src/routes/blog/[title]/+page.server.ts
Normal file
16
src/routes/blog/[title]/+page.server.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import { env } from "$env/dynamic/private";
|
||||||
|
import { compile } from "mdsvex";
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ params }) => {
|
||||||
|
return {
|
||||||
|
post: await fetch(env.VITE_API_URL + "/api/v1/blog/" + params.title).then(
|
||||||
|
(res) => res.json()
|
||||||
|
).catch(() => ({})),
|
||||||
|
content: await fetch(env.VITE_API_URL + "/api/v1/blog/" + params.title)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((res) => compile(res.content))
|
||||||
|
.then((res) => res?.code)
|
||||||
|
.catch(() => ({})),
|
||||||
|
};
|
||||||
|
};
|
26
src/routes/blog/[title]/+page.svelte
Normal file
26
src/routes/blog/[title]/+page.svelte
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
export let data: PageData;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-4 bg-secondary p-4 rounded-2">
|
||||||
|
<span class="text-xl font-bold flex flex-row items-center gap-2"><a href="/blog" class="flex flex-row items-center gap-2"><div class="i-fa6-solid:arrow-left" /> Back</a> - {data.post.title}</span>
|
||||||
|
<div class="flex flex-col md:(flex-row gap-4) gap-2">
|
||||||
|
{#if data.post.tags.length > 0}
|
||||||
|
<div class="flex flex-row items-center gap-2">
|
||||||
|
<div class="i-fa6-solid:tags" />
|
||||||
|
{#each data.post.tags as tag}
|
||||||
|
<a href="/blog/tags/{tag}" class="no-underline">{tag}</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<a href="/blog/authors/{data.post.author}" class="flex items-center gap-2 no-underline"><div class="i-fa6-solid:user" />{data.post.author}</a>
|
||||||
|
<span class="flex items-center gap-2"><div class="i-fa6-solid:calendar" /> {dayjs
|
||||||
|
.unix(data.post.created)
|
||||||
|
.format("ddd, DD MMM YYYY HH:mm")}</span>
|
||||||
|
<span class="flex items-center gap-2"><div class="i-fa6-solid:pencil" /> {data.post.words} words</span>
|
||||||
|
<span class="flex items-center gap-2"><div class="i-fa6-solid:book-open-reader" /> {data.post.readingTime} minute read</span>
|
||||||
|
</div>
|
||||||
|
{@html data.content}
|
||||||
|
</div>
|
10
src/routes/blog/authors/+page.server.ts
Normal file
10
src/routes/blog/authors/+page.server.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import { env } from "$env/dynamic/private";
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ params }) => {
|
||||||
|
return {
|
||||||
|
authors: await fetch(env.VITE_API_URL + "/api/v1/blog/authors").then(
|
||||||
|
(res) => res.json()
|
||||||
|
).catch(() => ({})),
|
||||||
|
};
|
||||||
|
};
|
12
src/routes/blog/authors/+page.svelte
Normal file
12
src/routes/blog/authors/+page.svelte
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
export let data: PageData;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1>Blog authors</h1>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
{#each data.authors as author}
|
||||||
|
<a href="/blog/authors/{author}" class="bg-secondary w-fit p-2 rounded-2 no-underline">{author}</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
11
src/routes/blog/authors/[author]/+page.server.ts
Normal file
11
src/routes/blog/authors/[author]/+page.server.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import { env } from "$env/dynamic/private";
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ params }) => {
|
||||||
|
return {
|
||||||
|
posts: await fetch(env.VITE_API_URL + "/api/v1/blog/authors/" + params.author).then(
|
||||||
|
(res) => res.json()
|
||||||
|
).catch(() => ({})),
|
||||||
|
authorName: params.author
|
||||||
|
};
|
||||||
|
};
|
33
src/routes/blog/authors/[author]/+page.svelte
Normal file
33
src/routes/blog/authors/[author]/+page.svelte
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
export let data: PageData;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1>Blog author {data.authorName}</h1>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-10">
|
||||||
|
{#each data.posts as post}
|
||||||
|
<div class="flex flex-col gap-4 bg-secondary p-4 rounded-2">
|
||||||
|
<span class="text-xl font-bold">{post.title}</span>
|
||||||
|
<div class="flex flex-col md:(flex-row gap-4) gap-2">
|
||||||
|
{#if post.tags.length > 0}
|
||||||
|
<div class="flex flex-row items-center gap-2">
|
||||||
|
<div class="i-fa6-solid:tags" />
|
||||||
|
{#each post.tags as tag}
|
||||||
|
<a href="/blog/tags/{tag}" class="no-underline">{tag}</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<a href="/blog/authors/{post.author}" class="flex items-center gap-2 no-underline"><div class="i-fa6-solid:user" />{post.author}</a>
|
||||||
|
<span class="flex items-center gap-2"><div class="i-fa6-solid:calendar" /> {dayjs
|
||||||
|
.unix(post.created)
|
||||||
|
.format("ddd, DD MMM YYYY HH:mm")}</span>
|
||||||
|
<span class="flex items-center gap-2"><div class="i-fa6-solid:pencil" /> {post.words} words</span>
|
||||||
|
<span class="flex items-center gap-2"><div class="i-fa6-solid:book-open-reader" /> {post.readingTime} minute read</span>
|
||||||
|
</div>
|
||||||
|
<span>{post.content.split(" ").slice(0, 20).join(" ") + "..."}</span>
|
||||||
|
<a href="/blog/{post.title}">Read more...</a>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
10
src/routes/blog/tags/+page.server.ts
Normal file
10
src/routes/blog/tags/+page.server.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import { env } from "$env/dynamic/private";
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ params }) => {
|
||||||
|
return {
|
||||||
|
tags: await fetch(env.VITE_API_URL + "/api/v1/blog/tags").then(
|
||||||
|
(res) => res.json()
|
||||||
|
).catch(() => ({})),
|
||||||
|
};
|
||||||
|
};
|
12
src/routes/blog/tags/+page.svelte
Normal file
12
src/routes/blog/tags/+page.svelte
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
export let data: PageData;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1>Blog tags</h1>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
{#each data.tags as tag}
|
||||||
|
<a href="/blog/tags/{tag}" class="bg-secondary w-fit p-2 rounded-2 no-underline">{tag}</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
11
src/routes/blog/tags/[tag]/+page.server.ts
Normal file
11
src/routes/blog/tags/[tag]/+page.server.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import { env } from "$env/dynamic/private";
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ params }) => {
|
||||||
|
return {
|
||||||
|
posts: await fetch(env.VITE_API_URL + "/api/v1/blog/tags/" + params.tag).then(
|
||||||
|
(res) => res.json()
|
||||||
|
).catch(() => ({})),
|
||||||
|
tagName: params.tag
|
||||||
|
};
|
||||||
|
};
|
33
src/routes/blog/tags/[tag]/+page.svelte
Normal file
33
src/routes/blog/tags/[tag]/+page.svelte
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
export let data: PageData;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1>Blog tag {data.tagName}</h1>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-10">
|
||||||
|
{#each data.posts as post}
|
||||||
|
<div class="flex flex-col gap-4 bg-secondary p-4 rounded-2">
|
||||||
|
<span class="text-xl font-bold">{post.title}</span>
|
||||||
|
<div class="flex flex-col md:(flex-row gap-4) gap-2">
|
||||||
|
{#if post.tags.length > 0}
|
||||||
|
<div class="flex flex-row items-center gap-2">
|
||||||
|
<div class="i-fa6-solid:tags" />
|
||||||
|
{#each post.tags as tag}
|
||||||
|
<a href="/blog/tags/{tag}" class="no-underline">{tag}</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<a href="/blog/authors/{post.author}" class="flex items-center gap-2 no-underline"><div class="i-fa6-solid:user" />{post.author}</a>
|
||||||
|
<span class="flex items-center gap-2"><div class="i-fa6-solid:calendar" /> {dayjs
|
||||||
|
.unix(post.created)
|
||||||
|
.format("ddd, DD MMM YYYY HH:mm")}</span>
|
||||||
|
<span class="flex items-center gap-2"><div class="i-fa6-solid:pencil" /> {post.words} words</span>
|
||||||
|
<span class="flex items-center gap-2"><div class="i-fa6-solid:book-open-reader" /> {post.readingTime} minute read</span>
|
||||||
|
</div>
|
||||||
|
<span>{post.content.split(" ").slice(0, 20).join(" ") + "..."}</span>
|
||||||
|
<a href="/blog/{post.title}">Read more...</a>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
@ -2,8 +2,10 @@ import type { PageServerLoad } from "../$types";
|
|||||||
import { env } from "$env/dynamic/private";
|
import { env } from "$env/dynamic/private";
|
||||||
|
|
||||||
export const load: PageServerLoad = async () => {
|
export const load: PageServerLoad = async () => {
|
||||||
return {
|
return {
|
||||||
state: await fetch(env.VITE_API_URL + "/api/v1/state/form").then((res) => res.json()),
|
state: await fetch(env.VITE_API_URL + "/api/v1/state/form").then(
|
||||||
apiUrl: env.VITE_API_URL
|
(res) => res.json()
|
||||||
}
|
).catch(() => ({})),
|
||||||
}
|
apiUrl: env.VITE_API_URL
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -1,60 +1,65 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Note, Captcha, Form, Meta, TextArea } from "$lib/Form";
|
import { Note, Captcha, Form, Meta, TextArea } from "$lib/Form";
|
||||||
import type { PageData } from "./$types";
|
import type { PageData } from "./$types";
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
let title = "Contact us | Project Segfault";
|
let title = "Contact us | Project Segfault";
|
||||||
let description = "Do you want to contact us? (you don't)";
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<meta name="description" content={description}>
|
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<h1>Contact us</h1>
|
<h1>Contact us</h1>
|
||||||
|
|
||||||
<span>{description}</span>
|
|
||||||
|
|
||||||
<div class="contact-form">
|
<div class="contact-form">
|
||||||
<h2>Contact form</h2>
|
<h2>Contact form</h2>
|
||||||
{#if data.state.enabled === true}
|
{#if data.state.enabled === true}
|
||||||
<Form
|
<Form
|
||||||
action="{data.apiUrl}/api/v1/form"
|
action="{data.apiUrl}/api/v1/form"
|
||||||
method="POST"
|
method="POST"
|
||||||
id="contact-form"
|
id="contact-form"
|
||||||
>
|
>
|
||||||
<Note
|
<Note
|
||||||
content="Your IP will be logged for anti-abuse measures."
|
content="Your IP will be logged for anti-abuse measures."
|
||||||
icon="i-fa6-solid:lock"
|
icon="i-fa6-solid:lock"
|
||||||
/>
|
/>
|
||||||
<Meta
|
<Meta
|
||||||
inputType="email"
|
inputType="email"
|
||||||
inputPlaceholder="Your email"
|
inputPlaceholder="Your email"
|
||||||
selectType="commentType"
|
selectType="commentType"
|
||||||
>
|
>
|
||||||
<option value="" selected disabled
|
<option
|
||||||
>Select a type of comment</option
|
value=""
|
||||||
>
|
selected
|
||||||
<option value="Feedback">Feedback</option>
|
disabled>Select a type of comment</option
|
||||||
<option value="Suggestion">Suggestion</option>
|
>
|
||||||
<option value="Question">Question</option>
|
<option value="Feedback">Feedback</option>
|
||||||
<option value="Bug">Bug</option>
|
<option value="Suggestion">Suggestion</option>
|
||||||
</Meta>
|
<option value="Question">Question</option>
|
||||||
<TextArea id="comment" name="message" placeholder="Your message" />
|
<option value="Bug">Bug</option>
|
||||||
<Captcha />
|
</Meta>
|
||||||
</Form>
|
<TextArea
|
||||||
{:else}
|
id="comment"
|
||||||
<div class="flex items-center gap-1">
|
name="message"
|
||||||
<div class="i-fa6-solid:circle-info" />
|
placeholder="Your message"
|
||||||
<span>The contact form is currently disabled.</span>
|
/>
|
||||||
</div>
|
<Captcha />
|
||||||
{/if}
|
</Form>
|
||||||
|
{:else}
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<div class="i-fa6-solid:circle-info" />
|
||||||
|
<span>The contact form is currently disabled.</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<noscript>
|
<noscript>
|
||||||
<Note content="The contact form does not work without JavaScript enabled." icon="i-fa6-solid:circle-info" />
|
<Note
|
||||||
|
content="The contact form does not work without JavaScript enabled."
|
||||||
|
icon="i-fa6-solid:circle-info"
|
||||||
|
/>
|
||||||
<style>
|
<style>
|
||||||
.contact-form {
|
.contact-form {
|
||||||
display: none;
|
display: none;
|
||||||
@ -62,16 +67,29 @@
|
|||||||
</style>
|
</style>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
|
<h2>Matrix</h2>
|
||||||
|
<span
|
||||||
|
>We have a Matrix space for general discussion, support and announcements
|
||||||
|
about Project Segfault over at <a
|
||||||
|
href="https://matrix.to/#/#project-segfault:projectsegfau.lt/"
|
||||||
|
>this link</a
|
||||||
|
>.</span
|
||||||
|
>
|
||||||
|
|
||||||
<h2>Our Email</h2>
|
<h2>Our Email</h2>
|
||||||
|
|
||||||
<a href="mailto:contact@projectsegfau.lt">contact@projectsegfau.lt</a>
|
<a href="mailto:contact@projectsegfau.lt">contact@projectsegfau.lt</a>
|
||||||
|
|
||||||
<span class="italic">
|
<span class="italic">
|
||||||
Please be aware that Microsoft often blocks non-popular emails, if you do contact us through there, make sure to check your spam and mark it as not-spam!
|
Please be aware that Microsoft often blocks non-popular emails, if you do
|
||||||
|
contact us through there, make sure to check your spam and mark it as
|
||||||
|
not-spam!
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<h2>People</h2>
|
<h2>People</h2>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
You can find ways to contact individual team members <a href="/team">on our team page</a>.
|
You can find ways to contact individual team members <a href="/team"
|
||||||
|
>on our team page</a
|
||||||
|
>.
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
title: Frequently Asked Questions
|
|
||||||
description: Frequently Asked Questions
|
|
||||||
---
|
|
||||||
|
|
||||||
# { title }
|
|
||||||
|
|
||||||
## Who is the project owner?
|
|
||||||
|
|
||||||
There are two owners that operate Project Segfault, Midou and Mrlerien. A list of the people involved and their positions can be found [on our members page](/team).
|
|
||||||
|
|
||||||
## What's the backstory to Project Segfault?
|
|
||||||
|
|
||||||
We have a rather interesting backstory, if I do say so myself. [Click here to see a timeline of things that happened in Project Segfault's history](/timeline).
|
|
||||||
|
|
||||||
## One of your services contains toxic people!
|
|
||||||
|
|
||||||
You can contact us by mail or Matrix and we can figure this out with you. But we recommend that you put most of these requests in our support channel at [#support:projectsegfau.lt](https://matrix.to/#/#support:projectsegfau.lt) on Matrix. If it's something personal, just say that you have a report against someone on one of our services and you'd like to be contacted by an admin and we'll contact you as soon as possible. We generally tend to be active throughout the day.
|
|
||||||
|
|
||||||
## How can I trust your services?
|
|
||||||
|
|
||||||
Well, you really can't. We don't make our logs or anything else public, however, if you would like access to the data we have on you, please contact us. If you're extremely privacy/security conscious, **you are allowed to use Tor on our services**, but we don't host any `.onion` links.
|
|
||||||
|
|
||||||
## Which ways do you prefer to communicate?
|
|
||||||
|
|
||||||
Look at [Contact](/contact).
|
|
@ -3,7 +3,11 @@ import { env } from "$env/dynamic/private";
|
|||||||
|
|
||||||
export const load: PageServerLoad = async () => {
|
export const load: PageServerLoad = async () => {
|
||||||
return {
|
return {
|
||||||
state: await fetch(env.VITE_API_URL + "/api/v1/state/status").then((res) => res.json()),
|
state: await fetch(env.VITE_API_URL + "/api/v1/state/status").then(
|
||||||
instances: await fetch(env.VITE_API_URL + "/api/v1/status").then((res) => res.json())
|
(res) => res.json()
|
||||||
}
|
).catch(() => ({})),
|
||||||
}
|
instances: await fetch(env.VITE_API_URL + "/api/v1/status").then(
|
||||||
|
(res) => res.json()
|
||||||
|
).catch(() => ({}))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { CardInner, CardOuter, LinksOuter } from "$lib/Card";
|
import { CardInner, CardOuter, LinksOuter } from "$lib/Card";
|
||||||
import InstanceLink from "./InstanceLink.svelte";
|
import InstanceLink from "./InstanceLink.svelte";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import type { PageData } from "./$types";
|
import type { PageData } from "./$types";
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Our instances | Project Segfault</title>
|
<title>Our instances | Project Segfault</title>
|
||||||
<meta name="description" content="Our collection of instances." />
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Our collection of instances."
|
||||||
|
/>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<h1>Our instances</h1>
|
<h1>Our instances</h1>
|
||||||
@ -17,10 +20,10 @@
|
|||||||
{#if data.state.enabled}
|
{#if data.state.enabled}
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<CardOuter>
|
<CardOuter>
|
||||||
<div class="wrapper">
|
<div class="flex flex-col">
|
||||||
{#each data.instances.status as group}
|
{#each data.instances.status as group}
|
||||||
<h2>{group.name}</h2>
|
<h2>{group.name}</h2>
|
||||||
<div class="items">
|
<div class="flex flex-row flex-wrap gap-8">
|
||||||
{#each group.data as item}
|
{#each group.data as item}
|
||||||
<CardInner
|
<CardInner
|
||||||
title={item.name}
|
title={item.name}
|
||||||
@ -28,14 +31,26 @@
|
|||||||
icon={item.icon}
|
icon={item.icon}
|
||||||
>
|
>
|
||||||
<LinksOuter>
|
<LinksOuter>
|
||||||
<InstanceLink url={item.link} item={item.status} type="main" />
|
<InstanceLink
|
||||||
|
url={item.link}
|
||||||
|
item={item.status}
|
||||||
|
type="main"
|
||||||
|
/>
|
||||||
|
|
||||||
{#if item.us}
|
{#if item.us}
|
||||||
<InstanceLink url={item.us} item={item.statusUs} type="us" />
|
<InstanceLink
|
||||||
|
url={item.us}
|
||||||
|
item={item.statusUs}
|
||||||
|
type="us"
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if item.bp}
|
{#if item.bp}
|
||||||
<InstanceLink url={item.bp} item={item.statusBp} type="backup" />
|
<InstanceLink
|
||||||
|
url={item.bp}
|
||||||
|
item={item.statusBp}
|
||||||
|
type="backup"
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</LinksOuter>
|
</LinksOuter>
|
||||||
</CardInner>
|
</CardInner>
|
||||||
@ -44,29 +59,16 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</CardOuter>
|
</CardOuter>
|
||||||
|
|
||||||
<span>Last updated: {dayjs
|
<span class="bg-accent w-fit p-2 rounded-2 text-primary"
|
||||||
.unix(data.instances.updated)
|
>Instances status last updated: {dayjs
|
||||||
.format("DD/MM/YYYY HH:mm:ss")}
|
.unix(data.instances.updated)
|
||||||
|
.format("DD/MM/YYYY HH:mm:ss")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
{:else}
|
||||||
<style>
|
<div class="flex items-center gap-2 mt-16">
|
||||||
.wrapper {
|
<div class="i-fa6-solid:circle-info" />
|
||||||
display: flex;
|
<span>Instances are currently disabled.</span>
|
||||||
flex-direction: column;
|
</div>
|
||||||
}
|
|
||||||
|
|
||||||
.items {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 2rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{:else}
|
|
||||||
<div class="flex items-center gap-1 mt-16">
|
|
||||||
<div class="i-fa6-solid:circle-info" />
|
|
||||||
<span>Instances are currently disabled.</span>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -1,31 +1,34 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Link } from "$lib/Card";
|
import { Link } from "$lib/Card";
|
||||||
|
|
||||||
export let url: string;
|
export let url: string;
|
||||||
export let item: any;
|
export let item: any;
|
||||||
export let type: "main" | "us" | "backup";
|
export let type: "main" | "us" | "backup";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Link {url} class="web {item === true ? "" : "pointer-events-none cursor-default opacity-50"}">
|
<Link
|
||||||
<div class="withText">
|
{url}
|
||||||
<div class="{item === true ? "i-fa6-solid:arrow-up-right-from-square" : "i-fa6-solid:xmark"}" />
|
class="web {item === 200
|
||||||
<span>
|
? ''
|
||||||
{#if type === "main"}
|
: 'pointer-events-none cursor-default opacity-50'}"
|
||||||
Visit
|
>
|
||||||
{:else if type === "us"}
|
<div class="flex items-center gap-2 text-base">
|
||||||
US
|
<div
|
||||||
{:else if type === "backup"}
|
class={item === 200
|
||||||
Backup
|
? "i-fa6-solid:arrow-up-right-from-square"
|
||||||
{/if}
|
: "i-fa6-solid:xmark"}
|
||||||
</span>
|
/>
|
||||||
</div>
|
<span>
|
||||||
|
{#if item !== 200}
|
||||||
|
({item})
|
||||||
|
{/if}
|
||||||
|
{#if type === "main"}
|
||||||
|
Visit
|
||||||
|
{:else if type === "us"}
|
||||||
|
US
|
||||||
|
{:else if type === "backup"}
|
||||||
|
Backup
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<style>
|
|
||||||
.withText {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
font-size: medium;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -9,3 +9,13 @@ Since we care about transparency, privacy and safety we have created some docume
|
|||||||
- [Privacy Policy](/legal/privacy-policy)
|
- [Privacy Policy](/legal/privacy-policy)
|
||||||
- [Terms of Service](/legal/tos)
|
- [Terms of Service](/legal/tos)
|
||||||
- [Transparency reports](https://git.projectsegfau.lt/ProjectSegfault/transparency/)
|
- [Transparency reports](https://git.projectsegfau.lt/ProjectSegfault/transparency/)
|
||||||
|
|
||||||
|
## Legal FAQ
|
||||||
|
|
||||||
|
### One of your services contains toxic people!
|
||||||
|
|
||||||
|
You can contact us by mail or Matrix and we can figure this out with you. But we recommend that you put most of these requests in our support channel at [#support:projectsegfau.lt](https://matrix.to/#/#support:projectsegfau.lt) on Matrix. If it's something personal, just say that you have a report against someone on one of our services and you'd like to be contacted by an admin and we'll contact you as soon as possible. We generally tend to be active throughout the day.
|
||||||
|
|
||||||
|
### How can I trust your services?
|
||||||
|
|
||||||
|
Well, you really can't. We don't make our logs or anything else public, however, if you would like access to the data we have on you, please contact us. If you're extremely privacy/security conscious, **you are allowed to use Tor on our services**, but we don't host any `.onion` links.
|
||||||
|
@ -5,58 +5,89 @@
|
|||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Our team | Project Segfault</title>
|
<title>Our team | Project Segfault</title>
|
||||||
<meta name="description" content="Team members of Project Segfault." />
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Team members of Project Segfault."
|
||||||
|
/>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<h1>Our team</h1>
|
<h1>Our team</h1>
|
||||||
<CardOuter>
|
<CardOuter>
|
||||||
{#each members as { name, discord, matrix, position, description, github, git, pgp, website, email, picture }}
|
{#each members as { name, discord, matrix, position, description, github, git, pgp, website, email, picture }}
|
||||||
<CardInner title={name} {position} {description}>
|
<CardInner
|
||||||
|
title={name}
|
||||||
|
{position}
|
||||||
|
{description}
|
||||||
|
>
|
||||||
<LinksOuter>
|
<LinksOuter>
|
||||||
{#if matrix}
|
{#if matrix}
|
||||||
<Link url={matrix} class="matrixcolored">
|
<Link
|
||||||
|
url={matrix}
|
||||||
|
class="matrixcolored"
|
||||||
|
>
|
||||||
<div class="i-simple-icons:matrix" />
|
<div class="i-simple-icons:matrix" />
|
||||||
</Link>
|
</Link>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if discord}
|
{#if discord}
|
||||||
<Link url={discord} class="discordcolored">
|
<Link
|
||||||
|
url={discord}
|
||||||
|
class="discordcolored"
|
||||||
|
>
|
||||||
<div class="i-simple-icons:discord" />
|
<div class="i-simple-icons:discord" />
|
||||||
</Link>
|
</Link>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if github}
|
{#if github}
|
||||||
<Link url={github} class="githubcolored">
|
<Link
|
||||||
|
url={github}
|
||||||
|
class="githubcolored"
|
||||||
|
>
|
||||||
<div class="i-simple-icons:github" />
|
<div class="i-simple-icons:github" />
|
||||||
</Link>
|
</Link>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if git}
|
{#if git}
|
||||||
<Link url={git} class="gitcolored">
|
<Link
|
||||||
|
url={git}
|
||||||
|
class="gitcolored"
|
||||||
|
>
|
||||||
<div class="i-simple-icons:git" />
|
<div class="i-simple-icons:git" />
|
||||||
</Link>
|
</Link>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if website}
|
{#if website}
|
||||||
<Link url={website} class="web">
|
<Link
|
||||||
|
url={website}
|
||||||
|
class="web"
|
||||||
|
>
|
||||||
<div class="i-fa6-solid:globe" />
|
<div class="i-fa6-solid:globe" />
|
||||||
</Link>
|
</Link>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if email}
|
{#if email}
|
||||||
<Link url="mailto:{email}" class="email">
|
<Link
|
||||||
|
url="mailto:{email}"
|
||||||
|
class="email"
|
||||||
|
>
|
||||||
<div class="i-fa6-solid:envelope" />
|
<div class="i-fa6-solid:envelope" />
|
||||||
</Link>
|
</Link>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if pgp}
|
{#if pgp}
|
||||||
<Link url={pgp} class="pgp">
|
<Link
|
||||||
|
url={pgp}
|
||||||
|
class="pgp"
|
||||||
|
>
|
||||||
<div class="i-fa6-solid:key" />
|
<div class="i-fa6-solid:key" />
|
||||||
</Link>
|
</Link>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if picture}
|
{#if picture}
|
||||||
<Link url={picture} class="picture">
|
<Link
|
||||||
|
url={picture}
|
||||||
|
class="picture"
|
||||||
|
>
|
||||||
<div class="i-fa6-solid:camera" />
|
<div class="i-fa6-solid:camera" />
|
||||||
</Link>
|
</Link>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -19,8 +19,11 @@
|
|||||||
/>
|
/>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Hero>
|
<Hero marginTop="4">
|
||||||
<h1>A timeline of <span>Project Segfault</span>'s history</h1>
|
<h1 class="text-5xl font-800">
|
||||||
|
A timeline of <span class="text-accent">Project Segfault</span>'s
|
||||||
|
history
|
||||||
|
</h1>
|
||||||
</Hero>
|
</Hero>
|
||||||
|
|
||||||
<Timeline position="alternate">
|
<Timeline position="alternate">
|
||||||
@ -245,14 +248,3 @@
|
|||||||
</TimelineContent>
|
</TimelineContent>
|
||||||
</TimelineItem>
|
</TimelineItem>
|
||||||
</Timeline>
|
</Timeline>
|
||||||
|
|
||||||
<style>
|
|
||||||
h1 {
|
|
||||||
font-size: 50px;
|
|
||||||
font-weight: 800;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
BIN
static/JetBrainsMono.ttf
Normal file
BIN
static/JetBrainsMono.ttf
Normal file
Binary file not shown.
Binary file not shown.
@ -20,6 +20,34 @@ export default defineConfig({
|
|||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
|
||||||
|
theme: {
|
||||||
|
fontFamily: {
|
||||||
|
primary: ["var(--font-primary)"]
|
||||||
|
},
|
||||||
|
margin: {
|
||||||
|
"0-auto": "0 auto"
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
accent: "var(--accent)",
|
||||||
|
accentTranslucent: "var(--accent-translucent)",
|
||||||
|
primary: "var(--primary)",
|
||||||
|
secondary: "var(--secondary)",
|
||||||
|
tertiary: "var(--tertiary)",
|
||||||
|
text: "var(--text)",
|
||||||
|
grey: "var(--grey)",
|
||||||
|
alt: "var(--alt)",
|
||||||
|
altText: "var(--alt-text)"
|
||||||
|
},
|
||||||
|
breakpoints: {
|
||||||
|
sm: "640px",
|
||||||
|
md: "768px",
|
||||||
|
lg: "1024px",
|
||||||
|
xl: "1280px",
|
||||||
|
"2xl": "1536px",
|
||||||
|
nav: "1030px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
transformers: [transformerVariantGroup(), transformerDirectives()],
|
transformers: [transformerVariantGroup(), transformerDirectives()],
|
||||||
|
|
||||||
safelist: ["i-fa6-solid:moon", "i-fa6-solid:sun"]
|
safelist: ["i-fa6-solid:moon", "i-fa6-solid:sun"]
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { sveltekit } from "@sveltejs/kit/vite";
|
import { sveltekit } from "@sveltejs/kit/vite";
|
||||||
import unoCSS from "unocss/vite";
|
import unoCSS from "unocss/vite";
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
/** @type {import('vite').UserConfig} */
|
const config = defineConfig({
|
||||||
const config = {
|
|
||||||
plugins: [sveltekit(), unoCSS()]
|
plugins: [sveltekit(), unoCSS()]
|
||||||
};
|
});
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
Loading…
Reference in New Issue
Block a user