Both codes now render 🌦️ instead of 🌧️ in the CODE_MAP. Updated the weather-codes tests accordingly.
10902 lines
334 KiB
HTML
10902 lines
334 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDQ4IDQ4Ij4KICA8IS0tIFN1biBjaXJjbGUgLS0+CiAgPGNpcmNsZSBjeD0iMjIiIGN5PSIyMiIgcj0iMTIiIGZpbGw9IiNmYmJmMjQiIC8+CiAgPCEtLSBTdW4gcmF5cyAtLT4KICA8ZyBzdHJva2U9IiNmYmJmMjQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj4KICAgIDxsaW5lIHgxPSIyMiIgeTE9IjQiIHgyPSIyMiIgeTI9IjkiIC8+CiAgICA8bGluZSB4MT0iMjIiIHkxPSIzNSIgeDI9IjIyIiB5Mj0iNDAiIC8+CiAgICA8bGluZSB4MT0iNCIgeTE9IjIyIiB4Mj0iOSIgeTI9IjIyIiAvPgogICAgPGxpbmUgeDE9IjM1IiB5MT0iMjIiIHgyPSI0MCIgeTI9IjIyIiAvPgogICAgPGxpbmUgeDE9IjkuNSIgeTE9IjkuNSIgeDI9IjEzIiB5Mj0iMTMiIC8+CiAgICA8bGluZSB4MT0iMzEiIHkxPSIzMSIgeDI9IjM0LjUiIHkyPSIzNC41IiAvPgogICAgPGxpbmUgeDE9IjkuNSIgeTE9IjM0LjUiIHgyPSIxMyIgeTI9IjMxIiAvPgogICAgPGxpbmUgeDE9IjMxIiB5MT0iMTMiIHgyPSIzNC41IiB5Mj0iOS41IiAvPgogIDwvZz4KICA8IS0tIENsb3VkIG92ZXJsYXkgLS0+CiAgPHBhdGggZD0iTTIyIDM0IFExNiAzNCAxNCAzMSBRMTIgMjggMTUgMjcgUTEzIDIzIDE3IDIxIFEyMSAxOSAyNSAyMSBRMjkgMTcgMzQgMTkgUTM4IDIxIDM3IDI2IFE0MSAyNyAzOSAzMSBRMzcgMzUgMjIgMzRaIiBmaWxsPSIjNjBhNWZhIiBvcGFjaXR5PSIwLjkiIC8+Cjwvc3ZnPgo=" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>WeatherLens</title>
|
||
<script type="module" crossorigin>//#region \0rolldown/runtime.js
|
||
var __defProp = Object.defineProperty;
|
||
var __esmMin = (fn, res, err) => () => {
|
||
if (err) throw err[0];
|
||
try {
|
||
return fn && (res = fn(fn = 0)), res;
|
||
} catch (e) {
|
||
throw err = [e], e;
|
||
}
|
||
};
|
||
var __exportAll = (all, no_symbols) => {
|
||
let target = {};
|
||
for (var name in all) __defProp(target, name, {
|
||
get: all[name],
|
||
enumerable: true
|
||
});
|
||
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
||
return target;
|
||
};
|
||
//#endregion
|
||
//#region \0vite/modulepreload-polyfill.js
|
||
(function polyfill() {
|
||
const relList = document.createElement("link").relList;
|
||
if (relList && relList.supports && relList.supports("modulepreload")) return;
|
||
for (const link of document.querySelectorAll("link[rel=\"modulepreload\"]")) processPreload(link);
|
||
new MutationObserver((mutations) => {
|
||
for (const mutation of mutations) {
|
||
if (mutation.type !== "childList") continue;
|
||
for (const node of mutation.addedNodes) if (node.tagName === "LINK" && node.rel === "modulepreload") processPreload(node);
|
||
}
|
||
}).observe(document, {
|
||
childList: true,
|
||
subtree: true
|
||
});
|
||
function getFetchOpts(link) {
|
||
const fetchOpts = {};
|
||
if (link.integrity) fetchOpts.integrity = link.integrity;
|
||
if (link.referrerPolicy) fetchOpts.referrerPolicy = link.referrerPolicy;
|
||
if (link.crossOrigin === "use-credentials") fetchOpts.credentials = "include";
|
||
else if (link.crossOrigin === "anonymous") fetchOpts.credentials = "omit";
|
||
else fetchOpts.credentials = "same-origin";
|
||
return fetchOpts;
|
||
}
|
||
function processPreload(link) {
|
||
if (link.ep) return;
|
||
link.ep = true;
|
||
const fetchOpts = getFetchOpts(link);
|
||
fetch(link.href, fetchOpts);
|
||
}
|
||
})();
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/shared/utils.js
|
||
var is_array = Array.isArray;
|
||
var index_of = Array.prototype.indexOf;
|
||
var includes = Array.prototype.includes;
|
||
var array_from = Array.from;
|
||
var define_property = Object.defineProperty;
|
||
var get_descriptor = Object.getOwnPropertyDescriptor;
|
||
var get_descriptors = Object.getOwnPropertyDescriptors;
|
||
var object_prototype = Object.prototype;
|
||
var array_prototype = Array.prototype;
|
||
var get_prototype_of = Object.getPrototypeOf;
|
||
var is_extensible = Object.isExtensible;
|
||
var noop = () => {};
|
||
/** @param {Array<() => void>} arr */
|
||
function run_all(arr) {
|
||
for (var i = 0; i < arr.length; i++) arr[i]();
|
||
}
|
||
/**
|
||
* TODO replace with Promise.withResolvers once supported widely enough
|
||
* @template [T=void]
|
||
*/
|
||
function deferred() {
|
||
/** @type {(value: T) => void} */
|
||
var resolve;
|
||
/** @type {(reason: any) => void} */
|
||
var reject;
|
||
return {
|
||
promise: new Promise((res, rej) => {
|
||
resolve = res;
|
||
reject = rej;
|
||
}),
|
||
resolve,
|
||
reject
|
||
};
|
||
}
|
||
var CLEAN = 1024;
|
||
var DIRTY = 2048;
|
||
var MAYBE_DIRTY = 4096;
|
||
var INERT = 8192;
|
||
var DESTROYED = 16384;
|
||
/** Set once a reaction has run for the first time */
|
||
var REACTION_RAN = 32768;
|
||
/** Effect is in the process of getting destroyed. Can be observed in child teardown functions */
|
||
var DESTROYING = 1 << 25;
|
||
/**
|
||
* 'Transparent' effects do not create a transition boundary.
|
||
* This is on a block effect 99% of the time but may also be on a branch effect if its parent block effect was pruned
|
||
*/
|
||
var EFFECT_TRANSPARENT = 65536;
|
||
var EFFECT_PRESERVED = 1 << 19;
|
||
var USER_EFFECT = 1 << 20;
|
||
var EFFECT_OFFSCREEN = 1 << 25;
|
||
/**
|
||
* Tells that we marked this derived and its reactions as visited during the "mark as (maybe) dirty"-phase.
|
||
* Will be lifted during execution of the derived and during checking its dirty state (both are necessary
|
||
* because a derived might be checked but not executed). This is a pure performance optimization flag and
|
||
* should not be used for any other purpose!
|
||
*/
|
||
var WAS_MARKED = 65536;
|
||
var REACTION_IS_UPDATING = 1 << 21;
|
||
var ASYNC = 1 << 22;
|
||
var ERROR_VALUE = 1 << 23;
|
||
var STATE_SYMBOL = Symbol("$state");
|
||
var LEGACY_PROPS = Symbol("legacy props");
|
||
var LOADING_ATTR_SYMBOL = Symbol("");
|
||
var ATTRIBUTES_CACHE = Symbol("attributes");
|
||
var CLASS_CACHE = Symbol("class");
|
||
var STYLE_CACHE = Symbol("style");
|
||
var TEXT_CACHE = Symbol("text");
|
||
var FORM_RESET_HANDLER = Symbol("form reset");
|
||
/** allow users to ignore aborted signal errors if `reason.name === 'StaleReactionError` */
|
||
var STALE_REACTION = new class StaleReactionError extends Error {
|
||
name = "StaleReactionError";
|
||
message = "The reaction that called `getAbortSignal()` was re-run or destroyed";
|
||
}();
|
||
var IS_XHTML = !!globalThis.document?.contentType && /* @__PURE__ */ globalThis.document.contentType.includes("xml");
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/errors.js
|
||
/**
|
||
* Cannot create a `$derived(...)` with an `await` expression outside of an effect tree
|
||
* @returns {never}
|
||
*/
|
||
function async_derived_orphan() {
|
||
throw new Error(`https://svelte.dev/e/async_derived_orphan`);
|
||
}
|
||
/**
|
||
* Keyed each block has duplicate key `%value%` at indexes %a% and %b%
|
||
* @param {string} a
|
||
* @param {string} b
|
||
* @param {string | undefined | null} [value]
|
||
* @returns {never}
|
||
*/
|
||
function each_key_duplicate(a, b, value) {
|
||
throw new Error(`https://svelte.dev/e/each_key_duplicate`);
|
||
}
|
||
/**
|
||
* `%rune%` cannot be used inside an effect cleanup function
|
||
* @param {string} rune
|
||
* @returns {never}
|
||
*/
|
||
function effect_in_teardown(rune) {
|
||
throw new Error(`https://svelte.dev/e/effect_in_teardown`);
|
||
}
|
||
/**
|
||
* Effect cannot be created inside a `$derived` value that was not itself created inside an effect
|
||
* @returns {never}
|
||
*/
|
||
function effect_in_unowned_derived() {
|
||
throw new Error(`https://svelte.dev/e/effect_in_unowned_derived`);
|
||
}
|
||
/**
|
||
* `%rune%` can only be used inside an effect (e.g. during component initialisation)
|
||
* @param {string} rune
|
||
* @returns {never}
|
||
*/
|
||
function effect_orphan(rune) {
|
||
throw new Error(`https://svelte.dev/e/effect_orphan`);
|
||
}
|
||
/**
|
||
* Maximum update depth exceeded. This typically indicates that an effect reads and writes the same piece of state
|
||
* @returns {never}
|
||
*/
|
||
function effect_update_depth_exceeded() {
|
||
throw new Error(`https://svelte.dev/e/effect_update_depth_exceeded`);
|
||
}
|
||
/**
|
||
* Cannot do `bind:%key%={undefined}` when `%key%` has a fallback value
|
||
* @param {string} key
|
||
* @returns {never}
|
||
*/
|
||
function props_invalid_value(key) {
|
||
throw new Error(`https://svelte.dev/e/props_invalid_value`);
|
||
}
|
||
/**
|
||
* Property descriptors defined on `$state` objects must contain `value` and always be `enumerable`, `configurable` and `writable`.
|
||
* @returns {never}
|
||
*/
|
||
function state_descriptors_fixed() {
|
||
throw new Error(`https://svelte.dev/e/state_descriptors_fixed`);
|
||
}
|
||
/**
|
||
* Cannot set prototype of `$state` object
|
||
* @returns {never}
|
||
*/
|
||
function state_prototype_fixed() {
|
||
throw new Error(`https://svelte.dev/e/state_prototype_fixed`);
|
||
}
|
||
/**
|
||
* Updating state inside `$derived(...)`, `$inspect(...)` or a template expression is forbidden. If the value should not be reactive, declare it without `$state`
|
||
* @returns {never}
|
||
*/
|
||
function state_unsafe_mutation() {
|
||
throw new Error(`https://svelte.dev/e/state_unsafe_mutation`);
|
||
}
|
||
/**
|
||
* A `<svelte:boundary>` `reset` function cannot be called while an error is still being handled
|
||
* @returns {never}
|
||
*/
|
||
function svelte_boundary_reset_onerror() {
|
||
throw new Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`);
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/constants.js
|
||
var HYDRATION_ERROR = {};
|
||
var UNINITIALIZED = Symbol("uninitialized");
|
||
var NAMESPACE_HTML = "http://www.w3.org/1999/xhtml";
|
||
/**
|
||
* Reading a derived belonging to a now-destroyed effect may result in stale values
|
||
*/
|
||
function derived_inert() {
|
||
console.warn(`https://svelte.dev/e/derived_inert`);
|
||
}
|
||
/**
|
||
* Hydration failed because the initial UI does not match what was rendered on the server. The error occurred near %location%
|
||
* @param {string | undefined | null} [location]
|
||
*/
|
||
function hydration_mismatch(location) {
|
||
console.warn(`https://svelte.dev/e/hydration_mismatch`);
|
||
}
|
||
/**
|
||
* The `value` property of a `<select multiple>` element should be an array, but it received a non-array value. The selection will be kept as is.
|
||
*/
|
||
function select_multiple_invalid_value() {
|
||
console.warn(`https://svelte.dev/e/select_multiple_invalid_value`);
|
||
}
|
||
/**
|
||
* A `<svelte:boundary>` `reset` function only resets the boundary the first time it is called
|
||
*/
|
||
function svelte_boundary_reset_noop() {
|
||
console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`);
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/hydration.js
|
||
/** @import { TemplateNode } from '#client' */
|
||
/**
|
||
* Use this variable to guard everything related to hydration code so it can be treeshaken out
|
||
* if the user doesn't use the `hydrate` method and these code paths are therefore not needed.
|
||
*/
|
||
var hydrating = false;
|
||
/** @param {boolean} value */
|
||
function set_hydrating(value) {
|
||
hydrating = value;
|
||
}
|
||
/**
|
||
* The node that is currently being hydrated. This starts out as the first node inside the opening
|
||
* \x3C!--[--> comment, and updates each time a component calls `$.child(...)` or `$.sibling(...)`.
|
||
* When entering a block (e.g. `{#if ...}`), `hydrate_node` is the block opening comment; by the
|
||
* time we leave the block it is the closing comment, which serves as the block's anchor.
|
||
* @type {TemplateNode}
|
||
*/
|
||
var hydrate_node;
|
||
/** @param {TemplateNode | null} node */
|
||
function set_hydrate_node(node) {
|
||
if (node === null) {
|
||
hydration_mismatch();
|
||
throw HYDRATION_ERROR;
|
||
}
|
||
return hydrate_node = node;
|
||
}
|
||
function hydrate_next() {
|
||
return set_hydrate_node(/* @__PURE__ */ get_next_sibling(hydrate_node));
|
||
}
|
||
/** @param {TemplateNode} node */
|
||
function reset(node) {
|
||
if (!hydrating) return;
|
||
if (/* @__PURE__ */ get_next_sibling(hydrate_node) !== null) {
|
||
hydration_mismatch();
|
||
throw HYDRATION_ERROR;
|
||
}
|
||
hydrate_node = node;
|
||
}
|
||
function next(count = 1) {
|
||
if (hydrating) {
|
||
var i = count;
|
||
var node = hydrate_node;
|
||
while (i--) node = /* @__PURE__ */ get_next_sibling(node);
|
||
hydrate_node = node;
|
||
}
|
||
}
|
||
/**
|
||
* Skips or removes (depending on {@link remove}) all nodes starting at `hydrate_node` up until the next hydration end comment
|
||
* @param {boolean} remove
|
||
*/
|
||
function skip_nodes(remove = true) {
|
||
var depth = 0;
|
||
var node = hydrate_node;
|
||
while (true) {
|
||
if (node.nodeType === 8) {
|
||
var data = node.data;
|
||
if (data === "]") {
|
||
if (depth === 0) return node;
|
||
depth -= 1;
|
||
} else if (data === "[" || data === "[!" || data[0] === "[" && !isNaN(Number(data.slice(1)))) depth += 1;
|
||
}
|
||
var next = /* @__PURE__ */ get_next_sibling(node);
|
||
if (remove) node.remove();
|
||
node = next;
|
||
}
|
||
}
|
||
/**
|
||
*
|
||
* @param {TemplateNode} node
|
||
*/
|
||
function read_hydration_instruction(node) {
|
||
if (!node || node.nodeType !== 8) {
|
||
hydration_mismatch();
|
||
throw HYDRATION_ERROR;
|
||
}
|
||
return node.data;
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/reactivity/equality.js
|
||
/** @import { Equals } from '#client' */
|
||
/** @type {Equals} */
|
||
function equals(value) {
|
||
return value === this.v;
|
||
}
|
||
/**
|
||
* @param {unknown} a
|
||
* @param {unknown} b
|
||
* @returns {boolean}
|
||
*/
|
||
function safe_not_equal(a, b) {
|
||
return a != a ? b == b : a !== b || a !== null && typeof a === "object" || typeof a === "function";
|
||
}
|
||
/** @type {Equals} */
|
||
function safe_equals(value) {
|
||
return !safe_not_equal(value, this.v);
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/flags/index.js
|
||
/** True if experimental.async=true */
|
||
var async_mode_flag = false;
|
||
/** True if we're not certain that we only have Svelte 5 code in the compilation */
|
||
var legacy_mode_flag = false;
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/context.js
|
||
/** @import { ComponentContext, DevStackEntry, Effect } from '#client' */
|
||
/** @type {ComponentContext | null} */
|
||
var component_context = null;
|
||
/** @param {ComponentContext | null} context */
|
||
function set_component_context(context) {
|
||
component_context = context;
|
||
}
|
||
/**
|
||
* @param {Record<string, unknown>} props
|
||
* @param {any} runes
|
||
* @param {Function} [fn]
|
||
* @returns {void}
|
||
*/
|
||
function push(props, runes = false, fn) {
|
||
component_context = {
|
||
p: component_context,
|
||
i: false,
|
||
c: null,
|
||
e: null,
|
||
s: props,
|
||
x: null,
|
||
r: active_effect,
|
||
l: legacy_mode_flag && !runes ? {
|
||
s: null,
|
||
u: null,
|
||
$: []
|
||
} : null
|
||
};
|
||
}
|
||
/**
|
||
* @template {Record<string, any>} T
|
||
* @param {T} [component]
|
||
* @returns {T}
|
||
*/
|
||
function pop(component) {
|
||
var context = component_context;
|
||
var effects = context.e;
|
||
if (effects !== null) {
|
||
context.e = null;
|
||
for (var fn of effects) create_user_effect(fn);
|
||
}
|
||
if (component !== void 0) context.x = component;
|
||
context.i = true;
|
||
component_context = context.p;
|
||
return component ?? {};
|
||
}
|
||
/** @returns {boolean} */
|
||
function is_runes() {
|
||
return !legacy_mode_flag || component_context !== null && component_context.l === null;
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/task.js
|
||
/** @type {Array<() => void>} */
|
||
var micro_tasks = [];
|
||
function run_micro_tasks() {
|
||
var tasks = micro_tasks;
|
||
micro_tasks = [];
|
||
run_all(tasks);
|
||
}
|
||
/**
|
||
* @param {() => void} fn
|
||
*/
|
||
function queue_micro_task(fn) {
|
||
if (micro_tasks.length === 0 && !is_flushing_sync) {
|
||
var tasks = micro_tasks;
|
||
queueMicrotask(() => {
|
||
if (tasks === micro_tasks) run_micro_tasks();
|
||
});
|
||
}
|
||
micro_tasks.push(fn);
|
||
}
|
||
/* @__PURE__ */ new WeakMap();
|
||
/**
|
||
* @param {unknown} error
|
||
*/
|
||
function handle_error(error) {
|
||
var effect = active_effect;
|
||
if (effect === null) {
|
||
/** @type {Derived} */ active_reaction.f |= ERROR_VALUE;
|
||
return error;
|
||
}
|
||
if ((effect.f & 32768) === 0 && (effect.f & 4) === 0) throw error;
|
||
invoke_error_boundary(error, effect);
|
||
}
|
||
/**
|
||
* @param {unknown} error
|
||
* @param {Effect | null} effect
|
||
*/
|
||
function invoke_error_boundary(error, effect) {
|
||
if (effect !== null && (effect.f & 16384) !== 0) return;
|
||
while (effect !== null) {
|
||
if ((effect.f & 128) !== 0) {
|
||
if ((effect.f & 32768) === 0) throw error;
|
||
try {
|
||
/** @type {Boundary} */ effect.b.error(error);
|
||
return;
|
||
} catch (e) {
|
||
error = e;
|
||
}
|
||
}
|
||
effect = effect.parent;
|
||
}
|
||
throw error;
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/reactivity/status.js
|
||
/** @import { Derived, Signal } from '#client' */
|
||
var STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN);
|
||
/**
|
||
* @param {Signal} signal
|
||
* @param {number} status
|
||
*/
|
||
function set_signal_status(signal, status) {
|
||
signal.f = signal.f & STATUS_MASK | status;
|
||
}
|
||
/**
|
||
* Set a derived's status to CLEAN or MAYBE_DIRTY based on its connection state.
|
||
* @param {Derived} derived
|
||
*/
|
||
function update_derived_status(derived) {
|
||
if ((derived.f & 512) !== 0 || derived.deps === null) set_signal_status(derived, CLEAN);
|
||
else set_signal_status(derived, MAYBE_DIRTY);
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/reactivity/utils.js
|
||
/** @import { Derived, Effect, Value } from '#client' */
|
||
/**
|
||
* @param {Value[] | null} deps
|
||
*/
|
||
function clear_marked(deps) {
|
||
if (deps === null) return;
|
||
for (const dep of deps) {
|
||
if ((dep.f & 2) === 0 || (dep.f & 65536) === 0) continue;
|
||
dep.f ^= WAS_MARKED;
|
||
clear_marked(
|
||
/** @type {Derived} */
|
||
dep.deps
|
||
);
|
||
}
|
||
}
|
||
/**
|
||
* @param {Effect} effect
|
||
* @param {Set<Effect>} dirty_effects
|
||
* @param {Set<Effect>} maybe_dirty_effects
|
||
*/
|
||
function defer_effect(effect, dirty_effects, maybe_dirty_effects) {
|
||
if ((effect.f & 2048) !== 0) dirty_effects.add(effect);
|
||
else if ((effect.f & 4096) !== 0) maybe_dirty_effects.add(effect);
|
||
clear_marked(effect.deps);
|
||
set_signal_status(effect, CLEAN);
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/reactivity/store.js
|
||
/**
|
||
* We set this to `true` when updating a store so that we correctly
|
||
* schedule effects if the update takes place inside a `$:` effect
|
||
*/
|
||
var legacy_is_updating_store = false;
|
||
/**
|
||
* Whether or not the prop currently being read is a store binding, as in
|
||
* `<Child bind:x={$y} />`. If it is, we treat the prop as mutable even in
|
||
* runes mode, and skip `binding_property_non_reactive` validation
|
||
*/
|
||
var is_store_binding = false;
|
||
/**
|
||
* Returns a tuple that indicates whether `fn()` reads a prop that is a store binding.
|
||
* Used to prevent `binding_property_non_reactive` validation false positives and
|
||
* ensure that these props are treated as mutable even in runes mode
|
||
* @template T
|
||
* @param {() => T} fn
|
||
* @returns {[T, boolean]}
|
||
*/
|
||
function capture_store_binding(fn) {
|
||
var previous_is_store_binding = is_store_binding;
|
||
try {
|
||
is_store_binding = false;
|
||
return [fn(), is_store_binding];
|
||
} finally {
|
||
is_store_binding = previous_is_store_binding;
|
||
}
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/elements/misc.js
|
||
var listening_to_form_reset = false;
|
||
function add_form_reset_listener() {
|
||
if (!listening_to_form_reset) {
|
||
listening_to_form_reset = true;
|
||
document.addEventListener("reset", (evt) => {
|
||
Promise.resolve().then(() => {
|
||
if (!evt.defaultPrevented) for (const e of evt.target.elements)
|
||
/** @type {any} */ e[FORM_RESET_HANDLER]?.();
|
||
});
|
||
}, { capture: true });
|
||
}
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/elements/bindings/shared.js
|
||
/**
|
||
* @template T
|
||
* @param {() => T} fn
|
||
*/
|
||
function without_reactive_context(fn) {
|
||
var previous_reaction = active_reaction;
|
||
var previous_effect = active_effect;
|
||
set_active_reaction(null);
|
||
set_active_effect(null);
|
||
try {
|
||
return fn();
|
||
} finally {
|
||
set_active_reaction(previous_reaction);
|
||
set_active_effect(previous_effect);
|
||
}
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/reactivity/create-subscriber.js
|
||
/**
|
||
* Returns a `subscribe` function that integrates external event-based systems with Svelte's reactivity.
|
||
* It's particularly useful for integrating with web APIs like `MediaQuery`, `IntersectionObserver`, or `WebSocket`.
|
||
*
|
||
* If `subscribe` is called inside an effect (including indirectly, for example inside a getter),
|
||
* the `start` callback will be called with an `update` function. Whenever `update` is called, the effect re-runs.
|
||
*
|
||
* If `start` returns a cleanup function, it will be called when the effect is destroyed.
|
||
*
|
||
* If `subscribe` is called in multiple effects, `start` will only be called once as long as the effects
|
||
* are active, and the returned teardown function will only be called when all effects are destroyed.
|
||
*
|
||
* It's best understood with an example. Here's an implementation of [`MediaQuery`](https://svelte.dev/docs/svelte/svelte-reactivity#MediaQuery):
|
||
*
|
||
* ```js
|
||
* import { createSubscriber } from 'svelte/reactivity';
|
||
* import { on } from 'svelte/events';
|
||
*
|
||
* export class MediaQuery {
|
||
* #query;
|
||
* #subscribe;
|
||
*
|
||
* constructor(query) {
|
||
* this.#query = window.matchMedia(`(${query})`);
|
||
*
|
||
* this.#subscribe = createSubscriber((update) => {
|
||
* // when the `change` event occurs, re-run any effects that read `this.current`
|
||
* const off = on(this.#query, 'change', update);
|
||
*
|
||
* // stop listening when all the effects are destroyed
|
||
* return () => off();
|
||
* });
|
||
* }
|
||
*
|
||
* get current() {
|
||
* // This makes the getter reactive, if read in an effect
|
||
* this.#subscribe();
|
||
*
|
||
* // Return the current state of the query, whether or not we're in an effect
|
||
* return this.#query.matches;
|
||
* }
|
||
* }
|
||
* ```
|
||
* @param {(update: () => void) => (() => void) | void} start
|
||
* @since 5.7.0
|
||
*/
|
||
function createSubscriber(start) {
|
||
let subscribers = 0;
|
||
let version = source(0);
|
||
/** @type {(() => void) | void} */
|
||
let stop;
|
||
return () => {
|
||
if (effect_tracking()) {
|
||
get(version);
|
||
render_effect(() => {
|
||
if (subscribers === 0) stop = untrack(() => start(() => increment(version)));
|
||
subscribers += 1;
|
||
return () => {
|
||
queue_micro_task(() => {
|
||
subscribers -= 1;
|
||
if (subscribers === 0) {
|
||
stop?.();
|
||
stop = void 0;
|
||
increment(version);
|
||
}
|
||
});
|
||
};
|
||
});
|
||
}
|
||
};
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/blocks/boundary.js
|
||
/** @import { Effect, Source, TemplateNode, } from '#client' */
|
||
/**
|
||
* @typedef {{
|
||
* onerror?: ((error: unknown, reset: () => void) => void) | null;
|
||
* failed?: ((anchor: Node, error: () => unknown, reset: () => () => void) => void) | null;
|
||
* pending?: ((anchor: Node) => void) | null;
|
||
* }} BoundaryProps
|
||
*/
|
||
var flags = EFFECT_TRANSPARENT | EFFECT_PRESERVED;
|
||
/**
|
||
* @param {TemplateNode} node
|
||
* @param {BoundaryProps} props
|
||
* @param {((anchor: Node) => void)} children
|
||
* @param {((error: unknown) => unknown) | undefined} [transform_error]
|
||
* @returns {void}
|
||
*/
|
||
function boundary(node, props, children, transform_error) {
|
||
new Boundary(node, props, children, transform_error);
|
||
}
|
||
var Boundary = class {
|
||
/** @type {Boundary | null} */
|
||
parent;
|
||
is_pending = false;
|
||
/**
|
||
* API-level transformError transform function. Transforms errors before they reach the `failed` snippet.
|
||
* Inherited from parent boundary, or defaults to identity.
|
||
* @type {(error: unknown) => unknown}
|
||
*/
|
||
transform_error;
|
||
/** @type {TemplateNode} */
|
||
#anchor;
|
||
/** @type {TemplateNode | null} */
|
||
#hydrate_open = hydrating ? hydrate_node : null;
|
||
/** @type {BoundaryProps} */
|
||
#props;
|
||
/** @type {((anchor: Node) => void)} */
|
||
#children;
|
||
/** @type {Effect} */
|
||
#effect;
|
||
/** @type {Effect | null} */
|
||
#main_effect = null;
|
||
/** @type {Effect | null} */
|
||
#pending_effect = null;
|
||
/** @type {Effect | null} */
|
||
#failed_effect = null;
|
||
/** @type {DocumentFragment | null} */
|
||
#offscreen_fragment = null;
|
||
#local_pending_count = 0;
|
||
#pending_count = 0;
|
||
#pending_count_update_queued = false;
|
||
/** @type {Set<Effect>} */
|
||
#dirty_effects = /* @__PURE__ */ new Set();
|
||
/** @type {Set<Effect>} */
|
||
#maybe_dirty_effects = /* @__PURE__ */ new Set();
|
||
/**
|
||
* A source containing the number of pending async deriveds/expressions.
|
||
* Only created if `$effect.pending()` is used inside the boundary,
|
||
* otherwise updating the source results in needless `Batch.ensure()`
|
||
* calls followed by no-op flushes
|
||
* @type {Source<number> | null}
|
||
*/
|
||
#effect_pending = null;
|
||
#effect_pending_subscriber = createSubscriber(() => {
|
||
this.#effect_pending = source(this.#local_pending_count);
|
||
return () => {
|
||
this.#effect_pending = null;
|
||
};
|
||
});
|
||
/**
|
||
* @param {TemplateNode} node
|
||
* @param {BoundaryProps} props
|
||
* @param {((anchor: Node) => void)} children
|
||
* @param {((error: unknown) => unknown) | undefined} [transform_error]
|
||
*/
|
||
constructor(node, props, children, transform_error) {
|
||
this.#anchor = node;
|
||
this.#props = props;
|
||
this.#children = (anchor) => {
|
||
var effect = active_effect;
|
||
effect.b = this;
|
||
effect.f |= 128;
|
||
children(anchor);
|
||
};
|
||
this.parent = active_effect.b;
|
||
this.transform_error = transform_error ?? this.parent?.transform_error ?? ((e) => e);
|
||
this.#effect = block(() => {
|
||
if (hydrating) {
|
||
const comment = this.#hydrate_open;
|
||
hydrate_next();
|
||
const server_rendered_pending = comment.data === "[!";
|
||
if (comment.data.startsWith("[?")) {
|
||
const serialized_error = JSON.parse(comment.data.slice(2));
|
||
this.#hydrate_failed_content(serialized_error);
|
||
} else if (server_rendered_pending) this.#hydrate_pending_content();
|
||
else this.#hydrate_resolved_content();
|
||
} else this.#render();
|
||
}, flags);
|
||
if (hydrating) this.#anchor = hydrate_node;
|
||
}
|
||
#hydrate_resolved_content() {
|
||
try {
|
||
this.#main_effect = branch(() => this.#children(this.#anchor));
|
||
} catch (error) {
|
||
this.error(error);
|
||
}
|
||
}
|
||
/**
|
||
* @param {unknown} error The deserialized error from the server's hydration comment
|
||
*/
|
||
#hydrate_failed_content(error) {
|
||
const failed = this.#props.failed;
|
||
if (!failed) return;
|
||
this.#failed_effect = branch(() => {
|
||
failed(this.#anchor, () => error, () => () => {});
|
||
});
|
||
}
|
||
#hydrate_pending_content() {
|
||
const pending = this.#props.pending;
|
||
if (!pending) return;
|
||
this.is_pending = true;
|
||
this.#pending_effect = branch(() => pending(this.#anchor));
|
||
queue_micro_task(() => {
|
||
var fragment = this.#offscreen_fragment = document.createDocumentFragment();
|
||
var anchor = create_text();
|
||
fragment.append(anchor);
|
||
this.#main_effect = this.#run(() => {
|
||
return branch(() => this.#children(anchor));
|
||
});
|
||
if (this.#pending_count === 0) {
|
||
this.#anchor.before(fragment);
|
||
this.#offscreen_fragment = null;
|
||
pause_effect(this.#pending_effect, () => {
|
||
this.#pending_effect = null;
|
||
});
|
||
this.#resolve(current_batch);
|
||
}
|
||
});
|
||
}
|
||
#render() {
|
||
try {
|
||
this.is_pending = this.has_pending_snippet();
|
||
this.#pending_count = 0;
|
||
this.#local_pending_count = 0;
|
||
this.#main_effect = branch(() => {
|
||
this.#children(this.#anchor);
|
||
});
|
||
if (this.#pending_count > 0) {
|
||
var fragment = this.#offscreen_fragment = document.createDocumentFragment();
|
||
move_effect(this.#main_effect, fragment);
|
||
const pending = this.#props.pending;
|
||
this.#pending_effect = branch(() => pending(this.#anchor));
|
||
} else this.#resolve(current_batch);
|
||
} catch (error) {
|
||
this.error(error);
|
||
}
|
||
}
|
||
/**
|
||
* @param {Batch} batch
|
||
*/
|
||
#resolve(batch) {
|
||
this.is_pending = false;
|
||
batch.transfer_effects(this.#dirty_effects, this.#maybe_dirty_effects);
|
||
}
|
||
/**
|
||
* Defer an effect inside a pending boundary until the boundary resolves
|
||
* @param {Effect} effect
|
||
*/
|
||
defer_effect(effect) {
|
||
defer_effect(effect, this.#dirty_effects, this.#maybe_dirty_effects);
|
||
}
|
||
/**
|
||
* Returns `false` if the effect exists inside a boundary whose pending snippet is shown
|
||
* @returns {boolean}
|
||
*/
|
||
is_rendered() {
|
||
return !this.is_pending && (!this.parent || this.parent.is_rendered());
|
||
}
|
||
has_pending_snippet() {
|
||
return !!this.#props.pending;
|
||
}
|
||
/**
|
||
* @template T
|
||
* @param {() => T} fn
|
||
*/
|
||
#run(fn) {
|
||
var previous_effect = active_effect;
|
||
var previous_reaction = active_reaction;
|
||
var previous_ctx = component_context;
|
||
set_active_effect(this.#effect);
|
||
set_active_reaction(this.#effect);
|
||
set_component_context(this.#effect.ctx);
|
||
try {
|
||
Batch.ensure();
|
||
return fn();
|
||
} catch (e) {
|
||
handle_error(e);
|
||
return null;
|
||
} finally {
|
||
set_active_effect(previous_effect);
|
||
set_active_reaction(previous_reaction);
|
||
set_component_context(previous_ctx);
|
||
}
|
||
}
|
||
/**
|
||
* Updates the pending count associated with the currently visible pending snippet,
|
||
* if any, such that we can replace the snippet with content once work is done
|
||
* @param {1 | -1} d
|
||
* @param {Batch} batch
|
||
*/
|
||
#update_pending_count(d, batch) {
|
||
if (!this.has_pending_snippet()) {
|
||
if (this.parent) this.parent.#update_pending_count(d, batch);
|
||
return;
|
||
}
|
||
this.#pending_count += d;
|
||
if (this.#pending_count === 0) {
|
||
this.#resolve(batch);
|
||
if (this.#pending_effect) pause_effect(this.#pending_effect, () => {
|
||
this.#pending_effect = null;
|
||
});
|
||
if (this.#offscreen_fragment) {
|
||
this.#anchor.before(this.#offscreen_fragment);
|
||
this.#offscreen_fragment = null;
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* Update the source that powers `$effect.pending()` inside this boundary,
|
||
* and controls when the current `pending` snippet (if any) is removed.
|
||
* Do not call from inside the class
|
||
* @param {1 | -1} d
|
||
* @param {Batch} batch
|
||
*/
|
||
update_pending_count(d, batch) {
|
||
this.#update_pending_count(d, batch);
|
||
this.#local_pending_count += d;
|
||
if (!this.#effect_pending || this.#pending_count_update_queued) return;
|
||
this.#pending_count_update_queued = true;
|
||
queue_micro_task(() => {
|
||
this.#pending_count_update_queued = false;
|
||
if (this.#effect_pending) internal_set(this.#effect_pending, this.#local_pending_count);
|
||
});
|
||
}
|
||
get_effect_pending() {
|
||
this.#effect_pending_subscriber();
|
||
return get(this.#effect_pending);
|
||
}
|
||
/** @param {unknown} error */
|
||
error(error) {
|
||
if (!this.#props.onerror && !this.#props.failed) throw error;
|
||
if (current_batch?.is_fork) {
|
||
if (this.#main_effect) current_batch.skip_effect(this.#main_effect);
|
||
if (this.#pending_effect) current_batch.skip_effect(this.#pending_effect);
|
||
if (this.#failed_effect) current_batch.skip_effect(this.#failed_effect);
|
||
current_batch.oncommit(() => {
|
||
this.#handle_error(error);
|
||
});
|
||
} else this.#handle_error(error);
|
||
}
|
||
/**
|
||
* @param {unknown} error
|
||
*/
|
||
#handle_error(error) {
|
||
if (this.#main_effect) {
|
||
destroy_effect(this.#main_effect);
|
||
this.#main_effect = null;
|
||
}
|
||
if (this.#pending_effect) {
|
||
destroy_effect(this.#pending_effect);
|
||
this.#pending_effect = null;
|
||
}
|
||
if (this.#failed_effect) {
|
||
destroy_effect(this.#failed_effect);
|
||
this.#failed_effect = null;
|
||
}
|
||
if (hydrating) {
|
||
set_hydrate_node(this.#hydrate_open);
|
||
next();
|
||
set_hydrate_node(skip_nodes());
|
||
}
|
||
var onerror = this.#props.onerror;
|
||
let failed = this.#props.failed;
|
||
var did_reset = false;
|
||
var calling_on_error = false;
|
||
const reset = () => {
|
||
if (did_reset) {
|
||
svelte_boundary_reset_noop();
|
||
return;
|
||
}
|
||
did_reset = true;
|
||
if (calling_on_error) svelte_boundary_reset_onerror();
|
||
if (this.#failed_effect !== null) pause_effect(this.#failed_effect, () => {
|
||
this.#failed_effect = null;
|
||
});
|
||
this.#run(() => {
|
||
this.#render();
|
||
});
|
||
};
|
||
/** @param {unknown} transformed_error */
|
||
const handle_error_result = (transformed_error) => {
|
||
try {
|
||
calling_on_error = true;
|
||
onerror?.(transformed_error, reset);
|
||
calling_on_error = false;
|
||
} catch (error) {
|
||
invoke_error_boundary(error, this.#effect && this.#effect.parent);
|
||
}
|
||
if (failed) this.#failed_effect = this.#run(() => {
|
||
try {
|
||
return branch(() => {
|
||
var effect = active_effect;
|
||
effect.b = this;
|
||
effect.f |= 128;
|
||
failed(this.#anchor, () => transformed_error, () => reset);
|
||
});
|
||
} catch (error) {
|
||
invoke_error_boundary(error, this.#effect.parent);
|
||
return null;
|
||
}
|
||
});
|
||
};
|
||
queue_micro_task(() => {
|
||
/** @type {unknown} */
|
||
var result;
|
||
try {
|
||
result = this.transform_error(error);
|
||
} catch (e) {
|
||
invoke_error_boundary(e, this.#effect && this.#effect.parent);
|
||
return;
|
||
}
|
||
if (result !== null && typeof result === "object" && typeof result.then === "function")
|
||
/** @type {any} */ result.then(
|
||
handle_error_result,
|
||
/** @param {unknown} e */
|
||
(e) => invoke_error_boundary(e, this.#effect && this.#effect.parent)
|
||
);
|
||
else handle_error_result(result);
|
||
});
|
||
}
|
||
};
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/reactivity/async.js
|
||
/** @import { Blocker, Effect, Source, Value } from '#client' */
|
||
/**
|
||
* @param {Blocker[]} blockers
|
||
* @param {Array<() => any>} sync
|
||
* @param {Array<() => Promise<any>>} async
|
||
* @param {(values: Value[]) => any} fn
|
||
*/
|
||
function flatten(blockers, sync, async, fn) {
|
||
const d = is_runes() ? derived : derived_safe_equal;
|
||
var pending = blockers.filter((b) => !b.settled);
|
||
var deriveds = sync.map(d);
|
||
if (async.length === 0 && pending.length === 0) {
|
||
fn(deriveds);
|
||
return;
|
||
}
|
||
var parent = active_effect;
|
||
var restore = capture();
|
||
var blocker_promise = pending.length === 1 ? pending[0].promise : pending.length > 1 ? Promise.all(pending.map((b) => b.promise)) : null;
|
||
/**
|
||
* @param {Source[]} async
|
||
*/
|
||
function finish(async) {
|
||
if ((parent.f & 16384) !== 0) return;
|
||
restore();
|
||
try {
|
||
fn([...deriveds, ...async]);
|
||
} catch (error) {
|
||
invoke_error_boundary(error, parent);
|
||
}
|
||
unset_context();
|
||
}
|
||
var decrement_pending = increment_pending();
|
||
if (async.length === 0) {
|
||
/** @type {Promise<any>} */ blocker_promise.then(() => finish([])).finally(decrement_pending);
|
||
return;
|
||
}
|
||
function run() {
|
||
Promise.all(async.map((expression) => /* @__PURE__ */ async_derived(expression))).then(finish).catch((error) => invoke_error_boundary(error, parent)).finally(decrement_pending);
|
||
}
|
||
if (blocker_promise) blocker_promise.then(() => {
|
||
restore();
|
||
run();
|
||
unset_context();
|
||
});
|
||
else run();
|
||
}
|
||
/**
|
||
* Captures the current effect context so that we can restore it after
|
||
* some asynchronous work has happened (so that e.g. `await a + b`
|
||
* causes `b` to be registered as a dependency).
|
||
*/
|
||
function capture() {
|
||
var previous_effect = active_effect;
|
||
var previous_reaction = active_reaction;
|
||
var previous_component_context = component_context;
|
||
var previous_batch = current_batch;
|
||
return function restore(activate_batch = true) {
|
||
set_active_effect(previous_effect);
|
||
set_active_reaction(previous_reaction);
|
||
set_component_context(previous_component_context);
|
||
if (activate_batch && (previous_effect.f & 16384) === 0) {
|
||
previous_batch?.activate();
|
||
previous_batch?.apply();
|
||
}
|
||
};
|
||
}
|
||
function unset_context(deactivate_batch = true) {
|
||
set_active_effect(null);
|
||
set_active_reaction(null);
|
||
set_component_context(null);
|
||
if (deactivate_batch) current_batch?.deactivate();
|
||
}
|
||
/**
|
||
* @returns {(skip?: boolean) => void}
|
||
*/
|
||
function increment_pending() {
|
||
var effect = active_effect;
|
||
var boundary = effect.b;
|
||
var batch = current_batch;
|
||
var blocking = !!boundary?.is_rendered();
|
||
boundary?.update_pending_count(1, batch);
|
||
batch.increment(blocking, effect);
|
||
return () => {
|
||
boundary?.update_pending_count(-1, batch);
|
||
batch.decrement(blocking, effect);
|
||
};
|
||
}
|
||
/* @__PURE__ */ new Set();
|
||
/**
|
||
* @template V
|
||
* @param {() => V} fn
|
||
* @returns {Derived<V>}
|
||
*/
|
||
/*#__NO_SIDE_EFFECTS__*/
|
||
function derived(fn) {
|
||
var flags = 2 | DIRTY;
|
||
if (active_effect !== null) active_effect.f |= EFFECT_PRESERVED;
|
||
return {
|
||
ctx: component_context,
|
||
deps: null,
|
||
effects: null,
|
||
equals,
|
||
f: flags,
|
||
fn,
|
||
reactions: null,
|
||
rv: 0,
|
||
v: UNINITIALIZED,
|
||
wv: 0,
|
||
parent: active_effect,
|
||
ac: null
|
||
};
|
||
}
|
||
var OBSOLETE = Symbol("obsolete");
|
||
/**
|
||
* @template V
|
||
* @param {() => V | Promise<V>} fn
|
||
* @param {string} [label]
|
||
* @param {string} [location] If provided, print a warning if the value is not read immediately after update
|
||
* @returns {Promise<Source<V>>}
|
||
*/
|
||
/*#__NO_SIDE_EFFECTS__*/
|
||
function async_derived(fn, label, location) {
|
||
let parent = active_effect;
|
||
if (parent === null) async_derived_orphan();
|
||
var promise = void 0;
|
||
var signal = source(UNINITIALIZED);
|
||
var should_suspend = !active_reaction;
|
||
/** @type {Set<ReturnType<typeof deferred<V>>>} */
|
||
var deferreds = /* @__PURE__ */ new Set();
|
||
async_effect(() => {
|
||
var effect = active_effect;
|
||
/** @type {ReturnType<typeof deferred<V>>} */
|
||
var d = deferred();
|
||
promise = d.promise;
|
||
try {
|
||
Promise.resolve(fn()).then(d.resolve, (e) => {
|
||
if (e !== STALE_REACTION) d.reject(e);
|
||
}).finally(unset_context);
|
||
} catch (error) {
|
||
d.reject(error);
|
||
unset_context();
|
||
}
|
||
var batch = current_batch;
|
||
if (should_suspend) {
|
||
if ((effect.f & 32768) !== 0) var decrement_pending = increment_pending();
|
||
if (parent.b?.is_rendered()) batch.async_deriveds.get(effect)?.reject(OBSOLETE);
|
||
else for (const d of deferreds.values()) d.reject(OBSOLETE);
|
||
deferreds.add(d);
|
||
batch.async_deriveds.set(effect, d);
|
||
}
|
||
/**
|
||
* @param {any} value
|
||
* @param {unknown} error
|
||
*/
|
||
const handler = (value, error = void 0) => {
|
||
decrement_pending?.();
|
||
deferreds.delete(d);
|
||
if (error === OBSOLETE) return;
|
||
batch.activate();
|
||
if (error) {
|
||
signal.f |= ERROR_VALUE;
|
||
internal_set(signal, error);
|
||
} else {
|
||
if ((signal.f & 8388608) !== 0) signal.f ^= ERROR_VALUE;
|
||
internal_set(signal, value);
|
||
}
|
||
batch.deactivate();
|
||
};
|
||
d.promise.then(handler, (e) => handler(null, e || "unknown"));
|
||
});
|
||
teardown(() => {
|
||
for (const d of deferreds) d.reject(OBSOLETE);
|
||
});
|
||
return new Promise((fulfil) => {
|
||
/** @param {Promise<V>} p */
|
||
function next(p) {
|
||
function go() {
|
||
if (p === promise) fulfil(signal);
|
||
else next(promise);
|
||
}
|
||
p.then(go, go);
|
||
}
|
||
next(promise);
|
||
});
|
||
}
|
||
/**
|
||
* @template V
|
||
* @param {() => V} fn
|
||
* @returns {Derived<V>}
|
||
*/
|
||
/*#__NO_SIDE_EFFECTS__*/
|
||
function user_derived(fn) {
|
||
const d = /* @__PURE__ */ derived(fn);
|
||
if (!async_mode_flag) push_reaction_value(d);
|
||
return d;
|
||
}
|
||
/**
|
||
* @template V
|
||
* @param {() => V} fn
|
||
* @returns {Derived<V>}
|
||
*/
|
||
/*#__NO_SIDE_EFFECTS__*/
|
||
function derived_safe_equal(fn) {
|
||
const signal = /* @__PURE__ */ derived(fn);
|
||
signal.equals = safe_equals;
|
||
return signal;
|
||
}
|
||
/**
|
||
* @param {Derived} derived
|
||
* @returns {void}
|
||
*/
|
||
function destroy_derived_effects(derived) {
|
||
var effects = derived.effects;
|
||
if (effects !== null) {
|
||
derived.effects = null;
|
||
for (var i = 0; i < effects.length; i += 1) destroy_effect(effects[i]);
|
||
}
|
||
}
|
||
/**
|
||
* @template T
|
||
* @param {Derived} derived
|
||
* @returns {T}
|
||
*/
|
||
function execute_derived(derived) {
|
||
var value;
|
||
var prev_active_effect = active_effect;
|
||
var parent = derived.parent;
|
||
if (!is_destroying_effect && parent !== null && derived.v !== UNINITIALIZED && (parent.f & 24576) !== 0) {
|
||
derived_inert();
|
||
return derived.v;
|
||
}
|
||
set_active_effect(parent);
|
||
try {
|
||
derived.f &= ~WAS_MARKED;
|
||
destroy_derived_effects(derived);
|
||
value = update_reaction(derived);
|
||
} finally {
|
||
set_active_effect(prev_active_effect);
|
||
}
|
||
return value;
|
||
}
|
||
/**
|
||
* @param {Derived} derived
|
||
* @returns {void}
|
||
*/
|
||
function update_derived(derived) {
|
||
var value = execute_derived(derived);
|
||
if (!derived.equals(value)) {
|
||
derived.wv = increment_write_version();
|
||
if (!current_batch?.is_fork || derived.deps === null) {
|
||
if (current_batch !== null) {
|
||
current_batch.capture(derived, value, true);
|
||
previous_batch?.capture(derived, value, true);
|
||
} else derived.v = value;
|
||
if (derived.deps === null) {
|
||
set_signal_status(derived, CLEAN);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
if (is_destroying_effect) return;
|
||
if (batch_values !== null) {
|
||
if (effect_tracking() || current_batch?.is_fork) batch_values.set(derived, value);
|
||
} else update_derived_status(derived);
|
||
}
|
||
/**
|
||
* @param {Derived} derived
|
||
*/
|
||
function freeze_derived_effects(derived) {
|
||
if (derived.effects === null) return;
|
||
for (const e of derived.effects) if (e.teardown || e.ac) {
|
||
e.teardown?.();
|
||
if (e.ac !== null) without_reactive_context(() => {
|
||
/** @type {AbortController} */ e.ac.abort(STALE_REACTION);
|
||
e.ac = null;
|
||
});
|
||
if (e.fn !== null) e.teardown = noop;
|
||
remove_reactions(e, 0);
|
||
destroy_effect_children(e);
|
||
}
|
||
}
|
||
/**
|
||
* @param {Derived} derived
|
||
*/
|
||
function unfreeze_derived_effects(derived) {
|
||
if (derived.effects === null) return;
|
||
for (const e of derived.effects) if (e.teardown && e.fn !== null) update_effect(e);
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/reactivity/batch.js
|
||
/** @import { Fork } from 'svelte' */
|
||
/** @import { Derived, Effect, Reaction, Source, Value } from '#client' */
|
||
/** @type {Batch | null} */
|
||
var first_batch = null;
|
||
/** @type {Batch | null} */
|
||
var last_batch = null;
|
||
/** @type {Batch | null} */
|
||
var current_batch = null;
|
||
/**
|
||
* This is needed to avoid overwriting inputs
|
||
* @type {Batch | null}
|
||
*/
|
||
var previous_batch = null;
|
||
/**
|
||
* When time travelling (i.e. working in one batch, while other batches
|
||
* still have ongoing work), we ignore the real values of affected
|
||
* signals in favour of their values within the batch
|
||
* @type {Map<Value, any> | null}
|
||
*/
|
||
var batch_values = null;
|
||
/** @type {Effect | null} */
|
||
var last_scheduled_effect = null;
|
||
var is_flushing_sync = false;
|
||
var is_processing = false;
|
||
/**
|
||
* During traversal, this is an array. Newly created effects are (if not immediately
|
||
* executed) pushed to this array, rather than going through the scheduling
|
||
* rigamarole that would cause another turn of the flush loop.
|
||
* @type {Effect[] | null}
|
||
*/
|
||
var collected_effects = null;
|
||
/**
|
||
* An array of effects that are marked during traversal as a result of a `set`
|
||
* (not `internal_set`) call. These will be added to the next batch and
|
||
* trigger another `batch.process()`
|
||
* @type {Effect[] | null}
|
||
* @deprecated when we get rid of legacy mode and stores, we can get rid of this
|
||
*/
|
||
var legacy_updates = null;
|
||
var flush_count = 0;
|
||
/* @__PURE__ */ new Set();
|
||
var uid = 1;
|
||
var Batch = class Batch {
|
||
id = uid++;
|
||
/** True as soon as `#process` was called */
|
||
#started = false;
|
||
linked = true;
|
||
/** @type {Batch | null} */
|
||
#prev = null;
|
||
/** @type {Batch | null} */
|
||
#next = null;
|
||
/** @type {Map<Effect, ReturnType<typeof deferred<any>>>} */
|
||
async_deriveds = /* @__PURE__ */ new Map();
|
||
/**
|
||
* The current values of any signals that are updated in this batch.
|
||
* Tuple format: [value, is_derived] (note: is_derived is false for deriveds, too, if they were overridden via assignment)
|
||
* They keys of this map are identical to `this.#previous`
|
||
* @type {Map<Value, [any, boolean]>}
|
||
*/
|
||
current = /* @__PURE__ */ new Map();
|
||
/**
|
||
* The values of any signals (sources and deriveds) that are updated in this batch _before_ those updates took place.
|
||
* They keys of this map are identical to `this.#current`
|
||
* @type {Map<Value, any>}
|
||
*/
|
||
previous = /* @__PURE__ */ new Map();
|
||
/**
|
||
* When the batch is committed (and the DOM is updated), we need to remove old branches
|
||
* and append new ones by calling the functions added inside (if/each/key/etc) blocks
|
||
* @type {Set<(batch: Batch) => void>}
|
||
*/
|
||
#commit_callbacks = /* @__PURE__ */ new Set();
|
||
/**
|
||
* If a fork is discarded, we need to destroy any effects that are no longer needed
|
||
* @type {Set<(batch: Batch) => void>}
|
||
*/
|
||
#discard_callbacks = /* @__PURE__ */ new Set();
|
||
/**
|
||
* The number of async effects that are currently in flight
|
||
*/
|
||
#pending = 0;
|
||
/**
|
||
* Async effects that are currently in flight, _not_ inside a pending boundary
|
||
* @type {Map<Effect, number>}
|
||
*/
|
||
#blocking_pending = /* @__PURE__ */ new Map();
|
||
/**
|
||
* A deferred that resolves when the batch is committed, used with `settled()`
|
||
* TODO replace with Promise.withResolvers once supported widely enough
|
||
* @type {{ promise: Promise<void>, resolve: (value?: any) => void, reject: (reason: unknown) => void } | null}
|
||
*/
|
||
#deferred = null;
|
||
/**
|
||
* The root effects that need to be flushed
|
||
* @type {Effect[]}
|
||
*/
|
||
#roots = [];
|
||
/**
|
||
* Effects created while this batch was active.
|
||
* @type {Effect[]}
|
||
*/
|
||
#new_effects = [];
|
||
/**
|
||
* Deferred effects (which run after async work has completed) that are DIRTY
|
||
* @type {Set<Effect>}
|
||
*/
|
||
#dirty_effects = /* @__PURE__ */ new Set();
|
||
/**
|
||
* Deferred effects that are MAYBE_DIRTY
|
||
* @type {Set<Effect>}
|
||
*/
|
||
#maybe_dirty_effects = /* @__PURE__ */ new Set();
|
||
/**
|
||
* A map of branches that still exist, but will be destroyed when this batch
|
||
* is committed — we skip over these during `process`.
|
||
* The value contains child effects that were dirty/maybe_dirty before being reset,
|
||
* so they can be rescheduled if the branch survives.
|
||
* @type {Map<Effect, { d: Effect[], m: Effect[] }>}
|
||
*/
|
||
#skipped_branches = /* @__PURE__ */ new Map();
|
||
/**
|
||
* Inverse of #skipped_branches which we need to tell prior batches to unskip them when committing
|
||
* @type {Set<Effect>}
|
||
*/
|
||
#unskipped_branches = /* @__PURE__ */ new Set();
|
||
is_fork = false;
|
||
#decrement_queued = false;
|
||
constructor() {
|
||
if (last_batch === null) first_batch = last_batch = this;
|
||
else {
|
||
last_batch.#next = this;
|
||
this.#prev = last_batch;
|
||
}
|
||
last_batch = this;
|
||
}
|
||
#is_deferred() {
|
||
if (this.is_fork) return true;
|
||
for (const effect of this.#blocking_pending.keys()) {
|
||
var e = effect;
|
||
var skipped = false;
|
||
while (e.parent !== null) {
|
||
if (this.#skipped_branches.has(e)) {
|
||
skipped = true;
|
||
break;
|
||
}
|
||
e = e.parent;
|
||
}
|
||
if (!skipped) return true;
|
||
}
|
||
return false;
|
||
}
|
||
/**
|
||
* Add an effect to the #skipped_branches map and reset its children
|
||
* @param {Effect} effect
|
||
*/
|
||
skip_effect(effect) {
|
||
if (!this.#skipped_branches.has(effect)) this.#skipped_branches.set(effect, {
|
||
d: [],
|
||
m: []
|
||
});
|
||
this.#unskipped_branches.delete(effect);
|
||
}
|
||
/**
|
||
* Remove an effect from the #skipped_branches map and reschedule
|
||
* any tracked dirty/maybe_dirty child effects
|
||
* @param {Effect} effect
|
||
* @param {(e: Effect) => void} callback
|
||
*/
|
||
unskip_effect(effect, callback = (e) => this.schedule(e)) {
|
||
var tracked = this.#skipped_branches.get(effect);
|
||
if (tracked) {
|
||
this.#skipped_branches.delete(effect);
|
||
for (var e of tracked.d) {
|
||
set_signal_status(e, DIRTY);
|
||
callback(e);
|
||
}
|
||
for (e of tracked.m) {
|
||
set_signal_status(e, MAYBE_DIRTY);
|
||
callback(e);
|
||
}
|
||
}
|
||
this.#unskipped_branches.add(effect);
|
||
}
|
||
#process() {
|
||
this.#started = true;
|
||
if (flush_count++ > 1e3) {
|
||
this.#unlink();
|
||
infinite_loop_guard();
|
||
}
|
||
for (const e of this.#dirty_effects) {
|
||
this.#maybe_dirty_effects.delete(e);
|
||
set_signal_status(e, DIRTY);
|
||
this.schedule(e);
|
||
}
|
||
for (const e of this.#maybe_dirty_effects) {
|
||
set_signal_status(e, MAYBE_DIRTY);
|
||
this.schedule(e);
|
||
}
|
||
const roots = this.#roots;
|
||
this.#roots = [];
|
||
this.apply();
|
||
/** @type {Effect[]} */
|
||
var effects = collected_effects = [];
|
||
/** @type {Effect[]} */
|
||
var render_effects = [];
|
||
/**
|
||
* @type {Effect[]}
|
||
* @deprecated when we get rid of legacy mode and stores, we can get rid of this
|
||
*/
|
||
var updates = legacy_updates = [];
|
||
for (const root of roots) try {
|
||
this.#traverse(root, effects, render_effects);
|
||
} catch (e) {
|
||
reset_all(root);
|
||
if (!this.#is_deferred()) this.discard();
|
||
throw e;
|
||
}
|
||
current_batch = null;
|
||
if (updates.length > 0) {
|
||
var batch = Batch.ensure();
|
||
for (const e of updates) batch.schedule(e);
|
||
}
|
||
collected_effects = null;
|
||
legacy_updates = null;
|
||
if (this.#is_deferred()) {
|
||
this.#defer_effects(render_effects);
|
||
this.#defer_effects(effects);
|
||
for (const [e, t] of this.#skipped_branches) reset_branch(e, t);
|
||
if (updates.length > 0)
|
||
/** @type {Batch} */ current_batch.#process();
|
||
return;
|
||
}
|
||
const earlier_batch = this.#find_earlier_batch();
|
||
if (earlier_batch) {
|
||
this.#defer_effects(render_effects);
|
||
this.#defer_effects(effects);
|
||
earlier_batch.#merge(this);
|
||
return;
|
||
}
|
||
this.#dirty_effects.clear();
|
||
this.#maybe_dirty_effects.clear();
|
||
for (const fn of this.#commit_callbacks) fn(this);
|
||
this.#commit_callbacks.clear();
|
||
previous_batch = this;
|
||
flush_queued_effects(render_effects);
|
||
flush_queued_effects(effects);
|
||
previous_batch = null;
|
||
this.#deferred?.resolve();
|
||
var next_batch = current_batch;
|
||
if (this.#pending === 0 && (this.#roots.length === 0 || next_batch !== null)) {
|
||
this.#unlink();
|
||
if (async_mode_flag) {
|
||
this.#commit();
|
||
current_batch = next_batch;
|
||
}
|
||
}
|
||
if (this.#roots.length > 0) if (next_batch !== null) {
|
||
const batch = next_batch;
|
||
batch.#roots.push(...this.#roots.filter((r) => !batch.#roots.includes(r)));
|
||
} else next_batch = this;
|
||
if (next_batch !== null) next_batch.#process();
|
||
}
|
||
/**
|
||
* Traverse the effect tree, executing effects or stashing
|
||
* them for later execution as appropriate
|
||
* @param {Effect} root
|
||
* @param {Effect[]} effects
|
||
* @param {Effect[]} render_effects
|
||
*/
|
||
#traverse(root, effects, render_effects) {
|
||
root.f ^= CLEAN;
|
||
var effect = root.first;
|
||
while (effect !== null) {
|
||
var flags = effect.f;
|
||
var is_branch = (flags & 96) !== 0;
|
||
if (!(is_branch && (flags & 1024) !== 0 || (flags & 8192) !== 0 || this.#skipped_branches.has(effect)) && effect.fn !== null) {
|
||
if (is_branch) effect.f ^= CLEAN;
|
||
else if ((flags & 4) !== 0) effects.push(effect);
|
||
else if (async_mode_flag && (flags & 16777224) !== 0) render_effects.push(effect);
|
||
else if (is_dirty(effect)) {
|
||
if ((flags & 16) !== 0) this.#maybe_dirty_effects.add(effect);
|
||
update_effect(effect);
|
||
}
|
||
var child = effect.first;
|
||
if (child !== null) {
|
||
effect = child;
|
||
continue;
|
||
}
|
||
}
|
||
while (effect !== null) {
|
||
var next = effect.next;
|
||
if (next !== null) {
|
||
effect = next;
|
||
break;
|
||
}
|
||
effect = effect.parent;
|
||
}
|
||
}
|
||
}
|
||
#find_earlier_batch() {
|
||
var batch = this.#prev;
|
||
while (batch !== null) {
|
||
if (!batch.is_fork) {
|
||
for (const [value, [, is_derived]] of this.current) if (batch.current.has(value) && !is_derived) return batch;
|
||
}
|
||
batch = batch.#prev;
|
||
}
|
||
return null;
|
||
}
|
||
/**
|
||
* @param {Batch} batch
|
||
*/
|
||
#merge(batch) {
|
||
for (const [source, value] of batch.current) {
|
||
if (!this.previous.has(source) && batch.previous.has(source)) this.previous.set(source, batch.previous.get(source));
|
||
this.current.set(source, value);
|
||
}
|
||
for (const [effect, deferred] of batch.async_deriveds) {
|
||
const d = this.async_deriveds.get(effect);
|
||
if (d) deferred.promise.then(d.resolve).catch(d.reject);
|
||
}
|
||
batch.async_deriveds.clear();
|
||
this.transfer_effects(batch.#dirty_effects, batch.#maybe_dirty_effects);
|
||
/**
|
||
* mark all effects that depend on `batch.current`, except the
|
||
* async effects that we just resolved (TODO unless they depend
|
||
* on values in this batch that are NOT in the later batch?).
|
||
* Through this we also will populate the correct #skipped_branches,
|
||
* oncommit callbacks etc, so we don't need to merge them separately.
|
||
* @param {Value} value
|
||
*/
|
||
const mark = (value) => {
|
||
var reactions = value.reactions;
|
||
if (reactions === null) return;
|
||
if ((value.f & 2) !== 0 && (value.f & 6144) === 0) return;
|
||
for (const reaction of reactions) {
|
||
var flags = reaction.f;
|
||
if ((flags & 2) !== 0) mark(reaction);
|
||
else {
|
||
var effect = reaction;
|
||
if (flags & 4194320 && !this.async_deriveds.has(effect)) {
|
||
this.#maybe_dirty_effects.delete(effect);
|
||
set_signal_status(effect, DIRTY);
|
||
this.schedule(effect);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
for (const source of this.current.keys()) mark(source);
|
||
this.oncommit(() => batch.discard());
|
||
batch.#unlink();
|
||
current_batch = this;
|
||
this.#process();
|
||
}
|
||
/**
|
||
* @param {Effect[]} effects
|
||
*/
|
||
#defer_effects(effects) {
|
||
for (var i = 0; i < effects.length; i += 1) defer_effect(effects[i], this.#dirty_effects, this.#maybe_dirty_effects);
|
||
}
|
||
/**
|
||
* Associate a change to a given source with the current
|
||
* batch, noting its previous and current values
|
||
* @param {Value} source
|
||
* @param {any} value
|
||
* @param {boolean} [is_derived]
|
||
*/
|
||
capture(source, value, is_derived = false) {
|
||
if (source.v !== UNINITIALIZED && !this.previous.has(source)) this.previous.set(source, source.v);
|
||
if ((source.f & 8388608) === 0) {
|
||
this.current.set(source, [value, is_derived]);
|
||
batch_values?.set(source, value);
|
||
}
|
||
if (!this.is_fork) source.v = value;
|
||
}
|
||
activate() {
|
||
current_batch = this;
|
||
}
|
||
deactivate() {
|
||
current_batch = null;
|
||
batch_values = null;
|
||
}
|
||
flush() {
|
||
try {
|
||
is_processing = true;
|
||
current_batch = this;
|
||
this.#process();
|
||
} finally {
|
||
flush_count = 0;
|
||
last_scheduled_effect = null;
|
||
collected_effects = null;
|
||
legacy_updates = null;
|
||
is_processing = false;
|
||
current_batch = null;
|
||
batch_values = null;
|
||
old_values.clear();
|
||
}
|
||
}
|
||
discard() {
|
||
for (const fn of this.#discard_callbacks) fn(this);
|
||
this.#discard_callbacks.clear();
|
||
for (const deferred of this.async_deriveds.values()) deferred.reject(OBSOLETE);
|
||
this.#unlink();
|
||
this.#deferred?.resolve();
|
||
}
|
||
/**
|
||
* @param {Effect} effect
|
||
*/
|
||
register_created_effect(effect) {
|
||
this.#new_effects.push(effect);
|
||
}
|
||
#commit() {
|
||
for (let batch = first_batch; batch !== null; batch = batch.#next) {
|
||
var is_earlier = batch.id < this.id;
|
||
/** @type {Source[]} */
|
||
var sources = [];
|
||
for (const [source, [value, is_derived]] of this.current) {
|
||
if (batch.current.has(source)) {
|
||
var batch_value = batch.current.get(source)[0];
|
||
if (is_earlier && value !== batch_value) batch.current.set(source, [value, is_derived]);
|
||
else continue;
|
||
}
|
||
sources.push(source);
|
||
}
|
||
if (is_earlier) for (const [effect, deferred] of this.async_deriveds) {
|
||
const d = batch.async_deriveds.get(effect);
|
||
if (d) deferred.promise.then(d.resolve).catch(d.reject);
|
||
}
|
||
var current = [...batch.current.keys()].filter((source) => !batch.current.get(source)[1]);
|
||
if (!batch.#started || current.length === 0) continue;
|
||
var others = current.filter((source) => !this.current.has(source));
|
||
if (others.length === 0) {
|
||
if (is_earlier) batch.discard();
|
||
} else if (sources.length > 0) {
|
||
if (is_earlier) for (const unskipped of this.#unskipped_branches) batch.unskip_effect(unskipped, (e) => {
|
||
if ((e.f & 4194320) !== 0) batch.schedule(e);
|
||
else batch.#defer_effects([e]);
|
||
});
|
||
batch.activate();
|
||
/** @type {Set<Value>} */
|
||
var marked = /* @__PURE__ */ new Set();
|
||
/** @type {Map<Reaction, boolean>} */
|
||
var checked = /* @__PURE__ */ new Map();
|
||
for (var source of sources) mark_effects(source, others, marked, checked);
|
||
checked = /* @__PURE__ */ new Map();
|
||
var current_unequal = [...batch.current].filter(([c, v1]) => {
|
||
const v2 = this.current.get(c);
|
||
if (!v2) return true;
|
||
return v2[0] !== v1[0] || v2[1] !== v1[1];
|
||
}).map(([c]) => c);
|
||
if (current_unequal.length > 0) {
|
||
for (const effect of this.#new_effects) if ((effect.f & 155648) === 0 && depends_on(effect, current_unequal, checked)) if ((effect.f & 4194320) !== 0) {
|
||
set_signal_status(effect, DIRTY);
|
||
batch.schedule(effect);
|
||
} else batch.#dirty_effects.add(effect);
|
||
}
|
||
if (batch.#roots.length > 0 && !batch.#decrement_queued) {
|
||
batch.apply();
|
||
for (var root of batch.#roots) batch.#traverse(root, [], []);
|
||
batch.#roots = [];
|
||
}
|
||
batch.deactivate();
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* @param {boolean} blocking
|
||
* @param {Effect} effect
|
||
*/
|
||
increment(blocking, effect) {
|
||
this.#pending += 1;
|
||
if (blocking) {
|
||
let blocking_pending_count = this.#blocking_pending.get(effect) ?? 0;
|
||
this.#blocking_pending.set(effect, blocking_pending_count + 1);
|
||
}
|
||
}
|
||
/**
|
||
* @param {boolean} blocking
|
||
* @param {Effect} effect
|
||
*/
|
||
decrement(blocking, effect) {
|
||
this.#pending -= 1;
|
||
if (blocking) {
|
||
let blocking_pending_count = this.#blocking_pending.get(effect) ?? 0;
|
||
if (blocking_pending_count === 1) this.#blocking_pending.delete(effect);
|
||
else this.#blocking_pending.set(effect, blocking_pending_count - 1);
|
||
}
|
||
if (this.#decrement_queued) return;
|
||
this.#decrement_queued = true;
|
||
queue_micro_task(() => {
|
||
this.#decrement_queued = false;
|
||
if (this.linked) this.flush();
|
||
});
|
||
}
|
||
/**
|
||
* @param {Set<Effect>} dirty_effects
|
||
* @param {Set<Effect>} maybe_dirty_effects
|
||
*/
|
||
transfer_effects(dirty_effects, maybe_dirty_effects) {
|
||
for (const e of dirty_effects) this.#dirty_effects.add(e);
|
||
for (const e of maybe_dirty_effects) this.#maybe_dirty_effects.add(e);
|
||
dirty_effects.clear();
|
||
maybe_dirty_effects.clear();
|
||
}
|
||
/** @param {(batch: Batch) => void} fn */
|
||
oncommit(fn) {
|
||
this.#commit_callbacks.add(fn);
|
||
}
|
||
/** @param {(batch: Batch) => void} fn */
|
||
ondiscard(fn) {
|
||
this.#discard_callbacks.add(fn);
|
||
}
|
||
settled() {
|
||
return (this.#deferred ??= deferred()).promise;
|
||
}
|
||
static ensure() {
|
||
if (current_batch === null) {
|
||
const batch = current_batch = new Batch();
|
||
if (!is_processing && !is_flushing_sync) queue_micro_task(() => {
|
||
if (!batch.#started) batch.flush();
|
||
});
|
||
}
|
||
return current_batch;
|
||
}
|
||
apply() {
|
||
if (!async_mode_flag || !this.is_fork && this.#prev === null && this.#next === null) {
|
||
batch_values = null;
|
||
return;
|
||
}
|
||
batch_values = /* @__PURE__ */ new Map();
|
||
for (const [source, [value]] of this.current) batch_values.set(source, value);
|
||
for (let batch = first_batch; batch !== null; batch = batch.#next) {
|
||
if (batch === this || batch.is_fork) continue;
|
||
var intersects = false;
|
||
if (batch.id < this.id) for (const [source, [, is_derived]] of batch.current) {
|
||
if (is_derived) continue;
|
||
if (this.current.has(source)) {
|
||
intersects = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!intersects) {
|
||
for (const [source, previous] of batch.previous) if (!batch_values.has(source)) batch_values.set(source, previous);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
*
|
||
* @param {Effect} effect
|
||
*/
|
||
schedule(effect) {
|
||
last_scheduled_effect = effect;
|
||
if (effect.b?.is_pending && (effect.f & 16777228) !== 0 && (effect.f & 32768) === 0) {
|
||
effect.b.defer_effect(effect);
|
||
return;
|
||
}
|
||
var e = effect;
|
||
while (e.parent !== null) {
|
||
e = e.parent;
|
||
var flags = e.f;
|
||
if (collected_effects !== null && e === active_effect) {
|
||
if (async_mode_flag) return;
|
||
if ((active_reaction === null || (active_reaction.f & 2) === 0) && !legacy_is_updating_store) return;
|
||
}
|
||
if ((flags & 96) !== 0) {
|
||
if ((flags & 1024) === 0) return;
|
||
e.f ^= CLEAN;
|
||
}
|
||
}
|
||
this.#roots.push(e);
|
||
}
|
||
#unlink() {
|
||
if (!this.linked) return;
|
||
var prev = this.#prev;
|
||
var next = this.#next;
|
||
if (prev === null) first_batch = next;
|
||
else prev.#next = next;
|
||
if (next === null) last_batch = prev;
|
||
else next.#prev = prev;
|
||
this.linked = false;
|
||
}
|
||
};
|
||
function infinite_loop_guard() {
|
||
try {
|
||
effect_update_depth_exceeded();
|
||
} catch (error) {
|
||
invoke_error_boundary(error, last_scheduled_effect);
|
||
}
|
||
}
|
||
/** @type {Set<Effect> | null} */
|
||
var eager_block_effects = null;
|
||
/**
|
||
* @param {Array<Effect>} effects
|
||
* @returns {void}
|
||
*/
|
||
function flush_queued_effects(effects) {
|
||
var length = effects.length;
|
||
if (length === 0) return;
|
||
var i = 0;
|
||
while (i < length) {
|
||
var effect = effects[i++];
|
||
if ((effect.f & 24576) === 0 && is_dirty(effect)) {
|
||
eager_block_effects = /* @__PURE__ */ new Set();
|
||
update_effect(effect);
|
||
if (effect.deps === null && effect.first === null && effect.nodes === null && effect.teardown === null && effect.ac === null) unlink_effect(effect);
|
||
if (eager_block_effects?.size > 0) {
|
||
old_values.clear();
|
||
for (const e of eager_block_effects) {
|
||
if ((e.f & 24576) !== 0) continue;
|
||
/** @type {Effect[]} */
|
||
const ordered_effects = [e];
|
||
let ancestor = e.parent;
|
||
while (ancestor !== null) {
|
||
if (eager_block_effects.has(ancestor)) {
|
||
eager_block_effects.delete(ancestor);
|
||
ordered_effects.push(ancestor);
|
||
}
|
||
ancestor = ancestor.parent;
|
||
}
|
||
for (let j = ordered_effects.length - 1; j >= 0; j--) {
|
||
const e = ordered_effects[j];
|
||
if ((e.f & 24576) !== 0) continue;
|
||
update_effect(e);
|
||
}
|
||
}
|
||
eager_block_effects.clear();
|
||
}
|
||
}
|
||
}
|
||
eager_block_effects = null;
|
||
}
|
||
/**
|
||
* This is similar to `mark_reactions`, but it only marks async/block effects
|
||
* depending on `value` and at least one of the other `sources`, so that
|
||
* these effects can re-run after another batch has been committed
|
||
* @param {Value} value
|
||
* @param {Source[]} sources
|
||
* @param {Set<Value>} marked
|
||
* @param {Map<Reaction, boolean>} checked
|
||
*/
|
||
function mark_effects(value, sources, marked, checked) {
|
||
if (marked.has(value)) return;
|
||
marked.add(value);
|
||
if (value.reactions !== null) for (const reaction of value.reactions) {
|
||
const flags = reaction.f;
|
||
if ((flags & 2) !== 0) mark_effects(reaction, sources, marked, checked);
|
||
else if ((flags & 4194320) !== 0 && (flags & 2048) === 0 && depends_on(reaction, sources, checked)) {
|
||
set_signal_status(reaction, DIRTY);
|
||
schedule_effect(reaction);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* @param {Reaction} reaction
|
||
* @param {Source[]} sources
|
||
* @param {Map<Reaction, boolean>} checked
|
||
*/
|
||
function depends_on(reaction, sources, checked) {
|
||
const depends = checked.get(reaction);
|
||
if (depends !== void 0) return depends;
|
||
if (reaction.deps !== null) for (const dep of reaction.deps) {
|
||
if (includes.call(sources, dep)) return true;
|
||
if ((dep.f & 2) !== 0 && depends_on(dep, sources, checked)) {
|
||
checked.set(dep, true);
|
||
return true;
|
||
}
|
||
}
|
||
checked.set(reaction, false);
|
||
return false;
|
||
}
|
||
/**
|
||
* @param {Effect} effect
|
||
* @returns {void}
|
||
*/
|
||
function schedule_effect(effect) {
|
||
/** @type {Batch} */ current_batch.schedule(effect);
|
||
}
|
||
/**
|
||
* Mark all the effects inside a skipped branch CLEAN, so that
|
||
* they can be correctly rescheduled later. Tracks dirty and maybe_dirty
|
||
* effects so they can be rescheduled if the branch survives.
|
||
* @param {Effect} effect
|
||
* @param {{ d: Effect[], m: Effect[] }} tracked
|
||
*/
|
||
function reset_branch(effect, tracked) {
|
||
if ((effect.f & 32) !== 0 && (effect.f & 1024) !== 0) return;
|
||
if ((effect.f & 2048) !== 0) tracked.d.push(effect);
|
||
else if ((effect.f & 4096) !== 0) tracked.m.push(effect);
|
||
set_signal_status(effect, CLEAN);
|
||
var e = effect.first;
|
||
while (e !== null) {
|
||
reset_branch(e, tracked);
|
||
e = e.next;
|
||
}
|
||
}
|
||
/**
|
||
* Mark an entire effect tree clean following an error
|
||
* @param {Effect} effect
|
||
*/
|
||
function reset_all(effect) {
|
||
set_signal_status(effect, CLEAN);
|
||
var e = effect.first;
|
||
while (e !== null) {
|
||
reset_all(e);
|
||
e = e.next;
|
||
}
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/reactivity/sources.js
|
||
/** @import { Derived, Effect, Source, Value } from '#client' */
|
||
/** @type {Set<Effect>} */
|
||
var eager_effects = /* @__PURE__ */ new Set();
|
||
/** @type {Map<Source, any>} */
|
||
var old_values = /* @__PURE__ */ new Map();
|
||
var eager_effects_deferred = false;
|
||
/**
|
||
* @template V
|
||
* @param {V} v
|
||
* @param {Error | null} [stack]
|
||
* @returns {Source<V>}
|
||
*/
|
||
function source(v, stack) {
|
||
return {
|
||
f: 0,
|
||
v,
|
||
reactions: null,
|
||
equals,
|
||
rv: 0,
|
||
wv: 0
|
||
};
|
||
}
|
||
/**
|
||
* @template V
|
||
* @param {V} v
|
||
* @param {Error | null} [stack]
|
||
*/
|
||
/*#__NO_SIDE_EFFECTS__*/
|
||
function state(v, stack) {
|
||
const s = source(v, stack);
|
||
push_reaction_value(s);
|
||
return s;
|
||
}
|
||
/**
|
||
* @template V
|
||
* @param {V} initial_value
|
||
* @param {boolean} [immutable]
|
||
* @returns {Source<V>}
|
||
*/
|
||
/*#__NO_SIDE_EFFECTS__*/
|
||
function mutable_source(initial_value, immutable = false, trackable = true) {
|
||
const s = source(initial_value);
|
||
if (!immutable) s.equals = safe_equals;
|
||
if (legacy_mode_flag && trackable && component_context !== null && component_context.l !== null) (component_context.l.s ??= []).push(s);
|
||
return s;
|
||
}
|
||
/**
|
||
* @template V
|
||
* @param {Source<V>} source
|
||
* @param {V} value
|
||
* @param {boolean} [should_proxy]
|
||
* @returns {V}
|
||
*/
|
||
function set(source, value, should_proxy = false) {
|
||
if (active_reaction !== null && (!untracking || (active_reaction.f & 131072) !== 0) && is_runes() && (active_reaction.f & 4325394) !== 0 && (current_sources === null || !current_sources.has(source))) state_unsafe_mutation();
|
||
return internal_set(source, should_proxy ? proxy(value) : value, legacy_updates);
|
||
}
|
||
/**
|
||
* @template V
|
||
* @param {Source<V>} source
|
||
* @param {V} value
|
||
* @param {Effect[] | null} [updated_during_traversal]
|
||
* @returns {V}
|
||
*/
|
||
function internal_set(source, value, updated_during_traversal = null) {
|
||
if (!source.equals(value)) {
|
||
old_values.set(source, is_destroying_effect ? value : source.v);
|
||
var batch = Batch.ensure();
|
||
batch.capture(source, value);
|
||
if ((source.f & 2) !== 0) {
|
||
const derived = source;
|
||
if ((source.f & 2048) !== 0) execute_derived(derived);
|
||
if (batch_values === null) update_derived_status(derived);
|
||
}
|
||
source.wv = increment_write_version();
|
||
mark_reactions(source, DIRTY, updated_during_traversal);
|
||
if (is_runes() && active_effect !== null && (active_effect.f & 1024) !== 0 && (active_effect.f & 96) === 0) if (untracked_writes === null) set_untracked_writes([source]);
|
||
else untracked_writes.push(source);
|
||
if (!batch.is_fork && eager_effects.size > 0 && !eager_effects_deferred) flush_eager_effects();
|
||
}
|
||
return value;
|
||
}
|
||
function flush_eager_effects() {
|
||
eager_effects_deferred = false;
|
||
for (const effect of eager_effects) {
|
||
if ((effect.f & 1024) !== 0) set_signal_status(effect, MAYBE_DIRTY);
|
||
let dirty;
|
||
try {
|
||
dirty = is_dirty(effect);
|
||
} catch {
|
||
dirty = true;
|
||
}
|
||
if (dirty) update_effect(effect);
|
||
}
|
||
eager_effects.clear();
|
||
}
|
||
/**
|
||
* @template {number | bigint} T
|
||
* @param {Source<T>} source
|
||
* @param {1 | -1} [d]
|
||
* @returns {T}
|
||
*/
|
||
function update(source, d = 1) {
|
||
var value = get(source);
|
||
var result = d === 1 ? value++ : value--;
|
||
set(source, value);
|
||
return result;
|
||
}
|
||
/**
|
||
* Silently (without using `get`) increment a source
|
||
* @param {Source<number>} source
|
||
*/
|
||
function increment(source) {
|
||
set(source, source.v + 1);
|
||
}
|
||
/**
|
||
* @param {Value} signal
|
||
* @param {number} status should be DIRTY or MAYBE_DIRTY
|
||
* @param {Effect[] | null} updated_during_traversal
|
||
* @returns {void}
|
||
*/
|
||
function mark_reactions(signal, status, updated_during_traversal) {
|
||
var reactions = signal.reactions;
|
||
if (reactions === null) return;
|
||
var runes = is_runes();
|
||
var length = reactions.length;
|
||
for (var i = 0; i < length; i++) {
|
||
var reaction = reactions[i];
|
||
var flags = reaction.f;
|
||
if (!runes && reaction === active_effect) continue;
|
||
var not_dirty = (flags & DIRTY) === 0;
|
||
if (not_dirty) set_signal_status(reaction, status);
|
||
if ((flags & 131072) !== 0) eager_effects.add(reaction);
|
||
else if ((flags & 2) !== 0) {
|
||
var derived = reaction;
|
||
batch_values?.delete(derived);
|
||
if ((flags & 65536) === 0) {
|
||
if (flags & 512 && (active_effect === null || (active_effect.f & 2097152) === 0)) reaction.f |= WAS_MARKED;
|
||
mark_reactions(derived, MAYBE_DIRTY, updated_during_traversal);
|
||
}
|
||
} else if (not_dirty) {
|
||
var effect = reaction;
|
||
if ((flags & 16) !== 0 && eager_block_effects !== null) eager_block_effects.add(effect);
|
||
if (updated_during_traversal !== null) updated_during_traversal.push(effect);
|
||
else schedule_effect(effect);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* @template T
|
||
* @param {T} value
|
||
* @returns {T}
|
||
*/
|
||
function proxy(value) {
|
||
if (typeof value !== "object" || value === null || STATE_SYMBOL in value) return value;
|
||
const prototype = get_prototype_of(value);
|
||
if (prototype !== object_prototype && prototype !== array_prototype) return value;
|
||
/** @type {Map<any, Source<any>>} */
|
||
var sources = /* @__PURE__ */ new Map();
|
||
var is_proxied_array = is_array(value);
|
||
var version = /* @__PURE__ */ state(0);
|
||
var stack = null;
|
||
var parent_version = update_version;
|
||
/**
|
||
* Executes the proxy in the context of the reaction it was originally created in, if any
|
||
* @template T
|
||
* @param {() => T} fn
|
||
*/
|
||
var with_parent = (fn) => {
|
||
if (update_version === parent_version) return fn();
|
||
var reaction = active_reaction;
|
||
var version = update_version;
|
||
set_active_reaction(null);
|
||
set_update_version(parent_version);
|
||
var result = fn();
|
||
set_active_reaction(reaction);
|
||
set_update_version(version);
|
||
return result;
|
||
};
|
||
if (is_proxied_array) sources.set("length", /* @__PURE__ */ state(
|
||
/** @type {any[]} */
|
||
value.length,
|
||
stack
|
||
));
|
||
return new Proxy(value, {
|
||
defineProperty(_, prop, descriptor) {
|
||
if (!("value" in descriptor) || descriptor.configurable === false || descriptor.enumerable === false || descriptor.writable === false) state_descriptors_fixed();
|
||
var s = sources.get(prop);
|
||
if (s === void 0) with_parent(() => {
|
||
var s = /* @__PURE__ */ state(descriptor.value, stack);
|
||
sources.set(prop, s);
|
||
return s;
|
||
});
|
||
else set(s, descriptor.value, true);
|
||
return true;
|
||
},
|
||
deleteProperty(target, prop) {
|
||
var s = sources.get(prop);
|
||
if (s === void 0) {
|
||
if (prop in target) {
|
||
const s = with_parent(() => /* @__PURE__ */ state(UNINITIALIZED, stack));
|
||
sources.set(prop, s);
|
||
increment(version);
|
||
}
|
||
} else {
|
||
set(s, UNINITIALIZED);
|
||
increment(version);
|
||
}
|
||
return true;
|
||
},
|
||
get(target, prop, receiver) {
|
||
if (prop === STATE_SYMBOL) return value;
|
||
var s = sources.get(prop);
|
||
var exists = prop in target;
|
||
if (s === void 0 && (!exists || get_descriptor(target, prop)?.writable)) {
|
||
s = with_parent(() => {
|
||
return /* @__PURE__ */ state(proxy(exists ? target[prop] : UNINITIALIZED), stack);
|
||
});
|
||
sources.set(prop, s);
|
||
}
|
||
if (s !== void 0) {
|
||
var v = get(s);
|
||
return v === UNINITIALIZED ? void 0 : v;
|
||
}
|
||
return Reflect.get(target, prop, receiver);
|
||
},
|
||
getOwnPropertyDescriptor(target, prop) {
|
||
var descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
|
||
if (descriptor && "value" in descriptor) {
|
||
var s = sources.get(prop);
|
||
if (s) descriptor.value = get(s);
|
||
} else if (descriptor === void 0) {
|
||
var source = sources.get(prop);
|
||
var value = source?.v;
|
||
if (source !== void 0 && value !== UNINITIALIZED) return {
|
||
enumerable: true,
|
||
configurable: true,
|
||
value,
|
||
writable: true
|
||
};
|
||
}
|
||
return descriptor;
|
||
},
|
||
has(target, prop) {
|
||
if (prop === STATE_SYMBOL) return true;
|
||
var s = sources.get(prop);
|
||
var has = s !== void 0 && s.v !== UNINITIALIZED || Reflect.has(target, prop);
|
||
if (s !== void 0 || active_effect !== null && (!has || get_descriptor(target, prop)?.writable)) {
|
||
if (s === void 0) {
|
||
s = with_parent(() => {
|
||
return /* @__PURE__ */ state(has ? proxy(target[prop]) : UNINITIALIZED, stack);
|
||
});
|
||
sources.set(prop, s);
|
||
}
|
||
if (get(s) === UNINITIALIZED) return false;
|
||
}
|
||
return has;
|
||
},
|
||
set(target, prop, value, receiver) {
|
||
var s = sources.get(prop);
|
||
var has = prop in target;
|
||
if (is_proxied_array && prop === "length") for (var i = value; i < s.v; i += 1) {
|
||
var other_s = sources.get(i + "");
|
||
if (other_s !== void 0) set(other_s, UNINITIALIZED);
|
||
else if (i in target) {
|
||
other_s = with_parent(() => /* @__PURE__ */ state(UNINITIALIZED, stack));
|
||
sources.set(i + "", other_s);
|
||
}
|
||
}
|
||
if (s === void 0) {
|
||
if (!has || get_descriptor(target, prop)?.writable) {
|
||
s = with_parent(() => /* @__PURE__ */ state(void 0, stack));
|
||
set(s, proxy(value));
|
||
sources.set(prop, s);
|
||
}
|
||
} else {
|
||
has = s.v !== UNINITIALIZED;
|
||
var p = with_parent(() => proxy(value));
|
||
set(s, p);
|
||
}
|
||
var descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
|
||
if (descriptor?.set) descriptor.set.call(receiver, value);
|
||
if (!has) {
|
||
if (is_proxied_array && typeof prop === "string") {
|
||
var ls = sources.get("length");
|
||
var n = Number(prop);
|
||
if (Number.isInteger(n) && n >= ls.v) set(ls, n + 1);
|
||
}
|
||
increment(version);
|
||
}
|
||
return true;
|
||
},
|
||
ownKeys(target) {
|
||
get(version);
|
||
var own_keys = Reflect.ownKeys(target).filter((key) => {
|
||
var source = sources.get(key);
|
||
return source === void 0 || source.v !== UNINITIALIZED;
|
||
});
|
||
for (var [key, source] of sources) if (source.v !== UNINITIALIZED && !(key in target)) own_keys.push(key);
|
||
return own_keys;
|
||
},
|
||
setPrototypeOf() {
|
||
state_prototype_fixed();
|
||
}
|
||
});
|
||
}
|
||
/**
|
||
* @param {any} value
|
||
*/
|
||
function get_proxied_value(value) {
|
||
try {
|
||
if (value !== null && typeof value === "object" && STATE_SYMBOL in value) return value[STATE_SYMBOL];
|
||
} catch {}
|
||
return value;
|
||
}
|
||
/**
|
||
* @param {any} a
|
||
* @param {any} b
|
||
*/
|
||
function is(a, b) {
|
||
return Object.is(get_proxied_value(a), get_proxied_value(b));
|
||
}
|
||
/* @__PURE__ */ new Set([
|
||
"copyWithin",
|
||
"fill",
|
||
"pop",
|
||
"push",
|
||
"reverse",
|
||
"shift",
|
||
"sort",
|
||
"splice",
|
||
"unshift"
|
||
]);
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/operations.js
|
||
/** @import { Effect, TemplateNode } from '#client' */
|
||
/** @type {Window} */
|
||
var $window;
|
||
/** @type {boolean} */
|
||
var is_firefox;
|
||
/** @type {() => Node | null} */
|
||
var first_child_getter;
|
||
/** @type {() => Node | null} */
|
||
var next_sibling_getter;
|
||
/**
|
||
* Initialize these lazily to avoid issues when using the runtime in a server context
|
||
* where these globals are not available while avoiding a separate server entry point
|
||
*/
|
||
function init_operations() {
|
||
if ($window !== void 0) return;
|
||
$window = window;
|
||
is_firefox = /Firefox/.test(navigator.userAgent);
|
||
var element_prototype = Element.prototype;
|
||
var node_prototype = Node.prototype;
|
||
var text_prototype = Text.prototype;
|
||
first_child_getter = get_descriptor(node_prototype, "firstChild").get;
|
||
next_sibling_getter = get_descriptor(node_prototype, "nextSibling").get;
|
||
if (is_extensible(element_prototype)) {
|
||
/** @type {any} */ element_prototype[CLASS_CACHE] = void 0;
|
||
/** @type {any} */ element_prototype[ATTRIBUTES_CACHE] = null;
|
||
/** @type {any} */ element_prototype[STYLE_CACHE] = void 0;
|
||
element_prototype.__e = void 0;
|
||
}
|
||
if (is_extensible(text_prototype))
|
||
/** @type {any} */ text_prototype[TEXT_CACHE] = void 0;
|
||
}
|
||
/**
|
||
* @param {string} value
|
||
* @returns {Text}
|
||
*/
|
||
function create_text(value = "") {
|
||
return document.createTextNode(value);
|
||
}
|
||
/**
|
||
* @template {Node} N
|
||
* @param {N} node
|
||
*/
|
||
/*@__NO_SIDE_EFFECTS__*/
|
||
function get_first_child(node) {
|
||
return first_child_getter.call(node);
|
||
}
|
||
/**
|
||
* @template {Node} N
|
||
* @param {N} node
|
||
*/
|
||
/*@__NO_SIDE_EFFECTS__*/
|
||
function get_next_sibling(node) {
|
||
return next_sibling_getter.call(node);
|
||
}
|
||
/**
|
||
* Don't mark this as side-effect-free, hydration needs to walk all nodes
|
||
* @template {Node} N
|
||
* @param {N} node
|
||
* @param {boolean} is_text
|
||
* @returns {TemplateNode | null}
|
||
*/
|
||
function child(node, is_text) {
|
||
if (!hydrating) return /* @__PURE__ */ get_first_child(node);
|
||
var child = /* @__PURE__ */ get_first_child(hydrate_node);
|
||
if (child === null) child = hydrate_node.appendChild(create_text());
|
||
else if (is_text && child.nodeType !== 3) {
|
||
var text = create_text();
|
||
child?.before(text);
|
||
set_hydrate_node(text);
|
||
return text;
|
||
}
|
||
if (is_text) merge_text_nodes(child);
|
||
set_hydrate_node(child);
|
||
return child;
|
||
}
|
||
/**
|
||
* Don't mark this as side-effect-free, hydration needs to walk all nodes
|
||
* @param {TemplateNode} node
|
||
* @param {boolean} [is_text]
|
||
* @returns {TemplateNode | null}
|
||
*/
|
||
function first_child(node, is_text = false) {
|
||
if (!hydrating) {
|
||
var first = /* @__PURE__ */ get_first_child(node);
|
||
if (first instanceof Comment && first.data === "") return /* @__PURE__ */ get_next_sibling(first);
|
||
return first;
|
||
}
|
||
if (is_text) {
|
||
if (hydrate_node?.nodeType !== 3) {
|
||
var text = create_text();
|
||
hydrate_node?.before(text);
|
||
set_hydrate_node(text);
|
||
return text;
|
||
}
|
||
merge_text_nodes(hydrate_node);
|
||
}
|
||
return hydrate_node;
|
||
}
|
||
/**
|
||
* Don't mark this as side-effect-free, hydration needs to walk all nodes
|
||
* @param {TemplateNode} node
|
||
* @param {number} count
|
||
* @param {boolean} is_text
|
||
* @returns {TemplateNode | null}
|
||
*/
|
||
function sibling(node, count = 1, is_text = false) {
|
||
let next_sibling = hydrating ? hydrate_node : node;
|
||
var last_sibling;
|
||
while (count--) {
|
||
last_sibling = next_sibling;
|
||
next_sibling = /* @__PURE__ */ get_next_sibling(next_sibling);
|
||
}
|
||
if (!hydrating) return next_sibling;
|
||
if (is_text) {
|
||
if (next_sibling?.nodeType !== 3) {
|
||
var text = create_text();
|
||
if (next_sibling === null) last_sibling?.after(text);
|
||
else next_sibling.before(text);
|
||
set_hydrate_node(text);
|
||
return text;
|
||
}
|
||
merge_text_nodes(next_sibling);
|
||
}
|
||
set_hydrate_node(next_sibling);
|
||
return next_sibling;
|
||
}
|
||
/**
|
||
* @template {Node} N
|
||
* @param {N} node
|
||
* @returns {void}
|
||
*/
|
||
function clear_text_content(node) {
|
||
node.textContent = "";
|
||
}
|
||
/**
|
||
* Returns `true` if we're updating the current block, for example `condition` in
|
||
* an `{#if condition}` block just changed. In this case, the branch should be
|
||
* appended (or removed) at the same time as other updates within the
|
||
* current `<svelte:boundary>`
|
||
*/
|
||
function should_defer_append() {
|
||
if (!async_mode_flag) return false;
|
||
if (eager_block_effects !== null) return false;
|
||
return (active_effect.f & REACTION_RAN) !== 0;
|
||
}
|
||
/**
|
||
* Branching here is intentional and load-bearing for perf. `createElement(tag)`
|
||
* hits a fast path in Blink that `createElementNS(NAMESPACE_HTML, tag)` doesn't,
|
||
* and passing an explicit `undefined` as the trailing options arg measurably
|
||
* slows both APIs. Funnelling every case through a single `createElementNS(ns,
|
||
* tag, options)` call would be smaller but slower on the HTML path.
|
||
*
|
||
* @template {keyof HTMLElementTagNameMap | string} T
|
||
* @param {T} tag
|
||
* @param {string} [namespace]
|
||
* @param {string} [is]
|
||
* @returns {T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : Element}
|
||
*/
|
||
function create_element(tag, namespace, is) {
|
||
if (namespace == null || namespace === "http://www.w3.org/1999/xhtml") return is ? document.createElement(tag, { is }) : document.createElement(tag);
|
||
return is ? document.createElementNS(namespace, tag, { is }) : document.createElementNS(namespace, tag);
|
||
}
|
||
/**
|
||
* Browsers split text nodes larger than 65536 bytes when parsing.
|
||
* For hydration to succeed, we need to stitch them back together
|
||
* @param {Text} text
|
||
*/
|
||
function merge_text_nodes(text) {
|
||
if (text.nodeValue.length < 65536) return;
|
||
let next = text.nextSibling;
|
||
while (next !== null && next.nodeType === 3) {
|
||
next.remove();
|
||
/** @type {string} */ text.nodeValue += next.nodeValue;
|
||
next = text.nextSibling;
|
||
}
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/reactivity/effects.js
|
||
/** @import { Blocker, ComponentContext, ComponentContextLegacy, Derived, Effect, TemplateNode, TransitionManager } from '#client' */
|
||
/**
|
||
* @param {'$effect' | '$effect.pre' | '$inspect'} rune
|
||
*/
|
||
function validate_effect(rune) {
|
||
if (active_effect === null) {
|
||
if (active_reaction === null) effect_orphan(rune);
|
||
effect_in_unowned_derived();
|
||
}
|
||
if (is_destroying_effect) effect_in_teardown(rune);
|
||
}
|
||
/**
|
||
* @param {Effect} effect
|
||
* @param {Effect} parent_effect
|
||
*/
|
||
function push_effect(effect, parent_effect) {
|
||
var parent_last = parent_effect.last;
|
||
if (parent_last === null) parent_effect.last = parent_effect.first = effect;
|
||
else {
|
||
parent_last.next = effect;
|
||
effect.prev = parent_last;
|
||
parent_effect.last = effect;
|
||
}
|
||
}
|
||
/**
|
||
* @param {number} type
|
||
* @param {null | (() => void | (() => void))} fn
|
||
* @returns {Effect}
|
||
*/
|
||
function create_effect(type, fn) {
|
||
var parent = active_effect;
|
||
if (parent !== null && (parent.f & 8192) !== 0) type |= INERT;
|
||
/** @type {Effect} */
|
||
var effect = {
|
||
ctx: component_context,
|
||
deps: null,
|
||
nodes: null,
|
||
f: type | DIRTY | 512,
|
||
first: null,
|
||
fn,
|
||
last: null,
|
||
next: null,
|
||
parent,
|
||
b: parent && parent.b,
|
||
prev: null,
|
||
teardown: null,
|
||
wv: 0,
|
||
ac: null
|
||
};
|
||
current_batch?.register_created_effect(effect);
|
||
/** @type {Effect | null} */
|
||
var e = effect;
|
||
if ((type & 4) !== 0) if (collected_effects !== null) collected_effects.push(effect);
|
||
else Batch.ensure().schedule(effect);
|
||
else if (fn !== null) {
|
||
try {
|
||
update_effect(effect);
|
||
} catch (e) {
|
||
destroy_effect(effect);
|
||
throw e;
|
||
}
|
||
if (e.deps === null && e.teardown === null && e.nodes === null && e.first === e.last && (e.f & 524288) === 0) {
|
||
e = e.first;
|
||
if ((type & 16) !== 0 && (type & 65536) !== 0 && e !== null) e.f |= EFFECT_TRANSPARENT;
|
||
}
|
||
}
|
||
if (e !== null) {
|
||
e.parent = parent;
|
||
if (parent !== null) push_effect(e, parent);
|
||
if (active_reaction !== null && (active_reaction.f & 2) !== 0 && (type & 64) === 0) {
|
||
var derived = active_reaction;
|
||
(derived.effects ??= []).push(e);
|
||
}
|
||
}
|
||
return effect;
|
||
}
|
||
/**
|
||
* Internal representation of `$effect.tracking()`
|
||
* @returns {boolean}
|
||
*/
|
||
function effect_tracking() {
|
||
return active_reaction !== null && !untracking;
|
||
}
|
||
/**
|
||
* @param {() => void} fn
|
||
*/
|
||
function teardown(fn) {
|
||
const effect = create_effect(8, null);
|
||
set_signal_status(effect, CLEAN);
|
||
effect.teardown = fn;
|
||
return effect;
|
||
}
|
||
/**
|
||
* Internal representation of `$effect(...)`
|
||
* @param {() => void | (() => void)} fn
|
||
*/
|
||
function user_effect(fn) {
|
||
validate_effect("$effect");
|
||
var flags = active_effect.f;
|
||
if (!active_reaction && (flags & 32) !== 0 && component_context !== null && !component_context.i) {
|
||
var context = component_context;
|
||
(context.e ??= []).push(fn);
|
||
} else return create_user_effect(fn);
|
||
}
|
||
/**
|
||
* @param {() => void | (() => void)} fn
|
||
*/
|
||
function create_user_effect(fn) {
|
||
return create_effect(4 | USER_EFFECT, fn);
|
||
}
|
||
/**
|
||
* An effect root whose children can transition out
|
||
* @param {() => void} fn
|
||
* @returns {(options?: { outro?: boolean }) => Promise<void>}
|
||
*/
|
||
function component_root(fn) {
|
||
Batch.ensure();
|
||
const effect = create_effect(64 | EFFECT_PRESERVED, fn);
|
||
return (options = {}) => {
|
||
return new Promise((fulfil) => {
|
||
if (options.outro) pause_effect(effect, () => {
|
||
destroy_effect(effect);
|
||
fulfil(void 0);
|
||
});
|
||
else {
|
||
destroy_effect(effect);
|
||
fulfil(void 0);
|
||
}
|
||
});
|
||
};
|
||
}
|
||
/**
|
||
* @param {() => void | (() => void)} fn
|
||
* @returns {Effect}
|
||
*/
|
||
function effect(fn) {
|
||
return create_effect(4, fn);
|
||
}
|
||
/**
|
||
* @param {() => void | (() => void)} fn
|
||
* @returns {Effect}
|
||
*/
|
||
function async_effect(fn) {
|
||
return create_effect(ASYNC | EFFECT_PRESERVED, fn);
|
||
}
|
||
/**
|
||
* @param {() => void | (() => void)} fn
|
||
* @returns {Effect}
|
||
*/
|
||
function render_effect(fn, flags = 0) {
|
||
return create_effect(8 | flags, fn);
|
||
}
|
||
/**
|
||
* @param {(...expressions: any) => void | (() => void)} fn
|
||
* @param {Array<() => any>} sync
|
||
* @param {Array<() => Promise<any>>} async
|
||
* @param {Blocker[]} blockers
|
||
*/
|
||
function template_effect(fn, sync = [], async = [], blockers = []) {
|
||
flatten(blockers, sync, async, (values) => {
|
||
create_effect(8, () => {
|
||
fn(...values.map(get));
|
||
});
|
||
});
|
||
}
|
||
/**
|
||
* @param {(() => void)} fn
|
||
* @param {number} flags
|
||
*/
|
||
function block(fn, flags = 0) {
|
||
return create_effect(16 | flags, fn);
|
||
}
|
||
/**
|
||
* @param {(() => void)} fn
|
||
*/
|
||
function branch(fn) {
|
||
return create_effect(32 | EFFECT_PRESERVED, fn);
|
||
}
|
||
/**
|
||
* @param {Effect} effect
|
||
*/
|
||
function execute_effect_teardown(effect) {
|
||
var teardown = effect.teardown;
|
||
if (teardown !== null) {
|
||
const previously_destroying_effect = is_destroying_effect;
|
||
const previous_reaction = active_reaction;
|
||
set_is_destroying_effect(true);
|
||
set_active_reaction(null);
|
||
try {
|
||
teardown.call(null);
|
||
} finally {
|
||
set_is_destroying_effect(previously_destroying_effect);
|
||
set_active_reaction(previous_reaction);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* @param {Effect} signal
|
||
* @param {boolean} remove_dom
|
||
* @returns {void}
|
||
*/
|
||
function destroy_effect_children(signal, remove_dom = false) {
|
||
var effect = signal.first;
|
||
signal.first = signal.last = null;
|
||
while (effect !== null) {
|
||
const controller = effect.ac;
|
||
if (controller !== null) without_reactive_context(() => {
|
||
controller.abort(STALE_REACTION);
|
||
});
|
||
var next = effect.next;
|
||
if ((effect.f & 64) !== 0) effect.parent = null;
|
||
else destroy_effect(effect, remove_dom);
|
||
effect = next;
|
||
}
|
||
}
|
||
/**
|
||
* @param {Effect} signal
|
||
* @returns {void}
|
||
*/
|
||
function destroy_block_effect_children(signal) {
|
||
var effect = signal.first;
|
||
while (effect !== null) {
|
||
var next = effect.next;
|
||
if ((effect.f & 32) === 0) destroy_effect(effect);
|
||
effect = next;
|
||
}
|
||
}
|
||
/**
|
||
* @param {Effect} effect
|
||
* @param {boolean} [remove_dom]
|
||
* @returns {void}
|
||
*/
|
||
function destroy_effect(effect, remove_dom = true) {
|
||
var removed = false;
|
||
if ((remove_dom || (effect.f & 262144) !== 0) && effect.nodes !== null && effect.nodes.end !== null) {
|
||
remove_effect_dom(effect.nodes.start, effect.nodes.end);
|
||
removed = true;
|
||
}
|
||
effect.f |= DESTROYING;
|
||
destroy_effect_children(effect, remove_dom && !removed);
|
||
remove_reactions(effect, 0);
|
||
var transitions = effect.nodes && effect.nodes.t;
|
||
if (transitions !== null) for (const transition of transitions) transition.stop();
|
||
execute_effect_teardown(effect);
|
||
effect.f ^= DESTROYING;
|
||
effect.f |= DESTROYED;
|
||
var parent = effect.parent;
|
||
if (parent !== null && parent.first !== null) unlink_effect(effect);
|
||
effect.next = effect.prev = effect.teardown = effect.ctx = effect.deps = effect.fn = effect.nodes = effect.ac = effect.b = null;
|
||
}
|
||
/**
|
||
*
|
||
* @param {TemplateNode | null} node
|
||
* @param {TemplateNode} end
|
||
*/
|
||
function remove_effect_dom(node, end) {
|
||
while (node !== null) {
|
||
/** @type {TemplateNode | null} */
|
||
var next = node === end ? null : /* @__PURE__ */ get_next_sibling(node);
|
||
node.remove();
|
||
node = next;
|
||
}
|
||
}
|
||
/**
|
||
* Detach an effect from the effect tree, freeing up memory and
|
||
* reducing the amount of work that happens on subsequent traversals
|
||
* @param {Effect} effect
|
||
*/
|
||
function unlink_effect(effect) {
|
||
var parent = effect.parent;
|
||
var prev = effect.prev;
|
||
var next = effect.next;
|
||
if (prev !== null) prev.next = next;
|
||
if (next !== null) next.prev = prev;
|
||
if (parent !== null) {
|
||
if (parent.first === effect) parent.first = next;
|
||
if (parent.last === effect) parent.last = prev;
|
||
}
|
||
}
|
||
/**
|
||
* When a block effect is removed, we don't immediately destroy it or yank it
|
||
* out of the DOM, because it might have transitions. Instead, we 'pause' it.
|
||
* It stays around (in memory, and in the DOM) until outro transitions have
|
||
* completed, and if the state change is reversed then we _resume_ it.
|
||
* A paused effect does not update, and the DOM subtree becomes inert.
|
||
* @param {Effect} effect
|
||
* @param {() => void} [callback]
|
||
* @param {boolean} [destroy]
|
||
*/
|
||
function pause_effect(effect, callback, destroy = true) {
|
||
/** @type {TransitionManager[]} */
|
||
var transitions = [];
|
||
pause_children(effect, transitions, true);
|
||
var fn = () => {
|
||
if (destroy) destroy_effect(effect);
|
||
if (callback) callback();
|
||
};
|
||
var remaining = transitions.length;
|
||
if (remaining > 0) {
|
||
var check = () => --remaining || fn();
|
||
for (var transition of transitions) transition.out(check);
|
||
} else fn();
|
||
}
|
||
/**
|
||
* @param {Effect} effect
|
||
* @param {TransitionManager[]} transitions
|
||
* @param {boolean} local
|
||
*/
|
||
function pause_children(effect, transitions, local) {
|
||
if ((effect.f & 8192) !== 0) return;
|
||
effect.f ^= INERT;
|
||
var t = effect.nodes && effect.nodes.t;
|
||
if (t !== null) {
|
||
for (const transition of t) if (transition.is_global || local) transitions.push(transition);
|
||
}
|
||
var child = effect.first;
|
||
while (child !== null) {
|
||
var sibling = child.next;
|
||
if ((child.f & 64) === 0) {
|
||
var transparent = (child.f & 65536) !== 0 || (child.f & 32) !== 0 && (effect.f & 16) !== 0;
|
||
pause_children(child, transitions, transparent ? local : false);
|
||
}
|
||
child = sibling;
|
||
}
|
||
}
|
||
/**
|
||
* The opposite of `pause_effect`. We call this if (for example)
|
||
* `x` becomes falsy then truthy: `{#if x}...{/if}`
|
||
* @param {Effect} effect
|
||
*/
|
||
function resume_effect(effect) {
|
||
resume_children(effect, true);
|
||
}
|
||
/**
|
||
* @param {Effect} effect
|
||
* @param {boolean} local
|
||
*/
|
||
function resume_children(effect, local) {
|
||
if ((effect.f & 8192) === 0) return;
|
||
effect.f ^= INERT;
|
||
if ((effect.f & 1024) === 0) {
|
||
set_signal_status(effect, DIRTY);
|
||
Batch.ensure().schedule(effect);
|
||
}
|
||
var child = effect.first;
|
||
while (child !== null) {
|
||
var sibling = child.next;
|
||
var transparent = (child.f & 65536) !== 0 || (child.f & 32) !== 0;
|
||
resume_children(child, transparent ? local : false);
|
||
child = sibling;
|
||
}
|
||
var t = effect.nodes && effect.nodes.t;
|
||
if (t !== null) {
|
||
for (const transition of t) if (transition.is_global || local) transition.in();
|
||
}
|
||
}
|
||
/**
|
||
* @param {Effect} effect
|
||
* @param {DocumentFragment} fragment
|
||
*/
|
||
function move_effect(effect, fragment) {
|
||
if (!effect.nodes) return;
|
||
/** @type {TemplateNode | null} */
|
||
var node = effect.nodes.start;
|
||
var end = effect.nodes.end;
|
||
while (node !== null) {
|
||
/** @type {TemplateNode | null} */
|
||
var next = node === end ? null : /* @__PURE__ */ get_next_sibling(node);
|
||
fragment.append(node);
|
||
node = next;
|
||
}
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/legacy.js
|
||
/**
|
||
* @type {Set<Value> | null}
|
||
* @deprecated
|
||
*/
|
||
var captured_signals = null;
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/runtime.js
|
||
/** @import { Derived, Effect, Reaction, Source, Value } from '#client' */
|
||
/**
|
||
* True if updating in an effect context that is reactive (i.e. not branch/root effects)
|
||
*/
|
||
var is_updating_effect = false;
|
||
var is_destroying_effect = false;
|
||
/** @param {boolean} value */
|
||
function set_is_destroying_effect(value) {
|
||
is_destroying_effect = value;
|
||
}
|
||
/** @type {null | Reaction} */
|
||
var active_reaction = null;
|
||
var untracking = false;
|
||
/** @param {null | Reaction} reaction */
|
||
function set_active_reaction(reaction) {
|
||
active_reaction = reaction;
|
||
}
|
||
/** @type {null | Effect} */
|
||
var active_effect = null;
|
||
/** @param {null | Effect} effect */
|
||
function set_active_effect(effect) {
|
||
active_effect = effect;
|
||
}
|
||
/**
|
||
* When sources are created within a reaction, reading and writing
|
||
* them within that reaction should not cause a re-run
|
||
* @type {null | Set<Source>}
|
||
*/
|
||
var current_sources = null;
|
||
/** @param {Value} value */
|
||
function push_reaction_value(value) {
|
||
if (active_reaction !== null && (!async_mode_flag || (active_reaction.f & 2) !== 0)) (current_sources ??= /* @__PURE__ */ new Set()).add(value);
|
||
}
|
||
/**
|
||
* The dependencies of the reaction that is currently being executed. In many cases,
|
||
* the dependencies are unchanged between runs, and so this will be `null` unless
|
||
* and until a new dependency is accessed — we track this via `skipped_deps`
|
||
* @type {null | Value[]}
|
||
*/
|
||
var new_deps = null;
|
||
var skipped_deps = 0;
|
||
/**
|
||
* Tracks writes that the effect it's executed in doesn't listen to yet,
|
||
* so that the dependency can be added to the effect later on if it then reads it
|
||
* @type {null | Source[]}
|
||
*/
|
||
var untracked_writes = null;
|
||
/** @param {null | Source[]} value */
|
||
function set_untracked_writes(value) {
|
||
untracked_writes = value;
|
||
}
|
||
/**
|
||
* @type {number} Used by sources and deriveds for handling updates.
|
||
* Version starts from 1 so that unowned deriveds differentiate between a created effect and a run one for tracing
|
||
**/
|
||
var write_version = 1;
|
||
/** @type {number} Used to version each read of a source of derived to avoid duplicating depedencies inside a reaction */
|
||
var read_version = 0;
|
||
var update_version = read_version;
|
||
/** @param {number} value */
|
||
function set_update_version(value) {
|
||
update_version = value;
|
||
}
|
||
function increment_write_version() {
|
||
return ++write_version;
|
||
}
|
||
/**
|
||
* Determines whether a derived or effect is dirty.
|
||
* If it is MAYBE_DIRTY, will set the status to CLEAN
|
||
* @param {Reaction} reaction
|
||
* @returns {boolean}
|
||
*/
|
||
function is_dirty(reaction) {
|
||
var flags = reaction.f;
|
||
if ((flags & 2048) !== 0) return true;
|
||
if (flags & 2) reaction.f &= ~WAS_MARKED;
|
||
if ((flags & 4096) !== 0) {
|
||
var dependencies = reaction.deps;
|
||
var length = dependencies.length;
|
||
for (var i = 0; i < length; i++) {
|
||
var dependency = dependencies[i];
|
||
if (is_dirty(dependency)) update_derived(dependency);
|
||
if (dependency.wv > reaction.wv) return true;
|
||
}
|
||
if ((flags & 512) !== 0 && batch_values === null) set_signal_status(reaction, CLEAN);
|
||
}
|
||
return false;
|
||
}
|
||
/**
|
||
* @param {Value} signal
|
||
* @param {Effect} effect
|
||
* @param {boolean} [root]
|
||
*/
|
||
function schedule_possible_effect_self_invalidation(signal, effect, root = true) {
|
||
var reactions = signal.reactions;
|
||
if (reactions === null) return;
|
||
if (!async_mode_flag && current_sources !== null && current_sources.has(signal)) return;
|
||
for (var i = 0; i < reactions.length; i++) {
|
||
var reaction = reactions[i];
|
||
if ((reaction.f & 2) !== 0) schedule_possible_effect_self_invalidation(reaction, effect, false);
|
||
else if (effect === reaction) {
|
||
if (root) set_signal_status(reaction, DIRTY);
|
||
else if ((reaction.f & 1024) !== 0) set_signal_status(reaction, MAYBE_DIRTY);
|
||
schedule_effect(reaction);
|
||
}
|
||
}
|
||
}
|
||
/** @param {Reaction} reaction */
|
||
function update_reaction(reaction) {
|
||
var previous_deps = new_deps;
|
||
var previous_skipped_deps = skipped_deps;
|
||
var previous_untracked_writes = untracked_writes;
|
||
var previous_reaction = active_reaction;
|
||
var previous_sources = current_sources;
|
||
var previous_component_context = component_context;
|
||
var previous_untracking = untracking;
|
||
var previous_update_version = update_version;
|
||
var flags = reaction.f;
|
||
new_deps = null;
|
||
skipped_deps = 0;
|
||
untracked_writes = null;
|
||
active_reaction = (flags & 96) === 0 ? reaction : null;
|
||
current_sources = null;
|
||
set_component_context(reaction.ctx);
|
||
untracking = false;
|
||
update_version = ++read_version;
|
||
if (reaction.ac !== null) {
|
||
without_reactive_context(() => {
|
||
/** @type {AbortController} */ reaction.ac.abort(STALE_REACTION);
|
||
});
|
||
reaction.ac = null;
|
||
}
|
||
try {
|
||
reaction.f |= REACTION_IS_UPDATING;
|
||
var fn = reaction.fn;
|
||
var result = fn();
|
||
reaction.f |= REACTION_RAN;
|
||
var deps = reaction.deps;
|
||
var is_fork = current_batch?.is_fork;
|
||
if (new_deps !== null) {
|
||
var i;
|
||
if (!is_fork) remove_reactions(reaction, skipped_deps);
|
||
if (deps !== null && skipped_deps > 0) {
|
||
deps.length = skipped_deps + new_deps.length;
|
||
for (i = 0; i < new_deps.length; i++) deps[skipped_deps + i] = new_deps[i];
|
||
} else reaction.deps = deps = new_deps;
|
||
if (effect_tracking() && (reaction.f & 512) !== 0) for (i = skipped_deps; i < deps.length; i++) (deps[i].reactions ??= []).push(reaction);
|
||
} else if (!is_fork && deps !== null && skipped_deps < deps.length) {
|
||
remove_reactions(reaction, skipped_deps);
|
||
deps.length = skipped_deps;
|
||
}
|
||
if (is_runes() && untracked_writes !== null && !untracking && deps !== null && (reaction.f & 6146) === 0) for (i = 0; i < untracked_writes.length; i++) schedule_possible_effect_self_invalidation(untracked_writes[i], reaction);
|
||
if (previous_reaction !== null && previous_reaction !== reaction) {
|
||
read_version++;
|
||
if (previous_reaction.deps !== null) for (let i = 0; i < previous_skipped_deps; i += 1) previous_reaction.deps[i].rv = read_version;
|
||
if (previous_deps !== null) for (const dep of previous_deps) dep.rv = read_version;
|
||
if (untracked_writes !== null) if (previous_untracked_writes === null) previous_untracked_writes = untracked_writes;
|
||
else previous_untracked_writes.push(...untracked_writes);
|
||
}
|
||
if ((reaction.f & 8388608) !== 0) reaction.f ^= ERROR_VALUE;
|
||
return result;
|
||
} catch (error) {
|
||
return handle_error(error);
|
||
} finally {
|
||
reaction.f ^= REACTION_IS_UPDATING;
|
||
new_deps = previous_deps;
|
||
skipped_deps = previous_skipped_deps;
|
||
untracked_writes = previous_untracked_writes;
|
||
active_reaction = previous_reaction;
|
||
current_sources = previous_sources;
|
||
set_component_context(previous_component_context);
|
||
untracking = previous_untracking;
|
||
update_version = previous_update_version;
|
||
}
|
||
}
|
||
/**
|
||
* @template V
|
||
* @param {Reaction} signal
|
||
* @param {Value<V>} dependency
|
||
* @returns {void}
|
||
*/
|
||
function remove_reaction(signal, dependency) {
|
||
let reactions = dependency.reactions;
|
||
if (reactions !== null) {
|
||
var index = index_of.call(reactions, signal);
|
||
if (index !== -1) {
|
||
var new_length = reactions.length - 1;
|
||
if (new_length === 0) reactions = dependency.reactions = null;
|
||
else {
|
||
reactions[index] = reactions[new_length];
|
||
reactions.pop();
|
||
}
|
||
}
|
||
}
|
||
if (reactions === null && (dependency.f & 2) !== 0 && (new_deps === null || !includes.call(new_deps, dependency))) {
|
||
var derived = dependency;
|
||
if ((derived.f & 512) !== 0) {
|
||
derived.f ^= 512;
|
||
derived.f &= ~WAS_MARKED;
|
||
}
|
||
if (derived.v !== UNINITIALIZED) update_derived_status(derived);
|
||
if (derived.ac !== null) without_reactive_context(() => {
|
||
/** @type {AbortController} */ derived.ac.abort(STALE_REACTION);
|
||
derived.ac = null;
|
||
set_signal_status(derived, DIRTY);
|
||
});
|
||
freeze_derived_effects(derived);
|
||
remove_reactions(derived, 0);
|
||
}
|
||
}
|
||
/**
|
||
* @param {Reaction} signal
|
||
* @param {number} start_index
|
||
* @returns {void}
|
||
*/
|
||
function remove_reactions(signal, start_index) {
|
||
var dependencies = signal.deps;
|
||
if (dependencies === null) return;
|
||
for (var i = start_index; i < dependencies.length; i++) remove_reaction(signal, dependencies[i]);
|
||
}
|
||
/**
|
||
* @param {Effect} effect
|
||
* @returns {void}
|
||
*/
|
||
function update_effect(effect) {
|
||
var flags = effect.f;
|
||
if ((flags & 16384) !== 0) return;
|
||
set_signal_status(effect, CLEAN);
|
||
var previous_effect = active_effect;
|
||
var was_updating_effect = is_updating_effect;
|
||
active_effect = effect;
|
||
is_updating_effect = (flags & 96) === 0;
|
||
try {
|
||
if ((flags & 16777232) !== 0) destroy_block_effect_children(effect);
|
||
else destroy_effect_children(effect);
|
||
execute_effect_teardown(effect);
|
||
var teardown = update_reaction(effect);
|
||
effect.teardown = typeof teardown === "function" ? teardown : null;
|
||
effect.wv = write_version;
|
||
} finally {
|
||
is_updating_effect = was_updating_effect;
|
||
active_effect = previous_effect;
|
||
}
|
||
}
|
||
/**
|
||
* @template V
|
||
* @param {Value<V>} signal
|
||
* @returns {V}
|
||
*/
|
||
function get(signal) {
|
||
var is_derived = (signal.f & 2) !== 0;
|
||
captured_signals?.add(signal);
|
||
if (active_reaction !== null && !untracking) {
|
||
if (!(active_effect !== null && (active_effect.f & 16384) !== 0) && (current_sources === null || !current_sources.has(signal))) {
|
||
var deps = active_reaction.deps;
|
||
if ((active_reaction.f & 2097152) !== 0) {
|
||
if (signal.rv < read_version) {
|
||
signal.rv = read_version;
|
||
if (new_deps === null && deps !== null && deps[skipped_deps] === signal) skipped_deps++;
|
||
else if (new_deps === null) new_deps = [signal];
|
||
else new_deps.push(signal);
|
||
}
|
||
} else {
|
||
active_reaction.deps ??= [];
|
||
if (!includes.call(active_reaction.deps, signal)) active_reaction.deps.push(signal);
|
||
var reactions = signal.reactions;
|
||
if (reactions === null) signal.reactions = [active_reaction];
|
||
else if (!includes.call(reactions, active_reaction)) reactions.push(active_reaction);
|
||
}
|
||
}
|
||
}
|
||
if (is_destroying_effect && old_values.has(signal)) return old_values.get(signal);
|
||
if (is_derived) {
|
||
var derived = signal;
|
||
if (is_destroying_effect) {
|
||
var value = derived.v;
|
||
if ((derived.f & 1024) === 0 && derived.reactions !== null || depends_on_old_values(derived)) value = execute_derived(derived);
|
||
old_values.set(derived, value);
|
||
return value;
|
||
}
|
||
var should_connect = (derived.f & 512) === 0 && !untracking && active_reaction !== null && (is_updating_effect || (active_reaction.f & 512) !== 0);
|
||
var is_new = (derived.f & REACTION_RAN) === 0;
|
||
if (is_dirty(derived)) {
|
||
if (should_connect) derived.f |= 512;
|
||
update_derived(derived);
|
||
}
|
||
if (should_connect && !is_new) {
|
||
unfreeze_derived_effects(derived);
|
||
reconnect(derived);
|
||
}
|
||
}
|
||
if (batch_values?.has(signal)) return batch_values.get(signal);
|
||
if ((signal.f & 8388608) !== 0) throw signal.v;
|
||
return signal.v;
|
||
}
|
||
/**
|
||
* (Re)connect a disconnected derived, so that it is notified
|
||
* of changes in `mark_reactions`
|
||
* @param {Derived} derived
|
||
*/
|
||
function reconnect(derived) {
|
||
derived.f |= 512;
|
||
if (derived.deps === null) return;
|
||
for (const dep of derived.deps) {
|
||
(dep.reactions ??= []).push(derived);
|
||
if ((dep.f & 2) !== 0 && (dep.f & 512) === 0) {
|
||
unfreeze_derived_effects(dep);
|
||
reconnect(dep);
|
||
}
|
||
}
|
||
}
|
||
/** @param {Derived} derived */
|
||
function depends_on_old_values(derived) {
|
||
if (derived.v === UNINITIALIZED) return true;
|
||
if (derived.deps === null) return false;
|
||
for (const dep of derived.deps) {
|
||
if (old_values.has(dep)) return true;
|
||
if ((dep.f & 2) !== 0 && depends_on_old_values(dep)) return true;
|
||
}
|
||
return false;
|
||
}
|
||
/**
|
||
* When used inside a [`$derived`](https://svelte.dev/docs/svelte/$derived) or [`$effect`](https://svelte.dev/docs/svelte/$effect),
|
||
* any state read inside `fn` will not be treated as a dependency.
|
||
*
|
||
* ```ts
|
||
* $effect(() => {
|
||
* // this will run when `data` changes, but not when `time` changes
|
||
* save(data, {
|
||
* timestamp: untrack(() => time)
|
||
* });
|
||
* });
|
||
* ```
|
||
* @template T
|
||
* @param {() => T} fn
|
||
* @returns {T}
|
||
*/
|
||
function untrack(fn) {
|
||
var previous_untracking = untracking;
|
||
try {
|
||
untracking = true;
|
||
return fn();
|
||
} finally {
|
||
untracking = previous_untracking;
|
||
}
|
||
}
|
||
/**
|
||
* Possibly traverse an object and read all its properties so that they're all reactive in case this is `$state`.
|
||
* Does only check first level of an object for performance reasons (heuristic should be good for 99% of all cases).
|
||
* @param {any} value
|
||
* @returns {void}
|
||
*/
|
||
function deep_read_state(value) {
|
||
if (typeof value !== "object" || !value || value instanceof EventTarget) return;
|
||
if (STATE_SYMBOL in value) deep_read(value);
|
||
else if (!Array.isArray(value)) for (let key in value) {
|
||
const prop = value[key];
|
||
if (typeof prop === "object" && prop && STATE_SYMBOL in prop) deep_read(prop);
|
||
}
|
||
}
|
||
/**
|
||
* Deeply traverse an object and read all its properties
|
||
* so that they're all reactive in case this is `$state`
|
||
* @param {any} value
|
||
* @param {Set<any>} visited
|
||
* @returns {void}
|
||
*/
|
||
function deep_read(value, visited = /* @__PURE__ */ new Set()) {
|
||
if (typeof value === "object" && value !== null && !(value instanceof EventTarget) && !visited.has(value)) {
|
||
visited.add(value);
|
||
if (value instanceof Date) value.getTime();
|
||
for (let key in value) try {
|
||
deep_read(value[key], visited);
|
||
} catch (e) {}
|
||
const proto = get_prototype_of(value);
|
||
if (proto !== Object.prototype && proto !== Array.prototype && proto !== Map.prototype && proto !== Set.prototype && proto !== Date.prototype) {
|
||
const descriptors = get_descriptors(proto);
|
||
for (let key in descriptors) {
|
||
const get = descriptors[key].get;
|
||
if (get) try {
|
||
get.call(value);
|
||
} catch (e) {}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* Subset of delegated events which should be passive by default.
|
||
* These two are already passive via browser defaults on window, document and body.
|
||
* But since
|
||
* - we're delegating them
|
||
* - they happen often
|
||
* - they apply to mobile which is generally less performant
|
||
* we're marking them as passive by default for other elements, too.
|
||
*/
|
||
var PASSIVE_EVENTS = ["touchstart", "touchmove"];
|
||
/**
|
||
* Returns `true` if `name` is a passive event
|
||
* @param {string} name
|
||
*/
|
||
function is_passive_event(name) {
|
||
return PASSIVE_EVENTS.includes(name);
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/elements/events.js
|
||
/**
|
||
* Used on elements, as a map of event type -> event handler,
|
||
* and on events themselves to track which element handled an event
|
||
*/
|
||
var event_symbol = Symbol("events");
|
||
/** @type {Set<string>} */
|
||
var all_registered_events = /* @__PURE__ */ new Set();
|
||
/** @type {Set<(events: Array<string>) => void>} */
|
||
var root_event_handles = /* @__PURE__ */ new Set();
|
||
/**
|
||
* @param {string} event_name
|
||
* @param {EventTarget} dom
|
||
* @param {EventListener} [handler]
|
||
* @param {AddEventListenerOptions} [options]
|
||
*/
|
||
function create_event(event_name, dom, handler, options = {}) {
|
||
/**
|
||
* @this {EventTarget}
|
||
*/
|
||
function target_handler(event) {
|
||
if (!options.capture) handle_event_propagation.call(dom, event);
|
||
if (!event.cancelBubble) return without_reactive_context(() => {
|
||
return handler?.call(this, event);
|
||
});
|
||
}
|
||
if (event_name.startsWith("pointer") || event_name.startsWith("touch") || event_name === "wheel") queue_micro_task(() => {
|
||
dom.addEventListener(event_name, target_handler, options);
|
||
});
|
||
else dom.addEventListener(event_name, target_handler, options);
|
||
return target_handler;
|
||
}
|
||
/**
|
||
* @param {string} event_name
|
||
* @param {Element} dom
|
||
* @param {EventListener} [handler]
|
||
* @param {boolean} [capture]
|
||
* @param {boolean} [passive]
|
||
* @returns {void}
|
||
*/
|
||
function event(event_name, dom, handler, capture, passive) {
|
||
var options = {
|
||
capture,
|
||
passive
|
||
};
|
||
var target_handler = create_event(event_name, dom, handler, options);
|
||
if (dom === document.body || dom === window || dom === document || dom instanceof HTMLMediaElement) teardown(() => {
|
||
dom.removeEventListener(event_name, target_handler, options);
|
||
});
|
||
}
|
||
/**
|
||
* @param {string} event_name
|
||
* @param {Element} element
|
||
* @param {EventListener} [handler]
|
||
* @returns {void}
|
||
*/
|
||
function delegated(event_name, element, handler) {
|
||
(element[event_symbol] ??= {})[event_name] = handler;
|
||
}
|
||
/**
|
||
* @param {Array<string>} events
|
||
* @returns {void}
|
||
*/
|
||
function delegate(events) {
|
||
for (var i = 0; i < events.length; i++) all_registered_events.add(events[i]);
|
||
for (var fn of root_event_handles) fn(events);
|
||
}
|
||
var last_propagated_event = null;
|
||
/**
|
||
* @this {EventTarget}
|
||
* @param {Event} event
|
||
* @returns {void}
|
||
*/
|
||
function handle_event_propagation(event) {
|
||
var handler_element = this;
|
||
var owner_document = handler_element.ownerDocument;
|
||
var event_name = event.type;
|
||
var path = event.composedPath?.() || [];
|
||
var current_target = path[0] || event.target;
|
||
last_propagated_event = event;
|
||
var path_idx = 0;
|
||
var handled_at = last_propagated_event === event && event[event_symbol];
|
||
if (handled_at) {
|
||
var at_idx = path.indexOf(handled_at);
|
||
if (at_idx !== -1 && (handler_element === document || handler_element === window)) {
|
||
event[event_symbol] = handler_element;
|
||
return;
|
||
}
|
||
var handler_idx = path.indexOf(handler_element);
|
||
if (handler_idx === -1) return;
|
||
if (at_idx <= handler_idx) path_idx = at_idx;
|
||
}
|
||
current_target = path[path_idx] || event.target;
|
||
if (current_target === handler_element) return;
|
||
define_property(event, "currentTarget", {
|
||
configurable: true,
|
||
get() {
|
||
return current_target || owner_document;
|
||
}
|
||
});
|
||
var previous_reaction = active_reaction;
|
||
var previous_effect = active_effect;
|
||
set_active_reaction(null);
|
||
set_active_effect(null);
|
||
try {
|
||
/**
|
||
* @type {unknown}
|
||
*/
|
||
var throw_error;
|
||
/**
|
||
* @type {unknown[]}
|
||
*/
|
||
var other_errors = [];
|
||
while (current_target !== null) {
|
||
if (current_target === handler_element) break;
|
||
try {
|
||
var delegated = current_target[event_symbol]?.[event_name];
|
||
if (delegated != null && (!current_target.disabled || event.target === current_target)) delegated.call(current_target, event);
|
||
} catch (error) {
|
||
if (throw_error) other_errors.push(error);
|
||
else throw_error = error;
|
||
}
|
||
if (event.cancelBubble) break;
|
||
path_idx++;
|
||
current_target = path_idx < path.length ? path[path_idx] : null;
|
||
}
|
||
if (throw_error) {
|
||
for (let error of other_errors) queueMicrotask(() => {
|
||
throw error;
|
||
});
|
||
throw throw_error;
|
||
}
|
||
} finally {
|
||
event[event_symbol] = handler_element;
|
||
delete event.currentTarget;
|
||
set_active_reaction(previous_reaction);
|
||
set_active_effect(previous_effect);
|
||
}
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/reconciler.js
|
||
var policy = globalThis?.window?.trustedTypes && /* @__PURE__ */ globalThis.window.trustedTypes.createPolicy("svelte-trusted-html", {
|
||
/** @param {string} html */
|
||
createHTML: (html) => {
|
||
return html;
|
||
} });
|
||
/** @param {string} html */
|
||
function create_trusted_html(html) {
|
||
return policy?.createHTML(html) ?? html;
|
||
}
|
||
/**
|
||
* @param {string} html
|
||
*/
|
||
function create_fragment_from_html(html) {
|
||
var elem = create_element("template");
|
||
elem.innerHTML = create_trusted_html(html.replaceAll("<!>", "\x3C!---->"));
|
||
return elem.content;
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/template.js
|
||
/** @import { Effect, EffectNodes, TemplateNode } from '#client' */
|
||
/** @import { TemplateStructure } from './types' */
|
||
/**
|
||
* @param {TemplateNode} start
|
||
* @param {TemplateNode | null} end
|
||
*/
|
||
function assign_nodes(start, end) {
|
||
var effect = active_effect;
|
||
if (effect.nodes === null) effect.nodes = {
|
||
start,
|
||
end,
|
||
a: null,
|
||
t: null
|
||
};
|
||
}
|
||
/**
|
||
* @param {string} content
|
||
* @param {number} flags
|
||
* @returns {() => Node | Node[]}
|
||
*/
|
||
/*#__NO_SIDE_EFFECTS__*/
|
||
function from_html(content, flags) {
|
||
var is_fragment = (flags & 1) !== 0;
|
||
var use_import_node = (flags & 2) !== 0;
|
||
/** @type {Node} */
|
||
var node;
|
||
/**
|
||
* Whether or not the first item is a text/element node. If not, we need to
|
||
* create an additional comment node to act as `effect.nodes.start`
|
||
*/
|
||
var has_start = !content.startsWith("<!>");
|
||
return () => {
|
||
if (hydrating) {
|
||
assign_nodes(hydrate_node, null);
|
||
return hydrate_node;
|
||
}
|
||
if (node === void 0) {
|
||
node = create_fragment_from_html(has_start ? content : "<!>" + content);
|
||
if (!is_fragment) node = /* @__PURE__ */ get_first_child(node);
|
||
}
|
||
var clone = use_import_node || is_firefox ? document.importNode(node, true) : node.cloneNode(true);
|
||
if (is_fragment) {
|
||
var start = /* @__PURE__ */ get_first_child(clone);
|
||
var end = clone.lastChild;
|
||
assign_nodes(start, end);
|
||
} else assign_nodes(clone, clone);
|
||
return clone;
|
||
};
|
||
}
|
||
/**
|
||
* @returns {TemplateNode | DocumentFragment}
|
||
*/
|
||
function comment() {
|
||
if (hydrating) {
|
||
assign_nodes(hydrate_node, null);
|
||
return hydrate_node;
|
||
}
|
||
var frag = document.createDocumentFragment();
|
||
var start = document.createComment("");
|
||
var anchor = create_text();
|
||
frag.append(start, anchor);
|
||
assign_nodes(start, anchor);
|
||
return frag;
|
||
}
|
||
/**
|
||
* Assign the created (or in hydration mode, traversed) dom elements to the current block
|
||
* and insert the elements into the dom (in client mode).
|
||
* @param {Text | Comment | Element} anchor
|
||
* @param {DocumentFragment | Element} dom
|
||
*/
|
||
function append(anchor, dom) {
|
||
if (hydrating) {
|
||
var effect = active_effect;
|
||
if ((effect.f & 32768) === 0 || effect.nodes.end === null) effect.nodes.end = hydrate_node;
|
||
hydrate_next();
|
||
return;
|
||
}
|
||
if (anchor === null) return;
|
||
anchor.before(dom);
|
||
}
|
||
/**
|
||
* @param {Element} text
|
||
* @param {string} value
|
||
* @returns {void}
|
||
*/
|
||
function set_text(text, value) {
|
||
var str = value == null ? "" : typeof value === "object" ? `${value}` : value;
|
||
if (str !== (text[TEXT_CACHE] ??= text.nodeValue)) {
|
||
/** @type {any} */ text[TEXT_CACHE] = str;
|
||
text.nodeValue = `${str}`;
|
||
}
|
||
}
|
||
/**
|
||
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component.
|
||
* Transitions will play during the initial render unless the `intro` option is set to `false`.
|
||
*
|
||
* @template {Record<string, any>} Props
|
||
* @template {Record<string, any>} Exports
|
||
* @param {ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>} component
|
||
* @param {MountOptions<Props>} options
|
||
* @returns {Exports}
|
||
*/
|
||
function mount(component, options) {
|
||
return _mount(component, options);
|
||
}
|
||
/** @type {Map<EventTarget, Map<string, number>>} */
|
||
var listeners = /* @__PURE__ */ new Map();
|
||
/**
|
||
* @template {Record<string, any>} Exports
|
||
* @param {ComponentType<SvelteComponent<any>> | Component<any>} Component
|
||
* @param {MountOptions} options
|
||
* @returns {Exports}
|
||
*/
|
||
function _mount(Component, { target, anchor, props = {}, events, context, intro = true, transformError }) {
|
||
init_operations();
|
||
/** @type {Exports} */
|
||
var component = void 0;
|
||
var unmount = component_root(() => {
|
||
var anchor_node = anchor ?? target.appendChild(create_text());
|
||
boundary(anchor_node, { pending: () => {} }, (anchor_node) => {
|
||
push({});
|
||
var ctx = component_context;
|
||
if (context) ctx.c = context;
|
||
if (events)
|
||
/** @type {any} */ props.$$events = events;
|
||
if (hydrating) assign_nodes(anchor_node, null);
|
||
component = Component(anchor_node, props) || {};
|
||
if (hydrating) {
|
||
/** @type {Effect & { nodes: EffectNodes }} */ active_effect.nodes.end = hydrate_node;
|
||
if (hydrate_node === null || hydrate_node.nodeType !== 8 || hydrate_node.data !== "]") {
|
||
hydration_mismatch();
|
||
throw HYDRATION_ERROR;
|
||
}
|
||
}
|
||
pop();
|
||
}, transformError);
|
||
/** @type {Set<string>} */
|
||
var registered_events = /* @__PURE__ */ new Set();
|
||
/** @param {Array<string>} events */
|
||
var event_handle = (events) => {
|
||
for (var i = 0; i < events.length; i++) {
|
||
var event_name = events[i];
|
||
if (registered_events.has(event_name)) continue;
|
||
registered_events.add(event_name);
|
||
var passive = is_passive_event(event_name);
|
||
for (const node of [target, document]) {
|
||
var counts = listeners.get(node);
|
||
if (counts === void 0) {
|
||
counts = /* @__PURE__ */ new Map();
|
||
listeners.set(node, counts);
|
||
}
|
||
var count = counts.get(event_name);
|
||
if (count === void 0) {
|
||
node.addEventListener(event_name, handle_event_propagation, { passive });
|
||
counts.set(event_name, 1);
|
||
} else counts.set(event_name, count + 1);
|
||
}
|
||
}
|
||
};
|
||
event_handle(array_from(all_registered_events));
|
||
root_event_handles.add(event_handle);
|
||
return () => {
|
||
for (var event_name of registered_events) for (const node of [target, document]) {
|
||
var counts = listeners.get(node);
|
||
var count = counts.get(event_name);
|
||
if (--count == 0) {
|
||
node.removeEventListener(event_name, handle_event_propagation);
|
||
counts.delete(event_name);
|
||
if (counts.size === 0) listeners.delete(node);
|
||
} else counts.set(event_name, count);
|
||
}
|
||
root_event_handles.delete(event_handle);
|
||
if (anchor_node !== anchor) anchor_node.parentNode?.removeChild(anchor_node);
|
||
};
|
||
});
|
||
mounted_components.set(component, unmount);
|
||
return component;
|
||
}
|
||
/**
|
||
* References of the components that were mounted or hydrated.
|
||
* Uses a `WeakMap` to avoid memory leaks.
|
||
*/
|
||
var mounted_components = /* @__PURE__ */ new WeakMap();
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/blocks/branches.js
|
||
/** @import { Effect, TemplateNode } from '#client' */
|
||
/**
|
||
* @typedef {{ effect: Effect, fragment: DocumentFragment }} Branch
|
||
*/
|
||
/**
|
||
* @template Key
|
||
*/
|
||
var BranchManager = class {
|
||
/** @type {TemplateNode} */
|
||
anchor;
|
||
/** @type {Map<Batch, Key>} */
|
||
#batches = /* @__PURE__ */ new Map();
|
||
/**
|
||
* Map of keys to effects that are currently rendered in the DOM.
|
||
* These effects are visible and actively part of the document tree.
|
||
* Example:
|
||
* ```
|
||
* {#if condition}
|
||
* foo
|
||
* {:else}
|
||
* bar
|
||
* {/if}
|
||
* ```
|
||
* Can result in the entries `true->Effect` and `false->Effect`
|
||
* @type {Map<Key, Effect>}
|
||
*/
|
||
#onscreen = /* @__PURE__ */ new Map();
|
||
/**
|
||
* Similar to #onscreen with respect to the keys, but contains branches that are not yet
|
||
* in the DOM, because their insertion is deferred.
|
||
* @type {Map<Key, Branch>}
|
||
*/
|
||
#offscreen = /* @__PURE__ */ new Map();
|
||
/**
|
||
* Keys of effects that are currently outroing
|
||
* @type {Set<Key>}
|
||
*/
|
||
#outroing = /* @__PURE__ */ new Set();
|
||
/**
|
||
* Whether to pause (i.e. outro) on change, or destroy immediately.
|
||
* This is necessary for `<svelte:element>`
|
||
*/
|
||
#transition = true;
|
||
/**
|
||
* @param {TemplateNode} anchor
|
||
* @param {boolean} transition
|
||
*/
|
||
constructor(anchor, transition = true) {
|
||
this.anchor = anchor;
|
||
this.#transition = transition;
|
||
}
|
||
/**
|
||
* @param {Batch} batch
|
||
*/
|
||
#commit = (batch) => {
|
||
if (!this.#batches.has(batch)) return;
|
||
var key = this.#batches.get(batch);
|
||
var onscreen = this.#onscreen.get(key);
|
||
if (onscreen) {
|
||
resume_effect(onscreen);
|
||
this.#outroing.delete(key);
|
||
} else {
|
||
var offscreen = this.#offscreen.get(key);
|
||
if (offscreen) {
|
||
resume_effect(offscreen.effect);
|
||
this.#onscreen.set(key, offscreen.effect);
|
||
this.#offscreen.delete(key);
|
||
/** @type {TemplateNode} */ offscreen.fragment.lastChild.remove();
|
||
this.anchor.before(offscreen.fragment);
|
||
onscreen = offscreen.effect;
|
||
}
|
||
}
|
||
for (const [b, k] of this.#batches) {
|
||
this.#batches.delete(b);
|
||
if (b === batch) break;
|
||
const offscreen = this.#offscreen.get(k);
|
||
if (offscreen) {
|
||
destroy_effect(offscreen.effect);
|
||
this.#offscreen.delete(k);
|
||
}
|
||
}
|
||
for (const [k, effect] of this.#onscreen) {
|
||
if (k === key || this.#outroing.has(k)) continue;
|
||
const on_destroy = () => {
|
||
if (Array.from(this.#batches.values()).includes(k)) {
|
||
var fragment = document.createDocumentFragment();
|
||
move_effect(effect, fragment);
|
||
fragment.append(create_text());
|
||
this.#offscreen.set(k, {
|
||
effect,
|
||
fragment
|
||
});
|
||
} else destroy_effect(effect);
|
||
this.#outroing.delete(k);
|
||
this.#onscreen.delete(k);
|
||
};
|
||
if (this.#transition || !onscreen) {
|
||
this.#outroing.add(k);
|
||
pause_effect(effect, on_destroy, false);
|
||
} else on_destroy();
|
||
}
|
||
};
|
||
/**
|
||
* @param {Batch} batch
|
||
*/
|
||
#discard = (batch) => {
|
||
this.#batches.delete(batch);
|
||
const keys = Array.from(this.#batches.values());
|
||
for (const [k, branch] of this.#offscreen) if (!keys.includes(k)) {
|
||
destroy_effect(branch.effect);
|
||
this.#offscreen.delete(k);
|
||
}
|
||
};
|
||
/**
|
||
*
|
||
* @param {any} key
|
||
* @param {null | ((target: TemplateNode) => void)} fn
|
||
*/
|
||
ensure(key, fn) {
|
||
var batch = current_batch;
|
||
var defer = should_defer_append();
|
||
if (fn && !this.#onscreen.has(key) && !this.#offscreen.has(key)) if (defer) {
|
||
var fragment = document.createDocumentFragment();
|
||
var target = create_text();
|
||
fragment.append(target);
|
||
this.#offscreen.set(key, {
|
||
effect: branch(() => fn(target)),
|
||
fragment
|
||
});
|
||
} else this.#onscreen.set(key, branch(() => fn(this.anchor)));
|
||
this.#batches.set(batch, key);
|
||
if (defer) {
|
||
for (const [k, effect] of this.#onscreen) if (k === key) batch.unskip_effect(effect);
|
||
else batch.skip_effect(effect);
|
||
for (const [k, branch] of this.#offscreen) if (k === key) batch.unskip_effect(branch.effect);
|
||
else batch.skip_effect(branch.effect);
|
||
batch.oncommit(this.#commit);
|
||
batch.ondiscard(this.#discard);
|
||
} else {
|
||
if (hydrating) this.anchor = hydrate_node;
|
||
this.#commit(batch);
|
||
}
|
||
}
|
||
};
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/blocks/if.js
|
||
/** @import { TemplateNode } from '#client' */
|
||
/**
|
||
* @param {TemplateNode} node
|
||
* @param {(branch: (fn: (anchor: Node) => void, key?: number | false) => void) => void} fn
|
||
* @param {boolean} [elseif] True if this is an `{:else if ...}` block rather than an `{#if ...}`, as that affects which transitions are considered 'local'
|
||
* @returns {void}
|
||
*/
|
||
function if_block(node, fn, elseif = false) {
|
||
/** @type {TemplateNode | undefined} */
|
||
var marker;
|
||
if (hydrating) {
|
||
marker = hydrate_node;
|
||
hydrate_next();
|
||
}
|
||
var branches = new BranchManager(node);
|
||
var flags = elseif ? EFFECT_TRANSPARENT : 0;
|
||
/**
|
||
* @param {number | false} key
|
||
* @param {null | ((anchor: Node) => void)} fn
|
||
*/
|
||
function update_branch(key, fn) {
|
||
if (hydrating) {
|
||
var data = read_hydration_instruction(marker);
|
||
if (key !== parseInt(data.substring(1))) {
|
||
var anchor = skip_nodes();
|
||
set_hydrate_node(anchor);
|
||
branches.anchor = anchor;
|
||
set_hydrating(false);
|
||
branches.ensure(key, fn);
|
||
set_hydrating(true);
|
||
return;
|
||
}
|
||
}
|
||
branches.ensure(key, fn);
|
||
}
|
||
block(() => {
|
||
var has_branch = false;
|
||
fn((fn, key = 0) => {
|
||
has_branch = true;
|
||
update_branch(key, fn);
|
||
});
|
||
if (!has_branch) update_branch(-1, null);
|
||
}, flags);
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/blocks/each.js
|
||
/** @import { EachItem, EachOutroGroup, EachState, Effect, EffectNodes, MaybeSource, Source, TemplateNode, TransitionManager, Value } from '#client' */
|
||
/** @import { Batch } from '../../reactivity/batch.js'; */
|
||
/**
|
||
* @param {any} _
|
||
* @param {number} i
|
||
*/
|
||
function index(_, i) {
|
||
return i;
|
||
}
|
||
/**
|
||
* Pause multiple effects simultaneously, and coordinate their
|
||
* subsequent destruction. Used in each blocks
|
||
* @param {EachState} state
|
||
* @param {Effect[]} to_destroy
|
||
* @param {null | Node} controlled_anchor
|
||
*/
|
||
function pause_effects(state, to_destroy, controlled_anchor) {
|
||
/** @type {TransitionManager[]} */
|
||
var transitions = [];
|
||
var length = to_destroy.length;
|
||
/** @type {EachOutroGroup} */
|
||
var group;
|
||
var remaining = to_destroy.length;
|
||
for (var i = 0; i < length; i++) {
|
||
let effect = to_destroy[i];
|
||
pause_effect(effect, () => {
|
||
if (group) {
|
||
group.pending.delete(effect);
|
||
group.done.add(effect);
|
||
if (group.pending.size === 0) {
|
||
var groups = state.outrogroups;
|
||
destroy_effects(state, array_from(group.done));
|
||
groups.delete(group);
|
||
if (groups.size === 0) state.outrogroups = null;
|
||
}
|
||
} else remaining -= 1;
|
||
}, false);
|
||
}
|
||
if (remaining === 0) {
|
||
var fast_path = transitions.length === 0 && controlled_anchor !== null;
|
||
if (fast_path) {
|
||
var anchor = controlled_anchor;
|
||
var parent_node = anchor.parentNode;
|
||
clear_text_content(parent_node);
|
||
parent_node.append(anchor);
|
||
state.items.clear();
|
||
}
|
||
destroy_effects(state, to_destroy, !fast_path);
|
||
} else {
|
||
group = {
|
||
pending: new Set(to_destroy),
|
||
done: /* @__PURE__ */ new Set()
|
||
};
|
||
(state.outrogroups ??= /* @__PURE__ */ new Set()).add(group);
|
||
}
|
||
}
|
||
/**
|
||
* @param {EachState} state
|
||
* @param {Effect[]} to_destroy
|
||
* @param {boolean} remove_dom
|
||
*/
|
||
function destroy_effects(state, to_destroy, remove_dom = true) {
|
||
/** @type {Set<Effect> | undefined} */
|
||
var preserved_effects;
|
||
if (state.pending.size > 0) {
|
||
preserved_effects = /* @__PURE__ */ new Set();
|
||
for (const keys of state.pending.values()) for (const key of keys) preserved_effects.add(
|
||
/** @type {EachItem} */
|
||
state.items.get(key).e
|
||
);
|
||
}
|
||
for (var i = 0; i < to_destroy.length; i++) {
|
||
var e = to_destroy[i];
|
||
if (preserved_effects?.has(e)) {
|
||
e.f |= EFFECT_OFFSCREEN;
|
||
move_effect(e, document.createDocumentFragment());
|
||
} else destroy_effect(to_destroy[i], remove_dom);
|
||
}
|
||
}
|
||
/** @type {TemplateNode} */
|
||
var offscreen_anchor;
|
||
/**
|
||
* @template V
|
||
* @param {Element | Comment} node The next sibling node, or the parent node if this is a 'controlled' block
|
||
* @param {number} flags
|
||
* @param {() => V[]} get_collection
|
||
* @param {(value: V, index: number) => any} get_key
|
||
* @param {(anchor: Node, item: MaybeSource<V>, index: MaybeSource<number>) => void} render_fn
|
||
* @param {null | ((anchor: Node) => void)} fallback_fn
|
||
* @returns {void}
|
||
*/
|
||
function each(node, flags, get_collection, get_key, render_fn, fallback_fn = null) {
|
||
var anchor = node;
|
||
/** @type {Map<any, EachItem>} */
|
||
var items = /* @__PURE__ */ new Map();
|
||
if ((flags & 4) !== 0) {
|
||
var parent_node = node;
|
||
anchor = hydrating ? set_hydrate_node(/* @__PURE__ */ get_first_child(parent_node)) : parent_node.appendChild(create_text());
|
||
}
|
||
if (hydrating) hydrate_next();
|
||
/** @type {Effect | null} */
|
||
var fallback = null;
|
||
var each_array = /* @__PURE__ */ derived_safe_equal(() => {
|
||
var collection = get_collection();
|
||
return is_array(collection) ? collection : collection == null ? [] : array_from(collection);
|
||
});
|
||
/** @type {V[]} */
|
||
var array;
|
||
/** @type {Map<Batch, Set<any>>} */
|
||
var pending = /* @__PURE__ */ new Map();
|
||
var first_run = true;
|
||
/**
|
||
* @param {Batch} batch
|
||
*/
|
||
function commit(batch) {
|
||
if ((state.effect.f & 16384) !== 0) return;
|
||
state.pending.delete(batch);
|
||
state.fallback = fallback;
|
||
reconcile(state, array, anchor, flags, get_key);
|
||
if (fallback !== null) if (array.length === 0) if ((fallback.f & 33554432) === 0) resume_effect(fallback);
|
||
else {
|
||
fallback.f ^= EFFECT_OFFSCREEN;
|
||
move(fallback, null, anchor);
|
||
}
|
||
else pause_effect(fallback, () => {
|
||
fallback = null;
|
||
});
|
||
}
|
||
/**
|
||
* @param {Batch} batch
|
||
*/
|
||
function discard(batch) {
|
||
state.pending.delete(batch);
|
||
}
|
||
/** @type {EachState} */
|
||
var state = {
|
||
effect: block(() => {
|
||
array = get(each_array);
|
||
var length = array.length;
|
||
/** `true` if there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
|
||
let mismatch = false;
|
||
if (hydrating) {
|
||
if (read_hydration_instruction(anchor) === "[!" !== (length === 0)) {
|
||
anchor = skip_nodes();
|
||
set_hydrate_node(anchor);
|
||
set_hydrating(false);
|
||
mismatch = true;
|
||
}
|
||
}
|
||
var keys = /* @__PURE__ */ new Set();
|
||
var batch = current_batch;
|
||
var defer = should_defer_append();
|
||
for (var index = 0; index < length; index += 1) {
|
||
if (hydrating && hydrate_node.nodeType === 8 && hydrate_node.data === "]") {
|
||
anchor = hydrate_node;
|
||
mismatch = true;
|
||
set_hydrating(false);
|
||
}
|
||
var value = array[index];
|
||
var key = get_key(value, index);
|
||
var item = first_run ? null : items.get(key);
|
||
if (item) {
|
||
if (item.v) internal_set(item.v, value);
|
||
if (item.i) internal_set(item.i, index);
|
||
if (defer) batch.unskip_effect(item.e);
|
||
} else {
|
||
item = create_item(items, first_run ? anchor : offscreen_anchor ??= create_text(), value, key, index, render_fn, flags, get_collection);
|
||
if (!first_run) item.e.f |= EFFECT_OFFSCREEN;
|
||
items.set(key, item);
|
||
}
|
||
keys.add(key);
|
||
}
|
||
if (length === 0 && fallback_fn && !fallback) if (first_run) fallback = branch(() => fallback_fn(anchor));
|
||
else {
|
||
fallback = branch(() => fallback_fn(offscreen_anchor ??= create_text()));
|
||
fallback.f |= EFFECT_OFFSCREEN;
|
||
}
|
||
if (length > keys.size) each_key_duplicate("", "", "");
|
||
if (hydrating && length > 0) set_hydrate_node(skip_nodes());
|
||
if (!first_run) {
|
||
pending.set(batch, keys);
|
||
if (defer) {
|
||
for (const [key, item] of items) if (!keys.has(key)) batch.skip_effect(item.e);
|
||
batch.oncommit(commit);
|
||
batch.ondiscard(discard);
|
||
} else commit(batch);
|
||
}
|
||
if (mismatch) set_hydrating(true);
|
||
get(each_array);
|
||
}),
|
||
flags,
|
||
items,
|
||
pending,
|
||
outrogroups: null,
|
||
fallback
|
||
};
|
||
first_run = false;
|
||
if (hydrating) anchor = hydrate_node;
|
||
}
|
||
/**
|
||
* Skip past any non-branch effects (which could be created with `createSubscriber`, for example) to find the next branch effect
|
||
* @param {Effect | null} effect
|
||
* @returns {Effect | null}
|
||
*/
|
||
function skip_to_branch(effect) {
|
||
while (effect !== null && (effect.f & 32) === 0) effect = effect.next;
|
||
return effect;
|
||
}
|
||
/**
|
||
* Add, remove, or reorder items output by an each block as its input changes
|
||
* @template V
|
||
* @param {EachState} state
|
||
* @param {Array<V>} array
|
||
* @param {Element | Comment | Text} anchor
|
||
* @param {number} flags
|
||
* @param {(value: V, index: number) => any} get_key
|
||
* @returns {void}
|
||
*/
|
||
function reconcile(state, array, anchor, flags, get_key) {
|
||
var is_animated = (flags & 8) !== 0;
|
||
var length = array.length;
|
||
var items = state.items;
|
||
var current = skip_to_branch(state.effect.first);
|
||
/** @type {undefined | Set<Effect>} */
|
||
var seen;
|
||
/** @type {Effect | null} */
|
||
var prev = null;
|
||
/** @type {undefined | Set<Effect>} */
|
||
var to_animate;
|
||
/** @type {Effect[]} */
|
||
var matched = [];
|
||
/** @type {Effect[]} */
|
||
var stashed = [];
|
||
/** @type {V} */
|
||
var value;
|
||
/** @type {any} */
|
||
var key;
|
||
/** @type {Effect | undefined} */
|
||
var effect;
|
||
/** @type {number} */
|
||
var i;
|
||
if (is_animated) for (i = 0; i < length; i += 1) {
|
||
value = array[i];
|
||
key = get_key(value, i);
|
||
effect = items.get(key).e;
|
||
if ((effect.f & 33554432) === 0) {
|
||
effect.nodes?.a?.measure();
|
||
(to_animate ??= /* @__PURE__ */ new Set()).add(effect);
|
||
}
|
||
}
|
||
for (i = 0; i < length; i += 1) {
|
||
value = array[i];
|
||
key = get_key(value, i);
|
||
effect = items.get(key).e;
|
||
if (state.outrogroups !== null) for (const group of state.outrogroups) {
|
||
group.pending.delete(effect);
|
||
group.done.delete(effect);
|
||
}
|
||
if ((effect.f & 8192) !== 0) {
|
||
resume_effect(effect);
|
||
if (is_animated) {
|
||
effect.nodes?.a?.unfix();
|
||
(to_animate ??= /* @__PURE__ */ new Set()).delete(effect);
|
||
}
|
||
}
|
||
if ((effect.f & 33554432) !== 0) {
|
||
effect.f ^= EFFECT_OFFSCREEN;
|
||
if (effect === current) move(effect, null, anchor);
|
||
else {
|
||
var next = prev ? prev.next : current;
|
||
if (effect === state.effect.last) state.effect.last = effect.prev;
|
||
if (effect.prev) effect.prev.next = effect.next;
|
||
if (effect.next) effect.next.prev = effect.prev;
|
||
link(state, prev, effect);
|
||
link(state, effect, next);
|
||
move(effect, next, anchor);
|
||
prev = effect;
|
||
matched = [];
|
||
stashed = [];
|
||
current = skip_to_branch(prev.next);
|
||
continue;
|
||
}
|
||
}
|
||
if (effect !== current) {
|
||
if (seen !== void 0 && seen.has(effect)) {
|
||
if (matched.length < stashed.length) {
|
||
var start = stashed[0];
|
||
var j;
|
||
prev = start.prev;
|
||
var a = matched[0];
|
||
var b = matched[matched.length - 1];
|
||
for (j = 0; j < matched.length; j += 1) move(matched[j], start, anchor);
|
||
for (j = 0; j < stashed.length; j += 1) seen.delete(stashed[j]);
|
||
link(state, a.prev, b.next);
|
||
link(state, prev, a);
|
||
link(state, b, start);
|
||
current = start;
|
||
prev = b;
|
||
i -= 1;
|
||
matched = [];
|
||
stashed = [];
|
||
} else {
|
||
seen.delete(effect);
|
||
move(effect, current, anchor);
|
||
link(state, effect.prev, effect.next);
|
||
link(state, effect, prev === null ? state.effect.first : prev.next);
|
||
link(state, prev, effect);
|
||
prev = effect;
|
||
}
|
||
continue;
|
||
}
|
||
matched = [];
|
||
stashed = [];
|
||
while (current !== null && current !== effect) {
|
||
(seen ??= /* @__PURE__ */ new Set()).add(current);
|
||
stashed.push(current);
|
||
current = skip_to_branch(current.next);
|
||
}
|
||
if (current === null) continue;
|
||
}
|
||
if ((effect.f & 33554432) === 0) matched.push(effect);
|
||
prev = effect;
|
||
current = skip_to_branch(effect.next);
|
||
}
|
||
if (state.outrogroups !== null) {
|
||
for (const group of state.outrogroups) if (group.pending.size === 0) {
|
||
destroy_effects(state, array_from(group.done));
|
||
state.outrogroups?.delete(group);
|
||
}
|
||
if (state.outrogroups.size === 0) state.outrogroups = null;
|
||
}
|
||
if (current !== null || seen !== void 0) {
|
||
/** @type {Effect[]} */
|
||
var to_destroy = [];
|
||
if (seen !== void 0) {
|
||
for (effect of seen) if ((effect.f & 8192) === 0) to_destroy.push(effect);
|
||
}
|
||
while (current !== null) {
|
||
if ((current.f & 8192) === 0 && current !== state.fallback) to_destroy.push(current);
|
||
current = skip_to_branch(current.next);
|
||
}
|
||
var destroy_length = to_destroy.length;
|
||
if (destroy_length > 0) {
|
||
var controlled_anchor = (flags & 4) !== 0 && length === 0 ? anchor : null;
|
||
if (is_animated) {
|
||
for (i = 0; i < destroy_length; i += 1) to_destroy[i].nodes?.a?.measure();
|
||
for (i = 0; i < destroy_length; i += 1) to_destroy[i].nodes?.a?.fix();
|
||
}
|
||
pause_effects(state, to_destroy, controlled_anchor);
|
||
}
|
||
}
|
||
if (is_animated) queue_micro_task(() => {
|
||
if (to_animate === void 0) return;
|
||
for (effect of to_animate) effect.nodes?.a?.apply();
|
||
});
|
||
}
|
||
/**
|
||
* @template V
|
||
* @param {Map<any, EachItem>} items
|
||
* @param {Node} anchor
|
||
* @param {V} value
|
||
* @param {unknown} key
|
||
* @param {number} index
|
||
* @param {(anchor: Node, item: V | Source<V>, index: number | Value<number>, collection: () => V[]) => void} render_fn
|
||
* @param {number} flags
|
||
* @param {() => V[]} get_collection
|
||
* @returns {EachItem}
|
||
*/
|
||
function create_item(items, anchor, value, key, index, render_fn, flags, get_collection) {
|
||
var v = (flags & 1) !== 0 ? (flags & 16) === 0 ? /* @__PURE__ */ mutable_source(value, false, false) : source(value) : null;
|
||
var i = (flags & 2) !== 0 ? source(index) : null;
|
||
return {
|
||
v,
|
||
i,
|
||
e: branch(() => {
|
||
render_fn(anchor, v ?? value, i ?? index, get_collection);
|
||
return () => {
|
||
items.delete(key);
|
||
};
|
||
})
|
||
};
|
||
}
|
||
/**
|
||
* @param {Effect} effect
|
||
* @param {Effect | null} next
|
||
* @param {Text | Element | Comment} anchor
|
||
*/
|
||
function move(effect, next, anchor) {
|
||
if (!effect.nodes) return;
|
||
var node = effect.nodes.start;
|
||
var end = effect.nodes.end;
|
||
var dest = next && (next.f & 33554432) === 0 ? next.nodes.start : anchor;
|
||
while (node !== null) {
|
||
var next_node = /* @__PURE__ */ get_next_sibling(node);
|
||
dest.before(node);
|
||
if (node === end) return;
|
||
node = next_node;
|
||
}
|
||
}
|
||
/**
|
||
* @param {EachState} state
|
||
* @param {Effect | null} prev
|
||
* @param {Effect | null} next
|
||
*/
|
||
function link(state, prev, next) {
|
||
if (prev === null) state.effect.first = next;
|
||
else prev.next = next;
|
||
if (next === null) state.effect.last = prev;
|
||
else next.prev = prev;
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/elements/actions.js
|
||
/** @import { ActionPayload } from '#client' */
|
||
/**
|
||
* @template P
|
||
* @param {Element} dom
|
||
* @param {(dom: Element, value?: P) => ActionPayload<P>} action
|
||
* @param {() => P} [get_value]
|
||
* @returns {void}
|
||
*/
|
||
function action(dom, action, get_value) {
|
||
effect(() => {
|
||
var payload = untrack(() => action(dom, get_value?.()) || {});
|
||
if (get_value && payload?.update) {
|
||
var inited = false;
|
||
/** @type {P} */
|
||
var prev = {};
|
||
render_effect(() => {
|
||
var value = get_value();
|
||
deep_read_state(value);
|
||
if (inited && safe_not_equal(prev, value)) {
|
||
prev = value;
|
||
/** @type {Function} */ payload.update(value);
|
||
}
|
||
});
|
||
inited = true;
|
||
}
|
||
if (payload?.destroy) return () => payload.destroy();
|
||
});
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/shared/attributes.js
|
||
var whitespace = [..." \n\r\f\xA0\v"];
|
||
/**
|
||
* @param {any} value
|
||
* @param {string | null} [hash]
|
||
* @param {Record<string, boolean>} [directives]
|
||
* @returns {string | null}
|
||
*/
|
||
function to_class(value, hash, directives) {
|
||
var classname = value == null ? "" : "" + value;
|
||
if (hash) classname = classname ? classname + " " + hash : hash;
|
||
if (directives) {
|
||
for (var key of Object.keys(directives)) if (directives[key]) classname = classname ? classname + " " + key : key;
|
||
else if (classname.length) {
|
||
var len = key.length;
|
||
var a = 0;
|
||
while ((a = classname.indexOf(key, a)) >= 0) {
|
||
var b = a + len;
|
||
if ((a === 0 || whitespace.includes(classname[a - 1])) && (b === classname.length || whitespace.includes(classname[b]))) classname = (a === 0 ? "" : classname.substring(0, a)) + classname.substring(b + 1);
|
||
else a = b;
|
||
}
|
||
}
|
||
}
|
||
return classname === "" ? null : classname;
|
||
}
|
||
/**
|
||
*
|
||
* @param {Record<string,any>} styles
|
||
* @param {boolean} important
|
||
*/
|
||
function append_styles(styles, important = false) {
|
||
var separator = important ? " !important;" : ";";
|
||
var css = "";
|
||
for (var key of Object.keys(styles)) {
|
||
var value = styles[key];
|
||
if (value != null && value !== "") css += " " + key + ": " + value + separator;
|
||
}
|
||
return css;
|
||
}
|
||
/**
|
||
* @param {string} name
|
||
* @returns {string}
|
||
*/
|
||
function to_css_name(name) {
|
||
if (name[0] !== "-" || name[1] !== "-") return name.toLowerCase();
|
||
return name;
|
||
}
|
||
/**
|
||
* @param {any} value
|
||
* @param {Record<string, any> | [Record<string, any>, Record<string, any>]} [styles]
|
||
* @returns {string | null}
|
||
*/
|
||
function to_style(value, styles) {
|
||
if (styles) {
|
||
var new_style = "";
|
||
/** @type {Record<string,any> | undefined} */
|
||
var normal_styles;
|
||
/** @type {Record<string,any> | undefined} */
|
||
var important_styles;
|
||
if (Array.isArray(styles)) {
|
||
normal_styles = styles[0];
|
||
important_styles = styles[1];
|
||
} else normal_styles = styles;
|
||
if (value) {
|
||
value = String(value).replaceAll(/\s*\/\*.*?\*\/\s*/g, "").trim();
|
||
/** @type {boolean | '"' | "'"} */
|
||
var in_str = false;
|
||
var in_apo = 0;
|
||
var in_comment = false;
|
||
var reserved_names = [];
|
||
if (normal_styles) reserved_names.push(...Object.keys(normal_styles).map(to_css_name));
|
||
if (important_styles) reserved_names.push(...Object.keys(important_styles).map(to_css_name));
|
||
var start_index = 0;
|
||
var name_index = -1;
|
||
const len = value.length;
|
||
for (var i = 0; i < len; i++) {
|
||
var c = value[i];
|
||
if (in_comment) {
|
||
if (c === "/" && value[i - 1] === "*") in_comment = false;
|
||
} else if (in_str) {
|
||
if (in_str === c) in_str = false;
|
||
} else if (c === "/" && value[i + 1] === "*") in_comment = true;
|
||
else if (c === "\"" || c === "'") in_str = c;
|
||
else if (c === "(") in_apo++;
|
||
else if (c === ")") in_apo--;
|
||
if (!in_comment && in_str === false && in_apo === 0) {
|
||
if (c === ":" && name_index === -1) name_index = i;
|
||
else if (c === ";" || i === len - 1) {
|
||
if (name_index !== -1) {
|
||
var name = to_css_name(value.substring(start_index, name_index).trim());
|
||
if (!reserved_names.includes(name)) {
|
||
if (c !== ";") i++;
|
||
var property = value.substring(start_index, i).trim();
|
||
new_style += " " + property + ";";
|
||
}
|
||
}
|
||
start_index = i + 1;
|
||
name_index = -1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (normal_styles) new_style += append_styles(normal_styles);
|
||
if (important_styles) new_style += append_styles(important_styles, true);
|
||
new_style = new_style.trim();
|
||
return new_style === "" ? null : new_style;
|
||
}
|
||
return value == null ? null : String(value);
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/elements/class.js
|
||
/**
|
||
* @param {Element} dom
|
||
* @param {boolean | number} is_html
|
||
* @param {string | null} value
|
||
* @param {string} [hash]
|
||
* @param {Record<string, any>} [prev_classes]
|
||
* @param {Record<string, any>} [next_classes]
|
||
* @returns {Record<string, boolean> | undefined}
|
||
*/
|
||
function set_class(dom, is_html, value, hash, prev_classes, next_classes) {
|
||
var prev = dom[CLASS_CACHE];
|
||
if (hydrating || prev !== value || prev === void 0) {
|
||
var next_class_name = to_class(value, hash, next_classes);
|
||
if (!hydrating || next_class_name !== dom.getAttribute("class")) if (next_class_name == null) dom.removeAttribute("class");
|
||
else if (is_html) dom.className = next_class_name;
|
||
else dom.setAttribute("class", next_class_name);
|
||
/** @type {any} */ dom[CLASS_CACHE] = value;
|
||
} else if (next_classes && prev_classes !== next_classes) for (var key in next_classes) {
|
||
var is_present = !!next_classes[key];
|
||
if (prev_classes == null || is_present !== !!prev_classes[key]) dom.classList.toggle(key, is_present);
|
||
}
|
||
return next_classes;
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/elements/style.js
|
||
/**
|
||
* @param {Element & ElementCSSInlineStyle} dom
|
||
* @param {Record<string, any>} prev
|
||
* @param {Record<string, any>} next
|
||
* @param {string} [priority]
|
||
*/
|
||
function update_styles(dom, prev = {}, next, priority) {
|
||
for (var key in next) {
|
||
var value = next[key];
|
||
if (prev[key] !== value) if (next[key] == null) dom.style.removeProperty(key);
|
||
else dom.style.setProperty(key, value, priority);
|
||
}
|
||
}
|
||
/**
|
||
* @param {Element & ElementCSSInlineStyle} dom
|
||
* @param {string | null} value
|
||
* @param {Record<string, any> | [Record<string, any>, Record<string, any>]} [prev_styles]
|
||
* @param {Record<string, any> | [Record<string, any>, Record<string, any>]} [next_styles]
|
||
*/
|
||
function set_style(dom, value, prev_styles, next_styles) {
|
||
var prev = dom[STYLE_CACHE];
|
||
if (hydrating || prev !== value) {
|
||
var next_style_attr = to_style(value, next_styles);
|
||
if (!hydrating || next_style_attr !== dom.getAttribute("style")) if (next_style_attr == null) dom.removeAttribute("style");
|
||
else dom.style.cssText = next_style_attr;
|
||
/** @type {any} */ dom[STYLE_CACHE] = value;
|
||
} else if (next_styles) if (Array.isArray(next_styles)) {
|
||
update_styles(dom, prev_styles?.[0], next_styles[0]);
|
||
update_styles(dom, prev_styles?.[1], next_styles[1], "important");
|
||
} else update_styles(dom, prev_styles, next_styles);
|
||
return next_styles;
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/elements/bindings/select.js
|
||
/**
|
||
* Selects the correct option(s) (depending on whether this is a multiple select)
|
||
* @template V
|
||
* @param {HTMLSelectElement} select
|
||
* @param {V} value
|
||
* @param {boolean} mounting
|
||
*/
|
||
function select_option(select, value, mounting = false) {
|
||
if (select.multiple) {
|
||
if (value == void 0) return;
|
||
if (!is_array(value)) return select_multiple_invalid_value();
|
||
for (var option of select.options) option.selected = value.includes(get_option_value(option));
|
||
return;
|
||
}
|
||
for (option of select.options) if (is(get_option_value(option), value)) {
|
||
option.selected = true;
|
||
return;
|
||
}
|
||
if (!mounting || value !== void 0) select.selectedIndex = -1;
|
||
}
|
||
/**
|
||
* Selects the correct option(s) if `value` is given,
|
||
* and then sets up a mutation observer to sync the
|
||
* current selection to the dom when it changes. Such
|
||
* changes could for example occur when options are
|
||
* inside an `#each` block.
|
||
* @param {HTMLSelectElement} select
|
||
*/
|
||
function init_select(select) {
|
||
var observer = new MutationObserver(() => {
|
||
select_option(select, select.__value);
|
||
});
|
||
observer.observe(select, {
|
||
childList: true,
|
||
subtree: true,
|
||
attributes: true,
|
||
attributeFilter: ["value"]
|
||
});
|
||
teardown(() => {
|
||
observer.disconnect();
|
||
});
|
||
}
|
||
/** @param {HTMLOptionElement} option */
|
||
function get_option_value(option) {
|
||
if ("__value" in option) return option.__value;
|
||
else return option.value;
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/elements/attributes.js
|
||
/** @import { Blocker, Effect } from '#client' */
|
||
var IS_CUSTOM_ELEMENT = Symbol("is custom element");
|
||
var IS_HTML = Symbol("is html");
|
||
var LINK_TAG = IS_XHTML ? "link" : "LINK";
|
||
var PROGRESS_TAG = IS_XHTML ? "progress" : "PROGRESS";
|
||
/**
|
||
* The value/checked attribute in the template actually corresponds to the defaultValue property, so we need
|
||
* to remove it upon hydration to avoid a bug when someone resets the form value.
|
||
* @param {HTMLInputElement} input
|
||
* @returns {void}
|
||
*/
|
||
function remove_input_defaults(input) {
|
||
if (!hydrating) return;
|
||
var already_removed = false;
|
||
var remove_defaults = () => {
|
||
if (already_removed) return;
|
||
already_removed = true;
|
||
if (input.hasAttribute("value")) {
|
||
var value = input.value;
|
||
set_attribute(input, "value", null);
|
||
input.value = value;
|
||
}
|
||
if (input.hasAttribute("checked")) {
|
||
var checked = input.checked;
|
||
set_attribute(input, "checked", null);
|
||
input.checked = checked;
|
||
}
|
||
};
|
||
/** @type {any} */ input[FORM_RESET_HANDLER] = remove_defaults;
|
||
queue_micro_task(remove_defaults);
|
||
add_form_reset_listener();
|
||
}
|
||
/**
|
||
* @param {Element} element
|
||
* @param {any} value
|
||
*/
|
||
function set_value(element, value) {
|
||
var attributes = get_attributes(element);
|
||
if (attributes.value === (attributes.value = value ?? void 0) || element.value === value && (value !== 0 || element.nodeName !== PROGRESS_TAG)) return;
|
||
element.value = value ?? "";
|
||
}
|
||
/**
|
||
* @param {Element} element
|
||
* @param {boolean} checked
|
||
*/
|
||
function set_checked(element, checked) {
|
||
var attributes = get_attributes(element);
|
||
if (attributes.checked === (attributes.checked = checked ?? void 0)) return;
|
||
element.checked = checked;
|
||
}
|
||
/**
|
||
* @param {Element} element
|
||
* @param {string} attribute
|
||
* @param {string | null} value
|
||
* @param {boolean} [skip_warning]
|
||
*/
|
||
function set_attribute(element, attribute, value, skip_warning) {
|
||
var attributes = get_attributes(element);
|
||
if (hydrating) {
|
||
attributes[attribute] = element.getAttribute(attribute);
|
||
if (attribute === "src" || attribute === "srcset" || attribute === "href" && element.nodeName === LINK_TAG) {
|
||
if (!skip_warning);
|
||
return;
|
||
}
|
||
}
|
||
if (attributes[attribute] === (attributes[attribute] = value)) return;
|
||
if (attribute === "loading") element[LOADING_ATTR_SYMBOL] = value;
|
||
if (value == null) element.removeAttribute(attribute);
|
||
else if (typeof value !== "string" && get_setters(element).includes(attribute)) element[attribute] = value;
|
||
else element.setAttribute(attribute, value);
|
||
}
|
||
/**
|
||
*
|
||
* @param {Element} element
|
||
*/
|
||
function get_attributes(element) {
|
||
return element[ATTRIBUTES_CACHE] ??= {
|
||
[IS_CUSTOM_ELEMENT]: element.nodeName.includes("-"),
|
||
[IS_HTML]: element.namespaceURI === NAMESPACE_HTML
|
||
};
|
||
}
|
||
/** @type {Map<string, string[]>} */
|
||
var setters_cache = /* @__PURE__ */ new Map();
|
||
/** @param {Element} element */
|
||
function get_setters(element) {
|
||
var cache_key = element.getAttribute("is") || element.nodeName;
|
||
var setters = setters_cache.get(cache_key);
|
||
if (setters) return setters;
|
||
setters_cache.set(cache_key, setters = []);
|
||
var descriptors;
|
||
var proto = element;
|
||
var element_proto = Element.prototype;
|
||
while (element_proto !== proto) {
|
||
descriptors = get_descriptors(proto);
|
||
for (var key in descriptors) if (descriptors[key].set && key !== "innerHTML" && key !== "textContent" && key !== "innerText") setters.push(key);
|
||
proto = get_prototype_of(proto);
|
||
}
|
||
return setters;
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/elements/bindings/size.js
|
||
/**
|
||
* We create one listener for all elements
|
||
* @see {@link https://groups.google.com/a/chromium.org/g/blink-dev/c/z6ienONUb5A/m/F5-VcUZtBAAJ Explanation}
|
||
*/
|
||
var ResizeObserverSingleton = class ResizeObserverSingleton {
|
||
/** */
|
||
#listeners = /* @__PURE__ */ new WeakMap();
|
||
/** @type {ResizeObserver | undefined} */
|
||
#observer;
|
||
/** @type {ResizeObserverOptions} */
|
||
#options;
|
||
/** @static */
|
||
static entries = /* @__PURE__ */ new WeakMap();
|
||
/** @param {ResizeObserverOptions} options */
|
||
constructor(options) {
|
||
this.#options = options;
|
||
}
|
||
/**
|
||
* @param {Element} element
|
||
* @param {(entry: ResizeObserverEntry) => any} listener
|
||
*/
|
||
observe(element, listener) {
|
||
var listeners = this.#listeners.get(element) || /* @__PURE__ */ new Set();
|
||
listeners.add(listener);
|
||
this.#listeners.set(element, listeners);
|
||
this.#getObserver().observe(element, this.#options);
|
||
return () => {
|
||
var listeners = this.#listeners.get(element);
|
||
listeners.delete(listener);
|
||
if (listeners.size === 0) {
|
||
this.#listeners.delete(element);
|
||
/** @type {ResizeObserver} */ this.#observer.unobserve(element);
|
||
}
|
||
};
|
||
}
|
||
#getObserver() {
|
||
return this.#observer ?? (this.#observer = new ResizeObserver(
|
||
/** @param {any} entries */
|
||
(entries) => {
|
||
for (var entry of entries) {
|
||
ResizeObserverSingleton.entries.set(entry.target, entry);
|
||
for (var listener of this.#listeners.get(entry.target) || []) listener(entry);
|
||
}
|
||
}
|
||
));
|
||
}
|
||
};
|
||
/* @__PURE__ */ new ResizeObserverSingleton({ box: "content-box" });
|
||
/* @__PURE__ */ new ResizeObserverSingleton({ box: "border-box" });
|
||
/* @__PURE__ */ new ResizeObserverSingleton({ box: "device-pixel-content-box" });
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/dom/elements/bindings/this.js
|
||
/** @import { ComponentContext, Effect } from '#client' */
|
||
/**
|
||
* @param {any} bound_value
|
||
* @param {Element} element_or_component
|
||
* @returns {boolean}
|
||
*/
|
||
function is_bound_this(bound_value, element_or_component) {
|
||
return bound_value === element_or_component || bound_value?.[STATE_SYMBOL] === element_or_component;
|
||
}
|
||
/**
|
||
* @param {any} element_or_component
|
||
* @param {(value: unknown, ...parts: unknown[]) => void} update
|
||
* @param {(...parts: unknown[]) => unknown} get_value
|
||
* @param {() => unknown[]} [get_parts] Set if the this binding is used inside an each block,
|
||
* returns all the parts of the each block context that are used in the expression
|
||
* @returns {void}
|
||
*/
|
||
function bind_this(element_or_component = {}, update, get_value, get_parts) {
|
||
var component_effect = component_context.r;
|
||
var parent = active_effect;
|
||
effect(() => {
|
||
/** @type {unknown[]} */
|
||
var old_parts;
|
||
/** @type {unknown[]} */
|
||
var parts;
|
||
render_effect(() => {
|
||
old_parts = parts;
|
||
parts = get_parts?.() || [];
|
||
untrack(() => {
|
||
if (!is_bound_this(get_value(...parts), element_or_component)) {
|
||
update(element_or_component, ...parts);
|
||
if (old_parts && is_bound_this(get_value(...old_parts), element_or_component)) update(null, ...old_parts);
|
||
}
|
||
});
|
||
});
|
||
return () => {
|
||
let p = parent;
|
||
while (p !== component_effect && p.parent !== null && p.parent.f & 33554432) p = p.parent;
|
||
const teardown = () => {
|
||
if (parts && is_bound_this(get_value(...parts), element_or_component)) update(null, ...parts);
|
||
};
|
||
const original_teardown = p.teardown;
|
||
p.teardown = () => {
|
||
teardown();
|
||
original_teardown?.();
|
||
};
|
||
};
|
||
});
|
||
return element_or_component;
|
||
}
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/client/reactivity/props.js
|
||
/** @import { Derived, Effect, Source } from './types.js' */
|
||
/**
|
||
* This function is responsible for synchronizing a possibly bound prop with the inner component state.
|
||
* It is used whenever the compiler sees that the component writes to the prop, or when it has a default prop_value.
|
||
* @template V
|
||
* @param {Record<string, unknown>} props
|
||
* @param {string} key
|
||
* @param {number} flags
|
||
* @param {V | (() => V)} [fallback]
|
||
* @returns {(() => V | ((arg: V) => V) | ((arg: V, mutation: boolean) => V))}
|
||
*/
|
||
function prop(props, key, flags, fallback) {
|
||
var runes = !legacy_mode_flag || (flags & 2) !== 0;
|
||
var bindable = (flags & 8) !== 0;
|
||
var lazy = (flags & 16) !== 0;
|
||
var fallback_value = fallback;
|
||
var fallback_dirty = true;
|
||
var fallback_signal = void 0;
|
||
var get_fallback = () => {
|
||
if (lazy && runes) {
|
||
fallback_signal ??= /* @__PURE__ */ derived(fallback);
|
||
return get(fallback_signal);
|
||
}
|
||
if (fallback_dirty) {
|
||
fallback_dirty = false;
|
||
fallback_value = lazy ? untrack(fallback) : fallback;
|
||
}
|
||
return fallback_value;
|
||
};
|
||
/** @type {((v: V) => void) | undefined} */
|
||
let setter;
|
||
if (bindable) {
|
||
var is_entry_props = STATE_SYMBOL in props || LEGACY_PROPS in props;
|
||
setter = get_descriptor(props, key)?.set ?? (is_entry_props && key in props ? (v) => props[key] = v : void 0);
|
||
}
|
||
/** @type {V} */
|
||
var initial_value;
|
||
var is_store_sub = false;
|
||
if (bindable) [initial_value, is_store_sub] = capture_store_binding(() => props[key]);
|
||
else initial_value = props[key];
|
||
if (initial_value === void 0 && fallback !== void 0) {
|
||
initial_value = get_fallback();
|
||
if (setter) {
|
||
if (runes) props_invalid_value(key);
|
||
setter(initial_value);
|
||
}
|
||
}
|
||
/** @type {() => V} */
|
||
var getter;
|
||
if (runes) getter = () => {
|
||
var value = props[key];
|
||
if (value === void 0) return get_fallback();
|
||
fallback_dirty = true;
|
||
return value;
|
||
};
|
||
else getter = () => {
|
||
var value = props[key];
|
||
if (value !== void 0) fallback_value = void 0;
|
||
return value === void 0 ? fallback_value : value;
|
||
};
|
||
if (runes && (flags & 4) === 0) return getter;
|
||
if (setter) {
|
||
var legacy_parent = props.$$legacy;
|
||
return (function(value, mutation) {
|
||
if (arguments.length > 0) {
|
||
if (!runes || !mutation || legacy_parent || is_store_sub)
|
||
/** @type {Function} */ setter(mutation ? getter() : value);
|
||
return value;
|
||
}
|
||
return getter();
|
||
});
|
||
}
|
||
var overridden = false;
|
||
var d = ((flags & 1) !== 0 ? derived : derived_safe_equal)(() => {
|
||
overridden = false;
|
||
return getter();
|
||
});
|
||
if (bindable) get(d);
|
||
var parent_effect = active_effect;
|
||
return (function(value, mutation) {
|
||
if (arguments.length > 0) {
|
||
const new_value = mutation ? get(d) : runes && bindable ? proxy(value) : value;
|
||
set(d, new_value);
|
||
overridden = true;
|
||
if (fallback_value !== void 0) fallback_value = new_value;
|
||
return value;
|
||
}
|
||
if (is_destroying_effect && overridden || (parent_effect.f & 16384) !== 0) return d.v;
|
||
return get(d);
|
||
});
|
||
}
|
||
if (typeof HTMLElement === "function");
|
||
//#endregion
|
||
//#region node_modules/svelte/src/internal/disclose-version.js
|
||
if (typeof window !== "undefined") ((window.__svelte ??= {}).v ??= /* @__PURE__ */ new Set()).add("5");
|
||
//#endregion
|
||
//#region node_modules/idb/build/index.js
|
||
function getIdbProxyableTypes() {
|
||
return idbProxyableTypes || (idbProxyableTypes = [
|
||
IDBDatabase,
|
||
IDBObjectStore,
|
||
IDBIndex,
|
||
IDBCursor,
|
||
IDBTransaction
|
||
]);
|
||
}
|
||
function getCursorAdvanceMethods() {
|
||
return cursorAdvanceMethods || (cursorAdvanceMethods = [
|
||
IDBCursor.prototype.advance,
|
||
IDBCursor.prototype.continue,
|
||
IDBCursor.prototype.continuePrimaryKey
|
||
]);
|
||
}
|
||
function promisifyRequest(request) {
|
||
const promise = new Promise((resolve, reject) => {
|
||
const unlisten = () => {
|
||
request.removeEventListener("success", success);
|
||
request.removeEventListener("error", error);
|
||
};
|
||
const success = () => {
|
||
resolve(wrap(request.result));
|
||
unlisten();
|
||
};
|
||
const error = () => {
|
||
reject(request.error);
|
||
unlisten();
|
||
};
|
||
request.addEventListener("success", success);
|
||
request.addEventListener("error", error);
|
||
});
|
||
reverseTransformCache.set(promise, request);
|
||
return promise;
|
||
}
|
||
function cacheDonePromiseForTransaction(tx) {
|
||
if (transactionDoneMap.has(tx)) return;
|
||
const done = new Promise((resolve, reject) => {
|
||
const unlisten = () => {
|
||
tx.removeEventListener("complete", complete);
|
||
tx.removeEventListener("error", error);
|
||
tx.removeEventListener("abort", error);
|
||
};
|
||
const complete = () => {
|
||
resolve();
|
||
unlisten();
|
||
};
|
||
const error = () => {
|
||
reject(tx.error || new DOMException("AbortError", "AbortError"));
|
||
unlisten();
|
||
};
|
||
tx.addEventListener("complete", complete);
|
||
tx.addEventListener("error", error);
|
||
tx.addEventListener("abort", error);
|
||
});
|
||
transactionDoneMap.set(tx, done);
|
||
}
|
||
function replaceTraps(callback) {
|
||
idbProxyTraps = callback(idbProxyTraps);
|
||
}
|
||
function wrapFunction(func) {
|
||
if (getCursorAdvanceMethods().includes(func)) return function(...args) {
|
||
func.apply(unwrap(this), args);
|
||
return wrap(this.request);
|
||
};
|
||
return function(...args) {
|
||
return wrap(func.apply(unwrap(this), args));
|
||
};
|
||
}
|
||
function transformCachableValue(value) {
|
||
if (typeof value === "function") return wrapFunction(value);
|
||
if (value instanceof IDBTransaction) cacheDonePromiseForTransaction(value);
|
||
if (instanceOfAny(value, getIdbProxyableTypes())) return new Proxy(value, idbProxyTraps);
|
||
return value;
|
||
}
|
||
function wrap(value) {
|
||
if (value instanceof IDBRequest) return promisifyRequest(value);
|
||
if (transformCache.has(value)) return transformCache.get(value);
|
||
const newValue = transformCachableValue(value);
|
||
if (newValue !== value) {
|
||
transformCache.set(value, newValue);
|
||
reverseTransformCache.set(newValue, value);
|
||
}
|
||
return newValue;
|
||
}
|
||
/**
|
||
* Open a database.
|
||
*
|
||
* @param name Name of the database.
|
||
* @param version Schema version.
|
||
* @param callbacks Additional callbacks.
|
||
*/
|
||
function openDB(name, version, { blocked, upgrade, blocking, terminated } = {}) {
|
||
const request = indexedDB.open(name, version);
|
||
const openPromise = wrap(request);
|
||
if (upgrade) request.addEventListener("upgradeneeded", (event) => {
|
||
upgrade(wrap(request.result), event.oldVersion, event.newVersion, wrap(request.transaction), event);
|
||
});
|
||
if (blocked) request.addEventListener("blocked", (event) => blocked(event.oldVersion, event.newVersion, event));
|
||
openPromise.then((db) => {
|
||
if (terminated) db.addEventListener("close", () => terminated());
|
||
if (blocking) db.addEventListener("versionchange", (event) => blocking(event.oldVersion, event.newVersion, event));
|
||
}).catch(() => {});
|
||
return openPromise;
|
||
}
|
||
function getMethod(target, prop) {
|
||
if (!(target instanceof IDBDatabase && !(prop in target) && typeof prop === "string")) return;
|
||
if (cachedMethods.get(prop)) return cachedMethods.get(prop);
|
||
const targetFuncName = prop.replace(/FromIndex$/, "");
|
||
const useIndex = prop !== targetFuncName;
|
||
const isWrite = writeMethods.includes(targetFuncName);
|
||
if (!(targetFuncName in (useIndex ? IDBIndex : IDBObjectStore).prototype) || !(isWrite || readMethods.includes(targetFuncName))) return;
|
||
const method = async function(storeName, ...args) {
|
||
const tx = this.transaction(storeName, isWrite ? "readwrite" : "readonly");
|
||
let target = tx.store;
|
||
if (useIndex) target = target.index(args.shift());
|
||
return (await Promise.all([target[targetFuncName](...args), isWrite && tx.done]))[0];
|
||
};
|
||
cachedMethods.set(prop, method);
|
||
return method;
|
||
}
|
||
async function* iterate(...args) {
|
||
let cursor = this;
|
||
if (!(cursor instanceof IDBCursor)) cursor = await cursor.openCursor(...args);
|
||
if (!cursor) return;
|
||
cursor = cursor;
|
||
const proxiedCursor = new Proxy(cursor, cursorIteratorTraps);
|
||
ittrProxiedCursorToOriginalProxy.set(proxiedCursor, cursor);
|
||
reverseTransformCache.set(proxiedCursor, unwrap(cursor));
|
||
while (cursor) {
|
||
yield proxiedCursor;
|
||
cursor = await (advanceResults.get(proxiedCursor) || cursor.continue());
|
||
advanceResults.delete(proxiedCursor);
|
||
}
|
||
}
|
||
function isIteratorProp(target, prop) {
|
||
return prop === Symbol.asyncIterator && instanceOfAny(target, [
|
||
IDBIndex,
|
||
IDBObjectStore,
|
||
IDBCursor
|
||
]) || prop === "iterate" && instanceOfAny(target, [IDBIndex, IDBObjectStore]);
|
||
}
|
||
var instanceOfAny, idbProxyableTypes, cursorAdvanceMethods, transactionDoneMap, transformCache, reverseTransformCache, idbProxyTraps, unwrap, readMethods, writeMethods, cachedMethods, advanceMethodProps, methodMap, advanceResults, ittrProxiedCursorToOriginalProxy, cursorIteratorTraps;
|
||
var init_build = __esmMin((() => {
|
||
instanceOfAny = (object, constructors) => constructors.some((c) => object instanceof c);
|
||
transactionDoneMap = /* @__PURE__ */ new WeakMap();
|
||
transformCache = /* @__PURE__ */ new WeakMap();
|
||
reverseTransformCache = /* @__PURE__ */ new WeakMap();
|
||
idbProxyTraps = {
|
||
get(target, prop, receiver) {
|
||
if (target instanceof IDBTransaction) {
|
||
if (prop === "done") return transactionDoneMap.get(target);
|
||
if (prop === "store") return receiver.objectStoreNames[1] ? void 0 : receiver.objectStore(receiver.objectStoreNames[0]);
|
||
}
|
||
return wrap(target[prop]);
|
||
},
|
||
set(target, prop, value) {
|
||
target[prop] = value;
|
||
return true;
|
||
},
|
||
has(target, prop) {
|
||
if (target instanceof IDBTransaction && (prop === "done" || prop === "store")) return true;
|
||
return prop in target;
|
||
}
|
||
};
|
||
unwrap = (value) => reverseTransformCache.get(value);
|
||
readMethods = [
|
||
"get",
|
||
"getKey",
|
||
"getAll",
|
||
"getAllKeys",
|
||
"count"
|
||
];
|
||
writeMethods = [
|
||
"put",
|
||
"add",
|
||
"delete",
|
||
"clear"
|
||
];
|
||
cachedMethods = /* @__PURE__ */ new Map();
|
||
replaceTraps((oldTraps) => ({
|
||
...oldTraps,
|
||
get: (target, prop, receiver) => getMethod(target, prop) || oldTraps.get(target, prop, receiver),
|
||
has: (target, prop) => !!getMethod(target, prop) || oldTraps.has(target, prop)
|
||
}));
|
||
advanceMethodProps = [
|
||
"continue",
|
||
"continuePrimaryKey",
|
||
"advance"
|
||
];
|
||
methodMap = {};
|
||
advanceResults = /* @__PURE__ */ new WeakMap();
|
||
ittrProxiedCursorToOriginalProxy = /* @__PURE__ */ new WeakMap();
|
||
cursorIteratorTraps = { get(target, prop) {
|
||
if (!advanceMethodProps.includes(prop)) return target[prop];
|
||
let cachedFunc = methodMap[prop];
|
||
if (!cachedFunc) cachedFunc = methodMap[prop] = function(...args) {
|
||
advanceResults.set(this, ittrProxiedCursorToOriginalProxy.get(this)[prop](...args));
|
||
};
|
||
return cachedFunc;
|
||
} };
|
||
replaceTraps((oldTraps) => ({
|
||
...oldTraps,
|
||
get(target, prop, receiver) {
|
||
if (isIteratorProp(target, prop)) return iterate;
|
||
return oldTraps.get(target, prop, receiver);
|
||
},
|
||
has(target, prop) {
|
||
return isIteratorProp(target, prop) || oldTraps.has(target, prop);
|
||
}
|
||
}));
|
||
}));
|
||
//#endregion
|
||
//#region src/lib/storage/db.js
|
||
var db_exports = /* @__PURE__ */ __exportAll({
|
||
DEFAULT_SETTINGS: () => DEFAULT_SETTINGS,
|
||
addLocation: () => addLocation,
|
||
clearAllLocations: () => clearAllLocations,
|
||
deleteLocation: () => deleteLocation,
|
||
getLocations: () => getLocations,
|
||
loadSettings: () => loadSettings,
|
||
reorderLocations: () => reorderLocations,
|
||
saveSettings: () => saveSettings,
|
||
setCurrentLocation: () => setCurrentLocation,
|
||
updateLocation: () => updateLocation
|
||
});
|
||
function getDb() {
|
||
return openDB(DB_NAME, DB_VERSION, { upgrade(db) {
|
||
if (!db.objectStoreNames.contains("locations")) db.createObjectStore("locations", { keyPath: "id" });
|
||
if (!db.objectStoreNames.contains("settings")) db.createObjectStore("settings");
|
||
} });
|
||
}
|
||
/**
|
||
* Get all saved locations sorted by order.
|
||
* @returns {Promise<Array>}
|
||
*/
|
||
async function getLocations() {
|
||
return (await (await getDb()).getAll("locations")).sort((a, b) => (a.order || 0) - (b.order || 0));
|
||
}
|
||
/**
|
||
* Add a new location.
|
||
* @param {object} location - { name, lat, lon, isCurrent, order? }
|
||
* @returns {Promise<string>} The created location id
|
||
*/
|
||
async function addLocation(location) {
|
||
const db = await getDb();
|
||
const id = `loc_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
||
const maxOrder = (await db.getAll("locations")).reduce((max, l) => Math.max(max, l.order || 0), 0);
|
||
const entry = {
|
||
id,
|
||
name: location.name,
|
||
lat: location.lat,
|
||
lon: location.lon,
|
||
isCurrent: !!location.isCurrent,
|
||
order: location.order != null ? location.order : maxOrder + 1
|
||
};
|
||
await db.put("locations", entry);
|
||
return id;
|
||
}
|
||
/**
|
||
* Update an existing location.
|
||
* @param {object} location - Full location object with id
|
||
*/
|
||
async function updateLocation(location) {
|
||
const db = await getDb();
|
||
const existing = await db.get("locations", location.id);
|
||
if (!existing) throw new Error("Location not found");
|
||
await db.put("locations", {
|
||
...existing,
|
||
...location
|
||
});
|
||
}
|
||
/**
|
||
* Delete a location by id.
|
||
* @param {string} id
|
||
*/
|
||
async function deleteLocation(id) {
|
||
await (await getDb()).delete("locations", id);
|
||
}
|
||
/**
|
||
* Set a location as "current" (GPS-based) and clear previous current flag.
|
||
* @param {string} id
|
||
*/
|
||
async function setCurrentLocation(id) {
|
||
const db = await getDb();
|
||
const all = await db.getAll("locations");
|
||
for (const loc of all) if (loc.isCurrent && loc.id !== id) await db.put("locations", {
|
||
...loc,
|
||
isCurrent: false
|
||
});
|
||
const target = await db.get("locations", id);
|
||
if (target) await db.put("locations", {
|
||
...target,
|
||
isCurrent: true
|
||
});
|
||
}
|
||
/**
|
||
* Reorder locations.
|
||
* @param {Array<{ id: string, order: number }>} orderUpdates
|
||
*/
|
||
async function reorderLocations(orderUpdates) {
|
||
const db = await getDb();
|
||
for (const update of orderUpdates) {
|
||
const loc = await db.get("locations", update.id);
|
||
if (loc) await db.put("locations", {
|
||
...loc,
|
||
order: update.order
|
||
});
|
||
}
|
||
}
|
||
/**
|
||
* Remove all locations (full reset).
|
||
*/
|
||
async function clearAllLocations() {
|
||
await (await getDb()).clear("locations");
|
||
}
|
||
/**
|
||
* Load settings. Returns defaults if none saved.
|
||
* @returns {Promise<object>}
|
||
*/
|
||
async function loadSettings() {
|
||
const saved = await (await getDb()).get("settings", "prefs");
|
||
return saved ? {
|
||
...DEFAULT_SETTINGS,
|
||
...saved
|
||
} : { ...DEFAULT_SETTINGS };
|
||
}
|
||
/**
|
||
* Save settings.
|
||
* @param {object} settings
|
||
*/
|
||
async function saveSettings(settings) {
|
||
await (await getDb()).put("settings", JSON.parse(JSON.stringify(settings)), "prefs");
|
||
}
|
||
var DB_NAME, DB_VERSION, DEFAULT_SETTINGS;
|
||
var init_db = __esmMin((() => {
|
||
init_build();
|
||
DB_NAME = "weatherlens-db";
|
||
DB_VERSION = 1;
|
||
DEFAULT_SETTINGS = {
|
||
units: "metric",
|
||
source: "open-meteo",
|
||
refreshInterval: 30,
|
||
alertsEnabled: true,
|
||
showLightning: false,
|
||
alertThresholds: {
|
||
precip: 70,
|
||
windGust: 40,
|
||
uvIndex: 6,
|
||
tempHigh: 35,
|
||
tempLow: 0,
|
||
thunderstorm: true
|
||
},
|
||
dismissedAlerts: []
|
||
};
|
||
}));
|
||
//#endregion
|
||
//#region src/lib/api/weather.js
|
||
var weather_exports = /* @__PURE__ */ __exportAll({
|
||
fetchForecast: () => fetchForecast,
|
||
searchLocations: () => searchLocations
|
||
});
|
||
/**
|
||
* Fetch weather forecast for a location.
|
||
* @param {number} lat
|
||
* @param {number} lon
|
||
* @param {string} [unit='celsius'] - 'celsius' or 'fahrenheit'
|
||
* @returns {Promise<object>} Raw Open-Meteo forecast response
|
||
*/
|
||
async function fetchForecast(lat, lon, units = "metric") {
|
||
const tempUnit = units === "imperial" ? "fahrenheit" : "celsius";
|
||
const windUnit = units === "imperial" ? "mph" : "kmh";
|
||
const params = new URLSearchParams({
|
||
latitude: lat.toString(),
|
||
longitude: lon.toString(),
|
||
current: [
|
||
"temperature_2m",
|
||
"relative_humidity_2m",
|
||
"apparent_temperature",
|
||
"weather_code",
|
||
"wind_speed_10m",
|
||
"wind_direction_10m",
|
||
"wind_gusts_10m",
|
||
"pressure_msl",
|
||
"uv_index",
|
||
"is_day"
|
||
].join(","),
|
||
hourly: [
|
||
"temperature_2m",
|
||
"precipitation_probability",
|
||
"weather_code",
|
||
"wind_speed_10m"
|
||
].join(","),
|
||
daily: [
|
||
"temperature_2m_max",
|
||
"temperature_2m_min",
|
||
"precipitation_probability_max",
|
||
"weather_code",
|
||
"sunrise",
|
||
"sunset",
|
||
"uv_index_max",
|
||
"wind_speed_10m_max"
|
||
].join(","),
|
||
timezone: "auto",
|
||
forecast_days: "7",
|
||
temperature_unit: tempUnit,
|
||
wind_speed_unit: windUnit
|
||
});
|
||
const url = `${FORECAST_BASE}?${params.toString()}`;
|
||
const res = await fetch(url);
|
||
if (!res.ok) throw new Error(`Weather API error: ${res.status} ${res.statusText}`);
|
||
return res.json();
|
||
}
|
||
/**
|
||
* Search for locations by name using the Open-Meteo geocoding API.
|
||
* @param {string} query - Location name to search for
|
||
* @returns {Promise<Array<{ id: number, name: string, latitude: number, longitude: number, country: string, admin1: string, timezone: string }>>}
|
||
*/
|
||
async function searchLocations(query) {
|
||
const params = new URLSearchParams({
|
||
name: query,
|
||
count: "8",
|
||
language: "en",
|
||
format: "json"
|
||
});
|
||
const url = `${GEOCODING_BASE}?${params.toString()}`;
|
||
const res = await fetch(url);
|
||
if (!res.ok) throw new Error(`Geocoding API error: ${res.status} ${res.statusText}`);
|
||
return ((await res.json()).results || []).map((r) => ({
|
||
id: r.id,
|
||
name: r.name,
|
||
latitude: r.latitude,
|
||
longitude: r.longitude,
|
||
country: r.country || "",
|
||
admin1: r.admin1 || "",
|
||
timezone: r.timezone || "UTC"
|
||
}));
|
||
}
|
||
var FORECAST_BASE, GEOCODING_BASE;
|
||
var init_weather = __esmMin((() => {
|
||
FORECAST_BASE = "https://api.open-meteo.com/v1/forecast";
|
||
GEOCODING_BASE = "https://geocoding-api.open-meteo.com/v1/search";
|
||
}));
|
||
//#endregion
|
||
//#region src/lib/config.js
|
||
init_db();
|
||
init_weather();
|
||
var NWS_USER_AGENT = `WeatherLens/0.1 (explorigin@gmail.com)`;
|
||
//#endregion
|
||
//#region src/lib/api/nws.js
|
||
/**
|
||
* NWS API (api.weather.gov) client + normalizer.
|
||
*
|
||
* Converts the National Weather Service forecast into the same flat shape the
|
||
* app's components expect (an Open-Meteo-style response), so the rest of the
|
||
* UI needs no per-source branches. US-only: NWS is queried via
|
||
* /points/{lat},{lon} which resolves to the nearest gridpoint forecast.
|
||
*/
|
||
var API = "https://api.weather.gov";
|
||
async function jsonGet(url) {
|
||
if (!url) throw new Error("Missing NWS forecast URL");
|
||
const res = await fetch(url, { headers: {
|
||
"User-Agent": NWS_USER_AGENT,
|
||
"Accept": "application/geo+json"
|
||
} });
|
||
if (!res.ok) throw new Error(`NWS API error: ${res.status} ${res.statusText}`);
|
||
return res.json();
|
||
}
|
||
function ftoC(f) {
|
||
return (f - 32) * 5 / 9;
|
||
}
|
||
function mphToKmh(mph) {
|
||
return mph * 1.609344;
|
||
}
|
||
/** Normalize a value to the requested unit. */
|
||
function temp(v, units) {
|
||
return units === "imperial" ? v : ftoC(v);
|
||
}
|
||
function speed(v, units) {
|
||
return units === "imperial" ? v : mphToKmh(v);
|
||
}
|
||
var CARD = [
|
||
"N",
|
||
"NNE",
|
||
"NE",
|
||
"ENE",
|
||
"E",
|
||
"ESE",
|
||
"SE",
|
||
"SSE",
|
||
"S",
|
||
"SSW",
|
||
"SW",
|
||
"WSW",
|
||
"W",
|
||
"WNW",
|
||
"NW",
|
||
"NNW"
|
||
];
|
||
function compassToDeg(dir) {
|
||
if (dir == null) return null;
|
||
const s = String(dir).trim().toUpperCase();
|
||
const i = CARD.indexOf(s);
|
||
return i >= 0 ? i * 22.5 : null;
|
||
}
|
||
/** Extract the max wind speed (mph) from strings like "5 to 10 mph" or "12 mph". */
|
||
function parseWind(s) {
|
||
if (s == null) return null;
|
||
const nums = String(s).match(/\d+(\.\d+)?/g);
|
||
if (!nums || !nums.length) return null;
|
||
return Math.max(...nums.map(Number));
|
||
}
|
||
/**
|
||
* Map an NWS shortForecast string to a WMO code for icons/severity.
|
||
* @param {string} text - NWS shortForecast text, e.g. "Slight Chance Showers And Thunderstorms"
|
||
* @param {number|null} prob - precipitation probability (0-100) for that period,
|
||
* so low-chance wording does not render as a full weather condition.
|
||
*/
|
||
function shortForecastToWmo(text, prob = null) {
|
||
const t = (text || "").toLowerCase();
|
||
const chanceLike = () => prob != null && prob < 30 || /\bslight chance\b|chance of|scattered|isolated|fewer chance|\bchance\b/.test(t);
|
||
if (/thunderstorm|tstm/.test(t)) {
|
||
if (/\bsevere\b|\bstrong\b|warning|watch/.test(t)) return 95;
|
||
if (chanceLike()) return 80;
|
||
return 95;
|
||
}
|
||
if (/freez|x-ice|ice pellets|freezing/.test(t) && /rain|drizzle|shower/.test(t)) return 66;
|
||
if (/snow|blizzard|flurr|snow shower/.test(t)) return /heavy|blizzard/.test(t) ? 75 : 71;
|
||
if (/rain|shower|drizzle|sprinkl/.test(t)) {
|
||
if (/heavy|strong|torrential/.test(t)) return 65;
|
||
if (chanceLike()) return 61;
|
||
return 63;
|
||
}
|
||
if (/fog|haze|smoke|brume/.test(t)) return 45;
|
||
if (/partly (cloudy|sunny)/.test(t)) return 2;
|
||
if (/overcast|covering|cloudy/.test(t)) return 3;
|
||
if (/sunny|clear|fair/.test(t)) return /most|mostly/.test(t) ? 2 : 0;
|
||
return 1;
|
||
}
|
||
function wmoRank(code) {
|
||
if (code >= 95) return 8;
|
||
if (code >= 80 && code <= 86) return 7;
|
||
if (code >= 65 && code <= 82) return 6;
|
||
if (code >= 71 && code <= 77) return 5;
|
||
if (code >= 45 && code <= 48) return 4;
|
||
if (code <= 3) return 1;
|
||
return 2;
|
||
}
|
||
/** Local calendar date "YYYY-MM-DD" for an ISO timestamp in a timezone. */
|
||
function dateKey(iso, tz) {
|
||
const p = new Intl.DateTimeFormat("en-CA", {
|
||
timeZone: tz,
|
||
year: "numeric",
|
||
month: "2-digit",
|
||
day: "2-digit"
|
||
}).formatToParts(new Date(iso));
|
||
const P = Object.fromEntries(p.map((x) => [x.type, x.value]));
|
||
return `${P.year}-${P.month}-${P.day}`;
|
||
}
|
||
/** Local naive wall-clock "YYYY-MM-DDTHH:MM" (no offset) for an ISO timestamp. */
|
||
function localTimeString(iso, tz) {
|
||
const p = new Intl.DateTimeFormat("en-CA", {
|
||
timeZone: tz,
|
||
year: "numeric",
|
||
month: "2-digit",
|
||
day: "2-digit",
|
||
hour: "2-digit",
|
||
minute: "2-digit",
|
||
hour12: false
|
||
}).formatToParts(new Date(iso));
|
||
const P = Object.fromEntries(p.map((x) => [x.type, x.value]));
|
||
const h = P.hour === "24" ? "00" : String(P.hour).padStart(2, "0");
|
||
return `${P.year}-${P.month}-${P.day}T${h}:${String(P.minute).padStart(2, "0")}`;
|
||
}
|
||
function prob(period) {
|
||
const v = period?.probabilityOfPrecipitation?.value;
|
||
return v == null ? 0 : v;
|
||
}
|
||
/** Build daily arrays from NWS forecast periods (14 periods ~ 7 days). */
|
||
function buildDaily(periods, tz, units) {
|
||
const byDate = /* @__PURE__ */ new Map();
|
||
for (const pd of periods) {
|
||
const key = dateKey(pd.startTime, tz);
|
||
if (!byDate.has(key)) byDate.set(key, {
|
||
highs: [],
|
||
lows: [],
|
||
precip: [],
|
||
codes: [],
|
||
winds: []
|
||
});
|
||
const d = byDate.get(key);
|
||
const t = pd.temperature;
|
||
if (t != null) {
|
||
const c = temp(t, units);
|
||
if (pd.isDaytime) d.highs.push(c);
|
||
else d.lows.push(c);
|
||
}
|
||
d.precip.push(prob(pd));
|
||
d.codes.push(shortForecastToWmo(pd.shortForecast, prob(pd)));
|
||
const ws = parseWind(pd.windSpeed);
|
||
if (ws != null) d.winds.push(ws);
|
||
}
|
||
const dates = [...byDate.keys()].sort();
|
||
const time = [], temperature_2m_max = [], temperature_2m_min = [];
|
||
const precipitation_probability_max = [], weather_code = [], wind_speed_10m_max = [];
|
||
for (const key of dates) {
|
||
const d = byDate.get(key);
|
||
const high = d.highs.length ? Math.max(...d.highs) : d.lows.length ? Math.max(...d.lows) : null;
|
||
const low = d.lows.length ? Math.min(...d.lows) : d.highs.length ? Math.min(...d.highs) : null;
|
||
time.push(key);
|
||
temperature_2m_max.push(high);
|
||
temperature_2m_min.push(low);
|
||
precipitation_probability_max.push(Math.max(...d.precip, 0));
|
||
weather_code.push(d.codes.reduce((a, b) => wmoRank(b) > wmoRank(a) ? b : a));
|
||
wind_speed_10m_max.push(speed(Math.max(...d.winds, 0), units));
|
||
}
|
||
return {
|
||
time,
|
||
temperature_2m_max,
|
||
temperature_2m_min,
|
||
precipitation_probability_max,
|
||
weather_code,
|
||
wind_speed_10m_max
|
||
};
|
||
}
|
||
/**
|
||
* Normalize raw NWS responses into the app's shared shape.
|
||
* @param {object} src - { points, forecast, hourly, grid, timeZone, units }
|
||
*/
|
||
function normalizeNWS(src) {
|
||
const { points, forecast, hourly, grid, timeZone, units } = src;
|
||
const tz = timeZone || "UTC";
|
||
const hourlyPeriods = hourly?.properties?.periods || [];
|
||
const forecastPeriods = forecast?.properties?.periods || [];
|
||
const cur = hourlyPeriods[0];
|
||
let current = null;
|
||
if (cur) {
|
||
const ws = parseWind(cur.windSpeed);
|
||
const apparent = cur.apparentTemperature != null ? temp(cur.apparentTemperature, units) : temp(cur.temperature, units);
|
||
current = {
|
||
time: localTimeString(cur.startTime, tz),
|
||
temperature_2m: temp(cur.temperature, units),
|
||
apparent_temperature: apparent,
|
||
relative_humidity_2m: cur.relativeHumidity?.value ?? null,
|
||
weather_code: shortForecastToWmo(cur.shortForecast, prob(cur)),
|
||
wind_speed_10m: ws != null ? speed(ws, units) : null,
|
||
wind_direction_10m: compassToDeg(cur.windDirection),
|
||
wind_gusts_10m: null,
|
||
pressure_msl: null,
|
||
uv_index: null,
|
||
is_day: cur.isDaytime ? 1 : 0
|
||
};
|
||
}
|
||
const time = [], temperature_2m = [], precipitation_probability = [], weather_code = [], wind_speed_10m = [];
|
||
for (const pd of hourlyPeriods) {
|
||
time.push(localTimeString(pd.startTime, tz));
|
||
temperature_2m.push(temp(pd.temperature, units));
|
||
precipitation_probability.push(prob(pd));
|
||
weather_code.push(shortForecastToWmo(pd.shortForecast, prob(pd)));
|
||
const ws = parseWind(pd.windSpeed);
|
||
wind_speed_10m.push(ws != null ? speed(ws, units) : null);
|
||
}
|
||
const daily = buildDaily(forecastPeriods, tz, units);
|
||
const rise = grid?.properties?.riseSet;
|
||
if (rise) {
|
||
const sr = rise.sunrise?.[0]?.time;
|
||
const ss = rise.sunset?.[0]?.time;
|
||
if (sr) daily.sunrise = [localTimeString(sr, tz)];
|
||
if (ss) daily.sunset = [localTimeString(ss, tz)];
|
||
}
|
||
return {
|
||
_source: "nws",
|
||
timezone: tz,
|
||
utc_offset_seconds: null,
|
||
elevation: grid?.properties?.elevation?.value,
|
||
current,
|
||
hourly: {
|
||
time,
|
||
temperature_2m,
|
||
precipitation_probability,
|
||
weather_code,
|
||
wind_speed_10m
|
||
},
|
||
daily
|
||
};
|
||
}
|
||
/**
|
||
* Fetch + normalize the NWS forecast for a latitude/longitude.
|
||
* US-only; throws for non-US points (NWS returns 404 outside its domain).
|
||
* @param {number} lat
|
||
* @param {number} lon
|
||
* @param {string} [units='imperial'] - 'imperial' or 'metric'
|
||
*/
|
||
async function fetchForecastNWS(lat, lon, units = "imperial") {
|
||
const points = await jsonGet(`${API}/points/${lat},${lon}`);
|
||
const props = points.properties || {};
|
||
const tz = props.timeZone || "America/New_York";
|
||
const [forecast, hourly, grid] = await Promise.all([
|
||
jsonGet(props.forecast),
|
||
jsonGet(props.forecastHourly),
|
||
jsonGet(props.forecastGridData)
|
||
]);
|
||
return normalizeNWS({
|
||
points,
|
||
forecast,
|
||
hourly,
|
||
grid,
|
||
timeZone: tz,
|
||
units
|
||
});
|
||
}
|
||
/**
|
||
* Backfill fields that NWS does not provide (pressure, UV index) from an
|
||
* Open-Meteo response, tagging them so the UI can show a tooltip that the
|
||
* value came from the non-selected source.
|
||
*/
|
||
function mergeFallbackData(nwsData, omData) {
|
||
const fallback = {};
|
||
const cur = nwsData.current || {};
|
||
const omCur = omData?.current || {};
|
||
if (omCur.pressure_msl != null) {
|
||
cur.pressure_msl = omCur.pressure_msl;
|
||
fallback.pressure = true;
|
||
}
|
||
if (omCur.uv_index != null) {
|
||
cur.uv_index = omCur.uv_index;
|
||
fallback.uv = true;
|
||
}
|
||
const omDaily = omData?.daily || {};
|
||
if (omDaily.uv_index_max?.length && nwsData.daily) {
|
||
nwsData.daily.uv_index_max = omDaily.uv_index_max;
|
||
fallback.uv = true;
|
||
}
|
||
if (Object.keys(fallback).length) nwsData._fallback = fallback;
|
||
return nwsData;
|
||
}
|
||
//#endregion
|
||
//#region \0vite/preload-helper.js
|
||
var scriptRel = "modulepreload";
|
||
var assetsURL = function(dep, importerUrl) {
|
||
return new URL(dep, importerUrl).href;
|
||
};
|
||
var seen = {};
|
||
var __vitePreload = function preload(baseModule, deps, importerUrl) {
|
||
let promise = Promise.resolve();
|
||
if (deps && deps.length > 0) {
|
||
const links = document.getElementsByTagName("link");
|
||
const cspNonceMeta = document.querySelector("meta[property=csp-nonce]");
|
||
const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute("nonce");
|
||
function allSettled(promises) {
|
||
return Promise.all(promises.map((p) => Promise.resolve(p).then((value) => ({
|
||
status: "fulfilled",
|
||
value
|
||
}), (reason) => ({
|
||
status: "rejected",
|
||
reason
|
||
}))));
|
||
}
|
||
function importMetaResolve(specifier) {
|
||
if (import.meta.resolve) return import.meta.resolve(specifier);
|
||
return new URL(
|
||
specifier,
|
||
/** #__KEEP__ */
|
||
import.meta.url
|
||
).href;
|
||
}
|
||
promise = allSettled(deps.map((dep) => {
|
||
dep = assetsURL(dep, importerUrl);
|
||
dep = importMetaResolve(dep);
|
||
if (dep in seen) return;
|
||
seen[dep] = true;
|
||
const isCss = dep.endsWith(".css");
|
||
for (let i = links.length - 1; i >= 0; i--) {
|
||
const link = links[i];
|
||
if (link.href === dep && (!isCss || link.rel === "stylesheet")) return;
|
||
}
|
||
const link = document.createElement("link");
|
||
link.rel = isCss ? "stylesheet" : scriptRel;
|
||
if (!isCss) link.as = "script";
|
||
link.crossOrigin = "";
|
||
link.href = dep;
|
||
if (cspNonce) link.setAttribute("nonce", cspNonce);
|
||
document.head.appendChild(link);
|
||
if (isCss) return new Promise((res, rej) => {
|
||
link.addEventListener("load", res);
|
||
link.addEventListener("error", () => rej(/* @__PURE__ */ new Error(`Unable to preload CSS for ${dep}`)));
|
||
});
|
||
}));
|
||
}
|
||
function handlePreloadError(err) {
|
||
const e = new Event("vite:preloadError", { cancelable: true });
|
||
e.payload = err;
|
||
window.dispatchEvent(e);
|
||
if (!e.defaultPrevented) throw err;
|
||
}
|
||
return promise.then((res) => {
|
||
for (const item of res || []) {
|
||
if (item.status !== "rejected") continue;
|
||
handlePreloadError(item.reason);
|
||
}
|
||
return baseModule().catch(handlePreloadError);
|
||
});
|
||
};
|
||
//#endregion
|
||
//#region src/lib/stores/app.svelte.js
|
||
var AppStore = class {
|
||
#locations = /* @__PURE__ */ state(proxy([]));
|
||
get locations() {
|
||
return get(this.#locations);
|
||
}
|
||
set locations(value) {
|
||
set(this.#locations, value, true);
|
||
}
|
||
#selectedLocationId = /* @__PURE__ */ state(null);
|
||
get selectedLocationId() {
|
||
return get(this.#selectedLocationId);
|
||
}
|
||
set selectedLocationId(value) {
|
||
set(this.#selectedLocationId, value, true);
|
||
}
|
||
#selectedLocation = /* @__PURE__ */ user_derived(() => this.locations.find((l) => l.id === this.selectedLocationId) || null);
|
||
get selectedLocation() {
|
||
return get(this.#selectedLocation);
|
||
}
|
||
set selectedLocation(value) {
|
||
set(this.#selectedLocation, value);
|
||
}
|
||
#forecastData = /* @__PURE__ */ state(null);
|
||
get forecastData() {
|
||
return get(this.#forecastData);
|
||
}
|
||
set forecastData(value) {
|
||
set(this.#forecastData, value, true);
|
||
}
|
||
#loading = /* @__PURE__ */ state(false);
|
||
get loading() {
|
||
return get(this.#loading);
|
||
}
|
||
set loading(value) {
|
||
set(this.#loading, value, true);
|
||
}
|
||
#error = /* @__PURE__ */ state("");
|
||
get error() {
|
||
return get(this.#error);
|
||
}
|
||
set error(value) {
|
||
set(this.#error, value, true);
|
||
}
|
||
#geocodingResults = /* @__PURE__ */ state(proxy([]));
|
||
get geocodingResults() {
|
||
return get(this.#geocodingResults);
|
||
}
|
||
set geocodingResults(value) {
|
||
set(this.#geocodingResults, value, true);
|
||
}
|
||
#geocodingLoading = /* @__PURE__ */ state(false);
|
||
get geocodingLoading() {
|
||
return get(this.#geocodingLoading);
|
||
}
|
||
set geocodingLoading(value) {
|
||
set(this.#geocodingLoading, value, true);
|
||
}
|
||
#geocodingError = /* @__PURE__ */ state("");
|
||
get geocodingError() {
|
||
return get(this.#geocodingError);
|
||
}
|
||
set geocodingError(value) {
|
||
set(this.#geocodingError, value, true);
|
||
}
|
||
#showAddLocation = /* @__PURE__ */ state(false);
|
||
get showAddLocation() {
|
||
return get(this.#showAddLocation);
|
||
}
|
||
set showAddLocation(value) {
|
||
set(this.#showAddLocation, value, true);
|
||
}
|
||
#showSettings = /* @__PURE__ */ state(false);
|
||
get showSettings() {
|
||
return get(this.#showSettings);
|
||
}
|
||
set showSettings(value) {
|
||
set(this.#showSettings, value, true);
|
||
}
|
||
#sidebarOpen = /* @__PURE__ */ state(false);
|
||
get sidebarOpen() {
|
||
return get(this.#sidebarOpen);
|
||
}
|
||
set sidebarOpen(value) {
|
||
set(this.#sidebarOpen, value, true);
|
||
}
|
||
#settings = /* @__PURE__ */ state(proxy({
|
||
units: "metric",
|
||
source: "open-meteo",
|
||
refreshInterval: 30,
|
||
alertsEnabled: true,
|
||
showLightning: false,
|
||
alertThresholds: {
|
||
precip: 70,
|
||
windGust: 40,
|
||
uvIndex: 6,
|
||
tempHigh: 35,
|
||
tempLow: 0,
|
||
thunderstorm: true
|
||
}
|
||
}));
|
||
get settings() {
|
||
return get(this.#settings);
|
||
}
|
||
set settings(value) {
|
||
set(this.#settings, value, true);
|
||
}
|
||
async init() {
|
||
try {
|
||
this.settings = await loadSettings();
|
||
} catch (e) {
|
||
console.error("Failed to load settings:", e);
|
||
}
|
||
try {
|
||
this.locations = await getLocations();
|
||
} catch (e) {
|
||
console.error("Failed to load locations:", e);
|
||
}
|
||
if (this.locations.length > 0) {
|
||
const current = this.locations.find((l) => l.isCurrent);
|
||
if (current) this.selectedLocationId = current.id;
|
||
else this.selectedLocationId = this.locations[0].id;
|
||
await this.refreshForecast();
|
||
}
|
||
}
|
||
/**
|
||
* Refresh settings from DB.
|
||
*/
|
||
async refreshSettings() {
|
||
try {
|
||
this.settings = await loadSettings();
|
||
} catch (e) {
|
||
console.error("Failed to refresh settings:", e);
|
||
}
|
||
}
|
||
/**
|
||
* Refresh the forecasts list from DB.
|
||
*/
|
||
async refreshLocations() {
|
||
try {
|
||
this.locations = await getLocations();
|
||
} catch (e) {
|
||
console.error("Failed to refresh locations:", e);
|
||
}
|
||
}
|
||
/**
|
||
* Fetch forecast data for the currently selected location.
|
||
*/
|
||
async refreshForecast(locationId = null) {
|
||
const locId = locationId || this.selectedLocationId;
|
||
if (!locId) return;
|
||
const location = this.locations.find((l) => l.id === locId);
|
||
if (!location) return;
|
||
this.loading = true;
|
||
this.error = "";
|
||
try {
|
||
const units = this.settings.units;
|
||
const source = this.settings.source || "open-meteo";
|
||
let data;
|
||
if (source === "nws") try {
|
||
data = await fetchForecastNWS(location.lat, location.lon, units);
|
||
try {
|
||
const om = await fetchForecast(location.lat, location.lon, units);
|
||
data = mergeFallbackData(data, om);
|
||
} catch (omErr) {
|
||
console.warn("Open-Meteo fields unavailable:", omErr.message);
|
||
}
|
||
} catch (nwsErr) {
|
||
console.warn("NWS unavailable, falling back to Open-Meteo:", nwsErr.message);
|
||
data = await fetchForecast(location.lat, location.lon, units);
|
||
}
|
||
else data = await fetchForecast(location.lat, location.lon, units);
|
||
this.forecastData = data;
|
||
if (data.timezone_abbreviation && !location.timezone) {}
|
||
} catch (e) {
|
||
this.error = e.message || "Failed to fetch weather data";
|
||
console.error("Forecast fetch error:", e);
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
}
|
||
/**
|
||
* Start (or restart) the auto-refresh timer based on the configured
|
||
* interval. A value of 0 (or missing) disables auto-refresh.
|
||
*/
|
||
startAutoRefresh() {
|
||
this.stopAutoRefresh();
|
||
const minutes = this.settings.refreshInterval;
|
||
if (!minutes || minutes <= 0) return;
|
||
this._refreshTimer = setInterval(() => {
|
||
this.refreshForecast();
|
||
}, minutes * 60 * 1e3);
|
||
}
|
||
/**
|
||
* Stop the auto-refresh timer if it is running.
|
||
*/
|
||
stopAutoRefresh() {
|
||
if (this._refreshTimer) {
|
||
clearInterval(this._refreshTimer);
|
||
this._refreshTimer = null;
|
||
}
|
||
}
|
||
/**
|
||
* Select a location and fetch its forecast.
|
||
* @param {string} id
|
||
*/
|
||
async selectLocation(id) {
|
||
this.selectedLocationId = id;
|
||
this.sidebarOpen = false;
|
||
await this.refreshForecast(id);
|
||
}
|
||
/**
|
||
* Search locations via geocoding API.
|
||
* @param {string} query
|
||
*/
|
||
async searchGeocoding(query) {
|
||
if (!query || query.length < 2) {
|
||
this.geocodingResults = [];
|
||
return;
|
||
}
|
||
this.geocodingLoading = true;
|
||
this.geocodingError = "";
|
||
try {
|
||
const { searchLocations } = await __vitePreload(async () => {
|
||
const { searchLocations } = await Promise.resolve().then(() => (init_weather(), weather_exports));
|
||
return { searchLocations };
|
||
}, void 0, import.meta.url);
|
||
this.geocodingResults = await searchLocations(query);
|
||
} catch (e) {
|
||
this.geocodingError = e.message || "Search failed";
|
||
this.geocodingResults = [];
|
||
} finally {
|
||
this.geocodingLoading = false;
|
||
}
|
||
}
|
||
};
|
||
var app$1 = new AppStore();
|
||
//#endregion
|
||
//#region src/lib/api/weather-codes.js
|
||
/**
|
||
* WMO Weather Interpretation Codes (WW) → icon + description.
|
||
* Reference: https://www.nodc.noaa.gov/archive/arc0021/0002199/1.1/data/0-data/HTML/WMO-CODE/WMO4677.HTM
|
||
*/
|
||
var CODE_MAP = {
|
||
0: {
|
||
icon: "☀️",
|
||
label: "Clear",
|
||
desc: "Clear sky"
|
||
},
|
||
1: {
|
||
icon: "🌤️",
|
||
label: "Mostly Clear",
|
||
desc: "Mainly clear"
|
||
},
|
||
2: {
|
||
icon: "⛅",
|
||
label: "Partly Cloudy",
|
||
desc: "Partly cloudy"
|
||
},
|
||
3: {
|
||
icon: "☁️",
|
||
label: "Overcast",
|
||
desc: "Overcast"
|
||
},
|
||
45: {
|
||
icon: "🌫️",
|
||
label: "Fog",
|
||
desc: "Fog"
|
||
},
|
||
48: {
|
||
icon: "🌫️",
|
||
label: "Rime Fog",
|
||
desc: "Depositing rime fog"
|
||
},
|
||
51: {
|
||
icon: "🌦️",
|
||
label: "Light Drizzle",
|
||
desc: "Light drizzle"
|
||
},
|
||
53: {
|
||
icon: "🌦️",
|
||
label: "Drizzle",
|
||
desc: "Moderate drizzle"
|
||
},
|
||
55: {
|
||
icon: "🌧️",
|
||
label: "Heavy Drizzle",
|
||
desc: "Dense drizzle"
|
||
},
|
||
56: {
|
||
icon: "🌧️",
|
||
label: "Freezing Drizzle",
|
||
desc: "Light freezing drizzle"
|
||
},
|
||
57: {
|
||
icon: "🌧️",
|
||
label: "Freezing Drizzle",
|
||
desc: "Dense freezing drizzle"
|
||
},
|
||
61: {
|
||
icon: "🌦️",
|
||
label: "Light Rain",
|
||
desc: "Slight rain"
|
||
},
|
||
63: {
|
||
icon: "🌧️",
|
||
label: "Rain",
|
||
desc: "Moderate rain"
|
||
},
|
||
65: {
|
||
icon: "🌧️",
|
||
label: "Heavy Rain",
|
||
desc: "Heavy rain"
|
||
},
|
||
66: {
|
||
icon: "🌧️",
|
||
label: "Freezing Rain",
|
||
desc: "Light freezing rain"
|
||
},
|
||
67: {
|
||
icon: "🌧️",
|
||
label: "Freezing Rain",
|
||
desc: "Heavy freezing rain"
|
||
},
|
||
71: {
|
||
icon: "🌨️",
|
||
label: "Light Snow",
|
||
desc: "Slight snowfall"
|
||
},
|
||
73: {
|
||
icon: "🌨️",
|
||
label: "Snow",
|
||
desc: "Moderate snowfall"
|
||
},
|
||
75: {
|
||
icon: "❄️",
|
||
label: "Heavy Snow",
|
||
desc: "Heavy snowfall"
|
||
},
|
||
77: {
|
||
icon: "❄️",
|
||
label: "Snow Grains",
|
||
desc: "Snow grains"
|
||
},
|
||
80: {
|
||
icon: "🌦️",
|
||
label: "Light Showers",
|
||
desc: "Slight rain showers"
|
||
},
|
||
81: {
|
||
icon: "🌧️",
|
||
label: "Showers",
|
||
desc: "Moderate rain showers"
|
||
},
|
||
82: {
|
||
icon: "⛈️",
|
||
label: "Heavy Showers",
|
||
desc: "Violent rain showers"
|
||
},
|
||
85: {
|
||
icon: "🌨️",
|
||
label: "Snow Showers",
|
||
desc: "Slight snow showers"
|
||
},
|
||
86: {
|
||
icon: "❄️",
|
||
label: "Snow Showers",
|
||
desc: "Heavy snow showers"
|
||
},
|
||
95: {
|
||
icon: "⛈️",
|
||
label: "Thunderstorm",
|
||
desc: "Thunderstorm"
|
||
},
|
||
96: {
|
||
icon: "⛈️",
|
||
label: "Thunderstorm",
|
||
desc: "Thunderstorm with slight hail"
|
||
},
|
||
99: {
|
||
icon: "⛈️",
|
||
label: "Thunderstorm",
|
||
desc: "Thunderstorm with heavy hail"
|
||
}
|
||
};
|
||
/**
|
||
* Get weather display info for a WMO code.
|
||
* @param {number} code - WMO weather code (0-99)
|
||
* @returns {{ icon: string, label: string, desc: string }}
|
||
*/
|
||
var RAIN_CODES = /* @__PURE__ */ new Set([
|
||
56,
|
||
57,
|
||
61,
|
||
63,
|
||
65,
|
||
66,
|
||
67,
|
||
80,
|
||
81,
|
||
82,
|
||
95,
|
||
96,
|
||
99
|
||
]);
|
||
var LOW_CHANCE_PRECIP = 35;
|
||
/**
|
||
* Get weather display info for a WMO code.
|
||
* @param {number} code - WMO weather code (0-99)
|
||
* @param {number|null} prob - precipitation chance (%) for the period. When a
|
||
* rain/storm code has only a low chance, show light drizzle instead.
|
||
* @returns {{ icon: string, label: string, desc: string }}
|
||
*/
|
||
function getWeatherInfo(code, prob = null) {
|
||
const info = CODE_MAP[code] || {
|
||
icon: "❓",
|
||
label: "Unknown",
|
||
desc: "Unknown conditions"
|
||
};
|
||
if (prob != null && prob < LOW_CHANCE_PRECIP && RAIN_CODES.has(code)) return {
|
||
icon: CODE_MAP[51].icon,
|
||
label: "Light Drizzle",
|
||
desc: "Chance of light rain"
|
||
};
|
||
return info;
|
||
}
|
||
/**
|
||
* Get a severity level for color-coding weather.
|
||
* @param {number} code
|
||
* @returns {'clear'|'cloudy'|'rain'|'storm'|'snow'|'fog'}
|
||
*/
|
||
function getWeatherSeverity(code) {
|
||
if (code === 0) return "clear";
|
||
if (code <= 3) return "cloudy";
|
||
if (code <= 48) return "fog";
|
||
if (code <= 67) return "rain";
|
||
if (code <= 86) return "snow";
|
||
return "storm";
|
||
}
|
||
/**
|
||
* Check if a weather code indicates dangerous conditions.
|
||
* @param {number} code
|
||
* @returns {boolean}
|
||
*/
|
||
function isSevereWeather(code) {
|
||
return code === 65 || code === 75 || code === 82 || code === 86 || code === 95 || code === 96 || code === 99;
|
||
}
|
||
//#endregion
|
||
//#region src/lib/dates.js
|
||
/**
|
||
* Date helpers for Open-Meteo strings.
|
||
*
|
||
* Open-Meteo returns calendar dates like "2026-08-19" (in the location's
|
||
* timezone). `new Date("2026-08-19")` parses that as UTC midnight, so in U.S.
|
||
* Central (UTC-5) it renders as the PREVIOUS weekday, shifting every day
|
||
* label one day back. Build the Date from its parts instead so it is a local
|
||
* calendar date with no offset/timezone shift.
|
||
*/
|
||
function parseLocalDate(dateStr) {
|
||
const [y, m, d] = String(dateStr).split("-").map(Number);
|
||
return new Date(y, (m || 1) - 1, d || 1);
|
||
}
|
||
//#endregion
|
||
//#region src/lib/stores/notifications.svelte.js
|
||
init_db();
|
||
var NotificationStore = class {
|
||
#alerts = /* @__PURE__ */ state(proxy([]));
|
||
get alerts() {
|
||
return get(this.#alerts);
|
||
}
|
||
set alerts(value) {
|
||
set(this.#alerts, value, true);
|
||
}
|
||
#dismissedIds = /* @__PURE__ */ state(proxy(/* @__PURE__ */ new Set()));
|
||
get dismissedIds() {
|
||
return get(this.#dismissedIds);
|
||
}
|
||
set dismissedIds(value) {
|
||
set(this.#dismissedIds, value, true);
|
||
}
|
||
lastForecastDate = null;
|
||
_dismissalsLoaded = false;
|
||
analyze() {
|
||
const data = app$1.forecastData;
|
||
const settings = app$1.settings;
|
||
if (!data || !settings.alertsEnabled) {
|
||
this.alerts = [];
|
||
return;
|
||
}
|
||
if (!this._dismissalsLoaded) {
|
||
this._dismissalsLoaded = true;
|
||
this._loadDismissed();
|
||
}
|
||
const forecastDate = data.daily?.time?.[0] || null;
|
||
if (forecastDate && this.lastForecastDate && forecastDate !== this.lastForecastDate) this.resetDismissed();
|
||
this.lastForecastDate = forecastDate;
|
||
const t = settings.alertThresholds;
|
||
const newAlerts = [];
|
||
function add(id, type, severity, message, icon) {
|
||
if (!app$1.settings.alertsEnabled) return;
|
||
newAlerts.push({
|
||
id,
|
||
type,
|
||
severity,
|
||
message,
|
||
icon
|
||
});
|
||
}
|
||
const current = data.current;
|
||
const daily = data.daily || {};
|
||
if (t.thunderstorm && isSevereWeather(current.weather_code)) add("storm-now", "storm", "danger", "⚡ Thunderstorm active — seek shelter if outdoors", "⛈️");
|
||
if (daily.time) for (let i = 0; i < daily.time.length && i < 3; i++) {
|
||
const day = daily.time[i];
|
||
const dateLabel = i === 0 ? "Today" : i === 1 ? "Tomorrow" : parseLocalDate(day).toLocaleDateString("en-US", { weekday: "short" });
|
||
const precip = daily.precipitation_probability_max?.[i] || 0;
|
||
if (precip >= t.precip) add(`rain-${i}`, "precip", "warning", `🌧️ ${precip}% chance of rain ${dateLabel.toLowerCase()}`, "🌧️");
|
||
const code = daily.weather_code?.[i];
|
||
if (t.thunderstorm && isSevereWeather(code)) add(`storm-${i}`, "storm", "danger", `⛈️ Thunderstorms expected ${dateLabel.toLowerCase()}`, "⛈️");
|
||
const tempHigh = daily.temperature_2m_max?.[i];
|
||
if (tempHigh != null && tempHigh > t.tempHigh) add(`heat-${i}`, "heat", "warning", `🔥 ${dateLabel} high of ${Math.round(tempHigh)}° — stay hydrated`, "🔥");
|
||
const tempMin = daily.temperature_2m_min?.[i];
|
||
if (tempMin != null && tempMin < t.tempLow) add(`cold-${i}`, "cold", "warning", `🥶 ${dateLabel} low of ${Math.round(tempMin)}° — bundle up`, "❄️");
|
||
const uv = daily.uv_index_max?.[i];
|
||
if (uv != null && uv >= t.uvIndex) add(`uv-${i}`, "uv", "warning", `☀️ High UV index (${uv}) ${dateLabel.toLowerCase()} — wear sunscreen`, "🧴");
|
||
const wind = daily.wind_speed_10m_max?.[i];
|
||
if (wind != null && wind >= t.windGust) add(`wind-${i}`, "wind", "warning", `💨 Strong winds (${Math.round(wind)} ${app$1.settings.units === "imperial" ? "mph" : "km/h"}) ${dateLabel.toLowerCase()}`, "💨");
|
||
}
|
||
this.alerts = newAlerts.filter((a) => !this.dismissedIds.has(a.id));
|
||
}
|
||
/**
|
||
* Dismiss a specific alert. Persists to IndexedDB.
|
||
* @param {string} alertId
|
||
*/
|
||
dismiss(alertId) {
|
||
this.dismissedIds.add(alertId);
|
||
this.alerts = this.alerts.filter((a) => a.id !== alertId);
|
||
this._persistDismissed();
|
||
}
|
||
/**
|
||
* Reset all dismissed alerts (e.g., when forecast date changes).
|
||
*/
|
||
resetDismissed() {
|
||
this.dismissedIds.clear();
|
||
this._persistDismissed();
|
||
}
|
||
/**
|
||
* Load dismissed alert IDs from the persisted settings.
|
||
*/
|
||
_loadDismissed() {
|
||
const saved = app$1.settings.dismissedAlerts || [];
|
||
this.dismissedIds = new Set(saved);
|
||
}
|
||
/**
|
||
* Save dismissed alert IDs to settings in IndexedDB.
|
||
*/
|
||
async _persistDismissed() {
|
||
const arr = Array.from(this.dismissedIds);
|
||
app$1.settings.dismissedAlerts = arr;
|
||
try {
|
||
await saveSettings(app$1.settings);
|
||
} catch (e) {
|
||
console.error("Failed to persist dismissed alerts:", e);
|
||
}
|
||
}
|
||
};
|
||
var notifications = new NotificationStore();
|
||
//#endregion
|
||
//#region src/lib/autofocus.js
|
||
/**
|
||
* Svelte action: autofocus an element on mount.
|
||
* Usage: <input use:autofocus />
|
||
*/
|
||
function autofocus(node) {
|
||
requestAnimationFrame(() => {
|
||
node.focus();
|
||
if ("ontouchstart" in window) setTimeout(() => node.focus(), 100);
|
||
});
|
||
}
|
||
//#endregion
|
||
//#region src/components/LocationSidebar.svelte
|
||
init_db();
|
||
var root$10 = /* @__PURE__ */ from_html(`<span class="search-spinner svelte-1j1qnn9">⏳</span>`);
|
||
var root_1$9 = /* @__PURE__ */ from_html(`<button class="geocoding-item svelte-1j1qnn9"><span class="geo-name svelte-1j1qnn9"> </span> <span class="geo-detail svelte-1j1qnn9"> </span></button>`);
|
||
var root_2$8 = /* @__PURE__ */ from_html(`<div class="geocoding-dropdown svelte-1j1qnn9"></div>`);
|
||
var root_3$7 = /* @__PURE__ */ from_html(`<div class="location-row current-row svelte-1j1qnn9"><button><span class="loc-icon svelte-1j1qnn9">📍</span> <span class="loc-name truncate svelte-1j1qnn9"> </span></button></div>`);
|
||
var root_4$5 = /* @__PURE__ */ from_html(`<div class="location-row svelte-1j1qnn9"><button><span class="loc-icon svelte-1j1qnn9">🏙️</span> <span class="loc-name truncate svelte-1j1qnn9"> </span></button> <div class="loc-actions svelte-1j1qnn9"><button class="loc-action-btn svelte-1j1qnn9" title="Set as current location">📍</button> <button class="loc-action-btn svelte-1j1qnn9" title="Remove location">✕</button></div></div>`);
|
||
var root_5$5 = /* @__PURE__ */ from_html(`<div class="empty-locations svelte-1j1qnn9"><p class="text-muted text-sm">No saved locations.</p> <p class="text-muted text-xs mt-1">Enable location access or add one below.</p></div>`);
|
||
var root_6$3 = /* @__PURE__ */ from_html(`<div class="sidebar-content svelte-1j1qnn9"><div class="sidebar-header svelte-1j1qnn9"><h2 class="svelte-1j1qnn9">🌤️ WeatherLens</h2></div> <div class="search-box svelte-1j1qnn9"><input type="search" placeholder="Search locations..." autocomplete="off" class="svelte-1j1qnn9"/> <!> <!></div> <nav class="locations-nav svelte-1j1qnn9"><!> <!> <!></nav> <div class="sidebar-footer svelte-1j1qnn9"><button class="btn btn-ghost btn-sm w-full">⚙️ Settings</button> <button class="btn btn-primary btn-sm w-full mt-2">+ Add Location</button></div></div>`);
|
||
function LocationSidebar($$anchor, $$props) {
|
||
push($$props, true);
|
||
let searchQuery = /* @__PURE__ */ state("");
|
||
let searchTimer = null;
|
||
function handleSearch(e) {
|
||
const val = e.target.value;
|
||
set(searchQuery, val, true);
|
||
clearTimeout(searchTimer);
|
||
if (val.length >= 2) searchTimer = setTimeout(() => app$1.searchGeocoding(val), 300);
|
||
else app$1.geocodingResults = [];
|
||
}
|
||
async function handleDeleteLocation(id) {
|
||
try {
|
||
await deleteLocation(id);
|
||
const locations = await __vitePreload(() => Promise.resolve().then(() => (init_db(), db_exports)).then((m) => m.getLocations()), void 0, import.meta.url);
|
||
if (id === app$1.selectedLocationId) if (locations.length > 0) await app$1.selectLocation(locations[0].id);
|
||
else {
|
||
app$1.selectedLocationId = null;
|
||
app$1.forecastData = null;
|
||
}
|
||
await app$1.refreshLocations();
|
||
} catch (e) {
|
||
console.error("Failed to delete location:", e);
|
||
}
|
||
}
|
||
function handleSetCurrent(id) {
|
||
if (app$1.selectedLocation?.isCurrent) return;
|
||
setCurrentLocation(id).then(() => app$1.refreshLocations());
|
||
}
|
||
function openAddLocation() {
|
||
set(searchQuery, "");
|
||
app$1.geocodingResults = [];
|
||
app$1.showAddLocation = true;
|
||
}
|
||
async function handleSelectGeocoding(result) {
|
||
set(searchQuery, "");
|
||
app$1.geocodingResults = [];
|
||
const { addLocation } = await __vitePreload(async () => {
|
||
const { addLocation } = await Promise.resolve().then(() => (init_db(), db_exports));
|
||
return { addLocation };
|
||
}, void 0, import.meta.url);
|
||
await addLocation({
|
||
name: `${result.name}${result.country ? ", " + result.country : ""}`,
|
||
lat: result.latitude,
|
||
lon: result.longitude,
|
||
isCurrent: false
|
||
});
|
||
await app$1.refreshLocations();
|
||
const locations = await __vitePreload(() => Promise.resolve().then(() => (init_db(), db_exports)).then((m) => m.getLocations()), void 0, import.meta.url);
|
||
const newest = locations[locations.length - 1];
|
||
if (newest) app$1.selectLocation(newest.id);
|
||
}
|
||
var div = root_6$3();
|
||
var div_1 = sibling(child(div), 2);
|
||
var input = child(div_1);
|
||
remove_input_defaults(input);
|
||
var node = sibling(input, 2);
|
||
var consequent = ($$anchor) => {
|
||
append($$anchor, root$10());
|
||
};
|
||
if_block(node, ($$render) => {
|
||
if (app$1.geocodingLoading) $$render(consequent);
|
||
});
|
||
var node_1 = sibling(node, 2);
|
||
var consequent_1 = ($$anchor) => {
|
||
var div_2 = root_2$8();
|
||
each(div_2, 21, () => app$1.geocodingResults, index, ($$anchor, result) => {
|
||
var button = root_1$9();
|
||
var span_1 = child(button);
|
||
var text = child(span_1, true);
|
||
reset(span_1);
|
||
var span_2 = sibling(span_1, 2);
|
||
var text_1 = child(span_2);
|
||
reset(span_2);
|
||
reset(button);
|
||
template_effect(() => {
|
||
set_text(text, get(result).name);
|
||
set_text(text_1, `${(get(result).admin1 || "") ?? ""}${get(result).country ? ", " + get(result).country : ""}`);
|
||
});
|
||
delegated("click", button, () => handleSelectGeocoding(get(result)));
|
||
append($$anchor, button);
|
||
});
|
||
reset(div_2);
|
||
append($$anchor, div_2);
|
||
};
|
||
if_block(node_1, ($$render) => {
|
||
if (app$1.geocodingResults.length > 0) $$render(consequent_1);
|
||
});
|
||
reset(div_1);
|
||
var nav = sibling(div_1, 2);
|
||
var node_2 = child(nav);
|
||
each(node_2, 17, () => app$1.locations, index, ($$anchor, location) => {
|
||
var fragment = comment();
|
||
var node_3 = first_child(fragment);
|
||
var consequent_2 = ($$anchor) => {
|
||
var div_3 = root_3$7();
|
||
var button_1 = child(div_3);
|
||
let classes;
|
||
var span_3 = sibling(child(button_1), 2);
|
||
var text_2 = child(span_3, true);
|
||
reset(span_3);
|
||
reset(button_1);
|
||
reset(div_3);
|
||
template_effect(() => {
|
||
classes = set_class(button_1, 1, "location-item svelte-1j1qnn9", null, classes, { active: app$1.selectedLocationId === get(location).id });
|
||
set_text(text_2, get(location).name);
|
||
});
|
||
delegated("click", button_1, () => app$1.selectLocation(get(location).id));
|
||
append($$anchor, div_3);
|
||
};
|
||
if_block(node_3, ($$render) => {
|
||
if (get(location).isCurrent) $$render(consequent_2);
|
||
});
|
||
append($$anchor, fragment);
|
||
});
|
||
var node_4 = sibling(node_2, 2);
|
||
each(node_4, 17, () => app$1.locations, index, ($$anchor, location) => {
|
||
var fragment_1 = comment();
|
||
var node_5 = first_child(fragment_1);
|
||
var consequent_3 = ($$anchor) => {
|
||
var div_4 = root_4$5();
|
||
var button_2 = child(div_4);
|
||
let classes_1;
|
||
var span_4 = sibling(child(button_2), 2);
|
||
var text_3 = child(span_4, true);
|
||
reset(span_4);
|
||
reset(button_2);
|
||
var div_5 = sibling(button_2, 2);
|
||
var button_3 = child(div_5);
|
||
var button_4 = sibling(button_3, 2);
|
||
reset(div_5);
|
||
reset(div_4);
|
||
template_effect(() => {
|
||
classes_1 = set_class(button_2, 1, "location-item svelte-1j1qnn9", null, classes_1, { active: app$1.selectedLocationId === get(location).id });
|
||
set_text(text_3, get(location).name);
|
||
});
|
||
delegated("click", button_2, () => app$1.selectLocation(get(location).id));
|
||
delegated("click", button_3, () => handleSetCurrent(get(location).id));
|
||
delegated("click", button_4, () => handleDeleteLocation(get(location).id));
|
||
append($$anchor, div_4);
|
||
};
|
||
if_block(node_5, ($$render) => {
|
||
if (!get(location).isCurrent) $$render(consequent_3);
|
||
});
|
||
append($$anchor, fragment_1);
|
||
});
|
||
var node_6 = sibling(node_4, 2);
|
||
var consequent_4 = ($$anchor) => {
|
||
append($$anchor, root_5$5());
|
||
};
|
||
if_block(node_6, ($$render) => {
|
||
if (app$1.locations.length === 0) $$render(consequent_4);
|
||
});
|
||
reset(nav);
|
||
var div_7 = sibling(nav, 2);
|
||
var button_5 = child(div_7);
|
||
var button_6 = sibling(button_5, 2);
|
||
reset(div_7);
|
||
reset(div);
|
||
template_effect(() => set_value(input, get(searchQuery)));
|
||
delegated("input", input, handleSearch);
|
||
delegated("click", button_5, () => app$1.showSettings = true);
|
||
delegated("click", button_6, openAddLocation);
|
||
append($$anchor, div);
|
||
pop();
|
||
}
|
||
delegate(["input", "click"]);
|
||
//#endregion
|
||
//#region src/components/SourceTooltip.svelte
|
||
var root$9 = /* @__PURE__ */ from_html(`<span class="srctip svelte-1hhb1ss" tabindex="0" role="note"><span class="srctip-icon svelte-1hhb1ss" aria-hidden="true">ⓘ</span> <span class="srctip-bubble svelte-1hhb1ss"> </span></span>`);
|
||
function SourceTooltip($$anchor, $$props) {
|
||
/** Small info badge whose tooltip explains the value came from the
|
||
* non-selected source (e.g. Open-Meteo while NWS is selected). */
|
||
let label = prop($$props, "label", 3, "");
|
||
var span = root$9();
|
||
var span_1 = sibling(child(span), 2);
|
||
var text = child(span_1, true);
|
||
reset(span_1);
|
||
reset(span);
|
||
template_effect(() => {
|
||
set_attribute(span, "aria-label", label());
|
||
set_text(text, label());
|
||
});
|
||
append($$anchor, span);
|
||
}
|
||
//#endregion
|
||
//#region src/components/CurrentWeather.svelte
|
||
var root$8 = /* @__PURE__ */ from_html(`<span class="feels-like svelte-1k8dsh"> </span>`);
|
||
var root_1$8 = /* @__PURE__ */ from_html(`<div class="hero-range svelte-1k8dsh"><span class="range-high svelte-1k8dsh"> </span> <span class="range-low svelte-1k8dsh"> </span></div>`);
|
||
var root_2$7 = /* @__PURE__ */ from_html(`<span class="metric-sub svelte-1k8dsh"> </span>`);
|
||
var root_3$6 = /* @__PURE__ */ from_html(`<div class="metric-card svelte-1k8dsh"><span class="metric-icon svelte-1k8dsh">🌪️</span> <span class="metric-label svelte-1k8dsh">Gusts</span> <span class="metric-value svelte-1k8dsh"> </span></div>`);
|
||
var root_4$4 = /* @__PURE__ */ from_html(`<div class="sun-times svelte-1k8dsh"><div class="sun-time svelte-1k8dsh"><span class="sun-icon svelte-1k8dsh">🌅</span> <span> </span></div> <div class="sun-divider svelte-1k8dsh"></div> <div class="sun-time svelte-1k8dsh"><span class="sun-icon svelte-1k8dsh">🌇</span> <span> </span></div></div>`);
|
||
var root_5$4 = /* @__PURE__ */ from_html(`<div><div class="hero-main svelte-1k8dsh"><div class="hero-temp svelte-1k8dsh"><span class="temp-value svelte-1k8dsh"> </span> <span class="temp-unit svelte-1k8dsh"> </span></div> <div class="hero-icon svelte-1k8dsh"> </div></div> <div class="hero-desc svelte-1k8dsh"> <!></div> <!> <div class="metrics-grid svelte-1k8dsh"><div class="metric-card svelte-1k8dsh"><span class="metric-icon svelte-1k8dsh">💧</span> <span class="metric-label svelte-1k8dsh">Humidity</span> <span class="metric-value svelte-1k8dsh"> </span></div> <div class="metric-card svelte-1k8dsh"><span class="metric-icon svelte-1k8dsh">💨</span> <span class="metric-label svelte-1k8dsh">Wind</span> <span class="metric-value svelte-1k8dsh"> </span> <span class="metric-sub svelte-1k8dsh"> </span></div> <div class="metric-card svelte-1k8dsh"><span class="metric-icon svelte-1k8dsh">🌡️</span> <span class="metric-label svelte-1k8dsh">Pressure</span> <span class="metric-value svelte-1k8dsh"> <!></span></div> <div class="metric-card svelte-1k8dsh"><span class="metric-icon svelte-1k8dsh">☀️</span> <span class="metric-label svelte-1k8dsh">UV Index</span> <span class="metric-value svelte-1k8dsh"> <!></span> <!></div> <!></div> <!></div>`);
|
||
function CurrentWeather($$anchor, $$props) {
|
||
push($$props, true);
|
||
const data = /* @__PURE__ */ user_derived(() => app$1.forecastData);
|
||
const current = /* @__PURE__ */ user_derived(() => get(data)?.current);
|
||
const daily = /* @__PURE__ */ user_derived(() => get(data)?.daily);
|
||
const weatherInfo = /* @__PURE__ */ user_derived(() => getWeatherInfo(get(current)?.weather_code || 0));
|
||
const severity = /* @__PURE__ */ user_derived(() => getWeatherSeverity(get(current)?.weather_code || 0));
|
||
const bgClass = /* @__PURE__ */ user_derived(() => `weather-bg-${get(severity)}`);
|
||
function formatTime(isoString) {
|
||
if (!isoString) return "--";
|
||
const timePart = isoString.split("T")[1];
|
||
if (!timePart) return "--";
|
||
const [h, m] = timePart.split(":");
|
||
const hours = parseInt(h, 10);
|
||
const ampm = hours >= 12 ? "PM" : "AM";
|
||
return `${hours % 12 || 12}:${m} ${ampm}`;
|
||
}
|
||
function windCompass(deg) {
|
||
if (deg == null) return "--";
|
||
return [
|
||
"N",
|
||
"NNE",
|
||
"NE",
|
||
"ENE",
|
||
"E",
|
||
"ESE",
|
||
"SE",
|
||
"SSE",
|
||
"S",
|
||
"SSW",
|
||
"SW",
|
||
"WSW",
|
||
"W",
|
||
"WNW",
|
||
"NW",
|
||
"NNW"
|
||
][Math.round(deg / 22.5) % 16];
|
||
}
|
||
const unit = /* @__PURE__ */ user_derived(() => app$1.settings.units === "imperial" ? "°F" : "°C");
|
||
const windUnit = /* @__PURE__ */ user_derived(() => app$1.settings.units === "imperial" ? "mph" : "km/h");
|
||
const fbPressure = /* @__PURE__ */ user_derived(() => !!get(data)?._fallback?.pressure);
|
||
const fbUv = /* @__PURE__ */ user_derived(() => !!get(data)?._fallback?.uv);
|
||
var fragment = comment();
|
||
var node = first_child(fragment);
|
||
var consequent_7 = ($$anchor) => {
|
||
var div = root_5$4();
|
||
var div_1 = child(div);
|
||
var div_2 = child(div_1);
|
||
var span = child(div_2);
|
||
var text = child(span, true);
|
||
reset(span);
|
||
var span_1 = sibling(span, 2);
|
||
var text_1 = child(span_1, true);
|
||
reset(span_1);
|
||
reset(div_2);
|
||
var div_3 = sibling(div_2, 2);
|
||
var text_2 = child(div_3, true);
|
||
reset(div_3);
|
||
reset(div_1);
|
||
var div_4 = sibling(div_1, 2);
|
||
var text_3 = child(div_4);
|
||
var node_1 = sibling(text_3);
|
||
var consequent = ($$anchor) => {
|
||
var span_2 = root$8();
|
||
var text_4 = child(span_2);
|
||
reset(span_2);
|
||
template_effect(($0) => set_text(text_4, `· Feels like ${$0 ?? ""}${get(unit) ?? ""}`), [() => Math.round(get(current).apparent_temperature)]);
|
||
append($$anchor, span_2);
|
||
};
|
||
var d = /* @__PURE__ */ user_derived(() => Math.abs(get(current).temperature_2m - get(current).apparent_temperature) >= 2);
|
||
if_block(node_1, ($$render) => {
|
||
if (get(d)) $$render(consequent);
|
||
});
|
||
reset(div_4);
|
||
var node_2 = sibling(div_4, 2);
|
||
var consequent_1 = ($$anchor) => {
|
||
var div_5 = root_1$8();
|
||
var span_3 = child(div_5);
|
||
var text_5 = child(span_3);
|
||
reset(span_3);
|
||
var span_4 = sibling(span_3, 2);
|
||
var text_6 = child(span_4);
|
||
reset(span_4);
|
||
reset(div_5);
|
||
template_effect(($0, $1) => {
|
||
set_text(text_5, `▲ H ${$0 ?? ""}${get(unit) ?? ""}`);
|
||
set_text(text_6, `▼ L ${$1 ?? ""}${get(unit) ?? ""}`);
|
||
}, [() => Math.round(get(daily).temperature_2m_max[0]), () => Math.round(get(daily).temperature_2m_min[0])]);
|
||
append($$anchor, div_5);
|
||
};
|
||
if_block(node_2, ($$render) => {
|
||
if (get(daily)?.temperature_2m_max?.[0] != null && get(daily)?.temperature_2m_min?.[0] != null) $$render(consequent_1);
|
||
});
|
||
var div_6 = sibling(node_2, 2);
|
||
var div_7 = child(div_6);
|
||
var span_5 = sibling(child(div_7), 4);
|
||
var text_7 = child(span_5);
|
||
reset(span_5);
|
||
reset(div_7);
|
||
var div_8 = sibling(div_7, 2);
|
||
var span_6 = sibling(child(div_8), 4);
|
||
var text_8 = child(span_6);
|
||
reset(span_6);
|
||
var span_7 = sibling(span_6, 2);
|
||
var text_9 = child(span_7, true);
|
||
reset(span_7);
|
||
reset(div_8);
|
||
var div_9 = sibling(div_8, 2);
|
||
var span_8 = sibling(child(div_9), 4);
|
||
var text_10 = child(span_8);
|
||
var node_3 = sibling(text_10);
|
||
var consequent_2 = ($$anchor) => {
|
||
SourceTooltip($$anchor, { label: "Pressure from Open-Meteo — NWS does not provide it" });
|
||
};
|
||
if_block(node_3, ($$render) => {
|
||
if (get(fbPressure)) $$render(consequent_2);
|
||
});
|
||
reset(span_8);
|
||
reset(div_9);
|
||
var div_10 = sibling(div_9, 2);
|
||
var span_9 = sibling(child(div_10), 4);
|
||
var text_11 = child(span_9, true);
|
||
var node_4 = sibling(text_11);
|
||
var consequent_3 = ($$anchor) => {
|
||
SourceTooltip($$anchor, { label: "UV index from Open-Meteo — NWS does not provide it" });
|
||
};
|
||
if_block(node_4, ($$render) => {
|
||
if (get(fbUv)) $$render(consequent_3);
|
||
});
|
||
reset(span_9);
|
||
var node_5 = sibling(span_9, 2);
|
||
var consequent_4 = ($$anchor) => {
|
||
var span_10 = root_2$7();
|
||
var text_12 = child(span_10, true);
|
||
reset(span_10);
|
||
template_effect(() => set_text(text_12, get(current).uv_index <= 2 ? "Low" : get(current).uv_index <= 5 ? "Moderate" : get(current).uv_index <= 7 ? "High" : "Very High"));
|
||
append($$anchor, span_10);
|
||
};
|
||
if_block(node_5, ($$render) => {
|
||
if (get(current).uv_index != null) $$render(consequent_4);
|
||
});
|
||
reset(div_10);
|
||
var node_6 = sibling(div_10, 2);
|
||
var consequent_5 = ($$anchor) => {
|
||
var div_11 = root_3$6();
|
||
var span_11 = sibling(child(div_11), 4);
|
||
var text_13 = child(span_11);
|
||
reset(span_11);
|
||
reset(div_11);
|
||
template_effect(($0) => set_text(text_13, `${$0 ?? ""} ${get(windUnit) ?? ""}`), [() => Math.round(get(current).wind_gusts_10m)]);
|
||
append($$anchor, div_11);
|
||
};
|
||
if_block(node_6, ($$render) => {
|
||
if (get(current).wind_gusts_10m) $$render(consequent_5);
|
||
});
|
||
reset(div_6);
|
||
var node_7 = sibling(div_6, 2);
|
||
var consequent_6 = ($$anchor) => {
|
||
var div_12 = root_4$4();
|
||
var div_13 = child(div_12);
|
||
var span_12 = sibling(child(div_13), 2);
|
||
var text_14 = child(span_12);
|
||
reset(span_12);
|
||
reset(div_13);
|
||
var div_14 = sibling(div_13, 4);
|
||
var span_13 = sibling(child(div_14), 2);
|
||
var text_15 = child(span_13);
|
||
reset(span_13);
|
||
reset(div_14);
|
||
reset(div_12);
|
||
template_effect(($0, $1) => {
|
||
set_text(text_14, `Sunrise ${$0 ?? ""}`);
|
||
set_text(text_15, `Sunset ${$1 ?? ""}`);
|
||
}, [() => formatTime(get(daily).sunrise[0]), () => formatTime(get(daily).sunset[0])]);
|
||
append($$anchor, div_12);
|
||
};
|
||
if_block(node_7, ($$render) => {
|
||
if (get(daily)?.sunrise?.[0] && get(daily)?.sunset?.[0]) $$render(consequent_6);
|
||
});
|
||
reset(div);
|
||
template_effect(($0, $1, $2, $3, $4) => {
|
||
set_class(div, 1, `current-hero ${get(bgClass) ?? ""}`, "svelte-1k8dsh");
|
||
set_text(text, $0);
|
||
set_text(text_1, get(unit));
|
||
set_text(text_2, get(weatherInfo).icon);
|
||
set_text(text_3, `${get(weatherInfo).desc ?? ""} `);
|
||
set_text(text_7, `${get(current).relative_humidity_2m ?? ""}%`);
|
||
set_text(text_8, `${$1 ?? ""} ${get(windUnit) ?? ""}`);
|
||
set_text(text_9, $2);
|
||
set_text(text_10, `${$3 ?? ""} hPa`);
|
||
set_text(text_11, $4);
|
||
}, [
|
||
() => Math.round(get(current).temperature_2m),
|
||
() => Math.round(get(current).wind_speed_10m),
|
||
() => windCompass(get(current).wind_direction_10m),
|
||
() => Math.round(get(current).pressure_msl || 0),
|
||
() => get(current).uv_index != null ? Math.round(get(current).uv_index) : "--"
|
||
]);
|
||
append($$anchor, div);
|
||
};
|
||
if_block(node, ($$render) => {
|
||
if (get(current)) $$render(consequent_7);
|
||
});
|
||
append($$anchor, fragment);
|
||
pop();
|
||
}
|
||
//#endregion
|
||
//#region src/components/HourlyForecast.svelte
|
||
var root$7 = /* @__PURE__ */ from_html(`<div class="precip-bar-container svelte-1q03bs0"><div class="precip-bar svelte-1q03bs0"></div></div> <span class="precip-value svelte-1q03bs0"> </span>`, 1);
|
||
var root_1$7 = /* @__PURE__ */ from_html(`<div class="precip-bar-container svelte-1q03bs0"><div class="precip-bar svelte-1q03bs0" style="--precip: 0%"></div></div> <span class="precip-value svelte-1q03bs0">--</span>`, 1);
|
||
var root_2$6 = /* @__PURE__ */ from_html(`<div><span class="hour-time svelte-1q03bs0"> </span> <span class="hour-icon svelte-1q03bs0"> </span> <span class="hour-temp svelte-1q03bs0"> </span> <!> <span class="hour-wind svelte-1q03bs0" title="Wind speed"> </span></div>`);
|
||
var root_3$5 = /* @__PURE__ */ from_html(`<div class="hourly-section svelte-1q03bs0"><h3 class="section-title svelte-1q03bs0">Hourly Forecast</h3> <div class="hourly-scroll svelte-1q03bs0"></div></div>`);
|
||
function HourlyForecast($$anchor, $$props) {
|
||
push($$props, true);
|
||
const data = /* @__PURE__ */ user_derived(() => app$1.forecastData);
|
||
const hourly = /* @__PURE__ */ user_derived(() => get(data)?.hourly);
|
||
const unit = /* @__PURE__ */ user_derived(() => app$1.settings.units === "imperial" ? "°F" : "°C");
|
||
/* @__PURE__ */ user_derived(() => app$1.settings.units === "imperial" ? "mph" : "km/h");
|
||
const next24Hours = /* @__PURE__ */ user_derived(() => {
|
||
if (!get(hourly)?.time) return [];
|
||
const now = /* @__PURE__ */ new Date();
|
||
const result = [];
|
||
for (let i = 0; i < get(hourly).time.length && result.length < 24; i++) {
|
||
const time = new Date(get(hourly).time[i]);
|
||
if (time >= /* @__PURE__ */ new Date(now.getTime() - 3600 * 1e3)) result.push({
|
||
time,
|
||
temp: get(hourly).temperature_2m?.[i],
|
||
precip: get(hourly).precipitation_probability?.[i],
|
||
code: get(hourly).weather_code?.[i],
|
||
windSpeed: get(hourly).wind_speed_10m?.[i]
|
||
});
|
||
}
|
||
return result;
|
||
});
|
||
function formatHour(time) {
|
||
const hours = time.getHours();
|
||
if (hours === 0) return "12 AM";
|
||
if (hours === 12) return "12 PM";
|
||
return hours < 12 ? `${hours} AM` : `${hours - 12} PM`;
|
||
}
|
||
function isNow(index) {
|
||
return index === 0;
|
||
}
|
||
var fragment = comment();
|
||
var node = first_child(fragment);
|
||
var consequent_1 = ($$anchor) => {
|
||
var div = root_3$5();
|
||
var div_1 = sibling(child(div), 2);
|
||
each(div_1, 21, () => get(next24Hours), index, ($$anchor, hour, i) => {
|
||
const info = /* @__PURE__ */ user_derived(() => getWeatherInfo(get(hour).code));
|
||
var div_2 = root_2$6();
|
||
let classes;
|
||
var span = child(div_2);
|
||
var text = child(span, true);
|
||
reset(span);
|
||
var span_1 = sibling(span, 2);
|
||
var text_1 = child(span_1, true);
|
||
reset(span_1);
|
||
var span_2 = sibling(span_1, 2);
|
||
var text_2 = child(span_2);
|
||
reset(span_2);
|
||
var node_1 = sibling(span_2, 2);
|
||
var consequent = ($$anchor) => {
|
||
var fragment_1 = root$7();
|
||
var div_3 = first_child(fragment_1);
|
||
var div_4 = child(div_3);
|
||
reset(div_3);
|
||
var span_3 = sibling(div_3, 2);
|
||
var text_3 = child(span_3);
|
||
reset(span_3);
|
||
template_effect(($0) => {
|
||
set_style(div_4, `--precip: ${$0 ?? ""}%`);
|
||
set_text(text_3, `${get(hour).precip ?? ""}%`);
|
||
}, [() => Math.min(get(hour).precip, 100)]);
|
||
append($$anchor, fragment_1);
|
||
};
|
||
var alternate = ($$anchor) => {
|
||
var fragment_2 = root_1$7();
|
||
next(2);
|
||
append($$anchor, fragment_2);
|
||
};
|
||
if_block(node_1, ($$render) => {
|
||
if (get(hour).precip > 0) $$render(consequent);
|
||
else $$render(alternate, -1);
|
||
});
|
||
var span_4 = sibling(node_1, 2);
|
||
var text_4 = child(span_4);
|
||
reset(span_4);
|
||
reset(div_2);
|
||
template_effect(($0, $1, $2, $3) => {
|
||
classes = set_class(div_2, 1, "hour-item svelte-1q03bs0", null, classes, $0);
|
||
set_text(text, $1);
|
||
set_text(text_1, get(info).icon);
|
||
set_text(text_2, `${$2 ?? ""}${get(unit) ?? ""}`);
|
||
set_text(text_4, `💨 ${$3 ?? ""}`);
|
||
}, [
|
||
() => ({ now: isNow(i) }),
|
||
() => isNow(i) ? "Now" : formatHour(get(hour).time),
|
||
() => Math.round(get(hour).temp),
|
||
() => Math.round(get(hour).windSpeed)
|
||
]);
|
||
append($$anchor, div_2);
|
||
});
|
||
reset(div_1);
|
||
reset(div);
|
||
append($$anchor, div);
|
||
};
|
||
if_block(node, ($$render) => {
|
||
if (get(next24Hours).length > 0) $$render(consequent_1);
|
||
});
|
||
append($$anchor, fragment);
|
||
pop();
|
||
}
|
||
//#endregion
|
||
//#region src/components/DailyForecast.svelte
|
||
var root$6 = /* @__PURE__ */ from_html(`<div class="precip-row svelte-ss3yj8"><span class="precip-icon svelte-ss3yj8">💧</span> <div class="precip-bar-wrap svelte-ss3yj8"><div class="precip-fill svelte-ss3yj8"></div></div> <span class="precip-label svelte-ss3yj8"> </span></div>`);
|
||
var root_1$6 = /* @__PURE__ */ from_html(`<span class="wind-info svelte-ss3yj8"> </span>`);
|
||
var root_2$5 = /* @__PURE__ */ from_html(`<div><div class="day-left svelte-ss3yj8"><span class="day-name svelte-ss3yj8"> </span> <span class="day-date text-xs text-muted svelte-ss3yj8"> </span></div> <div class="day-center svelte-ss3yj8"><span class="day-icon svelte-ss3yj8"> </span> <span class="day-desc svelte-ss3yj8"> </span></div> <div class="day-right svelte-ss3yj8"><div class="day-temps svelte-ss3yj8"><span class="temp-high svelte-ss3yj8"> </span> <span class="temp-low svelte-ss3yj8"> </span></div> <!> <!></div></div>`);
|
||
var root_3$4 = /* @__PURE__ */ from_html(`<div class="daily-section svelte-ss3yj8"><h3 class="section-title svelte-ss3yj8">7-Day Forecast</h3> <div class="daily-list svelte-ss3yj8"></div></div>`);
|
||
function DailyForecast($$anchor, $$props) {
|
||
push($$props, true);
|
||
const data = /* @__PURE__ */ user_derived(() => app$1.forecastData);
|
||
const daily = /* @__PURE__ */ user_derived(() => get(data)?.daily);
|
||
const unit = /* @__PURE__ */ user_derived(() => app$1.settings.units === "imperial" ? "°F" : "°C");
|
||
const windUnit = /* @__PURE__ */ user_derived(() => app$1.settings.units === "imperial" ? "mph" : "km/h");
|
||
function formatDay(dateStr, index) {
|
||
if (index === 0) return "Today";
|
||
if (index === 1) return "Tomorrow";
|
||
return parseLocalDate(dateStr).toLocaleDateString("en-US", {
|
||
weekday: "short",
|
||
month: "short",
|
||
day: "numeric"
|
||
});
|
||
}
|
||
function getDayName(dateStr, index) {
|
||
if (index === 0) return "Today";
|
||
if (index === 1) return "Tomorrow";
|
||
return parseLocalDate(dateStr).toLocaleDateString("en-US", { weekday: "long" });
|
||
}
|
||
var fragment = comment();
|
||
var node = first_child(fragment);
|
||
var consequent_3 = ($$anchor) => {
|
||
var div = root_3$4();
|
||
var div_1 = sibling(child(div), 2);
|
||
each(div_1, 21, () => get(daily).time, index, ($$anchor, day, i) => {
|
||
var fragment_1 = comment();
|
||
var node_1 = first_child(fragment_1);
|
||
var consequent_2 = ($$anchor) => {
|
||
const info = /* @__PURE__ */ user_derived(() => getWeatherInfo(get(daily).weather_code[i], precip));
|
||
const tempMax = /* @__PURE__ */ user_derived(() => Math.round(get(daily).temperature_2m_max[i]));
|
||
const tempMin = /* @__PURE__ */ user_derived(() => Math.round(get(daily).temperature_2m_min[i]));
|
||
const precip = /* @__PURE__ */ user_derived(() => get(daily).precipitation_probability_max?.[i] || 0);
|
||
const wind = /* @__PURE__ */ user_derived(() => get(daily).wind_speed_10m_max?.[i]);
|
||
var div_2 = root_2$5();
|
||
set_class(div_2, 1, "day-card card-glass svelte-ss3yj8", null, {}, { today: i === 0 });
|
||
var div_3 = child(div_2);
|
||
var span = child(div_3);
|
||
var text = child(span, true);
|
||
reset(span);
|
||
var span_1 = sibling(span, 2);
|
||
var text_1 = child(span_1, true);
|
||
reset(span_1);
|
||
reset(div_3);
|
||
var div_4 = sibling(div_3, 2);
|
||
var span_2 = child(div_4);
|
||
var text_2 = child(span_2, true);
|
||
reset(span_2);
|
||
var span_3 = sibling(span_2, 2);
|
||
var text_3 = child(span_3, true);
|
||
reset(span_3);
|
||
reset(div_4);
|
||
var div_5 = sibling(div_4, 2);
|
||
var div_6 = child(div_5);
|
||
var span_4 = child(div_6);
|
||
var text_4 = child(span_4);
|
||
reset(span_4);
|
||
var span_5 = sibling(span_4, 2);
|
||
var text_5 = child(span_5);
|
||
reset(span_5);
|
||
reset(div_6);
|
||
var node_2 = sibling(div_6, 2);
|
||
var consequent = ($$anchor) => {
|
||
var div_7 = root$6();
|
||
var div_8 = sibling(child(div_7), 2);
|
||
var div_9 = child(div_8);
|
||
reset(div_8);
|
||
var span_6 = sibling(div_8, 2);
|
||
var text_6 = child(span_6);
|
||
reset(span_6);
|
||
reset(div_7);
|
||
template_effect(($0) => {
|
||
set_style(div_9, `width: ${$0 ?? ""}%`);
|
||
set_text(text_6, `${get(precip) ?? ""}%`);
|
||
}, [() => Math.min(get(precip), 100)]);
|
||
append($$anchor, div_7);
|
||
};
|
||
if_block(node_2, ($$render) => {
|
||
if (get(precip) > 0) $$render(consequent);
|
||
});
|
||
var node_3 = sibling(node_2, 2);
|
||
var consequent_1 = ($$anchor) => {
|
||
var span_7 = root_1$6();
|
||
var text_7 = child(span_7);
|
||
reset(span_7);
|
||
template_effect(($0) => set_text(text_7, `💨 ${$0 ?? ""} ${get(windUnit) ?? ""}`), [() => Math.round(get(wind))]);
|
||
append($$anchor, span_7);
|
||
};
|
||
if_block(node_3, ($$render) => {
|
||
if (get(wind) > 0) $$render(consequent_1);
|
||
});
|
||
reset(div_5);
|
||
reset(div_2);
|
||
template_effect(($0, $1) => {
|
||
set_text(text, $0);
|
||
set_text(text_1, $1);
|
||
set_text(text_2, get(info).icon);
|
||
set_text(text_3, get(info).label);
|
||
set_text(text_4, `${get(tempMax) ?? ""}${get(unit) ?? ""}`);
|
||
set_text(text_5, `/ ${get(tempMin) ?? ""}${get(unit) ?? ""}`);
|
||
}, [() => getDayName(get(day), i), () => formatDay(get(day), i)]);
|
||
append($$anchor, div_2);
|
||
};
|
||
if_block(node_1, ($$render) => {
|
||
if (i < 7) $$render(consequent_2);
|
||
});
|
||
append($$anchor, fragment_1);
|
||
});
|
||
reset(div_1);
|
||
reset(div);
|
||
append($$anchor, div);
|
||
};
|
||
if_block(node, ($$render) => {
|
||
if (get(daily)?.time) $$render(consequent_3);
|
||
});
|
||
append($$anchor, fragment);
|
||
pop();
|
||
}
|
||
//#endregion
|
||
//#region src/components/WeatherDetail.svelte
|
||
var root$5 = /* @__PURE__ */ from_html(`<span> </span>`);
|
||
var root_1$5 = /* @__PURE__ */ from_html(`<div class="detail-row svelte-8lxt71"><span>24h Average</span> <span class="detail-value svelte-8lxt71"> </span></div>`);
|
||
var root_2$4 = /* @__PURE__ */ from_html(`<div class="detail-section svelte-8lxt71"><h3 class="section-title svelte-8lxt71">Weather Details</h3> <div class="detail-grid svelte-8lxt71"><div class="detail-card card svelte-8lxt71"><div class="detail-header svelte-8lxt71"><span class="detail-icon svelte-8lxt71">💨</span> <span class="detail-label svelte-8lxt71">Wind</span></div> <div class="detail-body svelte-8lxt71"><div class="detail-row svelte-8lxt71"><span>Speed</span> <span class="detail-value svelte-8lxt71"> </span></div> <div class="detail-row svelte-8lxt71"><span>Direction</span> <span class="detail-value svelte-8lxt71"> </span></div> <div class="detail-row svelte-8lxt71"><span>Gusts</span> <span class="detail-value svelte-8lxt71"> </span></div> <div class="detail-row svelte-8lxt71"><span>Condition</span> <span class="detail-value svelte-8lxt71"> </span></div></div></div> <div class="detail-card card svelte-8lxt71"><div class="detail-header svelte-8lxt71"><span class="detail-icon svelte-8lxt71">🌡️</span> <span class="detail-label svelte-8lxt71">Atmosphere</span></div> <div class="detail-body svelte-8lxt71"><div class="detail-row svelte-8lxt71"><span>Humidity</span> <span class="detail-value svelte-8lxt71"> </span></div> <div class="detail-row svelte-8lxt71"><span>Pressure</span> <span class="detail-value svelte-8lxt71"> <!></span></div> <div class="detail-row svelte-8lxt71"><span>Feels Like</span> <span class="detail-value svelte-8lxt71"> </span></div> <div class="detail-row svelte-8lxt71"><span>Is Day</span> <span class="detail-value svelte-8lxt71"> </span></div></div></div> <div class="detail-card card svelte-8lxt71"><div class="detail-header svelte-8lxt71"><span class="detail-icon svelte-8lxt71">☀️</span> <span class="detail-label svelte-8lxt71">Sun & UV</span></div> <div class="detail-body svelte-8lxt71"><div class="detail-row svelte-8lxt71"><span>UV Index</span> <span class="detail-value svelte-8lxt71"> <!> <!></span></div> <div class="detail-row svelte-8lxt71"><span>Sunrise</span> <span class="detail-value svelte-8lxt71"> </span></div> <div class="detail-row svelte-8lxt71"><span>Sunset</span> <span class="detail-value svelte-8lxt71"> </span></div> <div class="detail-row svelte-8lxt71"><span>Max UV Today</span> <span class="detail-value svelte-8lxt71"> <!></span></div></div></div> <div class="detail-card card svelte-8lxt71"><div class="detail-header svelte-8lxt71"><span class="detail-icon svelte-8lxt71">📊</span> <span class="detail-label svelte-8lxt71">Temperature Range</span></div> <div class="detail-body svelte-8lxt71"><div class="detail-row svelte-8lxt71"><span>Today's High</span> <span class="detail-value svelte-8lxt71"> </span></div> <div class="detail-row svelte-8lxt71"><span>Today's Low</span> <span class="detail-value svelte-8lxt71"> </span></div> <!> <div class="detail-row svelte-8lxt71"><span>Precip Chance</span> <span class="detail-value svelte-8lxt71"> </span></div></div></div></div> <div class="time-info card mt-3 svelte-8lxt71"><div class="time-row svelte-8lxt71"><span>Data source</span> <span class="detail-value svelte-8lxt71"> </span></div> <div class="time-row svelte-8lxt71"><span>Local time</span> <span class="detail-value svelte-8lxt71"> </span></div> <div class="time-row svelte-8lxt71"><span>Data updated</span> <span class="detail-value svelte-8lxt71"> </span></div> <div class="time-row svelte-8lxt71"><span>Elevation</span> <span class="detail-value svelte-8lxt71"> </span></div></div></div>`);
|
||
function WeatherDetail($$anchor, $$props) {
|
||
push($$props, true);
|
||
const data = /* @__PURE__ */ user_derived(() => app$1.forecastData);
|
||
const current = /* @__PURE__ */ user_derived(() => get(data)?.current);
|
||
const daily = /* @__PURE__ */ user_derived(() => get(data)?.daily);
|
||
const hourly = /* @__PURE__ */ user_derived(() => get(data)?.hourly);
|
||
const unit = /* @__PURE__ */ user_derived(() => app$1.settings.units === "imperial" ? "°F" : "°C");
|
||
const windUnit = /* @__PURE__ */ user_derived(() => app$1.settings.units === "imperial" ? "mph" : "km/h");
|
||
const fbPressure = /* @__PURE__ */ user_derived(() => !!get(data)?._fallback?.pressure);
|
||
const fbUv = /* @__PURE__ */ user_derived(() => !!get(data)?._fallback?.uv);
|
||
function windCompass(deg) {
|
||
if (deg == null) return "--";
|
||
return [
|
||
"N",
|
||
"NNE",
|
||
"NE",
|
||
"ENE",
|
||
"E",
|
||
"ESE",
|
||
"SE",
|
||
"SSE",
|
||
"S",
|
||
"SSW",
|
||
"SW",
|
||
"WSW",
|
||
"W",
|
||
"WNW",
|
||
"NW",
|
||
"NNW"
|
||
][Math.round(deg / 22.5) % 16];
|
||
}
|
||
function formatTime(iso) {
|
||
if (!iso) return "--";
|
||
const [h, m] = iso.split("T")[1].split(":");
|
||
const hours = parseInt(h, 10);
|
||
const ampm = hours >= 12 ? "PM" : "AM";
|
||
return `${hours % 12 || 12}:${m} ${ampm}`;
|
||
}
|
||
function getBeaufort(speed) {
|
||
const kmh = app$1.settings.units === "imperial" ? speed * 1.60934 : speed;
|
||
if (kmh < 2) return "Calm";
|
||
if (kmh < 6) return "Light air";
|
||
if (kmh < 12) return "Light breeze";
|
||
if (kmh < 20) return "Gentle breeze";
|
||
if (kmh < 29) return "Moderate breeze";
|
||
if (kmh < 39) return "Fresh breeze";
|
||
if (kmh < 50) return "Strong breeze";
|
||
if (kmh < 62) return "High wind";
|
||
if (kmh < 75) return "Gale";
|
||
return "Strong gale";
|
||
}
|
||
function getUVLevel(uv) {
|
||
if (uv == null) return "--";
|
||
if (uv <= 2) return "Low";
|
||
if (uv <= 5) return "Moderate";
|
||
if (uv <= 7) return "High";
|
||
if (uv <= 10) return "Very High";
|
||
return "Extreme";
|
||
}
|
||
var fragment = comment();
|
||
var node = first_child(fragment);
|
||
var consequent_6 = ($$anchor) => {
|
||
var div = root_2$4();
|
||
var div_1 = sibling(child(div), 2);
|
||
var div_2 = child(div_1);
|
||
var div_3 = sibling(child(div_2), 2);
|
||
var div_4 = child(div_3);
|
||
var span = sibling(child(div_4), 2);
|
||
var text = child(span);
|
||
reset(span);
|
||
reset(div_4);
|
||
var div_5 = sibling(div_4, 2);
|
||
var span_1 = sibling(child(div_5), 2);
|
||
var text_1 = child(span_1);
|
||
reset(span_1);
|
||
reset(div_5);
|
||
var div_6 = sibling(div_5, 2);
|
||
var span_2 = sibling(child(div_6), 2);
|
||
var text_2 = child(span_2, true);
|
||
reset(span_2);
|
||
reset(div_6);
|
||
var div_7 = sibling(div_6, 2);
|
||
var span_3 = sibling(child(div_7), 2);
|
||
var text_3 = child(span_3, true);
|
||
reset(span_3);
|
||
reset(div_7);
|
||
reset(div_3);
|
||
reset(div_2);
|
||
var div_8 = sibling(div_2, 2);
|
||
var div_9 = sibling(child(div_8), 2);
|
||
var div_10 = child(div_9);
|
||
var span_4 = sibling(child(div_10), 2);
|
||
var text_4 = child(span_4);
|
||
reset(span_4);
|
||
reset(div_10);
|
||
var div_11 = sibling(div_10, 2);
|
||
var span_5 = sibling(child(div_11), 2);
|
||
var text_5 = child(span_5);
|
||
var node_1 = sibling(text_5);
|
||
var consequent = ($$anchor) => {
|
||
SourceTooltip($$anchor, { label: "Pressure from Open-Meteo — NWS does not provide it" });
|
||
};
|
||
if_block(node_1, ($$render) => {
|
||
if (get(fbPressure)) $$render(consequent);
|
||
});
|
||
reset(span_5);
|
||
reset(div_11);
|
||
var div_12 = sibling(div_11, 2);
|
||
var span_6 = sibling(child(div_12), 2);
|
||
var text_6 = child(span_6);
|
||
reset(span_6);
|
||
reset(div_12);
|
||
var div_13 = sibling(div_12, 2);
|
||
var span_7 = sibling(child(div_13), 2);
|
||
var text_7 = child(span_7, true);
|
||
reset(span_7);
|
||
reset(div_13);
|
||
reset(div_9);
|
||
reset(div_8);
|
||
var div_14 = sibling(div_8, 2);
|
||
var div_15 = sibling(child(div_14), 2);
|
||
var div_16 = child(div_15);
|
||
var span_8 = sibling(child(div_16), 2);
|
||
var text_8 = child(span_8);
|
||
var node_2 = sibling(text_8);
|
||
var consequent_1 = ($$anchor) => {
|
||
var span_9 = root$5();
|
||
var text_9 = child(span_9, true);
|
||
reset(span_9);
|
||
template_effect(($0, $1) => {
|
||
set_class(span_9, 1, `uv-badge uv-${$0 ?? ""}`, "svelte-8lxt71");
|
||
set_text(text_9, $1);
|
||
}, [() => getUVLevel(get(current).uv_index).toLowerCase().replace(" ", "-"), () => getUVLevel(get(current).uv_index)]);
|
||
append($$anchor, span_9);
|
||
};
|
||
if_block(node_2, ($$render) => {
|
||
if (get(current).uv_index != null) $$render(consequent_1);
|
||
});
|
||
var node_3 = sibling(node_2, 2);
|
||
var consequent_2 = ($$anchor) => {
|
||
SourceTooltip($$anchor, { label: "UV index from Open-Meteo — NWS does not provide it" });
|
||
};
|
||
if_block(node_3, ($$render) => {
|
||
if (get(fbUv)) $$render(consequent_2);
|
||
});
|
||
reset(span_8);
|
||
reset(div_16);
|
||
var div_17 = sibling(div_16, 2);
|
||
var span_10 = sibling(child(div_17), 2);
|
||
var text_10 = child(span_10, true);
|
||
reset(span_10);
|
||
reset(div_17);
|
||
var div_18 = sibling(div_17, 2);
|
||
var span_11 = sibling(child(div_18), 2);
|
||
var text_11 = child(span_11, true);
|
||
reset(span_11);
|
||
reset(div_18);
|
||
var div_19 = sibling(div_18, 2);
|
||
var span_12 = sibling(child(div_19), 2);
|
||
var text_12 = child(span_12, true);
|
||
var node_4 = sibling(text_12);
|
||
var consequent_3 = ($$anchor) => {
|
||
SourceTooltip($$anchor, { label: "UV from Open-Meteo — NWS does not provide it" });
|
||
};
|
||
if_block(node_4, ($$render) => {
|
||
if (get(fbUv)) $$render(consequent_3);
|
||
});
|
||
reset(span_12);
|
||
reset(div_19);
|
||
reset(div_15);
|
||
reset(div_14);
|
||
var div_20 = sibling(div_14, 2);
|
||
var div_21 = sibling(child(div_20), 2);
|
||
var div_22 = child(div_21);
|
||
var span_13 = sibling(child(div_22), 2);
|
||
var text_13 = child(span_13, true);
|
||
reset(span_13);
|
||
reset(div_22);
|
||
var div_23 = sibling(div_22, 2);
|
||
var span_14 = sibling(child(div_23), 2);
|
||
var text_14 = child(span_14, true);
|
||
reset(span_14);
|
||
reset(div_23);
|
||
var node_5 = sibling(div_23, 2);
|
||
var consequent_5 = ($$anchor) => {
|
||
const temps = /* @__PURE__ */ user_derived(() => get(hourly).temperature_2m || []);
|
||
const validTemps = /* @__PURE__ */ user_derived(() => get(temps).filter((t) => t != null));
|
||
var fragment_4 = comment();
|
||
var node_6 = first_child(fragment_4);
|
||
var consequent_4 = ($$anchor) => {
|
||
var div_24 = root_1$5();
|
||
var span_15 = sibling(child(div_24), 2);
|
||
var text_15 = child(span_15);
|
||
reset(span_15);
|
||
reset(div_24);
|
||
template_effect(($0) => set_text(text_15, `${$0 ?? ""}${get(unit) ?? ""}`), [() => Math.round(get(validTemps).reduce((a, b) => a + b, 0) / get(validTemps).length)]);
|
||
append($$anchor, div_24);
|
||
};
|
||
if_block(node_6, ($$render) => {
|
||
if (get(validTemps).length > 0) $$render(consequent_4);
|
||
});
|
||
append($$anchor, fragment_4);
|
||
};
|
||
if_block(node_5, ($$render) => {
|
||
if (get(hourly)) $$render(consequent_5);
|
||
});
|
||
var div_25 = sibling(node_5, 2);
|
||
var span_16 = sibling(child(div_25), 2);
|
||
var text_16 = child(span_16);
|
||
reset(span_16);
|
||
reset(div_25);
|
||
reset(div_21);
|
||
reset(div_20);
|
||
reset(div_1);
|
||
var div_26 = sibling(div_1, 2);
|
||
var div_27 = child(div_26);
|
||
var span_17 = sibling(child(div_27), 2);
|
||
var text_17 = child(span_17, true);
|
||
reset(span_17);
|
||
reset(div_27);
|
||
var div_28 = sibling(div_27, 2);
|
||
var span_18 = sibling(child(div_28), 2);
|
||
var text_18 = child(span_18, true);
|
||
reset(span_18);
|
||
reset(div_28);
|
||
var div_29 = sibling(div_28, 2);
|
||
var span_19 = sibling(child(div_29), 2);
|
||
var text_19 = child(span_19, true);
|
||
reset(span_19);
|
||
reset(div_29);
|
||
var div_30 = sibling(div_29, 2);
|
||
var span_20 = sibling(child(div_30), 2);
|
||
var text_20 = child(span_20, true);
|
||
reset(span_20);
|
||
reset(div_30);
|
||
reset(div_26);
|
||
reset(div);
|
||
template_effect(($0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) => {
|
||
set_text(text, `${$0 ?? ""} ${get(windUnit) ?? ""}`);
|
||
set_text(text_1, `${$1 ?? ""} (${get(current).wind_direction_10m ?? ""}°)`);
|
||
set_text(text_2, $2);
|
||
set_text(text_3, $3);
|
||
set_text(text_4, `${get(current).relative_humidity_2m ?? ""}%`);
|
||
set_text(text_5, `${$4 ?? ""} hPa`);
|
||
set_text(text_6, `${$5 ?? ""}${get(unit) ?? ""}`);
|
||
set_text(text_7, get(current).is_day ? "☀️ Yes" : "🌙 No");
|
||
set_text(text_8, `${$6 ?? ""} `);
|
||
set_text(text_10, $7);
|
||
set_text(text_11, $8);
|
||
set_text(text_12, $9);
|
||
set_text(text_13, $10);
|
||
set_text(text_14, $11);
|
||
set_text(text_16, `${(get(daily)?.precipitation_probability_max?.[0] || 0) ?? ""}%`);
|
||
set_text(text_17, get(data)?._source === "nws" ? "NWS (National Weather Service)" : "Open-Meteo");
|
||
set_text(text_18, get(data).timezone || "--");
|
||
set_text(text_19, $12);
|
||
set_text(text_20, $13);
|
||
}, [
|
||
() => Math.round(get(current).wind_speed_10m),
|
||
() => windCompass(get(current).wind_direction_10m),
|
||
() => get(current).wind_gusts_10m != null ? Math.round(get(current).wind_gusts_10m) + " " + get(windUnit) : "--",
|
||
() => getBeaufort(get(current).wind_speed_10m),
|
||
() => Math.round(get(current).pressure_msl || 0),
|
||
() => Math.round(get(current).apparent_temperature),
|
||
() => get(current).uv_index != null ? Math.round(get(current).uv_index) : "--",
|
||
() => formatTime(get(daily)?.sunrise?.[0] || ""),
|
||
() => formatTime(get(daily)?.sunset?.[0] || ""),
|
||
() => get(daily)?.uv_index_max?.[0] != null ? Math.round(get(daily).uv_index_max[0]) : "--",
|
||
() => get(daily)?.temperature_2m_max?.[0] != null ? Math.round(get(daily).temperature_2m_max[0]) + get(unit) : "--",
|
||
() => get(daily)?.temperature_2m_min?.[0] != null ? Math.round(get(daily).temperature_2m_min[0]) + get(unit) : "--",
|
||
() => get(current).time ? new Date(get(current).time).toLocaleTimeString() : "--",
|
||
() => get(data).elevation != null ? app$1.settings.units === "imperial" ? Math.round(get(data).elevation * 3.28084) + " ft" : Math.round(get(data).elevation) + " m" : "--"
|
||
]);
|
||
append($$anchor, div);
|
||
};
|
||
if_block(node, ($$render) => {
|
||
if (get(current)) $$render(consequent_6);
|
||
});
|
||
append($$anchor, fragment);
|
||
pop();
|
||
}
|
||
//#endregion
|
||
//#region src/lib/api/lightning.js
|
||
/**
|
||
* Lightning strike API helper.
|
||
*
|
||
* The browser cannot talk directly to the Blitzortung feed (WebSocket +
|
||
* binary protocol, no CORS), so this app reads strikes through a small CORS
|
||
* relay at https://cors.thecookiejar.me. The relay is NOT deployed yet, so
|
||
* every request here must fail gracefully: callers catch the error and show a
|
||
* "lightning unavailable" state rather than crashing/blocking the radar.
|
||
*
|
||
* Expected relay contract (once it exists):
|
||
* GET /lightning?lat=<lat>&lon=<lon>&radiusKm=<n>&limit=<n>
|
||
* -> 200 { "source": "blitzortung", "generatedAt": <unix>,
|
||
* "strikes": [ { "lat": <number>, "lon": <number>,
|
||
* "time": <unix>, "strength": <number|undefined> } ] }
|
||
*
|
||
* The relay is intended to hold a recent WS connection to Blitzortung and
|
||
* answer point-in-radius queries for recent strikes in that area.
|
||
*/
|
||
var LIGHTNING_PROXY_BASE = "https://cors.thecookiejar.me/lightning";
|
||
/**
|
||
* Fetch recent lightning strikes within radiusKm of a location.
|
||
*
|
||
* @param {Object} opts
|
||
* @param {number} opts.lat
|
||
* @param {number} opts.lon
|
||
* @param {number} [opts.radiusKm=150]
|
||
* @param {number} [opts.limit=200]
|
||
* @returns {Promise<Array<{ lat: number, lon: number, time: number, strength?: number }>>}
|
||
* Resolves to the strike list. Throws if the relay is unreachable, returns a
|
||
* non-OK status, or the response is malformed.
|
||
*/
|
||
async function fetchLightningStrikes({ lat, lon, radiusKm = 150, limit = 200 } = {}) {
|
||
const url = `${LIGHTNING_PROXY_BASE}?${new URLSearchParams({
|
||
lat: lat.toString(),
|
||
lon: lon.toString(),
|
||
radiusKm: radiusKm.toString(),
|
||
limit: limit.toString()
|
||
}).toString()}`;
|
||
const controller = new AbortController();
|
||
const timer = setTimeout(() => controller.abort(), 8e3);
|
||
let res;
|
||
try {
|
||
res = await fetch(url, { signal: controller.signal });
|
||
} finally {
|
||
clearTimeout(timer);
|
||
}
|
||
if (!res.ok) throw new Error(`Lightning relay error: ${res.status} ${res.statusText}`);
|
||
const data = await res.json();
|
||
if (!data || !Array.isArray(data.strikes)) throw new Error("Lightning relay returned an invalid response");
|
||
return data.strikes;
|
||
}
|
||
/**
|
||
* Project a lat/lon onto the radar tile layer's pixel coordinates.
|
||
*
|
||
* Mirrors the slippy-map math in PrecipitationRadar: the layer is a grid of
|
||
* TILE_SIZE tiles centered on the location's center tile (cx, cy in tile
|
||
* space). A strike at world coordinates is converted to floating tile
|
||
* coordinates at the given zoom, then to pixels relative to the tile layer's
|
||
* origin (the tile two rows/cols above-left of the center tile, since the grid
|
||
* is 5x5 and centered).
|
||
*
|
||
* @param {number} lat
|
||
* @param {number} lon
|
||
* @param {Object} opts
|
||
* @param {number} opts.zoom
|
||
* @param {number} opts.cx - center tile X (float)
|
||
* @param {number} opts.cy - center tile Y (float)
|
||
* @param {number} [opts.tileSize=256]
|
||
* @returns {{ x: number, y: number }} Pixel coords within the tile layer.
|
||
*/
|
||
function projectStrikeToLayer(lat, lon, { zoom, cx, cy, tileSize = 256 }) {
|
||
const fx = tileX(lon, zoom);
|
||
const fy = tileY(lat, zoom);
|
||
const tcx = Math.floor(cx);
|
||
const tcy = Math.floor(cy);
|
||
return {
|
||
x: (fx - (tcx - 2)) * tileSize,
|
||
y: (fy - (tcy - 2)) * tileSize
|
||
};
|
||
}
|
||
function tileX(lon, z) {
|
||
return (lon + 180) / 360 * Math.pow(2, z);
|
||
}
|
||
function tileY(lat, z) {
|
||
const rad = lat * Math.PI / 180;
|
||
return (1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math.PI) * Math.pow(2, z - 1);
|
||
}
|
||
//#endregion
|
||
//#region src/components/PrecipitationRadar.svelte
|
||
init_db();
|
||
var root$4 = /* @__PURE__ */ from_html(`<button role="switch" title="Toggle lightning strike layer">⚡ Lightning</button>`);
|
||
var root_1$4 = /* @__PURE__ */ from_html(`<div class="radar-status radar-status-err svelte-tjvfz7"><span class="svelte-tjvfz7"> </span> <button class="btn btn-ghost btn-sm svelte-tjvfz7">Retry</button></div>`);
|
||
var root_2$3 = /* @__PURE__ */ from_html(`<img class="radar-tile tile-overlay svelte-tjvfz7" alt="" loading="lazy" draggable="false"/>`);
|
||
var root_3$3 = /* @__PURE__ */ from_html(`<img class="radar-tile tile-base svelte-tjvfz7" alt="" loading="lazy" draggable="false"/> <!>`, 1);
|
||
var root_4$3 = /* @__PURE__ */ from_html(`<span class="lightning-strike svelte-tjvfz7"></span>`);
|
||
var root_5$3 = /* @__PURE__ */ from_html(`<div class="lightning-layer svelte-tjvfz7" aria-label="Lightning strikes"></div>`);
|
||
var root_6$2 = /* @__PURE__ */ from_html(`<div class="radar-layer svelte-tjvfz7"><!> <!></div> <div class="location-marker svelte-tjvfz7"><span class="marker-dot svelte-tjvfz7"></span></div>`, 1);
|
||
var root_7$1 = /* @__PURE__ */ from_html(`<div class="radar-overlay svelte-tjvfz7"><span class="loading-spinner svelte-tjvfz7">⏳</span> <span class="text-xs text-muted svelte-tjvfz7">Loading radar...</span></div>`);
|
||
var root_8 = /* @__PURE__ */ from_html(`<div class="radar-error-banner svelte-tjvfz7"><span class="text-xs svelte-tjvfz7"> </span> <button class="btn btn-ghost btn-sm svelte-tjvfz7">Retry</button></div>`);
|
||
var root_9 = /* @__PURE__ */ from_html(`<div class="lightning-status svelte-tjvfz7"><span class="text-xs svelte-tjvfz7">⏳ Loading lightning...</span></div>`);
|
||
var root_10 = /* @__PURE__ */ from_html(`<div class="lightning-status lightning-status-err svelte-tjvfz7"><span class="text-xs svelte-tjvfz7"> </span></div>`);
|
||
var root_11 = /* @__PURE__ */ from_html(`<div class="lightning-status svelte-tjvfz7"><span class="text-xs svelte-tjvfz7">⚡ No recent strikes nearby</span></div>`);
|
||
var root_12 = /* @__PURE__ */ from_html(`<div class="timeline svelte-tjvfz7"><div class="tl-controls svelte-tjvfz7"><button class="ctrl-btn svelte-tjvfz7" aria-label="Previous" title="Previous">⏮</button> <button class="ctrl-btn play-btn svelte-tjvfz7"> </button> <button class="ctrl-btn svelte-tjvfz7" aria-label="Next" title="Next">⏭</button></div> <div class="tl-slider svelte-tjvfz7"><input type="range" min="0" aria-label="Radar timeline" class="svelte-tjvfz7"/></div> <div class="tl-info svelte-tjvfz7"><span class="tl-time svelte-tjvfz7"> </span> <span class="tl-date text-muted svelte-tjvfz7"> </span> <span> </span> <span class="text-muted text-xs svelte-tjvfz7"> </span></div></div>`);
|
||
var root_13 = /* @__PURE__ */ from_html(`<div class="radar-status svelte-tjvfz7"><p class="text-muted svelte-tjvfz7">No radar data available.</p> <button class="btn btn-ghost btn-sm svelte-tjvfz7">Reload</button></div>`);
|
||
var root_14 = /* @__PURE__ */ from_html(`<div style="touch-action: none" role="img" aria-label="Precipitation radar map"><!> <!> <!> <!> <div class="zoom-ctrl svelte-tjvfz7"><button class="zoom-btn svelte-tjvfz7" aria-label="Zoom in">+</button> <span class="zoom-level text-xs text-muted svelte-tjvfz7"> </span> <button class="zoom-btn svelte-tjvfz7" aria-label="Zoom out">−</button></div></div> <!>`, 1);
|
||
var root_15 = /* @__PURE__ */ from_html(`<div class="radar-section svelte-tjvfz7"><div class="radar-title-row svelte-tjvfz7"><h3 class="section-title svelte-tjvfz7">Precipitation Radar</h3> <!></div> <!></div>`);
|
||
function PrecipitationRadar($$anchor, $$props) {
|
||
push($$props, true);
|
||
let radarData = /* @__PURE__ */ state(null);
|
||
let loading = /* @__PURE__ */ state(true);
|
||
let error = /* @__PURE__ */ state("");
|
||
let currentIdx = /* @__PURE__ */ state(0);
|
||
let playing = /* @__PURE__ */ state(false);
|
||
let zoom = /* @__PURE__ */ state(6);
|
||
let panX = /* @__PURE__ */ state(0);
|
||
let panY = /* @__PURE__ */ state(0);
|
||
let dragging = /* @__PURE__ */ state(false);
|
||
let dragStart = /* @__PURE__ */ state(proxy({
|
||
x: 0,
|
||
y: 0
|
||
}));
|
||
let dragBase = /* @__PURE__ */ state(proxy({
|
||
x: 0,
|
||
y: 0
|
||
}));
|
||
let animRef = null;
|
||
const TILE_SIZE = 256;
|
||
const RADAR_COLOR = 4;
|
||
const loc = /* @__PURE__ */ user_derived(() => app$1.selectedLocation);
|
||
let showLightning = /* @__PURE__ */ state(proxy(app$1.settings.showLightning || false));
|
||
let strikes = /* @__PURE__ */ state(proxy([]));
|
||
let lightningError = /* @__PURE__ */ state("");
|
||
let lightningLoading = /* @__PURE__ */ state(false);
|
||
let lightningTimer = null;
|
||
const LIGHTNING_REFRESH_MS = 3e4;
|
||
user_effect(() => {
|
||
if (get(loc)) {
|
||
set(panX, 0);
|
||
set(panY, 0);
|
||
loadRadar();
|
||
}
|
||
return () => {
|
||
if (animRef) clearTimeout(animRef);
|
||
};
|
||
});
|
||
async function loadRadar() {
|
||
set(loading, true);
|
||
set(error, "");
|
||
try {
|
||
const res = await fetch("https://api.rainviewer.com/public/weather-maps.json");
|
||
if (!res.ok) throw new Error(`Radar API error: ${res.status}`);
|
||
set(radarData, await res.json(), true);
|
||
const pastLen = get(radarData)?.radar?.past?.length || 0;
|
||
if (pastLen > 0) set(currentIdx, pastLen - 1);
|
||
} catch (e) {
|
||
set(error, e.message || "Failed to load radar", true);
|
||
} finally {
|
||
set(loading, false);
|
||
}
|
||
}
|
||
async function loadLightning() {
|
||
if (!get(loc) || !get(showLightning)) return;
|
||
set(lightningLoading, true);
|
||
set(lightningError, "");
|
||
try {
|
||
const radiusKm = Math.max(40, Math.min(400, 160 * Math.pow(2, 6 - get(zoom))));
|
||
set(strikes, await fetchLightningStrikes({
|
||
lat: get(loc).lat,
|
||
lon: get(loc).lon,
|
||
radiusKm,
|
||
limit: 400
|
||
}), true);
|
||
} catch (e) {
|
||
set(strikes, [], true);
|
||
set(lightningError, e.name === "AbortError" ? "Lightning layer timed out" : e.message || "Lightning layer unavailable", true);
|
||
console.warn("Lightning layer unavailable:", e.message);
|
||
} finally {
|
||
set(lightningLoading, false);
|
||
}
|
||
}
|
||
function toggleLightning() {
|
||
set(showLightning, !get(showLightning));
|
||
app$1.settings = {
|
||
...app$1.settings,
|
||
showLightning: get(showLightning)
|
||
};
|
||
saveSettings({
|
||
...app$1.settings,
|
||
alertThresholds: { ...app$1.settings.alertThresholds }
|
||
}).catch(() => {});
|
||
}
|
||
function startLightningTimer() {
|
||
stopLightningTimer();
|
||
lightningTimer = setInterval(loadLightning, LIGHTNING_REFRESH_MS);
|
||
}
|
||
function stopLightningTimer() {
|
||
if (lightningTimer) {
|
||
clearInterval(lightningTimer);
|
||
lightningTimer = null;
|
||
}
|
||
}
|
||
user_effect(() => {
|
||
if (get(showLightning) && get(loc)) {
|
||
loadLightning();
|
||
startLightningTimer();
|
||
return () => {
|
||
stopLightningTimer();
|
||
};
|
||
} else {
|
||
stopLightningTimer();
|
||
set(strikes, [], true);
|
||
set(lightningError, "");
|
||
}
|
||
});
|
||
const strikeMarkers = /* @__PURE__ */ user_derived(() => get(showLightning) ? get(strikes).map((s) => ({
|
||
...s,
|
||
...projectStrikeToLayer(s.lat, s.lon, {
|
||
zoom: get(zoom),
|
||
cx: get(cx),
|
||
cy: get(cy),
|
||
tileSize: TILE_SIZE
|
||
})
|
||
})) : []);
|
||
const pastFrames = /* @__PURE__ */ user_derived(() => get(radarData)?.radar?.past || []);
|
||
const nowcastFrames = /* @__PURE__ */ user_derived(() => get(radarData)?.radar?.nowcast || []);
|
||
const allFrames = /* @__PURE__ */ user_derived(() => [...get(pastFrames), ...get(nowcastFrames)]);
|
||
const totalFrames = /* @__PURE__ */ user_derived(() => get(allFrames).length);
|
||
const frame = /* @__PURE__ */ user_derived(() => get(allFrames)[get(currentIdx)] || null);
|
||
function tileX(lon, z) {
|
||
return (lon + 180) / 360 * Math.pow(2, z);
|
||
}
|
||
function tileY(lat, z) {
|
||
const rad = lat * Math.PI / 180;
|
||
return (1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math.PI) * Math.pow(2, z - 1);
|
||
}
|
||
const cx = /* @__PURE__ */ user_derived(() => get(loc) ? tileX(get(loc).lon, get(zoom)) : 0);
|
||
const cy = /* @__PURE__ */ user_derived(() => get(loc) ? tileY(get(loc).lat, get(zoom)) : 0);
|
||
const pxOff = /* @__PURE__ */ user_derived(() => (get(cx) - Math.floor(get(cx))) * TILE_SIZE);
|
||
const pyOff = /* @__PURE__ */ user_derived(() => (get(cy) - Math.floor(get(cy))) * TILE_SIZE);
|
||
const tiles = /* @__PURE__ */ user_derived(() => {
|
||
if (!get(loc)) return [];
|
||
const tcx = Math.floor(get(cx));
|
||
const tcy = Math.floor(get(cy));
|
||
const maxTile = Math.pow(2, get(zoom));
|
||
const result = [];
|
||
for (let row = -2; row <= 2; row++) for (let col = -2; col <= 2; col++) {
|
||
const tx = tcx + col;
|
||
const ty = tcy + row;
|
||
if (tx < 0 || tx >= maxTile || ty < 0 || ty >= maxTile) continue;
|
||
result.push({
|
||
tx,
|
||
ty,
|
||
col,
|
||
row
|
||
});
|
||
}
|
||
return result;
|
||
});
|
||
function osmUrl(tx, ty) {
|
||
return `https://${[
|
||
"a",
|
||
"b",
|
||
"c"
|
||
][(tx + ty) % 3]}.tile.openstreetmap.org/${get(zoom)}/${tx}/${ty}.png`;
|
||
}
|
||
function radarUrl(tx, ty) {
|
||
if (!get(frame)) return "";
|
||
return `https://tilecache.rainviewer.com${get(frame).path}/256/${get(zoom)}/${tx}/${ty}/${RADAR_COLOR}/1_1.png`;
|
||
}
|
||
function togglePlay() {
|
||
set(playing, !get(playing));
|
||
if (get(playing)) tick();
|
||
else if (animRef) {
|
||
clearTimeout(animRef);
|
||
animRef = null;
|
||
}
|
||
}
|
||
function tick() {
|
||
if (!get(playing) || get(totalFrames) === 0) return;
|
||
set(currentIdx, (get(currentIdx) + 1) % get(totalFrames));
|
||
animRef = setTimeout(tick, 600);
|
||
}
|
||
function stepBack() {
|
||
set(playing, false);
|
||
if (animRef) {
|
||
clearTimeout(animRef);
|
||
animRef = null;
|
||
}
|
||
set(currentIdx, get(currentIdx) > 0 ? get(currentIdx) - 1 : get(totalFrames) - 1, true);
|
||
}
|
||
function stepForward() {
|
||
set(playing, false);
|
||
if (animRef) {
|
||
clearTimeout(animRef);
|
||
animRef = null;
|
||
}
|
||
set(currentIdx, (get(currentIdx) + 1) % get(totalFrames));
|
||
}
|
||
function zoomIn() {
|
||
if (get(zoom) < 11) {
|
||
update(zoom);
|
||
set(panX, 0);
|
||
set(panY, 0);
|
||
}
|
||
}
|
||
function zoomOut() {
|
||
if (get(zoom) > 2) {
|
||
update(zoom, -1);
|
||
set(panX, 0);
|
||
set(panY, 0);
|
||
}
|
||
}
|
||
function onPointerDown(e) {
|
||
if (e.target.closest("button, input")) return;
|
||
set(dragging, true);
|
||
set(dragStart, {
|
||
x: e.clientX,
|
||
y: e.clientY
|
||
}, true);
|
||
set(dragBase, {
|
||
x: get(panX),
|
||
y: get(panY)
|
||
}, true);
|
||
e.currentTarget.setPointerCapture(e.pointerId);
|
||
}
|
||
function onPointerMove(e) {
|
||
if (!get(dragging)) return;
|
||
set(panX, get(dragBase).x + (e.clientX - get(dragStart).x));
|
||
set(panY, get(dragBase).y + (e.clientY - get(dragStart).y));
|
||
}
|
||
function onPointerUp() {
|
||
set(dragging, false);
|
||
}
|
||
function fmtTime(ts) {
|
||
if (!ts) return "";
|
||
return (/* @__PURE__ */ new Date(ts * 1e3)).toLocaleTimeString("en-US", {
|
||
hour: "numeric",
|
||
minute: "2-digit"
|
||
});
|
||
}
|
||
function fmtDate(ts) {
|
||
if (!ts) return "";
|
||
return (/* @__PURE__ */ new Date(ts * 1e3)).toLocaleDateString("en-US", {
|
||
month: "short",
|
||
day: "numeric"
|
||
});
|
||
}
|
||
const isPast = /* @__PURE__ */ user_derived(() => get(currentIdx) < get(pastFrames).length);
|
||
const frameLabel = /* @__PURE__ */ user_derived(() => get(isPast) ? "Past" : "Forecast");
|
||
var div = root_15();
|
||
var div_1 = child(div);
|
||
var node = sibling(child(div_1), 2);
|
||
var consequent = ($$anchor) => {
|
||
var button = root$4();
|
||
let classes;
|
||
template_effect(() => {
|
||
classes = set_class(button, 1, "layer-toggle svelte-tjvfz7", null, classes, { on: get(showLightning) });
|
||
set_attribute(button, "aria-checked", get(showLightning));
|
||
});
|
||
delegated("click", button, toggleLightning);
|
||
append($$anchor, button);
|
||
};
|
||
if_block(node, ($$render) => {
|
||
if (get(loc)) $$render(consequent);
|
||
});
|
||
reset(div_1);
|
||
var node_1 = sibling(div_1, 2);
|
||
var consequent_1 = ($$anchor) => {
|
||
var div_2 = root_1$4();
|
||
var span = child(div_2);
|
||
var text = child(span);
|
||
reset(span);
|
||
var button_1 = sibling(span, 2);
|
||
reset(div_2);
|
||
template_effect(() => set_text(text, `⚠️ ${get(error) ?? ""}`));
|
||
delegated("click", button_1, loadRadar);
|
||
append($$anchor, div_2);
|
||
};
|
||
var alternate = ($$anchor) => {
|
||
var fragment = root_14();
|
||
var div_3 = first_child(fragment);
|
||
let classes_1;
|
||
var node_2 = child(div_3);
|
||
var consequent_4 = ($$anchor) => {
|
||
var fragment_1 = root_6$2();
|
||
var div_4 = first_child(fragment_1);
|
||
var node_3 = child(div_4);
|
||
each(node_3, 17, () => get(tiles), (t) => t.col + "_" + t.row, ($$anchor, t) => {
|
||
var fragment_2 = root_3$3();
|
||
var img = first_child(fragment_2);
|
||
var node_4 = sibling(img, 2);
|
||
var consequent_2 = ($$anchor) => {
|
||
var img_1 = root_2$3();
|
||
template_effect(($0) => {
|
||
set_attribute(img_1, "src", $0);
|
||
set_style(img_1, `grid-column: ${get(t).col + 3}; grid-row: ${get(t).row + 3}`);
|
||
}, [() => radarUrl(get(t).tx, get(t).ty)]);
|
||
append($$anchor, img_1);
|
||
};
|
||
if_block(node_4, ($$render) => {
|
||
if (get(frame)) $$render(consequent_2);
|
||
});
|
||
template_effect(($0) => {
|
||
set_attribute(img, "src", $0);
|
||
set_style(img, `grid-column: ${get(t).col + 3}; grid-row: ${get(t).row + 3}`);
|
||
}, [() => osmUrl(get(t).tx, get(t).ty)]);
|
||
append($$anchor, fragment_2);
|
||
});
|
||
var node_5 = sibling(node_3, 2);
|
||
var consequent_3 = ($$anchor) => {
|
||
var div_5 = root_5$3();
|
||
each(div_5, 21, () => get(strikeMarkers), (s) => s.time + "_" + s.lat + "_" + s.lon, ($$anchor, s) => {
|
||
var span_1 = root_4$3();
|
||
template_effect(($0) => {
|
||
set_attribute(span_1, "title", $0);
|
||
set_style(span_1, `left: ${get(s).x ?? ""}px; top: ${get(s).y ?? ""}px`);
|
||
}, [() => "Strike " + (/* @__PURE__ */ new Date(get(s).time * 1e3)).toLocaleString()]);
|
||
append($$anchor, span_1);
|
||
});
|
||
reset(div_5);
|
||
append($$anchor, div_5);
|
||
};
|
||
if_block(node_5, ($$render) => {
|
||
if (get(showLightning)) $$render(consequent_3);
|
||
});
|
||
reset(div_4);
|
||
var div_6 = sibling(div_4, 2);
|
||
template_effect(() => {
|
||
set_style(div_4, `transform: translate(${get(panX) + 128 - get(pxOff)}px, ${get(panY) + 128 - get(pyOff)}px)`);
|
||
set_attribute(div_6, "title", get(loc)?.name || "");
|
||
set_style(div_6, `transform: translate(calc(-50% + ${get(panX) ?? ""}px), calc(-50% + ${get(panY) ?? ""}px))`);
|
||
});
|
||
append($$anchor, fragment_1);
|
||
};
|
||
if_block(node_2, ($$render) => {
|
||
if (get(loc)) $$render(consequent_4);
|
||
});
|
||
var node_6 = sibling(node_2, 2);
|
||
var consequent_5 = ($$anchor) => {
|
||
append($$anchor, root_7$1());
|
||
};
|
||
if_block(node_6, ($$render) => {
|
||
if (get(loading)) $$render(consequent_5);
|
||
});
|
||
var node_7 = sibling(node_6, 2);
|
||
var consequent_6 = ($$anchor) => {
|
||
var div_8 = root_8();
|
||
var span_2 = child(div_8);
|
||
var text_1 = child(span_2);
|
||
reset(span_2);
|
||
var button_2 = sibling(span_2, 2);
|
||
reset(div_8);
|
||
template_effect(() => set_text(text_1, `⚠️ ${get(error) ?? ""}`));
|
||
delegated("click", button_2, loadRadar);
|
||
append($$anchor, div_8);
|
||
};
|
||
if_block(node_7, ($$render) => {
|
||
if (get(error)) $$render(consequent_6);
|
||
});
|
||
var node_8 = sibling(node_7, 2);
|
||
var consequent_7 = ($$anchor) => {
|
||
append($$anchor, root_9());
|
||
};
|
||
var consequent_8 = ($$anchor) => {
|
||
var div_10 = root_10();
|
||
var span_3 = child(div_10);
|
||
var text_2 = child(span_3);
|
||
reset(span_3);
|
||
reset(div_10);
|
||
template_effect(() => set_text(text_2, `⚡ ${get(lightningError) ?? ""} (relay not live yet)`));
|
||
append($$anchor, div_10);
|
||
};
|
||
var consequent_9 = ($$anchor) => {
|
||
append($$anchor, root_11());
|
||
};
|
||
if_block(node_8, ($$render) => {
|
||
if (get(showLightning) && get(lightningLoading)) $$render(consequent_7);
|
||
else if (get(showLightning) && get(lightningError)) $$render(consequent_8, 1);
|
||
else if (get(showLightning) && get(strikes).length === 0 && !get(lightningLoading)) $$render(consequent_9, 2);
|
||
});
|
||
var div_12 = sibling(node_8, 2);
|
||
var button_3 = child(div_12);
|
||
var span_4 = sibling(button_3, 2);
|
||
var text_3 = child(span_4, true);
|
||
reset(span_4);
|
||
var button_4 = sibling(span_4, 2);
|
||
reset(div_12);
|
||
reset(div_3);
|
||
var node_9 = sibling(div_3, 2);
|
||
var consequent_10 = ($$anchor) => {
|
||
var div_13 = root_12();
|
||
var div_14 = child(div_13);
|
||
var button_5 = child(div_14);
|
||
var button_6 = sibling(button_5, 2);
|
||
var text_4 = child(button_6, true);
|
||
reset(button_6);
|
||
var button_7 = sibling(button_6, 2);
|
||
reset(div_14);
|
||
var div_15 = sibling(div_14, 2);
|
||
var input = child(div_15);
|
||
remove_input_defaults(input);
|
||
reset(div_15);
|
||
var div_16 = sibling(div_15, 2);
|
||
var span_5 = child(div_16);
|
||
var text_5 = child(span_5, true);
|
||
reset(span_5);
|
||
var span_6 = sibling(span_5, 2);
|
||
var text_6 = child(span_6, true);
|
||
reset(span_6);
|
||
var span_7 = sibling(span_6, 2);
|
||
let classes_2;
|
||
var text_7 = child(span_7, true);
|
||
reset(span_7);
|
||
var span_8 = sibling(span_7, 2);
|
||
var text_8 = child(span_8);
|
||
reset(span_8);
|
||
reset(div_16);
|
||
reset(div_13);
|
||
template_effect(($0, $1) => {
|
||
set_attribute(button_6, "aria-label", get(playing) ? "Pause" : "Play");
|
||
set_text(text_4, get(playing) ? "⏸" : "▶");
|
||
set_attribute(input, "max", get(totalFrames) - 1);
|
||
set_value(input, get(currentIdx));
|
||
set_text(text_5, $0);
|
||
set_text(text_6, $1);
|
||
classes_2 = set_class(span_7, 1, "tl-badge svelte-tjvfz7", null, classes_2, {
|
||
"tl-past": get(isPast),
|
||
"tl-future": !get(isPast)
|
||
});
|
||
set_text(text_7, get(frameLabel));
|
||
set_text(text_8, `${get(currentIdx) + 1}/${get(totalFrames) ?? ""}`);
|
||
}, [() => fmtTime(get(frame).time), () => fmtDate(get(frame).time)]);
|
||
delegated("click", button_5, stepBack);
|
||
delegated("click", button_6, togglePlay);
|
||
delegated("click", button_7, stepForward);
|
||
delegated("input", input, (e) => {
|
||
set(playing, false);
|
||
if (animRef) {
|
||
clearTimeout(animRef);
|
||
animRef = null;
|
||
}
|
||
set(currentIdx, Number(e.target.value), true);
|
||
});
|
||
append($$anchor, div_13);
|
||
};
|
||
var consequent_11 = ($$anchor) => {
|
||
var div_17 = root_13();
|
||
var button_8 = sibling(child(div_17), 2);
|
||
reset(div_17);
|
||
delegated("click", button_8, loadRadar);
|
||
append($$anchor, div_17);
|
||
};
|
||
if_block(node_9, ($$render) => {
|
||
if (get(frame)) $$render(consequent_10);
|
||
else if (!get(loading)) $$render(consequent_11, 1);
|
||
});
|
||
template_effect(() => {
|
||
classes_1 = set_class(div_3, 1, "radar-viewport svelte-tjvfz7", null, classes_1, { dragging: get(dragging) });
|
||
button_3.disabled = get(zoom) >= 11;
|
||
set_text(text_3, get(zoom));
|
||
button_4.disabled = get(zoom) <= 2;
|
||
});
|
||
delegated("pointerdown", div_3, onPointerDown);
|
||
delegated("pointermove", div_3, onPointerMove);
|
||
delegated("pointerup", div_3, onPointerUp);
|
||
event("pointerleave", div_3, onPointerUp);
|
||
delegated("click", button_3, zoomIn);
|
||
delegated("click", button_4, zoomOut);
|
||
append($$anchor, fragment);
|
||
};
|
||
if_block(node_1, ($$render) => {
|
||
if (get(error) && !get(loc)) $$render(consequent_1);
|
||
else $$render(alternate, -1);
|
||
});
|
||
reset(div);
|
||
append($$anchor, div);
|
||
pop();
|
||
}
|
||
delegate([
|
||
"click",
|
||
"pointerdown",
|
||
"pointermove",
|
||
"pointerup",
|
||
"input"
|
||
]);
|
||
//#endregion
|
||
//#region src/components/NotificationBell.svelte
|
||
var root$3 = /* @__PURE__ */ from_html(`<span class="bell-icon ringing svelte-hpnwii">🔔</span> <span class="bell-badge svelte-hpnwii"> </span>`, 1);
|
||
var root_1$3 = /* @__PURE__ */ from_html(`<span class="bell-icon svelte-hpnwii">🔕</span>`);
|
||
var root_2$2 = /* @__PURE__ */ from_html(`<button class="bell-dismiss-all svelte-hpnwii">Clear all</button>`);
|
||
var root_3$2 = /* @__PURE__ */ from_html(`<div><span class="bell-alert-icon svelte-hpnwii"> </span> <div class="bell-alert-content svelte-hpnwii"><span class="bell-alert-msg svelte-hpnwii"> </span> <span class="bell-alert-type svelte-hpnwii"> </span></div> <button class="bell-dismiss svelte-hpnwii">✕</button></div>`);
|
||
var root_4$2 = /* @__PURE__ */ from_html(`<div class="bell-empty svelte-hpnwii"><span class="bell-empty-icon svelte-hpnwii">✅</span> <span class="text-muted text-sm svelte-hpnwii">No weather alerts right now</span></div>`);
|
||
var root_5$2 = /* @__PURE__ */ from_html(`<div class="bell-dropdown animate-slide-down svelte-hpnwii"><div class="bell-header svelte-hpnwii"><span class="bell-title svelte-hpnwii"> </span> <!></div> <div class="bell-body svelte-hpnwii"><!></div></div>`);
|
||
var root_6$1 = /* @__PURE__ */ from_html(`<div class="bell-wrapper svelte-hpnwii"><button><!></button> <!></div>`);
|
||
function NotificationBell($$anchor, $$props) {
|
||
push($$props, true);
|
||
let open = /* @__PURE__ */ state(false);
|
||
let bellRef = /* @__PURE__ */ state(null);
|
||
function toggle() {
|
||
set(open, !get(open));
|
||
}
|
||
function handleDismiss(alertId) {
|
||
notifications.dismiss(alertId);
|
||
}
|
||
function handleDismissAll() {
|
||
for (const alert of [...notifications.alerts]) notifications.dismiss(alert.id);
|
||
set(open, false);
|
||
}
|
||
user_effect(() => {
|
||
if (!get(open)) return;
|
||
function onClick(e) {
|
||
if (get(bellRef) && !get(bellRef).contains(e.target)) set(open, false);
|
||
}
|
||
document.addEventListener("click", onClick);
|
||
return () => document.removeEventListener("click", onClick);
|
||
});
|
||
const count = /* @__PURE__ */ user_derived(() => notifications.alerts.length);
|
||
const hasDanger = /* @__PURE__ */ user_derived(() => notifications.alerts.some((a) => a.severity === "danger"));
|
||
var div = root_6$1();
|
||
var button = child(div);
|
||
let classes;
|
||
var node = child(button);
|
||
var consequent = ($$anchor) => {
|
||
var fragment = root$3();
|
||
var span = sibling(first_child(fragment), 2);
|
||
var text = child(span, true);
|
||
reset(span);
|
||
template_effect(() => set_text(text, get(count)));
|
||
append($$anchor, fragment);
|
||
};
|
||
var alternate = ($$anchor) => {
|
||
append($$anchor, root_1$3());
|
||
};
|
||
if_block(node, ($$render) => {
|
||
if (get(count) > 0) $$render(consequent);
|
||
else $$render(alternate, -1);
|
||
});
|
||
reset(button);
|
||
var node_1 = sibling(button, 2);
|
||
var consequent_3 = ($$anchor) => {
|
||
var div_1 = root_5$2();
|
||
var div_2 = child(div_1);
|
||
var span_2 = child(div_2);
|
||
var text_1 = child(span_2, true);
|
||
reset(span_2);
|
||
var node_2 = sibling(span_2, 2);
|
||
var consequent_1 = ($$anchor) => {
|
||
var button_1 = root_2$2();
|
||
delegated("click", button_1, handleDismissAll);
|
||
append($$anchor, button_1);
|
||
};
|
||
if_block(node_2, ($$render) => {
|
||
if (get(count) > 0) $$render(consequent_1);
|
||
});
|
||
reset(div_2);
|
||
var div_3 = sibling(div_2, 2);
|
||
var node_3 = child(div_3);
|
||
var consequent_2 = ($$anchor) => {
|
||
var fragment_1 = comment();
|
||
each(first_child(fragment_1), 17, () => notifications.alerts, (alert) => alert.id, ($$anchor, alert) => {
|
||
var div_4 = root_3$2();
|
||
var span_3 = child(div_4);
|
||
var text_2 = child(span_3, true);
|
||
reset(span_3);
|
||
var div_5 = sibling(span_3, 2);
|
||
var span_4 = child(div_5);
|
||
var text_3 = child(span_4, true);
|
||
reset(span_4);
|
||
var span_5 = sibling(span_4, 2);
|
||
var text_4 = child(span_5, true);
|
||
reset(span_5);
|
||
reset(div_5);
|
||
var button_2 = sibling(div_5, 2);
|
||
reset(div_4);
|
||
template_effect(() => {
|
||
set_class(div_4, 1, `bell-alert ${get(alert).severity ?? ""}`, "svelte-hpnwii");
|
||
set_text(text_2, get(alert).icon);
|
||
set_text(text_3, get(alert).message);
|
||
set_text(text_4, get(alert).type);
|
||
set_attribute(button_2, "aria-label", `Dismiss ${get(alert).type ?? ""}`);
|
||
});
|
||
delegated("click", button_2, () => handleDismiss(get(alert).id));
|
||
append($$anchor, div_4);
|
||
});
|
||
append($$anchor, fragment_1);
|
||
};
|
||
var alternate_1 = ($$anchor) => {
|
||
append($$anchor, root_4$2());
|
||
};
|
||
if_block(node_3, ($$render) => {
|
||
if (get(count) > 0) $$render(consequent_2);
|
||
else $$render(alternate_1, -1);
|
||
});
|
||
reset(div_3);
|
||
reset(div_1);
|
||
template_effect(() => set_text(text_1, get(count) > 0 ? `${get(count)} Weather Alert${get(count) !== 1 ? "s" : ""}` : "No Active Alerts"));
|
||
append($$anchor, div_1);
|
||
};
|
||
if_block(node_1, ($$render) => {
|
||
if (get(open)) $$render(consequent_3);
|
||
});
|
||
reset(div);
|
||
bind_this(div, ($$value) => set(bellRef, $$value), () => get(bellRef));
|
||
template_effect(() => {
|
||
classes = set_class(button, 1, "btn-icon bell-btn svelte-hpnwii", null, classes, {
|
||
"has-alerts": get(count) > 0,
|
||
"has-danger": get(hasDanger)
|
||
});
|
||
set_attribute(button, "aria-label", `${get(count) ?? ""} notifications`);
|
||
set_attribute(button, "title", `${get(count) ?? ""} weather alert${get(count) !== 1 ? "s" : ""}`);
|
||
});
|
||
delegated("click", button, toggle);
|
||
append($$anchor, div);
|
||
pop();
|
||
}
|
||
delegate(["click"]);
|
||
//#endregion
|
||
//#region src/components/SettingsDialog.svelte
|
||
init_db();
|
||
var root$2 = /* @__PURE__ */ from_html(`<div class="setting-row svelte-1koizbb"><span class="svelte-1koizbb">Rain threshold</span> <select class="svelte-1koizbb"><option>50%</option><option>60%</option><option>70%</option><option>80%</option><option>90%</option></select></div> <div class="setting-row svelte-1koizbb"><span class="svelte-1koizbb">Wind gust threshold</span> <select class="svelte-1koizbb"><option> </option><option> </option><option> </option><option> </option></select></div> <div class="setting-row svelte-1koizbb"><span class="svelte-1koizbb">UV Index threshold</span> <select class="svelte-1koizbb"><option>4+ (Moderate)</option><option>6+ (High)</option><option>8+ (Very High)</option><option>11+ (Extreme)</option></select></div> <div class="setting-row svelte-1koizbb"><span class="svelte-1koizbb">High temp alert</span> <select class="svelte-1koizbb"><option>30°C / 86°F</option><option>35°C / 95°F</option><option>38°C / 100°F</option><option>42°C / 108°F</option></select></div> <div class="setting-row svelte-1koizbb"><span class="svelte-1koizbb">Low temp alert</span> <select class="svelte-1koizbb"><option>5°C / 41°F</option><option>0°C / 32°F</option><option>-5°C / 23°F</option><option>-10°C / 14°F</option></select></div> <div class="setting-row svelte-1koizbb"><span class="svelte-1koizbb">Thunderstorm alerts</span> <label class="toggle svelte-1koizbb"><input type="checkbox" class="svelte-1koizbb"/> <span class="toggle-slider svelte-1koizbb"></span></label></div>`, 1);
|
||
var root_1$2 = /* @__PURE__ */ from_html(`<div class="modal-overlay svelte-1koizbb" role="presentation"><div class="modal svelte-1koizbb" role="dialog" aria-modal="true" aria-label="Settings" tabindex="-1"><div class="modal-header svelte-1koizbb"><h3>⚙️ Settings</h3> <button class="btn-icon" aria-label="Close">✕</button></div> <div class="settings-body svelte-1koizbb"><div class="settings-group svelte-1koizbb"><h4 class="svelte-1koizbb">Data source</h4> <div class="setting-row svelte-1koizbb"><div class="setting-col svelte-1koizbb"><span class="svelte-1koizbb">Weather provider</span> <span class="setting-help svelte-1koizbb">NWS is US-only; falls back to Open-Meteo if unavailable.</span></div> <select class="svelte-1koizbb"><option>Open-Meteo (global)</option><option>National Weather Service (US)</option></select></div></div> <div class="settings-group svelte-1koizbb"><h4 class="svelte-1koizbb">Auto-refresh</h4> <div class="setting-row svelte-1koizbb"><div class="setting-col svelte-1koizbb"><span class="svelte-1koizbb">Refresh interval</span> <span class="setting-help svelte-1koizbb">Automatically fetch updated weather on a schedule. Turn off to refresh only when you open the app.</span></div> <select class="svelte-1koizbb"><option>Off</option><option>Every 5 minutes</option><option>Every 10 minutes</option><option>Every 15 minutes</option><option>Every 30 minutes</option><option>Every hour</option></select></div></div> <div class="settings-group svelte-1koizbb"><h4 class="svelte-1koizbb">Units</h4> <div class="setting-row svelte-1koizbb"><span class="svelte-1koizbb">Measurement system</span> <select class="svelte-1koizbb"><option>Metric (°C, km/h)</option><option>Imperial (°F, mph)</option></select></div></div> <div class="settings-group svelte-1koizbb"><h4 class="svelte-1koizbb">Weather Alerts</h4> <div class="setting-row svelte-1koizbb"><span class="svelte-1koizbb">Enable Alerts</span> <label class="toggle svelte-1koizbb"><input type="checkbox" class="svelte-1koizbb"/> <span class="toggle-slider svelte-1koizbb"></span></label></div> <!></div> <div class="settings-group danger-zone svelte-1koizbb"><h4 class="svelte-1koizbb">Data</h4> <button class="btn btn-danger btn-sm">🗑 Reset All Data</button></div></div> <div class="modal-footer svelte-1koizbb"><button class="btn btn-primary"> </button> <button class="btn btn-ghost">Cancel</button></div></div></div>`);
|
||
function SettingsDialog($$anchor, $$props) {
|
||
push($$props, true);
|
||
let localSettings = proxy({
|
||
...app$1.settings,
|
||
alertThresholds: { ...app$1.settings.alertThresholds }
|
||
});
|
||
let saving = /* @__PURE__ */ state(false);
|
||
async function handleSave() {
|
||
set(saving, true);
|
||
try {
|
||
app$1.settings = { ...localSettings };
|
||
await saveSettings({
|
||
...localSettings,
|
||
alertThresholds: { ...localSettings.alertThresholds }
|
||
});
|
||
await app$1.refreshForecast();
|
||
$$props.onClose();
|
||
} catch (e) {
|
||
console.error("Failed to save settings:", e);
|
||
} finally {
|
||
set(saving, false);
|
||
}
|
||
}
|
||
function handleReset() {
|
||
if (confirm("Reset all locations and stored data?")) __vitePreload(() => Promise.resolve().then(() => (init_db(), db_exports)).then(async (db) => {
|
||
await db.clearAllLocations();
|
||
app$1.locations = [];
|
||
app$1.selectedLocationId = null;
|
||
app$1.forecastData = null;
|
||
$$props.onClose();
|
||
}), void 0, import.meta.url);
|
||
}
|
||
function handleBackdrop(e) {
|
||
if (e.target === e.currentTarget) $$props.onClose();
|
||
}
|
||
var div = root_1$2();
|
||
var div_1 = child(div);
|
||
var div_2 = child(div_1);
|
||
var button = sibling(child(div_2), 2);
|
||
reset(div_2);
|
||
var div_3 = sibling(div_2, 2);
|
||
var div_4 = child(div_3);
|
||
var div_5 = sibling(child(div_4), 2);
|
||
var select = sibling(child(div_5), 2);
|
||
var option = child(select);
|
||
option.value = option.__value = "open-meteo";
|
||
var option_1 = sibling(option);
|
||
option_1.value = option_1.__value = "nws";
|
||
reset(select);
|
||
var select_value;
|
||
init_select(select);
|
||
reset(div_5);
|
||
reset(div_4);
|
||
var div_6 = sibling(div_4, 2);
|
||
var div_7 = sibling(child(div_6), 2);
|
||
var select_1 = sibling(child(div_7), 2);
|
||
var option_2 = child(select_1);
|
||
option_2.value = option_2.__value = "0";
|
||
var option_3 = sibling(option_2);
|
||
option_3.value = option_3.__value = "5";
|
||
var option_4 = sibling(option_3);
|
||
option_4.value = option_4.__value = "10";
|
||
var option_5 = sibling(option_4);
|
||
option_5.value = option_5.__value = "15";
|
||
var option_6 = sibling(option_5);
|
||
option_6.value = option_6.__value = "30";
|
||
var option_7 = sibling(option_6);
|
||
option_7.value = option_7.__value = "60";
|
||
reset(select_1);
|
||
var select_1_value;
|
||
init_select(select_1);
|
||
reset(div_7);
|
||
reset(div_6);
|
||
var div_8 = sibling(div_6, 2);
|
||
var div_9 = sibling(child(div_8), 2);
|
||
var select_2 = sibling(child(div_9), 2);
|
||
var option_8 = child(select_2);
|
||
option_8.value = option_8.__value = "metric";
|
||
var option_9 = sibling(option_8);
|
||
option_9.value = option_9.__value = "imperial";
|
||
reset(select_2);
|
||
var select_2_value;
|
||
init_select(select_2);
|
||
reset(div_9);
|
||
reset(div_8);
|
||
var div_10 = sibling(div_8, 2);
|
||
var div_11 = sibling(child(div_10), 2);
|
||
var label = sibling(child(div_11), 2);
|
||
var input = child(label);
|
||
remove_input_defaults(input);
|
||
next(2);
|
||
reset(label);
|
||
reset(div_11);
|
||
var node = sibling(div_11, 2);
|
||
var consequent = ($$anchor) => {
|
||
var fragment = root$2();
|
||
var div_12 = first_child(fragment);
|
||
var select_3 = sibling(child(div_12), 2);
|
||
var option_10 = child(select_3);
|
||
option_10.value = option_10.__value = "50";
|
||
var option_11 = sibling(option_10);
|
||
option_11.value = option_11.__value = "60";
|
||
var option_12 = sibling(option_11);
|
||
option_12.value = option_12.__value = "70";
|
||
var option_13 = sibling(option_12);
|
||
option_13.value = option_13.__value = "80";
|
||
var option_14 = sibling(option_13);
|
||
option_14.value = option_14.__value = "90";
|
||
reset(select_3);
|
||
var select_3_value;
|
||
init_select(select_3);
|
||
reset(div_12);
|
||
var div_13 = sibling(div_12, 2);
|
||
var select_4 = sibling(child(div_13), 2);
|
||
var option_15 = child(select_4);
|
||
var text = child(option_15);
|
||
reset(option_15);
|
||
option_15.value = option_15.__value = "30";
|
||
var option_16 = sibling(option_15);
|
||
var text_1 = child(option_16);
|
||
reset(option_16);
|
||
option_16.value = option_16.__value = "40";
|
||
var option_17 = sibling(option_16);
|
||
var text_2 = child(option_17);
|
||
reset(option_17);
|
||
option_17.value = option_17.__value = "50";
|
||
var option_18 = sibling(option_17);
|
||
var text_3 = child(option_18);
|
||
reset(option_18);
|
||
option_18.value = option_18.__value = "60";
|
||
reset(select_4);
|
||
var select_4_value;
|
||
init_select(select_4);
|
||
reset(div_13);
|
||
var div_14 = sibling(div_13, 2);
|
||
var select_5 = sibling(child(div_14), 2);
|
||
var option_19 = child(select_5);
|
||
option_19.value = option_19.__value = "4";
|
||
var option_20 = sibling(option_19);
|
||
option_20.value = option_20.__value = "6";
|
||
var option_21 = sibling(option_20);
|
||
option_21.value = option_21.__value = "8";
|
||
var option_22 = sibling(option_21);
|
||
option_22.value = option_22.__value = "11";
|
||
reset(select_5);
|
||
var select_5_value;
|
||
init_select(select_5);
|
||
reset(div_14);
|
||
var div_15 = sibling(div_14, 2);
|
||
var select_6 = sibling(child(div_15), 2);
|
||
var option_23 = child(select_6);
|
||
option_23.value = option_23.__value = "30";
|
||
var option_24 = sibling(option_23);
|
||
option_24.value = option_24.__value = "35";
|
||
var option_25 = sibling(option_24);
|
||
option_25.value = option_25.__value = "38";
|
||
var option_26 = sibling(option_25);
|
||
option_26.value = option_26.__value = "42";
|
||
reset(select_6);
|
||
var select_6_value;
|
||
init_select(select_6);
|
||
reset(div_15);
|
||
var div_16 = sibling(div_15, 2);
|
||
var select_7 = sibling(child(div_16), 2);
|
||
var option_27 = child(select_7);
|
||
option_27.value = option_27.__value = "5";
|
||
var option_28 = sibling(option_27);
|
||
option_28.value = option_28.__value = "0";
|
||
var option_29 = sibling(option_28);
|
||
option_29.value = option_29.__value = "-5";
|
||
var option_30 = sibling(option_29);
|
||
option_30.value = option_30.__value = "-10";
|
||
reset(select_7);
|
||
var select_7_value;
|
||
init_select(select_7);
|
||
reset(div_16);
|
||
var div_17 = sibling(div_16, 2);
|
||
var label_1 = sibling(child(div_17), 2);
|
||
var input_1 = child(label_1);
|
||
remove_input_defaults(input_1);
|
||
next(2);
|
||
reset(label_1);
|
||
reset(div_17);
|
||
template_effect(($0, $1, $2, $3, $4) => {
|
||
if (select_3_value !== (select_3_value = $0)) select_3.value = (select_3.__value = $0) ?? "", select_option(select_3, $0);
|
||
set_text(text, `30 ${localSettings.windUnit ?? ""}`);
|
||
set_text(text_1, `40 ${localSettings.windUnit ?? ""}`);
|
||
set_text(text_2, `50 ${localSettings.windUnit ?? ""}`);
|
||
set_text(text_3, `60 ${localSettings.windUnit ?? ""}`);
|
||
if (select_4_value !== (select_4_value = $1)) select_4.value = (select_4.__value = $1) ?? "", select_option(select_4, $1);
|
||
if (select_5_value !== (select_5_value = $2)) select_5.value = (select_5.__value = $2) ?? "", select_option(select_5, $2);
|
||
if (select_6_value !== (select_6_value = $3)) select_6.value = (select_6.__value = $3) ?? "", select_option(select_6, $3);
|
||
if (select_7_value !== (select_7_value = $4)) select_7.value = (select_7.__value = $4) ?? "", select_option(select_7, $4);
|
||
set_checked(input_1, localSettings.alertThresholds.thunderstorm);
|
||
}, [
|
||
() => String(localSettings.alertThresholds.precip),
|
||
() => String(localSettings.alertThresholds.windGust),
|
||
() => String(localSettings.alertThresholds.uvIndex),
|
||
() => String(localSettings.alertThresholds.tempHigh),
|
||
() => String(localSettings.alertThresholds.tempLow)
|
||
]);
|
||
delegated("change", select_3, (e) => localSettings.alertThresholds.precip = parseInt(e.target.value));
|
||
delegated("change", select_4, (e) => localSettings.alertThresholds.windGust = parseInt(e.target.value));
|
||
delegated("change", select_5, (e) => localSettings.alertThresholds.uvIndex = parseInt(e.target.value));
|
||
delegated("change", select_6, (e) => localSettings.alertThresholds.tempHigh = parseInt(e.target.value));
|
||
delegated("change", select_7, (e) => localSettings.alertThresholds.tempLow = parseInt(e.target.value));
|
||
delegated("change", input_1, (e) => localSettings.alertThresholds.thunderstorm = e.target.checked);
|
||
append($$anchor, fragment);
|
||
};
|
||
if_block(node, ($$render) => {
|
||
if (localSettings.alertsEnabled) $$render(consequent);
|
||
});
|
||
reset(div_10);
|
||
var div_18 = sibling(div_10, 2);
|
||
var button_1 = sibling(child(div_18), 2);
|
||
reset(div_18);
|
||
reset(div_3);
|
||
var div_19 = sibling(div_3, 2);
|
||
var button_2 = child(div_19);
|
||
var text_4 = child(button_2, true);
|
||
reset(button_2);
|
||
var button_3 = sibling(button_2, 2);
|
||
reset(div_19);
|
||
reset(div_1);
|
||
reset(div);
|
||
template_effect(($0) => {
|
||
if (select_value !== (select_value = localSettings.source || "open-meteo")) select.value = (select.__value = localSettings.source || "open-meteo") ?? "", select_option(select, localSettings.source || "open-meteo");
|
||
if (select_1_value !== (select_1_value = $0)) select_1.value = (select_1.__value = $0) ?? "", select_option(select_1, $0);
|
||
if (select_2_value !== (select_2_value = localSettings.units)) select_2.value = (select_2.__value = localSettings.units) ?? "", select_option(select_2, localSettings.units);
|
||
set_checked(input, localSettings.alertsEnabled);
|
||
button_2.disabled = get(saving);
|
||
set_text(text_4, get(saving) ? "Saving..." : "Save Settings");
|
||
}, [() => String(localSettings.refreshInterval || 0)]);
|
||
delegated("click", div, handleBackdrop);
|
||
delegated("click", div_1, (e) => e.stopPropagation());
|
||
delegated("click", button, function(...$$args) {
|
||
$$props.onClose?.apply(this, $$args);
|
||
});
|
||
delegated("change", select, (e) => localSettings.source = e.target.value);
|
||
delegated("change", select_1, (e) => localSettings.refreshInterval = parseInt(e.target.value));
|
||
delegated("change", select_2, (e) => localSettings.units = e.target.value);
|
||
delegated("change", input, (e) => localSettings.alertsEnabled = e.target.checked);
|
||
delegated("click", button_1, handleReset);
|
||
delegated("click", button_2, handleSave);
|
||
delegated("click", button_3, function(...$$args) {
|
||
$$props.onClose?.apply(this, $$args);
|
||
});
|
||
append($$anchor, div);
|
||
pop();
|
||
}
|
||
delegate(["click", "change"]);
|
||
//#endregion
|
||
//#region src/components/AddLocationDialog.svelte
|
||
var root$1 = /* @__PURE__ */ from_html(`<span class="search-spinner svelte-1iarmsg">⏳</span>`);
|
||
var root_1$1 = /* @__PURE__ */ from_html(`<div class="error-msg svelte-1iarmsg"> </div>`);
|
||
var root_2$1 = /* @__PURE__ */ from_html(`<button class="result-item svelte-1iarmsg"><span class="result-icon svelte-1iarmsg">🏙️</span> <div class="result-info svelte-1iarmsg"><span class="result-name svelte-1iarmsg"> </span> <span class="result-detail svelte-1iarmsg"> </span></div> <span class="result-arrow svelte-1iarmsg">→</span></button>`);
|
||
var root_3$1 = /* @__PURE__ */ from_html(`<div class="results-list svelte-1iarmsg"></div>`);
|
||
var root_4$1 = /* @__PURE__ */ from_html(`<div class="no-results svelte-1iarmsg"><p class="text-muted text-sm"> </p></div>`);
|
||
var root_5$1 = /* @__PURE__ */ from_html(`<div class="modal-overlay svelte-1iarmsg" role="presentation"><div class="modal svelte-1iarmsg" role="dialog" aria-modal="true" aria-label="Add location" tabindex="-1"><div class="modal-header svelte-1iarmsg"><h3 class="svelte-1iarmsg">Add Location</h3> <button class="btn-icon" aria-label="Close">✕</button></div> <div class="search-row svelte-1iarmsg"><input type="search" placeholder="Search for a city..." autocomplete="off" class="svelte-1iarmsg"/> <!></div> <!> <!> <div class="geolocate-section svelte-1iarmsg"><button class="btn btn-ghost btn-sm w-full">📍 Use Current Location</button></div></div></div>`);
|
||
function AddLocationDialog($$anchor, $$props) {
|
||
push($$props, true);
|
||
let searchQuery = /* @__PURE__ */ state("");
|
||
let searchTimer = null;
|
||
let geocodingResults = /* @__PURE__ */ state(proxy([]));
|
||
let loading = /* @__PURE__ */ state(false);
|
||
let error = /* @__PURE__ */ state("");
|
||
function handleSearch(e) {
|
||
const val = e.target.value;
|
||
set(searchQuery, val, true);
|
||
clearTimeout(searchTimer);
|
||
if (val.length < 2) {
|
||
set(geocodingResults, [], true);
|
||
return;
|
||
}
|
||
set(loading, true);
|
||
set(error, "");
|
||
searchTimer = setTimeout(async () => {
|
||
try {
|
||
const { searchLocations } = await __vitePreload(async () => {
|
||
const { searchLocations } = await Promise.resolve().then(() => (init_weather(), weather_exports));
|
||
return { searchLocations };
|
||
}, void 0, import.meta.url);
|
||
set(geocodingResults, await searchLocations(val), true);
|
||
} catch (e) {
|
||
set(error, "Search failed: " + (e.message || "Unknown error"));
|
||
set(geocodingResults, [], true);
|
||
} finally {
|
||
set(loading, false);
|
||
}
|
||
}, 400);
|
||
}
|
||
function handleSelect(location) {
|
||
$$props.onAdd(location);
|
||
}
|
||
function handleBackdrop(e) {
|
||
if (e.target === e.currentTarget) $$props.onClose();
|
||
}
|
||
var div = root_5$1();
|
||
var div_1 = child(div);
|
||
var div_2 = child(div_1);
|
||
var button = sibling(child(div_2), 2);
|
||
reset(div_2);
|
||
var div_3 = sibling(div_2, 2);
|
||
var input = child(div_3);
|
||
remove_input_defaults(input);
|
||
action(input, ($$node) => autofocus?.($$node));
|
||
var node = sibling(input, 2);
|
||
var consequent = ($$anchor) => {
|
||
append($$anchor, root$1());
|
||
};
|
||
if_block(node, ($$render) => {
|
||
if (get(loading)) $$render(consequent);
|
||
});
|
||
reset(div_3);
|
||
var node_1 = sibling(div_3, 2);
|
||
var consequent_1 = ($$anchor) => {
|
||
var div_4 = root_1$1();
|
||
var text = child(div_4, true);
|
||
reset(div_4);
|
||
template_effect(() => set_text(text, get(error)));
|
||
append($$anchor, div_4);
|
||
};
|
||
if_block(node_1, ($$render) => {
|
||
if (get(error)) $$render(consequent_1);
|
||
});
|
||
var node_2 = sibling(node_1, 2);
|
||
var consequent_2 = ($$anchor) => {
|
||
var div_5 = root_3$1();
|
||
each(div_5, 21, () => get(geocodingResults), index, ($$anchor, result) => {
|
||
var button_1 = root_2$1();
|
||
var div_6 = sibling(child(button_1), 2);
|
||
var span_1 = child(div_6);
|
||
var text_1 = child(span_1, true);
|
||
reset(span_1);
|
||
var span_2 = sibling(span_1, 2);
|
||
var text_2 = child(span_2);
|
||
reset(span_2);
|
||
reset(div_6);
|
||
next(2);
|
||
reset(button_1);
|
||
template_effect(() => {
|
||
set_text(text_1, get(result).name);
|
||
set_text(text_2, `${(get(result).admin1 || "") ?? ""}${get(result).admin1 && get(result).country ? ", " : ""}${(get(result).country || "") ?? ""}`);
|
||
});
|
||
delegated("click", button_1, () => handleSelect(get(result)));
|
||
append($$anchor, button_1);
|
||
});
|
||
reset(div_5);
|
||
append($$anchor, div_5);
|
||
};
|
||
var consequent_3 = ($$anchor) => {
|
||
var div_7 = root_4$1();
|
||
var p = child(div_7);
|
||
var text_3 = child(p);
|
||
reset(p);
|
||
reset(div_7);
|
||
template_effect(() => set_text(text_3, `No locations found for "${get(searchQuery) ?? ""}".`));
|
||
append($$anchor, div_7);
|
||
};
|
||
if_block(node_2, ($$render) => {
|
||
if (get(geocodingResults).length > 0) $$render(consequent_2);
|
||
else if (get(searchQuery).length >= 2 && !get(loading)) $$render(consequent_3, 1);
|
||
});
|
||
var div_8 = sibling(node_2, 2);
|
||
var button_2 = child(div_8);
|
||
reset(div_8);
|
||
reset(div_1);
|
||
reset(div);
|
||
template_effect(() => set_value(input, get(searchQuery)));
|
||
delegated("click", div, handleBackdrop);
|
||
delegated("click", div_1, (e) => e.stopPropagation());
|
||
delegated("click", button, function(...$$args) {
|
||
$$props.onClose?.apply(this, $$args);
|
||
});
|
||
delegated("input", input, handleSearch);
|
||
delegated("click", button_2, async () => {
|
||
try {
|
||
const pos = await new Promise((resolve, reject) => {
|
||
navigator.geolocation.getCurrentPosition(resolve, reject, {
|
||
enableHighAccuracy: true,
|
||
timeout: 1e4
|
||
});
|
||
});
|
||
$$props.onAdd({
|
||
name: "Current Location",
|
||
latitude: pos.coords.latitude,
|
||
longitude: pos.coords.longitude
|
||
});
|
||
} catch (e) {
|
||
alert("Could not get location. Please search above.");
|
||
}
|
||
});
|
||
append($$anchor, div);
|
||
pop();
|
||
}
|
||
delegate(["click", "input"]);
|
||
//#endregion
|
||
//#region src/App.svelte
|
||
var root = /* @__PURE__ */ from_html(`<button class="sidebar-overlay svelte-1n46o8q" aria-label="Close sidebar"></button>`);
|
||
var root_1 = /* @__PURE__ */ from_html(`<span class="current-badge svelte-1n46o8q">📍 Current</span>`);
|
||
var root_2 = /* @__PURE__ */ from_html(`<div class="error-banner animate-slide-down svelte-1n46o8q"><span> </span> <button class="btn-icon" aria-label="Dismiss">✕</button></div>`);
|
||
var root_3 = /* @__PURE__ */ from_html(`<div class="empty-state svelte-1n46o8q"><div class="empty-icon svelte-1n46o8q">🌍</div> <h2 class="svelte-1n46o8q">Welcome to WeatherLens</h2> <p class="text-muted"> </p> <button class="btn btn-primary mt-3">+ Add a Location</button></div>`);
|
||
var root_4 = /* @__PURE__ */ from_html(`<div class="loading-state svelte-1n46o8q"><div class="loading-spinner svelte-1n46o8q">⏳</div> <p class="text-muted mt-2">Loading weather data...</p></div>`);
|
||
var root_5 = /* @__PURE__ */ from_html(`<div class="animate-fade-in"><!></div>`);
|
||
var root_6 = /* @__PURE__ */ from_html(`<div class="mobile-nav svelte-1n46o8q"><button>Now</button> <button>Hourly</button> <button>Daily</button> <button>More</button> <button>Radar</button></div> <div class="desktop-tabs svelte-1n46o8q"><button>Current</button> <button>Hourly</button> <button>7-Day Forecast</button> <button>Details</button> <button>Radar</button></div> <div class="content-area svelte-1n46o8q"><!></div>`, 1);
|
||
var root_7 = /* @__PURE__ */ from_html(`<div class="app-shell svelte-1n46o8q"><div class="mobile-header svelte-1n46o8q"><button class="btn-icon" aria-label="Toggle menu"> </button> <span class="mobile-title svelte-1n46o8q"> </span> <button class="btn-icon" aria-label="Settings" title="Settings">⚙️</button> <!></div> <!> <aside><!></aside> <main class="main-content svelte-1n46o8q"><div class="top-bar svelte-1n46o8q"><div class="top-bar-title svelte-1n46o8q"><h1 class="svelte-1n46o8q"> </h1> <!></div> <div class="top-bar-actions svelte-1n46o8q"><!></div></div> <!> <!> <!> <!></main> <!> <!></div>`);
|
||
function App($$anchor, $$props) {
|
||
push($$props, true);
|
||
let viewMode = /* @__PURE__ */ state("current");
|
||
let initDone = /* @__PURE__ */ state(false);
|
||
let geoError = /* @__PURE__ */ state("");
|
||
user_effect(() => {
|
||
initApp();
|
||
});
|
||
async function initApp() {
|
||
await app$1.init();
|
||
if (app$1.locations.length === 0) await tryGeolocation();
|
||
if (app$1.forecastData) notifications.analyze();
|
||
set(initDone, true);
|
||
}
|
||
user_effect(() => {
|
||
if (app$1.forecastData && app$1.settings.alertsEnabled) notifications.analyze();
|
||
});
|
||
user_effect(() => {
|
||
app$1.settings.refreshInterval;
|
||
app$1.startAutoRefresh();
|
||
});
|
||
async function tryGeolocation() {
|
||
if (!navigator.geolocation) {
|
||
set(geoError, "Geolocation not supported. Add a location to get started.");
|
||
return;
|
||
}
|
||
try {
|
||
const { latitude, longitude } = (await new Promise((resolve, reject) => {
|
||
navigator.geolocation.getCurrentPosition(resolve, reject, {
|
||
enableHighAccuracy: true,
|
||
timeout: 1e4,
|
||
maximumAge: 6e5
|
||
});
|
||
})).coords;
|
||
const { addLocation, setCurrentLocation } = await __vitePreload(async () => {
|
||
const { addLocation, setCurrentLocation } = await Promise.resolve().then(() => (init_db(), db_exports));
|
||
return {
|
||
addLocation,
|
||
setCurrentLocation
|
||
};
|
||
}, void 0, import.meta.url);
|
||
const id = await addLocation({
|
||
name: "Current Location",
|
||
lat: Math.round(latitude * 1e4) / 1e4,
|
||
lon: Math.round(longitude * 1e4) / 1e4,
|
||
isCurrent: true,
|
||
order: 0
|
||
});
|
||
await setCurrentLocation(id);
|
||
await app$1.refreshLocations();
|
||
app$1.selectedLocationId = id;
|
||
await app$1.refreshForecast();
|
||
} catch (e) {
|
||
set(geoError, "Location access denied. Add a location manually.");
|
||
}
|
||
}
|
||
async function handleAddLocation(location) {
|
||
const { addLocation } = await __vitePreload(async () => {
|
||
const { addLocation } = await Promise.resolve().then(() => (init_db(), db_exports));
|
||
return { addLocation };
|
||
}, void 0, import.meta.url);
|
||
await addLocation({
|
||
name: location.name,
|
||
lat: location.latitude,
|
||
lon: location.longitude,
|
||
isCurrent: false
|
||
});
|
||
await app$1.refreshLocations();
|
||
app$1.showAddLocation = false;
|
||
app$1.geocodingResults = [];
|
||
const updated = await __vitePreload(() => Promise.resolve().then(() => (init_db(), db_exports)).then((m) => m.getLocations()), void 0, import.meta.url);
|
||
const newest = updated[updated.length - 1];
|
||
if (newest) app$1.selectLocation(newest.id);
|
||
}
|
||
var div = root_7();
|
||
var div_1 = child(div);
|
||
var button = child(div_1);
|
||
var text = child(button, true);
|
||
reset(button);
|
||
var span = sibling(button, 2);
|
||
var text_1 = child(span, true);
|
||
reset(span);
|
||
var button_1 = sibling(span, 2);
|
||
NotificationBell(sibling(button_1, 2), {});
|
||
reset(div_1);
|
||
var node_1 = sibling(div_1, 2);
|
||
var consequent = ($$anchor) => {
|
||
var button_2 = root();
|
||
delegated("click", button_2, () => app$1.sidebarOpen = false);
|
||
append($$anchor, button_2);
|
||
};
|
||
if_block(node_1, ($$render) => {
|
||
if (app$1.sidebarOpen) $$render(consequent);
|
||
});
|
||
var aside = sibling(node_1, 2);
|
||
let classes;
|
||
LocationSidebar(child(aside), {});
|
||
reset(aside);
|
||
var main = sibling(aside, 2);
|
||
var div_2 = child(main);
|
||
var div_3 = child(div_2);
|
||
var h1 = child(div_3);
|
||
var text_2 = child(h1, true);
|
||
reset(h1);
|
||
var node_3 = sibling(h1, 2);
|
||
var consequent_1 = ($$anchor) => {
|
||
append($$anchor, root_1());
|
||
};
|
||
if_block(node_3, ($$render) => {
|
||
if (app$1.selectedLocation?.isCurrent) $$render(consequent_1);
|
||
});
|
||
reset(div_3);
|
||
var div_4 = sibling(div_3, 2);
|
||
NotificationBell(child(div_4), {});
|
||
reset(div_4);
|
||
reset(div_2);
|
||
var node_5 = sibling(div_2, 2);
|
||
var consequent_2 = ($$anchor) => {
|
||
var div_5 = root_2();
|
||
var span_2 = child(div_5);
|
||
var text_3 = child(span_2);
|
||
reset(span_2);
|
||
var button_3 = sibling(span_2, 2);
|
||
reset(div_5);
|
||
template_effect(() => set_text(text_3, `⚠️ ${app$1.error ?? ""}`));
|
||
delegated("click", button_3, () => app$1.error = "");
|
||
append($$anchor, div_5);
|
||
};
|
||
if_block(node_5, ($$render) => {
|
||
if (app$1.error) $$render(consequent_2);
|
||
});
|
||
var node_6 = sibling(node_5, 2);
|
||
var consequent_3 = ($$anchor) => {
|
||
var div_6 = root_3();
|
||
var p = sibling(child(div_6), 4);
|
||
var text_4 = child(p, true);
|
||
reset(p);
|
||
var button_4 = sibling(p, 2);
|
||
reset(div_6);
|
||
template_effect(() => set_text(text_4, get(geoError)));
|
||
delegated("click", button_4, () => app$1.showAddLocation = true);
|
||
append($$anchor, div_6);
|
||
};
|
||
if_block(node_6, ($$render) => {
|
||
if (get(geoError) && !app$1.forecastData) $$render(consequent_3);
|
||
});
|
||
var node_7 = sibling(node_6, 2);
|
||
var consequent_4 = ($$anchor) => {
|
||
append($$anchor, root_4());
|
||
};
|
||
if_block(node_7, ($$render) => {
|
||
if (app$1.loading && !app$1.forecastData) $$render(consequent_4);
|
||
});
|
||
var node_8 = sibling(node_7, 2);
|
||
var consequent_10 = ($$anchor) => {
|
||
var fragment = root_6();
|
||
var div_8 = first_child(fragment);
|
||
var button_5 = child(div_8);
|
||
let classes_1;
|
||
var button_6 = sibling(button_5, 2);
|
||
let classes_2;
|
||
var button_7 = sibling(button_6, 2);
|
||
let classes_3;
|
||
var button_8 = sibling(button_7, 2);
|
||
let classes_4;
|
||
var button_9 = sibling(button_8, 2);
|
||
let classes_5;
|
||
reset(div_8);
|
||
var div_9 = sibling(div_8, 2);
|
||
var button_10 = child(div_9);
|
||
let classes_6;
|
||
var button_11 = sibling(button_10, 2);
|
||
let classes_7;
|
||
var button_12 = sibling(button_11, 2);
|
||
let classes_8;
|
||
var button_13 = sibling(button_12, 2);
|
||
let classes_9;
|
||
var button_14 = sibling(button_13, 2);
|
||
let classes_10;
|
||
reset(div_9);
|
||
var div_10 = sibling(div_9, 2);
|
||
var node_9 = child(div_10);
|
||
var consequent_5 = ($$anchor) => {
|
||
var div_11 = root_5();
|
||
CurrentWeather(child(div_11), {});
|
||
reset(div_11);
|
||
append($$anchor, div_11);
|
||
};
|
||
var consequent_6 = ($$anchor) => {
|
||
var div_12 = root_5();
|
||
HourlyForecast(child(div_12), {});
|
||
reset(div_12);
|
||
append($$anchor, div_12);
|
||
};
|
||
var consequent_7 = ($$anchor) => {
|
||
var div_13 = root_5();
|
||
DailyForecast(child(div_13), {});
|
||
reset(div_13);
|
||
append($$anchor, div_13);
|
||
};
|
||
var consequent_8 = ($$anchor) => {
|
||
var div_14 = root_5();
|
||
WeatherDetail(child(div_14), {});
|
||
reset(div_14);
|
||
append($$anchor, div_14);
|
||
};
|
||
var consequent_9 = ($$anchor) => {
|
||
var div_15 = root_5();
|
||
PrecipitationRadar(child(div_15), {});
|
||
reset(div_15);
|
||
append($$anchor, div_15);
|
||
};
|
||
if_block(node_9, ($$render) => {
|
||
if (get(viewMode) === "current") $$render(consequent_5);
|
||
else if (get(viewMode) === "hourly") $$render(consequent_6, 1);
|
||
else if (get(viewMode) === "daily") $$render(consequent_7, 2);
|
||
else if (get(viewMode) === "detail") $$render(consequent_8, 3);
|
||
else if (get(viewMode) === "radar") $$render(consequent_9, 4);
|
||
});
|
||
reset(div_10);
|
||
template_effect(() => {
|
||
classes_1 = set_class(button_5, 1, "nav-btn svelte-1n46o8q", null, classes_1, { active: get(viewMode) === "current" });
|
||
classes_2 = set_class(button_6, 1, "nav-btn svelte-1n46o8q", null, classes_2, { active: get(viewMode) === "hourly" });
|
||
classes_3 = set_class(button_7, 1, "nav-btn svelte-1n46o8q", null, classes_3, { active: get(viewMode) === "daily" });
|
||
classes_4 = set_class(button_8, 1, "nav-btn svelte-1n46o8q", null, classes_4, { active: get(viewMode) === "detail" });
|
||
classes_5 = set_class(button_9, 1, "nav-btn svelte-1n46o8q", null, classes_5, { active: get(viewMode) === "radar" });
|
||
classes_6 = set_class(button_10, 1, "tab-btn svelte-1n46o8q", null, classes_6, { active: get(viewMode) === "current" });
|
||
classes_7 = set_class(button_11, 1, "tab-btn svelte-1n46o8q", null, classes_7, { active: get(viewMode) === "hourly" });
|
||
classes_8 = set_class(button_12, 1, "tab-btn svelte-1n46o8q", null, classes_8, { active: get(viewMode) === "daily" });
|
||
classes_9 = set_class(button_13, 1, "tab-btn svelte-1n46o8q", null, classes_9, { active: get(viewMode) === "detail" });
|
||
classes_10 = set_class(button_14, 1, "tab-btn svelte-1n46o8q", null, classes_10, { active: get(viewMode) === "radar" });
|
||
});
|
||
delegated("click", button_5, () => set(viewMode, "current"));
|
||
delegated("click", button_6, () => set(viewMode, "hourly"));
|
||
delegated("click", button_7, () => set(viewMode, "daily"));
|
||
delegated("click", button_8, () => set(viewMode, "detail"));
|
||
delegated("click", button_9, () => set(viewMode, "radar"));
|
||
delegated("click", button_10, () => set(viewMode, "current"));
|
||
delegated("click", button_11, () => set(viewMode, "hourly"));
|
||
delegated("click", button_12, () => set(viewMode, "daily"));
|
||
delegated("click", button_13, () => set(viewMode, "detail"));
|
||
delegated("click", button_14, () => set(viewMode, "radar"));
|
||
append($$anchor, fragment);
|
||
};
|
||
if_block(node_8, ($$render) => {
|
||
if (app$1.forecastData && app$1.selectedLocation) $$render(consequent_10);
|
||
});
|
||
reset(main);
|
||
var node_15 = sibling(main, 2);
|
||
var consequent_11 = ($$anchor) => {
|
||
SettingsDialog($$anchor, { onClose: () => app$1.showSettings = false });
|
||
};
|
||
if_block(node_15, ($$render) => {
|
||
if (app$1.showSettings) $$render(consequent_11);
|
||
});
|
||
var node_16 = sibling(node_15, 2);
|
||
var consequent_12 = ($$anchor) => {
|
||
AddLocationDialog($$anchor, {
|
||
onClose: () => {
|
||
app$1.showAddLocation = false;
|
||
app$1.geocodingResults = [];
|
||
},
|
||
onAdd: handleAddLocation
|
||
});
|
||
};
|
||
if_block(node_16, ($$render) => {
|
||
if (app$1.showAddLocation) $$render(consequent_12);
|
||
});
|
||
reset(div);
|
||
template_effect(() => {
|
||
set_text(text, app$1.sidebarOpen ? "✕" : "☰");
|
||
set_text(text_1, app$1.selectedLocation?.name || "WeatherLens");
|
||
classes = set_class(aside, 1, "sidebar svelte-1n46o8q", null, classes, { open: app$1.sidebarOpen });
|
||
set_text(text_2, app$1.selectedLocation?.name || "WeatherLens");
|
||
});
|
||
delegated("click", button, () => app$1.sidebarOpen = !app$1.sidebarOpen);
|
||
delegated("click", button_1, () => app$1.showSettings = true);
|
||
append($$anchor, div);
|
||
pop();
|
||
}
|
||
delegate(["click"]);
|
||
mount(App, { target: document.getElementById("app") });
|
||
//#endregion</script>
|
||
<style rel="stylesheet" crossorigin>/* ===== CSS Reset & Base ===== */
|
||
*, *::before, *::after {
|
||
box-sizing: border-box;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
:root {
|
||
--color-bg: #0a1628;
|
||
--color-bg-alt: #0d1f3c;
|
||
--color-surface: #132744;
|
||
--color-surface-hover: #1a3359;
|
||
--color-surface-card: #183056;
|
||
--color-border: #1e3a66;
|
||
--color-border-light: #264a7a;
|
||
--color-text: #e8eef5;
|
||
--color-text-secondary: #c0cde0;
|
||
--color-text-muted: #7b8eb5;
|
||
|
||
/* Weather colors */
|
||
--color-sun: #fbbf24;
|
||
--color-sun-glow: rgba(251, 191, 36, 0.15);
|
||
--color-sky: #38bdf8;
|
||
--color-sky-glow: rgba(56, 189, 248, 0.12);
|
||
--color-rain: #60a5fa;
|
||
--color-rain-glow: rgba(96, 165, 250, 0.12);
|
||
--color-cloud: #94a3b8;
|
||
--color-storm: #818cf8;
|
||
--color-storm-glow: rgba(129, 140, 248, 0.15);
|
||
--color-snow: #e2e8f0;
|
||
--color-danger: #ef4444;
|
||
--color-danger-glow: rgba(239, 68, 68, 0.12);
|
||
--color-warning: #f59e0b;
|
||
--color-warning-glow: rgba(245, 158, 11, 0.12);
|
||
--color-success: #34d399;
|
||
--color-success-glow: rgba(52, 211, 153, 0.12);
|
||
|
||
--color-primary: #38bdf8;
|
||
--color-primary-hover: #0ea5e9;
|
||
--color-accent: #fbbf24;
|
||
|
||
--radius-sm: 6px;
|
||
--radius-md: 10px;
|
||
--radius-lg: 16px;
|
||
--radius-xl: 24px;
|
||
--shadow-sm: 0 1px 4px rgba(0, 0, 0, 0.2);
|
||
--shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
|
||
--shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.4);
|
||
--transition: 180ms ease;
|
||
|
||
--safe-bottom: env(safe-area-inset-bottom, 0px);
|
||
--safe-top: env(safe-area-inset-top, 0px);
|
||
}
|
||
|
||
html {
|
||
font-size: 16px;
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||
background-color: var(--color-bg);
|
||
color: var(--color-text);
|
||
line-height: 1.5;
|
||
min-height: 100vh;
|
||
overscroll-behavior: none;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
#app {
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* ===== Typography ===== */
|
||
h1 { font-size: 1.5rem; font-weight: 700; letter-spacing: -0.02em; }
|
||
h2 { font-size: 1.25rem; font-weight: 600; }
|
||
h3 { font-size: 1.05rem; font-weight: 600; }
|
||
h4 { font-size: 0.95rem; font-weight: 600; }
|
||
|
||
/* ===== Buttons ===== */
|
||
.btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6px;
|
||
padding: 10px 18px;
|
||
border: none;
|
||
border-radius: var(--radius-md);
|
||
font-size: 0.9rem;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: background-color var(--transition), opacity var(--transition), transform var(--transition);
|
||
text-decoration: none;
|
||
white-space: nowrap;
|
||
user-select: none;
|
||
-webkit-user-select: none;
|
||
touch-action: manipulation;
|
||
}
|
||
|
||
.btn:active {
|
||
transform: scale(0.97);
|
||
}
|
||
|
||
.btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-hover));
|
||
color: #fff;
|
||
box-shadow: 0 2px 8px rgba(56, 189, 248, 0.3);
|
||
}
|
||
|
||
.btn-primary:hover:not(:disabled) {
|
||
background: linear-gradient(135deg, var(--color-primary-hover), #0284c7);
|
||
}
|
||
|
||
.btn-danger {
|
||
background-color: var(--color-danger);
|
||
color: #fff;
|
||
}
|
||
|
||
.btn-danger:hover:not(:disabled) {
|
||
background-color: #dc2626;
|
||
}
|
||
|
||
.btn-ghost {
|
||
background: transparent;
|
||
color: var(--color-text-secondary);
|
||
border: 1px solid var(--color-border);
|
||
}
|
||
|
||
.btn-ghost:hover:not(:disabled) {
|
||
background-color: var(--color-surface-hover);
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.btn-sm {
|
||
padding: 6px 12px;
|
||
font-size: 0.8rem;
|
||
border-radius: var(--radius-sm);
|
||
}
|
||
|
||
.btn-icon {
|
||
padding: 8px;
|
||
border-radius: var(--radius-sm);
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--color-text-muted);
|
||
cursor: pointer;
|
||
font-size: 1.1rem;
|
||
transition: color var(--transition), background-color var(--transition);
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 36px;
|
||
min-height: 36px;
|
||
}
|
||
|
||
.btn-icon:hover {
|
||
background-color: var(--color-surface-hover);
|
||
color: var(--color-text);
|
||
}
|
||
|
||
/* ===== Inputs ===== */
|
||
input[type="text"],
|
||
input[type="search"],
|
||
input[type="number"],
|
||
select {
|
||
width: 100%;
|
||
padding: 10px 14px;
|
||
background-color: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
color: var(--color-text);
|
||
font-size: 0.9rem;
|
||
font-family: inherit;
|
||
transition: border-color var(--transition), box-shadow var(--transition);
|
||
outline: none;
|
||
}
|
||
|
||
input:focus,
|
||
select:focus {
|
||
border-color: var(--color-primary);
|
||
box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.15);
|
||
}
|
||
|
||
input::placeholder {
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
/* ===== Cards ===== */
|
||
.card {
|
||
background: var(--color-surface-card);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
padding: 16px;
|
||
box-shadow: var(--shadow-sm);
|
||
}
|
||
|
||
.card-glass {
|
||
background: linear-gradient(135deg, var(--color-surface), var(--color-surface-card));
|
||
border: 1px solid var(--color-border-light);
|
||
border-radius: var(--radius-lg);
|
||
padding: 16px;
|
||
backdrop-filter: blur(8px);
|
||
-webkit-backdrop-filter: blur(8px);
|
||
}
|
||
|
||
/* ===== Scrollbar ===== */
|
||
::-webkit-scrollbar {
|
||
width: 4px;
|
||
height: 4px;
|
||
}
|
||
|
||
::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb {
|
||
background: var(--color-border);
|
||
border-radius: 2px;
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb:hover {
|
||
background: var(--color-border-light);
|
||
}
|
||
|
||
/* ===== Utility ===== */
|
||
.text-muted { color: var(--color-text-muted); }
|
||
.text-secondary { color: var(--color-text-secondary); }
|
||
.text-sm { font-size: 0.85rem; }
|
||
.text-xs { font-size: 0.75rem; }
|
||
.text-center { text-align: center; }
|
||
.mt-1 { margin-top: 4px; }
|
||
.mt-2 { margin-top: 8px; }
|
||
.mt-3 { margin-top: 12px; }
|
||
.mt-4 { margin-top: 16px; }
|
||
.mb-2 { margin-bottom: 8px; }
|
||
.mb-3 { margin-bottom: 12px; }
|
||
.mb-4 { margin-bottom: 16px; }
|
||
.flex { display: flex; }
|
||
.flex-col { flex-direction: column; }
|
||
.items-center { align-items: center; }
|
||
.justify-between { justify-content: space-between; }
|
||
.gap-1 { gap: 4px; }
|
||
.gap-2 { gap: 8px; }
|
||
.gap-3 { gap: 12px; }
|
||
.gap-4 { gap: 16px; }
|
||
.w-full { width: 100%; }
|
||
.flex-1 { flex: 1; }
|
||
.truncate {
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* ===== Animations ===== */
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; transform: translateY(8px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
@keyframes fadeInScale {
|
||
from { opacity: 0; transform: scale(0.95); }
|
||
to { opacity: 1; transform: scale(1); }
|
||
}
|
||
|
||
@keyframes slideDown {
|
||
from { opacity: 0; transform: translateY(-12px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.6; }
|
||
}
|
||
|
||
@keyframes spin {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
.animate-fade-in {
|
||
animation: fadeIn 0.3s ease-out;
|
||
}
|
||
|
||
.animate-slide-down {
|
||
animation: slideDown 0.3s ease-out;
|
||
}
|
||
|
||
.animate-pulse {
|
||
animation: pulse 2s ease-in-out infinite;
|
||
}
|
||
|
||
.sidebar-content.svelte-1j1qnn9 {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
}
|
||
|
||
.sidebar-header.svelte-1j1qnn9 {
|
||
padding: 16px;
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.sidebar-header.svelte-1j1qnn9 h2:where(.svelte-1j1qnn9) {
|
||
font-size: 1rem;
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* Search box */
|
||
.search-box.svelte-1j1qnn9 {
|
||
padding: 12px 12px 0;
|
||
position: relative;
|
||
}
|
||
|
||
.search-box.svelte-1j1qnn9 input:where(.svelte-1j1qnn9) {
|
||
padding: 8px 12px;
|
||
font-size: 0.85rem;
|
||
border-radius: var(--radius-md);
|
||
}
|
||
|
||
.search-spinner.svelte-1j1qnn9 {
|
||
position: absolute;
|
||
right: 22px;
|
||
top: 20px;
|
||
font-size: 0.85rem;
|
||
animation: svelte-1j1qnn9-spin 1s linear infinite;
|
||
pointer-events: none;
|
||
}
|
||
|
||
@keyframes svelte-1j1qnn9-spin {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
.geocoding-dropdown.svelte-1j1qnn9 {
|
||
position: absolute;
|
||
left: 12px;
|
||
right: 12px;
|
||
top: calc(100% - 4px);
|
||
background: var(--color-surface-card);
|
||
border: 1px solid var(--color-border-light);
|
||
border-radius: var(--radius-md);
|
||
box-shadow: var(--shadow-lg);
|
||
z-index: 70;
|
||
max-height: 260px;
|
||
overflow-y: auto;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.geocoding-item.svelte-1j1qnn9 {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
width: 100%;
|
||
padding: 10px 14px;
|
||
border: none;
|
||
background: transparent;
|
||
color: var(--color-text);
|
||
text-align: left;
|
||
cursor: pointer;
|
||
transition: background-color var(--transition);
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.geocoding-item.svelte-1j1qnn9:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.geocoding-item.svelte-1j1qnn9:hover {
|
||
background: var(--color-surface-hover);
|
||
}
|
||
|
||
.geo-name.svelte-1j1qnn9 {
|
||
font-size: 0.85rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.geo-detail.svelte-1j1qnn9 {
|
||
font-size: 0.75rem;
|
||
color: var(--color-text-muted);
|
||
margin-top: 2px;
|
||
}
|
||
|
||
/* Locations nav */
|
||
.locations-nav.svelte-1j1qnn9 {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 12px 8px;
|
||
}
|
||
|
||
.location-row.svelte-1j1qnn9 {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
.current-row.svelte-1j1qnn9 {
|
||
border-bottom: 1px solid var(--color-border);
|
||
padding-bottom: 8px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.location-item.svelte-1j1qnn9 {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
flex: 1;
|
||
min-width: 0;
|
||
padding: 9px 12px;
|
||
border: none;
|
||
border-radius: var(--radius-md);
|
||
background: transparent;
|
||
color: var(--color-text-secondary);
|
||
font-size: 0.88rem;
|
||
cursor: pointer;
|
||
transition: background-color var(--transition), color var(--transition);
|
||
text-align: left;
|
||
}
|
||
|
||
.location-item.svelte-1j1qnn9:hover {
|
||
background: var(--color-surface-hover);
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.location-item.active.svelte-1j1qnn9 {
|
||
background: rgba(56, 189, 248, 0.12);
|
||
color: var(--color-primary);
|
||
}
|
||
|
||
.loc-icon.svelte-1j1qnn9 {
|
||
font-size: 0.95rem;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.loc-name.svelte-1j1qnn9 {
|
||
flex: 1;
|
||
}
|
||
|
||
.loc-actions.svelte-1j1qnn9 {
|
||
display: flex;
|
||
gap: 2px;
|
||
padding-right: 4px;
|
||
opacity: 0;
|
||
transition: opacity 150ms;
|
||
}
|
||
|
||
.location-row.svelte-1j1qnn9:hover .loc-actions:where(.svelte-1j1qnn9) {
|
||
opacity: 1;
|
||
}
|
||
|
||
.loc-action-btn.svelte-1j1qnn9 {
|
||
background: none;
|
||
border: none;
|
||
cursor: pointer;
|
||
font-size: 0.75rem;
|
||
padding: 4px;
|
||
border-radius: var(--radius-sm);
|
||
transition: background-color 150ms;
|
||
}
|
||
|
||
.loc-action-btn.svelte-1j1qnn9:hover {
|
||
background: var(--color-surface-hover);
|
||
}
|
||
|
||
/* Empty state */
|
||
.empty-locations.svelte-1j1qnn9 {
|
||
padding: 24px 12px;
|
||
text-align: center;
|
||
}
|
||
|
||
/* Footer */
|
||
.sidebar-footer.svelte-1j1qnn9 {
|
||
padding: 12px;
|
||
border-top: 1px solid var(--color-border);
|
||
}
|
||
|
||
.srctip.svelte-1hhb1ss {
|
||
position: relative;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 17px;
|
||
height: 17px;
|
||
margin-left: 6px;
|
||
vertical-align: middle;
|
||
cursor: help;
|
||
flex: none;
|
||
color: var(--color-primary, #38bdf8);
|
||
}
|
||
.srctip-icon.svelte-1hhb1ss {
|
||
font-size: 0.98rem;
|
||
line-height: 1;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.srctip-bubble.svelte-1hhb1ss {
|
||
position: absolute;
|
||
bottom: calc(100% + 7px);
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: max-content;
|
||
max-width: 210px;
|
||
padding: 7px 10px;
|
||
border-radius: 7px;
|
||
background: #0b1220;
|
||
color: #e2e8f0;
|
||
font-size: 0.74rem;
|
||
font-weight: 500;
|
||
line-height: 1.4;
|
||
white-space: normal;
|
||
text-align: left;
|
||
box-shadow: 0 5px 16px rgba(0, 0, 0, 0.4);
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
pointer-events: none;
|
||
transition: opacity 0.12s ease, visibility 0.12s ease;
|
||
z-index: 60;
|
||
}
|
||
/* small arrow */
|
||
.srctip-bubble.svelte-1hhb1ss::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 100%;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
border: 5px solid transparent;
|
||
border-top-color: #0b1220;
|
||
}
|
||
.srctip.svelte-1hhb1ss:hover .srctip-bubble:where(.svelte-1hhb1ss),
|
||
.srctip.svelte-1hhb1ss:focus .srctip-bubble:where(.svelte-1hhb1ss),
|
||
.srctip.svelte-1hhb1ss:focus-visible .srctip-bubble:where(.svelte-1hhb1ss) {
|
||
opacity: 1;
|
||
visibility: visible;
|
||
}
|
||
.srctip.svelte-1hhb1ss:focus,
|
||
.srctip.svelte-1hhb1ss:focus-visible {
|
||
outline: 2px solid var(--color-primary, #38bdf8);
|
||
outline-offset: 2px;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.current-hero.svelte-1k8dsh {
|
||
border-radius: var(--radius-xl);
|
||
padding: 24px;
|
||
box-shadow: var(--shadow);
|
||
color: var(--color-text);
|
||
transition: background 0.5s ease;
|
||
}
|
||
|
||
/* Weather background gradients */
|
||
.weather-bg-clear.svelte-1k8dsh {
|
||
background: linear-gradient(135deg, #1a3a5c 0%, #2d6aa8 40%, #4a90d9 100%);
|
||
}
|
||
.weather-bg-cloudy.svelte-1k8dsh {
|
||
background: linear-gradient(135deg, #1e293b 0%, #334155 50%, #475569 100%);
|
||
}
|
||
.weather-bg-rain.svelte-1k8dsh {
|
||
background: linear-gradient(135deg, #17202e 0%, #253649 40%, #3b4f6b 100%);
|
||
}
|
||
.weather-bg-storm.svelte-1k8dsh {
|
||
background: linear-gradient(135deg, #120e1e 0%, #2d1f4e 40%, #4a3178 100%);
|
||
}
|
||
.weather-bg-snow.svelte-1k8dsh {
|
||
background: linear-gradient(135deg, #1c2838 0%, #2c3e58 50%, #4a6fa5 100%);
|
||
}
|
||
.weather-bg-fog.svelte-1k8dsh {
|
||
background: linear-gradient(135deg, #1e2430 0%, #2d3545 50%, #3d4757 100%);
|
||
}
|
||
|
||
.hero-main.svelte-1k8dsh {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.hero-temp.svelte-1k8dsh {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.temp-value.svelte-1k8dsh {
|
||
font-size: 4rem;
|
||
font-weight: 800;
|
||
line-height: 1;
|
||
letter-spacing: -0.03em;
|
||
}
|
||
|
||
.temp-unit.svelte-1k8dsh {
|
||
font-size: 1.4rem;
|
||
font-weight: 500;
|
||
color: var(--color-text-secondary);
|
||
margin-top: 8px;
|
||
margin-left: 2px;
|
||
}
|
||
|
||
.hero-icon.svelte-1k8dsh {
|
||
font-size: 4rem;
|
||
filter: drop-shadow(0 0 12px rgba(255, 255, 255, 0.1));
|
||
}
|
||
|
||
.hero-desc.svelte-1k8dsh {
|
||
font-size: 1.05rem;
|
||
color: var(--color-text-secondary);
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.feels-like.svelte-1k8dsh {
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
/* Today's high / low range */
|
||
.hero-range.svelte-1k8dsh {
|
||
display: flex;
|
||
gap: 14px;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
font-size: 0.9rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.range-high.svelte-1k8dsh {
|
||
color: #fca5a5;
|
||
}
|
||
|
||
.range-low.svelte-1k8dsh {
|
||
color: #7dd3fc;
|
||
}
|
||
|
||
/* Metrics grid */
|
||
.metrics-grid.svelte-1k8dsh {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
||
gap: 10px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.metric-card.svelte-1k8dsh {
|
||
background: rgba(0, 0, 0, 0.2);
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
border-radius: var(--radius-md);
|
||
padding: 12px;
|
||
text-align: center;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.metric-icon.svelte-1k8dsh {
|
||
font-size: 1.2rem;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.metric-label.svelte-1k8dsh {
|
||
font-size: 0.7rem;
|
||
color: var(--color-text-muted);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
}
|
||
|
||
.metric-value.svelte-1k8dsh {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.metric-sub.svelte-1k8dsh {
|
||
font-size: 0.7rem;
|
||
color: var(--color-text-muted);
|
||
margin-top: 1px;
|
||
}
|
||
|
||
/* Sunrise / Sunset */
|
||
.sun-times.svelte-1k8dsh {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 16px;
|
||
padding: 12px 16px;
|
||
background: rgba(0, 0, 0, 0.2);
|
||
border-radius: var(--radius-md);
|
||
}
|
||
|
||
.sun-time.svelte-1k8dsh {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 0.88rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.sun-icon.svelte-1k8dsh {
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.sun-divider.svelte-1k8dsh {
|
||
width: 1px;
|
||
height: 20px;
|
||
background: rgba(255, 255, 255, 0.15);
|
||
}
|
||
|
||
@media (max-width: 1023px) {
|
||
.current-hero.svelte-1k8dsh {
|
||
padding: 16px;
|
||
border-radius: var(--radius-lg);
|
||
}
|
||
|
||
.temp-value.svelte-1k8dsh {
|
||
font-size: 3.2rem;
|
||
}
|
||
|
||
.hero-icon.svelte-1k8dsh {
|
||
font-size: 3rem;
|
||
}
|
||
|
||
.metrics-grid.svelte-1k8dsh {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 8px;
|
||
}
|
||
|
||
.metric-card.svelte-1k8dsh {
|
||
padding: 10px 8px;
|
||
}
|
||
|
||
.sun-times.svelte-1k8dsh {
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.sun-divider.svelte-1k8dsh {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
.hourly-section.svelte-1q03bs0 {
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.section-title.svelte-1q03bs0 {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
margin-bottom: 12px;
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.hourly-scroll.svelte-1q03bs0 {
|
||
display: flex;
|
||
gap: 4px;
|
||
overflow-x: auto;
|
||
padding: 4px 0;
|
||
-webkit-overflow-scrolling: touch;
|
||
scroll-snap-type: x proximity;
|
||
}
|
||
|
||
.hour-item.svelte-1q03bs0 {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 6px;
|
||
min-width: 72px;
|
||
padding: 12px 8px;
|
||
background: var(--color-surface-card);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
scroll-snap-align: start;
|
||
transition: background-color var(--transition);
|
||
}
|
||
|
||
.hour-item.now.svelte-1q03bs0 {
|
||
background: rgba(56, 189, 248, 0.1);
|
||
border-color: var(--color-primary);
|
||
box-shadow: 0 0 12px rgba(56, 189, 248, 0.1);
|
||
}
|
||
|
||
.hour-time.svelte-1q03bs0 {
|
||
font-size: 0.72rem;
|
||
color: var(--color-text-muted);
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.hour-item.now.svelte-1q03bs0 .hour-time:where(.svelte-1q03bs0) {
|
||
color: var(--color-primary);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.hour-icon.svelte-1q03bs0 {
|
||
font-size: 1.4rem;
|
||
}
|
||
|
||
.hour-temp.svelte-1q03bs0 {
|
||
font-size: 0.9rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.precip-bar-container.svelte-1q03bs0 {
|
||
width: 4px;
|
||
height: 30px;
|
||
background: rgba(96, 165, 250, 0.15);
|
||
border-radius: 2px;
|
||
overflow: hidden;
|
||
display: flex;
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.precip-bar.svelte-1q03bs0 {
|
||
width: 100%;
|
||
height: var(--precip);
|
||
background: var(--color-rain);
|
||
border-radius: 2px;
|
||
transition: height 0.3s ease, width 0.3s ease;
|
||
min-height: 0;
|
||
}
|
||
|
||
.precip-value.svelte-1q03bs0 {
|
||
font-size: 0.68rem;
|
||
color: var(--color-text-muted);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.hour-wind.svelte-1q03bs0 {
|
||
font-size: 0.68rem;
|
||
color: var(--color-text-muted);
|
||
white-space: nowrap;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
@media (max-width: 1023px) {
|
||
.hourly-scroll.svelte-1q03bs0 {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
overflow-x: visible;
|
||
scroll-snap-type: none;
|
||
}
|
||
|
||
.hour-item.svelte-1q03bs0 {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
min-width: 0;
|
||
padding: 10px 12px;
|
||
gap: 8px;
|
||
scroll-snap-align: none;
|
||
}
|
||
|
||
.hour-time.svelte-1q03bs0 {
|
||
font-size: 0.78rem;
|
||
min-width: 42px;
|
||
}
|
||
|
||
.hour-icon.svelte-1q03bs0 {
|
||
font-size: 1.3rem;
|
||
}
|
||
|
||
.hour-temp.svelte-1q03bs0 {
|
||
font-size: 0.85rem;
|
||
min-width: 40px;
|
||
text-align: right;
|
||
}
|
||
|
||
.precip-bar-container.svelte-1q03bs0 {
|
||
width: 50px;
|
||
height: 4px;
|
||
}
|
||
|
||
.precip-bar.svelte-1q03bs0 {
|
||
height: 100%;
|
||
width: var(--precip);
|
||
min-width: 0;
|
||
}
|
||
|
||
.precip-value.svelte-1q03bs0 {
|
||
font-size: 0.7rem;
|
||
min-width: 28px;
|
||
text-align: right;
|
||
}
|
||
|
||
.hour-wind.svelte-1q03bs0 {
|
||
font-size: 0.7rem;
|
||
color: var(--color-text-muted);
|
||
white-space: nowrap;
|
||
min-width: 48px;
|
||
text-align: right;
|
||
}
|
||
}
|
||
|
||
.daily-section.svelte-ss3yj8 {
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.section-title.svelte-ss3yj8 {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
margin-bottom: 12px;
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.daily-list.svelte-ss3yj8 {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.day-card.svelte-ss3yj8 {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 14px 16px;
|
||
transition: background var(--transition);
|
||
}
|
||
|
||
.day-card.today.svelte-ss3yj8 {
|
||
border-color: var(--color-primary);
|
||
box-shadow: 0 0 16px rgba(56, 189, 248, 0.08);
|
||
}
|
||
|
||
.day-left.svelte-ss3yj8 {
|
||
min-width: 100px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.day-name.svelte-ss3yj8 {
|
||
font-weight: 600;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.day-date.svelte-ss3yj8 {
|
||
margin-top: 1px;
|
||
}
|
||
|
||
.day-center.svelte-ss3yj8 {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
min-width: 0;
|
||
}
|
||
|
||
.day-icon.svelte-ss3yj8 {
|
||
font-size: 1.6rem;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.day-desc.svelte-ss3yj8 {
|
||
font-size: 0.82rem;
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.day-right.svelte-ss3yj8 {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
gap: 4px;
|
||
min-width: 0;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.day-temps.svelte-ss3yj8 {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 4px;
|
||
}
|
||
|
||
.temp-high.svelte-ss3yj8 {
|
||
font-weight: 700;
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.temp-low.svelte-ss3yj8 {
|
||
color: var(--color-text-muted);
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
.precip-row.svelte-ss3yj8 {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.precip-icon.svelte-ss3yj8 {
|
||
font-size: 0.7rem;
|
||
}
|
||
|
||
.precip-bar-wrap.svelte-ss3yj8 {
|
||
width: 40px;
|
||
height: 4px;
|
||
background: rgba(96, 165, 250, 0.15);
|
||
border-radius: 2px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.precip-fill.svelte-ss3yj8 {
|
||
height: 100%;
|
||
background: var(--color-rain);
|
||
border-radius: 2px;
|
||
transition: width 0.3s ease;
|
||
}
|
||
|
||
.precip-label.svelte-ss3yj8 {
|
||
font-size: 0.72rem;
|
||
color: var(--color-text-muted);
|
||
min-width: 24px;
|
||
}
|
||
|
||
.wind-info.svelte-ss3yj8 {
|
||
font-size: 0.72rem;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
@media (max-width: 1023px) {
|
||
.day-card.svelte-ss3yj8 {
|
||
flex-wrap: wrap;
|
||
padding: 12px 14px;
|
||
gap: 8px;
|
||
}
|
||
|
||
.day-left.svelte-ss3yj8 {
|
||
min-width: 80px;
|
||
}
|
||
|
||
.day-center.svelte-ss3yj8 {
|
||
flex: 2;
|
||
}
|
||
|
||
.day-right.svelte-ss3yj8 {
|
||
width: 100%;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.day-desc.svelte-ss3yj8 {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
.detail-section.svelte-8lxt71 {
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.section-title.svelte-8lxt71 {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
margin-bottom: 12px;
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.detail-grid.svelte-8lxt71 {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||
gap: 10px;
|
||
}
|
||
|
||
.detail-card.svelte-8lxt71 {
|
||
padding: 16px;
|
||
}
|
||
|
||
.detail-header.svelte-8lxt71 {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 12px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.detail-icon.svelte-8lxt71 {
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.detail-label.svelte-8lxt71 {
|
||
font-size: 0.85rem;
|
||
font-weight: 600;
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.detail-body.svelte-8lxt71 {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.detail-row.svelte-8lxt71 {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
font-size: 0.85rem;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.detail-value.svelte-8lxt71 {
|
||
font-weight: 500;
|
||
color: var(--color-text);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
|
||
.uv-badge.svelte-8lxt71 {
|
||
font-size: 0.7rem;
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.uv-badge.low.svelte-8lxt71 { background: rgba(52, 211, 153, 0.2); color: var(--color-success); }
|
||
.uv-badge.moderate.svelte-8lxt71 { background: rgba(251, 191, 36, 0.2); color: var(--color-sun); }
|
||
.uv-badge.high.svelte-8lxt71 { background: rgba(245, 158, 11, 0.2); color: var(--color-warning); }
|
||
.uv-badge.very-high.svelte-8lxt71 { background: rgba(239, 68, 68, 0.2); color: var(--color-danger); }
|
||
.uv-badge.extreme.svelte-8lxt71 { background: rgba(129, 140, 248, 0.25); color: var(--color-storm); }
|
||
|
||
/* Time info */
|
||
.time-info.svelte-8lxt71 {
|
||
padding: 14px 16px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.time-row.svelte-8lxt71 {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
font-size: 0.82rem;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
@media (max-width: 1023px) {
|
||
.detail-grid.svelte-8lxt71 {
|
||
grid-template-columns: 1fr;
|
||
gap: 8px;
|
||
}
|
||
|
||
.detail-card.svelte-8lxt71 {
|
||
padding: 14px;
|
||
}
|
||
}
|
||
|
||
.radar-section.svelte-tjvfz7 {
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.section-title.svelte-tjvfz7 {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
margin-bottom: 12px;
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.radar-title-row.svelte-tjvfz7 {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
}
|
||
|
||
.layer-toggle.svelte-tjvfz7 {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
padding: 5px 11px;
|
||
font-size: 0.78rem;
|
||
font-weight: 600;
|
||
border-radius: 16px;
|
||
border: 1px solid var(--color-border);
|
||
background: var(--color-surface);
|
||
color: var(--color-text-muted);
|
||
cursor: pointer;
|
||
margin-bottom: 12px;
|
||
transition: background var(--transition), color var(--transition), border-color var(--transition);
|
||
}
|
||
.layer-toggle.svelte-tjvfz7:hover {
|
||
border-color: var(--color-primary);
|
||
color: var(--color-text);
|
||
}
|
||
.layer-toggle.on.svelte-tjvfz7 {
|
||
background: rgba(250, 204, 21, 0.18);
|
||
border-color: #facc15;
|
||
color: #fde047;
|
||
}
|
||
|
||
.radar-status.svelte-tjvfz7,
|
||
.radar-status-err.svelte-tjvfz7 {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 48px 16px;
|
||
background: var(--color-surface-card);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
text-align: center;
|
||
}
|
||
|
||
.radar-status-err.svelte-tjvfz7 {
|
||
border-color: rgba(239, 68, 68, 0.3);
|
||
}
|
||
|
||
/* Loading overlay on top of map */
|
||
.radar-overlay.svelte-tjvfz7 {
|
||
position: absolute;
|
||
inset: 0;
|
||
background: rgba(10, 22, 40, 0.75);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
z-index: 10;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Error banner on top of map */
|
||
.radar-error-banner.svelte-tjvfz7 {
|
||
position: absolute;
|
||
top: 12px;
|
||
left: 12px;
|
||
right: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 8px 12px;
|
||
background: rgba(239, 68, 68, 0.15);
|
||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||
border-radius: var(--radius-sm);
|
||
z-index: 10;
|
||
gap: 8px;
|
||
}
|
||
|
||
.loading-spinner.svelte-tjvfz7 {
|
||
font-size: 2rem;
|
||
animation: svelte-tjvfz7-spin 1.5s linear infinite;
|
||
}
|
||
|
||
@keyframes svelte-tjvfz7-spin {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
/* ── Viewport ── */
|
||
.radar-viewport.svelte-tjvfz7 {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 420px;
|
||
overflow: hidden;
|
||
background: #aad3df;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
cursor: grab;
|
||
user-select: none;
|
||
-webkit-user-select: none;
|
||
}
|
||
|
||
.radar-viewport.dragging.svelte-tjvfz7 {
|
||
cursor: grabbing;
|
||
}
|
||
|
||
.radar-layer.svelte-tjvfz7 {
|
||
display: grid;
|
||
grid-template-columns: repeat(5, 256px);
|
||
grid-template-rows: repeat(5, 256px);
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
margin-left: -640px;
|
||
margin-top: -640px;
|
||
}
|
||
|
||
.radar-tile.svelte-tjvfz7 {
|
||
width: 256px;
|
||
height: 256px;
|
||
display: block;
|
||
pointer-events: none;
|
||
grid-area: auto;
|
||
}
|
||
|
||
.tile-base.svelte-tjvfz7 {
|
||
z-index: 0;
|
||
}
|
||
|
||
.tile-overlay.svelte-tjvfz7 {
|
||
z-index: 1;
|
||
}
|
||
|
||
/* ── Lightning strike layer ── */
|
||
.lightning-layer.svelte-tjvfz7 {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 2;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.lightning-strike.svelte-tjvfz7 {
|
||
position: absolute;
|
||
width: 8px;
|
||
height: 8px;
|
||
margin-left: -4px;
|
||
margin-top: -4px;
|
||
border-radius: 50%;
|
||
background: #facc15;
|
||
box-shadow: 0 0 6px 2px rgba(250, 204, 21, 0.85);
|
||
pointer-events: auto;
|
||
}
|
||
|
||
.lightning-status.svelte-tjvfz7 {
|
||
position: absolute;
|
||
top: 12px;
|
||
right: 12px;
|
||
z-index: 10;
|
||
padding: 5px 10px;
|
||
background: rgba(10, 22, 40, 0.8);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-sm);
|
||
pointer-events: none;
|
||
}
|
||
.lightning-status-err.svelte-tjvfz7 {
|
||
border-color: rgba(239, 68, 68, 0.4);
|
||
}
|
||
|
||
/* ── Location marker ── */
|
||
.location-marker.svelte-tjvfz7 {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
pointer-events: none;
|
||
z-index: 5;
|
||
}
|
||
|
||
.marker-dot.svelte-tjvfz7 {
|
||
display: block;
|
||
width: 14px;
|
||
height: 14px;
|
||
background: var(--color-primary);
|
||
border: 3px solid #fff;
|
||
border-radius: 50%;
|
||
box-shadow: 0 0 12px rgba(56, 189, 248, 0.7);
|
||
animation: svelte-tjvfz7-marker-pulse 2s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes svelte-tjvfz7-marker-pulse {
|
||
0%, 100% { box-shadow: 0 0 8px rgba(56, 189, 248, 0.4); }
|
||
50% { box-shadow: 0 0 20px rgba(56, 189, 248, 0.9); }
|
||
}
|
||
|
||
/* ── Zoom controls ── */
|
||
.zoom-ctrl.svelte-tjvfz7 {
|
||
position: absolute;
|
||
bottom: 12px;
|
||
right: 12px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 2px;
|
||
z-index: 6;
|
||
}
|
||
|
||
.zoom-btn.svelte-tjvfz7 {
|
||
width: 34px;
|
||
height: 34px;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-sm);
|
||
background: var(--color-surface);
|
||
color: var(--color-text);
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: background var(--transition);
|
||
}
|
||
|
||
.zoom-btn.svelte-tjvfz7:hover:not(:disabled) {
|
||
background: var(--color-surface-hover);
|
||
}
|
||
|
||
.zoom-btn.svelte-tjvfz7:disabled {
|
||
opacity: 0.4;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.zoom-level.svelte-tjvfz7 {
|
||
padding: 2px 4px;
|
||
}
|
||
|
||
/* ── Timeline ── */
|
||
.timeline.svelte-tjvfz7 {
|
||
margin-top: 12px;
|
||
padding: 12px 16px;
|
||
background: var(--color-surface-card);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.tl-controls.svelte-tjvfz7 {
|
||
display: flex;
|
||
gap: 4px;
|
||
align-items: center;
|
||
}
|
||
|
||
.ctrl-btn.svelte-tjvfz7 {
|
||
width: 34px;
|
||
height: 34px;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-sm);
|
||
background: var(--color-surface);
|
||
color: var(--color-text);
|
||
font-size: 0.85rem;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: background var(--transition);
|
||
}
|
||
|
||
.ctrl-btn.svelte-tjvfz7:hover {
|
||
background: var(--color-surface-hover);
|
||
}
|
||
|
||
.play-btn.svelte-tjvfz7 {
|
||
width: 38px;
|
||
height: 38px;
|
||
background: var(--color-primary);
|
||
border-color: var(--color-primary);
|
||
font-size: 0.8rem;
|
||
}
|
||
|
||
.play-btn.svelte-tjvfz7:hover {
|
||
background: var(--color-primary-hover);
|
||
}
|
||
|
||
.tl-slider.svelte-tjvfz7 {
|
||
flex: 1;
|
||
min-width: 120px;
|
||
}
|
||
|
||
.tl-slider.svelte-tjvfz7 input[type='range']:where(.svelte-tjvfz7) {
|
||
width: 100%;
|
||
height: 6px;
|
||
-webkit-appearance: none;
|
||
appearance: none;
|
||
background: var(--color-border);
|
||
border-radius: 3px;
|
||
outline: none;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.tl-slider.svelte-tjvfz7 input[type='range']:where(.svelte-tjvfz7)::-webkit-slider-thumb {
|
||
-webkit-appearance: none;
|
||
width: 16px;
|
||
height: 16px;
|
||
border-radius: 50%;
|
||
background: var(--color-primary);
|
||
border: 2px solid #fff;
|
||
cursor: pointer;
|
||
box-shadow: 0 0 6px rgba(56, 189, 248, 0.5);
|
||
}
|
||
|
||
.tl-slider.svelte-tjvfz7 input[type='range']:where(.svelte-tjvfz7)::-moz-range-thumb {
|
||
width: 16px;
|
||
height: 16px;
|
||
border-radius: 50%;
|
||
background: var(--color-primary);
|
||
border: 2px solid #fff;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.tl-info.svelte-tjvfz7 {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
width: 100%;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.tl-time.svelte-tjvfz7 {
|
||
font-weight: 600;
|
||
font-size: 0.95rem;
|
||
color: var(--color-primary);
|
||
}
|
||
|
||
.tl-date.svelte-tjvfz7 {
|
||
font-size: 0.82rem;
|
||
}
|
||
|
||
.tl-badge.svelte-tjvfz7 {
|
||
font-size: 0.7rem;
|
||
font-weight: 600;
|
||
padding: 2px 8px;
|
||
border-radius: 10px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
}
|
||
|
||
.tl-past.svelte-tjvfz7 {
|
||
background: rgba(96, 165, 250, 0.15);
|
||
color: var(--color-rain);
|
||
}
|
||
|
||
.tl-future.svelte-tjvfz7 {
|
||
background: rgba(52, 211, 153, 0.15);
|
||
color: var(--color-success);
|
||
}
|
||
|
||
/* ── Responsive ── */
|
||
@media (max-width: 1023px) {
|
||
.radar-viewport.svelte-tjvfz7 {
|
||
height: 320px;
|
||
border-radius: var(--radius-md);
|
||
}
|
||
|
||
.timeline.svelte-tjvfz7 {
|
||
padding: 10px 12px;
|
||
gap: 8px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.radar-viewport.svelte-tjvfz7 {
|
||
height: 280px;
|
||
}
|
||
|
||
.zoom-btn.svelte-tjvfz7 {
|
||
width: 30px;
|
||
height: 30px;
|
||
font-size: 1rem;
|
||
}
|
||
|
||
.ctrl-btn.svelte-tjvfz7 {
|
||
width: 30px;
|
||
height: 30px;
|
||
}
|
||
|
||
.play-btn.svelte-tjvfz7 {
|
||
width: 34px;
|
||
height: 34px;
|
||
}
|
||
}
|
||
|
||
.bell-wrapper.svelte-hpnwii {
|
||
position: relative;
|
||
display: inline-flex;
|
||
}
|
||
|
||
.bell-btn.svelte-hpnwii {
|
||
position: relative;
|
||
}
|
||
|
||
.bell-btn.has-alerts.svelte-hpnwii {
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.bell-btn.has-danger.svelte-hpnwii {
|
||
animation: svelte-hpnwii-urgentPulse 2s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes svelte-hpnwii-urgentPulse {
|
||
0%, 100% { color: var(--color-text); }
|
||
50% { color: var(--color-danger); }
|
||
}
|
||
|
||
.bell-icon.svelte-hpnwii {
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.bell-icon.ringing.svelte-hpnwii {
|
||
animation: svelte-hpnwii-ring 0.5s ease-in-out;
|
||
}
|
||
|
||
@keyframes svelte-hpnwii-ring {
|
||
0%, 100% { transform: rotate(0); }
|
||
15% { transform: rotate(12deg); }
|
||
30% { transform: rotate(-10deg); }
|
||
45% { transform: rotate(8deg); }
|
||
60% { transform: rotate(-6deg); }
|
||
75% { transform: rotate(3deg); }
|
||
90% { transform: rotate(-1deg); }
|
||
}
|
||
|
||
.bell-badge.svelte-hpnwii {
|
||
position: absolute;
|
||
top: -2px;
|
||
right: -2px;
|
||
min-width: 16px;
|
||
height: 16px;
|
||
padding: 0 4px;
|
||
border-radius: 8px;
|
||
background: var(--color-danger);
|
||
color: white;
|
||
font-size: 0.65rem;
|
||
font-weight: 700;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* Dropdown */
|
||
.bell-dropdown.svelte-hpnwii {
|
||
position: absolute;
|
||
top: calc(100% + 8px);
|
||
right: 0;
|
||
width: 340px;
|
||
max-width: calc(100vw - 16px);
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border-light);
|
||
border-radius: var(--radius-lg);
|
||
box-shadow: var(--shadow-lg);
|
||
z-index: 150;
|
||
overflow: hidden;
|
||
animation: svelte-hpnwii-fadeInScale 0.15s ease-out;
|
||
}
|
||
|
||
@keyframes svelte-hpnwii-fadeInScale {
|
||
from { opacity: 0; transform: scale(0.95) translateY(-4px); }
|
||
to { opacity: 1; transform: scale(1) translateY(0); }
|
||
}
|
||
|
||
.bell-header.svelte-hpnwii {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 12px 14px;
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.bell-title.svelte-hpnwii {
|
||
font-size: 0.85rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.bell-dismiss-all.svelte-hpnwii {
|
||
background: none;
|
||
border: none;
|
||
color: var(--color-text-muted);
|
||
font-size: 0.78rem;
|
||
cursor: pointer;
|
||
padding: 2px 6px;
|
||
border-radius: var(--radius-sm);
|
||
transition: color var(--transition);
|
||
}
|
||
|
||
.bell-dismiss-all.svelte-hpnwii:hover {
|
||
color: var(--color-danger);
|
||
}
|
||
|
||
.bell-body.svelte-hpnwii {
|
||
max-height: 360px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.bell-alert.svelte-hpnwii {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 10px;
|
||
padding: 10px 14px;
|
||
border-bottom: 1px solid var(--color-border);
|
||
transition: background-color var(--transition);
|
||
}
|
||
|
||
.bell-alert.svelte-hpnwii:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.bell-alert.svelte-hpnwii:hover {
|
||
background: var(--color-surface-hover);
|
||
}
|
||
|
||
.bell-alert.danger.svelte-hpnwii {
|
||
border-left: 3px solid var(--color-danger);
|
||
}
|
||
|
||
.bell-alert.warning.svelte-hpnwii {
|
||
border-left: 3px solid var(--color-warning);
|
||
}
|
||
|
||
.bell-alert.info.svelte-hpnwii {
|
||
border-left: 3px solid var(--color-primary);
|
||
}
|
||
|
||
.bell-alert-icon.svelte-hpnwii {
|
||
font-size: 1.1rem;
|
||
flex-shrink: 0;
|
||
margin-top: 1px;
|
||
}
|
||
|
||
.bell-alert-content.svelte-hpnwii {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.bell-alert-msg.svelte-hpnwii {
|
||
font-size: 0.82rem;
|
||
color: var(--color-text);
|
||
display: block;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.bell-alert-type.svelte-hpnwii {
|
||
font-size: 0.7rem;
|
||
color: var(--color-text-muted);
|
||
text-transform: capitalize;
|
||
margin-top: 2px;
|
||
display: block;
|
||
}
|
||
|
||
.bell-dismiss.svelte-hpnwii {
|
||
background: none;
|
||
border: none;
|
||
color: var(--color-text-muted);
|
||
font-size: 0.85rem;
|
||
cursor: pointer;
|
||
padding: 4px 6px;
|
||
border-radius: var(--radius-sm);
|
||
transition: color var(--transition), background-color var(--transition);
|
||
flex-shrink: 0;
|
||
opacity: 0;
|
||
}
|
||
|
||
.bell-alert.svelte-hpnwii:hover .bell-dismiss:where(.svelte-hpnwii) {
|
||
opacity: 1;
|
||
}
|
||
|
||
.bell-dismiss.svelte-hpnwii:hover {
|
||
background: rgba(239, 68, 68, 0.1);
|
||
color: var(--color-danger);
|
||
}
|
||
|
||
.bell-empty.svelte-hpnwii {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 24px 16px;
|
||
gap: 8px;
|
||
}
|
||
|
||
.bell-empty-icon.svelte-hpnwii {
|
||
font-size: 1.6rem;
|
||
}
|
||
|
||
@media (max-width: 1023px) {
|
||
.bell-dropdown.svelte-hpnwii {
|
||
position: fixed;
|
||
top: 52px;
|
||
left: 8px;
|
||
right: 8px;
|
||
bottom: auto;
|
||
width: auto;
|
||
max-width: none;
|
||
max-height: calc(100dvh - 52px - 16px);
|
||
}
|
||
}
|
||
|
||
.modal-overlay.svelte-1koizbb {
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0, 0, 0, 0.6);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 200;
|
||
padding: 1rem;
|
||
}
|
||
|
||
.modal.svelte-1koizbb {
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border-light);
|
||
border-radius: var(--radius-lg);
|
||
padding: 20px;
|
||
max-width: 460px;
|
||
width: 100%;
|
||
box-shadow: var(--shadow-lg);
|
||
max-height: 85vh;
|
||
overflow-y: auto;
|
||
animation: svelte-1koizbb-fadeInScale 0.2s ease-out;
|
||
}
|
||
|
||
@keyframes svelte-1koizbb-fadeInScale {
|
||
from { opacity: 0; transform: scale(0.95); }
|
||
to { opacity: 1; transform: scale(1); }
|
||
}
|
||
|
||
.modal-header.svelte-1koizbb {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.settings-body.svelte-1koizbb {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.settings-group.svelte-1koizbb {
|
||
padding: 16px;
|
||
background: var(--color-surface-card);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
}
|
||
|
||
.settings-group.svelte-1koizbb h4:where(.svelte-1koizbb) {
|
||
font-size: 0.9rem;
|
||
margin-bottom: 12px;
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.danger-zone.svelte-1koizbb {
|
||
border-color: rgba(239, 68, 68, 0.3);
|
||
}
|
||
|
||
.danger-zone.svelte-1koizbb h4:where(.svelte-1koizbb) {
|
||
color: var(--color-danger);
|
||
}
|
||
|
||
.setting-row.svelte-1koizbb {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 8px 0;
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
.setting-row.svelte-1koizbb + .setting-row:where(.svelte-1koizbb) {
|
||
border-top: 1px solid var(--color-border);
|
||
}
|
||
|
||
.setting-col.svelte-1koizbb {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
padding-right: 12px;
|
||
}
|
||
|
||
.setting-help.svelte-1koizbb {
|
||
font-size: 0.72rem;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.setting-row.svelte-1koizbb span:where(.svelte-1koizbb) {
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.setting-row.svelte-1koizbb select:where(.svelte-1koizbb) {
|
||
width: auto;
|
||
max-width: 160px;
|
||
padding: 6px 10px;
|
||
font-size: 0.82rem;
|
||
}
|
||
|
||
/* Toggle switch */
|
||
.toggle.svelte-1koizbb {
|
||
position: relative;
|
||
display: inline-block;
|
||
width: 44px;
|
||
height: 24px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.toggle.svelte-1koizbb input:where(.svelte-1koizbb) {
|
||
opacity: 0;
|
||
width: 0;
|
||
height: 0;
|
||
}
|
||
|
||
.toggle-slider.svelte-1koizbb {
|
||
position: absolute;
|
||
inset: 0;
|
||
background: var(--color-border);
|
||
border-radius: 24px;
|
||
transition: background-color 0.2s;
|
||
}
|
||
|
||
.toggle-slider.svelte-1koizbb::before {
|
||
content: '';
|
||
position: absolute;
|
||
height: 18px;
|
||
width: 18px;
|
||
left: 3px;
|
||
bottom: 3px;
|
||
background: white;
|
||
border-radius: 50%;
|
||
transition: transform 0.2s;
|
||
}
|
||
|
||
.toggle.svelte-1koizbb input:where(.svelte-1koizbb):checked + .toggle-slider:where(.svelte-1koizbb) {
|
||
background: var(--color-primary);
|
||
}
|
||
|
||
.toggle.svelte-1koizbb input:where(.svelte-1koizbb):checked + .toggle-slider:where(.svelte-1koizbb)::before {
|
||
transform: translateX(20px);
|
||
}
|
||
|
||
.modal-footer.svelte-1koizbb {
|
||
display: flex;
|
||
gap: 8px;
|
||
padding-top: 4px;
|
||
}
|
||
|
||
@media (max-width: 1023px) {
|
||
.modal.svelte-1koizbb {
|
||
max-height: 90dvh;
|
||
max-height: calc(100dvh - 52px);
|
||
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
||
margin-bottom: 0;
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
max-width: none;
|
||
padding-bottom: max(20px, var(--safe-bottom));
|
||
animation: svelte-1koizbb-slideUp 0.25s ease-out;
|
||
}
|
||
|
||
@keyframes svelte-1koizbb-slideUp {
|
||
from { transform: translateY(100%); }
|
||
to { transform: translateY(0); }
|
||
}
|
||
}
|
||
|
||
.modal-overlay.svelte-1iarmsg {
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0, 0, 0, 0.6);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 200;
|
||
padding: 1rem;
|
||
}
|
||
|
||
.modal.svelte-1iarmsg {
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border-light);
|
||
border-radius: var(--radius-lg);
|
||
padding: 20px;
|
||
max-width: 420px;
|
||
width: 100%;
|
||
box-shadow: var(--shadow-lg);
|
||
max-height: 80vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
animation: svelte-1iarmsg-fadeInScale 0.2s ease-out;
|
||
}
|
||
|
||
@keyframes svelte-1iarmsg-fadeInScale {
|
||
from { opacity: 0; transform: scale(0.95); }
|
||
to { opacity: 1; transform: scale(1); }
|
||
}
|
||
|
||
.modal-header.svelte-1iarmsg {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 14px;
|
||
}
|
||
|
||
.modal-header.svelte-1iarmsg h3:where(.svelte-1iarmsg) {
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.search-row.svelte-1iarmsg {
|
||
position: relative;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.search-row.svelte-1iarmsg input:where(.svelte-1iarmsg) {
|
||
padding: 10px 36px 10px 14px;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.search-spinner.svelte-1iarmsg {
|
||
position: absolute;
|
||
right: 12px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
font-size: 1rem;
|
||
animation: svelte-1iarmsg-spin 1s linear infinite;
|
||
pointer-events: none;
|
||
}
|
||
|
||
@keyframes svelte-1iarmsg-spin {
|
||
from { transform: translateY(-50%) rotate(0deg); }
|
||
to { transform: translateY(-50%) rotate(360deg); }
|
||
}
|
||
|
||
.error-msg.svelte-1iarmsg {
|
||
color: #fca5a5;
|
||
font-size: 0.8rem;
|
||
padding: 6px 0;
|
||
}
|
||
|
||
.results-list.svelte-1iarmsg {
|
||
max-height: 260px;
|
||
overflow-y: auto;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
margin: 8px 0;
|
||
background: var(--color-surface-card);
|
||
}
|
||
|
||
.result-item.svelte-1iarmsg {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
width: 100%;
|
||
padding: 10px 14px;
|
||
border: none;
|
||
border-bottom: 1px solid var(--color-border);
|
||
background: transparent;
|
||
color: var(--color-text);
|
||
text-align: left;
|
||
cursor: pointer;
|
||
transition: background-color var(--transition);
|
||
}
|
||
|
||
.result-item.svelte-1iarmsg:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.result-item.svelte-1iarmsg:hover {
|
||
background: var(--color-surface-hover);
|
||
}
|
||
|
||
.result-icon.svelte-1iarmsg {
|
||
font-size: 1.2rem;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.result-info.svelte-1iarmsg {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.result-name.svelte-1iarmsg {
|
||
font-size: 0.9rem;
|
||
font-weight: 500;
|
||
display: block;
|
||
}
|
||
|
||
.result-detail.svelte-1iarmsg {
|
||
font-size: 0.78rem;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.result-arrow.svelte-1iarmsg {
|
||
color: var(--color-text-muted);
|
||
font-size: 1rem;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.no-results.svelte-1iarmsg {
|
||
padding: 20px;
|
||
text-align: center;
|
||
}
|
||
|
||
.geolocate-section.svelte-1iarmsg {
|
||
margin-top: 12px;
|
||
padding-top: 12px;
|
||
border-top: 1px solid var(--color-border);
|
||
}
|
||
|
||
@media (max-width: 1023px) {
|
||
.modal-overlay.svelte-1iarmsg {
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.modal.svelte-1iarmsg {
|
||
max-height: 85dvh;
|
||
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
||
margin-bottom: 0;
|
||
max-width: none;
|
||
padding-bottom: max(20px, var(--safe-bottom));
|
||
animation: svelte-1iarmsg-slideUp 0.25s ease-out;
|
||
}
|
||
|
||
@keyframes svelte-1iarmsg-slideUp {
|
||
from { transform: translateY(100%); }
|
||
to { transform: translateY(0); }
|
||
}
|
||
}
|
||
|
||
.app-shell.svelte-1n46o8q {
|
||
display: flex;
|
||
min-height: 100vh;
|
||
min-height: 100dvh;
|
||
max-width: 100vw;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
/* Mobile header */
|
||
.mobile-header.svelte-1n46o8q {
|
||
display: none;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: max(8px, var(--safe-top)) 8px 8px max(12px, var(--safe-top));
|
||
background: var(--color-surface);
|
||
border-bottom: 1px solid var(--color-border);
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 100;
|
||
}
|
||
|
||
.mobile-title.svelte-1n46o8q {
|
||
font-weight: 700;
|
||
font-size: 0.95rem;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
flex: 1;
|
||
margin: 0 8px;
|
||
}
|
||
|
||
/* Sidebar */
|
||
.sidebar.svelte-1n46o8q {
|
||
width: 280px;
|
||
min-width: 280px;
|
||
background: var(--color-surface);
|
||
border-right: 1px solid var(--color-border);
|
||
height: 100vh;
|
||
height: 100dvh;
|
||
position: sticky;
|
||
top: 0;
|
||
overflow-y: auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
z-index: 60;
|
||
}
|
||
|
||
.sidebar-overlay.svelte-1n46o8q {
|
||
display: none;
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
z-index: 59;
|
||
border: none;
|
||
cursor: pointer;
|
||
padding: 0;
|
||
margin: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
/* Main content */
|
||
.main-content.svelte-1n46o8q {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
min-height: 100vh;
|
||
min-height: 100dvh;
|
||
}
|
||
|
||
.top-bar.svelte-1n46o8q {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 12px 20px;
|
||
background: var(--color-surface);
|
||
border-bottom: 1px solid var(--color-border);
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 50;
|
||
}
|
||
|
||
.top-bar-title.svelte-1n46o8q {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.top-bar-title.svelte-1n46o8q h1:where(.svelte-1n46o8q) {
|
||
font-size: 1.1rem;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.current-badge.svelte-1n46o8q {
|
||
font-size: 0.75rem;
|
||
background: rgba(56, 189, 248, 0.15);
|
||
color: var(--color-primary);
|
||
padding: 2px 8px;
|
||
border-radius: 12px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.top-bar-actions.svelte-1n46o8q {
|
||
display: flex;
|
||
gap: 6px;
|
||
align-items: center;
|
||
}
|
||
|
||
/* Error banner */
|
||
.error-banner.svelte-1n46o8q {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin: 8px 16px;
|
||
padding: 10px 14px;
|
||
background: rgba(239, 68, 68, 0.1);
|
||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||
border-radius: var(--radius-md);
|
||
color: #fca5a5;
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
/* Empty state */
|
||
.empty-state.svelte-1n46o8q {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex: 1;
|
||
padding: 40px 20px;
|
||
text-align: center;
|
||
}
|
||
|
||
.empty-icon.svelte-1n46o8q {
|
||
font-size: 4rem;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.empty-state.svelte-1n46o8q h2:where(.svelte-1n46o8q) {
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
/* Loading state */
|
||
.loading-state.svelte-1n46o8q {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex: 1;
|
||
padding: 60px 20px;
|
||
}
|
||
|
||
.loading-spinner.svelte-1n46o8q {
|
||
font-size: 3rem;
|
||
animation: svelte-1n46o8q-spin 1.5s linear infinite;
|
||
}
|
||
|
||
@keyframes svelte-1n46o8q-spin {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
/* Desktop tabs */
|
||
.desktop-tabs.svelte-1n46o8q {
|
||
display: flex;
|
||
gap: 0;
|
||
padding: 0 20px;
|
||
border-bottom: 1px solid var(--color-border);
|
||
background: var(--color-bg);
|
||
}
|
||
|
||
.tab-btn.svelte-1n46o8q {
|
||
padding: 10px 16px;
|
||
border: none;
|
||
border-bottom: 2px solid transparent;
|
||
background: transparent;
|
||
color: var(--color-text-muted);
|
||
font-size: 0.85rem;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: color var(--transition), border-color var(--transition);
|
||
}
|
||
|
||
.tab-btn.svelte-1n46o8q:hover {
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.tab-btn.active.svelte-1n46o8q {
|
||
color: var(--color-primary);
|
||
border-bottom-color: var(--color-primary);
|
||
}
|
||
|
||
/* Mobile bottom nav */
|
||
.mobile-nav.svelte-1n46o8q {
|
||
display: none;
|
||
position: sticky;
|
||
top: 52px;
|
||
z-index: 90;
|
||
padding: 4px 12px;
|
||
gap: 2px;
|
||
background: var(--color-surface);
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.nav-btn.svelte-1n46o8q {
|
||
flex: 1;
|
||
padding: 8px 4px;
|
||
border: none;
|
||
border-radius: var(--radius-md);
|
||
background: transparent;
|
||
color: var(--color-text-muted);
|
||
font-size: 0.78rem;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: background-color var(--transition), color var(--transition);
|
||
white-space: nowrap;
|
||
text-align: center;
|
||
}
|
||
|
||
.nav-btn.active.svelte-1n46o8q {
|
||
background: rgba(56, 189, 248, 0.12);
|
||
color: var(--color-primary);
|
||
}
|
||
|
||
.content-area.svelte-1n46o8q {
|
||
flex: 1;
|
||
padding: 16px;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Responsive */
|
||
@media (max-width: 1023px) {
|
||
.app-shell.svelte-1n46o8q {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.mobile-header.svelte-1n46o8q {
|
||
display: flex;
|
||
}
|
||
|
||
.top-bar.svelte-1n46o8q {
|
||
display: none;
|
||
}
|
||
|
||
.sidebar.svelte-1n46o8q {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
bottom: 0;
|
||
z-index: 60;
|
||
display: none;
|
||
transition: none;
|
||
width: min(300px, 85vw);
|
||
min-width: 0;
|
||
height: 100dvh;
|
||
padding-top: var(--safe-top);
|
||
}
|
||
|
||
.sidebar.open.svelte-1n46o8q {
|
||
display: flex;
|
||
}
|
||
|
||
.sidebar-overlay.svelte-1n46o8q {
|
||
display: block;
|
||
}
|
||
|
||
.desktop-tabs.svelte-1n46o8q {
|
||
display: none;
|
||
}
|
||
|
||
.mobile-nav.svelte-1n46o8q {
|
||
display: flex;
|
||
}
|
||
|
||
.content-area.svelte-1n46o8q {
|
||
padding: 8px 8px max(16px, var(--safe-bottom));
|
||
}
|
||
|
||
.error-banner.svelte-1n46o8q {
|
||
margin: 4px 8px;
|
||
}
|
||
}
|
||
|
||
@media (min-width: 1024px) {
|
||
.mobile-nav.svelte-1n46o8q {
|
||
display: none;
|
||
}
|
||
}
|
||
/*$vite$:1*/</style>
|
||
</head>
|
||
<body>
|
||
<div id="app"></div>
|
||
</body>
|
||
</html>
|