Compare commits

..

3 Commits

Author SHA1 Message Date
70d011423b
feat: tooltips!
All checks were successful
create archive with lfs / tag (push) Successful in 9s
2024-11-24 01:27:20 +03:00
1374dbece4
feat: wip visitors stuff 2024-11-24 00:18:44 +03:00
630a1c043c
feat: minor style improvements 2024-11-23 23:02:01 +03:00
6 changed files with 93 additions and 39 deletions

View File

@ -0,0 +1,17 @@
<script lang="ts">
import Window from "./window.svelte";
export let x: string = "translate-x-none"
export let y: string = "translate-y-full"
export let targetY: string = "group-hover:-translate-y-[105%]"
export let targetX: string = "group-hover:-translate-x-2/3"
</script>
<div class="group">
<div class="absolute scale-0 transition-all [transition-duration:300ms] opacity-0 group-hover:scale-100 group-hover:opacity-100 {y} {x} {targetY} {targetX} transform-gpu">
<Window tooltip>
<slot name="tooltipContent">Hello world!</slot>
</Window>
</div>
<slot/>
</div>

View File

@ -11,6 +11,7 @@
export let center: boolean = false; export let center: boolean = false;
export let layered: boolean = false; export let layered: boolean = false;
export let style: string = ""; export let style: string = "";
export let tooltip: boolean = false;
const scaleKeyframes = [ const scaleKeyframes = [
"window-open", "window-open",
@ -46,9 +47,9 @@
on:click={(data) => {focusWindow(data.currentTarget)}} on:click={(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 ? 'md:sticky md:-top-9' : ''} {center ? "mx-auto" : ""}
max-w-screen-md xl:max-w-screen-lg 2xl:max-w-screen-xl min-w-[30ch] lg:min-w-[40ch] w-full md:w-fit [height:fit-content] 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 border-8 border-t-[12px] bg-ralsei-black border-ralsei-white border-ridge {tooltip ? "border-[6px] border-t-[9px]" : "border-8 border-t-[12px]"}
animate-{chosenKeyframe} drop-shadow-[35px_35px_35px_rgba(1,1,1,0.5)] animate-{chosenKeyframe} drop-shadow-[24px_24px_24px_rgba(1,1,1,0.8)]
{style} {style}
" "
{id} {id}
@ -83,7 +84,7 @@
</div> </div>
{/if} {/if}
<div class=" <div class="
{removePadding ? "" : "p-2"} bg-gradient-to-tl {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
"> ">
<slot /> <slot />

View File

@ -7,17 +7,20 @@ 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 = { since: number }
const lastVisitors = writable<Visitor[]>([]);
const MAX_VISITORS = 10
const VISITOR_EXPIRY_SECONDS = 60 * 30 // half an hour is 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)
const ua = request.headers.get('user-agent') if (isBot(request)) { return currentVisitCount }
const isBot = ua ? ua.toLowerCase().match(/(bot|crawl|spider|walk)/) !== null : true
if (!isBot) {
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
const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || "0") const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || "0")
// get unix timestamp // get unix timestamp
const currentTime = new Date().getTime() const currentTime = Date.now()
const timeSinceVisit = currentTime - visitedTimestamp const timeSinceVisit = currentTime - visitedTimestamp
// check if this is the first time a client is visiting or if an hour has passed since they last visited // check if this is the first time a client is visiting or if an hour has passed since they last visited
if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) { if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) {
@ -28,10 +31,37 @@ export const incrementVisitCount = (request: Request, cookies: Cookies) => {
// write the visit count to a file so we can load it later again // write the visit count to a file so we can load it later again
writeFileSync(visitCountFile, currentVisitCount.toString()) writeFileSync(visitCountFile, currentVisitCount.toString())
} }
}
return currentVisitCount return currentVisitCount
} }
export const addLastVisitor = (request: Request, cookies: Cookies) => {
const currentTime = Date.now()
let visitors = get(lastVisitors).filter(
(value) => { return currentTime - value.since > 1000 * VISITOR_EXPIRY_SECONDS }
)
// 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 visitors }
const scopedCookies = scopeCookies(cookies, '/')
// parse the last visit timestamp from cookies if it exists
const visitorTimestamp = parseInt(scopedCookies.get('visitorTimestamp') || "0")
// get unix timestamp
const timeSinceVisit = currentTime - visitorTimestamp
// check if this is the first time a client is visiting or if an hour has passed since they last visited
if (visitorTimestamp === 0 || timeSinceVisit > 1000 * VISITOR_EXPIRY_SECONDS) {
visitors.push({ since: currentTime })
if (visitors.length > MAX_VISITORS) { visitors.shift() }
// update the cookie with the current timestamp
scopedCookies.set('visitorTimestamp', currentTime.toString())
}
return visitors
}
const isBot = (request: Request) => {
const ua = request.headers.get('user-agent')
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',

View File

@ -2,6 +2,7 @@
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import getTitle from '$lib/getTitle'; import getTitle from '$lib/getTitle';
import NavButton from '../components/navButton.svelte'; import NavButton from '../components/navButton.svelte';
import Tooltip from '../components/tooltip.svelte';
import Window from '../components/window.svelte'; import Window from '../components/window.svelte';
import '../styles/app.css'; import '../styles/app.css';
@ -157,14 +158,12 @@
<a class="hover:underline" href="https://xn--sr8hvo.ws">IndieWeb 🕸💍</a> <a class="hover:underline" href="https://xn--sr8hvo.ws">IndieWeb 🕸💍</a>
<a title="next site" class="hover:underline" href="https://xn--sr8hvo.ws/next"></a> <a title="next site" class="hover:underline" href="https://xn--sr8hvo.ws/next"></a>
</div> </div>
<div class="group navbox"> <Tooltip>
<div class="absolute transition-all opacity-0 group-hover:opacity-100 translate-y-2/3 -translate-x-1/3 group-hover:-translate-y-2/3 transform-gpu"> <svelte:fragment slot="tooltipContent">
<Window style="!min-w-fit">
<img class="min-w-64" style="image-rendering: crisp-edges pixelated;" alt="visits" src="https://count.getloli.com/@yusdacrawebsite?name=yusdacrawebsitetest&theme=booru-lewd&padding=5&offset=0&align=center&scale=1&pixelated=1&darkmode=0&num={data.visitCount}"/> <img class="min-w-64" style="image-rendering: crisp-edges pixelated;" alt="visits" src="https://count.getloli.com/@yusdacrawebsite?name=yusdacrawebsitetest&theme=booru-lewd&padding=5&offset=0&align=center&scale=1&pixelated=1&darkmode=0&num={data.visitCount}"/>
</Window> </svelte:fragment>
</div> <div class="navbox"><p><span class="text-ralsei-green-light text-shadow-green">{data.visitCount}</span> visit(s)</p></div>
<p><span class="text-ralsei-green-light text-shadow-green">{data.visitCount}</span> visit(s)</p> </Tooltip>
</div>
{#if isRoute("entries")} {#if isRoute("entries")}
<div class="navbox !gap-1"> <div class="navbox !gap-1">
<a class="align-middle hover:underline" href="/entries/_rss">rss</a> <a class="align-middle hover:underline" href="/entries/_rss">rss</a>

View File

@ -1,5 +1,6 @@
<script> <script>
import { PUBLIC_BASE_URL } from '$env/static/public'; import { PUBLIC_BASE_URL } from '$env/static/public';
import Tooltip from '../components/tooltip.svelte';
import Window from '../components/window.svelte'; import Window from '../components/window.svelte';
export let data; export let data;
@ -10,12 +11,14 @@
<Window title="readme?" iconUri="/icons/question.png"> <Window title="readme?" iconUri="/icons/question.png">
<div class="flex flex-col prose prose-ralsei prose-img:m-0 leading-none"> <div class="flex flex-col prose prose-ralsei prose-img:m-0 leading-none">
<div class="flex flex-grow"> <div class="flex flex-grow">
<Tooltip x="-translate-x-[40%]" targetX="group-hover:-translate-x-[10%]">
<svelte:fragment slot="tooltipContent">character from q.u.q. (good vn go read it NOW)</svelte:fragment>
<img <img
class="mt-1 ml-1 w-36 [height:9rem] u-photo" class="mt-1 ml-1 w-36 [height:9rem] u-photo"
src="/pfp.png" src="/pfp.png"
alt="character from q.u.q." alt="character from q.u.q."
title="character from q.u.q. (good vn go read it NOW)"
/> />
</Tooltip>
<ul <ul
class="place-self-center m-0 leading-none marker:[content:'->'] [list-style-type:'->']" class="place-self-center m-0 leading-none marker:[content:'->'] [list-style-type:'->']"
> >

View File

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import Window from '../../components/window.svelte'; import Tooltip from '../../components/tooltip.svelte';
import Window from '../../components/window.svelte';
export let data; export let data;
$: hasPreviousPage = data.page > 1; $: hasPreviousPage = data.page > 1;
@ -43,15 +44,18 @@
<p class="text-sm font-monospace">--- posted by ...</p> <p class="text-sm font-monospace">--- posted by ...</p>
</div> </div>
</div> </div>
<div class="entry flex flex-wrap gap-1.5 p-1"> <div class="entry flex flex-wrap gap-1.5 p-1 items-center">
<p class="text-xl ms-2">auth via:</p> <p class="text-xl ms-2 align-middle">auth via:</p>
{#each ['discord', 'github'] as platform} {#each ['discord', 'github'] as platform}
<Tooltip x="" y="translate-y-[70%]" targetY="" targetX="">
<svelte:fragment slot="tooltipContent">post with {platform}</svelte:fragment>
<input <input
type="submit" type="submit"
value={platform} value={platform}
formaction="?/post_{platform}" formaction="?/post_{platform}"
class="text-lg text-ralsei-green-light leading-5 hover:underline motion-safe:hover:animate-squiggle w-fit p-0.5" class="text-lg text-ralsei-green-light leading-5 hover:underline motion-safe:hover:animate-squiggle w-fit p-0.5"
/> />
</Tooltip>
{/each} {/each}
</div> </div>
{#if data.sendRatelimited} {#if data.sendRatelimited}