From ea172a2b391d5882a9abef3aa16bfce925575659 Mon Sep 17 00:00:00 2001 From: Eddie Pace Date: Wed, 5 Nov 2025 00:11:40 +0000 Subject: [PATCH 01/13] transplant weather pages --- src/routes/weather/+page.server.ts | 51 ++-- src/routes/weather/+page.svelte | 96 +++++--- src/routes/weather/Compass.svelte | 258 ++++++--------------- src/routes/weather/DrawCharacter.ts | 247 -------------------- src/routes/weather/Scene.svelte | 111 ++++++--- src/routes/weather/WeatherSceneAnimator.ts | 140 +++++++++++ src/routes/weather/weatherData.ts | 56 ----- src/routes/weather/weatherScene.ts | 128 ---------- src/routes/weather/weatherTime.ts | 44 ---- 9 files changed, 399 insertions(+), 732 deletions(-) delete mode 100644 src/routes/weather/DrawCharacter.ts create mode 100644 src/routes/weather/WeatherSceneAnimator.ts delete mode 100644 src/routes/weather/weatherData.ts delete mode 100644 src/routes/weather/weatherScene.ts delete mode 100644 src/routes/weather/weatherTime.ts diff --git a/src/routes/weather/+page.server.ts b/src/routes/weather/+page.server.ts index f3290860..ee6b532a 100644 --- a/src/routes/weather/+page.server.ts +++ b/src/routes/weather/+page.server.ts @@ -1,39 +1,60 @@ -import { error } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; +// import { error } from '@sveltejs/kit'; +import { error } from "@sveltejs/kit"; +import type { PageServerLoad } from "./$types"; export const prerender = false; -const openMeteoBaseUrl = 'https://api.open-meteo.com/v1/forecast'; +/* eslint-disable */ +const openMeteoBaseUrl = "https://api.open-meteo.com/v1/forecast"; // default to edinburgh let location = { latitude: 55.953251, - longitude: -3.188267 + longitude: -3.188267, }; const fetchWeather = async (latitude: number, longitude: number) => { const request = `${openMeteoBaseUrl}?latitude=${latitude}&longitude=${longitude}¤t_weather=true`; const result = await fetch(request); const data = await result.json(); - console.log(data); return data; }; export const load = (async () => { - if (typeof window !== 'undefined' && 'geolocation' in window.navigator) { - window.navigator.geolocation.getCurrentPosition((position) => { - location = { - latitude: position.coords.latitude, - longitude: position.coords.longitude - }; - }); + if ("geolocation" in navigator) { + window.navigator.geolocation.getCurrentPosition( + (position) => { + location = { + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }; + }, + (err) => { + console.log("error", err); + }, + ); } else { - console.log('geolocation permissions blocked, getting weather from Edinburgh instead'); + console.log(typeof window); + console.log("geolocation permissions blocked, getting weather from Edinburgh instead"); } try { + console.log(`using coords: ${location.latitude} ${location.longitude}`); const weather = await fetchWeather(location.latitude, location.longitude); - return weather.current_weather ?? undefined; + const currentWeather = weather.current_weather ?? undefined; + return { + latitude: weather.latitude, + longitude: weather.longitude, + ...currentWeather, + }; } catch { - error(404, "Wasn't able to get any weather data.\n\n It's probably raining"); + throw error(404, "Wasn't able to get any weather data.\n\n It's probably raining"); + return { + temperature: 16.3, + windspeed: 18.4, + winddirection: 78, + weathercode: 17, + is_day: 1, + time: Date.now(), + }; } }) satisfies PageServerLoad; diff --git a/src/routes/weather/+page.svelte b/src/routes/weather/+page.svelte index 666fb1d1..5e20f98a 100644 --- a/src/routes/weather/+page.svelte +++ b/src/routes/weather/+page.svelte @@ -1,35 +1,68 @@ + let time = new Date(data.time); -
- -
+ let elapsedTime: number; + let frameRate: number; -
-
Time:
-
{time.toLocaleTimeString()}
-
Temperature:
-
{data.temperature} °C
-
Weather:
-
{weather}
-
Wind speed:
-
{data.windspeed} km/h
-
Wind direction:
-
{windDirection}
-
+ let showDiagnostics: boolean = true; + let setTime: number = time.getHours(); + + $: updateTime(setTime); + + function updateTime(hour: number) { + const newTime = time; + newTime?.setHours(hour, 0, 0); + time = newTime; + } + -
- +
+ +
+ + +
+
+ + +
+
+
+ +
+ +
+
+
Latt/Long:
+
{`${data.latitude}, ${data.longitude}`}
+
Time:
+
{time.toLocaleTimeString()}
+
Temperature:
+
{data.temperature} °C
+
Weather:
+
{weather}
+
Wind speed:
+
{data.windspeed} km/h
+
Wind direction:
+
{windDirection}
+
+ {#if showDiagnostics && frameRate} +
+
+ fps: {frameRate?.toFixed(1)} +
+
+ {/if}
diff --git a/src/routes/weather/Compass.svelte b/src/routes/weather/Compass.svelte index f0083966..32544467 100644 --- a/src/routes/weather/Compass.svelte +++ b/src/routes/weather/Compass.svelte @@ -1,189 +1,83 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/routes/weather/DrawCharacter.ts b/src/routes/weather/DrawCharacter.ts deleted file mode 100644 index 872c102a..00000000 --- a/src/routes/weather/DrawCharacter.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { Artist } from '../../lib/canvas/Artist'; - -const AMBER = 'rgb(245, 167, 66)'; -// const DARK_ORANGE = "rgb(200, 100, 100)"; -const WHITE = 'white'; -const BLACK = 'black'; -// const GREY = "rgb(50, 50, 50)"; -const ORANGE_BROWN = 'rgb(220, 150, 110)'; -const SHADOW_BROWN = 'rgb(120, 80, 20)'; -const DARK_SHADOW_BROWN = 'rgb(71, 50, 44)'; -const DARK_GREY = 'rgb(50, 50, 50)'; -const PINK = 'rgb(220, 100, 150)'; -const DARK_PINK = 'rgb(150, 50, 100)'; - -export function drawCharacter(context: CanvasRenderingContext2D, x: number, y: number) { - const a = new Artist(context, x, y); - - const HEAD_POINTS: Points = [ - // top - [15, 0], - // ear - [15, -5], - [20, 0], - [10, 10], - // side - [5, 20], - [0, 10], - [-10, 40], - [-25, 50], - // chin - [-10, 10], - [-10, 5], - [-10, 0] - ]; - - const HEAD_PATCH_POINTS: Points = [ - [10, 0], - [15, -3], - [20, 0], - [10, 35], - [-20, -15], - [-15, -8], - [-20, 0] - ]; - - const RIGHT_EAR_POINTS: Points = [ - // top arc - [5, -6], - [4, -2], - [5, -3], - [15, -5], - // down right - [35, 18], - [2, 10], - [-5, 20], - // up left - [-15, 30], - [-5, 5], - [-3, 0], - [-10, -5], - [-5, -10], - [-5, -10], - [5, -20], - [-5, -20] - ]; - - const LEFT_EAR_POINTS: Points = [ - // top arc - [-5, -10], - [-15, -5], - [-12, 9], - // down right - [-20, 3], - [-10, 5], - [-5, 5], - [15, 35], - // up left - [10, 20], - [10, 5], - [12, -20], - [0, -25], - [5, -10] - ]; - - const NOSE: Points = [ - [5, 0], - [6, 5], - [2, 9], - [-2, 4], - [-4, 2], - [-3, 0], - [-3, -1] - ]; - - const TONGUE: Points = [ - [10, 4], - [3, -2], - [-1, 10], - [-4, 5], - [-4, 3], - [-4, 0] - ]; - - a.drawShape(HEAD_POINTS, true, 0, 0, 2, ORANGE_BROWN, ORANGE_BROWN); - - a.reset(0, 10); - context.beginPath(); - a.drawNextLine(0, 35); - a.endX = a.startX + 20; - a.endY = a.startY + 35; - context.bezierCurveTo(a.startX, a.startY, a.startX, a.startY + 15, a.endX, a.endY); - context.lineCap = 'round'; - context.lineWidth = 4; - context.strokeStyle = SHADOW_BROWN; - context.stroke(); - context.closePath(); - a.reset(0, 45); - context.beginPath(); - a.endX = a.startX - 20; - a.endY = a.startY + 35; - context.bezierCurveTo(a.startX, a.startY, a.startX, a.startY + 15, a.endX, a.endY); - context.lineCap = 'round'; - context.lineWidth = 4; - context.strokeStyle = SHADOW_BROWN; - context.stroke(); - context.closePath(); - - a.reset(0, 3); - a.drawShape(HEAD_PATCH_POINTS, true, 0, 3, 2, DARK_SHADOW_BROWN, DARK_SHADOW_BROWN); - a.reset(25, -2); - a.drawShape(RIGHT_EAR_POINTS, false, 0, 0, 2, SHADOW_BROWN, SHADOW_BROWN); - a.reset(-25, 0); - a.drawShape(LEFT_EAR_POINTS, false, 0, 0, 2, SHADOW_BROWN, SHADOW_BROWN); - a.reset(0, 80); - a.drawShape(NOSE, true, 0, 80, 2, DARK_GREY, DARK_GREY); - drawEyes(context, x, y); - a.reset(0, 108); - a.drawShape(TONGUE, true, 0, 108, 2, DARK_PINK, PINK); - a.reset(45, 80); - drawFaceLines(context, a.startX, a.startY); -} - -function drawFaceLines(context: CanvasRenderingContext2D, x: number, y: number) { - context.beginPath(); - let startX = x; - let startY = y; - let endX = startX - 5; - let endY = startY + 5; - context.bezierCurveTo(startX, startY, startX - 3, startY - 3, endX, endY); - - startX = endX; - startY = endY; - endX = startX - 25; - endY = startY + 25; - context.bezierCurveTo(startX, startY, startX, startY + 10, endX, endY); - - startX = endX; - startY = endY; - endX = startX - 14; - endY = startY - 5; - context.bezierCurveTo(startX, startY, startX - 10, startY + 2, endX, endY); - - startX = endX; - startY = endY; - endX = startX - 14; - endY = startY + 5; - context.bezierCurveTo(startX, startY, startX - 4, startY + 7, endX, endY); - - startX = endX; - startY = endY; - endX = startX - 25; - endY = startY - 25; - context.bezierCurveTo(startX, startY, startX - 25, startY - 15, endX, endY); - - startX = endX; - startY = endY; - endX = startX - 5; - endY = startY - 5; - context.bezierCurveTo(startX, startY, startX - 2, startY - 7, endX, endY); - - context.lineCap = 'round'; - context.lineWidth = 4; - context.strokeStyle = SHADOW_BROWN; - context.stroke(); - context.closePath(); -} - -function drawEyes(context: CanvasRenderingContext2D, x: number, y: number) { - const eyeSize = 17; - const eyePosX = 25; - const eyeTopPosY = 50; - const eyeBottomPosy = 34; - - // Right eye - context.beginPath(); - context.arc(x + eyePosX, y + eyeTopPosY, eyeSize, Math.PI * 1.2, Math.PI * 1.9); - context.fillStyle = DARK_GREY; - context.fill(); - context.closePath(); - context.beginPath(); - context.arc(x + eyePosX + 2, y + eyeBottomPosy, eyeSize, Math.PI * 2.2, Math.PI * 0.9); - context.fillStyle = DARK_GREY; - context.fill(); - - // Right pupil - context.beginPath(); - context.arc(x + eyePosX + 2, y + eyeTopPosY - 10, 7, Math.PI * 2, Math.PI * 1.1); - context.fillStyle = AMBER; - context.fill(); - - context.beginPath(); - context.arc(x + eyePosX + 2, y + eyeTopPosY - 8, 3, 0, Math.PI * 2); - context.fillStyle = BLACK; - context.fill(); - - context.beginPath(); - context.arc(x + eyePosX + 1, y + eyeTopPosY - 9, 1, 0, Math.PI * 2); - context.fillStyle = WHITE; - context.fill(); - - // Left eye - context.beginPath(); - context.arc(x - eyePosX, y + eyeTopPosY, eyeSize, Math.PI * 1.1, Math.PI * 1.8); - context.fillStyle = DARK_GREY; - context.fill(); - context.closePath(); - context.beginPath(); - context.arc(x - eyePosX - 2, y + eyeBottomPosy, eyeSize, Math.PI * 2.1, Math.PI * 0.8); - context.fillStyle = DARK_GREY; - context.fill(); - - // Left pupil - context.beginPath(); - context.arc(x - eyePosX - 2, y + eyeTopPosY - 10, 7, Math.PI * 1.9, Math.PI * 1); - context.fillStyle = AMBER; - context.fill(); - - context.beginPath(); - context.arc(x - eyePosX - 2, y + eyeTopPosY - 8, 3, 0, Math.PI * 2); - context.fillStyle = BLACK; - context.fill(); - - context.beginPath(); - context.arc(x - eyePosX - 3, y + eyeTopPosY - 9, 1, 0, Math.PI * 2); - context.fillStyle = WHITE; - context.fill(); -} diff --git a/src/routes/weather/Scene.svelte b/src/routes/weather/Scene.svelte index 57ad9496..17678373 100644 --- a/src/routes/weather/Scene.svelte +++ b/src/routes/weather/Scene.svelte @@ -1,39 +1,74 @@ - - +
+ + + +
diff --git a/src/lib/components/utils/BigIconButton.svelte b/src/lib/components/utils/BigIconButton.svelte new file mode 100644 index 00000000..7d171458 --- /dev/null +++ b/src/lib/components/utils/BigIconButton.svelte @@ -0,0 +1,48 @@ + + + + + diff --git a/src/lib/data/weatherData.ts b/src/lib/data/weatherData.ts new file mode 100644 index 00000000..36d59158 --- /dev/null +++ b/src/lib/data/weatherData.ts @@ -0,0 +1,92 @@ +export enum Weather { + Clear = "Clear", + Cloudy = "Cloudy", + Overcast = "Overcast", + Fog = "Fog", + Drizzle = "Drizzle", + Rain = "Rain", + Snow = "Snow", + Thunder = "Thunder", +} + +export const WEATHERS = Object.entries(Weather).map((w) => w[1]); + +export enum Direction { + N = "North", + NE = "North East", + E = "East", + SE = "South East", + S = "South", + SW = "South West", + W = "West", + NW = "North West", +} + +export interface WeatherAttributes { + name: string; + icon: string; + color: string; +} + +export const WEATHER_ATTRIBUTES: Record = { + Clear: { name: "Clear", color: "gold", icon: "sun" }, + Cloudy: { name: "Cloudy", color: "lightgray", icon: "cloud" }, + Overcast: { name: "Overcast", color: "gray", icon: "cloud" }, + Fog: { name: "Fog", color: "gray", icon: "smog" }, + Drizzle: { name: "Drizzle", color: "lightblue", icon: "cloud-rain" }, + Rain: { name: "Rain", color: "dodgerblue", icon: "cloud-rain" }, + Snow: { name: "Snow", color: "aliceblue", icon: "snowflake" }, + Thunder: { name: "Thunder", color: "darkgray", icon: "cloud-bolt" }, +}; + +/* +Code Description +0 Clear sky +1, 2, 3 Mainly clear, partly cloudy, and overcast +45, 48 Fog and depositing rime fog +51, 53, 55 Drizzle: Light, moderate, and dense intensity +56, 57 Freezing Drizzle: Light and dense intensity +61, 63, 65 Rain: Slight, moderate and heavy intensity +66, 67 Freezing Rain: Light and heavy intensity +71, 73, 75 Snow fall: Slight, moderate, and heavy intensity +77 Snow grains +80, 81, 82 Rain showers: Slight, moderate, and violent +85, 86 Snow showers slight and heavy +95 * Thunderstorm: Slight or moderate +96, 99 * Thunderstorm with slight and heavy hail +*/ + +export function getWeatherFromCode(code: number): Weather { + switch (code) { + case 0: + return Weather.Clear; + case 1 | 2: + return Weather.Cloudy; + case 3: + return Weather.Overcast; + case 45 | 48: + return Weather.Fog; + case 51 | 53 | 55 | 56 | 57: + return Weather.Drizzle; + case 61 | 63 | 65 | 66 | 67 | 80 | 81 | 82: + return Weather.Rain; + case 71 | 73 | 75 | 77 | 85 | 86: + return Weather.Snow; + case 95 | 96 | 99: + return Weather.Thunder; + default: + return Weather.Cloudy; + } +} + +export function getDirectionFromAngle(angle: number): Direction { + if (angle >= 337.5 || angle < 22.5) return Direction.N; + if (angle >= 22.5 && angle < 67.5) return Direction.NE; + if (angle >= 67.5 && angle < 112.5) return Direction.E; + if (angle >= 112.5 && angle < 157.5) return Direction.SE; + if (angle >= 157.5 && angle < 202.5) return Direction.S; + if (angle >= 202.5 && angle < 247.5) return Direction.SW; + if (angle >= 247.5 && angle < 292.5) return Direction.W; + if (angle >= 292.5 && angle < 337.5) return Direction.NW; + return Direction.N; +} diff --git a/src/routes/weather/+page.svelte b/src/routes/weather/+page.svelte index 5e20f98a..8c0250ce 100644 --- a/src/routes/weather/+page.svelte +++ b/src/routes/weather/+page.svelte @@ -1,9 +1,9 @@ + +
+ +
+ +
+
Time:
+
{time.toLocaleTimeString()}
+
Temperature:
+
{15} °C
+
Weather:
+
{weather}
+
Wind speed:
+
{20} km/h
+
Wind direction:
+
{windDirection}
+
+ +
+ +
+ + From 4b8bfc4dfb94698997ba8c3bd017e6183f362f81 Mon Sep 17 00:00:00 2001 From: Eddie Pace Date: Wed, 5 Nov 2025 00:27:22 +0000 Subject: [PATCH 03/13] transplant weather utils --- src/lib/canvas/weather/draw/tree.ts | 2 +- src/lib/utils/colour.ts | 16 ++++++++++ src/lib/utils/date.ts | 1 + src/lib/utils/maths.ts | 14 +++++++++ src/lib/utils/time.ts | 48 +++++++++++++++++++++++++++++ src/routes/weather/Scene.svelte | 2 +- 6 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/lib/utils/colour.ts create mode 100644 src/lib/utils/time.ts diff --git a/src/lib/canvas/weather/draw/tree.ts b/src/lib/canvas/weather/draw/tree.ts index 21f12bfb..f65f1413 100644 --- a/src/lib/canvas/weather/draw/tree.ts +++ b/src/lib/canvas/weather/draw/tree.ts @@ -1,4 +1,4 @@ -import { randRangeRGBString } from "$lib/utils"; +import { randRangeRGBString } from "$lib/utils/colour"; // inspired by // https://github.com/PavlyukVadim/amadev/blob/master/RecursiveTree/script.js diff --git a/src/lib/utils/colour.ts b/src/lib/utils/colour.ts new file mode 100644 index 00000000..6953995c --- /dev/null +++ b/src/lib/utils/colour.ts @@ -0,0 +1,16 @@ +import { randBetween } from "./maths"; + +export const randRangeRGBString = ( + redRange: [number, number] | number = [0, 255], + greenRange: [number, number] | number = [0, 255], + blueRange: [number, number] | number = [0, 255], +) => { + const getValue = (range: [number, number] | number) => { + return Array.isArray(range) ? randBetween(range[0], range[1]) : range; + }; + + const red = getValue(redRange); + const green = getValue(greenRange); + const blue = getValue(blueRange); + return `rgb(${red}, ${green}, ${blue})`; +}; diff --git a/src/lib/utils/date.ts b/src/lib/utils/date.ts index 99e340b8..68171b36 100644 --- a/src/lib/utils/date.ts +++ b/src/lib/utils/date.ts @@ -13,3 +13,4 @@ export function dayOfYear(date: Date) { const diff = date.valueOf() - new Date(date.getFullYear(), 0, 0).valueOf(); return Math.floor(diff / (1000 * 60 * 60 * 24)); } + diff --git a/src/lib/utils/maths.ts b/src/lib/utils/maths.ts index 17735332..826f8502 100644 --- a/src/lib/utils/maths.ts +++ b/src/lib/utils/maths.ts @@ -6,3 +6,17 @@ export function randomMinMax(min: number, max: number) { export function randomDirection(): 1 | -1 { return Math.random() >= 0.5 ? 1 : -1; } + +export function randBetween(min: number, max: number) { + return Math.random() * (max - min) + min; +} + +export function randIntBetween(min: number, max: number) { + return Math.floor(randBetween(min - 1, max)); +} + +export function randVariance(value: number, variance: number) { + const min = value * (1 - variance); + const max = value * (1 + variance); + return randBetween(min, max); +} diff --git a/src/lib/utils/time.ts b/src/lib/utils/time.ts new file mode 100644 index 00000000..24abcca2 --- /dev/null +++ b/src/lib/utils/time.ts @@ -0,0 +1,48 @@ +export function timeNoun(time: Date): string { + const hour = time.getHours(); + return hourNoun(hour); +} + +export function hourNoun(hour: number): string { + switch (hour) { + case 6: + return "dawn"; + case 7: + return "sunrise"; + case 8: + return "morning"; + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + return "day"; + case 19: + return "evening"; + case 20: + return "sunset"; + case 21: + return "dusk"; + case 22: + case 23: + case 24: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + return "night"; + default: + return "day"; + } +} + +export function timeAsTwelvethFraction(time: Date): number { + return time.getHours() + (6 % 12); +} diff --git a/src/routes/weather/Scene.svelte b/src/routes/weather/Scene.svelte index 17678373..89aba5df 100644 --- a/src/routes/weather/Scene.svelte +++ b/src/routes/weather/Scene.svelte @@ -1,6 +1,6 @@ + +
+
+ +
+ enforceMinMax(hourInput, timeHours)} + /> + : + enforceMinMax(minuteInput, timeMinutes)} + /> +
+ +
+
+ + diff --git a/src/routes/weather/+page.svelte b/src/routes/weather/+page.svelte index 8c0250ce..267631a3 100644 --- a/src/routes/weather/+page.svelte +++ b/src/routes/weather/+page.svelte @@ -4,6 +4,7 @@ import Compass from "./Compass.svelte"; import Scene from "./Scene.svelte"; import { getDirectionFromAngle, getWeatherFromCode } from "$lib/data/weatherData"; + import Clock from "$lib/components/creative/Clock.svelte"; export let data: PageData; @@ -15,22 +16,24 @@ let frameRate: number; let showDiagnostics: boolean = true; - let setTime: number = time.getHours(); + let setHours: number = time.getHours(); + let setMinutes: number = time.getMinutes(); - $: updateTime(setTime); + $: updateTime(setHours, setMinutes); - function updateTime(hour: number) { + function updateTime(hour: number, minutes: number) { const newTime = time; - newTime?.setHours(hour, 0, 0); + newTime?.setHours(hour, minutes, 0); time = newTime; }
+
- - + +
From 520ddb554a14ee31027ee7168f41020b6e0e79a8 Mon Sep 17 00:00:00 2001 From: Eddie Pace Date: Thu, 6 Nov 2025 00:29:41 +0000 Subject: [PATCH 08/13] add minutes to orbit --- src/lib/canvas/weather/animate/Bluebody.ts | 7 ++++++- src/lib/components/creative/Clock.svelte | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/lib/canvas/weather/animate/Bluebody.ts b/src/lib/canvas/weather/animate/Bluebody.ts index e86c6b37..d4f0478c 100644 --- a/src/lib/canvas/weather/animate/Bluebody.ts +++ b/src/lib/canvas/weather/animate/Bluebody.ts @@ -48,8 +48,13 @@ export class Bluebody extends Animator { updateTime(time: Date) { const hours = time.getHours(); - const moduloTime = hours + (6 % 24); + const minutes = time.getMinutes(); + + const decimal = hours + (minutes / 60); + const moduloTime = decimal + (6 % 24); const orbitAngle = Math.PI - ((((2 * Math.PI) / 24) * moduloTime) % Math.PI) - Math.PI / 2; + + const orbitLenX = Math.sin(orbitAngle) * this.orbitRadius; const orbitLenY = Math.cos(orbitAngle) * this.orbitRadius; this.orbitPosX = this.orbitCenX - orbitLenX; diff --git a/src/lib/components/creative/Clock.svelte b/src/lib/components/creative/Clock.svelte index 4f4495c0..0c0b4c4d 100644 --- a/src/lib/components/creative/Clock.svelte +++ b/src/lib/components/creative/Clock.svelte @@ -35,7 +35,7 @@ timeMinutes = (timeMinutes + 1) % 60; } const subtractMinute = () => { - timeMinutes = (timeMinutes - 1) % -60; + timeMinutes = timeMinutes <= 0 ? 59 : timeMinutes - 1; } @@ -50,7 +50,7 @@ min="00" max="24" bind:value={timeHours} - on:keyup={(e) => enforceMinMax(hourInput, timeHours)} + on:change={(e) => enforceMinMax(hourInput, timeHours)} /> : enforceMinMax(minuteInput, timeMinutes)} + on:change={(e) => enforceMinMax(minuteInput, timeMinutes)} />
From a4d2c104ee9ee2a45a955a53bbd7b4675325449a Mon Sep 17 00:00:00 2001 From: Eddie Pace Date: Thu, 6 Nov 2025 18:40:15 +0000 Subject: [PATCH 09/13] start adding mousedown fns --- src/lib/components/creative/Clock.svelte | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/components/creative/Clock.svelte b/src/lib/components/creative/Clock.svelte index 0c0b4c4d..5b6dded9 100644 --- a/src/lib/components/creative/Clock.svelte +++ b/src/lib/components/creative/Clock.svelte @@ -37,6 +37,18 @@ const subtractMinute = () => { timeMinutes = timeMinutes <= 0 ? 59 : timeMinutes - 1; } + + let intervalId: undefined | NodeJS.Timeout = undefined; + const mouseDown = (fnc: () => {}) => { + if (!intervalId) { + intervalId = setInterval(fnc, 100); + } + } + + const mouseup = () => { + clearInterval(intervalId); + intervalId = undefined; + }
From 81c53b0c624f88dfd222a0339bc8c140a570a915 Mon Sep 17 00:00:00 2001 From: Eddie Pace Date: Sun, 9 Nov 2025 21:42:13 +0000 Subject: [PATCH 10/13] wip clock --- .../creative/{ => clock}/Clock.svelte | 98 +++++++++++-------- 1 file changed, 57 insertions(+), 41 deletions(-) rename src/lib/components/creative/{ => clock}/Clock.svelte (55%) diff --git a/src/lib/components/creative/Clock.svelte b/src/lib/components/creative/clock/Clock.svelte similarity index 55% rename from src/lib/components/creative/Clock.svelte rename to src/lib/components/creative/clock/Clock.svelte index 5b6dded9..fdfd4865 100644 --- a/src/lib/components/creative/Clock.svelte +++ b/src/lib/components/creative/clock/Clock.svelte @@ -1,59 +1,73 @@
- +
enforceMinMax(hourInput, timeHours)} + on:change={() => setAndFormat(hourInput)} /> : enforceMinMax(minuteInput, timeMinutes)} + on:change={() => setAndFormat(minuteInput)} />
- +
@@ -87,7 +103,7 @@ } .clock-body { - background-color: var(--grey); + background-color: var(--primary-25); display: grid; grid-template-rows: auto 1fr auto; gap: 0; @@ -108,18 +124,18 @@ margin: 0; } - input[type='number'] { + input { -moz-appearance: textfield; appearance: textfield; } - input[type='number'] { + input { width: 3rem; padding: 0; font-size: 2rem; font-family: 'Silkscreen'; - color: var(--highlight); - background-color: var(--grey); + color: var(--highlight-inverse); + background-color: transparent; border: none; } @@ -131,7 +147,7 @@ button.spinner { border: none; background-color: transparent; - color: var(--highlight); + color: var(--highlight-inverse); font-size: 2.5rem; padding: 0; height: 2rem; @@ -141,7 +157,7 @@ } button.spinner:hover { - color: var(--primary); + color: var(--white); transition: 0.3s; } From 93272c9b155517001914d41e7a4fe392dc7186c6 Mon Sep 17 00:00:00 2001 From: Eddie Pace Date: Sun, 9 Nov 2025 22:16:51 +0000 Subject: [PATCH 11/13] fix up clock --- .../components/creative/clock/Clock.svelte | 172 +++++++----------- .../clock/PaddedNumberSpinnerInput.svelte | 85 +++++++++ 2 files changed, 146 insertions(+), 111 deletions(-) create mode 100644 src/lib/components/creative/clock/PaddedNumberSpinnerInput.svelte diff --git a/src/lib/components/creative/clock/Clock.svelte b/src/lib/components/creative/clock/Clock.svelte index fdfd4865..2f8da85f 100644 --- a/src/lib/components/creative/clock/Clock.svelte +++ b/src/lib/components/creative/clock/Clock.svelte @@ -1,97 +1,87 @@
- -
- setAndFormat(hourInput)} - /> +
+ : - setAndFormat(minuteInput)} +
-
@@ -104,60 +94,20 @@ .clock-body { background-color: var(--primary-25); - display: grid; - grid-template-rows: auto 1fr auto; - gap: 0; padding: 0.5rem 2rem; border-radius: 2rem; } - .clock-face { + .clock-dials__container { display: flex; justify-content: center; align-items: center; flex-direction: row; } - input[type='number']::-webkit-outer-spin-button, - input[type='number']::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; - } - - input { - -moz-appearance: textfield; - appearance: textfield; - } - - input { - width: 3rem; - padding: 0; - font-size: 2rem; - font-family: 'Silkscreen'; - color: var(--highlight-inverse); - background-color: transparent; - border: none; - } - span { font-weight: bold; color: var(--highlight); - } - - button.spinner { - border: none; - background-color: transparent; - color: var(--highlight-inverse); - font-size: 2.5rem; - padding: 0; - height: 2rem; - display: flex; - justify-content: center; - align-items: center; - } - - button.spinner:hover { - color: var(--white); - transition: 0.3s; + font-size: 2rem; } diff --git a/src/lib/components/creative/clock/PaddedNumberSpinnerInput.svelte b/src/lib/components/creative/clock/PaddedNumberSpinnerInput.svelte new file mode 100644 index 00000000..7ef75294 --- /dev/null +++ b/src/lib/components/creative/clock/PaddedNumberSpinnerInput.svelte @@ -0,0 +1,85 @@ + + +
+ + parseChange(e)}/> + +
+ + From 29306625b244d5ead936e5a67091e5d6e7cbabfd Mon Sep 17 00:00:00 2001 From: Eddie Pace Date: Sun, 9 Nov 2025 22:17:21 +0000 Subject: [PATCH 12/13] add additional dark/light theme color options --- .../creative/weather/WeatherControls.svelte | 7 +- src/lib/components/utils/BigIconButton.svelte | 70 ++++++++++--------- src/lib/theme.ts | 6 ++ src/routes/weather/+page.svelte | 26 ++++--- static/styles/global.css | 7 +- 5 files changed, 67 insertions(+), 49 deletions(-) diff --git a/src/lib/components/creative/weather/WeatherControls.svelte b/src/lib/components/creative/weather/WeatherControls.svelte index 666a7ce0..4f464f9e 100644 --- a/src/lib/components/creative/weather/WeatherControls.svelte +++ b/src/lib/components/creative/weather/WeatherControls.svelte @@ -23,9 +23,8 @@ diff --git a/src/lib/components/utils/BigIconButton.svelte b/src/lib/components/utils/BigIconButton.svelte index 7d171458..579eda00 100644 --- a/src/lib/components/utils/BigIconButton.svelte +++ b/src/lib/components/utils/BigIconButton.svelte @@ -1,48 +1,50 @@ diff --git a/src/lib/theme.ts b/src/lib/theme.ts index ee06b737..03171c49 100644 --- a/src/lib/theme.ts +++ b/src/lib/theme.ts @@ -9,23 +9,29 @@ export function applyDarkTheme(darkTheme: boolean) { if (darkTheme) { root.style.setProperty('--primary', 'var(--white)'); root.style.setProperty('--primary-50', 'var(--white-50)'); + root.style.setProperty('--primary-25', 'var(--white-25)'); root.style.setProperty('--secondary', 'var(--black)'); root.style.setProperty('--secondary-50', 'var(--black-50)'); + root.style.setProperty('--secondary-25', 'var(--black-25)'); root.style.setProperty('--tertiary', 'var(--dark-grey)'); root.style.setProperty('--bg0', 'var(--dark-grey)'); root.style.setProperty('--bg1', 'var(--grey)'); root.style.setProperty('--caption', 'var(--light-grey)'); root.style.setProperty('--highlight', `var(--${get(currentColourTheme)}-bright)`); + root.style.setProperty('--highlight-inverse', 'var(--highlight'); } else { root.style.setProperty('--primary', 'var(--black)'); root.style.setProperty('--primary-50', 'var(--black-50)'); + root.style.setProperty('--primary-25', 'var(--black-25)'); root.style.setProperty('--secondary', 'var(--white)'); root.style.setProperty('--secondary-50', 'var(--white-50)'); + root.style.setProperty('--secondary-25', 'var(--white-25)'); root.style.setProperty('--tertiary', 'var(--grey)'); root.style.setProperty('--bg0', 'var(--light-tan)'); root.style.setProperty('--bg1', 'var(--white)'); root.style.setProperty('--caption', 'var(--dark-grey)'); root.style.setProperty('--highlight', `var(--${get(currentColourTheme)}-dark)`); + root.style.setProperty('--highlight-inverse', `var(--${get(currentColourTheme)}-bright)`); } } } diff --git a/src/routes/weather/+page.svelte b/src/routes/weather/+page.svelte index 267631a3..ee92e8f6 100644 --- a/src/routes/weather/+page.svelte +++ b/src/routes/weather/+page.svelte @@ -4,7 +4,7 @@ import Compass from "./Compass.svelte"; import Scene from "./Scene.svelte"; import { getDirectionFromAngle, getWeatherFromCode } from "$lib/data/weatherData"; - import Clock from "$lib/components/creative/Clock.svelte"; + import Clock from "$lib/components/creative/clock/Clock.svelte"; export let data: PageData; @@ -28,17 +28,15 @@ } -
+
-
- - -
-
- - -
+ {#if import.meta.env.DEV} +
+ + +
+ {/if}
@@ -79,6 +77,14 @@ border-radius: 6px; } + .controls__container { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 2rem; + } + .key { text-align: right; } diff --git a/static/styles/global.css b/static/styles/global.css index f60f264f..2302bc54 100644 --- a/static/styles/global.css +++ b/static/styles/global.css @@ -9,8 +9,10 @@ /* Monochromes */ --white: rgb(250, 235, 215); --white-50: rgba(250, 235, 215, 0.5); + --white-25: rgba(250, 235, 215, 0.25); --black: rgb(0, 0, 0); --black-50: rgb(0, 0, 0, 0.5); + --black-25: rgb(0, 0, 0, 0.25); --light-tan: rgb(220, 215, 200); --light-grey: rgb(150, 150, 150); @@ -20,7 +22,7 @@ /* Themes */ --gold-bright: rgb(255, 177, 8); - --gold-dark: rgb(194, 132, 0); + --gold-dark: rgb(155, 107, 3); --teal-bright: rgb(18, 235, 152); --teal-dark: rgb(4, 168, 105); --sky-bright: rgb(124, 207, 255); @@ -35,8 +37,11 @@ --secondary: var(--black); --tertiary: var(--dark-grey); --highlight: var(--gold-bright); + --highlight-inverse: var(--gold-dark); --primary-50: var(--white-50); + --primary-25: var(--white-25); --secondary-50: var(--black-50); + --secondary-25: var(--black-25); --caption: var(--light-grey); /* Sizes */ From 40084c0ad47407c4144e50c3f8b01150c3aa5c67 Mon Sep 17 00:00:00 2001 From: Eddie Pace Date: Sun, 18 Jan 2026 00:53:24 +0000 Subject: [PATCH 13/13] start fixing clouds --- src/posts/weather-widget.md | 3 ++- src/routes/Header.svelte | 4 ++-- src/routes/weather/WeatherSceneAnimator.ts | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/posts/weather-widget.md b/src/posts/weather-widget.md index 6becb8e6..744c54dd 100644 --- a/src/posts/weather-widget.md +++ b/src/posts/weather-widget.md @@ -1,9 +1,10 @@ --- title: Weather - Widget description: A fun little widget that almost works well. -date: 01/01/2023 +date: 01/17/2026 projectId: programming published: false dev: true technologies: [Typescript, HTML5] --- + diff --git a/src/routes/Header.svelte b/src/routes/Header.svelte index 5fd05488..7ce091d2 100644 --- a/src/routes/Header.svelte +++ b/src/routes/Header.svelte @@ -18,9 +18,9 @@ > Projects - + diff --git a/src/routes/weather/WeatherSceneAnimator.ts b/src/routes/weather/WeatherSceneAnimator.ts index ab928373..d6b007f8 100644 --- a/src/routes/weather/WeatherSceneAnimator.ts +++ b/src/routes/weather/WeatherSceneAnimator.ts @@ -101,7 +101,7 @@ export class WeatherSceneController extends Animator { randIntBetween(0, this.height / 4), randIntBetween(20, 60), randIntBetween(10, 30), - speed, + Math.max(1.1, speed), `rgb(${gray}, ${gray}, ${gray})`, ), );