diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 6931566..4ae2fc1 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/Caddyfile b/Caddyfile index 0f94919..4f77dbe 100644 --- a/Caddyfile +++ b/Caddyfile @@ -1,6 +1,7 @@ (https_redirect) { @do_https_redirect { not header_regexp veryoldbrowser User-Agent Navigator|MSIE|Mosaic|Kindle|^curl|NintendoBrowser/ + not host *.onion *.*.onion protocol http } redir @do_https_redirect https://{host}{uri} @@ -205,7 +206,7 @@ Disallow: /" } } } -matrix.matdoes.dev, matrix.matdoes.dev:8448 { +matrix.matdoes.dev matrix.matdoes.dev:8448 { handle /.well-known/matrix/server { header content-type application/json respond "{\"m.server\":\"matrix.matdoes.dev\"}" @@ -229,7 +230,7 @@ fedi.matdoes.dev { } } -f.matdoes.dev { +f.matdoes.dev media.f.matdoes.dev cache.f.matdoes.dev { @chrome { header_regexp chrome User-Agent Chrome\/[0-9./]+\s(Mobile\s)?Safari\/[0-9./]+$ not header User-Agent *Googlebot/* @@ -241,7 +242,9 @@ f.matdoes.dev { git.matdoes.dev { reverse_proxy 127.0.0.1:3000 } -s.matdoes.dev { +http://s.matdoes.dev https://s.matdoes.dev { + import https_redirect + reverse_proxy 127.0.0.1:28019 } @@ -296,3 +299,19 @@ hetzner.matdoes.dev { xmpp.matdoes.dev { respond "" } + +mcassets.matdoes.dev { + reverse_proxy 127.0.0.1:10573 +} + +www.www.matdoes.dev { + header { + Content-Type text/html + Server "meow" + } + respond "nyaaaaaaaaaaaaaaaaaaa" 200 +} + +www.matdoes.dev { + redir https://matdoes.dev{uri} +} \ No newline at end of file diff --git a/package.json b/package.json index 9b0c608..cea77ee 100644 --- a/package.json +++ b/package.json @@ -14,38 +14,40 @@ "postinstall": "patch-package" }, "devDependencies": { - "@img/sharp-linux-x64": "^0.33.2", + "@img/sharp-linux-x64": "^0.33.3", "@sveltejs/vite-plugin-svelte": "^3.0.2", "@types/cookie": "^0.6.0", "@types/html-minifier": "^4.0.5", - "@typescript-eslint/eslint-plugin": "^7.3.1", - "@typescript-eslint/parser": "^7.3.1", - "eslint": "^8.57.0", + "@types/matter-js": "^0.19.6", + "@typescript-eslint/eslint-plugin": "^7.6.0", + "@typescript-eslint/parser": "^7.6.0", + "eslint": "^9.0.0", "eslint-config-prettier": "^9.1.0", "mdsvex": "^0.11.0", "prettier": "^3.2.5", "prettier-plugin-svelte": "^3.2.2", - "sharp": "^0.33.2", + "sharp": "^0.33.3", "svelte": "4.2.12", - "svelte-check": "^3.6.7", + "svelte-check": "^3.6.9", "svelte-preprocess": "^5.1.3", "tslib": "^2.6.2", - "typescript": "^5.4.2" + "typescript": "^5.4.4" }, "type": "module", "dependencies": { "@lukeed/uuid": "^2.0.1", "@sveltejs/adapter-node": "^5.0.1", "@sveltejs/adapter-static": "^3.0.1", - "@sveltejs/kit": "^2.5.4", + "@sveltejs/kit": "^2.5.5", "@types/js-yaml": "^4.0.9", - "cbor-x": "^1.5.8", + "cbor-x": "^1.5.9", "cookie": "^0.6.0", "html-minifier": "^4.0.0", + "matter-js": "^0.19.0", "patch-package": "^8.0.0", "postinstall-postinstall": "^2.1.0", "svelte-body": "^1.4.0", - "vite": "5.1.6" + "vite": "5.2.8" }, "engines": { "node": ">=16" diff --git a/src/app.css b/src/app.css index fe4cea0..811fd37 100644 --- a/src/app.css +++ b/src/app.css @@ -19,6 +19,12 @@ --text-font: 'Atkinson Hyperlegible', system-ui, -apple-system, BlinkMacSystemFont, sans-serif; } +*, +*:before, +*:after { + box-sizing: border-box; +} + html, body, #page { diff --git a/src/lib/gravity.ts b/src/lib/gravity.ts new file mode 100644 index 0000000..94e8afc --- /dev/null +++ b/src/lib/gravity.ts @@ -0,0 +1,157 @@ +import { browser } from '$app/environment' +import Matter from 'matter-js' + +const GRAVITY_QUERY_SELECTOR = 'p, h1, h2, .button, .icon' + +export function initGravity(): () => void { + const { Engine, Bodies, Composite, Runner, Mouse, MouseConstraint } = Matter + + console.log('gravity enabled') + + // create an engine + const engine = Engine.create({ enableSleeping: true }) + + const floorBody = Bodies.rectangle( + window.innerWidth / 2, + window.innerHeight - 5, + window.innerWidth, + 10, + { + isStatic: true, + } + ) + Composite.add(engine.world, [floorBody]) + + const trackedElements = new Map() + + async function enableGravityOnElement(el: HTMLElement) { + await new Promise((resolve) => requestAnimationFrame(resolve)) + + let originalTextContent: string | null = null + if (el.classList.contains('copyright') || el.id === 'main-title') { + el.style.width = 'fit-content' + el.style.margin = '0 auto' + if (el.id === 'main-title') { + originalTextContent = el.textContent + el.textContent = 'matdoesdev' + } + } + const bb = el.getBoundingClientRect() + if (originalTextContent) el.textContent = originalTextContent + + const body = Bodies.rectangle( + bb.left + bb.width / 2, + bb.top + bb.height / 2, + bb.width, + bb.height, + { restitution: 1 } + ) + el.style.position = 'absolute' + el.style.left = bb.left + 'px' + el.style.top = bb.top + 'px' + el.style.width = bb.width + 'px' + el.style.height = bb.height + 'px' + el.style.userSelect = 'none' + // el.style.outline = '1px solid red' + + Composite.add(engine.world, [body]) + trackedElements.set(el, body) + } + + function makeAnchorUndraggable(el: HTMLAnchorElement) { + // el.style.pointerEvents = 'none' + // el.style.cursor = 'pointer' + el.draggable = false + + // document.addEventListener('click', (e) => { + // el.style.pointerEvents = '' + // const clickedEl = document.elementFromPoint(e.clientX, e.clientY) + // el.style.pointerEvents = 'none' + // // if (clickedEl instanceof HTMLAnchorElement) { + // // e.preventDefault() + // // goto(clickedEl.href) + // // } + // if (el.contains(clickedEl)) { + // e.preventDefault() + // const url = new URL(el.href) + // if (url.origin === window.location.origin) { + // goto(el.href) + // } else { + // window.location.href = el.href + // } + // } + // }) + } + + document.querySelectorAll(GRAVITY_QUERY_SELECTOR).forEach((el) => { + enableGravityOnElement(el as HTMLElement) + }) + document.querySelectorAll('a').forEach((el) => { + makeAnchorUndraggable(el as HTMLAnchorElement) + }) + + // update positions every frame + Matter.Events.on(engine, 'beforeUpdate', function (event) { + trackedElements.forEach((body, el) => { + if (body.position.y < 0) { + // move up + body.position.y = window.innerHeight + } + + el.style.left = body.position.x - el.offsetWidth / 2 + 'px' + el.style.top = body.position.y - el.offsetHeight / 2 + 'px' + el.style.transform = `rotate(${body.angle}rad)` + + // rotate + // el.style.transform = '' + + // const [originalWidthStyle, originalHeightStyle] = [el.style.width, el.style.height] + // el.style.maxWidth = originalWidthStyle + // el.style.maxHeight = originalHeightStyle + // el.style.width = '' + // el.style.height = '' + // const bb = el.getBoundingClientRect() + // el.style.transform = `rotate(${body.angle}rad)` + // el.style.width = originalWidthStyle + // el.style.height = originalHeightStyle + // el.style.maxWidth = '' + // el.style.maxHeight = '' + + // const fakeBody = Bodies.rectangle( + // bb.left + bb.width / 2, + // bb.top + bb.height / 2, + // bb.width, + // bb.height, + // { + // angle: body.angle, + // } + // ) + // body.vertices = fakeBody.vertices + }) + }) + + // create runner + const runner = Runner.create() + + // run the engine + Runner.run(runner, engine) + + // add mouse control + var mouse = Mouse.create(document.body), + mouseConstraint = MouseConstraint.create(engine, { + mouse: mouse, + constraint: { + stiffness: 0.2, + render: { + visible: false, + }, + }, + }) + Composite.add(engine.world, mouseConstraint) + + console.log('running engine') + + return () => { + Runner.stop(runner) + } +} diff --git a/src/routes/(main)/+layout.svelte b/src/routes/(main)/+layout.svelte index 877da4f..e07a40c 100644 --- a/src/routes/(main)/+layout.svelte +++ b/src/routes/(main)/+layout.svelte @@ -2,6 +2,7 @@ import '../../app.css' import { fly } from 'svelte/transition' import type { LayoutData } from '../$types' + import { browser } from '$app/environment' export let data: LayoutData @@ -11,7 +12,7 @@ let previousPathname = data.pathname let currentPathName = data.pathname let flyDirection = 1 // 1 is right, -1 is left - $: { + $: if (browser) { if (previousPathname !== currentPathName) previousPathname = currentPathName currentPathName = data.pathname @@ -19,6 +20,35 @@ if (previousPathname === '/') flyDirection = 1 else if (previousPathname === '/blog' && currentPathName !== '/') flyDirection = 1 else flyDirection = -1 + onPathChange() + } + + let pathChangeTimestamps: number[] = [] + + let stopGravity: (() => void) | null = null + + async function onPathChange() { + // if we switched paths more than 10 times in the past 5 seconds, import $lib/gravity.js + pathChangeTimestamps.push(Date.now()) + while (pathChangeTimestamps[0] < Date.now() - 5000) pathChangeTimestamps.shift() + console.log(pathChangeTimestamps) + if (pathChangeTimestamps.length >= 10) { + const { initGravity } = await import('$lib/gravity') + // wait 200ms for the animation to finish + await new Promise((resolve) => setTimeout(resolve, 200)) + // and an animation frame + await new Promise((resolve) => requestAnimationFrame(resolve)) + + const lastPathChangeTimestamp = pathChangeTimestamps[pathChangeTimestamps.length - 1] + // only if it was over 200ms ago + if (lastPathChangeTimestamp < Date.now() - 200) { + if (currentPathName === '/') { + stopGravity = initGravity() + } else { + stopGravity?.() + } + } + } } @@ -33,15 +63,12 @@ {/key}