Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
690d04f96b | ||
|
|
f2371ec5ce | ||
|
|
6ede9b5620 | ||
|
|
6ffef099cd | ||
|
|
f0eb425ec6 | ||
|
|
793918eeff | ||
|
|
ba9834a3eb | ||
|
|
298c8ef597 |
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"userWords": ["ConfigInterface"]
|
||||||
|
}
|
||||||
Generated
+1
@@ -8,6 +8,7 @@
|
|||||||
"name": "open-meteo-weather",
|
"name": "open-meteo-weather",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@openmeteo/sdk": "^1.23.0",
|
||||||
"highcharts": "^12.4.0",
|
"highcharts": "^12.4.0",
|
||||||
"mode-watcher": "^1.1.0",
|
"mode-watcher": "^1.1.0",
|
||||||
"openmeteo": "^1.2.3"
|
"openmeteo": "^1.2.3"
|
||||||
|
|||||||
@@ -52,6 +52,7 @@
|
|||||||
"vitest-browser-svelte": "^2.0.1"
|
"vitest-browser-svelte": "^2.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@openmeteo/sdk": "^1.23.0",
|
||||||
"highcharts": "^12.4.0",
|
"highcharts": "^12.4.0",
|
||||||
"mode-watcher": "^1.1.0",
|
"mode-watcher": "^1.1.0",
|
||||||
"openmeteo": "^1.2.3"
|
"openmeteo": "^1.2.3"
|
||||||
|
|||||||
@@ -242,8 +242,8 @@
|
|||||||
{location.country || ''}
|
{location.country || ''}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||||
{location.latitude.toFixed(2)}°N {location.longitude.toFixed(2)}°E
|
{location.latitude?.toFixed(2)}°N {location.longitude?.toFixed(2)}°E
|
||||||
{#if location.elevation}• {location.elevation.toFixed(0)}m{/if}
|
{#if location.elevation}• {location.elevation?.toFixed(0)}m{/if}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+18
-18
@@ -1,24 +1,24 @@
|
|||||||
import { persisted } from 'svelte-persisted-store';
|
import { persisted } from 'svelte-persisted-store';
|
||||||
|
|
||||||
export interface GeoLocation {
|
export interface GeoLocation {
|
||||||
id: number;
|
id?: number;
|
||||||
name: string;
|
name?: string;
|
||||||
latitude: number;
|
latitude?: number;
|
||||||
longitude: number;
|
longitude?: number;
|
||||||
elevation: number;
|
elevation?: number;
|
||||||
feature_code: string;
|
feature_code?: string;
|
||||||
country_code: string | undefined;
|
country_code?: string;
|
||||||
admin1_id: number | undefined;
|
admin1_id?: number;
|
||||||
admin3_id?: number | undefined;
|
admin3_id?: number;
|
||||||
admin4_id?: number | undefined;
|
admin4_id?: number;
|
||||||
timezone: string;
|
timezone?: string;
|
||||||
population: number | undefined;
|
population?: number;
|
||||||
postcodes: string[] | undefined;
|
postcodes?: string[];
|
||||||
country_id: number | undefined;
|
country_id?: number;
|
||||||
country: string | undefined;
|
country?: string;
|
||||||
admin1: string | undefined;
|
admin1?: string;
|
||||||
admin3?: string | undefined;
|
admin3?: string;
|
||||||
admin4?: string | undefined;
|
admin4?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultLocation: GeoLocation = {
|
export const defaultLocation: GeoLocation = {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
export interface Parameters {
|
||||||
|
latitude?: number | number[];
|
||||||
|
longitude?: number | number[];
|
||||||
|
hourly?: string[];
|
||||||
|
models?: string[];
|
||||||
|
daily?: string[];
|
||||||
|
current?: string[];
|
||||||
|
minutely_15?: string[];
|
||||||
|
timezone?: string;
|
||||||
|
location_mode?: string;
|
||||||
|
csv_coordinates?: string;
|
||||||
|
time_mode?: string;
|
||||||
|
past_days?: string;
|
||||||
|
forecast_days?: string;
|
||||||
|
end_date?: string;
|
||||||
|
start_date?: string;
|
||||||
|
past_hours?: string;
|
||||||
|
cell_selection?: string;
|
||||||
|
forecast_hours?: string;
|
||||||
|
past_minutely_15?: string;
|
||||||
|
temporal_resolution?: string;
|
||||||
|
forecast_minutely_15?: string;
|
||||||
|
tilt?: string;
|
||||||
|
azimuth?: string;
|
||||||
|
timeformat?: string;
|
||||||
|
wind_speed_unit?: string;
|
||||||
|
temperature_unit?: string;
|
||||||
|
precipitation_unit?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
@@ -1,9 +1,26 @@
|
|||||||
export * from './ui.ts';
|
export * from './ui.ts';
|
||||||
export * from './meteo.ts';
|
export * from './meteo.ts';
|
||||||
|
|
||||||
|
export const isNumeric = (num: string | number) =>
|
||||||
|
(typeof num === 'number' || (typeof num === 'string' && num.trim() !== '')) &&
|
||||||
|
!isNaN(num as number);
|
||||||
|
|
||||||
export const pad = (n: string | number) => {
|
export const pad = (n: string | number) => {
|
||||||
if (n === null || n === undefined) {
|
if (n === null || n === undefined) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return ('0' + n).slice(-2);
|
return ('0' + n).slice(-2);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function debounce<F extends (...args: unknown[]) => unknown>(
|
||||||
|
func: F,
|
||||||
|
timeout = 100
|
||||||
|
): (this: ThisParameterType<F>, ...args: Parameters<F>) => void {
|
||||||
|
let timer: ReturnType<typeof setTimeout>;
|
||||||
|
return function (this: ThisParameterType<F>, ...args: Parameters<F>) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
func.apply(this, args);
|
||||||
|
}, timeout);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export const prerender = true;
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { get } from 'svelte/store';
|
|
||||||
import { fade, fly } from 'svelte/transition';
|
import { fade, fly } from 'svelte/transition';
|
||||||
|
|
||||||
import { storedLocation } from '$lib/stores/settings';
|
import { storedLocation } from '$lib/stores/settings';
|
||||||
@@ -15,7 +14,7 @@
|
|||||||
} from '$lib/components/ui/card';
|
} from '$lib/components/ui/card';
|
||||||
|
|
||||||
let mounted = $state(false);
|
let mounted = $state(false);
|
||||||
let location = get(storedLocation);
|
let location = $derived($storedLocation);
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
mounted = true;
|
mounted = true;
|
||||||
@@ -182,7 +181,9 @@
|
|||||||
Current Location: {location.name}
|
Current Location: {location.name}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription class="text-center">
|
<CardDescription class="text-center">
|
||||||
{location.country} • {location.latitude.toFixed(2)}°, {location.longitude.toFixed(2)}°
|
{location.country} • {location.latitude?.toFixed(2)}°, {location.longitude?.toFixed(
|
||||||
|
2
|
||||||
|
)}°
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent class="text-center">
|
<CardContent class="text-center">
|
||||||
|
|||||||
@@ -134,8 +134,8 @@
|
|||||||
{/if}{location.country}
|
{/if}{location.country}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||||
{location.latitude.toFixed(2)}°N, {location.longitude.toFixed(2)}°E
|
{location.latitude?.toFixed(2)}°N, {location.longitude?.toFixed(2)}°E
|
||||||
{#if location.elevation}• {location.elevation.toFixed(0)}m{/if}
|
{#if location.elevation}• {location.elevation?.toFixed(0)}m{/if}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { get } from 'svelte/store';
|
|||||||
|
|
||||||
import { storedLocation } from '$lib/stores/settings';
|
import { storedLocation } from '$lib/stores/settings';
|
||||||
|
|
||||||
import type { LayoutLoad } from './$types';
|
import type { LayoutLoad } from '././$types';
|
||||||
|
|
||||||
const location = get(storedLocation);
|
const location = get(storedLocation);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ import type { PageLoad } from './$types';
|
|||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
export const load = (async () => {
|
export const load: PageLoad = async () => {
|
||||||
throw redirect(303, '/weather/week/');
|
throw redirect(303, '/weather/week/');
|
||||||
}) satisfies PageLoad;
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { get } from 'svelte/store';
|
|||||||
|
|
||||||
import { storedLocation } from '$lib/stores/settings';
|
import { storedLocation } from '$lib/stores/settings';
|
||||||
|
|
||||||
import type { LayoutLoad } from './$types';
|
import type { LayoutLoad } from '././$types';
|
||||||
|
|
||||||
const location = get(storedLocation);
|
const location = get(storedLocation);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
|
import type { ConfigInterface } from '../config';
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
ctx: CanvasRenderingContext2D | null | undefined,
|
ctx: CanvasRenderingContext2D | null | undefined,
|
||||||
config: ConfigInterface,
|
config: ConfigInterface,
|
||||||
series: Float32Array | null | undefined,
|
series: Float32Array | null | undefined
|
||||||
canvasElement: HTMLCanvasElement
|
|
||||||
): void => {
|
): void => {
|
||||||
if (ctx && series) {
|
if (ctx && series) {
|
||||||
|
const fillColor = `hsla(${config.styles.mutedForeground}, 0.5)`;
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(0, 35 + (series[0] ** 1.5 / 1000) * 30);
|
ctx.moveTo(0, 35 + (series[0] ** 1.5 / 1000) * 30);
|
||||||
for (const [index, value] of series.entries()) {
|
for (const [index, value] of series.entries()) {
|
||||||
@@ -71,8 +74,8 @@ export default (
|
|||||||
);
|
);
|
||||||
|
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
//to fill the space in the shape
|
// USE PRE-CALC STYLE
|
||||||
ctx.fillStyle = `hsla(${getComputedStyle(canvasElement).getPropertyValue('--muted-foreground').split(' ').join(',')}, 0.5)`;
|
ctx.fillStyle = fillColor;
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { ConfigInterface } from '../config';
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
ctx: CanvasRenderingContext2D | null | undefined,
|
ctx: CanvasRenderingContext2D | null | undefined,
|
||||||
config: ConfigInterface,
|
config: ConfigInterface,
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
|
import type { ConfigInterface } from '../config';
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
ctx: CanvasRenderingContext2D | null | undefined,
|
ctx: CanvasRenderingContext2D | null | undefined,
|
||||||
config: ConfigInterface,
|
config: ConfigInterface,
|
||||||
series: Float32Array | null | undefined,
|
series: Float32Array | null | undefined
|
||||||
canvasElement: HTMLCanvasElement
|
|
||||||
): void => {
|
): void => {
|
||||||
if (ctx && series) {
|
if (ctx && series) {
|
||||||
|
const strokeStyle = `hsla(${config.styles.primary}, 1)`;
|
||||||
|
|
||||||
for (const [index, value] of series.entries()) {
|
for (const [index, value] of series.entries()) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(index * config.deltaX + 0.5 * config.deltaX, config.maxY);
|
ctx.moveTo(index * config.deltaX + 0.5 * config.deltaX, config.maxY);
|
||||||
|
|
||||||
ctx.strokeStyle = `hsla(${getComputedStyle(canvasElement).getPropertyValue('--primary').split(' ').join(',')}, 1)`;
|
ctx.strokeStyle = strokeStyle;
|
||||||
ctx.lineWidth = 12;
|
ctx.lineWidth = 12;
|
||||||
|
|
||||||
ctx.lineTo(index * config.deltaX + 0.5 * config.deltaX, config.maxY - value ** 0.55 * 45);
|
ctx.lineTo(index * config.deltaX + 0.5 * config.deltaX, config.maxY - value ** 0.55 * 45);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { ConfigInterface } from '../config';
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
ctx: CanvasRenderingContext2D | null | undefined,
|
ctx: CanvasRenderingContext2D | null | undefined,
|
||||||
config: ConfigInterface,
|
config: ConfigInterface,
|
||||||
@@ -18,7 +20,6 @@ export default (
|
|||||||
series[index].getHours() === today.getHours()
|
series[index].getHours() === today.getHours()
|
||||||
) {
|
) {
|
||||||
// fill now line
|
// fill now line
|
||||||
// TODO: update this line every minute
|
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { getColor } from '../utils/colors';
|
import { getColor } from '../utils/colors';
|
||||||
|
|
||||||
|
import type { ConfigInterface } from '../config';
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
ctx: CanvasRenderingContext2D | null | undefined,
|
ctx: CanvasRenderingContext2D | null | undefined,
|
||||||
config: ConfigInterface,
|
config: ConfigInterface,
|
||||||
@@ -8,10 +10,10 @@ export default (
|
|||||||
): void => {
|
): void => {
|
||||||
if (ctx && series) {
|
if (ctx && series) {
|
||||||
const tempGradientFill = ctx.createLinearGradient(0, 0, 0, config.maxY);
|
const tempGradientFill = ctx.createLinearGradient(0, 0, 0, config.maxY);
|
||||||
tempGradientFill.addColorStop(0, getColor(config.maxTemp.toFixed(0), unit) + '5c');
|
tempGradientFill.addColorStop(0, getColor(config.maxTemp, unit) + '5c');
|
||||||
tempGradientFill.addColorStop(0.25, getColor(config.maxTemp.toFixed(0), unit) + '5c');
|
tempGradientFill.addColorStop(0.25, getColor(config.maxTemp, unit) + '5c');
|
||||||
tempGradientFill.addColorStop(0.85, getColor(config.minTemp.toFixed(0), unit) + '06');
|
tempGradientFill.addColorStop(0.85, getColor(config.minTemp, unit) + '06');
|
||||||
tempGradientFill.addColorStop(1, getColor(config.minTemp.toFixed(0), unit) + '00');
|
tempGradientFill.addColorStop(1, getColor(config.minTemp, unit) + '00');
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(
|
ctx.moveTo(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { get } from 'svelte/store';
|
|||||||
|
|
||||||
import { storedLocation } from '$lib/stores/settings';
|
import { storedLocation } from '$lib/stores/settings';
|
||||||
|
|
||||||
import type { LayoutLoad } from './$types';
|
import type { LayoutLoad } from '././$types';
|
||||||
|
|
||||||
const location = get(storedLocation);
|
const location = get(storedLocation);
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,10 @@
|
|||||||
import { Label } from '$lib/components/ui/label';
|
import { Label } from '$lib/components/ui/label';
|
||||||
import { Switch } from '$lib/components/ui/switch';
|
import { Switch } from '$lib/components/ui/switch';
|
||||||
|
|
||||||
import { hourly, models as modelsFlat } from '../options';
|
import { hourly, models } from '../options';
|
||||||
import './highcharts.css';
|
import './highcharts.css';
|
||||||
import { defaultParameters } from './options';
|
import { defaultParameters } from './options';
|
||||||
|
|
||||||
// Wrap models in array to match template expectation of nested arrays like hourly
|
|
||||||
const models = [modelsFlat];
|
|
||||||
|
|
||||||
let node: HTMLElement;
|
let node: HTMLElement;
|
||||||
let chart: any;
|
let chart: any;
|
||||||
let Highcharts = $state<typeof import('highcharts') | null>(null);
|
let Highcharts = $state<typeof import('highcharts') | null>(null);
|
||||||
@@ -338,17 +335,15 @@
|
|||||||
<div
|
<div
|
||||||
class="absolute -top-1 ml-2 rounded-full border-2 border-foreground/25 bg-secondary px-3 py-1 text-sm no-underline"
|
class="absolute -top-1 ml-2 rounded-full border-2 border-foreground/25 bg-secondary px-3 py-1 text-sm no-underline"
|
||||||
>
|
>
|
||||||
{params.models?.length || 0} / {models.flat().length}
|
{params.models?.length || 0} / {models.length}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-2 grid sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4">
|
<div class="mt-2 grid sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4">
|
||||||
{#each models as group, i (i)}
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{#each group as item (item.value)}
|
{#each models as { value, label } (value)}
|
||||||
{@const { value, label } = item as { value: string; label: string }}
|
|
||||||
<div class="group flex items-center" title={label}>
|
<div class="group flex items-center" title={label}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="{value}_model"
|
id="{value}_model"
|
||||||
@@ -375,7 +370,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- HOURLY -->
|
<!-- HOURLY -->
|
||||||
|
|||||||
Vendored
-8
@@ -1,8 +0,0 @@
|
|||||||
interface ConfigInterface {
|
|
||||||
maxX: number;
|
|
||||||
maxY: number;
|
|
||||||
deltaX: number;
|
|
||||||
minTemp: number;
|
|
||||||
maxTemp: number;
|
|
||||||
diffTemp: number;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
export interface ConfigInterface {
|
||||||
|
maxX: number;
|
||||||
|
maxY: number;
|
||||||
|
deltaX: number;
|
||||||
|
minTemp: number;
|
||||||
|
maxTemp: number;
|
||||||
|
diffTemp: number;
|
||||||
|
styles: {
|
||||||
|
mutedForeground: string;
|
||||||
|
primary: string;
|
||||||
|
border: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -25,19 +25,18 @@ export function rgbToHex(rgb: string) {
|
|||||||
return hex;
|
return hex;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getColor = (tempString: string, unit = 'celsius'): string => {
|
export const getColor = (value: number, unit = 'celsius'): string => {
|
||||||
let index = 0;
|
let index = 0;
|
||||||
const temp = Number(tempString);
|
|
||||||
if (unit === 'celsius') {
|
if (unit === 'celsius') {
|
||||||
if (temp <= -40) {
|
if (value <= -40) {
|
||||||
index = 0;
|
index = 0;
|
||||||
} else if (temp >= 60) {
|
} else if (value >= 60) {
|
||||||
index = colorScaleHex.length - 1;
|
index = colorScaleHex.length - 1;
|
||||||
} else {
|
} else {
|
||||||
index = temp + 40;
|
index = value + 40;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const tempInCelsius = Math.round(((temp - 32) * 5) / 9);
|
const tempInCelsius = Math.round(((value - 32) * 5) / 9);
|
||||||
if (tempInCelsius <= -40) {
|
if (tempInCelsius <= -40) {
|
||||||
index = 0;
|
index = 0;
|
||||||
} else if (tempInCelsius >= 60) {
|
} else if (tempInCelsius >= 60) {
|
||||||
@@ -47,5 +46,34 @@ export const getColor = (tempString: string, unit = 'celsius'): string => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
index = Math.floor(index);
|
||||||
|
|
||||||
return colorScaleHex[index];
|
return colorScaleHex[index];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const textWhite = (hex: string): boolean => {
|
||||||
|
const cleaned = (hex || '').replace('#', '').trim().toLowerCase();
|
||||||
|
if (!cleaned) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = 0,
|
||||||
|
g = 0,
|
||||||
|
b = 0;
|
||||||
|
|
||||||
|
if (cleaned.length === 6) {
|
||||||
|
r = parseInt(cleaned.slice(0, 2), 16);
|
||||||
|
g = parseInt(cleaned.slice(2, 4), 16);
|
||||||
|
b = parseInt(cleaned.slice(4, 6), 16);
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid color format');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perceived brightness (YIQ / luma). If brightness is low, use white text.
|
||||||
|
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
||||||
|
return brightness < 128;
|
||||||
|
};
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import type { PageLoad } from './$types';
|
|||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
export const load = (async () => {
|
export const load: PageLoad = async () => {
|
||||||
const location = get(storedLocation);
|
const location = get(storedLocation);
|
||||||
const locationRoute = geoLocationNameToRoute(location.name);
|
const locationRoute = geoLocationNameToRoute(location.name || '');
|
||||||
throw redirect(
|
throw redirect(
|
||||||
303,
|
303,
|
||||||
'/weather/week/' +
|
'/weather/week/' +
|
||||||
@@ -22,4 +22,4 @@ export const load = (async () => {
|
|||||||
: locationRoute + '_' + location.id
|
: locationRoute + '_' + location.id
|
||||||
: locationRoute + '_' + location.id)
|
: locationRoute + '_' + location.id)
|
||||||
);
|
);
|
||||||
}) satisfies PageLoad;
|
};
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
import { getColor } from '../../utils/colors';
|
import { getColor } from '../../utils/colors';
|
||||||
import weatherCodes from '../../utils/weather-codes';
|
import weatherCodes from '../../utils/weather-codes';
|
||||||
|
|
||||||
|
import type { ConfigInterface } from '../../config';
|
||||||
|
|
||||||
let params = $state({
|
let params = $state({
|
||||||
latitude: [$storedLocation.latitude],
|
latitude: [$storedLocation.latitude],
|
||||||
longitude: [$storedLocation.longitude],
|
longitude: [$storedLocation.longitude],
|
||||||
@@ -113,15 +115,20 @@
|
|||||||
deltaX: deltaX,
|
deltaX: deltaX,
|
||||||
minTemp: minTemp,
|
minTemp: minTemp,
|
||||||
maxTemp: maxTemp,
|
maxTemp: maxTemp,
|
||||||
diffTemp: diffTemp
|
diffTemp: diffTemp,
|
||||||
|
styles: {
|
||||||
|
mutedForeground: '240 3.7% 15.9%',
|
||||||
|
primary: '222.2 47.4% 11.2%',
|
||||||
|
border: '214.3 31.8% 91.4%'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// create canvas
|
// create canvas
|
||||||
daylight(ctx, config, hourlyTime);
|
daylight(ctx, config, hourlyTime);
|
||||||
raster(ctx, config, hourlyTime, today, canvasElement!);
|
raster(ctx, config, hourlyTime, today, canvasElement!);
|
||||||
tempGradient(ctx, config, hourlyTemps, params.temperature_unit);
|
tempGradient(ctx, config, hourlyTemps, params.temperature_unit);
|
||||||
cloudCover(ctx, config, hourlyCloudCover, canvasElement!);
|
cloudCover(ctx, config, hourlyCloudCover);
|
||||||
precip(ctx, config, hourlyPrecip, canvasElement!);
|
precip(ctx, config, hourlyPrecip);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -348,14 +355,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="weather-temp-max flex min-w-[65px] justify-center rounded-t p-1 text-sm"
|
class="weather-temp-max flex min-w-[65px] justify-center rounded-t p-1 text-sm"
|
||||||
style={`background-color: ${getColor((wd.daily.temperature_2m_max.values(index) ?? 0).toFixed(0), params.temperature_unit)}; color: ${(wd.daily.temperature_2m_min.values(index) ?? 0) < (params.temperature_unit === 'celsius' ? 4 : 7) || (wd.daily.temperature_2m_min.values(index) ?? 0) >= (params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`}
|
style={`background-color: ${getColor(Math.round(wd.daily.temperature_2m_max.values(index) ?? 0), params.temperature_unit)}; color: ${(wd.daily.temperature_2m_min.values(index) ?? 0) < (params.temperature_unit === 'celsius' ? 4 : 7) || (wd.daily.temperature_2m_min.values(index) ?? 0) >= (params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`}
|
||||||
>
|
>
|
||||||
{wd.daily.temperature_2m_max.values(index)?.toFixed(1)}
|
{wd.daily.temperature_2m_max.values(index)?.toFixed(1)}
|
||||||
{params.temperature_unit === 'celsius' ? '°C' : '°F'}
|
{params.temperature_unit === 'celsius' ? '°C' : '°F'}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="weather-temp-min flex min-w-[65px] justify-center rounded-b p-1 text-sm"
|
class="weather-temp-min flex min-w-[65px] justify-center rounded-b p-1 text-sm"
|
||||||
style={`background: ${getColor((wd.daily.temperature_2m_min.values(index) ?? 0).toFixed(0), params.temperature_unit)}; color: ${(wd.daily.temperature_2m_min.values(index) ?? 0) < (params.temperature_unit === 'celsius' ? 4 : 7) || (wd.daily.temperature_2m_min.values(index) ?? 0) >= (params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`}
|
style={`background: ${getColor(Math.round(wd.daily.temperature_2m_min.values(index) ?? 0), params.temperature_unit)}; color: ${(wd.daily.temperature_2m_min.values(index) ?? 0) < (params.temperature_unit === 'celsius' ? 4 : 7) || (wd.daily.temperature_2m_min.values(index) ?? 0) >= (params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`}
|
||||||
>
|
>
|
||||||
{wd.daily.temperature_2m_min.values(index)?.toFixed(1)}
|
{wd.daily.temperature_2m_min.values(index)?.toFixed(1)}
|
||||||
{params.temperature_unit === 'celsius' ? '°C' : '°F'}
|
{params.temperature_unit === 'celsius' ? '°C' : '°F'}
|
||||||
@@ -536,7 +543,7 @@
|
|||||||
{entry.name === 'temperature_2m'
|
{entry.name === 'temperature_2m'
|
||||||
? 'background: ' +
|
? 'background: ' +
|
||||||
getColor(
|
getColor(
|
||||||
weather.entries[0].values![index].toFixed(0),
|
Math.round(weather.entries[0].values![index]),
|
||||||
params.temperature_unit
|
params.temperature_unit
|
||||||
)
|
)
|
||||||
: ''};
|
: ''};
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export const load: PageLoad = async (event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const locationRoute = geoLocationNameToRoute(location.name);
|
const locationRoute = geoLocationNameToRoute(location.name || '');
|
||||||
|
|
||||||
if (location.population && location.population > 543000) {
|
if (location.population && location.population > 543000) {
|
||||||
// 1000 biggest cities
|
// 1000 biggest cities
|
||||||
|
|||||||
+5
-1
@@ -7,7 +7,11 @@ const config = {
|
|||||||
// for more information about preprocessors
|
// for more information about preprocessors
|
||||||
preprocess: vitePreprocess(),
|
preprocess: vitePreprocess(),
|
||||||
|
|
||||||
kit: { adapter: adapter() }
|
kit: {
|
||||||
|
adapter: adapter({
|
||||||
|
fallback: '404.html'
|
||||||
|
})
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
Reference in New Issue
Block a user