get poetry stuff workin
This commit is contained in:
@@ -1,7 +1,52 @@
|
||||
<script lang="ts">
|
||||
import App from '$lib/components/scenes/app/App.svelte';
|
||||
<script>
|
||||
import '../../app.css';
|
||||
</script>
|
||||
|
||||
<App>
|
||||
<slot />
|
||||
</App>
|
||||
<div class="flex flex-col h-screen">
|
||||
<div class="navbar bg-base-300 sticky top-0 z-50">
|
||||
<div class="navbar-start">
|
||||
<div class="dropdown">
|
||||
<div tabindex="-1" class="btn btn-ghost lg:hidden">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M4 6h16M4 12h8m-8 6h16"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<ul
|
||||
tabindex="-1"
|
||||
class="menu menu-sm dropdown-content mt-3 z-10 p-2 shadow bg-base-300 rounded-box w-52"
|
||||
>
|
||||
<li><a href="/thoughts">Thoughts</a></li>
|
||||
<li><a href="/poetry">Poetry</a></li>
|
||||
<li><a href="/projects">Projects</a></li>
|
||||
<li><a href="/experiments">Experiments</a></li>
|
||||
<li><a href="/services">Services</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<a class="btn btn-outline btn-primary text-xl" href="/">silentsilas</a>
|
||||
</div>
|
||||
<div class="navbar-end hidden lg:flex">
|
||||
<ul class="menu menu-horizontal px-1">
|
||||
<li><a href="/thoughts">Thoughts</a></li>
|
||||
<li><a href="/poetry">Poetry</a></li>
|
||||
<li><a href="/projects">Projects</a></li>
|
||||
<li><a href="/experiments">Experiments</a></li>
|
||||
<li><a href="/services">Services</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center flex-1 overflow-auto">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,23 +1,106 @@
|
||||
<script lang="ts">
|
||||
import MenuItem from '$lib/components/scenes/app/MenuItem.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
type Greeting = {
|
||||
greeting: string;
|
||||
language: string;
|
||||
romanisation?: string;
|
||||
};
|
||||
const GREETINGS: Greeting[] = [
|
||||
{ greeting: '你好', language: 'Mandarin', romanisation: 'Nǐ hǎo' },
|
||||
{ greeting: 'नमस्ते', language: 'Hindi', romanisation: 'Namaste' },
|
||||
{ greeting: 'Hola', language: 'Spanish' },
|
||||
{ greeting: 'হ্যালো', language: 'Bengali', romanisation: 'Hyālō' },
|
||||
{ greeting: 'ہیلو', language: 'Urdu', romanisation: 'Hello' },
|
||||
{ greeting: 'Oi', language: 'Portuguese' },
|
||||
{ greeting: 'Здравствуйте', language: 'Russian', romanisation: 'Zdravstvuyte' },
|
||||
{ greeting: 'こんにちは', language: 'Japanese', romanisation: "Kon'nichiwa" },
|
||||
{ greeting: 'Merhaba', language: 'Turkish' },
|
||||
{ greeting: '안녕하세요', language: 'Korean', romanisation: 'Annyeonghaseyo' },
|
||||
{ greeting: 'Hallo', language: 'German' },
|
||||
{ greeting: 'สวัสดี', language: 'Thai', romanisation: 'S̄wạs̄dī' },
|
||||
{ greeting: 'مرحبا', language: 'Arabic', romanisation: 'Marhaba' },
|
||||
{ greeting: 'היי', language: 'Hebrew', romanisation: 'Hi' },
|
||||
{ greeting: 'Helo', language: 'Welsh' },
|
||||
{ greeting: 'Pozdrav', language: 'Croatian' },
|
||||
{ greeting: 'Hej', language: 'Swedish' },
|
||||
{ greeting: 'Hyālō', language: 'Bengali' },
|
||||
{ greeting: 'Salut', language: 'French' },
|
||||
{ greeting: 'Ciao', language: 'Italian' }
|
||||
];
|
||||
|
||||
let greetings = [...GREETINGS];
|
||||
|
||||
let currentGreeting: Greeting = { greeting: 'Hello', language: 'English' };
|
||||
let visible = false;
|
||||
|
||||
onMount(() => {
|
||||
visible = true;
|
||||
const interval = setInterval(getRandomGreeting, 3000);
|
||||
return () => clearInterval(interval);
|
||||
});
|
||||
|
||||
async function getRandomGreeting() {
|
||||
visible = false;
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
|
||||
if (greetings.length === 0) {
|
||||
greetings = [...GREETINGS];
|
||||
}
|
||||
|
||||
const greetingIndex = Math.floor(Math.random() * greetings.length);
|
||||
const greeting = greetings[greetingIndex];
|
||||
greetings = greetings.filter((_, index) => index !== greetingIndex);
|
||||
currentGreeting = greeting;
|
||||
visible = true;
|
||||
|
||||
return greeting;
|
||||
}
|
||||
</script>
|
||||
|
||||
<MenuItem position={[-2.5, 7.5, 0]} htmlContent="Poetry" href="/poetry" />
|
||||
|
||||
<MenuItem
|
||||
position={[2.5, 2.5, -2.5]}
|
||||
htmlContent="Projects"
|
||||
clickHandler={() => console.log('clicked!')}
|
||||
/>
|
||||
|
||||
<MenuItem
|
||||
position={[-2.5, -2.5, -2.5]}
|
||||
htmlContent="Thoughts"
|
||||
clickHandler={() => console.log('clicked!')}
|
||||
/>
|
||||
|
||||
<MenuItem
|
||||
position={[2.5, -7.5, 2.5]}
|
||||
htmlContent="About"
|
||||
clickHandler={() => console.log('clicked!')}
|
||||
/>
|
||||
<div class="container mx-auto flex flex-col justify-center items-center flex-1">
|
||||
<div class="justify-center items-center text-center m-10">
|
||||
{#if visible && currentGreeting}
|
||||
<div
|
||||
transition:fade={{ duration: 1200 }}
|
||||
>
|
||||
<span class="font-bold">{currentGreeting.greeting}</span>
|
||||
{#if currentGreeting.romanisation}
|
||||
<span class="text-gray-500">( {currentGreeting.romanisation} )</span>
|
||||
{/if}
|
||||
</div>
|
||||
<p class="mt-2 text-gray-700" transition:fade={{ delay: 400, duration: 400 }}>
|
||||
That's {currentGreeting.language} for hello!
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="text-center prose px-4">
|
||||
<p>
|
||||
The name's Silas. I write code for a living, and sometimes for fun. I use <a
|
||||
href="https://elixir-lang.org/"
|
||||
target="_blank">Elixir</a
|
||||
>
|
||||
at my day job, and recently have been messing around with
|
||||
<a href="https://elixir-lang.org/" target="_blank">Rust</a>,
|
||||
<a href="https://kit.svelte.dev/" target="_blank">Svelte</a>, and
|
||||
<a href="https://threejs.org/" target="_blank">three.js</a>
|
||||
</p>
|
||||
<p>
|
||||
Here you can browse my shower <a href="/thoughts" class="link">thoughts</a> and bad
|
||||
<a href="/poetry" class="link">poetry</a>. Opinions are personally mine and not endorsed by my
|
||||
employer.
|
||||
</p>
|
||||
<p>
|
||||
I tend to start a lot of <a href="/projects" class="link">projects</a>, but I'm trying to
|
||||
finish more. I also like to toy with weird web technologies and will host the
|
||||
<a href="/experiments" class="link">experiments</a> here.
|
||||
</p>
|
||||
<p>
|
||||
I self-host a lot of <a href="/services" class="link">services</a> I find useful. None of them
|
||||
run any analytics or log your activity, but the software/servers may be outdated, so use at your
|
||||
own risk.
|
||||
</p>
|
||||
<p>Shalom.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
15
src/routes/(app)/poetry/+page.server.ts
Normal file
15
src/routes/(app)/poetry/+page.server.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export const load = async ({ fetch, url }) => {
|
||||
const limit = 8;
|
||||
|
||||
const page = Number(url.searchParams.get('page')) || 1;
|
||||
|
||||
const response = await fetch(`/api/poetry?limit=${limit}&page=${page}`);
|
||||
const { posts, total } = await response.json();
|
||||
|
||||
return {
|
||||
posts,
|
||||
total,
|
||||
page,
|
||||
limit
|
||||
};
|
||||
};
|
@@ -1,39 +1,70 @@
|
||||
<script lang="ts">
|
||||
import MenuItem from '$lib/components/scenes/app/MenuItem.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import type { PageData } from '../poetry/$types';
|
||||
export let data: PageData;
|
||||
|
||||
const xMin = -2.5,
|
||||
xMax = 2.5;
|
||||
const yMin = -7.5,
|
||||
yMax = 7.5;
|
||||
const zMin = -2.5,
|
||||
zMax = 2.5;
|
||||
|
||||
// Calculate positions
|
||||
const calculatePositions = (numPosts: number): Array<[number, number, number]> => {
|
||||
const positions: Array<[number, number, number]> = [];
|
||||
const numRows = Math.ceil(Math.sqrt(numPosts));
|
||||
const numCols = Math.ceil(numPosts / numRows);
|
||||
|
||||
for (let i = 0; i < numPosts; i++) {
|
||||
const row = Math.floor(i / numCols);
|
||||
const col = i % numCols;
|
||||
|
||||
const x = xMin + (xMax - xMin) * (col / (numCols - 1));
|
||||
const y = yMin + (yMax - yMin) * (row / (numRows - 1));
|
||||
const z = zMin;
|
||||
|
||||
positions.push([x, y, z]);
|
||||
}
|
||||
|
||||
return positions;
|
||||
const formatDate = (date: string) => {
|
||||
return new Date(date).toLocaleDateString(undefined, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
// Generate positions based on the number of posts
|
||||
const positions = calculatePositions(data.posts.length);
|
||||
let { posts, total } = data;
|
||||
const limit = 8;
|
||||
|
||||
let currentPage = Number($page.url.searchParams.get('page')) || 1;
|
||||
let totalPages = Math.ceil(total / limit);
|
||||
$: $page.url.searchParams.get('page'),
|
||||
(currentPage = Number($page.url.searchParams.get('page')) || 1);
|
||||
|
||||
async function fetchData(page: number) {
|
||||
const response = await fetch(`/api/poetry?limit=${limit}&page=${page}`);
|
||||
const newData = await response.json();
|
||||
currentPage = page;
|
||||
posts = newData.posts;
|
||||
total = newData.total;
|
||||
totalPages = Math.ceil(total / limit);
|
||||
}
|
||||
|
||||
function navigate(page: number) {
|
||||
fetchData(page);
|
||||
goto(`/poetry/?page=${page}`, { replaceState: true });
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each data.posts as post, i}
|
||||
<MenuItem position={positions[i]} htmlContent={post.meta.title} href={post.path} />
|
||||
{/each}
|
||||
<div class="container mx-auto flex flex-col items-center py-10">
|
||||
<div class="prose">
|
||||
<h1 class="py-6">Poetry</h1>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
{#each posts as post}
|
||||
<li class="py-4">
|
||||
<h3 class="pb-1">
|
||||
<a class="link" href={post.path}>
|
||||
{post.meta.title}
|
||||
</a>
|
||||
</h3>
|
||||
<p class="text-sm">{formatDate(post.meta.date)}</p>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{#if total > 1}
|
||||
<nav class="join justify-end">
|
||||
<button
|
||||
class="join-item btn-primary btn btn-outline"
|
||||
on:click={() => navigate(currentPage - 1)}
|
||||
disabled={currentPage === 1}>Prev</button
|
||||
>
|
||||
<button class="join-item btn btn-outline">{currentPage} of {totalPages}</button>
|
||||
<button
|
||||
class="join-item btn btn-primary btn-outline"
|
||||
on:click={() => navigate(currentPage + 1)}
|
||||
disabled={currentPage === totalPages}>Next</button
|
||||
>
|
||||
</nav>
|
||||
{/if}
|
||||
|
@@ -1,19 +1,16 @@
|
||||
<script lang="ts">
|
||||
import MenuItem from '$lib/components/scenes/app/MenuItem.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import DomPortal from '$lib/utils/DomPortal.svelte';
|
||||
export let data: PageData;
|
||||
|
||||
const { title, date: _date, Content, categories: _ } = data;
|
||||
</script>
|
||||
|
||||
<MenuItem position={[-2.5, 10, 0]} htmlContent="Back" href="/poetry" />
|
||||
|
||||
<DomPortal target="#overlay">
|
||||
<div class="py-4">
|
||||
<h1 class="text-xl font-medium text-black pb-2">{title}</h1>
|
||||
<div>
|
||||
<Content />
|
||||
</div>
|
||||
<div class="container mx-auto flex flex-col items-center py-10">
|
||||
<div class="prose">
|
||||
<h1 class="py-6">{title}</h1>
|
||||
</div>
|
||||
</DomPortal>
|
||||
<div class="prose">
|
||||
<Content />
|
||||
</div>
|
||||
<a href="/poetry" class="link mt-10">Back to Poetry</a>
|
||||
</div>
|
||||
|
@@ -13,6 +13,7 @@ export async function load({ params }) {
|
||||
Content,
|
||||
title,
|
||||
date: validDate,
|
||||
categories
|
||||
categories,
|
||||
post
|
||||
};
|
||||
}
|
||||
|
@@ -1,38 +1 @@
|
||||
<script lang="ts">
|
||||
import CanvasContainer from '$lib/components/scenes/app/CanvasContainer.svelte';
|
||||
import '../app.css';
|
||||
</script>
|
||||
|
||||
<CanvasContainer>
|
||||
<slot />
|
||||
</CanvasContainer>
|
||||
|
||||
<div class="overlay container" id="overlay"></div>
|
||||
|
||||
<style>
|
||||
:global(body) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgb(0, 36, 6);
|
||||
background: linear-gradient(180deg, rgba(0, 36, 6, 1) 0%, rgba(0, 0, 0, 1) 100%);
|
||||
position: absolute;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgba(16, 56, 30, 0.9);
|
||||
}
|
||||
</style>
|
||||
<slot />
|
||||
|
@@ -1,2 +1,2 @@
|
||||
export const prerender = true
|
||||
export const ssr = false
|
||||
export const ssr = true
|
||||
|
@@ -2,13 +2,14 @@ import { fetchMarkdownPosts } from '$lib/utils';
|
||||
import { json } from '@sveltejs/kit';
|
||||
|
||||
export const GET = async ({ url }) => {
|
||||
const limit = Number(url.searchParams.get('limit')) || 6;
|
||||
const offset = Number(url.searchParams.get('offset')) || 0;
|
||||
const allPosts = await fetchMarkdownPosts('poetry', limit, offset);
|
||||
const page = Number(url.searchParams.get('page')) || 1;
|
||||
const limit = Number(url.searchParams.get('limit')) || 8;
|
||||
const offset = (page - 1) * limit;
|
||||
const {posts: allPosts, total: total} = await fetchMarkdownPosts('poetry', limit, offset);
|
||||
|
||||
const sortedPosts = allPosts.sort((a, b) => {
|
||||
return new Date(b.meta.date).getTime() - new Date(a.meta.date).getTime();
|
||||
});
|
||||
|
||||
return json(sortedPosts);
|
||||
return json({posts: sortedPosts, total: total, page: page});
|
||||
};
|
||||
|
42
src/routes/projects/+layout.svelte
Normal file
42
src/routes/projects/+layout.svelte
Normal file
@@ -0,0 +1,42 @@
|
||||
<script lang="ts">
|
||||
import App from '$lib/components/scenes/app/App.svelte';
|
||||
import CanvasContainer from '$lib/components/scenes/app/CanvasContainer.svelte';
|
||||
import '../../app.css'
|
||||
</script>
|
||||
|
||||
<CanvasContainer>
|
||||
<App>
|
||||
<slot />
|
||||
</App>
|
||||
|
||||
</CanvasContainer>
|
||||
|
||||
<div class="overlay container" id="overlay"></div>
|
||||
|
||||
<style>
|
||||
:global(body) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgb(0, 36, 6);
|
||||
background: linear-gradient(180deg, rgba(0, 36, 6, 1) 0%, rgba(0, 0, 0, 1) 100%);
|
||||
position: absolute;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgba(16, 56, 30, 0.9);
|
||||
}
|
||||
</style>
|
2
src/routes/projects/+layout.ts
Normal file
2
src/routes/projects/+layout.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const prerender = true
|
||||
export const ssr = false
|
37
src/routes/projects/+page.svelte
Normal file
37
src/routes/projects/+page.svelte
Normal file
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import MenuItem from '$lib/components/scenes/app/MenuItem.svelte';
|
||||
import type { PageData } from './$types';
|
||||
export let data: PageData;
|
||||
|
||||
const xMin = -2.5,
|
||||
xMax = 2.5;
|
||||
const yMin = -7.5,
|
||||
yMax = 7.5;
|
||||
const zMin = -2.5,
|
||||
zMax = 2.5;
|
||||
|
||||
const calculatePositions = (numPosts: number): Array<[number, number, number]> => {
|
||||
const positions: Array<[number, number, number]> = [];
|
||||
const numRows = Math.ceil(Math.sqrt(numPosts));
|
||||
const numCols = Math.ceil(numPosts / numRows);
|
||||
|
||||
for (let i = 0; i < numPosts; i++) {
|
||||
const row = Math.floor(i / numCols);
|
||||
const col = i % numCols;
|
||||
|
||||
const x = xMin + (xMax - xMin) * (col / (numCols - 1));
|
||||
const y = yMin + (yMax - yMin) * (row / (numRows - 1));
|
||||
const z = zMin;
|
||||
|
||||
positions.push([x, y, z]);
|
||||
}
|
||||
|
||||
return positions;
|
||||
};
|
||||
|
||||
const positions = calculatePositions(data.posts.length);
|
||||
</script>
|
||||
|
||||
{#each data.posts as post, i}
|
||||
<MenuItem position={positions[i]} htmlContent={post.meta.title} href={post.path} />
|
||||
{/each}
|
Reference in New Issue
Block a user