Compare commits
5 Commits
b4e9159048
...
081e709ba5
Author | SHA1 | Date | |
---|---|---|---|
081e709ba5 | |||
25ee5d47e3 | |||
e76819be04 | |||
22507a6ec3 | |||
0e46dc943e |
@ -1,24 +1,19 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Window from './window.svelte'
|
import Window from './window.svelte';
|
||||||
import '../styles/app.css'
|
import '../styles/app.css';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: any;
|
title: string;
|
||||||
sticky: any;
|
sticky: boolean;
|
||||||
prose?: boolean;
|
prose?: boolean;
|
||||||
children?: import('svelte').Snippet;
|
children?: import('svelte').Snippet;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let { title, sticky, prose = true, children }: Props = $props();
|
||||||
title,
|
|
||||||
sticky,
|
|
||||||
prose = true,
|
|
||||||
children
|
|
||||||
}: Props = $props();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Window {title} {sticky}>
|
<Window {title} {sticky}>
|
||||||
<div class="{prose ? "prose prose-ralsei leading-6 prose-ul:leading-5" : ""}">
|
<div class={prose ? 'prose prose-ralsei leading-6 prose-ul:leading-5' : ''}>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
</Window>
|
</Window>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import { draggable } from '@neodrag/svelte';
|
import { draggable } from '@neodrag/svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: string | undefined;
|
title?: string;
|
||||||
iconUri?: string;
|
iconUri?: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
sticky?: boolean;
|
sticky?: boolean;
|
||||||
@ -25,31 +25,33 @@
|
|||||||
removePadding = false,
|
removePadding = false,
|
||||||
center = false,
|
center = false,
|
||||||
layered = false,
|
layered = false,
|
||||||
style = "",
|
style = '',
|
||||||
tooltip = false,
|
tooltip = false,
|
||||||
children
|
children
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
const scaleKeyframes = [
|
const scaleKeyframes = [
|
||||||
"window-open",
|
'window-open',
|
||||||
"window-open-vertical",
|
'window-open-vertical',
|
||||||
"window-open-vertical",
|
'window-open-vertical',
|
||||||
"window-open-horizontal",
|
'window-open-horizontal',
|
||||||
"window-open-horizontal",
|
'window-open-horizontal',
|
||||||
"window-open-move-up",
|
'window-open-move-up',
|
||||||
"window-open-move-down",
|
'window-open-move-down',
|
||||||
"window-open-move-left",
|
'window-open-move-left',
|
||||||
"window-open-move-right",
|
'window-open-move-right'
|
||||||
];
|
];
|
||||||
let chosenKeyframe = $derived(scaleKeyframes.at(Math.floor(Math.random() * scaleKeyframes.length)))
|
let chosenKeyframe = $derived(
|
||||||
|
scaleKeyframes.at(Math.floor(Math.random() * scaleKeyframes.length))
|
||||||
|
);
|
||||||
|
|
||||||
const isOnMobile = isMobile()
|
const isOnMobile = isMobile();
|
||||||
const _draggable = isOnMobile ? () => {} : draggable;
|
const _draggable = isOnMobile ? () => {} : draggable;
|
||||||
|
|
||||||
const focusWindow = (node: HTMLElement) => {
|
const focusWindow = (node: HTMLElement) => {
|
||||||
$highestZIndex += 1
|
$highestZIndex += 1;
|
||||||
node.style.zIndex = $highestZIndex.toString()
|
node.style.zIndex = $highestZIndex.toString();
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
@ -59,13 +61,23 @@
|
|||||||
disabled: isOnMobile,
|
disabled: isOnMobile,
|
||||||
applyUserSelectHack: true,
|
applyUserSelectHack: true,
|
||||||
handle: '.window-titlebar',
|
handle: '.window-titlebar',
|
||||||
onDragStart: (data) => {focusWindow(data.currentNode)},
|
onDragStart: (data) => {
|
||||||
|
focusWindow(data.currentNode);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onclick={(data) => {
|
||||||
|
focusWindow(data.currentTarget);
|
||||||
}}
|
}}
|
||||||
onclick={(data) => {focusWindow(data.currentTarget)}}
|
|
||||||
class="
|
class="
|
||||||
relative {layered ? "col-[1] row-[1]" : ""} flex flex-col {sticky ? 'md:sticky md:-top-9' : ''} {center ? "mx-auto" : ""}
|
relative {layered ? 'col-[1] row-[1]' : ''} flex flex-col {sticky
|
||||||
max-w-screen-md xl:max-w-screen-lg 2xl:max-w-screen-xl {tooltip ? "min-w-fit" : "min-w-[30ch] lg:min-w-[40ch]"} w-full md:w-fit [height:fit-content]
|
? 'md:sticky md:-top-9'
|
||||||
bg-ralsei-black border-ralsei-white border-ridge {tooltip ? "border-[6px] border-t-[9px]" : "border-8 border-t-[12px]"}
|
: ''} {center ? 'mx-auto' : ''}
|
||||||
|
max-w-screen-md xl:max-w-screen-lg 2xl:max-w-screen-xl {tooltip
|
||||||
|
? 'min-w-fit'
|
||||||
|
: 'min-w-[30ch] lg:min-w-[40ch]'} w-full md:w-fit [height:fit-content]
|
||||||
|
bg-ralsei-black border-ralsei-white border-ridge {tooltip
|
||||||
|
? 'border-[6px] border-t-[9px]'
|
||||||
|
: 'border-8 border-t-[12px]'}
|
||||||
animate-{chosenKeyframe} drop-shadow-[24px_24px_24px_rgba(1,1,1,0.8)]
|
animate-{chosenKeyframe} drop-shadow-[24px_24px_24px_rgba(1,1,1,0.8)]
|
||||||
{style}
|
{style}
|
||||||
"
|
"
|
||||||
@ -76,7 +88,7 @@
|
|||||||
class="
|
class="
|
||||||
window-titlebar p-1 border-ralsei-white border-8
|
window-titlebar p-1 border-ralsei-white border-8
|
||||||
bg-gradient-to-l from-ralsei-pink-neon to-ralsei-black to-75%
|
bg-gradient-to-l from-ralsei-pink-neon to-ralsei-black to-75%
|
||||||
{!isOnMobile ? "cursor-move" : ""}
|
{!isOnMobile ? 'cursor-move' : ''}
|
||||||
"
|
"
|
||||||
style="border-style: hidden hidden ridge hidden;"
|
style="border-style: hidden hidden ridge hidden;"
|
||||||
>
|
>
|
||||||
@ -100,10 +112,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="
|
<div
|
||||||
{removePadding ? "" : tooltip ? "p-1" : "p-2"} bg-gradient-to-tl
|
class="
|
||||||
|
{removePadding ? '' : tooltip ? 'p-1' : 'p-2'} bg-gradient-to-tl
|
||||||
to-ralsei-pink-neon/15 from-ralsei-pink-regular/20
|
to-ralsei-pink-neon/15 from-ralsei-pink-regular/20
|
||||||
">
|
"
|
||||||
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,48 +1,56 @@
|
|||||||
import { env } from '$env/dynamic/private'
|
import { env } from '$env/dynamic/private';
|
||||||
import { Bot, type Post } from "@skyware/bot";
|
import { Bot, type Post } from '@skyware/bot';
|
||||||
import { get, writable } from 'svelte/store'
|
import { get, writable } from 'svelte/store';
|
||||||
|
|
||||||
const bskyClient = writable<null | Bot>(null)
|
const bskyClient = writable<null | Bot>(null);
|
||||||
|
|
||||||
export const getBskyClient = async () => {
|
export const getBskyClient = async () => {
|
||||||
let client = get(bskyClient)
|
let client = get(bskyClient);
|
||||||
if (client === null) {
|
if (client === null) {
|
||||||
client = await loginToBsky()
|
client = await loginToBsky();
|
||||||
bskyClient.set(client)
|
bskyClient.set(client);
|
||||||
}
|
}
|
||||||
return client
|
return client;
|
||||||
}
|
};
|
||||||
|
|
||||||
const loginToBsky = async () => {
|
const loginToBsky = async () => {
|
||||||
const bot = new Bot({ service: "https://gaze.systems" })
|
const bot = new Bot({ service: 'https://gaze.systems' });
|
||||||
await bot.login({ identifier: 'guestbook.gaze.systems', password: env.BSKY_PASSWORD ?? "" })
|
await bot.login({ identifier: 'guestbook.gaze.systems', password: env.BSKY_PASSWORD ?? '' });
|
||||||
return bot
|
return bot;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getUserPosts = async (did: string, count: number = 10, cursor: string | null = null) => {
|
export const getUserPosts = async (
|
||||||
const client = await getBskyClient()
|
did: string,
|
||||||
let feedCursor: string | null | undefined = cursor;
|
count: number = 10,
|
||||||
let posts: Post[] = []
|
cursor: string | null = null
|
||||||
// fetch requested amount of posts
|
) => {
|
||||||
while (posts.length < count - 1 && (typeof feedCursor === "string" || feedCursor === null)) {
|
const client = await getBskyClient();
|
||||||
let feedData = await client.getUserPosts(
|
let feedCursor: string | null | undefined = cursor;
|
||||||
did, { limit: count, filter: 'posts_no_replies', cursor: feedCursor === null ? undefined : feedCursor }
|
const posts: Post[] = [];
|
||||||
)
|
// fetch requested amount of posts
|
||||||
posts.push(...feedData.posts.filter((post) => post.author.did === did))
|
while (posts.length < count - 1 && (typeof feedCursor === 'string' || feedCursor === null)) {
|
||||||
feedCursor = feedData.cursor
|
const feedData = await client.getUserPosts(did, {
|
||||||
}
|
limit: count,
|
||||||
return { posts, cursor: feedCursor === null ? undefined : feedCursor }
|
filter: 'posts_no_replies',
|
||||||
}
|
cursor: feedCursor === null ? undefined : feedCursor
|
||||||
|
});
|
||||||
|
posts.push(...feedData.posts.filter((post) => post.author.did === did));
|
||||||
|
feedCursor = feedData.cursor;
|
||||||
|
}
|
||||||
|
return { posts, cursor: feedCursor === null ? undefined : feedCursor };
|
||||||
|
};
|
||||||
|
|
||||||
const lastPosts = writable<Post[]>([])
|
const lastPosts = writable<Post[]>([]);
|
||||||
|
|
||||||
export const updateLastPosts = async () => {
|
export const updateLastPosts = async () => {
|
||||||
try {
|
try {
|
||||||
const { posts } = await getUserPosts("did:plc:dfl62fgb7wtjj3fcbb72naae", 13)
|
const { posts } = await getUserPosts('did:plc:dfl62fgb7wtjj3fcbb72naae', 13);
|
||||||
lastPosts.set(posts)
|
lastPosts.set(posts);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(`can't update last posts ${err}`)
|
console.log(`can't update last posts ${err}`);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getLastPosts = () => { return get(lastPosts) }
|
export const getLastPosts = () => {
|
||||||
|
return get(lastPosts);
|
||||||
|
};
|
||||||
|
@ -1,37 +1,45 @@
|
|||||||
import { get, writable } from "svelte/store"
|
import { get, writable } from 'svelte/store';
|
||||||
|
|
||||||
const GET_RECENT_TRACKS_ENDPOINT = "https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=yusdacra&api_key=da1911d405b5b37383e200b8f36ee9ec&format=json&limit=1"
|
const GET_RECENT_TRACKS_ENDPOINT =
|
||||||
|
'https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=yusdacra&api_key=da1911d405b5b37383e200b8f36ee9ec&format=json&limit=1';
|
||||||
|
|
||||||
type LastTrack = {
|
type LastTrack = {
|
||||||
name: string,
|
name: string;
|
||||||
artist: string,
|
artist: string;
|
||||||
image: string | null,
|
image: string | null;
|
||||||
link: string,
|
link: string;
|
||||||
when: number,
|
when: number;
|
||||||
playing: boolean,
|
playing: boolean;
|
||||||
}
|
};
|
||||||
const lastTrack = writable<LastTrack | null>(null)
|
const lastTrack = writable<LastTrack | null>(null);
|
||||||
|
|
||||||
export const lastFmUpdateNowPlaying = async () => {
|
export const lastFmUpdateNowPlaying = async () => {
|
||||||
try {
|
try {
|
||||||
var resp = await (await fetch(GET_RECENT_TRACKS_ENDPOINT)).json()
|
const resp = await (await fetch(GET_RECENT_TRACKS_ENDPOINT)).json();
|
||||||
var track = resp.recenttracks.track[0] ?? null
|
const track = resp.recenttracks.track[0] ?? null;
|
||||||
if (!((track['@attr'] ?? {}).nowplaying ?? null)) {
|
if (!((track['@attr'] ?? {}).nowplaying ?? null)) {
|
||||||
throw "no nowplaying track found"
|
throw 'no nowplaying track found';
|
||||||
}
|
}
|
||||||
var data = {
|
const data = {
|
||||||
name: track.name,
|
name: track.name,
|
||||||
artist: track.artist['#text'],
|
artist: track.artist['#text'],
|
||||||
image: track.image[2]['#text'] ?? null,
|
image: track.image[2]['#text'] ?? null,
|
||||||
link: track.url,
|
link: track.url,
|
||||||
when: Date.now(),
|
when: Date.now(),
|
||||||
playing: true,
|
playing: true
|
||||||
}
|
};
|
||||||
lastTrack.set(data)
|
lastTrack.set(data);
|
||||||
} catch(why) {
|
} catch (why) {
|
||||||
console.log("could not fetch last fm: ", why)
|
console.log('could not fetch last fm: ', why);
|
||||||
lastTrack.update((t) => { if (t !== null) { t.playing = false; } return t })
|
lastTrack.update((t) => {
|
||||||
}
|
if (t !== null) {
|
||||||
}
|
t.playing = false;
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const getNowPlaying = () => { return get(lastTrack) }
|
export const getNowPlaying = () => {
|
||||||
|
return get(lastTrack);
|
||||||
|
};
|
||||||
|
@ -1,48 +1,55 @@
|
|||||||
import { env } from "$env/dynamic/private";
|
import { env } from '$env/dynamic/private';
|
||||||
import SGDB from "steamgriddb";
|
import SGDB from 'steamgriddb';
|
||||||
import { get, writable } from "svelte/store";
|
import { get, writable } from 'svelte/store';
|
||||||
|
|
||||||
const STEAM_ID = "76561198106829949"
|
const STEAM_ID = '76561198106829949';
|
||||||
const GET_PLAYER_SUMMARY_ENDPOINT = `http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${env.STEAM_API_KEY}&steamids=${STEAM_ID}&format=json`
|
const GET_PLAYER_SUMMARY_ENDPOINT = `http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${env.STEAM_API_KEY}&steamids=${STEAM_ID}&format=json`;
|
||||||
|
|
||||||
type LastGame = {
|
type LastGame = {
|
||||||
name: string,
|
name: string;
|
||||||
link: string,
|
link: string;
|
||||||
icon: string,
|
icon: string;
|
||||||
pfp: string,
|
pfp: string;
|
||||||
when: number,
|
when: number;
|
||||||
playing: boolean,
|
playing: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
const steamgriddbClient = writable<SGDB | null>(null)
|
const steamgriddbClient = writable<SGDB | null>(null);
|
||||||
const lastGame = writable<LastGame | null>(null)
|
const lastGame = writable<LastGame | null>(null);
|
||||||
|
|
||||||
export const steamUpdateNowPlaying = async () => {
|
export const steamUpdateNowPlaying = async () => {
|
||||||
var griddbClient = get(steamgriddbClient)
|
let griddbClient = get(steamgriddbClient);
|
||||||
if (griddbClient === null) {
|
if (griddbClient === null) {
|
||||||
griddbClient = new SGDB(env.STEAMGRIDDB_API_KEY)
|
griddbClient = new SGDB(env.STEAMGRIDDB_API_KEY);
|
||||||
steamgriddbClient.set(griddbClient)
|
steamgriddbClient.set(griddbClient);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var profile = (await (await fetch(GET_PLAYER_SUMMARY_ENDPOINT)).json()).response.players[0]
|
const profile = (await (await fetch(GET_PLAYER_SUMMARY_ENDPOINT)).json()).response.players[0];
|
||||||
if (!profile.gameid) {
|
if (!profile.gameid) {
|
||||||
throw "no game is being played"
|
throw 'no game is being played';
|
||||||
}
|
}
|
||||||
var icons = await griddbClient.getIconsBySteamAppId(profile.gameid, ['official', 'custom'])
|
const icons = await griddbClient.getIconsBySteamAppId(profile.gameid, ['official', 'custom']);
|
||||||
//console.log(icons)
|
//console.log(icons)
|
||||||
var game: LastGame = {
|
const game: LastGame = {
|
||||||
name: profile.gameextrainfo,
|
name: profile.gameextrainfo,
|
||||||
link: `https://store.steampowered.com/app/${profile.gameid}`,
|
link: `https://store.steampowered.com/app/${profile.gameid}`,
|
||||||
icon: icons[0].thumb.toString(),
|
icon: icons[0].thumb.toString(),
|
||||||
pfp: profile.avatarmedium,
|
pfp: profile.avatarmedium,
|
||||||
when: Date.now(),
|
when: Date.now(),
|
||||||
playing: true,
|
playing: true
|
||||||
}
|
};
|
||||||
lastGame.set(game)
|
lastGame.set(game);
|
||||||
} catch(why) {
|
} catch (why) {
|
||||||
console.log("could not fetch steam: ", why)
|
console.log('could not fetch steam: ', why);
|
||||||
lastGame.update((t) => { if (t !== null) { t.playing = false; } return t })
|
lastGame.update((t) => {
|
||||||
}
|
if (t !== null) {
|
||||||
}
|
t.playing = false;
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const getLastGame = () => { return get(lastGame) }
|
export const getLastGame = () => {
|
||||||
|
return get(lastGame);
|
||||||
|
};
|
||||||
|
@ -1,110 +1,122 @@
|
|||||||
import { env } from "$env/dynamic/private";
|
import { env } from '$env/dynamic/private';
|
||||||
import { scopeCookies } from "$lib";
|
import { scopeCookies } from '$lib';
|
||||||
import type { Cookies } from "@sveltejs/kit";
|
import type { Cookies } from '@sveltejs/kit';
|
||||||
import { existsSync, readFileSync, writeFileSync } from "fs";
|
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from 'nanoid';
|
||||||
import { get, writable } from "svelte/store";
|
import { get, writable } from 'svelte/store';
|
||||||
|
|
||||||
const visitCountFile = `${env.WEBSITE_DATA_DIR}/visitcount`
|
const visitCountFile = `${env.WEBSITE_DATA_DIR}/visitcount`;
|
||||||
const visitCount = writable(parseInt(existsSync(visitCountFile) ? readFileSync(visitCountFile).toString() : '0'))
|
const visitCount = writable(
|
||||||
|
parseInt(existsSync(visitCountFile) ? readFileSync(visitCountFile).toString() : '0')
|
||||||
|
);
|
||||||
|
|
||||||
type Visitor = { visits: number[] }
|
type Visitor = { visits: number[] };
|
||||||
const lastVisitors = writable<Map<string, Visitor>>(new Map())
|
const lastVisitors = writable<Map<string, Visitor>>(new Map());
|
||||||
const VISITOR_EXPIRY_SECONDS = 60 * 60 // an hour seems reasonable
|
const VISITOR_EXPIRY_SECONDS = 60 * 60; // an hour seems reasonable
|
||||||
|
|
||||||
export const incrementVisitCount = (request: Request, cookies: Cookies) => {
|
export const incrementVisitCount = (request: Request, cookies: Cookies) => {
|
||||||
let currentVisitCount = get(visitCount)
|
let currentVisitCount = get(visitCount);
|
||||||
// check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
|
// check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
|
||||||
if (isBot(request)) { return currentVisitCount }
|
if (isBot(request)) {
|
||||||
const scopedCookies = scopeCookies(cookies, '/')
|
return currentVisitCount;
|
||||||
// parse the last visit timestamp from cookies if it exists
|
}
|
||||||
const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || "0")
|
const scopedCookies = scopeCookies(cookies, '/');
|
||||||
// get unix timestamp
|
// parse the last visit timestamp from cookies if it exists
|
||||||
const currentTime = Date.now()
|
const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || '0');
|
||||||
const timeSinceVisit = currentTime - visitedTimestamp
|
// get unix timestamp
|
||||||
// check if this is the first time a client is visiting or if an hour has passed since they last visited
|
const currentTime = Date.now();
|
||||||
if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) {
|
const timeSinceVisit = currentTime - visitedTimestamp;
|
||||||
// increment current and write to the store
|
// check if this is the first time a client is visiting or if an hour has passed since they last visited
|
||||||
currentVisitCount += 1; visitCount.set(currentVisitCount)
|
if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) {
|
||||||
// update the cookie with the current timestamp
|
// increment current and write to the store
|
||||||
scopedCookies.set('visitedTimestamp', currentTime.toString())
|
currentVisitCount += 1;
|
||||||
// write the visit count to a file so we can load it later again
|
visitCount.set(currentVisitCount);
|
||||||
writeFileSync(visitCountFile, currentVisitCount.toString())
|
// update the cookie with the current timestamp
|
||||||
}
|
scopedCookies.set('visitedTimestamp', currentTime.toString());
|
||||||
return currentVisitCount
|
// write the visit count to a file so we can load it later again
|
||||||
}
|
writeFileSync(visitCountFile, currentVisitCount.toString());
|
||||||
|
}
|
||||||
|
return currentVisitCount;
|
||||||
|
};
|
||||||
|
|
||||||
export const addLastVisitor = (request: Request, cookies: Cookies) => {
|
export const addLastVisitor = (request: Request, cookies: Cookies) => {
|
||||||
let visitors = get(lastVisitors)
|
let visitors = get(lastVisitors);
|
||||||
visitors = _addLastVisitor(visitors, request, cookies)
|
visitors = _addLastVisitor(visitors, request, cookies);
|
||||||
lastVisitors.set(visitors)
|
lastVisitors.set(visitors);
|
||||||
return visitors
|
return visitors;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getVisitorId = (cookies: Cookies) => {
|
export const getVisitorId = (cookies: Cookies) => {
|
||||||
const scopedCookies = scopeCookies(cookies, '/')
|
const scopedCookies = scopeCookies(cookies, '/');
|
||||||
// parse the last visit timestamp from cookies if it exists
|
// parse the last visit timestamp from cookies if it exists
|
||||||
return scopedCookies.get('visitorId')
|
return scopedCookies.get('visitorId');
|
||||||
}
|
};
|
||||||
|
|
||||||
// why not use this for incrementVisitCount? cuz i wanna have separate visit counts (one per hour and one per day, per hour being recent visitors)
|
// why not use this for incrementVisitCount? cuz i wanna have separate visit counts (one per hour and one per day, per hour being recent visitors)
|
||||||
const _addLastVisitor = (visitors: Map<string, Visitor>, request: Request, cookies: Cookies) => {
|
const _addLastVisitor = (visitors: Map<string, Visitor>, request: Request, cookies: Cookies) => {
|
||||||
const currentTime = Date.now()
|
const currentTime = Date.now();
|
||||||
// filter out old entries
|
// filter out old entries
|
||||||
visitors.forEach((visitor, id, map) => {
|
visitors.forEach((visitor, id, map) => {
|
||||||
if (currentTime - visitor.visits[0] > 1000 * VISITOR_EXPIRY_SECONDS)
|
if (currentTime - visitor.visits[0] > 1000 * VISITOR_EXPIRY_SECONDS) map.delete(id);
|
||||||
map.delete(id)
|
else {
|
||||||
else {
|
visitor.visits = visitor.visits.filter((since) => {
|
||||||
visitor.visits = visitor.visits.filter((since) => {
|
return currentTime - since < 1000 * VISITOR_EXPIRY_SECONDS;
|
||||||
return currentTime - since < 1000 * VISITOR_EXPIRY_SECONDS
|
});
|
||||||
})
|
map.set(id, visitor);
|
||||||
map.set(id, visitor)
|
}
|
||||||
}
|
});
|
||||||
})
|
// check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
|
||||||
// check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
|
if (isBot(request)) {
|
||||||
if (isBot(request)) { return visitors }
|
return visitors;
|
||||||
const scopedCookies = scopeCookies(cookies, '/')
|
}
|
||||||
// parse the last visit timestamp from cookies if it exists
|
const scopedCookies = scopeCookies(cookies, '/');
|
||||||
let visitorId = scopedCookies.get('visitorId') || ""
|
// parse the last visit timestamp from cookies if it exists
|
||||||
// if no such id exists, create one and assign it to the client
|
let visitorId = scopedCookies.get('visitorId') || '';
|
||||||
if (! visitors.has(visitorId)) {
|
// if no such id exists, create one and assign it to the client
|
||||||
visitorId = nanoid()
|
if (!visitors.has(visitorId)) {
|
||||||
scopedCookies.set('visitorId', visitorId)
|
visitorId = nanoid();
|
||||||
console.log(`new client visitor id ${visitorId}`)
|
scopedCookies.set('visitorId', visitorId);
|
||||||
}
|
console.log(`new client visitor id ${visitorId}`);
|
||||||
// update the entry
|
}
|
||||||
let visitorEntry = visitors.get(visitorId) || {visits: []}
|
// update the entry
|
||||||
// put new visit in the front
|
const visitorEntry = visitors.get(visitorId) || { visits: [] };
|
||||||
visitorEntry.visits = [currentTime].concat(visitorEntry.visits)
|
// put new visit in the front
|
||||||
visitors.set(visitorId, visitorEntry);
|
visitorEntry.visits = [currentTime].concat(visitorEntry.visits);
|
||||||
return visitors
|
visitors.set(visitorId, visitorEntry);
|
||||||
}
|
return visitors;
|
||||||
|
};
|
||||||
|
|
||||||
const isBot = (request: Request) => {
|
const isBot = (request: Request) => {
|
||||||
const ua = request.headers.get('user-agent')
|
const ua = request.headers.get('user-agent');
|
||||||
return ua ? ua.toLowerCase().match(/(bot|crawl|spider|walk|fetch|scrap|proxy|image)/) !== null : true
|
return ua
|
||||||
}
|
? ua.toLowerCase().match(/(bot|crawl|spider|walk|fetch|scrap|proxy|image)/) !== null
|
||||||
|
: true;
|
||||||
|
};
|
||||||
|
|
||||||
export const notifyDarkVisitors = (url: URL, request: Request) => {
|
export const notifyDarkVisitors = (url: URL, request: Request) => {
|
||||||
fetch('https://api.darkvisitors.com/visits', {
|
fetch('https://api.darkvisitors.com/visits', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Bearer ${env.DARK_VISITORS_TOKEN}`,
|
authorization: `Bearer ${env.DARK_VISITORS_TOKEN}`,
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
request_path: url.pathname,
|
request_path: url.pathname,
|
||||||
request_method: request.method,
|
request_method: request.method,
|
||||||
request_headers: request.headers,
|
request_headers: request.headers
|
||||||
})
|
})
|
||||||
}).catch((why) => {
|
})
|
||||||
console.log("failed sending dark visitors analytics:", why)
|
.catch((why) => {
|
||||||
return null
|
console.log('failed sending dark visitors analytics:', why);
|
||||||
}).then(async (resp) => {
|
return null;
|
||||||
if (resp !== null) {
|
})
|
||||||
const msg = await resp.json()
|
.then(async (resp) => {
|
||||||
const host = `(${request.headers.get('host')} ${request.headers.get('x-real-ip')})`
|
if (resp !== null) {
|
||||||
console.log(`sent visitor analytic to dark visitors: ${resp.statusText}; ${msg.message ?? ''}${host}`)
|
const msg = await resp.json();
|
||||||
}
|
const host = `(${request.headers.get('host')} ${request.headers.get('x-real-ip')})`;
|
||||||
})
|
console.log(
|
||||||
}
|
`sent visitor analytic to dark visitors: ${resp.statusText}; ${msg.message ?? ''}${host}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
@ -1,9 +1,20 @@
|
|||||||
import { writable } from "svelte/store";
|
/* eslint-disable no-useless-escape */
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
export const highestZIndex = writable(0)
|
export const highestZIndex = writable(0);
|
||||||
|
|
||||||
export const isMobile = () => {
|
export const isMobile = () => {
|
||||||
let check = false;
|
let check = false;
|
||||||
(function (a) { if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; })(navigator.userAgent || navigator.vendor);
|
(function (a) {
|
||||||
return check;
|
if (
|
||||||
}
|
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(
|
||||||
|
a
|
||||||
|
) ||
|
||||||
|
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
|
||||||
|
a.slice(0, 4)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
check = true;
|
||||||
|
})(navigator.userAgent || navigator.vendor);
|
||||||
|
return check;
|
||||||
|
};
|
||||||
|
@ -5,26 +5,26 @@ import { error } from '@sveltejs/kit';
|
|||||||
export const csr = true;
|
export const csr = true;
|
||||||
export const ssr = true;
|
export const ssr = true;
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
export const trailingSlash = 'always';
|
export const trailingSlash = 'always';
|
||||||
|
|
||||||
export async function load({ request, cookies, url }) {
|
export async function load({ request, cookies, url }) {
|
||||||
notifyDarkVisitors(url, request) // no await so it doesnt block load
|
notifyDarkVisitors(url, request); // no await so it doesnt block load
|
||||||
|
|
||||||
// block any requests if the user agent is disallowed by our robots txt
|
// block any requests if the user agent is disallowed by our robots txt
|
||||||
if (await testUa(url.toString(), request.headers.get('user-agent') ?? "") === false) {
|
if ((await testUa(url.toString(), request.headers.get('user-agent') ?? '')) === false) {
|
||||||
throw error(403, "get a better user agent silly")
|
throw error(403, 'get a better user agent silly');
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastVisitors = addLastVisitor(request, cookies)
|
const lastVisitors = addLastVisitor(request, cookies);
|
||||||
let recentVisitCount = 0
|
let recentVisitCount = 0;
|
||||||
for (const [_, visitor] of lastVisitors) {
|
for (const [, visitor] of lastVisitors) {
|
||||||
recentVisitCount += visitor.visits.length
|
recentVisitCount += visitor.visits.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
route: url.pathname,
|
route: url.pathname,
|
||||||
visitCount: incrementVisitCount(request, cookies),
|
visitCount: incrementVisitCount(request, cookies),
|
||||||
lastVisitors,
|
lastVisitors,
|
||||||
recentVisitCount,
|
recentVisitCount
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import '../styles/app.css';
|
import '../styles/app.css';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
data: any;
|
data: any;
|
||||||
children?: import('svelte').Snippet;
|
children?: import('svelte').Snippet;
|
||||||
}
|
}
|
||||||
@ -73,7 +74,7 @@
|
|||||||
<filter id="squiggly-{index}">
|
<filter id="squiggly-{index}">
|
||||||
<feTurbulence
|
<feTurbulence
|
||||||
id="turbulence"
|
id="turbulence"
|
||||||
baseFrequency=0.03
|
baseFrequency="0.03"
|
||||||
numOctaves="3"
|
numOctaves="3"
|
||||||
result="noise"
|
result="noise"
|
||||||
seed={index}
|
seed={index}
|
||||||
@ -134,7 +135,9 @@
|
|||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<div class="md:h-[96vh] pb-[8vh] lg:px-[1vw] 2xl:px-[2vw] lg:pb-[3vh] lg:pt-[1vh] overflow-x-hidden [scrollbar-gutter:stable]">
|
<div
|
||||||
|
class="md:h-[96vh] pb-[8vh] lg:px-[1vw] 2xl:px-[2vw] lg:pb-[3vh] lg:pt-[1vh] overflow-x-hidden [scrollbar-gutter:stable]"
|
||||||
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -152,7 +155,12 @@
|
|||||||
{@const highlight = isRoute(item.href)}
|
{@const highlight = isRoute(item.href)}
|
||||||
<NavButton {highlight} {...item} />
|
<NavButton {highlight} {...item} />
|
||||||
{#if doAddPostItem && menuIdx == 1}
|
{#if doAddPostItem && menuIdx == 1}
|
||||||
<NavButton highlight name={routeComponents[2]} href={data.route.slice(1)} iconUri='/icons/entry.webp'/>
|
<NavButton
|
||||||
|
highlight
|
||||||
|
name={routeComponents[2]}
|
||||||
|
href={data.route.slice(1)}
|
||||||
|
iconUri="/icons/entry.webp"
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
<div class="hidden md:block grow"></div>
|
<div class="hidden md:block grow"></div>
|
||||||
@ -163,22 +171,33 @@
|
|||||||
</div>
|
</div>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
{#snippet tooltipContent()}
|
{#snippet tooltipContent()}
|
||||||
|
<p class="font-monospace">
|
||||||
<p class="font-monospace">
|
<nobr
|
||||||
<nobr>total visits = <span class="text-ralsei-green-light text-shadow-green">{data.visitCount.toString().padStart(9, ".")}</span></nobr>
|
>total visits = <span class="text-ralsei-green-light text-shadow-green"
|
||||||
<nobr>uniq recent visits = <span class="text-ralsei-green-light text-shadow-green">{data.lastVisitors.size.toString().padStart(3, ".")}</span></nobr>
|
>{data.visitCount.toString().padStart(9, '.')}</span
|
||||||
</p>
|
></nobr
|
||||||
|
>
|
||||||
{/snippet}
|
<nobr
|
||||||
<div class="navbox"><p><span class="text-ralsei-green-light text-shadow-green">{data.recentVisitCount}</span> recent clicks</p></div>
|
>uniq recent visits = <span class="text-ralsei-green-light text-shadow-green"
|
||||||
|
>{data.lastVisitors.size.toString().padStart(3, '.')}</span
|
||||||
|
></nobr
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
{/snippet}
|
||||||
|
<div class="navbox">
|
||||||
|
<p>
|
||||||
|
<span class="text-ralsei-green-light text-shadow-green">{data.recentVisitCount}</span> recent
|
||||||
|
clicks
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{#if isRoute("entries") || isRoute("log")}
|
{#if isRoute('entries') || isRoute('log')}
|
||||||
<div class="navbox !gap-1">
|
<div class="navbox !gap-1">
|
||||||
rss:
|
rss:
|
||||||
<a class="align-middle hover:underline" href="/entries/_rss">posts</a>
|
<a class="align-middle hover:underline" href="/entries/_rss">posts</a>
|
||||||
/
|
/
|
||||||
<a class="align-middle hover:underline" href="/log/_rss">log</a>
|
<a class="align-middle hover:underline" href="/log/_rss">log</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -189,4 +208,4 @@
|
|||||||
@apply flex gap-3 px-1.5 text-nowrap align-middle items-center text-center place-content-center border-ralsei-white border-4;
|
@apply flex gap-3 px-1.5 text-nowrap align-middle items-center text-center place-content-center border-ralsei-white border-4;
|
||||||
border-style: groove;
|
border-style: groove;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
import Note from '../components/note.svelte';
|
import Note from '../components/note.svelte';
|
||||||
import Window from '../components/window.svelte';
|
import Window from '../components/window.svelte';
|
||||||
import LatestStuff from './lateststuff.md';
|
import LatestStuff from './lateststuff.md';
|
||||||
import {renderDate, renderRelativeDate} from '$lib/dateFmt';
|
import { renderDate, renderRelativeDate } from '$lib/dateFmt';
|
||||||
|
import Tooltip from '../components/tooltip.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
data: any;
|
data: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,9 +18,7 @@
|
|||||||
<div class="flex flex-col gap-y-2 lg:gap-y-0 mx-auto">
|
<div class="flex flex-col gap-y-2 lg:gap-y-0 mx-auto">
|
||||||
<Window title="readme?" iconUri="/icons/question.webp" removePadding>
|
<Window title="readme?" iconUri="/icons/question.webp" removePadding>
|
||||||
<div class="flex flex-col p-1.5 gap-1.5 prose prose-ralsei prose-img:m-0 leading-none">
|
<div class="flex flex-col p-1.5 gap-1.5 prose prose-ralsei prose-img:m-0 leading-none">
|
||||||
<div
|
<div class="flex flex-row gap-3 mx-auto bg-ralsei-black/20 overflow-hidden">
|
||||||
class="flex flex-row gap-3 mx-auto bg-ralsei-black/20 overflow-hidden"
|
|
||||||
>
|
|
||||||
{#each data.banners as bannerNo, index}
|
{#each data.banners as bannerNo, index}
|
||||||
{@const hideIfMobile = index === data.banners.length - 1}
|
{@const hideIfMobile = index === data.banners.length - 1}
|
||||||
<img
|
<img
|
||||||
@ -35,11 +35,20 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-grow">
|
<div class="flex flex-grow">
|
||||||
<div
|
<Tooltip>
|
||||||
class="w-36 [padding:8px] place-content-center place-self-center bg-ralsei-black/20"
|
{#snippet tooltipContent()}
|
||||||
>
|
that's me! my angelsona :3c
|
||||||
<img class="w-36 u-photo hover:invert transition-all [transition-duration:300ms]" src="/pfp-iojkqpwerojnasduijf.webp" alt="my character" title="hi ;)"/>
|
{/snippet}
|
||||||
</div>
|
<div
|
||||||
|
class="w-36 [padding:8px] place-content-center place-self-center bg-ralsei-black/20"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="w-36 u-photo hover:invert transition-all [transition-duration:300ms]"
|
||||||
|
src="/pfp-iojkqpwerojnasduijf.webp"
|
||||||
|
alt="my angelsona"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
<div
|
<div
|
||||||
class="flex flex-row flex-grow place-content-center ml-1.5 [padding:8px] bg-ralsei-black/20"
|
class="flex flex-row flex-grow place-content-center ml-1.5 [padding:8px] bg-ralsei-black/20"
|
||||||
>
|
>
|
||||||
@ -47,7 +56,19 @@
|
|||||||
class="place-self-center m-0 mr-4 [padding-left:1em] sm:[padding-left:0.5em] leading-none marker:[content:'->'] [list-style-type:'->']"
|
class="place-self-center m-0 mr-4 [padding-left:1em] sm:[padding-left:0.5em] leading-none marker:[content:'->'] [list-style-type:'->']"
|
||||||
>
|
>
|
||||||
<li class="[list-style-type:'->'] p-note">trying to do stuff</li>
|
<li class="[list-style-type:'->'] p-note">trying to do stuff</li>
|
||||||
<li class="[list-style-type:'->'] p-note">is a thing that exists</li>
|
<li class="[list-style-type:'->'] p-note">
|
||||||
|
<Tooltip
|
||||||
|
x="translate-x-none"
|
||||||
|
y="translate-y-none"
|
||||||
|
targetX="group-hover:translate-x-[80%]"
|
||||||
|
targetY="group-hover:-translate-y-[70%]"
|
||||||
|
>
|
||||||
|
{#snippet tooltipContent()}
|
||||||
|
angelrobotdollpuppything
|
||||||
|
{/snippet}
|
||||||
|
is a thing (it/they)
|
||||||
|
</Tooltip>
|
||||||
|
</li>
|
||||||
<li class="[list-style-type:'->']">
|
<li class="[list-style-type:'->']">
|
||||||
<span class="p-category">software engineer</span>,
|
<span class="p-category">software engineer</span>,
|
||||||
<span class="p-category">indie game dev</span>
|
<span class="p-category">indie game dev</span>
|
||||||
@ -95,7 +116,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-y-2 lg:gap-y-0 mx-auto w-full md:w-fit place-items-end">
|
<div class="flex flex-col gap-y-2 lg:gap-y-0 mx-auto w-full md:w-fit place-items-end">
|
||||||
<Window title="links!" iconUri="/icons/contact.webp">
|
<Window title="links!" iconUri="/icons/contact.webp">
|
||||||
<div class="prose prose-ralsei prose-ul:px-[0.9rem] prose-ul:leading-[1.1rem] prose-headings:leading-none">
|
<div
|
||||||
|
class="prose prose-ralsei prose-ul:px-[0.9rem] prose-ul:leading-[1.1rem] prose-headings:leading-none"
|
||||||
|
>
|
||||||
<ul>
|
<ul>
|
||||||
<li>discord: yusdacra</li>
|
<li>discord: yusdacra</li>
|
||||||
<li>
|
<li>
|
||||||
@ -135,102 +158,110 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<h4>88x31</h4>
|
<h4>88x31</h4>
|
||||||
<div class="flex flex-row flex-wrap gap-1 prose-img:m-0">
|
<div class="flex flex-row flex-wrap gap-1 prose-img:m-0">
|
||||||
<img src="/88x31.gif" alt="88x31 banner" title="midnight AND sunrise! woaw"/>
|
<img src="/88x31.gif" alt="88x31 banner" title="midnight AND sunrise! woaw" />
|
||||||
<img src="/88x31_midnight.gif" alt="88x31 banner (midnight only)" title="it's midnight!"/>
|
<img
|
||||||
<img src="/88x31_sunrise.gif" alt="88x31 banner (sunrise only)" title="it's sunrise!"/>
|
src="/88x31_midnight.gif"
|
||||||
|
alt="88x31 banner (midnight only)"
|
||||||
|
title="it's midnight!"
|
||||||
|
/>
|
||||||
|
<img src="/88x31_sunrise.gif" alt="88x31 banner (sunrise only)" title="it's sunrise!" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Window>
|
</Window>
|
||||||
<Window title="status" style="mt-auto" removePadding>
|
<Window title="status" style="mt-auto" removePadding>
|
||||||
{#if data.lastNote}
|
{#if data.lastNote}
|
||||||
<div class="m-1.5 flex flex-col font-monospace text-sm">
|
<div class="m-1.5 flex flex-col font-monospace text-sm">
|
||||||
<p
|
<p
|
||||||
class="prose prose-ralsei p-1 border-4 text-sm bg-ralsei-black"
|
class="prose prose-ralsei p-1 border-4 text-sm bg-ralsei-black"
|
||||||
style="border-style: double double none double;"
|
style="border-style: double double none double;"
|
||||||
title={renderDate(data.lastNote.published)}
|
title={renderDate(data.lastNote.published)}
|
||||||
>
|
>
|
||||||
<a href="/entries">last log was…</a>
|
<a href="/entries">last log was…</a>
|
||||||
published {renderRelativeDate(data.lastNote.published)}!
|
published {renderRelativeDate(data.lastNote.published)}!
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-0 p-1.5 border-4 border-double bg-ralsei-black min-w-full max-w-[40ch]">
|
<div class="mt-0 p-1.5 border-4 border-double bg-ralsei-black min-w-full max-w-[40ch]">
|
||||||
<Note note={data.lastNote} onlyContent/>
|
<Note note={data.lastNote} onlyContent />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
{#if data.lastTrack}
|
{#if data.lastTrack}
|
||||||
<div class="flex flex-row gap-0.5 m-1.5 border-4 border-double bg-ralsei-black">
|
<div class="flex flex-row gap-0.5 m-1.5 border-4 border-double bg-ralsei-black">
|
||||||
<!-- svelte-ignore a11y_missing_attribute -->
|
<!-- svelte-ignore a11y_missing_attribute -->
|
||||||
{#if data.lastTrack.image}
|
{#if data.lastTrack.image}
|
||||||
|
<img
|
||||||
|
class="border-4 w-[4.5rem] h-[4.5rem]"
|
||||||
|
style="border-style: none double none none;"
|
||||||
|
src={data.lastTrack.image}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<img
|
||||||
|
class="border-4 w-[4.5rem] h-[4.5rem] p-2"
|
||||||
|
style="border-style: none double none none; image-rendering: pixelated;"
|
||||||
|
src="/icons/cd_audio.webp"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<div class="flex flex-col max-w-[40ch] p-2">
|
||||||
|
<p
|
||||||
|
class="text-shadow-green text-ralsei-green-light text-sm text-ellipsis text-nowrap overflow-hidden max-w-[30ch]"
|
||||||
|
>
|
||||||
|
<span class="text-sm text-shadow-white text-ralsei-white"
|
||||||
|
>{data.lastTrack.playing ? 'listening to' : 'listened to'}</span
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
title={data.lastTrack.name}
|
||||||
|
href="https://www.last.fm/user/yusdacra"
|
||||||
|
class="hover:underline motion-safe:hover:animate-squiggle">{data.lastTrack.name}</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="text-shadow-pink text-ralsei-pink-regular text-sm text-ellipsis text-nowrap overflow-hidden max-w-[30ch]"
|
||||||
|
>
|
||||||
|
<span class="text-shadow-white text-ralsei-white">by</span>
|
||||||
|
<span title={data.lastTrack.artist}>{data.lastTrack.artist}</span>
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="text-shadow-white text-ralsei-white text-xs text-ellipsis text-nowrap overflow-hidden max-w-[30ch]"
|
||||||
|
>
|
||||||
|
…{renderRelativeDate(data.lastTrack.when)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if data.lastGame}
|
||||||
|
<div class="flex flex-row m-1.5 border-4 border-double bg-ralsei-black">
|
||||||
|
<!-- svelte-ignore a11y_missing_attribute -->
|
||||||
<img
|
<img
|
||||||
class="border-4 w-[4.5rem] h-[4.5rem]"
|
class="border-4 w-[4.5rem] h-[4.5rem]"
|
||||||
style="border-style: none double none none;"
|
style="border-style: none double none none;"
|
||||||
src={data.lastTrack.image}
|
width="64"
|
||||||
|
height="64"
|
||||||
|
src={data.lastGame.icon}
|
||||||
/>
|
/>
|
||||||
{:else}
|
<div class="flex flex-col max-w-[40ch] p-2 gap-0.5 overflow-hidden">
|
||||||
<img
|
<p
|
||||||
class="border-4 w-[4.5rem] h-[4.5rem] p-2"
|
class="text-shadow-green text-ralsei-green-light text-sm text-ellipsis text-nowrap overflow-hidden max-w-[30ch]"
|
||||||
style="border-style: none double none none; image-rendering: pixelated;"
|
>
|
||||||
src="/icons/cd_audio.webp"
|
<span class="text-sm text-shadow-white text-ralsei-white"
|
||||||
/>
|
>{data.lastGame.playing ? 'playing' : 'played'}</span
|
||||||
{/if}
|
>
|
||||||
<div class="flex flex-col max-w-[40ch] p-2">
|
<a title={data.lastGame.name} class="hover:underline" href={data.lastGame.link}
|
||||||
<p
|
>{data.lastGame.name}</a
|
||||||
class="text-shadow-green text-ralsei-green-light text-sm text-ellipsis text-nowrap overflow-hidden max-w-[30ch]"
|
>
|
||||||
>
|
</p>
|
||||||
<span class="text-sm text-shadow-white text-ralsei-white">{data.lastTrack.playing ? "listening to" : "listened to"}</span>
|
<p
|
||||||
|
class="text-shadow-white text-ralsei-white text-xs text-ellipsis text-nowrap overflow-hidden max-w-[30ch]"
|
||||||
|
>
|
||||||
|
…{renderRelativeDate(data.lastGame.when)}
|
||||||
|
</p>
|
||||||
|
<!-- svelte-ignore a11y_missing_attribute -->
|
||||||
<a
|
<a
|
||||||
title={data.lastTrack.name}
|
href="https://steamcommunity.com/id/yusdacra"
|
||||||
href="https://www.last.fm/user/yusdacra"
|
class="text-xs hover:underline text-shadow-green text-ralsei-green-light"
|
||||||
class="hover:underline motion-safe:hover:animate-squiggle">{data.lastTrack.name}</a
|
><img class="inline w-4" src={data.lastGame.pfp} />
|
||||||
|
<span class="align-middle">steam profile</span></a
|
||||||
>
|
>
|
||||||
</p>
|
</div>
|
||||||
<p
|
|
||||||
class="text-shadow-pink text-ralsei-pink-regular text-sm text-ellipsis text-nowrap overflow-hidden max-w-[30ch]"
|
|
||||||
>
|
|
||||||
<span class="text-shadow-white text-ralsei-white">by</span>
|
|
||||||
<span title={data.lastTrack.artist}>{data.lastTrack.artist}</span>
|
|
||||||
</p>
|
|
||||||
<p
|
|
||||||
class="text-shadow-white text-ralsei-white text-xs text-ellipsis text-nowrap overflow-hidden max-w-[30ch]"
|
|
||||||
>
|
|
||||||
…{renderRelativeDate(data.lastTrack.when)}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if data.lastGame}
|
|
||||||
<div class="flex flex-row m-1.5 border-4 border-double bg-ralsei-black">
|
|
||||||
<!-- svelte-ignore a11y_missing_attribute -->
|
|
||||||
<img
|
|
||||||
class="border-4 w-[4.5rem] h-[4.5rem]"
|
|
||||||
style="border-style: none double none none;"
|
|
||||||
width="64"
|
|
||||||
height="64"
|
|
||||||
src={data.lastGame.icon}
|
|
||||||
/>
|
|
||||||
<div class="flex flex-col max-w-[40ch] p-2 gap-0.5 overflow-hidden">
|
|
||||||
<p
|
|
||||||
class="text-shadow-green text-ralsei-green-light text-sm text-ellipsis text-nowrap overflow-hidden max-w-[30ch]"
|
|
||||||
>
|
|
||||||
<span class="text-sm text-shadow-white text-ralsei-white">{data.lastGame.playing ? "playing" : "played"}</span>
|
|
||||||
<a title={data.lastGame.name} class="hover:underline" href={data.lastGame.link}
|
|
||||||
>{data.lastGame.name}</a
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
<p
|
|
||||||
class="text-shadow-white text-ralsei-white text-xs text-ellipsis text-nowrap overflow-hidden max-w-[30ch]"
|
|
||||||
>
|
|
||||||
…{renderRelativeDate(data.lastGame.when)}
|
|
||||||
</p>
|
|
||||||
<!-- svelte-ignore a11y_missing_attribute -->
|
|
||||||
<a
|
|
||||||
href="https://steamcommunity.com/id/yusdacra"
|
|
||||||
class="text-xs hover:underline text-shadow-green text-ralsei-green-light"
|
|
||||||
><img class="inline w-4" src={data.lastGame.pfp} />
|
|
||||||
<span class="align-middle">steam profile</span></a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</Window>
|
</Window>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,34 +1,37 @@
|
|||||||
import convertDate from "$lib/convertDate";
|
import convertDate from '$lib/convertDate';
|
||||||
|
|
||||||
export interface PostData {
|
export interface PostData {
|
||||||
path: string,
|
path: string;
|
||||||
published: string,
|
published: string;
|
||||||
metadata: Record<string, string>,
|
metadata: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const allPostFiles: Record<string, any> = import.meta.glob('./*/+page.md', { eager: true });
|
const allPostFiles: Record<string, any> = import.meta.glob('./*/+page.md', { eager: true });
|
||||||
const allPosts: PostData[] = Object.entries(allPostFiles).map(([path, post]) => {
|
const allPosts: PostData[] = Object.entries(allPostFiles)
|
||||||
const postPath = path.slice(2, -8);
|
.map(([path, post]) => {
|
||||||
return {
|
const postPath = path.slice(2, -8);
|
||||||
metadata: post.metadata,
|
return {
|
||||||
path: postPath,
|
metadata: post.metadata,
|
||||||
published: convertDate(post.metadata.date)
|
path: postPath,
|
||||||
};
|
published: convertDate(post.metadata.date)
|
||||||
}).map((post) => {
|
};
|
||||||
if (!("excerpt" in post.metadata)) {
|
})
|
||||||
post.metadata.excerpt = ""
|
.map((post) => {
|
||||||
}
|
if (!('excerpt' in post.metadata)) {
|
||||||
return post;
|
post.metadata.excerpt = '';
|
||||||
}).toSorted((post, opost) => {
|
}
|
||||||
const date = new Date(post.metadata.date);
|
return post;
|
||||||
const odate = new Date(opost.metadata.date);
|
})
|
||||||
return odate.getTime() - date.getTime()
|
.toSorted((post, opost) => {
|
||||||
});
|
const date = new Date(post.metadata.date);
|
||||||
|
const odate = new Date(opost.metadata.date);
|
||||||
|
return odate.getTime() - date.getTime();
|
||||||
|
});
|
||||||
export const _allPosts = allPosts;
|
export const _allPosts = allPosts;
|
||||||
|
|
||||||
export async function load({}) {
|
export async function load() {
|
||||||
if (!allPosts.length) {
|
if (!allPosts.length) {
|
||||||
return { status: 404 };
|
return { status: 404 };
|
||||||
}
|
}
|
||||||
return { posts: allPosts };
|
return { posts: allPosts };
|
||||||
}
|
}
|
||||||
|
@ -15,22 +15,24 @@
|
|||||||
<div class="mx-auto md:max-w-fit flex flex-col-reverse md:flex-row gap-y-4 gap-x-16">
|
<div class="mx-auto md:max-w-fit flex flex-col-reverse md:flex-row gap-y-4 gap-x-16">
|
||||||
<div class="flex flex-col gap-y-4">
|
<div class="flex flex-col gap-y-4">
|
||||||
{#each posts as post}
|
{#each posts as post}
|
||||||
<Window title={post.metadata.title} iconUri='/icons/entry.webp'>
|
<Window title={post.metadata.title} iconUri="/icons/entry.webp">
|
||||||
<a
|
<a
|
||||||
href="/entries/{post.path}"
|
href="/entries/{post.path}"
|
||||||
title="cd /entries/{post.path}"
|
title="cd /entries/{post.path}"
|
||||||
data-sveltekit-preload-data="off"
|
data-sveltekit-preload-data="off"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col prose prose-ralsei leading-5">
|
<div class="flex flex-col prose prose-ralsei leading-5">
|
||||||
<ul>
|
<ul>
|
||||||
<li>published on: <time datetime="{post.metadata.date} 00:00:00">{post.published}</time></li>
|
<li>
|
||||||
<li class="max-w-[34ch] text-wrap">excerpt: {post.metadata.excerpt}</li>
|
published on: <time datetime="{post.metadata.date} 00:00:00">{post.published}</time>
|
||||||
</ul>
|
</li>
|
||||||
<strong class="place-self-end text-ralsei-green-light"> read more... </strong>
|
<li class="max-w-[34ch] text-wrap">excerpt: {post.metadata.excerpt}</li>
|
||||||
</div>
|
</ul>
|
||||||
</a>
|
<strong class="place-self-end text-ralsei-green-light"> read more... </strong>
|
||||||
</Window>
|
</div>
|
||||||
|
</a>
|
||||||
|
</Window>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<LogPage {data}/>
|
<LogPage {data} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,15 +3,13 @@ import { _allPosts, type PostData } from '../+layout.server.ts';
|
|||||||
|
|
||||||
const entriesUrl = `${PUBLIC_BASE_URL}/entries`;
|
const entriesUrl = `${PUBLIC_BASE_URL}/entries`;
|
||||||
|
|
||||||
export const GET = async ({ }) => {
|
export const GET = async () => {
|
||||||
return new Response(
|
return new Response(render(_allPosts), {
|
||||||
render(_allPosts),
|
headers: {
|
||||||
{
|
'content-type': 'application/xml',
|
||||||
headers: {
|
'cache-control': 'no-store'
|
||||||
'content-type': 'application/xml',
|
}
|
||||||
'cache-control': 'no-store',
|
});
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const render = (posts: PostData[]) => `<?xml version="1.0" encoding="UTF-8" ?>
|
const render = (posts: PostData[]) => `<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
@ -21,13 +19,17 @@ const render = (posts: PostData[]) => `<?xml version="1.0" encoding="UTF-8" ?>
|
|||||||
<title>dusk's posts (@gaze.systems)</title>
|
<title>dusk's posts (@gaze.systems)</title>
|
||||||
<link>${entriesUrl}</link>
|
<link>${entriesUrl}</link>
|
||||||
<description>posts from my website</description>
|
<description>posts from my website</description>
|
||||||
${posts.map((post) => `<item>
|
${posts
|
||||||
|
.map(
|
||||||
|
(post) => `<item>
|
||||||
<guid>${entriesUrl}/${post.path}</guid>
|
<guid>${entriesUrl}/${post.path}</guid>
|
||||||
<title>${post.metadata.title}</title>
|
<title>${post.metadata.title}</title>
|
||||||
<link>${entriesUrl}/${post.path}</link>
|
<link>${entriesUrl}/${post.path}</link>
|
||||||
<description>${post.metadata.excerpt}</description>
|
<description>${post.metadata.excerpt}</description>
|
||||||
<pubDate>${new Date(post.metadata.date).toUTCString()}</pubDate>
|
<pubDate>${new Date(post.metadata.date).toUTCString()}</pubDate>
|
||||||
</item>`).join('')}
|
</item>`
|
||||||
|
)
|
||||||
|
.join('')}
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
`;
|
`;
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import images from './images.json'
|
import images from './images.json';
|
||||||
|
|
||||||
export async function load({}) {
|
export async function load() {
|
||||||
return {
|
return {
|
||||||
images: images.map((id) => {
|
images: images.map((id) => {
|
||||||
return {
|
return {
|
||||||
og: `https://res.cloudinary.com/dgtwf7mar/image/upload/v1/${id}`,
|
og: `https://res.cloudinary.com/dgtwf7mar/image/upload/v1/${id}`,
|
||||||
thumb: `https://res.cloudinary.com/dgtwf7mar/image/upload/c_fill,w_480,h_480,g_center/c_limit,w_480/f_auto/q_auto/v1/${id}`,
|
thumb: `https://res.cloudinary.com/dgtwf7mar/image/upload/c_fill,w_480,h_480,g_center/c_limit,w_480/f_auto/q_auto/v1/${id}`
|
||||||
}
|
};
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { getLastPosts } from '$lib/bluesky.js';
|
import { getLastPosts } from '$lib/bluesky.js';
|
||||||
import { noteFromBskyPost } from '../../components/note.svelte';
|
import { noteFromBskyPost } from '../../components/note.svelte';
|
||||||
|
|
||||||
export const load = async ({ }) => {
|
export const load = async () => {
|
||||||
return _load()
|
return _load();
|
||||||
}
|
};
|
||||||
|
|
||||||
export const _load = async () => {
|
export const _load = async () => {
|
||||||
return {
|
return {
|
||||||
feedPosts: getLastPosts().map(noteFromBskyPost),
|
feedPosts: getLastPosts().map(noteFromBskyPost)
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
@ -1,37 +1,64 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Window from '../../components/window.svelte';
|
import Window from '../../components/window.svelte';
|
||||||
import Token from '../../components/token.svelte';
|
import Token from '../../components/token.svelte';
|
||||||
import Note from '../../components/note.svelte';
|
import Note, { type NoteData } from '../../components/note.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: any;
|
data: {
|
||||||
}
|
feedPosts: NoteData[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let { data }: Props = $props();
|
let { data }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Window title="terminal" removePadding>
|
<Window title="terminal" removePadding>
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
prose prose-ralsei
|
prose prose-ralsei
|
||||||
prose-pre:rounded-none prose-pre:!m-0 prose-pre:!p-2
|
prose-pre:rounded-none prose-pre:!m-0 prose-pre:!p-2
|
||||||
prose-pre:!bg-ralsei-black prose-code:!bg-ralsei-black
|
prose-pre:!bg-ralsei-black prose-code:!bg-ralsei-black
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<pre class="language-bash"><code class="language-bash"><nobr>
|
<pre class="language-bash"><code class="language-bash"
|
||||||
<Token v="[" punct/>gazesystems <Token v="/" keywd/><Token v="]$" punct/> <Token v="source" funct/> <Token v="scripts/log.nu" />
|
><nobr>
|
||||||
<br>
|
<Token v="[" punct />gazesystems <Token v="/" keywd /><Token v="]$" punct /> <Token
|
||||||
<Token v="[" punct/>gazesystems <Token v="/" keywd/><Token v="]$" punct/> <Token v="let" funct/> <Token v="entries"/> <Token v="=" punct/> <Token v="(" punct/><Token v="ls" funct/> <Token v="logs" /> <Token v="|" punct/> <Token v="reverse" funct/> <Token v="|" punct/> <Token v="take" funct/> <Token v="13"/><Token v=")" punct/>
|
v="source"
|
||||||
<br>
|
funct
|
||||||
<Token v="[" punct/>gazesystems <Token v="/" keywd/><Token v="]$" punct/> <Token v="$entries" /> <Token v="|" punct/> <Token v="each" funct/> <Token v="{" punct/><Token v="|" punct/><Token v="file"/><Token v="|" punct/> <Token v="render" funct/> <Token v="(" punct/><Token v="open" funct/> <Token v="$file.name" /><Token v=")" punct/><Token v="}" punct/>
|
/> <Token v="scripts/log.nu" />
|
||||||
<br>
|
<br />
|
||||||
<br>
|
<Token v="[" punct />gazesystems <Token v="/" keywd /><Token v="]$" punct /> <Token
|
||||||
|
v="let"
|
||||||
|
funct
|
||||||
|
/> <Token v="entries" /> <Token v="=" punct /> <Token v="(" punct /><Token
|
||||||
|
v="ls"
|
||||||
|
funct
|
||||||
|
/> <Token v="logs" /> <Token v="|" punct /> <Token v="reverse" funct /> <Token
|
||||||
|
v="|"
|
||||||
|
punct
|
||||||
|
/> <Token v="take" funct /> <Token v="13" /><Token v=")" punct />
|
||||||
|
<br />
|
||||||
|
<Token v="[" punct />gazesystems <Token v="/" keywd /><Token v="]$" punct /> <Token
|
||||||
|
v="$entries"
|
||||||
|
/> <Token v="|" punct /> <Token v="each" funct /> <Token v="{" punct /><Token
|
||||||
|
v="|"
|
||||||
|
punct
|
||||||
|
/><Token v="file" /><Token v="|" punct /> <Token v="render" funct /> <Token
|
||||||
|
v="("
|
||||||
|
punct
|
||||||
|
/><Token v="open" funct /> <Token v="$file.name" /><Token v=")" punct /><Token
|
||||||
|
v="}"
|
||||||
|
punct
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
{#each data.feedPosts as note, index}
|
{#each data.feedPosts as note, index}
|
||||||
<Note {note}/>
|
<Note {note} />
|
||||||
{#if index < data.feedPosts.length - 1}
|
{#if index < data.feedPosts.length - 1}
|
||||||
<div class="mt-3"></div>
|
<div class="mt-3"></div>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</nobr></code></pre>
|
</nobr></code
|
||||||
</div>
|
></pre>
|
||||||
|
</div>
|
||||||
</Window>
|
</Window>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { redirect } from '@sveltejs/kit';
|
import { redirect } from '@sveltejs/kit';
|
||||||
|
|
||||||
export const GET = async ({ }) => {
|
export const GET = async () => {
|
||||||
redirect(301, "https://bsky.app/profile/did:plc:dfl62fgb7wtjj3fcbb72naae/rss")
|
redirect(301, 'https://bsky.app/profile/did:plc:dfl62fgb7wtjj3fcbb72naae/rss');
|
||||||
};
|
};
|
||||||
|
@ -5,375 +5,398 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
@apply font-sans-serif bg-ralsei-black text-ralsei-white;
|
@apply font-sans-serif bg-ralsei-black text-ralsei-white;
|
||||||
@apply prose-code:font-monospace prose-headings:font-monospace;
|
@apply prose-code:font-monospace prose-headings:font-monospace;
|
||||||
cursor: url('/icons/gaze_closed.webp'), default;
|
cursor: url('/icons/gaze_closed.webp'), default;
|
||||||
scrollbar-color: theme(colors.ralsei.green.dark) transparent;
|
scrollbar-color: theme(colors.ralsei.green.dark) transparent;
|
||||||
-webkit-font-smoothing: none !important;
|
-webkit-font-smoothing: none !important;
|
||||||
font-smooth: never !important;
|
font-smooth: never !important;
|
||||||
font-smoothing: none !important;
|
font-smoothing: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Fusion Pixel 10px Monospaced zh_hans';
|
font-family: 'Fusion Pixel 10px Monospaced zh_hans';
|
||||||
src: url('/fonts/fusion-pixel-sc-monospaced.woff2') format('woff2');
|
src: url('/fonts/fusion-pixel-sc-monospaced.woff2') format('woff2');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Fusion Pixel 10px Proportional zh_hans';
|
font-family: 'Fusion Pixel 10px Proportional zh_hans';
|
||||||
src: url('/fonts/fusion-pixel-sc-proportional.woff2') format('woff2');
|
src: url('/fonts/fusion-pixel-sc-proportional.woff2') format('woff2');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Doll Mono';
|
font-family: 'Doll Mono';
|
||||||
src: url('/fonts/dollmonoopt.woff2') format('woff2');
|
src: url('/fonts/dollmonoopt.woff2') format('woff2');
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose h1::before {
|
.prose h1::before {
|
||||||
content: '[ ';
|
content: '[ ';
|
||||||
}
|
}
|
||||||
.prose h1::after {
|
.prose h1::after {
|
||||||
content: ' ]';
|
content: ' ]';
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose h2::before {
|
.prose h2::before {
|
||||||
content: '[= ';
|
content: '[= ';
|
||||||
}
|
}
|
||||||
.prose h2::after {
|
.prose h2::after {
|
||||||
content: ' =]';
|
content: ' =]';
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose h3::before {
|
|
||||||
content: '[== ';
|
|
||||||
}
|
|
||||||
.prose h3::after {
|
|
||||||
content: ' ==]';
|
|
||||||
}
|
|
||||||
|
|
||||||
.prose h4::before {
|
|
||||||
content: '[=== ';
|
|
||||||
}
|
|
||||||
.prose h4::after {
|
|
||||||
content: ' ===]';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* .prose h1::after,.prose h2::after,.prose h3::after,.prose h4::after {
|
.prose h3::before {
|
||||||
|
content: '[== ';
|
||||||
|
}
|
||||||
|
.prose h3::after {
|
||||||
|
content: ' ==]';
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose h4::before {
|
||||||
|
content: '[=== ';
|
||||||
|
}
|
||||||
|
.prose h4::after {
|
||||||
|
content: ' ===]';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .prose h1::after,.prose h2::after,.prose h3::after,.prose h4::after {
|
||||||
@apply motion-safe:animate-blink;
|
@apply motion-safe:animate-blink;
|
||||||
content: '_';
|
content: '_';
|
||||||
} */
|
} */
|
||||||
|
|
||||||
.prose a {
|
.prose a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose a:hover {
|
.prose a:hover {
|
||||||
@apply motion-safe:animate-squiggle;
|
@apply motion-safe:animate-squiggle;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,h2,h3,h4,h5,h6,.text-shadow-pink {
|
h1,
|
||||||
text-shadow: 0 0 3px theme(colors.ralsei.black), 0 0 6px theme(colors.ralsei.pink.neon), 0 0 10px #fff3;
|
h2,
|
||||||
}
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6,
|
||||||
|
.text-shadow-pink {
|
||||||
|
text-shadow:
|
||||||
|
0 0 3px theme(colors.ralsei.black),
|
||||||
|
0 0 6px theme(colors.ralsei.pink.neon),
|
||||||
|
0 0 10px #fff3;
|
||||||
|
}
|
||||||
|
|
||||||
.text-shadow-red {
|
.text-shadow-red {
|
||||||
text-shadow: 0 0 1px theme(colors.ralsei.black), 0 0 5px theme(colors.red.600);
|
text-shadow:
|
||||||
}
|
0 0 1px theme(colors.ralsei.black),
|
||||||
|
0 0 5px theme(colors.red.600);
|
||||||
|
}
|
||||||
|
|
||||||
.text-shadow-none {
|
.text-shadow-none {
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose ul, ul {
|
.prose ul,
|
||||||
list-style-type: '>>';
|
ul {
|
||||||
}
|
list-style-type: '>>';
|
||||||
|
}
|
||||||
|
|
||||||
.text-shadow-green {
|
.text-shadow-green {
|
||||||
text-shadow: 0 0 2px theme(colors.ralsei.black), 0 0 5px theme(colors.ralsei.green.light);
|
text-shadow:
|
||||||
}
|
0 0 2px theme(colors.ralsei.black),
|
||||||
|
0 0 5px theme(colors.ralsei.green.light);
|
||||||
|
}
|
||||||
|
|
||||||
a,button,input[type=submit] {
|
a,
|
||||||
@apply text-shadow-green;
|
button,
|
||||||
cursor: url('/icons/gaze.webp'), pointer;
|
input[type='submit'] {
|
||||||
}
|
@apply text-shadow-green;
|
||||||
|
cursor: url('/icons/gaze.webp'), pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.animate-squiggle {
|
.animate-squiggle {
|
||||||
animation: squigglevision 0.3s infinite;
|
animation: squigglevision 0.3s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes squigglevision {
|
@keyframes squigglevision {
|
||||||
0% {
|
0% {
|
||||||
filter: url("#squiggly-0");
|
filter: url('#squiggly-0');
|
||||||
}
|
}
|
||||||
25% {
|
25% {
|
||||||
filter: url("#squiggly-1");
|
filter: url('#squiggly-1');
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
filter: url("#squiggly-2");
|
filter: url('#squiggly-2');
|
||||||
}
|
}
|
||||||
75% {
|
75% {
|
||||||
filter: url("#squiggly-3");
|
filter: url('#squiggly-3');
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
filter: url("#squiggly-4");
|
filter: url('#squiggly-4');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes blink {
|
@keyframes blink {
|
||||||
0% {
|
0% {
|
||||||
opacity: 1.0;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
opacity: 0.0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
opacity: 1.0;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
.text-error {
|
.text-error {
|
||||||
@apply text-xl text-red-600 text-shadow-red;
|
@apply text-xl text-red-600 text-shadow-red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-groove {
|
.border-groove {
|
||||||
border-style: groove;
|
border-style: groove;
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-ridge {
|
.border-ridge {
|
||||||
border-style: ridge;
|
border-style: ridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-grid-background-anim {
|
.app-grid-background-anim {
|
||||||
animation: 4s linear app-grid-move-first-layer infinite;
|
animation: 4s linear app-grid-move-first-layer infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-grid-background-second-layer-anim {
|
|
||||||
animation: 12s linear app-grid-move-second-layer infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes app-grid-move-first-layer {
|
.app-grid-background-second-layer-anim {
|
||||||
0% {
|
animation: 12s linear app-grid-move-second-layer infinite;
|
||||||
background-position: 0px 0px;
|
}
|
||||||
}
|
|
||||||
100% {
|
|
||||||
background-position: 126px 84px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes app-grid-move-second-layer {
|
|
||||||
0% {
|
|
||||||
background-position: 96px 120px;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
background-position: 0px 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
@keyframes app-grid-move-first-layer {
|
||||||
@keyframes bounce-reverse {
|
0% {
|
||||||
0%, 100% {
|
background-position: 0px 0px;
|
||||||
transform: none;
|
}
|
||||||
animation-timing-function: cubic-bezier(0,0,0.2,1);
|
100% {
|
||||||
}
|
background-position: 126px 84px;
|
||||||
50% {
|
}
|
||||||
transform: translateY(-25%);
|
}
|
||||||
animation-timing-function: cubic-bezier(0.8,0,1,1);
|
|
||||||
}
|
@keyframes app-grid-move-second-layer {
|
||||||
}
|
0% {
|
||||||
}
|
background-position: 96px 120px;
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
}
|
||||||
.animate-bounce-reverse:hover {
|
100% {
|
||||||
animation: bounce-reverse 1s infinite;
|
background-position: 0px 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
@keyframes bounce-reverse {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: none;
|
||||||
|
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-25%);
|
||||||
|
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
.animate-bounce-reverse:hover {
|
||||||
|
animation: bounce-reverse 1s infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.app-selected-route {
|
a.app-selected-route {
|
||||||
text-shadow: 0 0 2px theme(colors.ralsei.black), 0 0 5px theme(colors.ralsei.pink.regular);
|
text-shadow:
|
||||||
|
0 0 2px theme(colors.ralsei.black),
|
||||||
|
0 0 5px theme(colors.ralsei.pink.regular);
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-grid-background {
|
.app-grid-background {
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(theme(colors.ralsei.green.light / 0.4), transparent 2px),
|
linear-gradient(theme(colors.ralsei.green.light / 0.4), transparent 2px),
|
||||||
linear-gradient(to right, theme(colors.ralsei.green.light / 0.4), transparent 2px);
|
linear-gradient(to right, theme(colors.ralsei.green.light / 0.4), transparent 2px);
|
||||||
background-size: 100% 42px, 42px 100%;
|
background-size:
|
||||||
|
100% 42px,
|
||||||
|
42px 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-grid-background-second-layer {
|
.app-grid-background-second-layer {
|
||||||
background-image:
|
background-image:
|
||||||
linear-gradient(theme(colors.ralsei.pink.neon / 0.4), transparent 1px),
|
linear-gradient(theme(colors.ralsei.pink.neon / 0.4), transparent 1px),
|
||||||
linear-gradient(to right, theme(colors.ralsei.pink.neon / 0.4), transparent 1px);
|
linear-gradient(to right, theme(colors.ralsei.pink.neon / 0.4), transparent 1px);
|
||||||
background-size: 100% 24px, 24px 100%;
|
background-size:
|
||||||
|
100% 24px,
|
||||||
|
24px 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
.animate-window-open {
|
.animate-window-open {
|
||||||
animation: 0.5s ease-out window-open-scale forwards;
|
animation: 0.5s ease-out window-open-scale forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-window-open-vertical {
|
|
||||||
animation: 0.5s ease-out window-open-scale-vertical forwards;
|
|
||||||
transform-origin: bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-window-open-horizontal {
|
|
||||||
animation: 0.5s ease-out window-open-scale-horizontal forwards;
|
|
||||||
transform-origin: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-window-open-move-up {
|
.animate-window-open-vertical {
|
||||||
animation: 0.5s ease-out window-open-move-up forwards;
|
animation: 0.5s ease-out window-open-scale-vertical forwards;
|
||||||
}
|
transform-origin: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
.animate-window-open-move-down {
|
.animate-window-open-horizontal {
|
||||||
animation: 0.5s ease-out window-open-move-down forwards;
|
animation: 0.5s ease-out window-open-scale-horizontal forwards;
|
||||||
}
|
transform-origin: left;
|
||||||
|
}
|
||||||
|
|
||||||
.animate-window-open-move-left {
|
.animate-window-open-move-up {
|
||||||
animation: 0.5s ease-out window-open-move-left forwards;
|
animation: 0.5s ease-out window-open-move-up forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-window-open-move-right {
|
.animate-window-open-move-down {
|
||||||
animation: 0.5s ease-out window-open-move-right forwards;
|
animation: 0.5s ease-out window-open-move-down forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-overflow-keep-hidden {
|
.animate-window-open-move-left {
|
||||||
animation: 0.6s linear overflow-keep-hidden forwards;
|
animation: 0.5s ease-out window-open-move-left forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes window-open-scale {
|
|
||||||
0% {
|
|
||||||
scale: 0.0;
|
|
||||||
opacity: 0.0;
|
|
||||||
}
|
|
||||||
20% {
|
|
||||||
scale: 0.0;
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
scale: 1.0;
|
|
||||||
opacity: 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes window-open-scale-vertical {
|
|
||||||
0% {
|
|
||||||
scale: 1.0 0.0;
|
|
||||||
opacity: 0.0;
|
|
||||||
}
|
|
||||||
20% {
|
|
||||||
scale: 1.0 0.0;
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
scale: 1.0 1.0;
|
|
||||||
opacity: 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes window-open-scale-horizontal {
|
|
||||||
0% {
|
|
||||||
scale: 0.0 1.0;
|
|
||||||
opacity: 0.0;
|
|
||||||
}
|
|
||||||
20% {
|
|
||||||
scale: 0.0 1.0;
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
scale: 1.0 1.0;
|
|
||||||
opacity: 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes window-open-move-down {
|
.animate-window-open-move-right {
|
||||||
0% {
|
animation: 0.5s ease-out window-open-move-right forwards;
|
||||||
translate: 0 10rem;
|
}
|
||||||
opacity: 0.0;
|
|
||||||
}
|
|
||||||
20% {
|
|
||||||
translate: 0 10rem;
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
translate: normal;
|
|
||||||
opacity: 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes window-open-move-up {
|
.animate-overflow-keep-hidden {
|
||||||
0% {
|
animation: 0.6s linear overflow-keep-hidden forwards;
|
||||||
translate: 0 -10rem;
|
}
|
||||||
opacity: 0.0;
|
|
||||||
}
|
|
||||||
20% {
|
|
||||||
translate: 0 -10rem;
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
translate: normal;
|
|
||||||
opacity: 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes window-open-move-left {
|
@keyframes window-open-scale {
|
||||||
0% {
|
0% {
|
||||||
translate: 10rem 0;
|
scale: 0;
|
||||||
opacity: 0.0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
20% {
|
20% {
|
||||||
translate: 10rem 0;
|
scale: 0;
|
||||||
}
|
}
|
||||||
60% {
|
60% {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
translate: normal;
|
scale: 1;
|
||||||
opacity: 1.0;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes window-open-move-right {
|
@keyframes window-open-scale-vertical {
|
||||||
0% {
|
0% {
|
||||||
translate: -10rem 0;
|
scale: 1 0;
|
||||||
opacity: 0.0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
20% {
|
20% {
|
||||||
translate: -10rem 0;
|
scale: 1 0;
|
||||||
}
|
}
|
||||||
60% {
|
60% {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
translate: normal;
|
scale: 1 1;
|
||||||
opacity: 1.0;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes overflow-keep-hidden {
|
@keyframes window-open-scale-horizontal {
|
||||||
0% {
|
0% {
|
||||||
overflow: hidden;
|
scale: 0 1;
|
||||||
}
|
opacity: 0;
|
||||||
100% {
|
}
|
||||||
overflow: auto;
|
20% {
|
||||||
}
|
scale: 0 1;
|
||||||
}
|
}
|
||||||
|
60% {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
scale: 1 1;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes window-open-move-down {
|
||||||
|
0% {
|
||||||
|
translate: 0 10rem;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
translate: 0 10rem;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
translate: normal;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes window-open-move-up {
|
||||||
|
0% {
|
||||||
|
translate: 0 -10rem;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
translate: 0 -10rem;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
translate: normal;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes window-open-move-left {
|
||||||
|
0% {
|
||||||
|
translate: 10rem 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
translate: 10rem 0;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
translate: normal;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes window-open-move-right {
|
||||||
|
0% {
|
||||||
|
translate: -10rem 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
translate: -10rem 0;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
translate: normal;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes overflow-keep-hidden {
|
||||||
|
0% {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,61 +1,57 @@
|
|||||||
const colors = require('tailwindcss/colors')
|
const colors = require('tailwindcss/colors');
|
||||||
const plugin = require('tailwindcss/plugin')
|
const plugin = require('tailwindcss/plugin');
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: ['./src/**/*.{html,js,svelte,ts,md}'],
|
content: ['./src/**/*.{html,js,svelte,ts,md}'],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
typography: ({ theme }) => ({
|
typography: ({ theme }) => ({
|
||||||
ralsei: {
|
ralsei: {
|
||||||
css: {
|
css: {
|
||||||
'--tw-prose-body': theme('colors.ralsei.white'),
|
'--tw-prose-body': theme('colors.ralsei.white'),
|
||||||
'--tw-prose-headings': theme('colors.ralsei.pink.neon'),
|
'--tw-prose-headings': theme('colors.ralsei.pink.neon'),
|
||||||
'--tw-prose-lead': theme('colors.ralsei.white'),
|
'--tw-prose-lead': theme('colors.ralsei.white'),
|
||||||
'--tw-prose-links': theme('colors.ralsei.green.light'),
|
'--tw-prose-links': theme('colors.ralsei.green.light'),
|
||||||
'--tw-prose-bold': theme('colors.ralsei.white'),
|
'--tw-prose-bold': theme('colors.ralsei.white'),
|
||||||
'--tw-prose-counters': theme('colors.ralsei.pink.regular'),
|
'--tw-prose-counters': theme('colors.ralsei.pink.regular'),
|
||||||
'--tw-prose-bullets': theme('colors.ralsei.pink.regular'),
|
'--tw-prose-bullets': theme('colors.ralsei.pink.regular'),
|
||||||
'--tw-prose-hr': theme('colors.ralsei.white'),
|
'--tw-prose-hr': theme('colors.ralsei.white'),
|
||||||
'--tw-prose-quotes': theme('colors.ralsei.white'),
|
'--tw-prose-quotes': theme('colors.ralsei.white'),
|
||||||
'--tw-prose-quote-borders': theme('colors.ralsei.white'),
|
'--tw-prose-quote-borders': theme('colors.ralsei.white'),
|
||||||
'--tw-prose-captions': theme('colors.ralsei.white'),
|
'--tw-prose-captions': theme('colors.ralsei.white'),
|
||||||
'--tw-prose-code': theme('colors.ralsei.pink.regular'),
|
'--tw-prose-code': theme('colors.ralsei.pink.regular'),
|
||||||
'--tw-prose-pre-code': theme('colors.ralsei.white'),
|
'--tw-prose-pre-code': theme('colors.ralsei.white'),
|
||||||
'--tw-prose-pre-bg': theme('colors.ralsei.green.dark'),
|
'--tw-prose-pre-bg': theme('colors.ralsei.green.dark'),
|
||||||
'--tw-prose-th-borders': theme('colors.ralsei.white'),
|
'--tw-prose-th-borders': theme('colors.ralsei.white'),
|
||||||
'--tw-prose-td-borders': theme('colors.ralsei.white'),
|
'--tw-prose-td-borders': theme('colors.ralsei.white')
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
animation: {
|
animation: {
|
||||||
'bounce-slow': 'bounce 3s infinite',
|
'bounce-slow': 'bounce 3s infinite',
|
||||||
'pulse-fast': 'pulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite',
|
'pulse-fast': 'pulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite',
|
||||||
'blink': 'blink 1s step-start infinite',
|
blink: 'blink 1s step-start infinite'
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
ralsei: {
|
ralsei: {
|
||||||
pink: {
|
pink: {
|
||||||
regular: '#fe96e0',
|
regular: '#fe96e0',
|
||||||
neon: '#ff3eb7',
|
neon: '#ff3eb7'
|
||||||
},
|
},
|
||||||
white: '#fff9fe',
|
white: '#fff9fe',
|
||||||
black: '#000801',
|
black: '#000801',
|
||||||
green: {
|
green: {
|
||||||
light: '#4dcc8e',
|
light: '#4dcc8e',
|
||||||
dark: '#162d26',
|
dark: '#162d26'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
'sans-serif': ['"Fusion Pixel 10px Proportional zh_hans", sans-serif'],
|
'sans-serif': ['"Fusion Pixel 10px Proportional zh_hans", sans-serif'],
|
||||||
monospace: ['"Fusion Pixel 10px Monospaced zh_hans", monospace'],
|
monospace: ['"Fusion Pixel 10px Monospaced zh_hans", monospace']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [require('@tailwindcss/typography'), require('@tailwindcss/forms')]
|
||||||
require('@tailwindcss/typography'),
|
};
|
||||||
require('@tailwindcss/forms'),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user