weather_app/dist/index.html
hermes-explorigin 7e06c17bfa Use light-drizzle icon for WMO 61 (light rain) and 80 (light showers)
Both codes now render 🌦️ instead of 🌧️ in the CODE_MAP. Updated the
weather-codes tests accordingly.
2026-08-27 00:35:29 +00:00

10902 lines
334 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>