diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c8e9b7f..8b5c996 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -149,6 +149,17 @@ const Room: Component = () => { send({ type: "set_playlist_enabled", enabled }); }; + // Reorder an item in the up-next queue. 0-indexed. + const handleMoveQueueItem = (from: number, to: number) => { + if (from === to) return; + send({ type: "move_queue_item", from, to }); + }; + + // Shuffle the up-next queue. Server picks the seed. + const handleShuffleQueue = () => { + send({ type: "shuffle_queue" }); + }; + return (
@@ -240,6 +251,8 @@ const Room: Component = () => { onAddToPlaylist={handleAddToPlaylist} onRemoveFromPlaylist={handleRemoveFromPlaylist} onTogglePlaylist={handleTogglePlaylist} + onMoveQueueItem={handleMoveQueueItem} + onShuffleQueue={handleShuffleQueue} /> send({ type: "chat", content })} />
-

Up Next ({props.queue.length})

+

+ Up Next ({props.queue.length}) + {props.onShuffleQueue && props.queue.length > 1 && ( + + )} +

- {(item) => ( - + {(item, index) => ( +
+ + {props.onMoveQueueItem && ( +
+ + +
+ )} +
)}
{props.queue.length === 0 &&
Tracks you add will appear here
} diff --git a/frontend/src/style.css b/frontend/src/style.css index 7147042..cdc26e9 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -421,6 +421,65 @@ button.secondary:hover:not(:disabled) { margin-bottom: 0.4rem; letter-spacing: 0.06em; font-weight: 700; + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; +} +.queue-shuffle { + font-size: 0.7rem; + font-weight: 600; + text-transform: none; + letter-spacing: 0; + padding: 0.15rem 0.5rem; + border: 1px solid var(--border); + border-radius: 4px; + background: var(--surface); + color: var(--text-muted); + cursor: pointer; + transition: + color 0.15s, + border-color 0.15s, + background 0.15s; +} +.queue-shuffle:hover { + color: var(--accent); + border-color: var(--accent); + background: var(--surface-raised); +} +.queue-item-actions { + display: flex; + gap: 0.15rem; + flex-shrink: 0; +} +.queue-item-actions .queue-item-action { + display: flex; + font-size: 0.85rem; + width: 20px; + height: 20px; +} +.queue-item-actions .queue-item-action:disabled { + opacity: 0.25; + cursor: not-allowed; +} +.queue-item-actions .queue-item-action:disabled:hover { + color: var(--text-muted); + background: none; +} +.playlist-toggle { + display: inline-flex; + align-items: center; + gap: 0.3rem; + text-transform: none; + letter-spacing: 0; + font-size: 0.72rem; + color: var(--text-muted); + cursor: pointer; + user-select: none; +} +.playlist-toggle input { + margin: 0; + cursor: pointer; } .queue-item { display: flex; diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 05a75a4..e874e52 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -66,4 +66,6 @@ export type ClientMessage = | { type: "skip" } | { type: "track_ended"; item_id: string } | { type: "set_playlist_enabled"; enabled: boolean } - | { type: "ping" }; + | { type: "ping" } + | { type: "move_queue_item"; from: number; to: number } + | { type: "shuffle_queue" }; diff --git a/src/room.rs b/src/room.rs index d8bdef0..ea97cea 100644 --- a/src/room.rs +++ b/src/room.rs @@ -62,6 +62,11 @@ pub(crate) enum RoomCommand { SetPlaylistEnabled(bool), PublishState, Shutdown, + MoveQueueItem { + from: usize, + to: usize, + }, + ShuffleQueue, } /// Per-room event-loop actor. Owns all mutable state. @@ -399,6 +404,8 @@ impl RoomActor { } RoomCommand::PublishState => self.handle_publish_state().await, RoomCommand::Shutdown => self.handle_shutdown().await, + RoomCommand::MoveQueueItem { from, to } => self.handle_move_queue_item(from, to).await, + RoomCommand::ShuffleQueue => self.handle_shuffle_queue().await, } } @@ -674,6 +681,33 @@ impl RoomActor { false } + /// Reorder a single track in the queue. Out-of-range or no-op moves + /// return no effects from the state machine; everything is persisted + /// before the snapshot goes out. + async fn handle_move_queue_item(&mut self, from: usize, to: usize) -> bool { + let effects = self.state.transition(&Event::MoveQueueItem { from, to }, 0); + if effects.is_empty() { + return false; + } + self.persist_effects(&effects).await; + self.execute_effects(effects).await; + false + } + + /// Shuffle the queue using a server-picked seed. The seed comes from the + /// wall clock so concurrent shuffles are independent, and clients can't + /// predict or replay each other's shuffles. + async fn handle_shuffle_queue(&mut self) -> bool { + let seed = Utc::now().timestamp_nanos_opt().unwrap_or(0) as u64; + let effects = self.state.transition(&Event::ShuffleQueue { seed }, 0); + if effects.is_empty() { + return false; + } + self.persist_effects(&effects).await; + self.execute_effects(effects).await; + false + } + async fn handle_chat( &mut self, user_name: String, diff --git a/src/state.rs b/src/state.rs index 8975bb0..899e898 100644 --- a/src/state.rs +++ b/src/state.rs @@ -90,6 +90,13 @@ pub(crate) enum Event { PlaylistEntryRemoved(PlaylistEntryId), /// Toggle the perpetual playlist on/off. SetPlaylistEnabled(bool), + /// Reorder a track within the up-next queue. + /// `from` and `to` are 0-based queue indices. `from == to` is a no-op. + /// Indices out of range or queue empty are no-ops. + MoveQueueItem { from: usize, to: usize }, + /// Shuffle the up-next queue deterministically. Same seed → same result. + /// Empty/single-item queues are no-ops. Active track is not affected. + ShuffleQueue { seed: u64 }, } /// Side effects to execute after a transition. @@ -135,6 +142,11 @@ pub(crate) enum Effect { }, /// Remove a playlist entry. RemovePlaylistEntry(PlaylistEntryId), + /// Renumber queue positions to match `new_order`. Emitted by both + /// move and shuffle operations; the store updates the `position` + /// column for every queued track so a fresh `SELECT ... ORDER BY + /// position` returns the new order. + ReorderQueue { new_order: Vec }, } /// Pure playback state: no IO handles, no DB connections. @@ -189,6 +201,8 @@ impl PlaybackState { Event::PlaylistEntryAdded(entry) => self.handle_playlist_entry_added(entry), Event::PlaylistEntryRemoved(id) => self.handle_playlist_entry_removed(*id), Event::SetPlaylistEnabled(enabled) => self.handle_set_playlist_enabled(*enabled), + Event::MoveQueueItem { from, to } => self.handle_move_queue_item(*from, *to), + Event::ShuffleQueue { seed } => self.handle_shuffle_queue(*seed), } } @@ -488,6 +502,37 @@ impl PlaybackState { Some(entry) } + /// Reorder a track within the up-next queue. `from` and `to` are + /// 0-based queue indices. The active track is not affected. No-op if + /// either index is out of range, the queue is empty, or `from == to`. + fn handle_move_queue_item(&mut self, from: usize, to: usize) -> Vec { + if from == to || from >= self.queue.len() || to >= self.queue.len() { + return Vec::new(); + } + let track = self.queue.remove(from); + self.queue.insert(to, track); + let new_order: Vec = self.queue.iter().map(|t| t.id).collect(); + vec![Effect::ReorderQueue { new_order }, Effect::PublishSnapshot] + } + + /// Shuffle the up-next queue deterministically. Same seed → same order. + /// Fisher-Yates with a splitmix64 PRNG seeded by the caller. + /// No-op for queues with fewer than 2 items. + fn handle_shuffle_queue(&mut self, seed: u64) -> Vec { + if self.queue.len() < 2 { + return Vec::new(); + } + let mut rng = SplitMix64::new(seed); + let n = self.queue.len(); + // Fisher-Yates: walk from the end, swap each element with a random earlier one. + for i in (1..n).rev() { + let j = (rng.next() as usize) % (i + 1); + self.queue.swap(i, j); + } + let new_order: Vec = self.queue.iter().map(|t| t.id).collect(); + vec![Effect::ReorderQueue { new_order }, Effect::PublishSnapshot] + } + /// Pop the first track from the queue and start playing it. /// /// Returns effects for starting the pipeline and publishing state. @@ -560,6 +605,24 @@ impl PlaybackState { } } +/// Tiny splitmix64 PRNG — deterministic, no dependencies, good enough for +/// shuffling a user-visible queue. Not cryptographic. +struct SplitMix64(u64); + +impl SplitMix64 { + fn new(seed: u64) -> Self { + Self(seed) + } + + fn next(&mut self) -> u64 { + self.0 = self.0.wrapping_add(0x9E3779B97F4A7C15); + let mut z = self.0; + z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9); + z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB); + z ^ (z >> 31) + } +} + /// # Pure unit tests (no async, no DB, no IO) #[cfg(test)] mod tests { @@ -1472,6 +1535,138 @@ mod tests { // Active track should be the queued one. assert_eq!(s.active.as_ref().unwrap().title, "PL"); } + + // --- queue reorder / shuffle --- + + fn qid(n: u128) -> QueuedTrack { + QueuedTrack { + id: track_id(n), + title: format!("Q{n}"), + url: format!("https://example.com/q{n}"), + duration: "1:00".into(), + thumbnail: None, + pending: false, + } + } + + fn queue_with(n: usize) -> (PlaybackState, Vec) { + // Queue `n` tracks. The first becomes active (idle room); the rest + // sit in the queue. Total TrackQueued events = n + 1. + let mut s = PlaybackState::default(); + let active = qid(0); + let mut ids = vec![active.id]; + s.transition(&Event::TrackQueued(active), 0); + for i in 0..n { + let t = qid((i + 1) as u128); + ids.push(t.id); + s.transition(&Event::TrackQueued(t), 0); + } + (s, ids) + } + + #[test] + fn test_move_queue_item_basic() { + // Queue (after queue_with): [B, C, D]. Active is A. Move from 0 to 2 → [C, D, B]. + let (mut s, ids) = queue_with(3); + let effects = s.transition(&Event::MoveQueueItem { from: 0, to: 2 }, 0); + assert_eq!(s.queue[0].id, ids[2]); + assert_eq!(s.queue[1].id, ids[3]); + assert_eq!(s.queue[2].id, ids[1]); + // Effect carries the new queue order so the store can renumber. + assert!(effects.iter().any(|e| matches!( + e, + Effect::ReorderQueue { new_order } if new_order == &vec![ids[2], ids[3], ids[1]] + ))); + assert!(effects.iter().any(|e| matches!(e, Effect::PublishSnapshot))); + } + + #[test] + fn test_move_queue_item_same_index_is_noop() { + let (mut s, ids) = queue_with(3); + let effects = s.transition(&Event::MoveQueueItem { from: 1, to: 1 }, 0); + assert_eq!(s.queue[0].id, ids[1]); + assert!(effects.is_empty()); + } + + #[test] + fn test_move_queue_item_out_of_range_is_noop() { + let (mut s, _ids) = queue_with(3); + let effects = s.transition(&Event::MoveQueueItem { from: 5, to: 0 }, 0); + assert_eq!(s.queue.len(), 3); + assert!(effects.is_empty()); + let effects = s.transition(&Event::MoveQueueItem { from: 0, to: 5 }, 0); + assert!(effects.is_empty()); + } + + #[test] + fn test_move_queue_item_empty_queue_is_noop() { + let mut s = PlaybackState::default(); + let effects = s.transition(&Event::MoveQueueItem { from: 0, to: 0 }, 0); + assert!(effects.is_empty()); + } + + #[test] + fn test_shuffle_queue_deterministic() { + // Same seed → same order. Run twice and compare. + let (mut s1, _) = queue_with(8); + let (mut s2, _) = queue_with(8); + s1.transition(&Event::ShuffleQueue { seed: 42 }, 0); + s2.transition(&Event::ShuffleQueue { seed: 42 }, 0); + let order1: Vec = s1.queue.iter().map(|t| t.id).collect(); + let order2: Vec = s2.queue.iter().map(|t| t.id).collect(); + assert_eq!(order1, order2); + } + + #[test] + fn test_shuffle_queue_different_seeds_diverge() { + // Two different seeds → almost certainly different orders. + // (Birthday-paradox: collision chance with 8 items is ~1/2000.) + let (mut s1, _) = queue_with(8); + let (mut s2, _) = queue_with(8); + s1.transition(&Event::ShuffleQueue { seed: 1 }, 0); + s2.transition(&Event::ShuffleQueue { seed: 2 }, 0); + let order1: Vec = s1.queue.iter().map(|t| t.id).collect(); + let order2: Vec = s2.queue.iter().map(|t| t.id).collect(); + assert_ne!(order1, order2); + } + + #[test] + fn test_shuffle_queue_preserves_count() { + let (mut s, ids) = queue_with(10); + s.transition(&Event::ShuffleQueue { seed: 99 }, 0); + assert_eq!(s.queue.len(), 10); + // ids[0] is the active track; ids[1..] are the queue. + let mut sorted: Vec<_> = s.queue.iter().map(|t| t.id).collect(); + sorted.sort_by_key(|id| id.0.as_u128()); + let mut expected: Vec<_> = ids[1..].to_vec(); + expected.sort_by_key(|id| id.0.as_u128()); + assert_eq!(sorted, expected); + } + + #[test] + fn test_shuffle_queue_singleton_is_noop() { + let (mut s, ids) = queue_with(1); + let effects = s.transition(&Event::ShuffleQueue { seed: 0 }, 0); + assert_eq!(s.queue[0].id, ids[1]); + assert!(effects.is_empty()); + } + + #[test] + fn test_shuffle_queue_empty_is_noop() { + let mut s = PlaybackState::default(); + let effects = s.transition(&Event::ShuffleQueue { seed: 0 }, 0); + assert!(effects.is_empty()); + } + + #[test] + fn test_shuffle_queue_does_not_affect_active() { + // Active track stays put; the shuffle permutes only the queue Vec. + let (mut s, _ids) = queue_with(5); + let original_active = s.active.as_ref().unwrap().id; + s.transition(&Event::ShuffleQueue { seed: 7 }, 0); + assert_eq!(s.active.as_ref().unwrap().id, original_active); + assert_eq!(s.queue.len(), 5); + } } /// Deterministic simulation testing: random event sequences with invariant @@ -1506,17 +1701,23 @@ mod dst { MetadataUpdated(u64), DownloadResolved(u64), DownloadFailed(u64), + MoveQueueItem { from: u8, to: u8 }, + ShuffleQueue { seed: u64 }, } fn op_strategy() -> impl Strategy> { + // Move and shuffle are weighted higher than the other ops so the new + // handlers get meaningful coverage in the 1024-case run. let op = prop_oneof![ - (0u64..12).prop_map(Op::Queue), - Just(Op::Skip), - (0u64..12).prop_map(Op::TrackEnded), - (0u64..12).prop_map(Op::TrackFailed), - (0u64..12).prop_map(Op::MetadataUpdated), - (0u64..12).prop_map(Op::DownloadResolved), - (0u64..12).prop_map(Op::DownloadFailed), + 2 => (0u64..12).prop_map(Op::Queue), + 1 => Just(Op::Skip), + 1 => (0u64..12).prop_map(Op::TrackEnded), + 1 => (0u64..12).prop_map(Op::TrackFailed), + 1 => (0u64..12).prop_map(Op::MetadataUpdated), + 1 => (0u64..12).prop_map(Op::DownloadResolved), + 1 => (0u64..12).prop_map(Op::DownloadFailed), + 3 => (0u8..16, 0u8..16).prop_map(|(from, to)| Op::MoveQueueItem { from, to }), + 2 => (0u64..1000).prop_map(|seed| Op::ShuffleQueue { seed }), ]; proptest::collection::vec(op, 0..50) } @@ -1624,6 +1825,56 @@ mod dst { ); effects } + Op::MoveQueueItem { from, to } => { + // The handler is a no-op for out-of-range or equal indices. + let queue_len = state.queue.len(); + let from_in = (*from as usize) < queue_len; + let to_in = (*to as usize) < queue_len; + let changed = from_in && to_in && from != to; + let expected_ids: Vec<_> = if changed { + let mut q = state.queue.clone(); + let t = q.remove(*from as usize); + q.insert(*to as usize, t); + q.into_iter().map(|t| t.id).collect() + } else { + state.queue.iter().map(|t| t.id).collect() + }; + let effects = state.transition( + &Event::MoveQueueItem { + from: *from as usize, + to: *to as usize, + }, + 0, + ); + let actual_ids: Vec<_> = state.queue.iter().map(|t| t.id).collect(); + assert_eq!( + actual_ids, expected_ids, + "MoveQueueItem {from},{to} produced wrong order", + ); + if changed { + assert!(effects.iter().any(|e| matches!(e, Effect::PublishSnapshot))); + } else { + assert!(effects.is_empty(), "no-op move should return no effects"); + } + effects + } + Op::ShuffleQueue { seed } => { + // Same seed → same order. Record the order, run the op, + // and verify the queue contains the same track IDs + // (it's a permutation, not a transformation). + let pre: std::collections::HashSet<_> = + state.queue.iter().map(|t| t.id).collect(); + let effects = state.transition(&Event::ShuffleQueue { seed: *seed }, 0); + let post: std::collections::HashSet<_> = + state.queue.iter().map(|t| t.id).collect(); + assert_eq!(pre, post, "shuffle must be a permutation"); + if state.queue.len() >= 2 { + assert!(effects.iter().any(|e| matches!(e, Effect::PublishSnapshot))); + } else { + assert!(effects.is_empty(), "tiny queue should be a no-op"); + } + effects + } }; check_invariants(&state, &effects, &queued_ids, &mut history_snapshot); @@ -1797,6 +2048,10 @@ mod dst { } proptest! { + #![proptest_config(ProptestConfig { + cases: 1024, + ..ProptestConfig::default() + })] #[test] fn dst_random_sequences_invariants_hold(ops in op_strategy()) { apply_ops(&ops); diff --git a/src/store.rs b/src/store.rs index 84d53f7..02c409a 100644 --- a/src/store.rs +++ b/src/store.rs @@ -292,6 +292,12 @@ impl RoomStore for InMemoryStore { Effect::RemovePlaylistEntry(id) => { room.playlist.retain(|e| e.id != *id); } + Effect::ReorderQueue { new_order } => { + // The state machine's in-memory `room.queue` is already in + // the new order; this arm keeps the effect exhaustively + // matched. The SQLite path handles position renumbering. + let _ = new_order; + } _ => {} } } @@ -591,6 +597,28 @@ impl SqliteStore { .execute(&mut *tx) .await?; } + Effect::ReorderQueue { new_order } => { + // Renumber every queued track's position to its index in + // new_order. Limit to un-played, un-started rows so we + // never touch the active or history rows. Using indexed + // UPDATEs (one per row) is fine for queues of typical + // size (< 100). + for (i, track_id) in new_order.iter().enumerate() { + sqlx::query( + "UPDATE tracks + SET position = ?1 + WHERE id = ?2 + AND room_id = ?3 + AND played = 0 + AND started_at_ms IS NULL", + ) + .bind(i as i64) + .bind(track_id.as_str_buf()) + .bind(room_id) + .execute(&mut *tx) + .await?; + } + } _ => {} } } diff --git a/src/transport.rs b/src/transport.rs index 646c3af..2b389f2 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -26,11 +26,24 @@ use crate::types::{ChatMessage, RoomId, RoomPublishers, TrackId}; #[derive(Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] enum WsClientMessage { - Chat { content: String }, + Chat { + content: String, + }, Skip, - TrackEnded { item_id: TrackId }, - SetPlaylistEnabled { enabled: bool }, + TrackEnded { + item_id: TrackId, + }, + SetPlaylistEnabled { + enabled: bool, + }, Ping, + /// Move a queue item from one position to another. 0-indexed. + MoveQueueItem { + from: usize, + to: usize, + }, + /// Shuffle the entire queue (server picks the seed). + ShuffleQueue, } /// Run a WebSocket session for a single client. @@ -128,6 +141,14 @@ pub(crate) async fn handle_ws_session( WsClientMessage::Ping => { let _ = ws_tx.send(Message::Ping(Bytes::new())).await; } + WsClientMessage::MoveQueueItem { from, to } => { + let _ = cmd_tx + .send(RoomCommand::MoveQueueItem { from, to }) + .await; + } + WsClientMessage::ShuffleQueue => { + let _ = cmd_tx.send(RoomCommand::ShuffleQueue).await; + } } } } diff --git a/static/dist/assets/index-DnsnxcTM.js b/static/dist/assets/index-DnsnxcTM.js new file mode 100644 index 0000000..cece5ac --- /dev/null +++ b/static/dist/assets/index-DnsnxcTM.js @@ -0,0 +1 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const o of i)if(o.type==="childList")for(const s of o.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(i){const o={};return i.integrity&&(o.integrity=i.integrity),i.referrerPolicy&&(o.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?o.credentials="include":i.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function r(i){if(i.ep)return;i.ep=!0;const o=n(i);fetch(i.href,o)}})();class U extends Error{source;constructor(t){super(),this.source=t}}class Ft extends Error{source;constructor(t,n){super(n instanceof Error?n.message:String(n),{cause:n}),this.source=t}}class Sn extends Error{constructor(){super("")}}class dr extends Error{constructor(){super("")}}const En=0,Ve=1,Pe=2,pt=4,le=8,Je=16,J=32,ye=64,$e=128,Gt=256,at=512,He=1024,wt=1,hr=2,bt=4,gr=8,Cn=16,xe=32,Jt=64,O=1,W=2,F=4,Ae=1,G=2,Ce=3,_={},yr=typeof Proxy=="function",Ht={},mr=Symbol("refresh");function On(e,t){const n=(e.i?.t?e.i.u?.o:e.i?.o)??-1;n>=e.o&&(e.o=n+1);const r=e.o,i=t.l[r];if(i===void 0)t.l[r]=e;else{const o=i.S;o.T=e,e.S=o,i.S=e}r>t._&&(t._=r)}function Qe(e,t){let n=e.O;n&(le|pt|He)||(n&Ve?e.O=n&-4|Pe|le:e.O=n|le,n&Je||On(e,t))}function Tn(e,t){let n=e.O;n&(le|pt|Je|He)||(e.O=n|Je,On(e,t))}function Ne(e,t){const n=e.O;if(!(n&(le|Je)))return;e.O=n&-25;const r=e.o;if(e.S===e)t.l[r]=void 0;else{const i=e.T,o=t.l[r],s=i??o;e===o?t.l[r]=i:e.S.T=i,s.S=e.S}e.S=e,e.T=void 0}function pr(e){if(!e.R){e.R=!0;for(let t=0;t<=e._;t++)for(let n=e.l[t];n!==void 0;n=n.T)n.O&le&&ft(n)}}function ft(e,t=Pe){const n=e.O;if(!((n&(Ve|Pe))>=t)){e.O=n&-4|t;for(let r=e.I;r!==null;r=r.p)ft(r.h,Ve);if(e.N!==null)for(let r=e.N;r!==null;r=r.A)for(let i=r.I;i!==null;i=i.p)ft(i.h,Ve)}}function Fe(e,t){for(e.R=!1,e.C=0;e.C<=e._;e.C++){let n=e.l[e.C];for(;n!==void 0;)n.O&le?t(n):wr(n,e),n=e.l[e.C]}e._=0}function wr(e,t){Ne(e,t);let n=e.o;for(let r=e.P;r;r=r.D){const i=r.m,o=i.V||i;o.L&&o.o>=n&&(n=o.o+1)}if(e.o!==n){e.o=n;for(let r=e.I;r!==null;r=r.p)Tn(r.h,t)}}const dt=new WeakMap,te=new Set;function br(e){let t=dt.get(e);if(t)return V(t);const n=e.U,r=n?.G?V(n.G):null;return t={k:e,F:new Set,W:[[],[]],H:null,M:b,j:r},dt.set(e,t),te.add(t),e.$=!1,t}function V(e){for(;e.H;)e=e.H;return e}function An(e,t){if(e=V(e),t=V(t),e===t)return e;t.H=e;for(const n of t.F)e.F.add(n);return e.W[0].push(...t.W[0]),e.W[1].push(...t.W[1]),e}function Re(e){const t=e.G;if(!t)return;const n=V(t);if(te.has(n))return n;e.G=void 0}function Be(e){return Re(e)?.M??e.M}function vt(e){return e.K!==void 0&&e.K!==_}function Yt(e,t){const n=V(t),r=e.G;if(r){if(r.H){e.G=t;return}const i=V(r);if(te.has(i)){i!==n&&!vt(e)&&(n.j&&V(n.j)===i?e.G=t:i.j&&V(i.j)===n||An(n,i));return}}e.G=t}const je=new Set,I={l:new Array(2e3).fill(void 0),R:!1,C:0,_:0},H={l:new Array(2e3).fill(void 0),R:!1,C:0,_:0};let M=0,b=null,Ye=!1,Bt=0,lt=null;const Ke=new Set;function vr(e){return je.size===0&&te.size===0&&e.Y.length===0&&e.Z.length===0&&e.q.size===0&&Ke.size===0}function $r(){if(Ke.size!==0)for(const e of Ke){if(e.I!==null){Ke.delete(e);continue}e.B===_&&(e.K!==void 0&&e.K!==_||(Ke.delete(e),e.X?.()))}}function _r(e){return!!lt?.has(e)}function nt(e){for(const t of te){if(t.H||t.F.size>0)continue;const n=t.W[e-1];n.length&&(t.W[e-1]=[],gt(n,e))}}function Sr(e){for(let t=e.I;t!==null;t=t.p){const n=t.h;if(!n.J)continue;if(n.J===Ce){n.ee||(n.ee=!0,n.te.enqueue(G,n.ne));continue}const r=n.O&J?H:I;r.C>n.o&&(r.C=n.o),Qe(n,r)}}function Er(e,t){t.ie=e,e.re.push(...t.re);for(const n of te)n.M===t&&(n.M=e);e.Z.push(...t.Z);for(const n of t.q)e.q.add(n);for(const[n,r]of t.oe){let i=e.oe.get(n);i||e.oe.set(n,i=new Set);for(const o of r)i.add(o)}for(const n of t.se)e.se.add(n)}function Cr(e){for(let t=0;t=0&&(this.Y.splice(n,1),t.i=null)}notify(t,n,r,i){return this.i?this.i.notify(t,n,r,i):!1}run(t){if(this.le[t-1].length){const n=this.le[t-1];this.le[t-1]=[],gt(n,t)}for(let n=0;n=I.C,cn(r.fe),b=null,!r.re.length&&!r.oe.size&&r.Z.length){lt=new Set;for(let i=0;i=I.C&&(Fe(I,X.Ee),ht())):(je.size&&Fe(H,X.Ee),kt());M++,Ye=I._>=I.C,te.size&&nt(Ae),this.run(Ae),te.size&&nt(G),this.run(G)}finally{this.ce=!1}}}notify(t,n,r,i){if(n&O){if(r&O){const o=i!==void 0?i:t.Re;if(b&&o){const s=o.source;let c=b.oe.get(s);c||b.oe.set(s,c=new Set);const l=c.size;c.add(t),c.size!==l&&Se()}}return!0}return!1}initTransition(t){if(t&&(t=Pn(t)),!(t&&t===b)&&!(!t&&b&&b.Ie===M)){if(!b)b=t??{Ie:M,fe:[],oe:new Map,Z:[],q:new Set,re:[],_e:{le:[[],[]],Y:[]},ie:!1,se:new Set};else if(t){const n=b;Er(t,n),je.delete(n),b=t}if(je.add(b),b.Ie=M,this.ae!==null&&(this.ae.M=b,b.fe.push(this.ae),this.ae=null),this.fe!==b.fe){for(let n=0;ni.h.o&&(s.C=i.h.o),Qe(i.h,s)}}function un(e){const t=e;if(!t.L){e.B!==_&&(e.ue=e.B,e.B=_);return}e.B!==_&&(e.ue=e.B,e.B=_,e.J&&e.J!==Ce&&(e.ee=!0)),t.O&=~He,t.he&O||(t.he&=~F),(t.Ne!==null||t.Ae!==null)&&X.Se(t,!1,!0)}function ht(){E.ae!==null&&(un(E.ae),E.ae=null);const e=E.fe;for(let t=0;t=I.C;if(r&&Fe(I,X.Ee),n){if(r&&ht(),Cr(e?e.Z:E.Z),e&&e.se.size){for(const i of e.se){if(i.O&ye)continue;if(i.J===Ce){i.ee||(i.ee=!0,i.te.enqueue(G,i.ne));continue}const o=i.O&J?H:I;o.C>i.o&&(o.C=i.o),Qe(i,o)}e.se.clear()}e?e.q:E.q,$r(),Or(e)}}function Rn(e){for(const t of e.Y)t.checkSources?.(),Rn(t)}function cn(e){for(let t=0;te(()=>n.dispose()))}function Xt(e){const t=e.m,n=e.D,r=e.p,i=e.Le;if(r!==null?r.Le=i:t.Ue=i,i!==null)i.p=r;else if(t.I=r,r===null){t.X?.();const o=t;o.L&&o.Oe&xe&&!(o.O&J)&&Ln(o)}return n}function In(e){const t=e.ye;let n=t!==null?t.D:e.P;if(n!==null){do n=Xt(n);while(n!==null);t!==null?t.D=null:e.P=null}}function Ln(e){Ne(e,e.O&J?H:I);let t=e.P;for(;t!==null;)t=Xt(t);e.P=null,e.ye=null,Xe(e,!0)}function De(e,t){const n=t.ye;if(n!==null&&n.m===e)return;let r=null;const i=t.O&pt;if(i&&(r=n!==null?n.D:t.P,r!==null&&r.m===e)){t.ye=r;return}const o=e.Ue;if(o!==null&&o.h===t&&(!i||Nr(o,t)))return;const s=t.ye=e.Ue={m:e,h:t,D:r,Le:o,p:null};n!==null?n.D=s:t.P=s,o!==null?o.p=s:e.I=s}function Nr(e,t){const n=t.ye;if(n!==null){let r=t.P;do{if(r===e)return!0;if(r===n)break;r=r.D}while(r!==null)}return!1}function qr(e,t){return e.Ce===t||e.Pe?.has(t)?!1:e.Ce?(e.Pe?e.Pe.add(t):e.Pe=new Set([e.Ce,t]),e.Ce=void 0,!0):(e.Ce=t,!0)}function Mr(e,t){return e.Ce?e.Ce!==t?!1:(e.Ce=void 0,!0):e.Pe?.delete(t)?(e.Pe.size===1?(e.Ce=e.Pe.values().next().value,e.Pe=void 0):e.Pe.size===0&&(e.Pe=void 0),!0):!1}function xn(e){e.Ce=void 0,e.Pe?.clear(),e.Pe=void 0}function yt(e,t,n){if(!t){e.Re=null;return}if(n instanceof U&&n.source===t){e.Re=n;return}const r=e.Re;(!(r instanceof U)||r.source!==t)&&(e.Re=new U(t))}function jt(e,t){for(let n=e.I;n!==null;n=n.p)t(n.h);for(let n=e.N;n!==null;n=n.A)for(let r=n.I;r!==null;r=r.p)t(r.h)}function Dr(e){let t=!1;const n=new Set,r=i=>{if(n.has(i)||!Mr(i,e))return;n.add(i),i.Ie=M;const o=i.Ce??i.Pe?.values().next().value;if(o)yt(i,o),Ee(i);else{if(i.he&=~O,yt(i),Ee(i),i.Ge){if(i.J===Ce){const s=i;s.ee||(s.ee=!0,s.te.enqueue(G,s.ne))}else{const s=i.O&J?H:I;s.C>i.o&&(s.C=i.o),Qe(i,s)}t=!0}i.Ge=!1}jt(i,r)};jt(e,r),t&&Se()}function Fr(e,t,n){let r=!1,i=!1;if(typeof t=="object"&&t!==null&&ee(()=>{r=t[Symbol.asyncIterator],i=!r&&typeof t.then=="function"}),!i&&!r)return e.ve=null,t;e.ve=t;let o;const s=l=>{e.ve===t&&(E.initTransition(Be(e)),en(e,l instanceof U?O:W,l),e.Ie=M)},c=(l,d)=>{if(e.ve!==t||e.O&(Pe|$e))return;E.initTransition(Be(e));const h=!!(e.he&F);In(e),Nn(e);const f=Re(e);if(f&&f.F.delete(e),e.K!==void 0)e.K!==void 0&&e.K!==_?e.B=l:(e.ue=l,ke(e)),e.Ie=M;else if(f){const g=e.J,p=e.ue,m=e.ke;(!g&&h||!m||!m(l,p))&&(e.ue=l,e.Ie=M,e.Fe&&ce(e.Fe,l),ke(e,!0))}else ce(e,()=>l);Dr(e),Se(),Ie(),d?.()};if(i){let l=!1,d=!0;if(t.then(h=>{d?(o=h,l=!0):c(h)},h=>{d||s(h)}),d=!1,!l)throw E.initTransition(Be(e)),new U($)}if(r){const l=t[Symbol.asyncIterator]();let d=!1,h=!1;_t(()=>{if(!h){h=!0;try{const p=l.return?.();p&&typeof p.then=="function"&&p.then(void 0,()=>{})}catch{}}});const f=()=>{let p,m=!1,u=!0;return l.next().then(a=>{if(u)p=a,m=!0,a.done&&(h=!0);else{if(e.ve!==t)return;a.done?(h=!0,Se(),Ie()):c(a.value,f)}},a=>{!u&&e.ve===t&&(h=!0,s(a))}),u=!1,m&&!p.done?(o=p.value,d=!0,f()):m&&p.done},g=f();if(!d&&!g)throw E.initTransition(Be(e)),new U($)}return o}function Nn(e,t=!1){(e.Ce||e.Pe)&&xn(e),e.Ge&&(e.Ge=!1),e.he=t?0:e.he&F,e.Re&&yt(e),e.We&&Ee(e),e.xe&&e.xe()}function en(e,t,n,r,i){t===W&&!(n instanceof Ft)&&!(n instanceof U)&&(n=new Ft(e,n));const o=t===O&&n instanceof U?n.source:void 0,s=o===e,c=t===O&&e.K!==void 0&&!s,l=c&&vt(e);r||(t===O&&o?(qr(e,o),e.he=O|e.he&F,yt(e,o,n)):(xn(e),e.he=t|(t!==W?e.he&F:0),e.Re=n),Ee(e)),i&&!r&&Yt(e,i);const d=r||l,h=r||c?void 0:i;if(e.xe){if(r&&t===O)return;d?e.xe(t,n):e.xe();return}jt(e,f=>{f.Ie=M,(t===O&&o&&f.Ce!==o&&!f.Pe?.has(o)||t!==O&&(f.Re!==n||f.Ce||f.Pe))&&(!d&&!f.M&&$t(f),en(f,t,n,d,h))})}let Br=null;X.Ee=ue;X.Se=Xe;let K=!1,ne=!1,$=null,z=null;function ue(e,t=!1){const n=e.J;t||(e.M&&(!n||b)&&b!==e.M&&E.initTransition(e.M),Ne(e,e.O&J?H:I),e.ve=null,e.M||n===Ce?Xe(e):(e.ge!==null||e.be!==null)&&(kn(e),e.Ae=e.be,e.Ne=e.ge,e.be=null,e.ge=null,e.me=0));let r=!!(e.O&$e);const i=e.K!==void 0&&e.K!==_,o=!!(e.he&O),s=!!(e.he&F),c=$;$=e,e.ye=null,e.O=pt,e.Ie=M;let l=e.B===_?e.ue:e.B,d=e.o,h=K,f=z;if(K=!0,r){const u=Re(e);u&&(z=u)}else if(b&&!t&&b.Z.length)for(let u=e.P;u;u=u.D){const a=u.m;if(a.O&$e){const y=Re(a);if(y){r=!0,z=y,e.O|=$e,Yt(e,y);break}}}const g=n&&n!==G,p=ne;g&&(ne=!0);try{if(e.Oe&Jt)l=e.L(l),e.ve=null;else{const u=e.ve,a=e.L(l),y=typeof a=="object"&&a!==null,w=e.ve!==u;l=w||!y?a:Fr(e,a),!w&&!y&&(e.ve=null)}if(Nn(e,t),e.G){const u=Re(e);u&&(u.F.delete(e),Ee(u.k))}}catch(u){if(u instanceof U&&z){const a=V(z);a.k!==e&&(a.F.add(e),e.G=a,Ee(a.k))}u instanceof U&&(e.Ge=!0),en(e,u instanceof U?O:W,u,void 0,u instanceof U?e.G:void 0)}finally{K=h,g&&(ne=p),e.O=En|(t?e.O&Gt:0),$=c}if(!e.Re){In(e);const u=i?e.K:e.B===_?e.ue:e.B,a=!n&&s||!e.ke||!e.ke(u,l);if(n&&a&&(e.ee=!e.Re,t||e.te.enqueue(n,X.Te.bind(null,e))),a){const y=i?e.K:void 0;t||n&&b!==e.M||r?(e.ue=l,i&&r&&(e.K=l,e.B=l)):e.B=l,i&&!r&&o&&!e.$&&(e.K=l),(!i||r||e.K!==y)&&ke(e,r||i)}else if(i)e.B=l;else if(e.o!=d)for(let y=e.I;y!==null;y=y.p)Tn(y.h,y.h.O&J?H:I)}z=f,(e.B!==_||e.Ne!==null||e.Ae!==null||!!(e.he&(O|F)))&&(!t||e.he&O)&&!e.M&&!(b&&i)&&$t(e),e.M&&n&&b!==e.M&&Pr(e.M,()=>ue(e))}function qn(e){if(e.O&Ve)for(let t=e.P;t;t=t.D){const n=t.m,r=n.V||n;if(r.L&&qn(r),e.O&Pe)break}(e.O&(Pe|$e)||e.Re&&e.Ie=(o?H.C:I.C)&&(ft(t),pr(o?H:I),qn(r));const s=r.o;s>=t.o&&e.i!==t&&(t.o=s+1)}if(r.he&O)if(t&&!(ne&&r.M&&b!==r.M))if(z){const o=r.G,s=V(z);if(o&&V(o)===s&&!vt(r))throw!K&&e!==t&&De(e,t),r.Re}else throw!K&&e!==t&&De(e,t),r.Re;else{if(t&&r!==e&&r.he&F)throw!K&&e!==t&&De(e,t),r.Re;if(!t&&r.he&F)throw r.Re}if(e.L&&e.he&W){if(e.Ie0)}return!0}return e.K!==void 0&&e.K===_&&!e.U?!1:e.B!==_&&!(t.he&F)?!0:!!(t.he&O&&!(t.he&F))}function Ee(e){if(e.We){const t=Vr(e),n=e.We;if(ce(n,t),!t&&n.G){const r=Re(e);if(r&&r.F.size>0){const i=V(n.G);i!==r&&An(r,i)}dt.delete(n),n.G=void 0}}}function zr(e,t=!0){const n=ne;ne=t;try{return e()}finally{ne=n}}function Gr(e,t=et()){if(!t)throw new Sn;const n=Hr(e,t)?t.Ve[e.id]:e.defaultValue;if(tn(n))throw new dr;return n}function Jr(e,t,n=et()){if(!n)throw new Sn;n.Ve={...n.Ve,[e.id]:tn(t)?e.defaultValue:t}}function Hr(e,t){return!tn(t?.Ve[e.id])}function tn(e){return typeof e>"u"}function Bn(e,t,n,r){const i=!!r?.user,o=jr(e,t,n,i?G:Ae,Yr,r);ue(o,!0),!r?.defer&&(o.J===G||r?.schedule?o.te.enqueue(o.J,Kt.bind(null,o)):Kt(o))}function Yr(e,t){const n=e!==void 0?e:this.he,r=t!==void 0?t:this.Re;if(n&W){let i=r;if(this.te.notify(this,O,0),this.J===G)try{return this.je?this.je(i,()=>{this.$e?.(),this.$e=void 0}):console.error(i)}catch(o){i=o}if(!this.te.notify(this,W,W))throw i}else this.J===Ae&&this.te.notify(this,O|W,n,r)}function Kt(e){if(!(!e.ee||e.O&ye)){e.$e?.(),e.$e=void 0;try{const t=e.Qe(e.ue,e.Me);e.$e=t,e.$e&&!e.Ke&&(e.Ke=!0,se(e.i,()=>_t(()=>e.$e?.())))}catch(t){if(e.Re=new Ft(e,t),e.he|=W,!e.te.notify(e,W,W))throw t}finally{e.Me=e.ue,e.ee=!1}}}X.Te=Kt;function Zr(e,t){const n=()=>{if(!(!r.ee||r.O&ye))try{r.ee=!1,ue(r)}finally{}},r=St(()=>{r.$e?.(),r.$e=void 0;const i=zr(e);r.$e=i},{...t,lazy:!0});r.$e=void 0,r.Oe=r.Oe&~xe|Cn,r.ee=!0,r.J=Ce,r.xe=(i,o)=>{if((i!==void 0?i:r.he)&W){r.te.notify(r,O,0);const c=o!==void 0?o:r.Re;if(!r.te.notify(r,W,W))throw c}},r.ne=n,r.te.enqueue(G,n),_t(()=>r.$e?.())}function jn(e){return _t(e)}function ge(e){const t=Fn.bind(null,e);return t[mr]=e,t}function Qr(e,t){if(typeof e=="function"){const r=St(e,t);return r.Oe&=~xe,[ge(r),Wr.bind(null,r)]}const n=Ue(e,t);return[ge(n),ce.bind(null,n)]}function _e(e,t){return ge(St(e,t))}function Xr(e,t,n){Bn(e,t.effect||t,t.error,{user:!0,...n})}function ei(e,t,n){Bn(e,t,void 0,n)}function ti(e,t){Zr(e,t)}function ni(e){const t=et();t&&!(t.Oe&Cn)?ti(()=>ee(e),void 0):E.enqueue(G,()=>{e()?.()})}const ri=Symbol(0),Ut=Symbol(0);function an(e){return e==null||typeof e!="object"||Object.isFrozen(e)?!1:typeof Node>"u"||!(e instanceof Node)}const Kn=Symbol(0);function fn(e){return e==="__proto__"||e==="constructor"||e==="prototype"}function We(e,t,n=0){let r,i=e;if(n{We(n,t)}},{DELETE:Kn});function rt(){return!0}const ii={get(e,t,n){return t===Ut?n:e.get(t)},has(e,t){return t===Ut?!0:e.has(t)},set:rt,deleteProperty:rt,getOwnPropertyDescriptor(e,t){return{configurable:!0,enumerable:!0,get(){return e.get(t)},set:rt,deleteProperty:rt}},ownKeys(e){return e.keys()}};function It(e){return(e=typeof e=="function"?e():e)?e:{}}const Lt=Symbol(0);function oi(...e){if(e.length===1&&typeof e[0]!="function")return e[0];let t=!1;const n=[];for(let l=0;l=0;d--){const h=It(n[d]);if(l in h)return h[l]}},has(l){for(let d=n.length-1;d>=0;d--)if(l in It(n[d]))return!0;return!1},keys(){const l=new Set;for(let d=0;d=0;l--){const d=n[l];if(!d){l===o&&o--;continue}const h=Object.getOwnPropertyNames(d);for(let f=h.length-1;f>=0;f--){const g=h[f];if(!(g==="__proto__"||g==="constructor")&&!r[g]){i=i||l!==o;const p=Object.getOwnPropertyDescriptor(d,g);r[g]=p.get?{enumerable:!0,configurable:!0,get:p.get.bind(d)}:p}}}if(!i)return n[o];const s={},c=Object.keys(r);for(let l=c.length-1;l>=0;l--){const d=c[l],h=r[d];h.get?Object.defineProperty(s,d,h):s[d]=h.value}return s[Lt]=n,s}function si(e,t,n){const r=typeof n?.keyed=="function"?n.keyed:void 0,i=t.length>1,o=t,s={Ze:ze(),qe:0,Be:e,ze:[],Xe:o,Je:[],et:[],tt:r,nt:r||n?.keyed===!1?[]:void 0,it:i&&n?.keyed!==!1?[]:void 0,rt:n?.keyed===!1,ot:n?.fallback},c=St(li.bind(s));return s.Ze.u=c,c.Oe&=~xe,ge(c)}const it={ownedWrite:!0};function li(){const e=this.Be()||[],t=e.length;return e[ri],se(this.Ze,()=>{let n,r,i=this.nt?this.rt?()=>(this.nt[r]=Ue(e[r],it),this.Xe(ge(this.nt[r]),r)):()=>(this.nt[r]=Ue(e[r],it),this.it&&(this.it[r]=Ue(r,it)),this.Xe(ge(this.nt[r]),this.it?ge(this.it[r]):void 0)):this.it?()=>{const o=e[r];return this.it[r]=Ue(r,it),this.Xe(o,ge(this.it[r]))}:()=>{const o=e[r];return this.Xe(o)};if(t===0)this.qe!==0&&(this.Ze.dispose(!1),this.et=[],this.ze=[],this.Je=[],this.qe=0,this.nt&&(this.nt=[]),this.it&&(this.it=[])),this.ot&&!this.Je[0]&&(this.Je[0]=se(this.et[0]=ze(),this.ot));else if(this.qe===0){for(this.et[0]&&this.et[0].dispose(),this.Je=new Array(t),r=0;r=o&&c>=o&&(this.ze[s]===e[c]||this.nt&&dn(this.tt,this.ze[s],e[c]));s--,c--)g[c]=this.Je[s],p[c]=this.et[s],m&&(m[c]=this.nt[s]),u&&(u[c]=this.it[s]);for(h=new Map,f=new Array(c+1),r=c;r>=o;r--)l=e[r],d=this.tt?this.tt(l):l,n=h.get(d),f[r]=n===void 0?-1:n,h.set(d,r);for(n=o;n<=s;n++)l=this.ze[n],d=this.tt?this.tt(l):l,r=h.get(d),r!==void 0&&r!==-1?(g[r]=this.Je[n],p[r]=this.et[n],m&&(m[r]=this.nt[n]),u&&(u[r]=this.it[n]),r=f[r],h.set(d,r)):this.et[n].dispose();for(r=o;r{let r=[];return Wt(n,r,{...t,doNotUnwrap:!1}),r}:n}return e}}function Wt(e,t=[],n){let r=null,i=!1;for(let o=0;o(Jr(r,i.value),rn(()=>i.children)))}return r.id=n,r.defaultValue=e,r}function Wn(e){return Gr(e)}function rn(e){const t=_e(e,{lazy:!0}),n=_e(()=>nn(t()),{lazy:!0,sync:!0});return n.toArray=()=>{const r=n();return Array.isArray(r)?r:r!=null?[r]:[]},n}class Te{static{for(const t of["all","allSettled","any","race","reject","resolve"])Te[t]=()=>new Te}catch(){return new Te}then(){return new Te}finally(){return new Te}}const Q=(...e)=>_e(...e),q=(...e)=>Qr(...e),ui=(...e)=>ei(...e),Et=(...e)=>Xr(...e);function L(e,t){return ee(()=>e(t||{}))}const ci=e=>`Stale read from <${e}>.`;function ut(e){const t="fallback"in e?{keyed:e.keyed,fallback:()=>e.fallback}:{keyed:e.keyed};return si(()=>e.each,e.children,t)}function Vn(e){const t=e.keyed,n=_e(()=>e.when,void 0),r=t?n:_e(n,{equals:(i,o)=>!i==!o,sync:!0});return _e(()=>{const i=r();if(i){const o=e.children;return typeof o=="function"&&o.length>0?ee(t?()=>o(i):()=>o(()=>{if(!ee(r))throw ci("Show");return n()})):o}return e.fallback},{sync:!0})}const j=Symbol("slot"),ai={transparent:!0,sync:!0},fi={sync:!0},Y=(e,t,n)=>ui(e,t,n?{transparent:!0,sync:!0,...n}:ai),D=e=>Q(()=>e(),fi);function di(e,t,n,r){let i=n.length,o=t.length,s=i,c=0,l=0,d=t[o-1],h=d[j],f=d.parentNode===e&&(!h||h===r)?d.nextSibling:r||null,g=null,p,m;for(;c=o-1||l>=s)break}while(t[c]===n[s-1]&&n[l]===t[o-1]);else do if(e.insertBefore(t[--o],p),l++,c>=o-1||l>=s)break;while(t[c]===n[s-1]&&n[l]===t[o-1]);else{if(!g){g=new Map;let a=l;for(;au-l){const C=t[c],A=C[j],k=C.parentNode===e&&(!A||A===r)?C:f;for(;l{if(i=o,t===document){const s=e();Y(()=>nn(s),()=>{})}else{const s=e();v(t,()=>s,t.firstChild?null:void 0,n,r.insertOptions)}},{id:r.renderId})}catch(o){throw i&&i(),yn(t),o}return()=>{i(),yn(t),t.textContent=""}}function gi(e,t,n){const r=document.createElement("template");return r.innerHTML=e,n===2?r.content.firstChild.firstChild:r.content.firstChild}function T(e,t){let n;return i=>(n||(n=gi(e,i,t))).cloneNode(!0)}function tt(e){for(let t=0,n=e.length;tzn(r,o,i)))}}function yi(e){const t=mi(e,e);t&&(t.roots=(t.roots||0)+1)}function yn(e){const t=Le.get(e);t&&(t.roots>1?t.roots--:delete t.roots),pi(e,e)}function mi(e,t=e){if(!e||!t)return;let n=Le.get(e);return n||Le.set(e,n={owners:new Map,handlers:new Map}),n.owners.set(t,(n.owners.get(t)||0)+1),Vt.forEach(r=>zn(r,e,n)),n}function pi(e,t=e){const n=Le.get(e);if(!n)return;const r=n.owners.get(t);r>1?n.owners.set(t,r-1):n.owners.delete(t),!n.owners.size&&(n.handlers.forEach((i,o)=>e.removeEventListener(o,i)),Le.delete(e))}function zn(e,t,n){if(n.handlers.has(e))return;const r=i=>vi(i,t,n);n.handlers.set(e,r),t.addEventListener(e,r)}function wi(e,t){let n=e,r=0;for(;n;){if(t.owners.has(n))return{owner:n,distance:r};r++,n=n._$host||n.parentNode||n.host}}function re(e,t,n){n==null||n===!1?e.removeAttribute(t):e.setAttribute(t,n===!0?"":n)}function Ze(e,t,n){if(t==null||t===!1){n&&e.removeAttribute("class");return}if(typeof t=="string"){t!==n&&e.setAttribute("class",t);return}typeof n=="string"?(n={},e.removeAttribute("class")):n=pn(n||{}),t=pn(t);const r=Object.keys(t||{}),i=Object.keys(n);let o,s;for(o=0,s=i.length;oi.call(e,n[1],o))}else e.addEventListener(t,n,typeof n!="function"&&n)}function bi(e,t){Array.isArray(e)?e.flat(1/0).forEach(n=>n&&n(t)):e(t)}function mt(e,t){const n=ee(e);se(null,()=>bi(n,t))}function v(e,t,n,r,i){const o=n!==void 0;if(o&&!r&&(r=[]),typeof t!="function"&&(t=Nt(t,r,o,!0),typeof t!="function"))return xt(e,t,r,n);if(o&&r.length===0){const c=document.createTextNode("");e.insertBefore(c,n),r=[c]}let s=r;Y(c=>{const l=Nt(t(),s,o,!0);return typeof l!="function"?l:(Y(()=>Nt(l,s,o),d=>{xt(e,d,s,n),s=d},c!==void 0&&!(i&&i.schedule)?{...i,schedule:!0}:i),gn)},c=>{c!==gn&&(xt(e,c,s,n),s=c)},i)}function pn(e){if(Array.isArray(e)){const t={};Gn(e,t),e=t}if(e&&typeof e=="object"){const t={},n=Object.keys(e);for(let r=0,i=n.length;rObject.defineProperty(e,"target",{configurable:!0,value:f}),d=()=>{const f=i[o];if(f&&!i.disabled){const g=i[`${o}Data`];if(g!==void 0?f.call(i,g,e):f.call(i,e),e.cancelBubble)return}return i.host&&typeof i.host!="string"&&!i.host._$host&&i.contains(e.target)&&l(i.host),!0},h=()=>{for(;d()&&!(i===c||i.parentNode===c);)i=i._$host||i.parentNode||i.host};if(Object.defineProperty(e,"currentTarget",{configurable:!0,get(){return i||c||document}}),e.composedPath){const f=e.composedPath();if(f.length){l(f[0]);for(let g=0;g=0;o--){const s=t[o];if(r!==s){const c=s[j],l=s.parentNode===e&&(!c||c===n);r&&!i&&!o?l?e.replaceChild(r,s):e.insertBefore(r,n):l&&s.remove()}else i=!0}}else r&&e.insertBefore(r,n);r&&n&&(r[j]=n)}const $i=!1;function _i(e,t,n,r={}){try{const i=hi(e,t,n,{...r,insertOptions:{schedule:!0}});return Ie(),i}finally{}}function Jn(){let e=new Set;function t(i){return e.add(i),()=>e.delete(i)}let n=!1;function r(i,o){if(n)return!(n=!1);const s={to:i,options:o,defaultPrevented:!1,preventDefault:()=>s.defaultPrevented=!0};for(const c of e)c.listener({...s,from:c.location,retry:l=>{l&&(n=!0),c.navigate(i,{...o,resolve:!1})}});return!s.defaultPrevented}return{subscribe:t,confirm:r}}let zt;function on(){(!window.history.state||window.history.state._depth==null)&&window.history.replaceState({...window.history.state,_depth:window.history.length-1},""),zt=window.history.state._depth}on();function Si(e){return{...e,_depth:window.history.state&&window.history.state._depth}}function Ei(e,t){let n=!1;return()=>{const r=zt;on();const i=r==null?null:zt-r;if(n){n=!1;return}i&&t(i)?(n=!0,window.history.go(-i)):e()}}const Ci=/^(?:[a-z0-9]+:)?\/\//i,Oi=/^\/+|(\/)\/+$/g,Hn="http://sr";function Ge(e,t=!1){const n=e.replace(Oi,"$1");return n?t||/^[?#]/.test(n)?n:"/"+n:""}function ct(e,t,n){if(Ci.test(t))return;const r=Ge(e),i=n&&Ge(n);let o="";return!i||t.startsWith("/")?o=r:i.toLowerCase().indexOf(r.toLowerCase())!==0?o=r+i:o=i,(o||"/")+Ge(t,!o)}function Ti(e,t){if(e==null)throw new Error(t);return e}function Ai(e,t){return Ge(e).replace(/\/*(\*.*)?$/g,"")+Ge(t)}function Yn(e){const t={};return e.searchParams.forEach((n,r)=>{r in t?Array.isArray(t[r])?t[r].push(n):t[r]=[t[r],n]:t[r]=n}),t}function Ri(e,t,n){const[r,i]=e.split("/*",2),o=r.split("/").filter(Boolean),s=o.length;return c=>{const l=c.split("/").filter(Boolean),d=l.length-s;if(d<0||d>0&&i===void 0&&!t)return null;const h={path:s?"":"/",params:{}},f=g=>n===void 0?void 0:n[g];for(let g=0;gr===e;return t===void 0?!0:typeof t=="string"?n(t):typeof t=="function"?t(e):Array.isArray(t)?t.some(n):t instanceof RegExp?t.test(e):!1}function Pi(e){const[t,n]=e.pattern.split("/*",2),r=t.split("/").filter(Boolean);return r.reduce((i,o)=>i+(o.startsWith(":")?2:3),r.length-(n===void 0?0:1))}function Zn(e){const t=new Map,n=et();return new Proxy({},{get(r,i){return t.has(i)||se(n,()=>t.set(i,Q(()=>e()[i]))),t.get(i)()},getOwnPropertyDescriptor(){return{enumerable:!0,configurable:!0}},ownKeys(){return Reflect.ownKeys(e())},has(r,i){return i in e()}})}function Qn(e){let t=/(\/?\:[^\/]+)\?/.exec(e);if(!t)return[e];let n=e.slice(0,t.index),r=e.slice(t.index+t[0].length);const i=[n,n+=t[1]];for(;t=/^(\/\:[^\/]+)\?/.exec(r);)i.push(n+=t[1]),r=r.slice(t[0].length);return Qn(r).reduce((o,s)=>[...o,...i.map(c=>c+s)],[])}const ki=100,Xn=Un(),er=Un();function Ii(e){try{return Wn(e)}catch{return}}const tr=()=>Ti(Wn(Xn)," and 'use' router primitives can be only used inside a Route."),nr=()=>tr().navigatorFactory(),Li=()=>tr().params;function xi(e,t=""){const{component:n,preload:r,children:i,info:o}=e,s=!i||Array.isArray(i)&&!i.length,c={key:e,component:n,preload:r,info:o};return rr(e.path).reduce((l,d)=>{for(const h of Qn(d)){const f=Ai(t,h);let g=s?f:f.split("/*",1)[0];g=g.split("/").map(p=>p.startsWith(":")||p.startsWith("*")?p:encodeURIComponent(p)).join("/"),l.push({...c,originalPath:d,pattern:g,matcher:Ri(g,!s,e.matchFilters)})}return l},[])}function Ni(e,t=0){return{routes:e,score:Pi(e[e.length-1])*1e4-t,matcher(n){const r=[];for(let i=e.length-1;i>=0;i--){const o=e[i],s=o.matcher(n);if(!s)return null;r.unshift({...s,route:o})}return r}}}function rr(e){return Array.isArray(e)?e:[e]}function ir(e,t="",n=[],r=[]){const i=rr(e);for(let o=0,s=i.length;os.score-o.score)}function Mt(e,t){for(let n=0,r=e.length;n{const f=e();try{return new URL(f,r)}catch{return console.error(`Invalid path ${f}`),h}},{equals:(h,f)=>h.href===f.href}),o=Q(()=>i().pathname),s=Q(()=>i().search),c=Q(()=>i().hash),l=()=>"",d=Q(()=>Yn(i()));return{get pathname(){return o()},get search(){return s()},get hash(){return c()},get state(){return t()},get key(){return l()},query:n?n(d):Zn(d)}}let ve;function Mi(){return ve}function Di(e,t,n,r={}){const{signal:[i,o],utils:s={}}=e,c=s.parsePath||(P=>P),l=s.renderPath||(P=>P),d=s.beforeLeave||Jn(),h=ct("",r.base||""),f=ee(i);if(h===void 0)throw new Error(`${h} is not a valid base path`);h&&!f.value&&o({value:h,replace:!0,scroll:!1});const[g,p]=q(!1,{ownedWrite:!0}),[m,u]=q(void 0,{ownedWrite:!0});let a;const y=Q(()=>m()??i()),w=qi(()=>y().value,()=>y().state,s.queryWrapper),C=[],A=q([],{ownedWrite:!0}),k=Q(()=>typeof r.transformUrl=="function"?Mt(t(),r.transformUrl(w.pathname)):Mt(t(),w.pathname)),B=()=>{const P=k(),x={};for(let Z=0;Zh,outlet:()=>null,resolvePath(P){return ct(h,P)}};return{base:me,location:w,params:Oe,isRouting:g,renderPath:l,parsePath:c,navigatorFactory:Ot,matches:k,beforeLeave:d,preloadRoute:qe,singleFlight:r.singleFlight===void 0?!0:r.singleFlight,submissions:A};function Ct(P,x,Z){ee(()=>{if(typeof x=="number"){x&&(s.go?s.go(x):console.warn("Router integration does not support relative routing"));return}const pe=!x||x[0]==="?",{replace:ae,resolve:ie,scroll:we,state:fe}={replace:!1,resolve:!pe,scroll:!0,...Z},de=ie?P.resolvePath(x):ct(pe&&w.pathname||"",x);if(de===void 0)throw new Error(`Path '${x}' is not a routable path`);if(C.length>=ki)throw new Error("Too many redirects");const he=y();if((de!==he.value||fe!==he.state)&&!$i){if(d.confirm(de,Z)){C.push({value:he.value,replace:ae,scroll:we,state:he.state});const be={value:de,state:fe};a===void 0&&(p(!0),Ie()),ve="navigate",a=be,a===be&&(u({...a}),queueMicrotask(()=>{a===be&&(ve=void 0,Tt(a),u(void 0),p(!1),a=void 0)}))}}})}function Ot(P){return P=P||Ii(er)||me,(x,Z)=>Ct(P,x,Z)}function Tt(P){const x=C[0];x&&(o({...P,replace:x.replace,scroll:x.scroll}),C.length=0)}function qe(P,x){const Z=Mt(t(),P.pathname),pe=ve;ve="preload";for(let ae in Z){const{route:ie,params:we}=Z[ae];ie.component&&ie.component.preload&&ie.component.preload();const{preload:fe}=ie;x&&fe&&se(n(),()=>fe({params:we,location:{pathname:P.pathname,search:P.search,hash:P.hash,query:Yn(P),state:null,key:""},intent:"preload"}))}ve=pe}}function Fi(e,t,n,r){const{base:i,location:o,params:s}=e,{pattern:c,component:l,preload:d}=r().route,h=Q(()=>r().path);l&&l.preload&&l.preload();const f=d?d({params:s,location:o,intent:ve||"initial"}):void 0;return{parent:t,pattern:c,path:h,outlet:()=>l?L(l,{params:s,location:o,data:f,get children(){return n()}}):n(),resolvePath(p){return ct(i.path(),p,h())}}}const Bi=e=>function(n){const{base:r,singleFlight:i,transformUrl:o,root:s,rootPreload:c,routeChildren:l}=ee(()=>({base:n.base,singleFlight:n.singleFlight,transformUrl:n.transformUrl,root:n.root,rootPreload:n.rootPreload,routeChildren:n.children})),d=rn(()=>l),h=Q(()=>ir(d(),r||""));let f;const g=Di(e,h,()=>f,{base:r,singleFlight:i,transformUrl:o});return e.create&&e.create(g),L(Xn,{value:g,get children(){return L(ji,{routerState:g,root:s,preload:c,get children(){return[D(()=>(f=et())&&null),L(Ki,{routerState:g,get branches(){return h()}})]}})}})};function ji(e){const t=e.routerState.location,n=e.routerState.params,r=Q(()=>e.preload&&ee(()=>{e.preload({params:n,location:t,intent:Mi()||"initial"})})),i=e.root;return i?L(i,{params:n,location:t,get data(){return r()},get children(){return e.children}}):e.children}function Ki(e){const t=[];let n,r;const i=Q(s=>{const c=e.routerState.matches(),l=r;let d=l&&c.length===l.length;const h=[];for(let f=0,g=c.length;f{t[f]=u,h[f]=Fi(e.routerState,h[f-1]||e.routerState.base,bn(()=>i()?.[f+1]),()=>{const a=e.routerState.matches();return a[f]??a[0]})}))}return t.splice(c.length).forEach(f=>f()),s&&d?(r=c,s):(n=h[0],r=c,h)}),o=bn(()=>i()&&n);return D(o)}const bn=e=>()=>{const t=e();if(t)return L(er,{value:t,get children(){return t.outlet()}})},vn=e=>{const t=rn(()=>e.children);return oi(e,{get children(){return t()}})};function Ui(e){let t=!1;const n=s=>typeof s=="string"?{value:s}:s,[r,i]=q(n(e.get()),{equals:(s,c)=>s.value===c.value&&s.state===c.state,ownedWrite:!0}),o=[r,s=>{!t&&e.set(s),i(s)}];return e.init&&jn(e.init((s=e.get())=>{t=!0,o[1](n(s)),t=!1})),Bi({signal:o,create:e.create,utils:e.utils})}function Wi(e,t,n){return e.addEventListener(t,n),()=>e.removeEventListener(t,n)}function Vi(e,t){const n=e&&document.getElementById(e);n?n.scrollIntoView():t&&window.scrollTo(0,0)}const zi=new Map;function Gi({preload:e=!0,explicitLinks:t=!1,actionBase:n="/_server",transformUrl:r}={}){return i=>{const o=i.base.path(),s=i.navigatorFactory(i.base);let c,l;function d(u){return u.namespaceURI==="http://www.w3.org/2000/svg"}function h(u){if(u.defaultPrevented||u.button!==0||u.metaKey||u.altKey||u.ctrlKey||u.shiftKey)return;const a=u.composedPath().find(B=>B instanceof Node&&B.nodeName.toUpperCase()==="A");if(!a||t&&!a.hasAttribute("link"))return;const y=d(a),w=y?a.href.baseVal:a.href;if((y?a.target.baseVal:a.target)||!w&&!a.hasAttribute("state"))return;const A=(a.getAttribute("rel")||"").split(/\s+/);if(a.hasAttribute("download")||A&&A.includes("external"))return;const k=y?new URL(w,document.baseURI):new URL(w);if(!(k.origin!==window.location.origin||o&&k.pathname&&!k.pathname.toLowerCase().startsWith(o.toLowerCase())))return[a,k]}function f(u){const a=h(u);if(!a)return;const[y,w]=a,C=i.parsePath(w.pathname+w.search+w.hash),A=y.getAttribute("state");u.preventDefault(),s(C,{resolve:!1,replace:y.hasAttribute("replace"),scroll:!y.hasAttribute("noscroll"),state:A?JSON.parse(A):void 0})}function g(u){const a=h(u);if(!a)return;const[y,w]=a;r&&(w.pathname=r(w.pathname)),i.preloadRoute(w,y.getAttribute("preload")!=="false")}function p(u){clearTimeout(c);const a=h(u);if(!a)return l=null;const[y,w]=a;l!==y&&(r&&(w.pathname=r(w.pathname)),c=setTimeout(()=>{i.preloadRoute(w,y.getAttribute("preload")!=="false"),l=y},20))}function m(u){if(u.defaultPrevented)return;let a=u.submitter&&u.submitter.hasAttribute("formaction")?u.submitter.getAttribute("formaction"):u.target.getAttribute("action");if(!a)return;if(!a.startsWith("https://action/")){const w=new URL(a,Hn);if(a=i.parsePath(w.pathname+w.search),!a.startsWith(n))return}if(u.target.method.toUpperCase()!=="POST")throw new Error("Only POST forms are supported for Actions");const y=zi.get(a);if(y){u.preventDefault();const w=new FormData(u.target,u.submitter);y.call({r:i,f:u.target},u.target.enctype==="multipart/form-data"?w:new URLSearchParams(w))}}tt(["click","submit"]),document.addEventListener("click",f),e&&(document.addEventListener("mousemove",p,{passive:!0}),document.addEventListener("focusin",g,{passive:!0}),document.addEventListener("touchstart",g,{passive:!0})),document.addEventListener("submit",m),jn(()=>{document.removeEventListener("click",f),e&&(document.removeEventListener("mousemove",p),document.removeEventListener("focusin",g),document.removeEventListener("touchstart",g)),document.removeEventListener("submit",m)})}}function Ji(e){const t=()=>{const r=window.location.pathname.replace(/^\/+/,"/")+window.location.search,i=window.history.state&&window.history.state._depth&&Object.keys(window.history.state).length===1?void 0:window.history.state;return{value:r+window.location.hash,state:i}},n=Jn();return Ui({get:t,set({value:r,replace:i,scroll:o,state:s}){i?window.history.replaceState(Si(s),"",r):window.history.pushState(s,"",r),Vi(decodeURIComponent(window.location.hash.slice(1)),o),on()},init:r=>Wi(window,"popstate",Ei(r,i=>{if(i)return!n.confirm(i);{const o=t();return!n.confirm(o.value,{state:o.state})}})),create:Gi({preload:e.preload,explicitLinks:e.explicitLinks,actionBase:e.actionBase,transformUrl:e.transformUrl}),utils:{go:r=>window.history.go(r),beforeLeave:n}})(e)}var Hi=T('