diff --git a/changelog/unreleased/inflight-queue-visible.md b/changelog/unreleased/inflight-queue-visible.md new file mode 100644 index 00000000..c4336b4c --- /dev/null +++ b/changelog/unreleased/inflight-queue-visible.md @@ -0,0 +1,11 @@ +### English + +- 1. Overview InflightLiveCard selecting (选号) wait rail is visible again: expanded `/api/live` polls at 400ms, 1.6s display dwell (Acquire still immediate), and queue→slot fly-in uses GSAP MotionPath into the bound concurrency cell; cell model/elapsed stays hidden until the fly lands (or a short grace if skipped). +- 2. `/api/live` adds `occupancy` from BoundRequests: each in-flight request maps to its own concurrency cell (model + Bind elapsed). Finishing cells fly to Done/Failed from their own rect (`recent_completions.request_id`), staggered when several finish together — not one shared account animation. +- 3. Worker rail and wait rail use GSAP FLIP for yield/reorder: busy rises with a drag-up feel; remaining wait pills slide up into vacated gaps without fighting the next queue→slot fly. + +### 中文 + +- 1. Overview「实时进行中」选号/排队重新可见:展开后 `/api/live` 400ms 轮询、选号展示约 1.6s(Acquire 不拖延);排队→槽位用 GSAP 弧线滑入 Bind 并发格,模型/计秒等飞入落地后再显示(飞不起来则短 grace)。 +- 2. `/api/live` 增加 `occupancy`(BoundRequests):每个在途请求占一个并发格(各格模型与 Bind 秒数);结束时各格从自身飞向完成/失败(`recent_completions.request_id`),同帧多格会错开——不是整账号共用一个飞出动画。 +- 3. Worker 轨与等待轨用 GSAP FLIP 避让上顶:busy 上浮、空闲下沉;选号 pill 飞走后剩余排队上滑补位,不和下一次飞入打架。 diff --git a/changelog/unreleased/overview-pool-cooldown-chip.md b/changelog/unreleased/overview-pool-cooldown-chip.md index 83738d3b..497bc472 100644 --- a/changelog/unreleased/overview-pool-cooldown-chip.md +++ b/changelog/unreleased/overview-pool-cooldown-chip.md @@ -1,7 +1,7 @@ ### English -- Overview Account pool collapses cooling accounts into a summary chip (`N cooling`); details expand on click with a quiet secondary wash. Ready workers stay primary in the list. +- 4. Overview Account pool collapses cooling accounts into a summary chip (`N cooling`); details expand on click with a quiet secondary wash. Ready workers stay primary in the list. ### 中文 -- Overview「账号池」将冷却账号收成摘要 chip(「N 冷却」),点击后以安静次级底展开明细;列表优先展示就绪 worker。 +- 4. Overview「账号池」将冷却账号收成摘要 chip(「N 冷却」),点击后以安静次级底展开明细;列表优先展示就绪 worker。 diff --git a/frontend/scripts/mockInflight.test.ts b/frontend/scripts/mockInflight.test.ts index a7528f2a..eb633a97 100644 --- a/frontend/scripts/mockInflight.test.ts +++ b/frontend/scripts/mockInflight.test.ts @@ -5,6 +5,7 @@ import { getMockInflightSnapshot, liveHasSignal, MOCK_INFLIGHT, + tickMockInflight, } from '../src/lib/mockInflight.ts' function run(name: string, fn: () => void) { @@ -307,8 +308,6 @@ run('applyLiveProjection maps max_inflight / in_flight to slot cells', () => { assert.equal(idle?.busy, false) }) -console.log('all mockInflight tests passed') - run('applyLiveProjection maps queue onto queued MockJobStatus', () => { const started = new Date(Date.now() - 5_000).toISOString() const snap = applyLiveProjection(getMockInflightSnapshot(), { @@ -341,6 +340,35 @@ run('applyLiveProjection maps queue onto queued MockJobStatus', () => { assert.equal(queued[1].id, 'req-2') }) +run('applyLiveProjection keeps queue accountId for slot handoff', () => { + const snap = applyLiveProjection(getMockInflightSnapshot(), { + in_flight: 1, + rpm: 1, + workers: [ + { + id: 'acc-x', + name: 'wb-x', + provider: 'workbuddy', + busy: true, + progress: 0.5, + rate_limited: false, + }, + ], + rate_limits: [], + sticky: { bindings: 0, hits: 0, misses: 0, capacity: 64, ttl_seconds: 600 }, + queue: [ + { id: 'req-dwell', name: 'gpt · dwell', status: 'started', account_id: 'acc-x' }, + ], + }) + const queued = snap.jobs.filter((j) => j.status === 'queued') + assert.equal(queued.length, 1) + assert.equal(queued[0].id, 'req-dwell') + assert.equal(queued[0].accountId, 'acc-x') + const running = snap.jobs.filter((j) => j.status === 'running') + assert.equal(running.length, 1) + assert.equal(running[0].name, 'gpt · dwell') +}) + run('liveHasSignal true when only queue present', () => { assert.equal( liveHasSignal({ @@ -354,3 +382,181 @@ run('liveHasSignal true when only queue present', () => { true, ) }) + +run('applyLiveProjection occupancy maps per-cell model and started_at', () => { + const t0 = new Date(Date.now() - 8_000).toISOString() + const t1 = new Date(Date.now() - 3_000).toISOString() + const snap = applyLiveProjection(getMockInflightSnapshot(), { + in_flight: 2, + rpm: 1, + workers: [ + { + id: 'acc-m', + name: 'wb-m', + provider: 'workbuddy', + busy: true, + progress: 0.5, + rate_limited: false, + max_inflight: 4, + in_flight: 2, + }, + ], + rate_limits: [], + sticky: { bindings: 0, hits: 0, misses: 0, capacity: 64, ttl_seconds: 600 }, + occupancy: [ + { id: 'req-a', name: 'claude-sonnet', status: 'started', started_at: t0, account_id: 'acc-m' }, + { id: 'req-b', name: 'gpt-4.1', status: 'started', started_at: t1, account_id: 'acc-m' }, + ], + }) + const w = snap.workers.find((x) => x.id === 'acc-m') + assert.ok(w) + assert.equal(w.cells?.length, 2) + assert.equal(w.cells?.[0].id, 'req-a') + assert.equal(w.cells?.[0].name, 'claude-sonnet') + assert.equal(w.cells?.[1].id, 'req-b') + assert.equal(w.cells?.[1].name, 'gpt-4.1') + assert.ok((w.cells?.[0].elapsedMs ?? 0) >= 7_000) + assert.ok((w.cells?.[1].elapsedMs ?? 0) >= 2_000) + assert.ok((w.cells?.[0].elapsedMs ?? 0) > (w.cells?.[1].elapsedMs ?? 0)) + const running = snap.jobs.filter((j) => j.status === 'running') + assert.equal(running.length, 2) + assert.equal(running[0].id, 'req-a') + assert.equal(running[1].name, 'gpt-4.1') +}) + +run('applyLiveProjection occupancy dwell stays on queue not double-running', () => { + const snap = applyLiveProjection(getMockInflightSnapshot(), { + in_flight: 1, + rpm: 1, + workers: [ + { + id: 'acc-d', + name: 'wb-d', + provider: 'workbuddy', + busy: true, + progress: 0.4, + rate_limited: false, + max_inflight: 2, + in_flight: 1, + }, + ], + rate_limits: [], + sticky: { bindings: 0, hits: 0, misses: 0, capacity: 64, ttl_seconds: 600 }, + queue: [{ id: 'req-dwell', name: 'sonnet', status: 'started', account_id: 'acc-d' }], + occupancy: [{ id: 'req-dwell', name: 'sonnet', status: 'started', account_id: 'acc-d' }], + }) + const queued = snap.jobs.filter((j) => j.status === 'queued') + const running = snap.jobs.filter((j) => j.status === 'running') + assert.equal(queued.length, 1) + assert.equal(queued[0].id, 'req-dwell') + assert.equal(running.length, 0) + const w = snap.workers.find((x) => x.id === 'acc-d') + assert.equal(w?.cells?.length, 1) + assert.equal(w?.cells?.[0].id, 'req-dwell') + assert.equal(w?.cells?.[0].name, 'sonnet') + // Dwell-bound cell is a fly target but chrome stays gated until land. + assert.equal(w?.cells?.[0].pendingInbound, true) +}) + +run('applyLiveProjection occupancy without queue is not pendingInbound', () => { + const snap = applyLiveProjection(getMockInflightSnapshot(), { + in_flight: 1, + rpm: 1, + workers: [ + { + id: 'acc-p', + name: 'wb-p', + provider: 'workbuddy', + busy: true, + progress: 0.5, + rate_limited: false, + max_inflight: 2, + in_flight: 1, + }, + ], + rate_limits: [], + sticky: { bindings: 0, hits: 0, misses: 0, capacity: 64, ttl_seconds: 600 }, + occupancy: [{ id: 'req-run', name: 'opus', status: 'started', account_id: 'acc-p' }], + }) + const w = snap.workers.find((x) => x.id === 'acc-p') + assert.equal(w?.cells?.[0].id, 'req-run') + assert.equal(w?.cells?.[0].pendingInbound, false) + assert.equal(snap.jobs.filter((j) => j.status === 'running').length, 1) +}) + +run('applyLiveProjection request_id completion matches that cell', () => { + const busy = applyLiveProjection(cloneMockInflight(MOCK_INFLIGHT), { + in_flight: 2, + rpm: 1, + workers: [ + { + id: 'acc-x', + name: 'wb-x', + provider: 'workbuddy', + busy: true, + progress: 0.5, + rate_limited: false, + max_inflight: 4, + in_flight: 2, + }, + ], + rate_limits: [], + sticky: { bindings: 0, hits: 0, misses: 0, capacity: 64, ttl_seconds: 600 }, + occupancy: [ + { id: 'req-keep', name: 'keep', status: 'started', account_id: 'acc-x' }, + { id: 'req-gone', name: 'gone', status: 'started', account_id: 'acc-x' }, + ], + }) + assert.equal(busy.jobs.filter((j) => j.status === 'running').length, 2) + const after = applyLiveProjection(busy, { + in_flight: 1, + rpm: 1, + workers: [ + { + id: 'acc-x', + name: 'wb-x', + provider: 'workbuddy', + busy: true, + progress: 0.5, + rate_limited: false, + max_inflight: 4, + in_flight: 1, + }, + ], + rate_limits: [], + sticky: { bindings: 0, hits: 0, misses: 0, capacity: 64, ttl_seconds: 600 }, + occupancy: [{ id: 'req-keep', name: 'keep', status: 'started', account_id: 'acc-x' }], + recent_completions: [{ request_id: 'req-gone', account_id: 'acc-x', status: 'error' }], + }) + assert.equal(after.jobs.filter((j) => j.status === 'running').length, 1) + assert.equal(after.jobs.find((j) => j.status === 'running')?.id, 'req-keep') + assert.ok(after.completions?.some((c) => c.id === 'req-gone' && c.ok === false)) + assert.equal(after.workers.find((w) => w.id === 'acc-x')?.cells?.length, 1) +}) + +run('tickMockInflight finishes one cell without clearing siblings', () => { + const base = cloneMockInflight(MOCK_INFLIGHT) + const w = base.workers.find((x) => x.id === 'acc-wb-a1') + assert.ok(w && (w.cells?.length ?? 0) >= 2) + const siblingIds = (w!.cells ?? []).map((c) => c.id).filter((id) => id !== 'j1') + assert.ok(siblingIds.length >= 1) + // Force first cell to completion threshold; leave siblings mid-flight. + for (const j of base.jobs) { + if (j.id === 'j1') j.progress = 0.999 + else if (j.accountId === 'acc-wb-a1') j.progress = 0.4 + } + const next = tickMockInflight(base, 300) + const w2 = next.workers.find((x) => x.id === 'acc-wb-a1') + assert.ok(w2) + // Finished cell is gone (may refill from queue, so length can stay the same). + assert.ok(!(w2!.cells ?? []).some((c) => c.id === 'j1')) + // At least one prior sibling still occupies a cell — account not wiped. + assert.ok(siblingIds.some((id) => (w2!.cells ?? []).some((c) => c.id === id))) + assert.ok((w2!.inFlight ?? 0) >= 1) + // One completion (or retry) for j1 only — not a bulk clear of all cells. + const terminal = next.completions?.filter((c) => c.id === 'j1') ?? [] + const retried = next.jobs.some((j) => j.id === 'j1' && j.status === 'queued') + assert.ok(terminal.length === 1 || retried) +}) + +console.log('all mockInflight tests passed') diff --git a/frontend/src/api/live.ts b/frontend/src/api/live.ts index e4100c1f..7e93db75 100644 --- a/frontend/src/api/live.ts +++ b/frontend/src/api/live.ts @@ -21,22 +21,26 @@ export type LiveWorker = { /** Terminal request finish drained this /api/live poll (Done vs Failed fly-out). */ export type LiveCompletion = { + /** Open request id — per-cell Done/Failed fly-out. */ + request_id?: string account_id: string /** ok | error | incomplete | canceled */ status: string } /** - * Selecting (选号) request for the InflightLiveCard waiting rail. - * status reuses request_logs RequestStatus* (typically started) — not a new machine. + * Selecting (选号) or bound occupancy request for InflightLiveCard. + * status reuses request_logs RequestStatus* — not a new machine. */ export type LiveJob = { id: string name: string - /** RequestStatus* from recorder (started while selecting). */ + /** RequestStatus* from recorder (started while selecting / bound). */ status: string - /** RFC3339 Prepare/Start time. */ + /** RFC3339: queue = Prepare/Start; occupancy = Bind (slot entry). */ started_at?: string + /** Queue: dwell fly target. Occupancy: always set (cell owner). */ + account_id?: string } /** Active cooldown window from AccountView.DownUntil / model_cooldowns. */ @@ -75,8 +79,10 @@ export type LiveSnapshot = { sticky: LiveSticky /** Terminal finishes since last poll — InflightLiveCard routes fly-outs. */ recent_completions?: LiveCompletion[] - /** Prepare→Acquire 选号 (RequestStatusStarted, no pool bind). */ + /** Prepare→Acquire 选号 (RequestStatusStarted / dwell). */ queue?: LiveJob[] + /** Bound open requests — one per concurrency cell (model + per-cell started_at). */ + occupancy?: LiveJob[] } export function fetchLive() { diff --git a/frontend/src/components/overview/InflightLiveCard.tsx b/frontend/src/components/overview/InflightLiveCard.tsx index 81cd25f7..becabda7 100644 --- a/frontend/src/components/overview/InflightLiveCard.tsx +++ b/frontend/src/components/overview/InflightLiveCard.tsx @@ -4,6 +4,7 @@ import { Card } from '@heroui/react' import { ArrowUpRight, CaretRight, Check, X } from '@phosphor-icons/react' import type { LiveSnapshot, LiveSticky } from '@/api/live' import CustomEase from 'gsap/CustomEase' +import { MotionPathPlugin } from 'gsap/MotionPathPlugin' import { Area, AreaChart, @@ -29,16 +30,40 @@ import { CountUp } from '@/components/overview/CountUp' import { ProviderMark } from '@/components/ProviderMark' import { duration, ease, gsap, motionDuration, prefersReducedMotion } from '@/lib/motion' -gsap.registerPlugin(CustomEase) +gsap.registerPlugin(CustomEase, MotionPathPlugin) const queueEase = CustomEase.get('inflightQueue') || CustomEase.create('inflightQueue', '0.22, 1, 0.36, 1') +const slideEase = + CustomEase.get('inflightSlide') || + CustomEase.create('inflightSlide', '0.16, 1, 0.3, 1') /** AffectUI overflow: first N queue pills; rest as kind-colored dots. */ const QUEUE_PILL_MAX = 3 /** AffectUI: show a short worker rail (busy first), not every account. */ const WORKER_VISIBLE_MAX = 4 +/** + * Inflight choreography timings (display-only — Acquire/Bind never delayed): + * selectingMinDwell 1600ms recorder: wait-rail visibility after Start + * LIVE_POLL_EXPANDED_MS 400ms catch several dwell frames while expanded + * QUEUE_TO_SLOT_FLY_S 0.88s GSAP MotionPath slide into the cell + * land hold+fade ~0.30s ghost settle after onLand + * Pipeline: 选号 pill → (dwell) → fly into cell → onLand reveals model/elapsed + * → run → Done/Failed fly-out. Occupied cell chrome stays gated until land + * (or NO_FLY_REVEAL_MS if the fly is skipped — no from-rect). Dwell 1.6s already + * covers one fly + one expanded poll for rail visibility; the flash fix is the + * land gate, not a longer Acquire delay. + * Wait rail: when one pill flies out, remaining slide up (QUEUE_VACANCY_FLIP_S). + * Mid-FLIP another selecting can still fly — vacancy FLIP is interrupt-safe and + * onUpdate keeps wait rectCache fresh for the next fromRect. + */ +const QUEUE_TO_SLOT_FLY_S = 0.88 +/** Wait-rail vacancy / reorder FLIP — remaining pills slide up into the gap. */ +const QUEUE_VACANCY_FLIP_S = 0.56 +/** Reveal occupied chrome when queue→slot fly cannot run (missing rects). */ +const NO_FLY_REVEAL_MS = 140 + type T = (key: string, vars?: Record) => string function formatElapsed(ms: number) { @@ -201,16 +226,22 @@ function DepthChart({ function SlotBar({ slot, - job, + jobById, clockMs, idleLabel, + landedCellIds, }: { slot: MockAccountSlot - job?: MockInflightJob + jobById: Map clockMs: number idleLabel: string + /** Cell ids whose inbound fly has landed (or no-fly grace / auto-land). */ + landedCellIds: ReadonlySet }) { - // In-progress only: no rate-limit / cooldown amber or remaining-time annotations. + // In-progress only: no rate-limit / cooldown amber. Per-cell model + elapsed + // (not one timer for the whole account) when concurrency > 1. + // Occupied chrome waits for land — pendingInbound (dwell) or mid-fly gate — + // so the slot never looks full while the selecting pill is still flying in. const concurrency = clampSlotConcurrency(slot.concurrency ?? 1) const occupiedRaw = slot.inFlight != null @@ -220,9 +251,11 @@ function SlotBar({ : 0 const occupied = Math.min(concurrency, occupiedRaw) const running = occupied > 0 - const label = job?.name ?? idleLabel - const elapsed = jobElapsedMs(job, clockMs) - const cells = Array.from({ length: concurrency }, (_, i) => i < occupied) + const occCells = (slot.cells ?? []).slice(0, occupied) + const titleBits = occCells + .filter((c) => !c.pendingInbound && landedCellIds.has(c.id)) + .map((c) => `${c.name} ${formatElapsed(Math.max(0, clockMs - c.startedAtMs))}`) + const title = `${slot.name} · ${occupied}/${concurrency}${titleBits.length ? ` · ${titleBits.join(', ')}` : ''}` return (
- {/* ProviderMark blends with slot wash — no contrasting wash (same as Overview accounts / supplier tiles). */} - + {slot.name} - {/* Job pill — AffectUI worker row: kind wash + concurrency segments + elapsed */} - - {job ? ( - - ) : null} - {label} - - {/* N equal cells = account concurrency; occupied cells wash + shimmer. */} + {/* N equal cells = account concurrency; each occupied cell shows model + own seconds. */}
- {cells.map((filled, i) => ( -
- {running && filled ? ( - <> - - - - - ) : null} -
- ))} + {Array.from({ length: concurrency }, (_, i) => { + const cell = occCells[i] + const reserved = Boolean(cell) + // Keep data-cell-id / data-slot-fill for fly targeting while chrome is gated. + const showChrome = + reserved && !cell!.pendingInbound && landedCellIds.has(cell!.id) + const job = showChrome ? jobById.get(cell!.id) : undefined + const elapsed = showChrome ? Math.max(0, clockMs - cell!.startedAtMs) : 0 + const model = cell?.name || idleLabel + return ( +
+ {showChrome ? ( + <> + + + {job ? ( + + ) : null} + + {model} + + + + {formatElapsed(elapsed)} + + + ) : ( + — + )} +
+ ) + })}
- - {job || occupied > 0 ? ( - formatElapsed(elapsed) - ) : ( - — - )} -
) } @@ -357,6 +380,41 @@ export function InflightLiveCard({ const [sticky, setSticky] = useState(null) /** Wall clock for live startedAtMs elapsed (mock uses tickMockInflight elapsedMs). */ const [clockMs, setClockMs] = useState(() => Date.now()) + /** + * Display-only land gate: occupied chrome (model + elapsed) shows only after + * the queue→slot fly lands (or no-fly grace / auto-land for cells that never + * flew). Survives clock re-renders that would wipe a GSAP opacity hide. + */ + const [landedCellIds, setLandedCellIds] = useState>(() => new Set()) + const revealTimersRef = useRef(new Map>()) + const markCellLanded = (id: string) => { + const tid = (id || '').trim() + if (!tid) return + const pending = revealTimersRef.current.get(tid) + if (pending) { + clearTimeout(pending) + revealTimersRef.current.delete(tid) + } + setLandedCellIds((prev) => { + if (prev.has(tid)) return prev + const next = new Set(prev) + next.add(tid) + return next + }) + } + const scheduleCellReveal = (id: string) => { + const tid = (id || '').trim() + if (!tid) return + const prev = revealTimersRef.current.get(tid) + if (prev) clearTimeout(prev) + revealTimersRef.current.set( + tid, + setTimeout(() => { + revealTimersRef.current.delete(tid) + markCellLanded(tid) + }, NO_FLY_REVEAL_MS), + ) + } const snap = controlled ? snapshot! : liveSnap const summary = useMemo(() => summarizeMockInflight(snap), [snap]) const rootRef = useRef(null) @@ -481,6 +539,20 @@ export function InflightLiveCard({ if (!expanded) enteredRef.current = false }, [expanded]) + // Drop land-gate + grace timers when the card collapses or unmounts. + useEffect(() => { + if (expanded) return + for (const t of revealTimersRef.current.values()) clearTimeout(t) + revealTimersRef.current.clear() + setLandedCellIds(new Set()) + }, [expanded]) + useEffect(() => { + return () => { + for (const t of revealTimersRef.current.values()) clearTimeout(t) + revealTimersRef.current.clear() + } + }, []) + /** Entrance once — public ui-agq-drop / slot / grow timings. */ useLayoutEffect(() => { const root = rootRef.current @@ -634,6 +706,11 @@ export function InflightLiveCard({ const id = el.getAttribute('data-slot-id') if (id) slotR[id] = el.getBoundingClientRect() }) + // Per-cell rects keyed by request id for queue→slot slide landings. + root.querySelectorAll('[data-cell-id]').forEach((el) => { + const id = el.getAttribute('data-cell-id') + if (id) slotR[`cell:${id}`] = el.getBoundingClientRect() + }) rectCache.current = { wait, slot: slotR, @@ -643,23 +720,85 @@ export function InflightLiveCard({ }, } } + /** Auto-land stable cells; leave queue→slot handoffs gated until fly/grace. */ + const syncLandGate = (mode: 'all' | 'stable') => { + const prevQueued = new Set( + prev.jobs.filter((j) => j.status === 'queued').map((j) => j.id), + ) + setLandedCellIds((prevLanded) => { + const liveIds = new Set() + const next = new Set() + for (const w of snap.workers) { + for (const c of w.cells ?? []) { + const id = c.id + liveIds.add(id) + if (c.pendingInbound) continue + if (prevLanded.has(id)) { + next.add(id) + continue + } + if (mode === 'all') { + next.add(id) + continue + } + const stillQueued = snap.jobs.some( + (j) => j.id === id && j.status === 'queued', + ) + if (stillQueued) continue + // Just left the wait rail — flyQueueToSlot / grace will markLanded. + if (prevQueued.has(id)) continue + next.add(id) + } + } + let same = next.size === prevLanded.size + if (same) { + for (const id of next) { + if (!prevLanded.has(id)) { + same = false + break + } + } + } + // Also detect pruned ids + if (same) { + for (const id of prevLanded) { + if (!liveIds.has(id)) { + same = false + break + } + } + } + return same ? prevLanded : next + }) + } + if (!root || prefersReducedMotion() || !enteredRef.current) { refreshCache() + // No motion / pre-entrance: show chrome immediately (still display-only). + syncLandGate('all') return } if (prev === snap) { refreshCache() + syncLandGate('stable') return } + syncLandGate('stable') + const prevJobs = new Map(prev.jobs.map((j) => [j.id, j])) const nextJobs = new Map(snap.jobs.map((j) => [j.id, j])) - const prevWorkerOf = new Map( - prev.workers.filter((w) => w.jobId).map((w) => [w.jobId!, w.id]), - ) - const nextWorkerOf = new Map( - snap.workers.filter((w) => w.jobId).map((w) => [w.jobId!, w.id]), - ) + // Map every concurrency cell → account (not only the legacy primary jobId). + const prevWorkerOf = new Map() + for (const w of prev.workers) { + if (w.jobId) prevWorkerOf.set(w.jobId, w.id) + for (const c of w.cells ?? []) prevWorkerOf.set(c.id, w.id) + } + const nextWorkerOf = new Map() + for (const w of snap.workers) { + if (w.jobId) nextWorkerOf.set(w.jobId, w.id) + for (const c of w.cells ?? []) nextWorkerOf.set(c.id, w.id) + } const flyGhost = ( from: DOMRect | HTMLElement | null | undefined, @@ -669,10 +808,16 @@ export function InflightLiveCard({ tone: 'run' | 'ok' | 'danger' | 'retry', beatEl?: HTMLElement | null, onLand?: () => void, + /** Stagger when several concurrency cells exit on the same tick (t240u). */ + delaySec = 0, ) => { const a = from instanceof HTMLElement ? from.getBoundingClientRect() : from const b = to instanceof HTMLElement ? to.getBoundingClientRect() : to - if (!a || !b || a.width < 2 || b.width < 2) return + // Missing rects: still invoke onLand so land-gate / grace can reveal chrome. + if (!a || !b || a.width < 2 || b.width < 2) { + onLand?.() + return + } // Opaque solid-glass pill clone (AffectUI: full job pill flies into slot, // not a faint label). Stay readable mid-flight; trail is separate afterimages. @@ -723,19 +868,26 @@ export function InflightLiveCard({ const trailShadow = `inset 0 1px 0 var(--surface-rim), 0 0 14px color-mix(in oklab, ${glowToken} 48%, transparent), 0 0 28px color-mix(in oklab, ${glowToken} 28%, transparent)` - const w = Math.max(a.width, 88) - const h = Math.max(a.height, 26) - // Beat center of the target (slot / Done / Failed). - const targetLeft = b.left + Math.max(0, (b.width - w * 0.92) / 2) - const targetTop = b.top + Math.max(0, (b.height - h) / 2) + const intoSlot = tone === 'run' + // Slot land: shrink toward the concurrency cell; outcomes keep pill size. + const w = intoSlot ? Math.max(a.width, 72) : Math.max(a.width, 88) + const h = intoSlot ? Math.max(a.height, 22) : Math.max(a.height, 26) + const endW = intoSlot ? Math.max(b.width, 40) : undefined + const endH = intoSlot ? Math.max(b.height, 22) : undefined + // Beat center of the target (cell / slot / Done / Failed). + const targetW = endW ?? w * 0.92 + const targetH = endH ?? h + const targetLeft = b.left + Math.max(0, (b.width - targetW) / 2) + const targetTop = b.top + Math.max(0, (b.height - targetH) / 2) // Outcome fly: settle slightly short of the button — don't dive into the control. const settle = tone === 'ok' || tone === 'danger' ? 0.86 : 1 const endLeft = a.left + (targetLeft - a.left) * settle const endTop = a.top + (targetTop - a.top) * settle - // Slightly longer than layout 560ms so the horizontal path reads clearly. - const flyDur = motionDuration(0.72) - const holdDur = motionDuration(0.16) - const fadeDur = motionDuration(0.14) + // Queue→slot: longer horizontal slide so it reads as 滑过去槽里. + const flyDur = motionDuration(intoSlot ? QUEUE_TO_SLOT_FLY_S : 0.72) + const holdDur = motionDuration(intoSlot ? 0.12 : 0.16) + const fadeDur = motionDuration(intoSlot ? 0.18 : 0.14) + const startDelay = motionDuration(Math.max(0, delaySec)) const makePill = (opts: { opacity: number; asTrail?: boolean }) => { const el = document.createElement('div') @@ -795,45 +947,97 @@ export function InflightLiveCard({ // Slot→slot / retry: mild shrink. Slot→Done/Failed: shrink hard toward the // outcome control so the flying pill does not stay slot-sized and ugly. const toOutcome = tone === 'ok' || tone === 'danger' - const endW = toOutcome ? w * 0.55 : w * 0.92 - const flyScale = tone === 'run' || tone === 'retry' ? 0.96 : 0.42 - const fadeScale = tone === 'run' || tone === 'retry' ? 0.9 : 0.28 + const outcomeEndW = toOutcome ? w * 0.55 : w * 0.92 + const landW = intoSlot ? (endW as number) : outcomeEndW + const flyScale = intoSlot ? 1 : tone === 'retry' ? 0.96 : 0.42 + const fadeScale = intoSlot ? 0.82 : tone === 'retry' ? 0.9 : 0.28 const ghost = makePill({ opacity: 1 }) - gsap.set(ghost, { transformOrigin: 'center center' }) + gsap.set(ghost, { transformOrigin: 'center center', x: 0, y: 0 }) + const dx = endLeft - a.left + const dy = endTop - a.top + // Soft arc above the straight line — horizontal slide into the cell. + const arc = intoSlot ? -Math.min(42, 14 + Math.abs(dx) * 0.1) : 0 + // 3 residual afterimages — stagger behind the lead pill (AffectUI trail). const trails = [0.72, 0.46, 0.26].map((op, i) => { - const t = makePill({ opacity: op, asTrail: true }) - gsap.set(t, { transformOrigin: 'center center' }) - gsap.to(t, { - left: endLeft, - top: endTop, - width: endW, - // Outcome trail shrinks with the lead; run trail keeps a motion stretch. - ...(toOutcome - ? { scale: flyScale * (0.92 - i * 0.08) } - : { scaleX: 1.28 + i * 0.1 }), - opacity: 0, - duration: flyDur, - delay: motionDuration(0.04 + i * 0.045), - ease: queueEase, - force3D: true, - onComplete: () => t.remove(), - }) - return t + const tr = makePill({ opacity: op, asTrail: true }) + gsap.set(tr, { transformOrigin: 'center center', x: 0, y: 0 }) + if (intoSlot) { + gsap.to(tr, { + motionPath: { + path: [ + { x: 0, y: 0 }, + { x: dx * 0.45, y: dy * 0.35 + arc }, + { x: dx, y: dy }, + ], + curviness: 1.35, + autoRotate: false, + }, + width: landW, + height: endH, + scaleX: 1.18 + i * 0.08, + opacity: 0, + duration: flyDur, + delay: startDelay + motionDuration(0.05 + i * 0.05), + ease: slideEase, + force3D: true, + onComplete: () => tr.remove(), + }) + } else { + gsap.to(tr, { + left: endLeft, + top: endTop, + width: landW, + ...(toOutcome + ? { scale: flyScale * (0.92 - i * 0.08) } + : { scaleX: 1.28 + i * 0.1 }), + opacity: 0, + duration: flyDur, + delay: startDelay + motionDuration(0.04 + i * 0.045), + ease: queueEase, + force3D: true, + onComplete: () => tr.remove(), + }) + } + return tr }) void trails + const leadVars = intoSlot + ? { + motionPath: { + path: [ + { x: 0, y: 0 }, + { x: dx * 0.45, y: dy * 0.35 + arc }, + { x: dx, y: dy }, + ], + curviness: 1.35, + autoRotate: false, + }, + width: landW, + height: endH, + scale: flyScale, + opacity: 1, + duration: flyDur, + delay: startDelay, + ease: slideEase, + force3D: true, + } + : { + left: endLeft, + top: endTop, + width: landW, + scale: flyScale, + opacity: 1, + duration: flyDur, + delay: startDelay, + ease: queueEase, + force3D: true, + } + gsap.to(ghost, { - left: endLeft, - top: endTop, - width: endW, - scale: flyScale, - // Keep the lead pill opaque for the whole flight — only fade after a short land hold. - opacity: 1, - duration: flyDur, - ease: queueEase, - force3D: true, + ...leadVars, onComplete: () => { // Settle: hairline flash, then playful left-right wiggle + scale // (stuffed-into the Done/Failed pill). Keep shrink fly above. @@ -891,135 +1095,263 @@ export function InflightLiveCard({ const cache = rectCache.current - // Queue → worker: was queued, now running on a slot. + // Queue → worker handoff. + // Mock keeps the same job id (queued→running). Live /api/live uses request + // ids on the wait rail and synthetic live-${accountId} on slots, so we also + // fly when a queued job leaves and we know its bind target (accountId from + // selectingMinDwell) — or a single newly-busy slot heuristic. const flewFromQueue = new Set() - for (const [id, job] of nextJobs) { - const before = prevJobs.get(id) - if (!before || before.status !== 'queued' || job.status !== 'running') continue - const workerId = nextWorkerOf.get(id) - if (!workerId) continue + const flyQueueToSlot = ( + id: string, + label: string, + kind: MockJobKind, + workerId: string, + ) => { + if (!workerId || flewFromQueue.has(id)) return flewFromQueue.add(id) const fromRect = cache.wait[id] const toEl = root.querySelector(`[data-slot-id="${CSS.escape(workerId)}"]`) - const toRect = toEl?.getBoundingClientRect() ?? cache.slot[workerId] - // Hide occupy chrome until land (AffectUI receive/occupy on land). - const landPill = toEl?.querySelector('[data-slot-job-pill]') ?? null - const landFills = toEl - ? Array.from(toEl.querySelectorAll('[data-slot-fill]')) - : [] - if (landPill) gsap.set(landPill, { opacity: 0, scale: 0.85 }) - landFills.forEach((el) => gsap.set(el, { opacity: 0, scaleX: 0.35, transformOrigin: 'left center' })) - flyGhost(fromRect, toRect, job.name, job.kind, 'run', null, () => { + // Prefer the specific concurrency cell for this request — 滑过去槽里. + const cellEl = + toEl?.querySelector(`[data-cell-id="${CSS.escape(id)}"]`) ?? + toEl?.querySelector('[data-slot-fill]:last-of-type') ?? + null + const toRect = + cellEl?.getBoundingClientRect() ?? + toEl?.getBoundingClientRect() ?? + cache.slot[workerId] + const landCell = cellEl + const canFly = + Boolean(fromRect && toRect && fromRect.width >= 2 && toRect.width >= 2) + // React land-gate keeps chrome empty until onLand (GSAP opacity alone is + // wiped by the ~150ms elapsed clock re-render). No from-rect → short grace. + if (!canFly) { + scheduleCellReveal(id) + return + } + flyGhost(fromRect, toRect, label, kind, 'run', null, () => { + markCellLanded(id) if (!toEl) return gsap.fromTo( toEl, - { filter: 'brightness(1.35)' }, - { filter: 'brightness(1)', duration: motionDuration(0.42), ease: ease.soft }, + { filter: 'brightness(1.28)' }, + { filter: 'brightness(1)', duration: motionDuration(0.48), ease: ease.soft }, ) - if (landPill) { - gsap.to(landPill, { - opacity: 1, - scale: 1, - duration: motionDuration(0.42), - ease: queueEase, - }) + if (landCell) { + gsap.fromTo( + landCell, + { scale: 0.92, transformOrigin: 'left center' }, + { scale: 1, duration: motionDuration(0.48), ease: slideEase }, + ) } - landFills.forEach((el, i) => { - gsap.to(el, { - opacity: 1, - scaleX: 1, - duration: motionDuration(0.56), - ease: queueEase, - delay: motionDuration(i * 0.03), - }) - }) }) } - // Queue vacancy FLIP — remaining pills/dots close the gap after fly-out (560ms). - if (flewFromQueue.size) { - const waitEls = root.querySelectorAll('[data-wait-pill][data-wait-id]') - waitEls.forEach((el) => { - const id = el.dataset.waitId - if (!id || flewFromQueue.has(id)) return - const first = cache.wait[id] - if (!first) return - const last = el.getBoundingClientRect() - const dx = first.left - last.left - const dy = first.top - last.top - if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5) return - gsap.fromTo( - el, - { x: dx, y: dy }, - { x: 0, y: 0, duration: motionDuration(0.56), ease: queueEase, force3D: true }, - ) - }) - // Overflow dots that promote into pill slots also FLIP from token rects. - const tokens = root.querySelectorAll('[data-wait-token][data-wait-id]') - tokens.forEach((el) => { - const id = el.dataset.waitId - if (!id) return - const first = cache.wait[id] - if (!first) return - const last = el.getBoundingClientRect() - const dx = first.left - last.left - const dy = first.top - last.top - if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5) return - gsap.fromTo( - el, - { x: dx, y: dy, scale: 1.15 }, - { x: 0, y: 0, scale: 1, duration: motionDuration(0.56), ease: queueEase }, - ) - }) + for (const [id, job] of nextJobs) { + const before = prevJobs.get(id) + if (!before || before.status !== 'queued' || job.status !== 'running') continue + const workerId = nextWorkerOf.get(id) || job.accountId || before.accountId + if (!workerId) continue + flyQueueToSlot(id, job.name, job.kind, workerId) } - // Worker → retry queue (same id back to queued) OR DONE/FAILED. + const newlyBusy = snap.workers.filter( + (w) => w.busy && !prev.workers.some((p) => p.id === w.id && p.busy), + ) for (const [id, before] of prevJobs) { - if (before.status !== 'running') continue - const still = nextJobs.get(id) - const workerId = prevWorkerOf.get(id) - const fromRect = workerId ? cache.slot[workerId] : undefined + if (before.status !== 'queued') continue + if (nextJobs.has(id)) continue + let workerId = (before.accountId || '').trim() + if (!workerId && newlyBusy.length === 1) workerId = newlyBusy[0].id + if (!workerId) continue + // Prefer a visible busy slot; skip if target never appears. + const target = snap.workers.find((w) => w.id === workerId) + if (target && !target.busy && newlyBusy.every((w) => w.id !== workerId)) continue + flyQueueToSlot(id, before.name, before.kind, workerId) + } + + // Left the wait rail but no fly was scheduled (unknown bind target) — grace reveal. + for (const [id, before] of prevJobs) { + if (before.status !== 'queued') continue + if (nextJobs.has(id)) continue + if (flewFromQueue.has(id)) continue + // Still present as a cell somewhere? + const hasCell = snap.workers.some((w) => (w.cells ?? []).some((c) => c.id === id)) + if (hasCell) scheduleCellReveal(id) + } - // Retry: running → queued (AffectUI: retry slides back). - if (still && still.status === 'queued') { - const toEl = - root.querySelector(`[data-wait-id="${CSS.escape(id)}"]`) ?? - root.querySelector('[data-queue-col]') - const toRect = toEl?.getBoundingClientRect() - flyGhost(fromRect, toRect, before.name, before.kind, 'retry', null, () => { - const pill = root.querySelector(`[data-wait-id="${CSS.escape(id)}"]`) - if (!pill) return + // Queue vacancy FLIP (t244u) — remaining wait pills/dots slide up into the gap + // when any selecting item leaves (fly or not). Interrupt-safe: kill prior FLIP, + // measure visual→layout so a mid-FLIP pill can still fly out next tick without + // fighting transforms. onUpdate keeps rectCache.wait fresh for that fromRect. + { + const leftQueue = new Set() + for (const [id, before] of prevJobs) { + if (before.status !== 'queued') continue + const still = nextJobs.get(id) + if (still && still.status === 'queued') continue + leftQueue.add(id) + } + if (leftQueue.size) { + const flipDur = motionDuration(QUEUE_VACANCY_FLIP_S) + const trackWaitRect = (el: HTMLElement) => { + const id = el.dataset.waitId + if (!id) return + rectCache.current.wait[id] = el.getBoundingClientRect() + } + const flipWaitEl = (el: HTMLElement, opts?: { promote?: boolean }) => { + const id = el.dataset.waitId + if (!id || leftQueue.has(id)) return + // Visual includes any in-flight FLIP transform. + const visual = el.getBoundingClientRect() + gsap.killTweensOf(el) + gsap.set(el, { x: 0, y: 0, scale: 1 }) + const layout = el.getBoundingClientRect() + const dx = visual.left - layout.left + const dy = visual.top - layout.top + if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5) { + trackWaitRect(el) + return + } gsap.fromTo( - pill, - { opacity: 0.4, scale: 0.85 }, - { opacity: 1, scale: 1, duration: motionDuration(0.42), ease: queueEase }, + el, + { + x: dx, + y: dy, + ...(opts?.promote ? { scale: 1.15 } : {}), + }, + { + x: 0, + y: 0, + scale: 1, + duration: flipDur, + ease: queueEase, + force3D: true, + onUpdate: () => trackWaitRect(el), + onComplete: () => { + gsap.set(el, { clearProps: 'transform' }) + trackWaitRect(el) + }, + }, ) + } + root + .querySelectorAll('[data-wait-pill][data-wait-id]') + .forEach((el) => flipWaitEl(el)) + // Overflow dots (and dots promoting toward pill slots) close the gap too. + root + .querySelectorAll('[data-wait-token][data-wait-id]') + .forEach((el) => flipWaitEl(el, { promote: true })) + } + } + + // t240u: each concurrency cell flies out on its own. Drive exits from + // prev.cells (not one shared account animation). Same-tick multi-cell + // finishes stagger slightly so three flights read as three. + { + const nextCellIds = new Set() + for (const w of snap.workers) { + for (const c of w.cells ?? []) nextCellIds.add(c.id) + } + type CellExit = { + id: string + name: string + kind: MockJobKind + workerId: string + cellIndex: number + } + const exits: CellExit[] = [] + const seenExit = new Set() + for (const w of prev.workers) { + const cells = w.cells ?? [] + cells.forEach((c, cellIndex) => { + if (nextCellIds.has(c.id) || seenExit.has(c.id)) return + const still = nextJobs.get(c.id) + if (still?.status === 'queued' || still?.status === 'running') return + seenExit.add(c.id) + const job = prevJobs.get(c.id) + exits.push({ + id: c.id, + name: c.name || job?.name || w.name, + kind: job?.kind ?? 'chat', + workerId: w.id, + cellIndex, + }) + }) + } + // Legacy single-slot jobs that never got a cells[] entry. + for (const [id, before] of prevJobs) { + if (before.status !== 'running') continue + if (seenExit.has(id) || nextCellIds.has(id)) continue + const still = nextJobs.get(id) + if (still?.status === 'queued' || still?.status === 'running') continue + seenExit.add(id) + exits.push({ + id, + name: before.name, + kind: before.kind, + workerId: prevWorkerOf.get(id) ?? '', + cellIndex: 0, }) - continue } - if (still && still.status === 'running') continue - // Prefer per-job completion (mock tick + live recent_completions / - // last_error_kind). Totals bump is a last-resort fallback only. - const comp = snap.completions?.find((c) => c.id === id) - let tone: 'ok' | 'danger' - if (comp) { - tone = comp.ok ? 'ok' : 'danger' - } else if (still?.status === 'failed') { - tone = 'danger' - } else if (still?.status === 'done') { - tone = 'ok' - } else { - const okBump = snap.totals.done > prev.totals.done - const failBump = snap.totals.failed > prev.totals.failed - // fail-only bump → Failed; otherwise Done (ambiguous same-tick needs completions). - tone = failBump && !okBump ? 'danger' : 'ok' + const byWorker = new Map() + for (const e of exits) { + const list = byWorker.get(e.workerId) ?? [] + list.push(e) + byWorker.set(e.workerId, list) } - const beat = root.querySelector( - tone === 'ok' ? '[data-outcome="ok"]' : '[data-outcome="danger"]', - ) - const toRect = beat?.getBoundingClientRect() ?? cache.outcome[tone] - flyGhost(fromRect, toRect, before.name, before.kind, tone, beat) + for (const list of byWorker.values()) { + list.sort((a, b) => a.cellIndex - b.cellIndex) + list.forEach((e, i) => { + const fromRect = + cache.slot[`cell:${e.id}`] ?? + (e.workerId ? cache.slot[e.workerId] : undefined) + const still = nextJobs.get(e.id) + const comp = snap.completions?.find((c) => c.id === e.id) + let tone: 'ok' | 'danger' + if (comp) { + tone = comp.ok ? 'ok' : 'danger' + } else if (still?.status === 'failed') { + tone = 'danger' + } else if (still?.status === 'done') { + tone = 'ok' + } else { + const okBump = snap.totals.done > prev.totals.done + const failBump = snap.totals.failed > prev.totals.failed + tone = failBump && !okBump ? 'danger' : 'ok' + } + const beat = root.querySelector( + tone === 'ok' ? '[data-outcome="ok"]' : '[data-outcome="danger"]', + ) + const toRect = beat?.getBoundingClientRect() ?? cache.outcome[tone] + flyGhost(fromRect, toRect, e.name, e.kind, tone, beat, undefined, i * 0.1) + }) + } + } + + // Retry: running → queued (AffectUI: retry slides back). One flight per id. + for (const [id, before] of prevJobs) { + if (before.status !== 'running') continue + const still = nextJobs.get(id) + if (!still || still.status !== 'queued') continue + const workerId = prevWorkerOf.get(id) + const fromRect = + cache.slot[`cell:${id}`] ?? + (workerId ? cache.slot[workerId] : undefined) + const toEl = + root.querySelector(`[data-wait-id="${CSS.escape(id)}"]`) ?? + root.querySelector('[data-queue-col]') + const toRect = toEl?.getBoundingClientRect() + flyGhost(fromRect, toRect, before.name, before.kind, 'retry', null, () => { + const pill = root.querySelector(`[data-wait-id="${CSS.escape(id)}"]`) + if (!pill) return + gsap.fromTo( + pill, + { opacity: 0.4, scale: 0.85 }, + { opacity: 1, scale: 1, duration: motionDuration(0.42), ease: queueEase }, + ) + }) } // New wait pills drop in (AffectUI: new jobs drop at the back). @@ -1050,19 +1382,37 @@ export function InflightLiveCard({ prevQueued.length === nextQueued.length && prevQueued.some((id, i) => id !== nextQueued[i]) if (orderChanged) { + const flipDur = motionDuration(QUEUE_VACANCY_FLIP_S) for (const id of nextQueued) { const el = root.querySelector(`[data-wait-id="${CSS.escape(id)}"]`) if (!el) continue - const first = cache.wait[id] - if (!first) continue - const last = el.getBoundingClientRect() - const dx = first.left - last.left - const dy = first.top - last.top - if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5) continue + const visual = el.getBoundingClientRect() + gsap.killTweensOf(el) + gsap.set(el, { x: 0, y: 0 }) + const layout = el.getBoundingClientRect() + const dx = visual.left - layout.left + const dy = visual.top - layout.top + if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5) { + rectCache.current.wait[id] = layout + continue + } gsap.fromTo( el, { x: dx, y: dy }, - { x: 0, y: 0, duration: motionDuration(0.56), ease: queueEase, force3D: true }, + { + x: 0, + y: 0, + duration: flipDur, + ease: queueEase, + force3D: true, + onUpdate: () => { + rectCache.current.wait[id] = el.getBoundingClientRect() + }, + onComplete: () => { + gsap.set(el, { clearProps: 'transform' }) + rectCache.current.wait[id] = el.getBoundingClientRect() + }, + }, ) } } @@ -1088,6 +1438,91 @@ export function InflightLiveCard({ } } + // t241u: worker rail FLIP — busy rises / idle drops with 避让上顶 yield. + // Rising row leads; yielding rows lag slightly so it reads as drag-up. + { + const orderOf = (workers: typeof snap.workers) => { + const busy = workers.filter((w) => w.busy) + const available = workers.filter((w) => !w.busy) + const out: string[] = [] + const seen = new Set() + for (const w of [...busy, ...available]) { + if (seen.has(w.id)) continue + seen.add(w.id) + out.push(w.id) + if (out.length >= WORKER_VISIBLE_MAX) break + } + return out + } + const prevOrder = orderOf(prev.workers) + const nextOrder = orderOf(snap.workers) + const orderChanged = + prevOrder.length !== nextOrder.length || + prevOrder.some((id, i) => id !== nextOrder[i]) + if (orderChanged) { + const prevIdx = new Map(prevOrder.map((id, i) => [id, i])) + for (let ni = 0; ni < nextOrder.length; ni++) { + const id = nextOrder[ni] + const el = root.querySelector(`[data-slot-id="${CSS.escape(id)}"]`) + const first = cache.slot[id] + if (!el || !first) continue + const last = el.getBoundingClientRect() + const dx = first.left - last.left + const dy = first.top - last.top + if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5) continue + const pi = prevIdx.get(id) + const rising = pi != null && ni < pi + const falling = pi != null && ni > pi + // Drag-up: riser starts immediately + slight scale; yielders lag. + const delay = motionDuration(rising ? 0 : falling ? 0.07 : 0.04) + const dur = motionDuration(rising ? 0.72 : 0.64) + gsap.killTweensOf(el) + gsap.fromTo( + el, + { + x: dx, + y: dy, + scale: rising ? 1.035 : 1, + zIndex: rising ? 6 : falling ? 2 : 3, + }, + { + x: 0, + y: 0, + scale: 1, + zIndex: 1, + duration: dur, + delay, + ease: queueEase, + force3D: true, + onComplete: () => { + gsap.set(el, { clearProps: 'transform,zIndex' }) + }, + }, + ) + } + // Newly visible at the bottom: slide up into place (cooling dropped off). + for (const id of nextOrder) { + if (prevIdx.has(id)) continue + const el = root.querySelector(`[data-slot-id="${CSS.escape(id)}"]`) + if (!el) continue + gsap.killTweensOf(el) + gsap.fromTo( + el, + { opacity: 0.35, y: 14, scale: 0.96 }, + { + opacity: 1, + y: 0, + scale: 1, + duration: motionDuration(0.56), + delay: motionDuration(0.08), + ease: queueEase, + onComplete: () => gsap.set(el, { clearProps: 'transform' }), + }, + ) + } + } + } + refreshCache() }, [snap, expanded]) @@ -1289,14 +1724,15 @@ export function InflightLiveCard({ {/* Vertical worker rail — growing bars, AffectUI layout */} -
+
{visibleWorkers.map((slot) => ( ))}
diff --git a/frontend/src/hooks/useLive.ts b/frontend/src/hooks/useLive.ts index 35eb3a63..acfbc08a 100644 --- a/frontend/src/hooks/useLive.ts +++ b/frontend/src/hooks/useLive.ts @@ -7,11 +7,17 @@ export const LIVE_SAMPLES = Math.round(60_000 / LIVE_POLL_MS) export type LiveSample = { at: number; value: number } +/** Faster poll while InflightLiveCard is expanded — selecting dwell is ~1.6s. */ +export const LIVE_POLL_EXPANDED_MS = 400 + /** - * Polls /api/live every 2.5s while the document is visible. Keeps the last - * good snapshot on error so the strip never flashes back to a placeholder. + * Polls /api/live while the document is visible. Keeps the last good snapshot + * on error so the strip never flashes back to a placeholder. + * Pass a shorter `intervalMs` while the inflight card is open so selecting / + * queue→slot handoffs are not missed between the default 2.5s ticks. */ -export function useLive() { +export function useLive(opts?: { intervalMs?: number }) { + const intervalMs = opts?.intervalMs ?? LIVE_POLL_MS const [live, setLive] = useState(null) const [samples, setSamples] = useState([]) @@ -24,14 +30,16 @@ export function useLive() { .then((data) => { if (cancelled) return setLive(data) - setSamples((prev) => [...prev, { at: Date.now(), value: data.in_flight }].slice(-LIVE_SAMPLES)) + // Keep ~60s of sparkline history even when expanded poll is faster. + const keep = Math.max(LIVE_SAMPLES, Math.round(60_000 / intervalMs)) + setSamples((prev) => [...prev, { at: Date.now(), value: data.in_flight }].slice(-keep)) }) .catch(() => { /* keep last good snapshot */ }) } const start = () => { if (timer || document.hidden) return tick() - timer = setInterval(tick, LIVE_POLL_MS) + timer = setInterval(tick, intervalMs) } const stop = () => { if (timer) clearInterval(timer) @@ -45,7 +53,7 @@ export function useLive() { stop() document.removeEventListener('visibilitychange', onVisibility) } - }, []) + }, [intervalMs]) return { live, samples } } diff --git a/frontend/src/lib/mockInflight.ts b/frontend/src/lib/mockInflight.ts index 0b2a6752..38117e89 100644 --- a/frontend/src/lib/mockInflight.ts +++ b/frontend/src/lib/mockInflight.ts @@ -41,6 +41,22 @@ export type MockInflightJob = { attempt?: number } +/** One occupied concurrency cell on an account slot bar. */ +export type MockSlotCell = { + id: string + /** Requested model / short label. */ + name: string + /** Wall-clock when this request entered the slot (Bind). */ + startedAtMs: number + elapsedMs: number + /** + * Still on the selecting wait rail (display dwell) — keep cell DOM for the + * queue→slot fly target, but InflightLiveCard must not show occupied chrome + * (model + elapsed) until the inbound fly lands. + */ + pendingInbound?: boolean +} + export type MockAccountSlot = { id: string /** Account display name. */ @@ -60,6 +76,8 @@ export type MockAccountSlot = { concurrency?: number /** Current occupied concurrency cells (in_flight). */ inFlight?: number + /** Per-cell model + elapsed (length ≤ inFlight). */ + cells?: MockSlotCell[] } export type MockRateLimitWindow = { @@ -110,18 +128,40 @@ export const MOCK_INFLIGHT: MockInflightSnapshot = { waitP95Ms: 2800, totals: { done: 186, failed: 4 }, workers: [ - { id: 'acc-wb-a1', name: 'wb-a1', provider: 'workbuddy', busy: true, progress: 0.72, jobId: 'j1', concurrency: 4, inFlight: 3 }, - { id: 'acc-wb-a2', name: 'wb-a2', provider: 'workbuddy', busy: true, progress: 0.41, jobId: 'j2', rateLimited: true, concurrency: 2, inFlight: 1 }, - { id: 'acc-qd-b1', name: 'qd-b1', provider: 'qoder', busy: true, progress: 0.88, jobId: 'j3', concurrency: 1, inFlight: 1 }, - { id: 'acc-qd-b2', name: 'qd-b2', provider: 'qoder', busy: false, progress: 0, concurrency: 4, inFlight: 0 }, - { id: 'acc-trae-c1', name: 'trae-c1', provider: 'trae', busy: true, progress: 0.23, jobId: 'j4', concurrency: 3, inFlight: 2 }, - { id: 'acc-codex-d1', name: 'codex-d1', provider: 'codex', busy: false, progress: 0, rateLimited: true, concurrency: 2, inFlight: 0 }, + { + id: 'acc-wb-a1', name: 'wb-a1', provider: 'workbuddy', busy: true, progress: 0.72, jobId: 'j1', concurrency: 4, inFlight: 3, + cells: [ + { id: 'j1', name: 'chat · sonnet', startedAtMs: 0, elapsedMs: 11_200 }, + { id: 'j1b', name: 'chat · opus', startedAtMs: 0, elapsedMs: 8400 }, + { id: 'j1c', name: 'embed · idx', startedAtMs: 0, elapsedMs: 4100 }, + ], + }, + { + id: 'acc-wb-a2', name: 'wb-a2', provider: 'workbuddy', busy: true, progress: 0.41, jobId: 'j2', rateLimited: true, concurrency: 2, inFlight: 1, + cells: [{ id: 'j2', name: 'embed · batch', startedAtMs: 0, elapsedMs: 9400 }], + }, + { + id: 'acc-qd-b1', name: 'qd-b1', provider: 'qoder', busy: true, progress: 0.88, jobId: 'j3', concurrency: 1, inFlight: 1, + cells: [{ id: 'j3', name: 'chat · gpt', startedAtMs: 0, elapsedMs: 6200 }], + }, + { id: 'acc-qd-b2', name: 'qd-b2', provider: 'qoder', busy: false, progress: 0, concurrency: 4, inFlight: 0, cells: [] }, + { + id: 'acc-trae-c1', name: 'trae-c1', provider: 'trae', busy: true, progress: 0.23, jobId: 'j4', concurrency: 3, inFlight: 2, + cells: [ + { id: 'j4', name: 'tools · patch', startedAtMs: 0, elapsedMs: 3100 }, + { id: 'j4b', name: 'chat · flash', startedAtMs: 0, elapsedMs: 1800 }, + ], + }, + { id: 'acc-codex-d1', name: 'codex-d1', provider: 'codex', busy: false, progress: 0, rateLimited: true, concurrency: 2, inFlight: 0, cells: [] }, ], jobs: [ { id: 'j1', name: 'chat · sonnet', kind: 'chat', status: 'running', accountId: 'acc-wb-a1', progress: 0.72, elapsedMs: 11_200, sticky: true }, + { id: 'j1b', name: 'chat · opus', kind: 'chat', status: 'running', accountId: 'acc-wb-a1', progress: 0.55, elapsedMs: 8400 }, + { id: 'j1c', name: 'embed · idx', kind: 'embed', status: 'running', accountId: 'acc-wb-a1', progress: 0.3, elapsedMs: 4100 }, { id: 'j2', name: 'embed · batch', kind: 'embed', status: 'running', accountId: 'acc-wb-a2', progress: 0.41, elapsedMs: 9400 }, { id: 'j3', name: 'chat · gpt', kind: 'chat', status: 'running', accountId: 'acc-qd-b1', progress: 0.88, elapsedMs: 6200 }, { id: 'j4', name: 'tools · patch', kind: 'tools', status: 'running', accountId: 'acc-trae-c1', progress: 0.23, elapsedMs: 3100 }, + { id: 'j4b', name: 'chat · flash', kind: 'chat', status: 'running', accountId: 'acc-trae-c1', progress: 0.15, elapsedMs: 1800 }, { id: 'j5', name: 'chat · wait', kind: 'chat', status: 'queued', progress: 0, elapsedMs: 7200 }, { id: 'j6', name: 'retry · cool', kind: 'chat', status: 'queued', progress: 0, elapsedMs: 5100 }, { id: 'j7', name: 'chat · sticky', kind: 'chat', status: 'queued', progress: 0, elapsedMs: 2800, sticky: true, priority: 'high' }, @@ -153,7 +193,10 @@ let seq = 100 export function cloneMockInflight(snap: MockInflightSnapshot = MOCK_INFLIGHT): MockInflightSnapshot { return { ...snap, - workers: snap.workers.map((w) => ({ ...w })), + workers: snap.workers.map((w) => ({ + ...w, + cells: w.cells?.map((c) => ({ ...c })), + })), jobs: snap.jobs.map((j) => ({ ...j })), rateLimits: snap.rateLimits.map((r) => ({ ...r })), depthSeries: [...snap.depthSeries], @@ -190,39 +233,54 @@ export function tickMockInflight(prev: MockInflightSnapshot, dtMs: number): Mock const jobById = new Map(next.jobs.map((j) => [j.id, j])) - // Progress + elapsed on busy non-frozen slots. + // Per-cell elapsed + independent progress (rate-limit freezes the account). for (const w of next.workers) { - if (!w.busy || !w.jobId) continue - const job = jobById.get(w.jobId) - if (!job || job.status !== 'running') continue - job.elapsedMs += dt - if (w.rateLimited) { - // Amber freeze — no progress (AffectUI: bars stop + amber). - continue + if (w.cells?.length) { + for (const c of w.cells) c.elapsedMs += dt + } + if (w.rateLimited) continue + let maxP = 0 + for (const c of w.cells ?? []) { + const job = jobById.get(c.id) + if (!job || job.status !== 'running') continue + job.elapsedMs = c.elapsedMs + // ~8–14s per cell from current progress, paced by dt + cell id. + const speed = 0.045 + (c.id.charCodeAt(c.id.length - 1) % 5) * 0.008 + job.progress = Math.min(1, job.progress + speed * (dt / 1000)) + maxP = Math.max(maxP, job.progress) + } + if ((w.cells?.length ?? 0) > 0) { + w.progress = maxP + w.busy = true + w.jobId = w.cells![0].id + w.inFlight = w.cells!.length } - // ~8–14s to complete from current progress, paced by dt. - const speed = 0.045 + (w.id.charCodeAt(w.id.length - 1) % 5) * 0.008 - w.progress = Math.min(1, w.progress + speed * (dt / 1000)) - job.progress = w.progress } - // Complete / fail when progress hits 1. + // Finish ONE ready cell per worker per tick so each concurrency cell gets + // its own Done/Failed fly-out (do not clear the whole account at once). const finished: Array<{ jobId: string; workerId: string; ok: boolean }> = [] for (const w of next.workers) { - if (!w.busy || !w.jobId || w.rateLimited) continue - if (w.progress < 0.995) continue - // ~12% fail flash (AffectUI shake path); rest → Done. - const ok = (w.jobId.charCodeAt(1) + Math.floor(next.generatedAt / 1000)) % 9 !== 0 - finished.push({ jobId: w.jobId, workerId: w.id, ok }) + if (w.rateLimited || !(w.cells?.length)) continue + const ready = w.cells.find((c) => { + const j = jobById.get(c.id) + return Boolean(j && j.status === 'running' && j.progress >= 0.995) + }) + if (!ready) continue + const ok = (ready.id.charCodeAt(1) + Math.floor(next.generatedAt / 1000)) % 9 !== 0 + finished.push({ jobId: ready.id, workerId: w.id, ok }) } for (const f of finished) { const job = jobById.get(f.jobId) const w = next.workers.find((x) => x.id === f.workerId) if (!job || !w) continue - w.busy = false - w.progress = 0 - w.jobId = undefined - w.inFlight = 0 + w.cells = (w.cells ?? []).filter((c) => c.id !== f.jobId) + w.inFlight = w.cells.length + w.jobId = w.cells[0]?.id + w.busy = w.cells.length > 0 + w.progress = w.busy + ? Math.max(0.04, ...w.cells.map((c) => jobById.get(c.id)?.progress ?? 0.04)) + : 0 job.accountId = undefined if (f.ok) { job.status = 'done' @@ -265,21 +323,26 @@ export function tickMockInflight(prev: MockInflightSnapshot, dtMs: number): Mock if (j.status === 'queued') j.elapsedMs += dt } - // Free slots pull front of queue (queue → worker handoff). + // Free concurrency cells pull front of queue (partially busy accounts included). const queue = next.jobs.filter((j) => j.status === 'queued') for (const w of next.workers) { - if (w.busy || w.rateLimited) continue - const job = queue.shift() - if (!job) break - job.status = 'running' - job.accountId = w.id - job.progress = 0.04 - job.elapsedMs = 0 - w.busy = true - w.progress = 0.04 - w.jobId = job.id + if (w.rateLimited) continue const cap = clampSlotConcurrency(w.concurrency ?? 1) - w.inFlight = Math.min(cap, Math.max(1, w.inFlight ?? 1)) + w.cells = w.cells ?? [] + while (w.cells.length < cap) { + const job = queue.shift() + if (!job) break + job.status = 'running' + job.accountId = w.id + job.progress = 0.04 + job.elapsedMs = 0 + const cell = { id: job.id, name: job.name, startedAtMs: Date.now(), elapsedMs: 0 } + w.cells = [...w.cells.filter((c) => c.id !== job.id), cell].slice(0, cap) + w.busy = true + w.progress = Math.max(w.progress, 0.04) + w.jobId = w.cells[0]?.id + w.inFlight = w.cells.length + } } // Soft enqueue so the waiting rail doesn’t drain dry. @@ -391,6 +454,8 @@ export type LiveWorkerLike = { } export type LiveCompletionLike = { + /** Open request id — match per-cell Done/Failed when present. */ + request_id?: string account_id: string status: string } @@ -416,6 +481,8 @@ export type LiveQueueJobLike = { /** RequestStatus* — UI maps selecting onto MockJobStatus queued. */ status: string started_at?: string + /** Bind target while dwell still projects onto the waiting rail. */ + account_id?: string } export type LiveProjectionInput = { @@ -427,6 +494,8 @@ export type LiveProjectionInput = { recent_completions?: LiveCompletionLike[] /** Selecting requests (started, unbound) from /api/live queue. */ queue?: LiveQueueJobLike[] + /** Bound open requests — per concurrency cell (model + Bind started_at). */ + occupancy?: LiveQueueJobLike[] } /** @@ -437,9 +506,11 @@ export type LiveProjectionInput = { * polls and bump from recent_completions / inferred completions (reset when * the card starts a new viewing session). depthSeries rolls prior *live* * samples only — mock seed/tick series are discarded on first projection. - * Elapsed comes from worker `started_at` (pool InFlightSince); if missing, - * keep the prior live job's startedAtMs or stamp Date.now() on first sight. - * Progress is occupancy only — UI uses indeterminate shimmer, not %. + * Per-cell elapsed comes from occupancy `started_at` (Bind / BoundAt); falls + * back to worker InFlightSince / prior cell stamp. Progress is occupancy only — + * UI uses per-cell model + elapsed, not %. Cells still on the selecting rail + * (dwell) set `pendingInbound` so the slot can exist as a fly target without + * showing occupied chrome until the inbound animation lands. */ export function applyLiveProjection( base: MockInflightSnapshot, @@ -448,29 +519,6 @@ export function applyLiveProjection( const now = Date.now() const prevById = new Map(base.jobs.map((j) => [j.id, j])) - const workers: MockAccountSlot[] = live.workers.map((w) => { - const concurrency = clampSlotConcurrency(w.max_inflight ?? 4) - const rawIn = - w.in_flight != null && Number.isFinite(w.in_flight) - ? Math.max(0, Math.floor(w.in_flight)) - : w.busy - ? 1 - : 0 - const inFlight = Math.min(concurrency, rawIn) - return { - id: w.id, - name: w.name, - provider: w.provider, - busy: w.busy || inFlight > 0, - // Keep a soft internal value for mock-compat; busy UI ignores % fill. - progress: w.busy || inFlight > 0 ? Math.max(0.04, Math.min(1, w.progress || 0)) : 0, - rateLimited: w.rate_limited, - jobId: w.busy || inFlight > 0 ? `live-${w.id}` : undefined, - concurrency, - inFlight, - } - }) - const n = Math.max(1, MOCK_INFLIGHT.depthSeries.length) const rateLimits: MockRateLimitWindow[] = live.rate_limits.map((r, i) => { const span = 0.14 @@ -484,38 +532,7 @@ export function applyLiveProjection( } }) - const runningJobs: MockInflightJob[] = workers - .filter((w) => w.busy && w.jobId) - .map((w) => { - const id = w.jobId! - const prev = prevById.get(id) - const liveWorker = live.workers.find((lw) => lw.id === w.id) - let startedAtMs: number | undefined - const raw = liveWorker?.started_at?.trim() - if (raw) { - const parsed = Date.parse(raw) - if (!Number.isNaN(parsed)) startedAtMs = parsed - } - if (startedAtMs == null && prev?.startedAtMs != null) { - startedAtMs = prev.startedAtMs - } - if (startedAtMs == null) { - startedAtMs = now - } - return { - id, - name: w.name, - kind: 'chat' as const, - status: 'running' as const, - accountId: w.id, - progress: w.progress, - startedAtMs, - elapsedMs: Math.max(0, now - startedAtMs), - } - }) - - // Selecting queue: RequestStatusStarted (etc.) → UI MockJobStatus queued. - // Do not invent parallel statuses — only map existing request_logs values. + // Selecting queue first — ids still on wait rail (incl. dwell-bound). const queuedJobs: MockInflightJob[] = (live.queue ?? []).map((q) => { const id = (q.id || '').trim() const prev = prevById.get(id) @@ -532,16 +549,142 @@ export function applyLiveProjection( startedAtMs = now } const name = (q.name || '').trim() || id + const accountId = (q.account_id || '').trim() || undefined return { id, name, kind: 'chat' as const, status: 'queued' as const, + accountId, progress: 0, startedAtMs, elapsedMs: Math.max(0, now - startedAtMs), } }) + const queueIds = new Set(queuedJobs.map((j) => j.id)) + + const dwellNameByAccount = new Map() + for (const q of queuedJobs) { + const acc = (q.accountId || '').trim() + if (acc && q.name && !dwellNameByAccount.has(acc)) dwellNameByAccount.set(acc, q.name) + } + + // Occupancy grouped by account (FIFO from API BoundRequests). + const occByAccount = new Map() + for (const o of live.occupancy ?? []) { + const acc = (o.account_id || '').trim() + const id = (o.id || '').trim() + if (!acc || !id) continue + const list = occByAccount.get(acc) ?? [] + list.push(o) + occByAccount.set(acc, list) + } + + const prevCellsByAccount = new Map() + for (const w of base.workers) { + if (w.cells?.length) prevCellsByAccount.set(w.id, w.cells) + } + + const parseStarted = ( + raw: string | undefined, + prevMs: number | undefined, + fallback: number, + ): number => { + const t = raw?.trim() + if (t) { + const parsed = Date.parse(t) + if (!Number.isNaN(parsed)) return parsed + } + if (prevMs != null) return prevMs + return fallback + } + + const workers: MockAccountSlot[] = live.workers.map((w) => { + const concurrency = clampSlotConcurrency(w.max_inflight ?? 4) + const rawIn = + w.in_flight != null && Number.isFinite(w.in_flight) + ? Math.max(0, Math.floor(w.in_flight)) + : w.busy + ? 1 + : 0 + const inFlight = Math.min(concurrency, rawIn) + const prevCells = prevCellsByAccount.get(w.id) ?? [] + const prevByCellId = new Map(prevCells.map((c) => [c.id, c])) + const occ = occByAccount.get(w.id) ?? [] + const cells: MockSlotCell[] = [] + + for (const o of occ) { + if (cells.length >= inFlight) break + const id = (o.id || '').trim() + if (!id) continue + const prevCell = prevByCellId.get(id) + const prevJob = prevById.get(id) + const startedAtMs = parseStarted( + o.started_at, + prevCell?.startedAtMs ?? prevJob?.startedAtMs, + now, + ) + const name = (o.name || '').trim() || id + cells.push({ + id, + name, + startedAtMs, + elapsedMs: Math.max(0, now - startedAtMs), + // Dwell-bound: still on wait rail — reserve the cell, gate chrome until land. + pendingInbound: queueIds.has(id), + }) + } + + // Pool says more occupied than recorder — pad anonymous cells (account started_at). + const liveWorker = live.workers.find((lw) => lw.id === w.id) + const accountStarted = parseStarted(liveWorker?.started_at, undefined, now) + while (cells.length < inFlight) { + const idx = cells.length + // First synthetic cell keeps legacy `live-${accountId}` id for fly-out routing. + const synthId = idx === 0 ? `live-${w.id}` : `live-${w.id}-${idx}` + const prevCell = prevByCellId.get(synthId) ?? prevCells[idx] + const startedAtMs = prevCell?.startedAtMs ?? accountStarted + cells.push({ + id: synthId, + name: dwellNameByAccount.get(w.id) ?? w.name, + startedAtMs, + elapsedMs: Math.max(0, now - startedAtMs), + }) + } + + const busy = w.busy || inFlight > 0 + return { + id: w.id, + name: w.name, + provider: w.provider, + busy, + progress: busy ? Math.max(0.04, Math.min(1, w.progress || 0)) : 0, + rateLimited: w.rate_limited, + jobId: cells[0]?.id, + concurrency, + inFlight, + cells, + } + }) + + // Running jobs = occupancy cells not still on the wait rail (dwell). + const runningJobs: MockInflightJob[] = [] + for (const w of workers) { + for (const c of w.cells ?? []) { + if (queueIds.has(c.id)) continue // still selecting for display + const prev = prevById.get(c.id) + runningJobs.push({ + id: c.id, + name: c.name, + kind: (prev?.kind as MockJobKind | undefined) ?? 'chat', + status: 'running', + accountId: w.id, + progress: w.progress, + startedAtMs: c.startedAtMs, + elapsedMs: c.elapsedMs, + }) + } + } const jobs: MockInflightJob[] = [...runningJobs, ...queuedJobs] @@ -555,12 +698,16 @@ export function applyLiveProjection( series.push(Math.max(0, live.in_flight)) while (series.length < n) series.unshift(0) - // Match busy→idle jobs to drained request finishes (or last_error_kind). + // Match busy→idle jobs to drained finishes: request_id first (per-cell), + // then account FIFO, then last_error_kind. + const pendingByRequest = new Map() const pendingByAccount = new Map() for (const c of live.recent_completions ?? []) { + const ok = (c.status || '').trim() === 'ok' + const rid = (c.request_id || '').trim() + if (rid) pendingByRequest.set(rid, ok) const acc = (c.account_id || '').trim() if (!acc) continue - const ok = (c.status || '').trim() === 'ok' const list = pendingByAccount.get(acc) ?? [] list.push(ok) pendingByAccount.set(acc, list) @@ -571,14 +718,19 @@ export function applyLiveProjection( if (prev.status !== 'running') continue if (stillIds.has(prev.id)) continue const acc = (prev.accountId || prev.id.replace(/^live-/, '')).trim() - const queue = pendingByAccount.get(acc) let ok = true - if (queue && queue.length > 0) { - ok = queue.shift()! + if (pendingByRequest.has(prev.id)) { + ok = pendingByRequest.get(prev.id)! + pendingByRequest.delete(prev.id) } else { - const lw = live.workers.find((w) => w.id === acc) - if (lw?.last_error_kind && lw.last_error_kind.trim() !== '') { - ok = false + const queue = pendingByAccount.get(acc) + if (queue && queue.length > 0) { + ok = queue.shift()! + } else { + const lw = live.workers.find((w) => w.id === acc) + if (lw?.last_error_kind && lw.last_error_kind.trim() !== '') { + ok = false + } } } completions.push({ @@ -633,6 +785,7 @@ export function liveHasSignal(live: LiveProjectionInput): boolean { live.rate_limits.length > 0 || live.in_flight > 0 || (live.queue?.length ?? 0) > 0 || + (live.occupancy?.length ?? 0) > 0 || (live.sticky?.bindings ?? 0) > 0 ) } diff --git a/frontend/src/pages/OverviewPage.tsx b/frontend/src/pages/OverviewPage.tsx index 596b8cc5..0b45985a 100644 --- a/frontend/src/pages/OverviewPage.tsx +++ b/frontend/src/pages/OverviewPage.tsx @@ -32,7 +32,7 @@ import { accountProviderFamilyLabel, accountProviderLabel } from '@/lib/provider import { ProviderMark } from '@/components/ProviderMark' import { LiveStrip } from '@/components/overview/LiveStrip' import { InflightLiveCard } from '@/components/overview/InflightLiveCard' -import { useLive } from '@/hooks/useLive' +import { LIVE_POLL_EXPANDED_MS, LIVE_POLL_MS, useLive } from '@/hooks/useLive' type AccountRow = NonNullable[number] @@ -140,9 +140,11 @@ export function OverviewPage() { const [modelCount, setModelCount] = useState(0) const [resources, setResources] = useState(null) const [now, setNow] = useState(() => Date.now()) - const { live, samples: liveSamples } = useLive() const [inflightOpen, setInflightOpen] = useState(false) const [coolingOpen, setCoolingOpen] = useState(false) + const { live, samples: liveSamples } = useLive({ + intervalMs: inflightOpen ? LIVE_POLL_EXPANDED_MS : LIVE_POLL_MS, + }) const liveById = new Map((live?.accounts ?? []).map((item) => [item.id, item])) const proxyOk = Boolean(overview?.proxy?.ok) diff --git a/internal/console/live.go b/internal/console/live.go index 5ce445d5..cb76b5ab 100644 --- a/internal/console/live.go +++ b/internal/console/live.go @@ -62,18 +62,24 @@ type LiveRateLimit struct { // LiveCompletion is one terminal request finish drained from the request // recorder for this /api/live poll (busy→idle Done vs Failed routing). type LiveCompletion struct { + // RequestID identifies the finished open request so InflightLiveCard can + // fly that concurrency cell to Done/Failed independently (not whole account). + RequestID string `json:"request_id,omitempty"` AccountID string `json:"account_id"` Status string `json:"status"` } -// LiveJob is one selecting (选号) request for the InflightLiveCard waiting -// queue. Status reuses accounts.RequestStatus* (started) — not a new state -// machine. Running occupancy stays on Workers via Pool InFlight. +// LiveJob is one selecting (选号) or bound occupancy request for the +// InflightLiveCard. Status reuses accounts.RequestStatus* — not a new state +// machine. Queue uses unbound/dwell selecting; Occupancy uses BoundRequests +// (per-cell model + started_at). Workers still carry Pool InFlight counts. type LiveJob struct { ID string `json:"id"` Name string `json:"name"` Status string `json:"status"` StartedAt string `json:"started_at,omitempty"` + // AccountID: dwell fly target on Queue; always set on Occupancy. + AccountID string `json:"account_id,omitempty"` } type LiveSnapshot struct { @@ -86,8 +92,11 @@ type LiveSnapshot struct { RateLimits []LiveRateLimit `json:"rate_limits"` Sticky executor.SessionAffinityStats `json:"sticky"` RecentCompletions []LiveCompletion `json:"recent_completions,omitempty"` - // Queue is Prepare→Acquire 选号 (RequestStatusStarted, no pool bind). + // Queue is Prepare→Acquire 选号 (RequestStatusStarted / dwell). Queue []LiveJob `json:"queue,omitempty"` + // Occupancy is bound open requests (one per concurrency cell) with model + // name and per-request started_at (Bind time) for InflightLiveCard. + Occupancy []LiveJob `json:"occupancy,omitempty"` } // buildLiveSnapshot sums in-flight over all accounts and capacity (the @@ -102,6 +111,7 @@ func buildLiveSnapshot(views []accounts.AccountView, rpm int, sticky executor.Se Workers: make([]LiveWorker, 0, len(views)), RateLimits: make([]LiveRateLimit, 0), Queue: make([]LiveJob, 0), + Occupancy: make([]LiveJob, 0), Sticky: sticky, } for _, v := range views { @@ -214,6 +224,7 @@ func (h *Handler) HandleLive(w http.ResponseWriter, r *http.Request) { if h.Recorder != nil { for _, c := range h.Recorder.DrainRecentCompletions() { snap.RecentCompletions = append(snap.RecentCompletions, LiveCompletion{ + RequestID: c.RequestID, AccountID: c.AccountID, Status: c.Status, }) @@ -232,6 +243,28 @@ func (h *Handler) HandleLive(w http.ResponseWriter, r *http.Request) { Name: name, Status: status, StartedAt: q.StartedAt.UTC().Format(time.RFC3339), + AccountID: strings.TrimSpace(q.AccountID), + }) + } + for _, b := range h.Recorder.BoundRequests() { + name := strings.TrimSpace(b.RequestedModel) + if name == "" { + name = b.ID + } + status := strings.TrimSpace(b.Status) + if status == "" { + status = accounts.RequestStatusStarted + } + started := b.BoundAt + if started.IsZero() { + started = b.StartedAt + } + snap.Occupancy = append(snap.Occupancy, LiveJob{ + ID: b.ID, + Name: name, + Status: status, + StartedAt: started.UTC().Format(time.RFC3339), + AccountID: strings.TrimSpace(b.AccountID), }) } } diff --git a/internal/console/live_test.go b/internal/console/live_test.go index bcf18916..781d3cfa 100644 --- a/internal/console/live_test.go +++ b/internal/console/live_test.go @@ -84,6 +84,12 @@ func TestBuildLiveSnapshot(t *testing.T) { if len(got.Queue) != 0 { t.Fatalf("queue = %+v", got.Queue) } + if got.Occupancy == nil { + t.Fatalf("occupancy must be non-nil empty slice") + } + if len(got.Occupancy) != 0 { + t.Fatalf("occupancy = %+v", got.Occupancy) + } } func TestBuildLiveSnapshotEmpty(t *testing.T) { diff --git a/internal/logs/recent_completions_test.go b/internal/logs/recent_completions_test.go index 8f268484..3121d033 100644 --- a/internal/logs/recent_completions_test.go +++ b/internal/logs/recent_completions_test.go @@ -94,8 +94,8 @@ func TestSelectingMinDwellDisplayOnly(t *testing.T) { }) r.BindAccount("fresh", "acc-x") got := r.SelectingRequests() - if len(got) != 1 || got[0].ID != "fresh" || got[0].AccountID != "" { - t.Fatalf("bound but within dwell must still project as selecting, got %+v", got) + if len(got) != 1 || got[0].ID != "fresh" || got[0].AccountID != "acc-x" { + t.Fatalf("bound but within dwell must still project as selecting with AccountID, got %+v", got) } old := now.Add(-(selectingMinDwell + time.Millisecond)) @@ -107,9 +107,6 @@ func TestSelectingMinDwellDisplayOnly(t *testing.T) { ids := map[string]bool{} for _, o := range got { ids[o.ID] = true - if o.AccountID != "" { - t.Fatalf("projection must clear AccountID, got %+v", o) - } } if !ids["fresh"] { t.Fatalf("fresh still in dwell missing: %+v", got) @@ -117,4 +114,50 @@ func TestSelectingMinDwellDisplayOnly(t *testing.T) { if ids["aged"] { t.Fatalf("aged past dwell must leave selecting projection: %+v", got) } + if got[0].AccountID != "acc-x" { + t.Fatalf("fresh must keep bind target for fly handoff, got %+v", got[0]) + } +} + +func TestBoundRequestsPerAccountElapsed(t *testing.T) { + r := NewRequestRecorder(persistOnly{}) + defer r.Close() + + past := time.Now().UTC().Add(-(selectingMinDwell + time.Second)) + r.Start(accounts.RequestLog{ID: "a1", RequestedModel: "sonnet", Status: accounts.RequestStatusStarted, CreatedAt: past}) + r.Start(accounts.RequestLog{ID: "a2", RequestedModel: "gpt", Status: accounts.RequestStatusStarted, CreatedAt: past.Add(time.Millisecond)}) + r.Start(accounts.RequestLog{ID: "q1", RequestedModel: "wait", Status: accounts.RequestStatusStarted, CreatedAt: past.Add(2 * time.Millisecond)}) + + if got := r.BoundRequests(); len(got) != 0 { + t.Fatalf("unbound must be empty, got %+v", got) + } + + r.BindAccount("a2", "acc-x") + time.Sleep(2 * time.Millisecond) + r.BindAccount("a1", "acc-x") + got := r.BoundRequests() + if len(got) != 2 { + t.Fatalf("want 2 bound, got %+v", got) + } + // FIFO by BoundAt: a2 bound first. + if got[0].ID != "a2" || got[1].ID != "a1" { + t.Fatalf("bound order = %+v", got) + } + if got[0].AccountID != "acc-x" || got[0].RequestedModel != "gpt" { + t.Fatalf("got[0]=%+v", got[0]) + } + if got[0].BoundAt.IsZero() || got[1].BoundAt.IsZero() { + t.Fatalf("bound must stamp BoundAt: %+v", got) + } + + r.UnbindAccount("a2", "acc-x") + got = r.BoundRequests() + if len(got) != 1 || got[0].ID != "a1" { + t.Fatalf("after unbind a2 = %+v", got) + } + if !got[0].BoundAt.IsZero() { + // a1 still bound — BoundAt must remain + } else { + t.Fatalf("remaining bound a1 lost BoundAt") + } } diff --git a/internal/logs/recorder.go b/internal/logs/recorder.go index bc7b5566..137c39eb 100644 --- a/internal/logs/recorder.go +++ b/internal/logs/recorder.go @@ -109,6 +109,7 @@ type StatsQuery struct { // DrainRecentCompletions call. /api/live drains these so Overview can route // busy→idle fly-outs to Done vs Failed without inventing a parallel stream. type RecentCompletion struct { + RequestID string AccountID string Status string At time.Time @@ -121,13 +122,17 @@ type RecentCompletion struct { // AccountID empty ⇒ still selecting (pre-Acquire); set by BindAccount on // Pool.Acquire; cleared by UnbindAccount on Release; entry removed on // terminal Finish. SelectingRequests may still project a bound request as -// selecting until selectingMinDwell after StartedAt (display-only). +// selecting until selectingMinDwell after StartedAt (display-only), keeping +// AccountID so the UI can hand off queue→slot. type OpenRequest struct { ID string RequestedModel string AccountID string Status string StartedAt time.Time + // BoundAt is when BindAccount attached the pool slot (elapsed-in-cell). + // Zero while unbound; cleared on UnbindAccount. + BoundAt time.Time } type RequestRecorder struct { @@ -248,6 +253,7 @@ func (r *RequestRecorder) noteCompletion(log accounts.RequestLog) { r.recentMu.Lock() defer r.recentMu.Unlock() r.recent = append(r.recent, RecentCompletion{ + RequestID: strings.TrimSpace(log.ID), AccountID: acc, Status: strings.TrimSpace(log.Status), At: time.Now(), @@ -275,7 +281,9 @@ const openRequestCap = 512 // selectingMinDwell keeps a request in the /api/live selecting (queue) // projection for at least this long after Start, even after BindAccount. // Display-only: BindAccount / Acquire / pickNext are not delayed. -const selectingMinDwell = 500 * time.Millisecond +// ~1.6s so Overview's expanded ~400ms /api/live poll can catch several frames +// (collapsed poll is 2.5s and would miss a 500ms window). +const selectingMinDwell = 1600 * time.Millisecond func (r *RequestRecorder) noteOpen(log accounts.RequestLog) { if r == nil { @@ -325,6 +333,9 @@ func (r *RequestRecorder) BindAccount(requestID, accountID string) { defer r.openMu.Unlock() if o, ok := r.open[requestID]; ok { o.AccountID = accountID + if o.BoundAt.IsZero() { + o.BoundAt = time.Now().UTC() + } } } @@ -347,6 +358,7 @@ func (r *RequestRecorder) UnbindAccount(requestID, accountID string) { } if accountID == "" || o.AccountID == "" || o.AccountID == accountID { o.AccountID = "" + o.BoundAt = time.Time{} } } @@ -364,9 +376,10 @@ func (r *RequestRecorder) clearOpen(requestID string) { } // SelectingRequests returns open requests still in 选号 for the InflightLiveCard -// waiting queue (FIFO by StartedAt). Real binds still clear AccountID on the +// waiting queue (FIFO by StartedAt). Real binds still set AccountID on the // open map immediately; this projection also keeps recently-bound requests // until selectingMinDwell after StartedAt so the UI can see the wait rail. +// Bound-within-dwell entries keep AccountID so the card can fly queue→slot. // Does not block Pool.Acquire / pickNext. func (r *RequestRecorder) SelectingRequests() []OpenRequest { if r == nil { @@ -385,8 +398,7 @@ func (r *RequestRecorder) SelectingRequests() []OpenRequest { if !now.Before(cp.StartedAt.Add(selectingMinDwell)) { continue } - // Project as still selecting for display only. - cp.AccountID = "" + // Still selecting for display; keep AccountID for queue→slot handoff. } out = append(out, cp) } @@ -399,6 +411,43 @@ func (r *RequestRecorder) SelectingRequests() []OpenRequest { return out } +// BoundRequests returns open requests currently attached to a pool account +// (post-Acquire), FIFO by BoundAt then StartedAt. Used by /api/live occupancy +// so InflightLiveCard can show per-concurrency-cell model + elapsed — not one +// timer for the whole account. Includes dwell-bound entries (still on the +// selecting rail) so the target cell exists for queue→slot fly-in. +func (r *RequestRecorder) BoundRequests() []OpenRequest { + if r == nil { + return nil + } + r.openMu.Lock() + defer r.openMu.Unlock() + out := make([]OpenRequest, 0, len(r.open)) + for _, o := range r.open { + if o == nil || strings.TrimSpace(o.AccountID) == "" { + continue + } + out = append(out, *o) + } + sort.SliceStable(out, func(i, j int) bool { + bi, bj := out[i].BoundAt, out[j].BoundAt + if bi.IsZero() { + bi = out[i].StartedAt + } + if bj.IsZero() { + bj = out[j].StartedAt + } + if !bi.Equal(bj) { + return bi.Before(bj) + } + if !out[i].StartedAt.Equal(out[j].StartedAt) { + return out[i].StartedAt.Before(out[j].StartedAt) + } + return out[i].ID < out[j].ID + }) + return out +} + func (r *RequestRecorder) trimOpenLocked() { if len(r.open) <= openRequestCap { return diff --git a/internal/webui/static/assets/CartesianGrid-sx1L6jkY.js b/internal/webui/static/assets/CartesianGrid-BfqmuU7N.js similarity index 99% rename from internal/webui/static/assets/CartesianGrid-sx1L6jkY.js rename to internal/webui/static/assets/CartesianGrid-BfqmuU7N.js index 816f1aa6..96565b8e 100644 --- a/internal/webui/static/assets/CartesianGrid-sx1L6jkY.js +++ b/internal/webui/static/assets/CartesianGrid-BfqmuU7N.js @@ -1,4 +1,4 @@ -import{$t as e,A as t,At as n,Bt as r,Cn as i,Ct as a,Dt as o,E as s,Et as c,F as l,Ft as u,Gt as d,H as f,Ht as p,I as m,It as h,J as g,Jt as _,K as v,Kt as y,L as b,Lt as x,M as S,Mt as C,N as w,Nt as T,O as E,Ot as D,P as O,Pt as k,R as ee,Rt as te,Sn as A,St as j,Tn as M,Tt as ne,Ut as re,Vt as ie,W as N,Wt as ae,X as oe,Xt as se,Y as P,Yt as ce,Z as le,Zt as ue,an as F,bt as de,cn as fe,ct as I,dn as pe,dt as me,en as L,et as he,fn as ge,gt as _e,ht as ve,it as ye,j as be,jt as xe,k as Se,kt as Ce,l as we,ln as Te,nn as Ee,nt as De,on as Oe,ot as ke,pn as Ae,q as R,qt as je,rt as Me,sn as Ne,st as Pe,tn as z,tt as Fe,u as Ie,ut as Le,vt as Re,wn as ze,wt as Be,xt as Ve,yt as B,z as He,zt as Ue}from"./index-DRQwlrIS.js";function We(e,t){let n=new Map;for(let r=0;rvoid 0)}function et(e,t,n,r,i=!1){if(t===e)return!0;switch(typeof t){case`object`:return tt(e,t,n,r,i);case`function`:return Object.keys(t).length>0?et(e,{...t},n,r,i):j(e,t);default:return Ve(e)&&i?typeof t!=`string`||t===``:j(e,t)}}function tt(e,t,n,r,i=!1){if(t==null)return!0;if(Array.isArray(t))return rt(e,t,n,r);if(t instanceof Map)return nt(e,t,n,r);if(t instanceof Set)return it(e,t,n,r);let a=Object.keys(t);if(e==null)return i&&a.length===0;if(i)Je(e)&&(e=Object(e));else{let t=y(e);if(t!==`[object Object]`&&t!==`[object Arguments]`)return!1}if(a.length===0)return!0;if(r?.has(t))return r.get(t)===e;r?.set(t,e);try{for(let i=0;ivoid 0)}function ot(e){return e=Qe(e),t=>at(t,e)}function st(e,t){return Xe(e,(n,i,a,s)=>{let c=t?.(n,i,a,s);if(c!==void 0)return c;if(typeof e==`object`){if(y(e)===`[object Object]`&&typeof e.constructor!=`function`){let t={};return s.set(e,t),H(t,e,a,s),t}switch(Object.prototype.toString.call(e)){case h:case r:case o:{let t=new e.constructor(e?.valueOf());return H(t,e),t}case Be:{let t={};return H(t,e),t.length=e.length,t[Symbol.iterator]=e[Symbol.iterator],t}default:return}}})}function ct(e){return st(e)}function lt(e){return typeof e==`object`&&!!e&&y(e)===`[object Arguments]`}function ut(e,t){let n;if(n=Array.isArray(t)?t:typeof t==`string`&&Te(t)&&!(t in Object(e))?Ne(t):[t],n.length===0)return!1;let r=e;for(let e=0;evoid 0)}function et(e,t,n,r,i=!1){if(t===e)return!0;switch(typeof t){case`object`:return tt(e,t,n,r,i);case`function`:return Object.keys(t).length>0?et(e,{...t},n,r,i):j(e,t);default:return Ve(e)&&i?typeof t!=`string`||t===``:j(e,t)}}function tt(e,t,n,r,i=!1){if(t==null)return!0;if(Array.isArray(t))return rt(e,t,n,r);if(t instanceof Map)return nt(e,t,n,r);if(t instanceof Set)return it(e,t,n,r);let a=Object.keys(t);if(e==null)return i&&a.length===0;if(i)Je(e)&&(e=Object(e));else{let t=y(e);if(t!==`[object Object]`&&t!==`[object Arguments]`)return!1}if(a.length===0)return!0;if(r?.has(t))return r.get(t)===e;r?.set(t,e);try{for(let i=0;ivoid 0)}function ot(e){return e=Qe(e),t=>at(t,e)}function st(e,t){return Xe(e,(n,i,a,s)=>{let c=t?.(n,i,a,s);if(c!==void 0)return c;if(typeof e==`object`){if(y(e)===`[object Object]`&&typeof e.constructor!=`function`){let t={};return s.set(e,t),H(t,e,a,s),t}switch(Object.prototype.toString.call(e)){case h:case r:case o:{let t=new e.constructor(e?.valueOf());return H(t,e),t}case Be:{let t={};return H(t,e),t.length=e.length,t[Symbol.iterator]=e[Symbol.iterator],t}default:return}}})}function ct(e){return st(e)}function lt(e){return typeof e==`object`&&!!e&&y(e)===`[object Arguments]`}function ut(e,t){let n;if(n=Array.isArray(t)?t:typeof t==`string`&&Te(t)&&!(t in Object(e))?Ne(t):[t],n.length===0)return!1;let r=e;for(let e=0;ee.length)&&(t=e.length);for(var n=0,r=Array(t);nW||Math.abs(e.left-t.left)>W||Math.abs(e.top-t.top)>W||Math.abs(e.width-t.width)>W}function wt(e){var t=e.getBoundingClientRect();return{height:t.height,left:t.left,top:t.top,width:t.width}}function Tt(){var e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:[],t=_t((0,U.useState)({height:0,left:0,top:0,width:0}),2),n=t[0],r=t[1],i=(0,U.useRef)(null),a=(0,U.useRef)(n);a.current=n;var o=(0,U.useCallback)(e=>{if(i.current!=null&&(i.current.disconnect(),i.current=null),e!=null){var t=wt(e);if(Ct(t,a.current)&&r(t),typeof ResizeObserver<`u`){var n=new ResizeObserver(()=>{var t=wt(e);Ct(t,a.current)&&r(t)});n.observe(e),i.current=n}}},[...e]);return(0,U.useEffect)(()=>()=>{var e;(e=i.current)==null||e.disconnect()},[]),[n,o]}function Et(){return Et=Object.assign?Object.assign.bind():function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);n{var n=t.separator,r=n===void 0?K.separator:n,i=t.contentStyle,a=t.itemStyle,o=t.labelStyle,s=o===void 0?K.labelStyle:o,c=t.payload,l=t.formatter,u=t.itemSorter,d=t.wrapperClassName,f=t.labelClassName,p=t.label,m=t.labelFormatter,h=t.accessibilityLayer,g=h===void 0?K.accessibilityLayer:h,_=()=>{if(c&&c.length){var e={padding:0,margin:0},t=Rt(c,u).map((e,t)=>{if(!e||e.type===`none`)return null;var n=e.formatter||l||Lt,i=e.value,o=e.name,s=i,u=o;if(n){var d=n(i,o,e,t,c);if(Array.isArray(d)){var f=jt(d,2);s=f[0],u=f[1]}else if(d!=null)s=d;else return null}var p=G(G({},K.itemStyle),{},{color:e.color||K.itemStyle.color},a);return U.createElement(`li`,{className:`recharts-tooltip-item`,key:`tooltip-item-${t}`,style:p},L(u)?U.createElement(`span`,{className:`recharts-tooltip-item-name`},u):null,L(u)?U.createElement(`span`,{className:`recharts-tooltip-item-separator`},r):null,U.createElement(`span`,{className:`recharts-tooltip-item-value`},s),U.createElement(`span`,{className:`recharts-tooltip-item-unit`},e.unit||``))});return U.createElement(`ul`,{className:`recharts-tooltip-item-list`,style:e},t)}return null},v=G(G({},K.contentStyle),i),y=G({margin:0},s),b=!e(p),x=b?p:``,S=A(`recharts-default-tooltip`,d),C=A(`recharts-tooltip-label`,f);b&&m&&c!=null&&(x=m(p,c));var w=g?{role:`status`,"aria-live":`assertive`}:{};return U.createElement(`div`,Et({className:S,style:v},w),U.createElement(`p`,{className:C,style:y},U.isValidElement(x)?x:`${x}`),_())},q=`recharts-tooltip-wrapper`,Bt={visibility:`hidden`};function Vt(e){var t=e.coordinate,n=e.translateX,r=e.translateY;return A(q,{[`${q}-right`]:z(n)&&t&&z(t.x)&&n>=t.x,[`${q}-left`]:z(n)&&t&&z(t.x)&&n=t.y,[`${q}-top`]:z(r)&&t&&z(t.y)&&r0?i:0),d=n[r]+i;if(t[r])return o[r]?u:d;var f=c[r];return f==null?0:o[r]?Math.max(uf+l?Math.max(u,f):Math.max(d,f)}function Ut(e){var t=e.translateX,n=e.translateY;return{transform:e.useTranslate3d?`translate3d(${t}px, ${n}px, 0)`:`translate(${t}px, ${n}px)`}}function Wt(e){var t=e.allowEscapeViewBox,n=e.coordinate,r=e.offsetTop,i=e.offsetLeft,a=e.position,o=e.reverseDirection,s=e.tooltipBox,c=e.useTranslate3d,l=e.viewBox,u,d,f;return s&&s.height>0&&s.width>0&&n?(d=Ht({allowEscapeViewBox:t,coordinate:n,key:`x`,offset:i,position:a,reverseDirection:o,tooltipDimension:s.width,viewBox:l,viewBoxDimension:l.width}),f=Ht({allowEscapeViewBox:t,coordinate:n,key:`y`,offset:r,position:a,reverseDirection:o,tooltipDimension:s.height,viewBox:l,viewBoxDimension:l.height}),u=Ut({translateX:d,translateY:f,useTranslate3d:c})):u=Bt,{cssProperties:u,cssClasses:Vt({translateX:d,translateY:f,coordinate:n})}}function Gt(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function J(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);n({dismissed:!1,dismissedAtCoordinate:{x:0,y:0}})),2),r=n[0],i=n[1];U.useEffect(()=>{var t=t=>{t.key===`Escape`&&i({dismissed:!0,dismissedAtCoordinate:{x:e.coordinate?.x??0,y:e.coordinate?.y??0}})};return document.addEventListener(`keydown`,t),()=>{document.removeEventListener(`keydown`,t)}},[e.coordinate?.x,e.coordinate?.y]),r.dismissed&&((e.coordinate?.x??0)!==r.dismissedAtCoordinate.x||(e.coordinate?.y??0)!==r.dismissedAtCoordinate.y)&&i(J(J({},r),{},{dismissed:!1}));var a=Wt({allowEscapeViewBox:e.allowEscapeViewBox,coordinate:e.coordinate,offsetLeft:typeof e.offset==`number`?e.offset:e.offset.x,offsetTop:typeof e.offset==`number`?e.offset:e.offset.y,position:e.position,reverseDirection:e.reverseDirection,tooltipBox:e.lastBoundingBox,useTranslate3d:e.useTranslate3d,viewBox:e.viewBox}),o=a.cssClasses,s=a.cssProperties,c=J(J({},e.hasPortalFromProps?{}:J(J({transition:tn({prefersReducedMotion:t,isAnimationActive:e.isAnimationActive,active:e.active,animationDuration:e.animationDuration,animationEasing:e.animationEasing})},s),{},{pointerEvents:`none`,position:`absolute`,top:0,left:0})),{},{visibility:!r.dismissed&&e.active&&e.hasPayload?`visible`:`hidden`},e.wrapperStyle);return U.createElement(`div`,{xmlns:`http://www.w3.org/1999/xhtml`,tabIndex:-1,className:o,style:c,ref:e.innerRef},e.children)}var rn=U.memo(nn),an=[`x`,`y`,`top`,`left`,`width`,`height`,`className`];function on(){return on=Object.assign?Object.assign.bind():function(e){for(var t=1;t`M${e},${i}v${r}M${a},${t}h${n}`,hn=e=>{var t=e.x,n=t===void 0?0:t,r=e.y,i=r===void 0?0:r,a=e.top,o=a===void 0?0:a,s=e.left,c=s===void 0?0:s,l=e.width,u=l===void 0?0:l,d=e.height,f=d===void 0?0:d,p=e.className,m=fn(e,an),h=cn({x:n,y:i,top:o,left:c,width:u,height:f},m);return!z(n)||!z(i)||!z(u)||!z(f)||!z(o)||!z(c)?null:U.createElement(`path`,on({},pe(h),{className:A(`recharts-cross`,p),d:mn(n,i,u,f,o,c)}))};function gn(e,t,n,r){var i=r/2;return{stroke:`none`,fill:`#ccc`,x:e===`horizontal`?t.x-i:n.left+.5,y:e===`horizontal`?n.top+.5:t.y-i,width:e===`horizontal`?r:n.width-1,height:e===`horizontal`?n.height-1:r}}function _n(e){var t=e.cx,n=e.cy,r=e.radius,i=e.startAngle,a=e.endAngle;return{points:[R(t,n,r,i),R(t,n,r,a)],cx:t,cy:n,radius:r,startAngle:i,endAngle:a}}var vn,yn,bn,xn,Sn,Cn,wn;function Tn(){return Tn=Object.assign?Object.assign.bind():function(e){for(var t=1;tEe(t-e)*Math.min(Math.abs(t-e),359.999),X=e=>{var t=e.cx,n=e.cy,r=e.radius,i=e.angle,a=e.sign,o=e.isExternal,s=e.cornerRadius,c=e.cornerIsExternal,l=s*(o?1:-1)+r,u=Math.asin(s/l)/v,d=c?i:i+a*u,f=R(t,n,l,d),p=R(t,n,r,d),m=c?i-a*u:i;return{center:f,circleTangency:p,lineTangency:R(t,n,l*Math.cos(u*v),m),theta:u}},Dn=e=>{var t=e.cx,n=e.cy,r=e.innerRadius,i=e.outerRadius,a=e.startAngle,o=e.endAngle,s=En(a,o),c=a+s,l=R(t,n,i,a),u=R(t,n,i,c),d=F(vn||=Y([`M `,`,`,` diff --git a/internal/webui/static/assets/CostChart-DfOEXWjY.js b/internal/webui/static/assets/CostChart-CarVbkc9.js similarity index 96% rename from internal/webui/static/assets/CostChart-DfOEXWjY.js rename to internal/webui/static/assets/CostChart-CarVbkc9.js index e532007a..e5547384 100644 --- a/internal/webui/static/assets/CostChart-DfOEXWjY.js +++ b/internal/webui/static/assets/CostChart-CarVbkc9.js @@ -1 +1 @@ -import{n as e,t}from"./CartesianGrid-sx1L6jkY.js";import{Tn as n,_n as r,a as i,at as a,gn as o,i as s,r as c,t as l,vn as u,wn as d,xn as f}from"./index-DRQwlrIS.js";var p=n(d(),1),m=f(),h=[`var(--accent)`,`var(--muted)`,`var(--success)`,`var(--warning)`];function g(e,t,n){let r=new Date(e);return Number.isFinite(r.getTime())?new Intl.DateTimeFormat(n===`zh`?`zh-CN`:`en-US`,t?{month:`short`,day:`numeric`}:{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`,hour12:!1}).format(r):``}function _(e,t){return t===`credits`?e.per_model_credits??{}:t===`tokens`?e.per_model_tokens??{}:e.per_model??{}}function v(e,t){return t===`cost`?o(e):t===`tokens`?r(e):u(e)}function y(e,t){return t===`cost`?`¥${e>=10?Math.round(e):Number(e.toFixed(1))}`:t===`tokens`?r(e):u(e)}function b({active:e,payload:t,label:n,daily:r,lang:i,models:a,totalLabel:o,metric:s}){if(!e||!t?.length)return null;let c=new Map(a.map(e=>[e.id,e.label])),l=t.filter(e=>(e.value??0)>0).sort((e,t)=>t.value-e.value),u=t.reduce((e,t)=>e+(t.value??0),0);return(0,m.jsxs)(`div`,{className:`rounded-xl bg-overlay px-3 py-2 text-caption text-overlay-foreground shadow-overlay`,children:[(0,m.jsx)(`div`,{className:`mono text-muted`,children:g(String(n),r,i)}),(0,m.jsxs)(`div`,{className:`mt-1.5 grid gap-1`,children:[l.map(e=>(0,m.jsxs)(`div`,{className:`flex items-center justify-between gap-6`,children:[(0,m.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[(0,m.jsx)(`span`,{className:`size-1.5 rounded-full`,style:{background:h[a.findIndex(t=>t.id===e.dataKey)%h.length]}}),c.get(e.dataKey)||e.dataKey]}),(0,m.jsx)(`span`,{className:`mono font-medium`,children:v(e.value,s)})]},e.dataKey)),(0,m.jsxs)(`div`,{className:`flex items-center justify-between gap-6 border-t border-separator pt-1`,children:[(0,m.jsx)(`span`,{className:`text-muted`,children:o}),(0,m.jsx)(`span`,{className:`mono font-medium`,children:v(u,s)})]})]})]})}function x({series:n,models:r,lang:o,emptyLabel:u,totalLabel:d,metric:f=`credits`}){let v=(0,p.useMemo)(()=>r.map(e=>e.id),[r]),x=(0,p.useMemo)(()=>n.map(e=>{let t=_(e,f),n={at:e.at};for(let e of v){let r=Number(t[e]??0);n[e]=f===`tokens`?r:Number(r.toFixed(4))}return n}),[n,v,f]),S=Math.max(0,...x.map(e=>v.reduce((t,n)=>t+Number(e[n]??0),0)))>0,C=(x.length>1?Date.parse(x[x.length-1].at)-Date.parse(x[0].at):0)>1728e5;return!n.length||!S?(0,m.jsx)(`div`,{className:`grid min-h-56 place-items-center px-6 py-12 text-center text-sm text-muted`,children:u}):(0,m.jsxs)(`div`,{className:`min-w-0`,children:[(0,m.jsx)(`div`,{className:`h-72 w-full`,children:(0,m.jsx)(a,{width:`100%`,height:`100%`,children:(0,m.jsxs)(l,{data:x,margin:{top:8,right:8,bottom:0,left:0},children:[(0,m.jsx)(`defs`,{children:v.map((e,t)=>(0,m.jsxs)(`linearGradient`,{id:`cost-${f}-${e}`,x1:`0`,y1:`0`,x2:`0`,y2:`1`,children:[(0,m.jsx)(`stop`,{offset:`0%`,stopColor:h[t%h.length],stopOpacity:.32}),(0,m.jsx)(`stop`,{offset:`100%`,stopColor:h[t%h.length],stopOpacity:.04})]},e))}),(0,m.jsx)(t,{vertical:!1,stroke:`var(--separator)`,strokeDasharray:`3 6`}),(0,m.jsx)(s,{dataKey:`at`,tickLine:!1,axisLine:!1,minTickGap:28,tick:{fill:`var(--muted)`,fontSize:10,fontFamily:`var(--font-mono)`},tickFormatter:e=>g(e,C,o)}),(0,m.jsx)(c,{width:52,tickLine:!1,axisLine:!1,tick:{fill:`var(--muted)`,fontSize:10,fontFamily:`var(--font-mono)`},tickFormatter:e=>y(e,f)}),(0,m.jsx)(e,{cursor:{stroke:`var(--border)`,strokeDasharray:`3 3`},content:(0,m.jsx)(b,{daily:C,lang:o,models:r,totalLabel:d,metric:f})}),v.map((e,t)=>(0,m.jsx)(i,{type:`monotone`,dataKey:e,stackId:`usage`,stroke:h[t%h.length],strokeWidth:1.5,fill:`url(#cost-${f}-${e})`,isAnimationActive:!1,dot:!1,activeDot:{r:3,strokeWidth:1.2}},e))]})})}),(0,m.jsx)(`div`,{className:`mt-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-[10px] text-muted`,children:r.map((e,t)=>(0,m.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[(0,m.jsx)(`span`,{className:`size-1.5 rounded-full`,style:{background:h[t%h.length]}}),e.label]},e.id))})]})}export{x as CostChart}; \ No newline at end of file +import{n as e,t}from"./CartesianGrid-BfqmuU7N.js";import{Tn as n,_n as r,a as i,at as a,gn as o,i as s,r as c,t as l,vn as u,wn as d,xn as f}from"./index-eWoLzhb1.js";var p=n(d(),1),m=f(),h=[`var(--accent)`,`var(--muted)`,`var(--success)`,`var(--warning)`];function g(e,t,n){let r=new Date(e);return Number.isFinite(r.getTime())?new Intl.DateTimeFormat(n===`zh`?`zh-CN`:`en-US`,t?{month:`short`,day:`numeric`}:{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`,hour12:!1}).format(r):``}function _(e,t){return t===`credits`?e.per_model_credits??{}:t===`tokens`?e.per_model_tokens??{}:e.per_model??{}}function v(e,t){return t===`cost`?o(e):t===`tokens`?r(e):u(e)}function y(e,t){return t===`cost`?`¥${e>=10?Math.round(e):Number(e.toFixed(1))}`:t===`tokens`?r(e):u(e)}function b({active:e,payload:t,label:n,daily:r,lang:i,models:a,totalLabel:o,metric:s}){if(!e||!t?.length)return null;let c=new Map(a.map(e=>[e.id,e.label])),l=t.filter(e=>(e.value??0)>0).sort((e,t)=>t.value-e.value),u=t.reduce((e,t)=>e+(t.value??0),0);return(0,m.jsxs)(`div`,{className:`rounded-xl bg-overlay px-3 py-2 text-caption text-overlay-foreground shadow-overlay`,children:[(0,m.jsx)(`div`,{className:`mono text-muted`,children:g(String(n),r,i)}),(0,m.jsxs)(`div`,{className:`mt-1.5 grid gap-1`,children:[l.map(e=>(0,m.jsxs)(`div`,{className:`flex items-center justify-between gap-6`,children:[(0,m.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[(0,m.jsx)(`span`,{className:`size-1.5 rounded-full`,style:{background:h[a.findIndex(t=>t.id===e.dataKey)%h.length]}}),c.get(e.dataKey)||e.dataKey]}),(0,m.jsx)(`span`,{className:`mono font-medium`,children:v(e.value,s)})]},e.dataKey)),(0,m.jsxs)(`div`,{className:`flex items-center justify-between gap-6 border-t border-separator pt-1`,children:[(0,m.jsx)(`span`,{className:`text-muted`,children:o}),(0,m.jsx)(`span`,{className:`mono font-medium`,children:v(u,s)})]})]})]})}function x({series:n,models:r,lang:o,emptyLabel:u,totalLabel:d,metric:f=`credits`}){let v=(0,p.useMemo)(()=>r.map(e=>e.id),[r]),x=(0,p.useMemo)(()=>n.map(e=>{let t=_(e,f),n={at:e.at};for(let e of v){let r=Number(t[e]??0);n[e]=f===`tokens`?r:Number(r.toFixed(4))}return n}),[n,v,f]),S=Math.max(0,...x.map(e=>v.reduce((t,n)=>t+Number(e[n]??0),0)))>0,C=(x.length>1?Date.parse(x[x.length-1].at)-Date.parse(x[0].at):0)>1728e5;return!n.length||!S?(0,m.jsx)(`div`,{className:`grid min-h-56 place-items-center px-6 py-12 text-center text-sm text-muted`,children:u}):(0,m.jsxs)(`div`,{className:`min-w-0`,children:[(0,m.jsx)(`div`,{className:`h-72 w-full`,children:(0,m.jsx)(a,{width:`100%`,height:`100%`,children:(0,m.jsxs)(l,{data:x,margin:{top:8,right:8,bottom:0,left:0},children:[(0,m.jsx)(`defs`,{children:v.map((e,t)=>(0,m.jsxs)(`linearGradient`,{id:`cost-${f}-${e}`,x1:`0`,y1:`0`,x2:`0`,y2:`1`,children:[(0,m.jsx)(`stop`,{offset:`0%`,stopColor:h[t%h.length],stopOpacity:.32}),(0,m.jsx)(`stop`,{offset:`100%`,stopColor:h[t%h.length],stopOpacity:.04})]},e))}),(0,m.jsx)(t,{vertical:!1,stroke:`var(--separator)`,strokeDasharray:`3 6`}),(0,m.jsx)(s,{dataKey:`at`,tickLine:!1,axisLine:!1,minTickGap:28,tick:{fill:`var(--muted)`,fontSize:10,fontFamily:`var(--font-mono)`},tickFormatter:e=>g(e,C,o)}),(0,m.jsx)(c,{width:52,tickLine:!1,axisLine:!1,tick:{fill:`var(--muted)`,fontSize:10,fontFamily:`var(--font-mono)`},tickFormatter:e=>y(e,f)}),(0,m.jsx)(e,{cursor:{stroke:`var(--border)`,strokeDasharray:`3 3`},content:(0,m.jsx)(b,{daily:C,lang:o,models:r,totalLabel:d,metric:f})}),v.map((e,t)=>(0,m.jsx)(i,{type:`monotone`,dataKey:e,stackId:`usage`,stroke:h[t%h.length],strokeWidth:1.5,fill:`url(#cost-${f}-${e})`,isAnimationActive:!1,dot:!1,activeDot:{r:3,strokeWidth:1.2}},e))]})})}),(0,m.jsx)(`div`,{className:`mt-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-[10px] text-muted`,children:r.map((e,t)=>(0,m.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[(0,m.jsx)(`span`,{className:`size-1.5 rounded-full`,style:{background:h[t%h.length]}}),e.label]},e.id))})]})}export{x as CostChart}; \ No newline at end of file diff --git a/internal/webui/static/assets/LiveSparkline-D3aaed6m.js b/internal/webui/static/assets/LiveSparkline-Dx76eXSw.js similarity index 80% rename from internal/webui/static/assets/LiveSparkline-D3aaed6m.js rename to internal/webui/static/assets/LiveSparkline-Dx76eXSw.js index bae2af2b..40b6eff3 100644 --- a/internal/webui/static/assets/LiveSparkline-D3aaed6m.js +++ b/internal/webui/static/assets/LiveSparkline-Dx76eXSw.js @@ -1 +1 @@ -import{a as e,at as t,r as n,t as r,xn as i}from"./index-DRQwlrIS.js";var a=i();function o({samples:i,capacity:o,label:s}){let c=i.length?i:[{at:0,value:0}],l=Math.max(1,o,...c.map(e=>e.value));return(0,a.jsx)(`div`,{className:`h-6 w-[120px] shrink-0`,role:`img`,"aria-label":s,children:(0,a.jsx)(t,{width:`100%`,height:`100%`,children:(0,a.jsxs)(r,{data:c,margin:{top:1,right:0,bottom:1,left:0},children:[(0,a.jsx)(n,{hide:!0,domain:[0,l]}),(0,a.jsx)(e,{type:`monotone`,dataKey:`value`,stroke:`var(--foreground)`,strokeOpacity:.7,strokeWidth:1.25,fill:`var(--foreground)`,fillOpacity:.08,isAnimationActive:!1,dot:!1})]})})})}export{o as LiveSparkline}; \ No newline at end of file +import{a as e,at as t,r as n,t as r,xn as i}from"./index-eWoLzhb1.js";var a=i();function o({samples:i,capacity:o,label:s}){let c=i.length?i:[{at:0,value:0}],l=Math.max(1,o,...c.map(e=>e.value));return(0,a.jsx)(`div`,{className:`h-6 w-[120px] shrink-0`,role:`img`,"aria-label":s,children:(0,a.jsx)(t,{width:`100%`,height:`100%`,children:(0,a.jsxs)(r,{data:c,margin:{top:1,right:0,bottom:1,left:0},children:[(0,a.jsx)(n,{hide:!0,domain:[0,l]}),(0,a.jsx)(e,{type:`monotone`,dataKey:`value`,stroke:`var(--foreground)`,strokeOpacity:.7,strokeWidth:1.25,fill:`var(--foreground)`,fillOpacity:.08,isAnimationActive:!1,dot:!1})]})})})}export{o as LiveSparkline}; \ No newline at end of file diff --git a/internal/webui/static/assets/TrafficChart-DCmRhy8p.js b/internal/webui/static/assets/TrafficChart-CdfYVZma.js similarity index 99% rename from internal/webui/static/assets/TrafficChart-DCmRhy8p.js rename to internal/webui/static/assets/TrafficChart-CdfYVZma.js index c9f165de..cdff77ad 100644 --- a/internal/webui/static/assets/TrafficChart-DCmRhy8p.js +++ b/internal/webui/static/assets/TrafficChart-CdfYVZma.js @@ -1 +1 @@ -import{n as e,t}from"./CartesianGrid-sx1L6jkY.js";import{$ as n,$t as r,B as i,C as a,D as o,G as s,Q as c,Qt as l,S as u,Sn as d,T as f,Tn as p,U as m,V as h,W as g,Y as _,Yt as v,_ as y,_n as b,_t as x,a as S,at as C,b as w,bn as T,c as E,ct as D,d as ee,dn as O,f as k,fn as A,ft as j,g as M,h as te,hn as ne,i as re,in as N,k as P,lt as F,m as I,mn as ie,mt as L,n as ae,o as oe,p as se,pt as R,r as ce,rn as le,s as ue,st as z,tt as de,un as fe,v as pe,w as me,wn as he,x as ge,xn as _e,y as ve,yn as B,yt as ye}from"./index-DRQwlrIS.js";var V=p(he()),be=[`animationElapsedTime`,`isAnimating`,`isEntrance`,`visibleLength`,`strokeDasharray`,`connectNulls`];function H(){return H=Object.assign?Object.assign.bind():function(e){for(var t=1;te+t,0);if(!i)return U(t,e);for(var a=Math.floor(e/i),o=e%i,s=[],c=0,l=0;co){s=[...r.slice(0,c),o-l];break}}var f=s.length%2==0?[0,t]:[t];return[...Te(r,a),...s,...f].map(e=>`${e}px`).join(`, `)}function De(e,t,n){return e?Ee(n,t,`${e}`.split(/[,\s]+/gim).map(e=>parseFloat(e))):U(t,n)}function Oe(e){e.animationElapsedTime,e.isAnimating,e.isEntrance;var t=e.visibleLength,n=e.strokeDasharray,r=e.connectNulls,i=xe(e,be),a=r??!1,o;if(t!=null){var s=i.pathRef;o=De(n,Ce(s?.current??null),t)}else n!=null&&(o=String(n));return V.createElement(_,H({},i,{connectNulls:a,strokeDasharray:o}))}function ke(e){var t=(0,V.useRef)(0),n=(0,V.useRef)(0),r=(0,V.useRef)(!1),i=(0,V.useRef)(e);return i.current!==e&&(t.current=n.current,i.current=e),(0,V.useCallback)((e,i)=>{if(r.current)return null;var a=Math.min(N(t.current+e*i),i);return e>0&&i>0&&(n.current=Math.max(n.current,a),a>=i)?(r.current=!0,null):a},[])}var Ae=[`children`];function je(e,t){if(e==null)return{};var n,r,i=Me(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r({x:0,y:0,value:0}),errorBarOffset:0});function Pe(e){var t=e.children,n=je(e,Ae);return V.createElement(Ne.Provider,{value:n},t)}var W=(e,t,n,r)=>i(e,`xAxis`,t,r),G=(e,t,n,r)=>h(e,`xAxis`,t,r),K=(e,t,n,r)=>i(e,`yAxis`,n,r),q=(e,t,n,r)=>h(e,`yAxis`,n,r),Fe=x([n,W,K,G,q],(e,t,n,r,i)=>L(e,`xAxis`)?D(t,r,!1):D(n,i,!1)),Ie=(e,t,n,r,i)=>i;function Le(e){return e.type===`line`}var Re=x([m,Ie],(e,t)=>e.filter(Le).find(e=>e.id===t)),ze=x([n,W,K,G,q,Re,Fe,s],(e,t,n,r,i,a,o,s)=>{var c=s.chartData,l=s.dataStartIndex,u=s.dataEndIndex;if(!(a==null||t==null||n==null||r==null||i==null||r.length===0||i.length===0||o==null||e!==`horizontal`&&e!==`vertical`)){var d=a.dataKey,f=a.data,p=f!=null&&f.length>0?f:c?.slice(l,u+1);if(p!=null)return at({layout:e,xAxis:t,yAxis:n,xAxisTicks:r,yAxisTicks:i,dataKey:d,bandSize:o,displayedData:p})}}),Be=[`id`],Ve=[`type`,`layout`,`connectNulls`,`needClip`,`shape`,`strokeDasharray`],He=[`activeDot`,`animateNewValues`,`animationBegin`,`animationDuration`,`animationEasing`,`connectNulls`,`dot`,`hide`,`isAnimationActive`,`label`,`legendType`,`xAxisId`,`yAxisId`,`id`];function J(){return J=Object.assign?Object.assign.bind():function(e){for(var t=1;t0?t/n:0}var Q={activeDot:!0,animateNewValues:!0,animationBegin:0,animationDuration:1500,animationEasing:`ease`,animationInterpolateFn:(e,t)=>{if(e==null)return[];if(t===1)return e.flatMap(e=>e.status===`removed`?[]:[e.next]);var n=Je(e),r=[];for(var i of e)if(i.status===`matched`)r.push(Z(Z({},i.next),{},{x:l(i.prev.x,i.next.x,t),y:l(i.prev.y,i.next.y,t)}));else if(i.status===`added`){if(i.next.x!=null){var a=i.next.x-n;r.push(Z(Z({},i.next),{},{x:l(a,i.next.x,t),y:i.next.y}))}else r.push(i.next)}else if(i.status===`removed`&&i.prev.x!=null){var o=i.prev.x+n;r.push(Z(Z({},i.prev),{},{x:l(i.prev.x,o,t),y:i.prev.y}))}return r},animationMatchBy:ve,connectNulls:!1,dot:!0,fill:`#fff`,hide:!1,isAnimationActive:`auto`,label:!1,legendType:`line`,shape:Oe,stroke:`#3182bd`,strokeWidth:1,xAxisId:0,yAxisId:0,zIndex:g.line,type:`linear`},Ye=e=>{var t=e.dataKey,n=e.name,r=e.stroke,i=e.legendType;return[{inactive:e.hide,dataKey:t,type:i,color:r,value:j(n,t),payload:e}]},Xe=V.memo(e=>{var t=e.dataKey,n=e.data,r=e.stroke,i=e.strokeWidth,a=e.fill,o=e.name,s=e.hide,c=e.unit,l=e.formatter,u=e.tooltipType,d=e.id,f={dataDefinedOnItem:n,getPosition:le,settings:{stroke:r,strokeWidth:i,fill:a,dataKey:t,nameKey:void 0,name:j(o,t),hide:s,type:u,color:r,unit:c,formatter:l,graphicalItemId:d}};return V.createElement(ge,{tooltipEntrySettings:f})});function Ze(e){var t=e.clipPathId,n=e.points,r=e.props,i=r.dot,a=r.dataKey,o=r.needClip;r.id;var s=Y(r,Be),c=A(s);return V.createElement(I,{points:n,dot:i,className:`recharts-line-dots`,dotClassName:`recharts-line-dot`,dataKey:a,baseProps:c,needClip:o,clipPathId:t})}function Qe(e){var t=e.showLabels,n=e.children,r=e.points,i=(0,V.useMemo)(()=>r?.map(e=>{var t={x:e.x??0,y:e.y??0,width:0,lowerWidth:0,upperWidth:0,height:0};return Z(Z({},t),{},{value:e.value,payload:e.payload,viewBox:t,parentViewBox:void 0,fill:void 0})}),[r]);return V.createElement(me,{value:t?i:void 0},n)}function $e(e){var t=e.clipPathId,n=e.pathRef,r=e.points,i=e.props,a=e.animationElapsedTime,o=e.isAnimating,s=e.isEntrance,c=e.visibleLength,l=i.type,d=i.layout,f=i.connectNulls,p=i.needClip,m=i.shape,h=i.strokeDasharray,g=Y(i,Ve),_=Z(Z({},O(g)),{},{fill:`none`,className:`recharts-line-curve`,clipPath:p?`url(#clipPath-${t})`:void 0,points:r,type:l,layout:d,connectNulls:f,strokeDasharray:h??i.strokeDasharray,pathRef:n,animationElapsedTime:a,isAnimating:o,isEntrance:i.animateNewValues?s:!1,visibleLength:c});return V.createElement(V.Fragment,null,r?.length>1&&V.createElement(u,{option:m,DefaultShape:Q.shape,shapeProps:_}),V.createElement(Ze,{points:r,clipPathId:t,props:i}))}function et(e){var t=e.clipPathId,n=e.props,r=e.pathRef,i=e.previousPointsRef,a=n.points,o=n.isAnimationActive,s=n.animationBegin,c=n.animationDuration,l=n.animationEasing,u=n.animationMatchBy,d=n.animationInterpolateFn,p=n.layout,m=qe(r.current),h=pe(n.onAnimationStart,n.onAnimationEnd),g=h.isAnimating,_=h.handleAnimationStart,v=h.handleAnimationEnd,b=!g,x=ke(a),S=(0,V.useCallback)(e=>e>0&&m>0,[m]);return V.createElement(Qe,{points:a,showLabels:b},n.children,V.createElement(y,{animationInput:a,animationIdPrefix:`recharts-line-`,items:a,previousItemsRef:i,isAnimationActive:o,animationBegin:s,animationDuration:c,animationEasing:l,onAnimationStart:_,onAnimationEnd:v,animationInterpolateFn:d,animationMatchBy:u,shouldUpdatePreviousRef:S,layout:p},(e,i,a)=>{var o=g||i<1,s=o?x(i,m):null;return V.createElement($e,{props:n,points:e,clipPathId:t,pathRef:r,animationElapsedTime:i,isAnimating:o,isEntrance:a,visibleLength:s})}),V.createElement(f,{label:n.label}))}function tt(e){var t=e.clipPathId,n=e.props,r=(0,V.useRef)(null),i=(0,V.useRef)(null);return V.createElement(et,{props:n,clipPathId:t,previousPointsRef:r,pathRef:i})}var nt=(e,t)=>({x:e.x??void 0,y:e.y??void 0,value:e.value,errorVal:R(e.payload,t)}),rt=class extends V.Component{render(){var e=this.props,t=e.hide,n=e.dot,r=e.points,i=e.className,o=e.xAxisId,s=e.yAxisId,c=e.top,l=e.left,u=e.width,f=e.height,p=e.id,m=e.needClip,h=e.zIndex;if(t)return null;var g=d(`recharts-line`,i),_=p,v=oe(n),y=v.r,b=v.strokeWidth,x=a(n),S=y*2+b,C=m?`url(#clipPath-${x?``:`dots-`}${_})`:void 0;return V.createElement(P,{zIndex:h},V.createElement(fe,{className:g},m&&V.createElement(`defs`,null,V.createElement(ue,{clipPathId:_,xAxisId:o,yAxisId:s}),!x&&V.createElement(`clipPath`,{id:`clipPath-dots-${_}`},V.createElement(`rect`,{x:l-S/2,y:c-S/2,width:u+S,height:f+S}))),V.createElement(Pe,{xAxisId:o,yAxisId:s,data:r,dataPointFormatter:nt,errorBarOffset:0},V.createElement(tt,{props:this.props,clipPathId:_}))),V.createElement(k,{activeDot:this.props.activeDot,points:r,mainColor:this.props.stroke,itemDataKey:this.props.dataKey,clipPath:C}))}};function it(e){var t=v(e,Q),n=t.activeDot,r=t.animateNewValues,i=t.animationBegin,a=t.animationDuration,o=t.animationEasing,s=t.connectNulls,c=t.dot,l=t.hide,u=t.isAnimationActive,d=t.label,f=t.legendType,p=t.xAxisId,m=t.yAxisId,h=t.id,g=Y(t,He),_=E(p,m).needClip,y=se(),b=de(),x=z(),S=ye(e=>ze(e,p,m,x,h));if(b!==`horizontal`&&b!==`vertical`||S==null||y==null)return null;var C=y.height,w=y.width,T=y.x,D=y.y;return V.createElement(rt,J({},g,{id:h,connectNulls:s,dot:c,activeDot:n,animateNewValues:r,animationBegin:i,animationDuration:a,animationEasing:o,isAnimationActive:u,hide:l,label:d,legendType:f,xAxisId:p,yAxisId:m,points:S,layout:b,height:C,width:w,left:T,top:D,needClip:_}))}function at(e){var t=e.layout,n=e.xAxis,i=e.yAxis,a=e.xAxisTicks,o=e.yAxisTicks,s=e.dataKey,c=e.bandSize;return e.displayedData.map((e,l)=>{var u=R(e,s);if(t===`horizontal`)return{x:F({axis:n,ticks:a,bandSize:c,entry:e,index:l}),y:(r(u)?null:i.scale.map(u))??null,value:u,payload:e};var d=r(u)?null:n.scale.map(u),f=F({axis:i,ticks:o,bandSize:c,entry:e,index:l});return d==null||f==null?null:{x:d,y:f,value:u,payload:e}}).filter(Boolean)}function ot(e){var t=v(e,Q),n=z();return V.createElement(M,{id:t.id,type:`line`},e=>V.createElement(V.Fragment,null,V.createElement(w,{legendPayload:Ye(t)}),V.createElement(Xe,{dataKey:t.dataKey,data:t.data,stroke:t.stroke,strokeWidth:t.strokeWidth,fill:t.fill,name:t.name,hide:t.hide,unit:t.unit,formatter:t.formatter,tooltipType:t.tooltipType,id:e}),V.createElement(te,{type:`line`,id:e,data:t.data,xAxisId:t.xAxisId,yAxisId:t.yAxisId,zAxisId:0,dataKey:t.dataKey,hide:t.hide,isPanorama:n}),V.createElement(it,J({},t,{id:e}))))}var st=V.memo(ot,c);st.displayName=`Line`;var ct=[`axis`],lt=(0,V.forwardRef)((e,t)=>V.createElement(ae,{chartName:`ComposedChart`,defaultTooltipEventType:`axis`,validateTooltipEventTypes:ct,tooltipPayloadSearcher:o,categoricalChartProps:e,ref:t})),ut={metric:24,title:18,section:15,body:13,xs:12,caption:12},$=_e();function dt(e,t,n){let r=new Date(e);return Number.isFinite(r.getTime())?new Intl.DateTimeFormat(n===`zh`?`zh-CN`:`en-US`,t?{month:`short`,day:`numeric`}:{hour:`2-digit`,minute:`2-digit`,hour12:!1}).format(r):``}function ft(e){if(e<=1)return 1;let t=10**Math.floor(Math.log10(e)),n=e/t;return(n<=1?1:n<=2?2:n<=5?5:10)*t}function pt(e){let t=Math.max(0,e.ok??e.requests-e.error),n=Math.min(Math.max(0,e.requests-t),Math.max(0,e.requests));return{...e,okCount:t,errorCount:n,inputTokens:e.prompt_tokens??0,outputTokens:e.completion_tokens??0,cacheTokens:e.cache_read_tokens??0,p50:e.latency_p50_ms??null,p95:e.latency_p95_ms??null}}function mt({active:e,payload:t,daily:n,lang:r,specs:i,format:a,totalLabel:o,total:s}){if(!e||!t?.[0])return null;let c=t[0].payload;return(0,$.jsxs)(`div`,{className:`rounded-xl bg-overlay px-3 py-2 text-caption text-overlay-foreground shadow-overlay`,children:[(0,$.jsx)(`div`,{className:`mono text-muted`,children:dt(c.at,n,r)}),(0,$.jsxs)(`div`,{className:`mt-1.5 grid gap-1`,children:[i.map(e=>(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-6`,children:[(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[(0,$.jsx)(`span`,{className:`size-1.5 rounded-full`,style:{background:e.color}}),e.label]}),(0,$.jsx)(`span`,{className:`mono font-medium`,children:a(c[e.key])})]},String(e.key))),s&&o?(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-6 pt-1`,children:[(0,$.jsx)(`span`,{className:`text-muted`,children:o}),(0,$.jsx)(`span`,{className:`mono font-medium`,children:a(s(c))})]}):null]})]})}function ht({series:n,lang:r,emptyLabel:i,okLabel:a,errorLabel:o,onSelectRange:s,selectHint:c,labels:l,initialMetric:u=`requests`}){let[d,f]=(0,V.useState)(u),[p,m]=(0,V.useState)(null),[h,g]=(0,V.useState)(null),_=(0,V.useRef)(!1),v=(0,V.useRef)(null),y=(0,V.useMemo)(()=>n.map(pt),[n]),x=y[0],w=y[y.length-1],E=(x&&w?Date.parse(w.at)-Date.parse(x.at):0)>1728e5,D=l??{requests:`Requests`,tokens:`Tokens`,latency:`p95 latency`,input:`input`,output:`output`,cacheRead:`cache read`,p50:`p50`,p95:`p95`,total:`total`},O=d===`tokens`?[{key:`inputTokens`,label:D.input,color:`var(--foreground)`,area:!0,width:1.5,opacity:.75},{key:`outputTokens`,label:D.output,color:`var(--success)`,area:!0,width:1.5},{key:`cacheTokens`,label:D.cacheRead,color:`var(--muted)`,width:1.25,opacity:.8}]:d===`latency`?[{key:`p95`,label:D.p95,color:`var(--warning)`,area:!0,width:1.75},{key:`p50`,label:D.p50,color:`var(--muted)`,width:1.25,opacity:.85}]:[{key:`okCount`,label:a,color:`var(--success)`,area:!0,width:1.75},{key:`errorCount`,label:o,color:`var(--danger)`,area:!0,width:1.25,opacity:.7}],k=e=>e==null?`—`:d===`latency`?B(e):b(e),A=d===`requests`?e=>e.requests:d===`tokens`?e=>e.inputTokens+e.outputTokens:void 0,j=y.flatMap(e=>O.map(t=>Number(e[t.key]??0))),M=d===`requests`?Math.max(0,...y.map(e=>e.requests)):Math.max(0,...j),te=ft(M),N=y.some(e=>e.requests>0),P=y.map(e=>`${e.at}:${e.requests}:${e.error}`).join(`|`);(0,V.useLayoutEffect)(()=>{let e=v.current;if(!e||!N||_.current)return;let t=!1,n=null,r=T.context(()=>{let r=T.matchMedia(),i=()=>{let t=T.utils.toArray(`.recharts-area-area`,e),n=T.utils.toArray(`.recharts-line-curve`,e);return!t.length&&!n.length?!1:(r.add(`(prefers-reduced-motion: reduce)`,()=>{T.set(n,{clearProps:`strokeDashoffset,strokeDasharray`}),T.set(t,{clearProps:`autoAlpha`})}),r.add(`(prefers-reduced-motion: no-preference)`,()=>{T.fromTo(t,{autoAlpha:0},{autoAlpha:1,duration:.45,ease:`power2.out`,stagger:.05}),n.forEach((e,t)=>{let n=e.getTotalLength();T.fromTo(e,{strokeDasharray:n,strokeDashoffset:n},{strokeDashoffset:0,duration:.7,delay:t*.05,ease:`power2.out`})})}),!0)};if(i()){t=!0,_.current=!0;return}n=new MutationObserver(()=>{t||i()&&(t=!0,_.current=!0,n?.disconnect())}),n.observe(e,{childList:!0,subtree:!0})},e);return()=>{n?.disconnect(),r.revert()}},[N,P]);let F=y.length>1?Date.parse(y[1].at)-Date.parse(y[0].at):36e5,I=()=>{if(p&&h&&s){let e=Date.parse(p),t=Date.parse(h);if(Number.isFinite(e)&&Number.isFinite(t)){let n=Math.min(e,t),r=Math.max(e,t)+F;s(new Date(n).toISOString(),new Date(r).toISOString())}}m(null),g(null)},L=e=>{let t=e?.activeLabel;return t==null?null:String(t)};return!n.length||!N?(0,$.jsx)(ne,{emptyLabel:i}):(0,$.jsxs)(`div`,{ref:v,className:`min-w-0`,children:[(0,$.jsx)(`div`,{className:`mb-3 flex justify-end`,children:(0,$.jsx)(ie,{value:d,onChange:e=>f(e),ariaLabel:D.requests,options:[{id:`requests`,label:D.requests},{id:`tokens`,label:D.tokens},{id:`latency`,label:D.latency}]})}),(0,$.jsx)(`div`,{className:`h-56 w-full`,children:(0,$.jsx)(C,{width:`100%`,height:`100%`,children:(0,$.jsxs)(lt,{data:y,margin:{top:8,right:8,bottom:0,left:0},style:s?{cursor:`crosshair`,userSelect:`none`}:void 0,onMouseDown:s?e=>{let t=L(e);t&&(m(t),g(t))}:void 0,onMouseMove:s?e=>{if(!p)return;let t=L(e);t&&g(t)}:void 0,onMouseUp:s?I:void 0,onMouseLeave:s?()=>{m(null),g(null)}:void 0,children:[(0,$.jsx)(`defs`,{children:O.filter(e=>e.area).map(e=>(0,$.jsxs)(`linearGradient`,{id:`traffic-fill-${String(e.key)}`,x1:`0`,y1:`0`,x2:`0`,y2:`1`,children:[(0,$.jsx)(`stop`,{offset:`0%`,stopColor:e.color,stopOpacity:.22}),(0,$.jsx)(`stop`,{offset:`100%`,stopColor:e.color,stopOpacity:.02})]},String(e.key)))}),(0,$.jsx)(t,{vertical:!1,stroke:`var(--separator)`,strokeDasharray:`3 6`}),(0,$.jsx)(re,{dataKey:`at`,tickLine:!1,axisLine:!1,minTickGap:28,tick:{fill:`var(--muted)`,fontSize:ut.caption,fontFamily:`var(--font-mono)`},tickFormatter:e=>dt(e,E,r)}),(0,$.jsx)(ce,{width:d===`latency`?44:36,domain:[0,te],tickLine:!1,axisLine:!1,allowDecimals:!1,tick:{fill:`var(--muted)`,fontSize:ut.caption,fontFamily:`var(--font-mono)`},tickFormatter:e=>d===`latency`?B(e):b(e)}),(0,$.jsx)(e,{cursor:{stroke:`var(--border)`,strokeDasharray:`3 3`},content:(0,$.jsx)(mt,{daily:E,lang:r,specs:O,format:k,totalLabel:D.total,total:A})}),O.filter(e=>e.area).map(e=>(0,$.jsx)(S,{type:`monotone`,dataKey:e.key,stroke:`none`,fill:`url(#traffic-fill-${String(e.key)})`,isAnimationActive:!1,activeDot:!1,dot:!1,connectNulls:!0},`area-${String(e.key)}`)),O.map(e=>(0,$.jsx)(st,{type:`monotone`,dataKey:e.key,stroke:e.color,strokeWidth:e.width??1.5,strokeOpacity:e.opacity??1,dot:!1,isAnimationActive:!1,connectNulls:!0,activeDot:{r:4,fill:`var(--surface)`,stroke:e.color,strokeWidth:1.5}},`line-${String(e.key)}`)),p&&h?(0,$.jsx)(ee,{x1:p,x2:h,fill:`var(--foreground)`,fillOpacity:.06,stroke:`none`,ifOverflow:`visible`}):null]})})}),(0,$.jsxs)(`div`,{className:`mt-2 flex min-h-5 items-center justify-between gap-3 text-caption text-muted`,children:[(0,$.jsx)(`div`,{className:`flex items-center gap-3`,children:O.map(e=>(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[(0,$.jsx)(`span`,{className:`size-1.5 rounded-full`,style:{background:e.color}}),e.label]},String(e.key)))}),(0,$.jsxs)(`span`,{className:`flex items-center gap-3`,children:[s&&c?(0,$.jsx)(`span`,{children:c}):null,(0,$.jsxs)(`span`,{className:`mono`,children:[k(M),` peak`]})]})]})]})}export{ht as TrafficChart}; \ No newline at end of file +import{n as e,t}from"./CartesianGrid-BfqmuU7N.js";import{$ as n,$t as r,B as i,C as a,D as o,G as s,Q as c,Qt as l,S as u,Sn as d,T as f,Tn as p,U as m,V as h,W as g,Y as _,Yt as v,_ as y,_n as b,_t as x,a as S,at as C,b as w,bn as T,c as E,ct as D,d as ee,dn as O,f as k,fn as A,ft as j,g as M,h as te,hn as ne,i as re,in as N,k as P,lt as F,m as I,mn as ie,mt as L,n as ae,o as oe,p as se,pt as R,r as ce,rn as le,s as ue,st as z,tt as de,un as fe,v as pe,w as me,wn as he,x as ge,xn as _e,y as ve,yn as B,yt as ye}from"./index-eWoLzhb1.js";var V=p(he()),be=[`animationElapsedTime`,`isAnimating`,`isEntrance`,`visibleLength`,`strokeDasharray`,`connectNulls`];function H(){return H=Object.assign?Object.assign.bind():function(e){for(var t=1;te+t,0);if(!i)return U(t,e);for(var a=Math.floor(e/i),o=e%i,s=[],c=0,l=0;co){s=[...r.slice(0,c),o-l];break}}var f=s.length%2==0?[0,t]:[t];return[...Te(r,a),...s,...f].map(e=>`${e}px`).join(`, `)}function De(e,t,n){return e?Ee(n,t,`${e}`.split(/[,\s]+/gim).map(e=>parseFloat(e))):U(t,n)}function Oe(e){e.animationElapsedTime,e.isAnimating,e.isEntrance;var t=e.visibleLength,n=e.strokeDasharray,r=e.connectNulls,i=xe(e,be),a=r??!1,o;if(t!=null){var s=i.pathRef;o=De(n,Ce(s?.current??null),t)}else n!=null&&(o=String(n));return V.createElement(_,H({},i,{connectNulls:a,strokeDasharray:o}))}function ke(e){var t=(0,V.useRef)(0),n=(0,V.useRef)(0),r=(0,V.useRef)(!1),i=(0,V.useRef)(e);return i.current!==e&&(t.current=n.current,i.current=e),(0,V.useCallback)((e,i)=>{if(r.current)return null;var a=Math.min(N(t.current+e*i),i);return e>0&&i>0&&(n.current=Math.max(n.current,a),a>=i)?(r.current=!0,null):a},[])}var Ae=[`children`];function je(e,t){if(e==null)return{};var n,r,i=Me(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r({x:0,y:0,value:0}),errorBarOffset:0});function Pe(e){var t=e.children,n=je(e,Ae);return V.createElement(Ne.Provider,{value:n},t)}var W=(e,t,n,r)=>i(e,`xAxis`,t,r),G=(e,t,n,r)=>h(e,`xAxis`,t,r),K=(e,t,n,r)=>i(e,`yAxis`,n,r),q=(e,t,n,r)=>h(e,`yAxis`,n,r),Fe=x([n,W,K,G,q],(e,t,n,r,i)=>L(e,`xAxis`)?D(t,r,!1):D(n,i,!1)),Ie=(e,t,n,r,i)=>i;function Le(e){return e.type===`line`}var Re=x([m,Ie],(e,t)=>e.filter(Le).find(e=>e.id===t)),ze=x([n,W,K,G,q,Re,Fe,s],(e,t,n,r,i,a,o,s)=>{var c=s.chartData,l=s.dataStartIndex,u=s.dataEndIndex;if(!(a==null||t==null||n==null||r==null||i==null||r.length===0||i.length===0||o==null||e!==`horizontal`&&e!==`vertical`)){var d=a.dataKey,f=a.data,p=f!=null&&f.length>0?f:c?.slice(l,u+1);if(p!=null)return at({layout:e,xAxis:t,yAxis:n,xAxisTicks:r,yAxisTicks:i,dataKey:d,bandSize:o,displayedData:p})}}),Be=[`id`],Ve=[`type`,`layout`,`connectNulls`,`needClip`,`shape`,`strokeDasharray`],He=[`activeDot`,`animateNewValues`,`animationBegin`,`animationDuration`,`animationEasing`,`connectNulls`,`dot`,`hide`,`isAnimationActive`,`label`,`legendType`,`xAxisId`,`yAxisId`,`id`];function J(){return J=Object.assign?Object.assign.bind():function(e){for(var t=1;t0?t/n:0}var Q={activeDot:!0,animateNewValues:!0,animationBegin:0,animationDuration:1500,animationEasing:`ease`,animationInterpolateFn:(e,t)=>{if(e==null)return[];if(t===1)return e.flatMap(e=>e.status===`removed`?[]:[e.next]);var n=Je(e),r=[];for(var i of e)if(i.status===`matched`)r.push(Z(Z({},i.next),{},{x:l(i.prev.x,i.next.x,t),y:l(i.prev.y,i.next.y,t)}));else if(i.status===`added`){if(i.next.x!=null){var a=i.next.x-n;r.push(Z(Z({},i.next),{},{x:l(a,i.next.x,t),y:i.next.y}))}else r.push(i.next)}else if(i.status===`removed`&&i.prev.x!=null){var o=i.prev.x+n;r.push(Z(Z({},i.prev),{},{x:l(i.prev.x,o,t),y:i.prev.y}))}return r},animationMatchBy:ve,connectNulls:!1,dot:!0,fill:`#fff`,hide:!1,isAnimationActive:`auto`,label:!1,legendType:`line`,shape:Oe,stroke:`#3182bd`,strokeWidth:1,xAxisId:0,yAxisId:0,zIndex:g.line,type:`linear`},Ye=e=>{var t=e.dataKey,n=e.name,r=e.stroke,i=e.legendType;return[{inactive:e.hide,dataKey:t,type:i,color:r,value:j(n,t),payload:e}]},Xe=V.memo(e=>{var t=e.dataKey,n=e.data,r=e.stroke,i=e.strokeWidth,a=e.fill,o=e.name,s=e.hide,c=e.unit,l=e.formatter,u=e.tooltipType,d=e.id,f={dataDefinedOnItem:n,getPosition:le,settings:{stroke:r,strokeWidth:i,fill:a,dataKey:t,nameKey:void 0,name:j(o,t),hide:s,type:u,color:r,unit:c,formatter:l,graphicalItemId:d}};return V.createElement(ge,{tooltipEntrySettings:f})});function Ze(e){var t=e.clipPathId,n=e.points,r=e.props,i=r.dot,a=r.dataKey,o=r.needClip;r.id;var s=Y(r,Be),c=A(s);return V.createElement(I,{points:n,dot:i,className:`recharts-line-dots`,dotClassName:`recharts-line-dot`,dataKey:a,baseProps:c,needClip:o,clipPathId:t})}function Qe(e){var t=e.showLabels,n=e.children,r=e.points,i=(0,V.useMemo)(()=>r?.map(e=>{var t={x:e.x??0,y:e.y??0,width:0,lowerWidth:0,upperWidth:0,height:0};return Z(Z({},t),{},{value:e.value,payload:e.payload,viewBox:t,parentViewBox:void 0,fill:void 0})}),[r]);return V.createElement(me,{value:t?i:void 0},n)}function $e(e){var t=e.clipPathId,n=e.pathRef,r=e.points,i=e.props,a=e.animationElapsedTime,o=e.isAnimating,s=e.isEntrance,c=e.visibleLength,l=i.type,d=i.layout,f=i.connectNulls,p=i.needClip,m=i.shape,h=i.strokeDasharray,g=Y(i,Ve),_=Z(Z({},O(g)),{},{fill:`none`,className:`recharts-line-curve`,clipPath:p?`url(#clipPath-${t})`:void 0,points:r,type:l,layout:d,connectNulls:f,strokeDasharray:h??i.strokeDasharray,pathRef:n,animationElapsedTime:a,isAnimating:o,isEntrance:i.animateNewValues?s:!1,visibleLength:c});return V.createElement(V.Fragment,null,r?.length>1&&V.createElement(u,{option:m,DefaultShape:Q.shape,shapeProps:_}),V.createElement(Ze,{points:r,clipPathId:t,props:i}))}function et(e){var t=e.clipPathId,n=e.props,r=e.pathRef,i=e.previousPointsRef,a=n.points,o=n.isAnimationActive,s=n.animationBegin,c=n.animationDuration,l=n.animationEasing,u=n.animationMatchBy,d=n.animationInterpolateFn,p=n.layout,m=qe(r.current),h=pe(n.onAnimationStart,n.onAnimationEnd),g=h.isAnimating,_=h.handleAnimationStart,v=h.handleAnimationEnd,b=!g,x=ke(a),S=(0,V.useCallback)(e=>e>0&&m>0,[m]);return V.createElement(Qe,{points:a,showLabels:b},n.children,V.createElement(y,{animationInput:a,animationIdPrefix:`recharts-line-`,items:a,previousItemsRef:i,isAnimationActive:o,animationBegin:s,animationDuration:c,animationEasing:l,onAnimationStart:_,onAnimationEnd:v,animationInterpolateFn:d,animationMatchBy:u,shouldUpdatePreviousRef:S,layout:p},(e,i,a)=>{var o=g||i<1,s=o?x(i,m):null;return V.createElement($e,{props:n,points:e,clipPathId:t,pathRef:r,animationElapsedTime:i,isAnimating:o,isEntrance:a,visibleLength:s})}),V.createElement(f,{label:n.label}))}function tt(e){var t=e.clipPathId,n=e.props,r=(0,V.useRef)(null),i=(0,V.useRef)(null);return V.createElement(et,{props:n,clipPathId:t,previousPointsRef:r,pathRef:i})}var nt=(e,t)=>({x:e.x??void 0,y:e.y??void 0,value:e.value,errorVal:R(e.payload,t)}),rt=class extends V.Component{render(){var e=this.props,t=e.hide,n=e.dot,r=e.points,i=e.className,o=e.xAxisId,s=e.yAxisId,c=e.top,l=e.left,u=e.width,f=e.height,p=e.id,m=e.needClip,h=e.zIndex;if(t)return null;var g=d(`recharts-line`,i),_=p,v=oe(n),y=v.r,b=v.strokeWidth,x=a(n),S=y*2+b,C=m?`url(#clipPath-${x?``:`dots-`}${_})`:void 0;return V.createElement(P,{zIndex:h},V.createElement(fe,{className:g},m&&V.createElement(`defs`,null,V.createElement(ue,{clipPathId:_,xAxisId:o,yAxisId:s}),!x&&V.createElement(`clipPath`,{id:`clipPath-dots-${_}`},V.createElement(`rect`,{x:l-S/2,y:c-S/2,width:u+S,height:f+S}))),V.createElement(Pe,{xAxisId:o,yAxisId:s,data:r,dataPointFormatter:nt,errorBarOffset:0},V.createElement(tt,{props:this.props,clipPathId:_}))),V.createElement(k,{activeDot:this.props.activeDot,points:r,mainColor:this.props.stroke,itemDataKey:this.props.dataKey,clipPath:C}))}};function it(e){var t=v(e,Q),n=t.activeDot,r=t.animateNewValues,i=t.animationBegin,a=t.animationDuration,o=t.animationEasing,s=t.connectNulls,c=t.dot,l=t.hide,u=t.isAnimationActive,d=t.label,f=t.legendType,p=t.xAxisId,m=t.yAxisId,h=t.id,g=Y(t,He),_=E(p,m).needClip,y=se(),b=de(),x=z(),S=ye(e=>ze(e,p,m,x,h));if(b!==`horizontal`&&b!==`vertical`||S==null||y==null)return null;var C=y.height,w=y.width,T=y.x,D=y.y;return V.createElement(rt,J({},g,{id:h,connectNulls:s,dot:c,activeDot:n,animateNewValues:r,animationBegin:i,animationDuration:a,animationEasing:o,isAnimationActive:u,hide:l,label:d,legendType:f,xAxisId:p,yAxisId:m,points:S,layout:b,height:C,width:w,left:T,top:D,needClip:_}))}function at(e){var t=e.layout,n=e.xAxis,i=e.yAxis,a=e.xAxisTicks,o=e.yAxisTicks,s=e.dataKey,c=e.bandSize;return e.displayedData.map((e,l)=>{var u=R(e,s);if(t===`horizontal`)return{x:F({axis:n,ticks:a,bandSize:c,entry:e,index:l}),y:(r(u)?null:i.scale.map(u))??null,value:u,payload:e};var d=r(u)?null:n.scale.map(u),f=F({axis:i,ticks:o,bandSize:c,entry:e,index:l});return d==null||f==null?null:{x:d,y:f,value:u,payload:e}}).filter(Boolean)}function ot(e){var t=v(e,Q),n=z();return V.createElement(M,{id:t.id,type:`line`},e=>V.createElement(V.Fragment,null,V.createElement(w,{legendPayload:Ye(t)}),V.createElement(Xe,{dataKey:t.dataKey,data:t.data,stroke:t.stroke,strokeWidth:t.strokeWidth,fill:t.fill,name:t.name,hide:t.hide,unit:t.unit,formatter:t.formatter,tooltipType:t.tooltipType,id:e}),V.createElement(te,{type:`line`,id:e,data:t.data,xAxisId:t.xAxisId,yAxisId:t.yAxisId,zAxisId:0,dataKey:t.dataKey,hide:t.hide,isPanorama:n}),V.createElement(it,J({},t,{id:e}))))}var st=V.memo(ot,c);st.displayName=`Line`;var ct=[`axis`],lt=(0,V.forwardRef)((e,t)=>V.createElement(ae,{chartName:`ComposedChart`,defaultTooltipEventType:`axis`,validateTooltipEventTypes:ct,tooltipPayloadSearcher:o,categoricalChartProps:e,ref:t})),ut={metric:24,title:18,section:15,body:13,xs:12,caption:12},$=_e();function dt(e,t,n){let r=new Date(e);return Number.isFinite(r.getTime())?new Intl.DateTimeFormat(n===`zh`?`zh-CN`:`en-US`,t?{month:`short`,day:`numeric`}:{hour:`2-digit`,minute:`2-digit`,hour12:!1}).format(r):``}function ft(e){if(e<=1)return 1;let t=10**Math.floor(Math.log10(e)),n=e/t;return(n<=1?1:n<=2?2:n<=5?5:10)*t}function pt(e){let t=Math.max(0,e.ok??e.requests-e.error),n=Math.min(Math.max(0,e.requests-t),Math.max(0,e.requests));return{...e,okCount:t,errorCount:n,inputTokens:e.prompt_tokens??0,outputTokens:e.completion_tokens??0,cacheTokens:e.cache_read_tokens??0,p50:e.latency_p50_ms??null,p95:e.latency_p95_ms??null}}function mt({active:e,payload:t,daily:n,lang:r,specs:i,format:a,totalLabel:o,total:s}){if(!e||!t?.[0])return null;let c=t[0].payload;return(0,$.jsxs)(`div`,{className:`rounded-xl bg-overlay px-3 py-2 text-caption text-overlay-foreground shadow-overlay`,children:[(0,$.jsx)(`div`,{className:`mono text-muted`,children:dt(c.at,n,r)}),(0,$.jsxs)(`div`,{className:`mt-1.5 grid gap-1`,children:[i.map(e=>(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-6`,children:[(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[(0,$.jsx)(`span`,{className:`size-1.5 rounded-full`,style:{background:e.color}}),e.label]}),(0,$.jsx)(`span`,{className:`mono font-medium`,children:a(c[e.key])})]},String(e.key))),s&&o?(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-6 pt-1`,children:[(0,$.jsx)(`span`,{className:`text-muted`,children:o}),(0,$.jsx)(`span`,{className:`mono font-medium`,children:a(s(c))})]}):null]})]})}function ht({series:n,lang:r,emptyLabel:i,okLabel:a,errorLabel:o,onSelectRange:s,selectHint:c,labels:l,initialMetric:u=`requests`}){let[d,f]=(0,V.useState)(u),[p,m]=(0,V.useState)(null),[h,g]=(0,V.useState)(null),_=(0,V.useRef)(!1),v=(0,V.useRef)(null),y=(0,V.useMemo)(()=>n.map(pt),[n]),x=y[0],w=y[y.length-1],E=(x&&w?Date.parse(w.at)-Date.parse(x.at):0)>1728e5,D=l??{requests:`Requests`,tokens:`Tokens`,latency:`p95 latency`,input:`input`,output:`output`,cacheRead:`cache read`,p50:`p50`,p95:`p95`,total:`total`},O=d===`tokens`?[{key:`inputTokens`,label:D.input,color:`var(--foreground)`,area:!0,width:1.5,opacity:.75},{key:`outputTokens`,label:D.output,color:`var(--success)`,area:!0,width:1.5},{key:`cacheTokens`,label:D.cacheRead,color:`var(--muted)`,width:1.25,opacity:.8}]:d===`latency`?[{key:`p95`,label:D.p95,color:`var(--warning)`,area:!0,width:1.75},{key:`p50`,label:D.p50,color:`var(--muted)`,width:1.25,opacity:.85}]:[{key:`okCount`,label:a,color:`var(--success)`,area:!0,width:1.75},{key:`errorCount`,label:o,color:`var(--danger)`,area:!0,width:1.25,opacity:.7}],k=e=>e==null?`—`:d===`latency`?B(e):b(e),A=d===`requests`?e=>e.requests:d===`tokens`?e=>e.inputTokens+e.outputTokens:void 0,j=y.flatMap(e=>O.map(t=>Number(e[t.key]??0))),M=d===`requests`?Math.max(0,...y.map(e=>e.requests)):Math.max(0,...j),te=ft(M),N=y.some(e=>e.requests>0),P=y.map(e=>`${e.at}:${e.requests}:${e.error}`).join(`|`);(0,V.useLayoutEffect)(()=>{let e=v.current;if(!e||!N||_.current)return;let t=!1,n=null,r=T.context(()=>{let r=T.matchMedia(),i=()=>{let t=T.utils.toArray(`.recharts-area-area`,e),n=T.utils.toArray(`.recharts-line-curve`,e);return!t.length&&!n.length?!1:(r.add(`(prefers-reduced-motion: reduce)`,()=>{T.set(n,{clearProps:`strokeDashoffset,strokeDasharray`}),T.set(t,{clearProps:`autoAlpha`})}),r.add(`(prefers-reduced-motion: no-preference)`,()=>{T.fromTo(t,{autoAlpha:0},{autoAlpha:1,duration:.45,ease:`power2.out`,stagger:.05}),n.forEach((e,t)=>{let n=e.getTotalLength();T.fromTo(e,{strokeDasharray:n,strokeDashoffset:n},{strokeDashoffset:0,duration:.7,delay:t*.05,ease:`power2.out`})})}),!0)};if(i()){t=!0,_.current=!0;return}n=new MutationObserver(()=>{t||i()&&(t=!0,_.current=!0,n?.disconnect())}),n.observe(e,{childList:!0,subtree:!0})},e);return()=>{n?.disconnect(),r.revert()}},[N,P]);let F=y.length>1?Date.parse(y[1].at)-Date.parse(y[0].at):36e5,I=()=>{if(p&&h&&s){let e=Date.parse(p),t=Date.parse(h);if(Number.isFinite(e)&&Number.isFinite(t)){let n=Math.min(e,t),r=Math.max(e,t)+F;s(new Date(n).toISOString(),new Date(r).toISOString())}}m(null),g(null)},L=e=>{let t=e?.activeLabel;return t==null?null:String(t)};return!n.length||!N?(0,$.jsx)(ne,{emptyLabel:i}):(0,$.jsxs)(`div`,{ref:v,className:`min-w-0`,children:[(0,$.jsx)(`div`,{className:`mb-3 flex justify-end`,children:(0,$.jsx)(ie,{value:d,onChange:e=>f(e),ariaLabel:D.requests,options:[{id:`requests`,label:D.requests},{id:`tokens`,label:D.tokens},{id:`latency`,label:D.latency}]})}),(0,$.jsx)(`div`,{className:`h-56 w-full`,children:(0,$.jsx)(C,{width:`100%`,height:`100%`,children:(0,$.jsxs)(lt,{data:y,margin:{top:8,right:8,bottom:0,left:0},style:s?{cursor:`crosshair`,userSelect:`none`}:void 0,onMouseDown:s?e=>{let t=L(e);t&&(m(t),g(t))}:void 0,onMouseMove:s?e=>{if(!p)return;let t=L(e);t&&g(t)}:void 0,onMouseUp:s?I:void 0,onMouseLeave:s?()=>{m(null),g(null)}:void 0,children:[(0,$.jsx)(`defs`,{children:O.filter(e=>e.area).map(e=>(0,$.jsxs)(`linearGradient`,{id:`traffic-fill-${String(e.key)}`,x1:`0`,y1:`0`,x2:`0`,y2:`1`,children:[(0,$.jsx)(`stop`,{offset:`0%`,stopColor:e.color,stopOpacity:.22}),(0,$.jsx)(`stop`,{offset:`100%`,stopColor:e.color,stopOpacity:.02})]},String(e.key)))}),(0,$.jsx)(t,{vertical:!1,stroke:`var(--separator)`,strokeDasharray:`3 6`}),(0,$.jsx)(re,{dataKey:`at`,tickLine:!1,axisLine:!1,minTickGap:28,tick:{fill:`var(--muted)`,fontSize:ut.caption,fontFamily:`var(--font-mono)`},tickFormatter:e=>dt(e,E,r)}),(0,$.jsx)(ce,{width:d===`latency`?44:36,domain:[0,te],tickLine:!1,axisLine:!1,allowDecimals:!1,tick:{fill:`var(--muted)`,fontSize:ut.caption,fontFamily:`var(--font-mono)`},tickFormatter:e=>d===`latency`?B(e):b(e)}),(0,$.jsx)(e,{cursor:{stroke:`var(--border)`,strokeDasharray:`3 3`},content:(0,$.jsx)(mt,{daily:E,lang:r,specs:O,format:k,totalLabel:D.total,total:A})}),O.filter(e=>e.area).map(e=>(0,$.jsx)(S,{type:`monotone`,dataKey:e.key,stroke:`none`,fill:`url(#traffic-fill-${String(e.key)})`,isAnimationActive:!1,activeDot:!1,dot:!1,connectNulls:!0},`area-${String(e.key)}`)),O.map(e=>(0,$.jsx)(st,{type:`monotone`,dataKey:e.key,stroke:e.color,strokeWidth:e.width??1.5,strokeOpacity:e.opacity??1,dot:!1,isAnimationActive:!1,connectNulls:!0,activeDot:{r:4,fill:`var(--surface)`,stroke:e.color,strokeWidth:1.5}},`line-${String(e.key)}`)),p&&h?(0,$.jsx)(ee,{x1:p,x2:h,fill:`var(--foreground)`,fillOpacity:.06,stroke:`none`,ifOverflow:`visible`}):null]})})}),(0,$.jsxs)(`div`,{className:`mt-2 flex min-h-5 items-center justify-between gap-3 text-caption text-muted`,children:[(0,$.jsx)(`div`,{className:`flex items-center gap-3`,children:O.map(e=>(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[(0,$.jsx)(`span`,{className:`size-1.5 rounded-full`,style:{background:e.color}}),e.label]},String(e.key)))}),(0,$.jsxs)(`span`,{className:`flex items-center gap-3`,children:[s&&c?(0,$.jsx)(`span`,{children:c}):null,(0,$.jsxs)(`span`,{className:`mono`,children:[k(M),` peak`]})]})]})]})}export{ht as TrafficChart}; \ No newline at end of file diff --git a/internal/webui/static/assets/index-DRQwlrIS.js b/internal/webui/static/assets/index-DRQwlrIS.js deleted file mode 100644 index bfd84c83..00000000 --- a/internal/webui/static/assets/index-DRQwlrIS.js +++ /dev/null @@ -1,174 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/TrafficChart-DCmRhy8p.js","assets/CartesianGrid-sx1L6jkY.js","assets/CostChart-DfOEXWjY.js"])))=>i.map(i=>d[i]); -var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),s=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},c=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},l=(n,r,o)=>(o=n==null?{}:e(i(n)),c(r||!n||!n.__esModule||!a.call(n,`default`)?t(o,`default`,{value:n,enumerable:!0}):o,n));(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var u=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.iterator;function m(e){return typeof e!=`object`||!e?null:(e=p&&e[p]||e[`@@iterator`],typeof e==`function`?e:null)}var h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,_={};function v(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function y(){}y.prototype=v.prototype;function b(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}var x=b.prototype=new y;x.constructor=b,g(x,v.prototype),x.isPureReactComponent=!0;var S=Array.isArray;function C(){}var w={H:null,A:null,T:null,S:null},T=Object.prototype.hasOwnProperty;function E(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function D(e,t){return E(e.type,t,e.props)}function O(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function k(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var A=/\/+/g;function j(e,t){return typeof e==`object`&&e&&e.key!=null?k(``+e.key):t.toString(36)}function M(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(C,C):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function N(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,N(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+j(e,0):a,S(o)?(i=``,c!=null&&(i=c.replace(A,`$&/`)+`/`),N(o,r,i,``,function(e){return e})):o!=null&&(O(o)&&(o=D(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(A,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(S(e))for(var u=0;u{t.exports=u()})),f=o((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0>>1,a=e[r];if(0>>1;ri(c,n))li(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(li(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m){if(n(c)!==null)m=!0,S||(S=!0,O());else{var t=n(l);t!==null&&j(x,t.startTime-e)}}}var S=!1,C=-1,w=5,T=-1;function E(){return g?!0:!(e.unstable_now()-Tt&&E());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&j(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?O():S=!1}}}var O;if(typeof y==`function`)O=function(){y(D)};else if(typeof MessageChannel<`u`){var k=new MessageChannel,A=k.port2;k.port1.onmessage=D,O=function(){A.postMessage(null)}}else O=function(){_(D,0)};function j(t,n){C=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(C),C=-1):h=!0,j(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,O()))),r},e.unstable_shouldYield=E,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),p=o(((e,t)=>{t.exports=f()})),m=o((e=>{var t=d();function n(e){var t=`https://react.dev/errors/`+e;if(1{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=m()})),g=o((e=>{var t=p(),n=d(),r=h();function i(e){var t=`https://react.dev/errors/`+e;if(1B||(e.current=z[B],z[B]=null,B--)}function ee(e,t){B++,z[B]=e.current,e.current=t}var te=V(null),ne=V(null),re=V(null),ie=V(null);function ae(e,t){switch(ee(re,t),ee(ne,e),ee(te,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Yd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Yd(t),e=Xd(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}H(te),ee(te,e)}function oe(){H(te),H(ne),H(re)}function se(e){e.memoizedState!==null&&ee(ie,e);var t=te.current,n=Xd(t,e.type);t!==n&&(ee(ne,e),ee(te,n))}function ce(e){ne.current===e&&(H(te),H(ne)),ie.current===e&&(H(ie),op._currentValue=R)}var le,ue;function de(e){if(le===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);le=t&&t[1]||``,ue=-1)`:-1i||c[r]!==l[i]){var u=` -`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(``)&&(u=u.replace(``,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{fe=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?de(n):``}function me(e,t){switch(e.tag){case 26:case 27:case 5:return de(e.type);case 16:return de(`Lazy`);case 13:return e.child!==t&&t!==null?de(`Suspense Fallback`):de(`Suspense`);case 19:return de(`SuspenseList`);case 0:case 15:return pe(e.type,!1);case 11:return pe(e.type.render,!1);case 1:return pe(e.type,!0);case 31:return de(`Activity`);default:return``}}function he(e){try{var t=``,n=null;do t+=me(e,n),n=e,e=e.return;while(e);return t}catch(e){return` -Error generating stack: `+e.message+` -`+e.stack}}var ge=Object.prototype.hasOwnProperty,_e=t.unstable_scheduleCallback,ve=t.unstable_cancelCallback,ye=t.unstable_shouldYield,be=t.unstable_requestPaint,xe=t.unstable_now,Se=t.unstable_getCurrentPriorityLevel,Ce=t.unstable_ImmediatePriority,we=t.unstable_UserBlockingPriority,Te=t.unstable_NormalPriority,Ee=t.unstable_LowPriority,De=t.unstable_IdlePriority,Oe=t.log,ke=t.unstable_setDisableYieldValue,Ae=null,je=null;function Me(e){if(typeof Oe==`function`&&ke(e),je&&typeof je.setStrictMode==`function`)try{je.setStrictMode(Ae,e)}catch{}}var Ne=Math.clz32?Math.clz32:Ie,Pe=Math.log,Fe=Math.LN2;function Ie(e){return e>>>=0,e===0?32:31-(Pe(e)/Fe|0)|0}var Le=256,Re=262144,ze=4194304;function Be(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Ve(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=Be(n))):i=Be(o):i=Be(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=Be(n))):i=Be(o)):i=Be(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function He(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function Ue(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function We(){var e=ze;return ze<<=1,!(ze&62914560)&&(ze=4194304),e}function Ge(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function Ke(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function qe(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0`u`||window.document===void 0||window.document.createElement===void 0),on=!1;if(an)try{var sn={};Object.defineProperty(sn,"passive",{get:function(){on=!0}}),window.addEventListener(`test`,sn,sn),window.removeEventListener(`test`,sn,sn)}catch{on=!1}var cn=null,ln=null,un=null;function dn(){if(un)return un;var e,t=ln,n=t.length,r,i=`value`in cn?cn.value:cn.textContent,a=i.length;for(e=0;e=Un),Kn=` `,qn=!1;function Jn(e,t){switch(e){case`keyup`:return Vn.indexOf(t.keyCode)!==-1;case`keydown`:return t.keyCode!==229;case`keypress`:case`mousedown`:case`focusout`:return!0;default:return!1}}function Yn(e){return e=e.detail,typeof e==`object`&&`data`in e?e.data:null}var Xn=!1;function Zn(e,t){switch(e){case`compositionend`:return Yn(t);case`keypress`:return t.which===32?(qn=!0,Kn):null;case`textInput`:return e=t.data,e===Kn&&qn?null:e;default:return null}}function Qn(e,t){if(Xn)return e===`compositionend`||!Hn&&Jn(e,t)?(e=dn(),un=ln=cn=null,Xn=!1,e):null;switch(e){case`paste`:return null;case`keypress`:if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}a:{for(;n;){if(n.nextSibling){n=n.nextSibling;break a}n=n.parentNode}n=void 0}n=br(n)}}function Sr(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Sr(e,t.parentNode):`contains`in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Cr(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Mt(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href==`string`}catch{n=!1}if(n)e=t.contentWindow;else break;t=Mt(e.document)}return t}function wr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t===`input`&&(e.type===`text`||e.type===`search`||e.type===`tel`||e.type===`url`||e.type===`password`)||t===`textarea`||e.contentEditable===`true`)}var Tr=an&&`documentMode`in document&&11>=document.documentMode,Er=null,Dr=null,Or=null,kr=!1;function Ar(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;kr||Er==null||Er!==Mt(r)||(r=Er,`selectionStart`in r&&wr(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Or&&yr(Or,r)||(Or=r,r=Nd(Dr,`onSelect`),0>=o,i-=o,Si=1<<32-Ne(t)+i|n<h?(g=d,d=null):g=d.sibling;var _=p(i,d,s[h],c);if(_===null){d===null&&(d=g);break}e&&d&&_.alternate===null&&t(i,d),a=o(_,a,h),u===null?l=_:u.sibling=_,u=_,d=g}if(h===s.length)return n(i,d),ji&&wi(i,h),l;if(d===null){for(;hg?(_=h,h=null):_=h.sibling;var y=p(a,h,v.value,l);if(y===null){h===null&&(h=_);break}e&&h&&y.alternate===null&&t(a,h),s=o(y,s,g),d===null?u=y:d.sibling=y,d=y,h=_}if(v.done)return n(a,h),ji&&wi(a,g),u;if(h===null){for(;!v.done;g++,v=c.next())v=f(a,v.value,l),v!==null&&(s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return ji&&wi(a,g),u}for(h=r(h);!v.done;g++,v=c.next())v=m(h,a,g,v.value,l),v!==null&&(e&&v.alternate!==null&&h.delete(v.key===null?g:v.key),s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return e&&h.forEach(function(e){return t(a,e)}),ji&&wi(a,g),u}function b(e,r,o,c){if(typeof o==`object`&&o&&o.type===y&&o.key===null&&(o=o.props.children),typeof o==`object`&&o){switch(o.$$typeof){case _:a:{for(var l=o.key;r!==null;){if(r.key===l){if(l=o.type,l===y){if(r.tag===7){n(e,r.sibling),c=a(r,o.props.children),c.return=e,e=c;break a}}else if(r.elementType===l||typeof l==`object`&&l&&l.$$typeof===O&&Ta(l)===r.type){n(e,r.sibling),c=a(r,o.props),Ma(c,o),c.return=e,e=c;break a}n(e,r);break}t(e,r),r=r.sibling}o.type===y?(c=li(o.props.children,e.mode,c,o.key),c.return=e,e=c):(c=ci(o.type,o.key,o.props,null,e.mode,c),Ma(c,o),c.return=e,e=c)}return s(e);case v:a:{for(l=o.key;r!==null;){if(r.key===l){if(r.tag===4&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),c=a(r,o.children||[]),c.return=e,e=c;break a}n(e,r);break}t(e,r),r=r.sibling}c=fi(o,e.mode,c),c.return=e,e=c}return s(e);case O:return o=Ta(o),b(e,r,o,c)}if(F(o))return h(e,r,o,c);if(M(o)){if(l=M(o),typeof l!=`function`)throw Error(i(150));return o=l.call(o),g(e,r,o,c)}if(typeof o.then==`function`)return b(e,r,ja(o),c);if(o.$$typeof===C)return b(e,r,$i(e,o),c);Na(e,o)}return typeof o==`string`&&o!==``||typeof o==`number`||typeof o==`bigint`?(o=``+o,r!==null&&r.tag===6?(n(e,r.sibling),c=a(r,o),c.return=e,e=c):(n(e,r),c=ui(o,e.mode,c),c.return=e,e=c),s(e)):n(e,r)}return function(e,t,n,r){try{Aa=0;var i=b(e,t,n,r);return ka=null,i}catch(t){if(t===ya||t===xa)throw t;var a=ii(29,t,null,e.mode);return a.lanes=r,a.return=e,a}}}var Fa=Pa(!0),Ia=Pa(!1),La=!1;function Ra(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function za(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Ba(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Va(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,zl&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,t=ti(e),ei(e,null,n),t}return Zr(e,r,t,n),ti(e)}function Ha(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,n&4194048)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Ye(e,n)}}function Ua(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};a===null?i=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?i=a=t:a=a.next=t}else i=a=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:a,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var Wa=!1;function Ga(){if(Wa){var e=ua;if(e!==null)throw e}}function Ka(e,t,n,r){Wa=!1;var i=e.updateQueue;La=!1;var a=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var c=s,l=c.next;c.next=null,o===null?a=l:o.next=l,o=c;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=l:s.next=l,u.lastBaseUpdate=c))}if(a!==null){var d=i.baseState;o=0,u=l=c=null,s=a;do{var f=s.lane&-536870913,p=f!==s.lane;if(p?(Hl&f)===f:(r&f)===f){f!==0&&f===la&&(Wa=!0),u!==null&&(u=u.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});a:{var h=e,g=s;f=t;var _=n;switch(g.tag){case 1:if(h=g.payload,typeof h==`function`){d=h.call(_,d,f);break a}d=h;break a;case 3:h.flags=h.flags&-65537|128;case 0:if(h=g.payload,f=typeof h==`function`?h.call(_,d,f):h,f==null)break a;d=m({},d,f);break a;case 2:La=!0}}f=s.callback,f!==null&&(e.flags|=64,p&&(e.flags|=8192),p=i.callbacks,p===null?i.callbacks=[f]:p.push(f))}else p={lane:f,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(l=u=p,c=d):u=u.next=p,o|=f;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;p=s,s=p.next,p.next=null,i.lastBaseUpdate=p,i.shared.pending=null}}while(1);u===null&&(c=d),i.baseState=c,i.firstBaseUpdate=l,i.lastBaseUpdate=u,a===null&&(i.shared.lanes=0),Xl|=o,e.lanes=o,e.memoizedState=d}}function qa(e,t){if(typeof e!=`function`)throw Error(i(191,e));e.call(t)}function Ja(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;ea?a:8;var o=I.T,s={};I.T=s,Ns(e,!1,t,n);try{var c=i(),l=I.S;l!==null&&l(s,c),typeof c==`object`&&c&&typeof c.then==`function`?Ms(e,t,pa(c,r),vu(e)):Ms(e,t,r,vu(e))}catch(n){Ms(e,t,{then:function(){},status:`rejected`,reason:n},vu())}finally{L.p=a,o!==null&&s.types!==null&&(o.types=s.types),I.T=o}}function Ss(){}function Cs(e,t,n,r){if(e.tag!==5)throw Error(i(476));var a=ws(e).queue;W(e,a,t,R,n===null?Ss:function(){return Ts(e),n(r)})}function ws(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:R,baseState:R,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Io,lastRenderedState:R},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Io,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Ts(e){var t=ws(e);t.next===null&&(t=e.alternate.memoizedState),Ms(e,t.next.queue,{},vu())}function Es(){return Qi(op)}function Ds(){return jo().memoizedState}function Os(){return jo().memoizedState}function ks(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=vu();e=Ba(n);var r=Va(t,e,n);r!==null&&(bu(r,t,n),Ha(r,t,n)),t={cache:aa()},e.payload=t;return}t=t.return}}function As(e,t,n){var r=vu();n={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Ps(e)?Fs(t,n):(n=Qr(e,t,n,r),n!==null&&(bu(n,e,r),Is(n,t,r)))}function js(e,t,n){Ms(e,t,n,vu())}function Ms(e,t,n,r){var i={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(Ps(e))Fs(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,s=a(o,n);if(i.hasEagerState=!0,i.eagerState=s,vr(s,o))return Zr(e,t,i,0),Bl===null&&Xr(),!1}catch{}if(n=Qr(e,t,i,r),n!==null)return bu(n,e,r),Is(n,t,r),!0}return!1}function Ns(e,t,n,r){if(r={lane:2,revertLane:_d(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},Ps(e)){if(t)throw Error(i(479))}else t=Qr(e,n,r,2),t!==null&&bu(t,e,2)}function Ps(e){var t=e.alternate;return e===uo||t!==null&&t===uo}function Fs(e,t){ho=mo=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Is(e,t,n){if(n&4194048){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Ye(e,n)}}var Ls={readContext:Qi,use:Po,useCallback:xo,useContext:xo,useEffect:xo,useImperativeHandle:xo,useLayoutEffect:xo,useInsertionEffect:xo,useMemo:xo,useReducer:xo,useRef:xo,useState:xo,useDebugValue:xo,useDeferredValue:xo,useTransition:xo,useSyncExternalStore:xo,useId:xo,useHostTransitionStatus:xo,useFormState:xo,useActionState:xo,useOptimistic:xo,useMemoCache:xo,useCacheRefresh:xo};Ls.useEffectEvent=xo;var Rs={readContext:Qi,use:Po,useCallback:function(e,t){return Ao().memoizedState=[e,t===void 0?null:t],e},useContext:Qi,useEffect:us,useImperativeHandle:function(e,t,n){n=n==null?null:n.concat([e]),cs(4194308,4,hs.bind(null,t,e),n)},useLayoutEffect:function(e,t){return cs(4194308,4,e,t)},useInsertionEffect:function(e,t){cs(4,2,e,t)},useMemo:function(e,t){var n=Ao();t=t===void 0?null:t;var r=e();if(go){Me(!0);try{e()}finally{Me(!1)}}return n.memoizedState=[r,t],r},useReducer:function(e,t,n){var r=Ao();if(n!==void 0){var i=n(t);if(go){Me(!0);try{n(t)}finally{Me(!1)}}}else i=t;return r.memoizedState=r.baseState=i,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:i},r.queue=e,e=e.dispatch=As.bind(null,uo,e),[r.memoizedState,e]},useRef:function(e){var t=Ao();return e={current:e},t.memoizedState=e},useState:function(e){e=Ko(e);var t=e.queue,n=js.bind(null,uo,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:_s,useDeferredValue:function(e,t){return bs(Ao(),e,t)},useTransition:function(){var e=Ko(!1);return e=W.bind(null,uo,e.queue,!0,!1),Ao().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var r=uo,a=Ao();if(ji){if(n===void 0)throw Error(i(407));n=n()}else{if(n=t(),Bl===null)throw Error(i(349));Hl&127||Vo(r,t,n)}a.memoizedState=n;var o={value:n,getSnapshot:t};return a.queue=o,us(Uo.bind(null,r,o,e),[e]),r.flags|=2048,os(9,{destroy:void 0},Ho.bind(null,r,o,n,t),null),n},useId:function(){var e=Ao(),t=Bl.identifierPrefix;if(ji){var n=Ci,r=Si;n=(r&~(1<<32-Ne(r)-1)).toString(32)+n,t=`_`+t+`R_`+n,n=_o++,0<\/script>`,o=o.removeChild(o.firstChild);break;case`select`:o=typeof r.is==`string`?s.createElement(`select`,{is:r.is}):s.createElement(`select`),r.multiple?o.multiple=!0:r.size&&(o.size=r.size);break;default:o=typeof r.is==`string`?s.createElement(a,{is:r.is}):s.createElement(a)}}o[nt]=t,o[rt]=r;a:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)o.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break a;for(;s.sibling===null;){if(s.return===null||s.return===t)break a;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=o;a:switch(Hd(o,a,r),a){case`button`:case`input`:case`select`:case`textarea`:r=!!r.autoFocus;break a;case`img`:r=!0;break a;default:r=!1}r&&Mc(t)}}return Ic(t),G(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==r&&Mc(t);else{if(typeof r!=`string`&&t.stateNode===null)throw Error(i(166));if(e=re.current,Ri(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,a=ki,a!==null)switch(a.tag){case 27:case 5:r=a.memoizedProps}e[nt]=t,e=!!(e.nodeValue===n||r!==null&&!0===r.suppressHydrationWarning||zd(e.nodeValue,n)),e||Fi(t,!0)}else e=Jd(e).createTextNode(r),e[nt]=t,t.stateNode=e}return Ic(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(r=Ri(t),n!==null){if(e===null){if(!r)throw Error(i(318));if(e=t.memoizedState,e=e===null?null:e.dehydrated,!e)throw Error(i(557));e[nt]=t}else zi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Ic(t),e=!1}else n=Bi(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(oo(t),t):(oo(t),null);if(t.flags&128)throw Error(i(558))}return Ic(t),null;case 13:if(r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=Ri(t),r!==null&&r.dehydrated!==null){if(e===null){if(!a)throw Error(i(318));if(a=t.memoizedState,a=a===null?null:a.dehydrated,!a)throw Error(i(317));a[nt]=t}else zi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Ic(t),a=!1}else a=Bi(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(oo(t),t):(oo(t),null)}return oo(t),t.flags&128?(t.lanes=n,t):(n=r!==null,e=e!==null&&e.memoizedState!==null,n&&(r=t.child,a=null,r.alternate!==null&&r.alternate.memoizedState!==null&&r.alternate.memoizedState.cachePool!==null&&(a=r.alternate.memoizedState.cachePool.pool),o=null,r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(o=r.memoizedState.cachePool.pool),o!==a&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),Pc(t,t.updateQueue),Ic(t),null);case 4:return oe(),e===null&&kd(t.stateNode.containerInfo),Ic(t),null;case 10:return Ki(t.type),Ic(t),null;case 19:if(H(so),r=t.memoizedState,r===null)return Ic(t),null;if(a=!!(t.flags&128),o=r.rendering,o===null){if(a)Fc(r,!1);else{if(Yl!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(o=co(e),o!==null){for(t.flags|=128,Fc(r,!1),e=o.updateQueue,t.updateQueue=e,Pc(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)si(n,e),n=n.sibling;return ee(so,so.current&1|2),ji&&wi(t,r.treeForkCount),t.child}e=e.sibling}r.tail!==null&&xe()>ou&&(t.flags|=128,a=!0,Fc(r,!1),t.lanes=4194304)}}else{if(!a){if(e=co(o),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,Pc(t,e),Fc(r,!0),r.tail===null&&r.tailMode===`hidden`&&!o.alternate&&!ji)return Ic(t),null}else 2*xe()-r.renderingStartTime>ou&&n!==536870912&&(t.flags|=128,a=!0,Fc(r,!1),t.lanes=4194304)}r.isBackwards?(o.sibling=t.child,t.child=o):(e=r.last,e===null?t.child=o:e.sibling=o,r.last=o)}return r.tail===null?(Ic(t),null):(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=xe(),e.sibling=null,n=so.current,ee(so,a?n&1|2:n&1),ji&&wi(t,r.treeForkCount),e);case 22:case 23:return oo(t),$a(),r=t.memoizedState!==null,e===null?r&&(t.flags|=8192):e.memoizedState!==null!==r&&(t.flags|=8192),r?n&536870912&&!(t.flags&128)&&(Ic(t),t.subtreeFlags&6&&(t.flags|=8192)):Ic(t),n=t.updateQueue,n!==null&&Pc(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),r=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),e!==null&&H(ha),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),Ki(ia),Ic(t),null;case 25:return null;case 30:return null}throw Error(i(156,t.tag))}function Rc(e,t){switch(Di(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Ki(ia),oe(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return ce(t),null;case 31:if(t.memoizedState!==null){if(oo(t),t.alternate===null)throw Error(i(340));zi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(oo(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(i(340));zi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return H(so),null;case 4:return oe(),null;case 10:return Ki(t.type),null;case 22:case 23:return oo(t),$a(),e!==null&&H(ha),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return Ki(ia),null;case 25:return null;default:return null}}function zc(e,t){switch(Di(t),t.tag){case 3:Ki(ia),oe();break;case 26:case 27:case 5:ce(t);break;case 4:oe();break;case 31:t.memoizedState!==null&&oo(t);break;case 13:oo(t);break;case 19:H(so);break;case 10:Ki(t.type);break;case 22:case 23:oo(t),$a(),e!==null&&H(ha);break;case 24:Ki(ia)}}function Bc(e,t){try{var n=t.updateQueue,r=n===null?null:n.lastEffect;if(r!==null){var i=r.next;n=i;do{if((n.tag&e)===e){r=void 0;var a=n.create,o=n.inst;r=a(),o.destroy=r}n=n.next}while(n!==i)}}catch(e){Xu(t,t.return,e)}}function Vc(e,t,n){try{var r=t.updateQueue,i=r===null?null:r.lastEffect;if(i!==null){var a=i.next;r=a;do{if((r.tag&e)===e){var o=r.inst,s=o.destroy;if(s!==void 0){o.destroy=void 0,i=t;var c=n,l=s;try{l()}catch(e){Xu(i,c,e)}}}r=r.next}while(r!==a)}}catch(e){Xu(t,t.return,e)}}function Hc(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{Ja(t,n)}catch(t){Xu(e,e.return,t)}}}function Uc(e,t,n){n.props=Gs(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(n){Xu(e,t,n)}}function Wc(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;case 30:r=e.stateNode;break;default:r=e.stateNode}typeof n==`function`?e.refCleanup=n(r):n.current=r}}catch(n){Xu(e,t,n)}}function Gc(e,t){var n=e.ref,r=e.refCleanup;if(n!==null){if(typeof r==`function`)try{r()}catch(n){Xu(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n==`function`)try{n(null)}catch(n){Xu(e,t,n)}else n.current=null}}function Kc(e){var t=e.type,n=e.memoizedProps,r=e.stateNode;try{a:switch(t){case`button`:case`input`:case`select`:case`textarea`:n.autoFocus&&r.focus();break a;case`img`:n.src?r.src=n.src:n.srcSet&&(r.srcset=n.srcSet)}}catch(t){Xu(e,e.return,t)}}function qc(e,t,n){try{var r=e.stateNode;Ud(r,e.type,n,t),r[rt]=t}catch(t){Xu(e,e.return,t)}}function Jc(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&of(e.type)||e.tag===4}function Yc(e){a:for(;;){for(;e.sibling===null;){if(e.return===null||Jc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&of(e.type)||e.flags&2||e.child===null||e.tag===4)continue a;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Xc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?(n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n).insertBefore(e,t):(t=n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n,t.appendChild(e),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Yt));else if(r!==4&&(r===27&&of(e.type)&&(n=e.stateNode,t=null),e=e.child,e!==null))for(Xc(e,t,n),e=e.sibling;e!==null;)Xc(e,t,n),e=e.sibling}function Zc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(r===27&&of(e.type)&&(n=e.stateNode),e=e.child,e!==null))for(Zc(e,t,n),e=e.sibling;e!==null;)Zc(e,t,n),e=e.sibling}function Qc(e){var t=e.stateNode,n=e.memoizedProps;try{for(var r=e.type,i=t.attributes;i.length;)t.removeAttributeNode(i[0]);Hd(t,r,n),t[nt]=e,t[rt]=n}catch(t){Xu(e,e.return,t)}}var $c=!1,el=!1,tl=!1,nl=typeof WeakSet==`function`?WeakSet:Set,rl=null;function il(e,t){if(e=e.containerInfo,Kd=hp,e=Cr(e),wr(e)){if(`selectionStart`in e)var n={start:e.selectionStart,end:e.selectionEnd};else a:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var a=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break a}var s=0,c=-1,l=-1,u=0,d=0,f=e,p=null;b:for(;;){for(var m;f!==n||a!==0&&f.nodeType!==3||(c=s+a),f!==o||r!==0&&f.nodeType!==3||(l=s+r),f.nodeType===3&&(s+=f.nodeValue.length),(m=f.firstChild)!==null;)p=f,f=m;for(;;){if(f===e)break b;if(p===n&&++u===a&&(c=s),p===o&&++d===r&&(l=s),(m=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=m}n=c===-1||l===-1?null:{start:c,end:l}}else n=null}n||={start:0,end:0}}else n=null;for(qd={focusedElem:e,selectionRange:n},hp=!1,rl=t;rl!==null;)if(t=rl,e=t.child,t.subtreeFlags&1028&&e!==null)e.return=t,rl=e;else for(;rl!==null;){switch(t=rl,o=t.alternate,e=t.flags,t.tag){case 0:if(e&4&&(e=t.updateQueue,e=e===null?null:e.events,e!==null))for(n=0;n title`))),Hd(o,r,n),o[nt]=e,ht(o),r=o;break a;case`link`:var s=Yf(`link`,`href`,a).get(r+(n.href||``));if(s){for(var c=0;cg&&(o=g,g=h,h=o);var _=xr(s,h),v=xr(s,g);if(_&&v&&(p.rangeCount!==1||p.anchorNode!==_.node||p.anchorOffset!==_.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=d.createRange();y.setStart(_.node,_.offset),p.removeAllRanges(),h>g?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(d=[],p=s;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof s.focus==`function`&&s.focus(),s=0;sn?32:n,I.T=null,n=mu,mu=null;var o=uu,s=fu;if(lu=0,du=uu=null,fu=0,zl&6)throw Error(i(331));var c=zl;if(zl|=4,Pl(o.current),El(o,o.current,s,n),zl=c,ud(0,!1),je&&typeof je.onPostCommitFiberRoot==`function`)try{je.onPostCommitFiberRoot(Ae,o)}catch{}return!0}finally{L.p=a,I.T=r,Ku(e,t)}}function Yu(e,t,n){t=mi(n,t),t=Zs(e.stateNode,t,2),e=Va(e,t,2),e!==null&&(Ke(e,2),ld(e))}function Xu(e,t,n){if(e.tag===3)Yu(e,e,n);else for(;t!==null;){if(t.tag===3){Yu(t,e,n);break}if(t.tag===1){var r=t.stateNode;if(typeof t.type.getDerivedStateFromError==`function`||typeof r.componentDidCatch==`function`&&(cu===null||!cu.has(r))){e=mi(n,e),n=Qs(2),r=Va(t,n,2),r!==null&&($s(n,r,t,e),Ke(r,2),ld(r));break}}t=t.return}}function Zu(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new Rl;var i=new Set;r.set(t,i)}else i=r.get(t),i===void 0&&(i=new Set,r.set(t,i));i.has(n)||(ql=!0,i.add(n),e=Qu.bind(null,e,t,n),t.then(e,e))}function Qu(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,Bl===e&&(Hl&n)===n&&(Yl===4||Yl===3&&(Hl&62914560)===Hl&&300>xe()-iu?!(zl&2)&&Du(e,0):Ql|=n,eu===Hl&&(eu=0)),ld(e)}function $u(e,t){t===0&&(t=We()),e=$r(e,t),e!==null&&(Ke(e,t),ld(e))}function ed(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),$u(e,n)}function td(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(i(314))}r!==null&&r.delete(t),$u(e,n)}function nd(e,t){return _e(e,t)}var rd=null,id=null,ad=!1,od=!1,sd=!1,cd=0;function ld(e){e!==id&&e.next===null&&(id===null?rd=id=e:id=id.next=e),od=!0,ad||(ad=!0,gd())}function ud(e,t){if(!sd&&od){sd=!0;do for(var n=!1,r=rd;r!==null;){if(!t){if(e!==0){var i=r.pendingLanes;if(i===0)var a=0;else{var o=r.suspendedLanes,s=r.pingedLanes;a=(1<<31-Ne(42|e)+1)-1,a&=i&~(o&~s),a=a&201326741?a&201326741|1:a?a|2:0}a!==0&&(n=!0,hd(r,a))}else a=Hl,a=Ve(r,r===Bl?a:0,r.cancelPendingCommit!==null||r.timeoutHandle!==-1),!(a&3)||He(r,a)||(n=!0,hd(r,a))}r=r.next}while(n);sd=!1}}function dd(){fd()}function fd(){od=ad=!1;var e=0;cd!==0&&$d()&&(e=cd);for(var t=xe(),n=null,r=rd;r!==null;){var i=r.next,a=pd(r,t);a===0?(r.next=null,n===null?rd=i:n.next=i,i===null&&(id=n)):(n=r,(e!==0||a&3)&&(od=!0)),r=i}lu!==0&&lu!==5||ud(e,!1),cd!==0&&(cd=0)}function pd(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirationTimes,a=e.pendingLanes&-62914561;0s)break;var u=c.transferSize,d=c.initiatorType;u&&Wd(d)&&(c=c.responseEnd,o+=u*(c`u`?null:document;function kf(e,t,n){var r=Of;if(r&&typeof t==`string`&&t){var i=Pt(t);i=`link[rel="`+e+`"][href="`+i+`"]`,typeof n==`string`&&(i+=`[crossorigin="`+n+`"]`),Cf.has(i)||(Cf.add(i),e={rel:e,crossOrigin:n,href:t},r.querySelector(i)===null&&(t=r.createElement(`link`),Hd(t,`link`,e),ht(t),r.head.appendChild(t)))}}function Af(e){Tf.D(e),kf(`dns-prefetch`,e,null)}function jf(e,t){Tf.C(e,t),kf(`preconnect`,e,t)}function Mf(e,t,n){Tf.L(e,t,n);var r=Of;if(r&&e&&t){var i=`link[rel="preload"][as="`+Pt(t)+`"]`;t===`image`&&n&&n.imageSrcSet?(i+=`[imagesrcset="`+Pt(n.imageSrcSet)+`"]`,typeof n.imageSizes==`string`&&(i+=`[imagesizes="`+Pt(n.imageSizes)+`"]`)):i+=`[href="`+Pt(e)+`"]`;var a=i;switch(t){case`style`:a=Rf(e);break;case`script`:a=Hf(e)}Sf.has(a)||(e=m({rel:`preload`,href:t===`image`&&n&&n.imageSrcSet?void 0:e,as:t},n),Sf.set(a,e),r.querySelector(i)!==null||t===`style`&&r.querySelector(zf(a))||t===`script`&&r.querySelector(Uf(a))||(t=r.createElement(`link`),Hd(t,`link`,e),ht(t),r.head.appendChild(t)))}}function Nf(e,t){Tf.m(e,t);var n=Of;if(n&&e){var r=t&&typeof t.as==`string`?t.as:`script`,i=`link[rel="modulepreload"][as="`+Pt(r)+`"][href="`+Pt(e)+`"]`,a=i;switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:a=Hf(e)}if(!Sf.has(a)&&(e=m({rel:`modulepreload`,href:e},t),Sf.set(a,e),n.querySelector(i)===null)){switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:if(n.querySelector(Uf(a)))return}r=n.createElement(`link`),Hd(r,`link`,e),ht(r),n.head.appendChild(r)}}}function Pf(e,t,n){Tf.S(e,t,n);var r=Of;if(r&&e){var i=mt(r).hoistableStyles,a=Rf(e);t||=`default`;var o=i.get(a);if(!o){var s={loading:0,preload:null};if(o=r.querySelector(zf(a)))s.loading=5;else{e=m({rel:`stylesheet`,href:e,"data-precedence":t},n),(n=Sf.get(a))&&Kf(e,n);var c=o=r.createElement(`link`);ht(c),Hd(c,`link`,e),c._p=new Promise(function(e,t){c.onload=e,c.onerror=t}),c.addEventListener(`load`,function(){s.loading|=1}),c.addEventListener(`error`,function(){s.loading|=2}),s.loading|=4,Gf(o,t,r)}o={type:`stylesheet`,instance:o,count:1,state:s},i.set(a,o)}}}function Ff(e,t){Tf.X(e,t);var n=Of;if(n&&e){var r=mt(n).hoistableScripts,i=Hf(e),a=r.get(i);a||(a=n.querySelector(Uf(i)),a||(e=m({src:e,async:!0},t),(t=Sf.get(i))&&qf(e,t),a=n.createElement(`script`),ht(a),Hd(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function If(e,t){Tf.M(e,t);var n=Of;if(n&&e){var r=mt(n).hoistableScripts,i=Hf(e),a=r.get(i);a||(a=n.querySelector(Uf(i)),a||(e=m({src:e,async:!0,type:`module`},t),(t=Sf.get(i))&&qf(e,t),a=n.createElement(`script`),ht(a),Hd(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function Lf(e,t,n,r){var a=(a=re.current)?wf(a):null;if(!a)throw Error(i(446));switch(e){case`meta`:case`title`:return null;case`style`:return typeof n.precedence==`string`&&typeof n.href==`string`?(t=Rf(n.href),n=mt(a).hoistableStyles,r=n.get(t),r||(r={type:`style`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};case`link`:if(n.rel===`stylesheet`&&typeof n.href==`string`&&typeof n.precedence==`string`){e=Rf(n.href);var o=mt(a).hoistableStyles,s=o.get(e);if(s||(a=a.ownerDocument||a,s={type:`stylesheet`,instance:null,count:0,state:{loading:0,preload:null}},o.set(e,s),(o=a.querySelector(zf(e)))&&!o._p&&(s.instance=o,s.state.loading=5),Sf.has(e)||(n={rel:`preload`,as:`style`,href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},Sf.set(e,n),o||Vf(a,e,n,s.state))),t&&r===null)throw Error(i(528,``));return s}if(t&&r!==null)throw Error(i(529,``));return null;case`script`:return t=n.async,n=n.src,typeof n==`string`&&t&&typeof t!=`function`&&typeof t!=`symbol`?(t=Hf(n),n=mt(a).hoistableScripts,r=n.get(t),r||(r={type:`script`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};default:throw Error(i(444,e))}}function Rf(e){return`href="`+Pt(e)+`"`}function zf(e){return`link[rel="stylesheet"][`+e+`]`}function Bf(e){return m({},e,{"data-precedence":e.precedence,precedence:null})}function Vf(e,t,n,r){e.querySelector(`link[rel="preload"][as="style"][`+t+`]`)?r.loading=1:(t=e.createElement(`link`),r.preload=t,t.addEventListener(`load`,function(){return r.loading|=1}),t.addEventListener(`error`,function(){return r.loading|=2}),Hd(t,`link`,n),ht(t),e.head.appendChild(t))}function Hf(e){return`[src="`+Pt(e)+`"]`}function Uf(e){return`script[async]`+e}function Wf(e,t,n){if(t.count++,t.instance===null)switch(t.type){case`style`:var r=e.querySelector(`style[data-href~="`+Pt(n.href)+`"]`);if(r)return t.instance=r,ht(r),r;var a=m({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return r=(e.ownerDocument||e).createElement(`style`),ht(r),Hd(r,`style`,a),Gf(r,n.precedence,e),t.instance=r;case`stylesheet`:a=Rf(n.href);var o=e.querySelector(zf(a));if(o)return t.state.loading|=4,t.instance=o,ht(o),o;r=Bf(n),(a=Sf.get(a))&&Kf(r,a),o=(e.ownerDocument||e).createElement(`link`),ht(o);var s=o;return s._p=new Promise(function(e,t){s.onload=e,s.onerror=t}),Hd(o,`link`,r),t.state.loading|=4,Gf(o,n.precedence,e),t.instance=o;case`script`:return o=Hf(n.src),(a=e.querySelector(Uf(o)))?(t.instance=a,ht(a),a):(r=n,(a=Sf.get(o))&&(r=m({},n),qf(r,a)),e=e.ownerDocument||e,a=e.createElement(`script`),ht(a),Hd(a,`link`,r),e.head.appendChild(a),t.instance=a);case`void`:return null;default:throw Error(i(443,t.type))}else t.type===`stylesheet`&&!(t.state.loading&4)&&(r=t.instance,t.state.loading|=4,Gf(r,n.precedence,e));return t.instance}function Gf(e,t,n){for(var r=n.querySelectorAll(`link[rel="stylesheet"][data-precedence],style[data-precedence]`),i=r.length?r[r.length-1]:null,a=i,o=0;o title`):null)}function Zf(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case`meta`:case`title`:return!0;case`style`:if(typeof t.precedence!=`string`||typeof t.href!=`string`||t.href===``)break;return!0;case`link`:if(typeof t.rel!=`string`||typeof t.href!=`string`||t.href===``||t.onLoad||t.onError)break;switch(t.rel){case`stylesheet`:return e=t.disabled,typeof t.precedence==`string`&&e==null;default:return!0}case`script`:if(t.async&&typeof t.async!=`function`&&typeof t.async!=`symbol`&&!t.onLoad&&!t.onError&&t.src&&typeof t.src==`string`)return!0}return!1}function Qf(e){return!(e.type===`stylesheet`&&!(e.state.loading&3))}function $f(e,t,n,r){if(n.type===`stylesheet`&&(typeof r.media!=`string`||!1!==matchMedia(r.media).matches)&&!(n.state.loading&4)){if(n.instance===null){var i=Rf(r.href),a=t.querySelector(zf(i));if(a){t=a._p,typeof t==`object`&&t&&typeof t.then==`function`&&(e.count++,e=np.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=a,ht(a);return}a=t.ownerDocument||t,r=Bf(r),(i=Sf.get(i))&&Kf(r,i),a=a.createElement(`link`),ht(a);var o=a;o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),Hd(a,`link`,r),n.instance=a}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&!(n.state.loading&3)&&(e.count++,n=np.bind(e),t.addEventListener(`load`,n),t.addEventListener(`error`,n))}}var ep=0;function tp(e,t){return e.stylesheets&&e.count===0&&ip(e,e.stylesheets),0ep?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(i)}}:null}function np(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)ip(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var rp=null;function ip(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,rp=new Map,t.forEach(ap,e),rp=null,np.call(e))}function ap(e,t){if(!(t.state.loading&4)){var n=rp.get(e);if(n)var r=n.get(null);else{n=new Map,rp.set(e,n);for(var i=e.querySelectorAll(`link[data-precedence],style[data-precedence]`),a=0;a{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=g()})),v=l(d(),1),y=_(),b=`modulepreload`,x=function(e){return`/`+e},S={},C=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=x(t,n),t=s(t),t in S)return;S[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:b,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},w=/^(?:[a-z][a-z0-9+.-]*:|[\\/]{2})/i,T=/^[\\/]{2}/;function E(e,t){return t+e.replace(/\\/g,`/`)}var D=`popstate`;function O(e){return typeof e==`object`&&!!e&&`pathname`in e&&`search`in e&&`hash`in e&&`state`in e&&`key`in e}function k(e={}){function t(e,t){let n=t.state?.masked,{pathname:r,search:i,hash:a}=n||e.location;return P(``,{pathname:r,search:i,hash:a},t.state&&t.state.usr||null,t.state&&t.state.key||`default`,n?{pathname:e.location.pathname,search:e.location.search,hash:e.location.hash}:void 0)}function n(e,t){return typeof t==`string`?t:F(t)}return L(t,n,null,e)}function A(e,t){if(e===!1||e==null)throw Error(t)}function j(e,t){if(!e){typeof console<`u`&&console.warn(t);try{throw Error(t)}catch{}}}function M(){return Math.random().toString(36).substring(2,10)}function N(e,t){return{usr:e.state,key:e.key,idx:t,masked:e.mask?{pathname:e.pathname,search:e.search,hash:e.hash}:void 0}}function P(e,t,n=null,r,i){return{pathname:typeof e==`string`?e:e.pathname,search:``,hash:``,...typeof t==`string`?I(t):t,state:n,key:t&&t.key||r||M(),mask:i}}function F({pathname:e=`/`,search:t=``,hash:n=``}){return t&&t!==`?`&&(e+=t.charAt(0)===`?`?t:`?`+t),n&&n!==`#`&&(e+=n.charAt(0)===`#`?n:`#`+n),e}function I(e){let t={};if(e){let n=e.indexOf(`#`);n>=0&&(t.hash=e.substring(n),e=e.substring(0,n));let r=e.indexOf(`?`);r>=0&&(t.search=e.substring(r),e=e.substring(0,r)),e&&(t.pathname=e)}return t}function L(e,t,n,r={}){let{window:i=document.defaultView,v5Compat:a=!1}=r,o=i.history,s=`POP`,c=null,l=u();l??(l=0,o.replaceState({...o.state,idx:l},``));function u(){return(o.state||{idx:null}).idx}function d(){s=`POP`;let e=u(),t=e==null?null:e-l;l=e,c&&c({action:s,location:h.location,delta:t})}function f(e,t){s=`PUSH`;let r=O(e)?e:P(h.location,e,t);n&&n(r,e),l=u()+1;let d=N(r,l),f=h.createHref(r.mask||r);try{o.pushState(d,``,f)}catch(e){if(e instanceof DOMException&&e.name===`DataCloneError`)throw e;i.location.assign(f)}a&&c&&c({action:s,location:h.location,delta:1})}function p(e,t){s=`REPLACE`;let r=O(e)?e:P(h.location,e,t);n&&n(r,e),l=u();let i=N(r,l),d=h.createHref(r.mask||r);o.replaceState(i,``,d),a&&c&&c({action:s,location:h.location,delta:0})}function m(e){return R(i,e)}let h={get action(){return s},get location(){return e(i,o)},listen(e){if(c)throw Error(`A history only accepts one active listener`);return i.addEventListener(D,d),c=e,()=>{i.removeEventListener(D,d),c=null}},createHref(e){return t(i,e)},createURL:m,encodeLocation(e){let t=m(e);return{pathname:t.pathname,search:t.search,hash:t.hash}},push:f,replace:p,go(e){return o.go(e)}};return h}function R(e,t,n=!1){let r=`http://localhost`;e&&(r=e.location.origin===`null`?e.location.href:e.location.origin),A(r,`No window.location.(origin|href) available to create URL`);let i=typeof t==`string`?t:F(t);return i=i.replace(/ $/,`%20`),!n&&T.test(i)&&(i=r+i),new URL(i,r)}function z(e,t,n=`/`){return B(e,t,n,!1)}function B(e,t,n,r,i){let a=ge((typeof t==`string`?I(t):t).pathname||`/`,n);if(a==null)return null;let o=i??V(e),s=null,c=he(a);for(let e=0;s==null&&e{let c={relativePath:s===void 0?e.path||``:s,caseSensitive:e.caseSensitive===!0,childrenIndex:a,route:e};if(c.relativePath.startsWith(`/`)){if(!c.relativePath.startsWith(r)&&o)return;A(c.relativePath.startsWith(r),`Absolute route path "${c.relativePath}" nested under path "${r}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),c.relativePath=c.relativePath.slice(r.length)}let l=we([r,c.relativePath]),u=n.concat(c);e.children&&e.children.length>0&&(A(e.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${l}".`),H(e.children,t,u,l,o)),!(e.path==null&&!e.index)&&t.push({path:l,score:le(l,e.index),routesMeta:u.map((e,t)=>{let[n,r]=me(e.relativePath,e.caseSensitive,t===u.length-1);return{...e,matcher:n,compiledParams:r}})})};return e.forEach((e,t)=>{if(e.path===``||!e.path?.includes(`?`))a(e,t);else for(let n of ee(e.path))a(e,t,!0,n)}),t}function ee(e){let t=e.split(`/`);if(t.length===0)return[];let[n,...r]=t,i=n.endsWith(`?`),a=n.replace(/\?$/,``);if(r.length===0)return i?[a,``]:[a];let o=ee(r.join(`/`)),s=[];return s.push(...o.map(e=>e===``?a:[a,e].join(`/`))),i&&s.push(...o),s.map(t=>e.startsWith(`/`)&&t===``?`/`:t)}function te(e){e.sort((e,t)=>e.score===t.score?ue(e.routesMeta.map(e=>e.childrenIndex),t.routesMeta.map(e=>e.childrenIndex)):t.score-e.score)}var ne=/^:[\w-]+$/,re=3,ie=2,ae=1,oe=10,se=-2,ce=e=>e===`*`;function le(e,t){let n=e.split(`/`),r=n.length;return n.some(ce)&&(r+=se),t&&(r+=ie),n.filter(e=>!ce(e)).reduce((e,t)=>e+(ne.test(t)?re:t===``?ae:oe),r)}function ue(e,t){return e.length===t.length&&e.slice(0,-1).every((e,n)=>e===t[n])?e[e.length-1]-t[t.length-1]:0}function de(e,t,n=!1){let{routesMeta:r}=e,i={},a=`/`,o=[];for(let e=0;e{if(t===`*`){let e=s[r]||``;o=a.slice(0,a.length-e.length).replace(/(.)\/+$/,`$1`)}let i=s[r];return e[t]=n&&!i?void 0:(i||``).replace(/%2F/g,`/`),e},{}),pathname:a,pathnameBase:o,pattern:e}}function me(e,t=!1,n=!0){j(e===`*`||!e.endsWith(`*`)||e.endsWith(`/*`),`Route path "${e}" will be treated as if it were "${e.replace(/\*$/,`/*`)}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${e.replace(/\*$/,`/*`)}".`);let r=[],i=`^`+e.replace(/\/*\*?$/,``).replace(/^\/*/,`/`).replace(/[\\.*+^${}|()[\]]/g,`\\$&`).replace(/\/:([\w-]+)(\?)?/g,(e,t,n,i,a)=>{if(r.push({paramName:t,isOptional:n!=null}),n){let t=a.charAt(i+e.length);return t&&t!==`/`?`/([^\\/]*)`:`(?:/([^\\/]*))?`}return`/([^\\/]+)`}).replace(/\/([\w-]+)\?(\/|$)/g,`(/$1)?$2`);return e.endsWith(`*`)?(r.push({paramName:`*`}),i+=e===`*`||e===`/*`?`(.*)$`:`(?:\\/(.+)|\\/*)$`):n?i+=`\\/*$`:e!==``&&e!==`/`&&(i+=`(?:(?=\\/|$))`),[new RegExp(i,t?void 0:`i`),r]}function he(e){try{return e.split(`/`).map(e=>decodeURIComponent(e).replace(/\//g,`%2F`)).join(`/`)}catch(t){return j(!1,`The URL path "${e}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${t}).`),e}}function ge(e,t){if(t===`/`)return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith(`/`)?t.length-1:t.length,r=e.charAt(n);return r&&r!==`/`?null:e.slice(n)||`/`}function _e(e,t=`/`){let{pathname:n,search:r=``,hash:i=``}=typeof e==`string`?I(e):e,a;return n?(n=Ce(n),a=n.startsWith(`/`)?ve(n.substring(1),`/`):ve(n,t)):a=t,{pathname:a,search:De(r),hash:Oe(i)}}function ve(e,t){let n=Te(t).split(`/`);return e.split(`/`).forEach(e=>{e===`..`?n.length>1&&n.pop():e!==`.`&&n.push(e)}),n.length>1?n.join(`/`):`/`}function ye(e,t,n,r){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(r)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function be(e){return e.filter((e,t)=>t===0||e.route.path&&e.route.path.length>0)}function xe(e){let t=be(e);return t.map((e,n)=>n===t.length-1?e.pathname:e.pathnameBase)}function Se(e,t,n,r=!1){let i;typeof e==`string`?i=I(e):(i={...e},A(!i.pathname||!i.pathname.includes(`?`),ye(`?`,`pathname`,`search`,i)),A(!i.pathname||!i.pathname.includes(`#`),ye(`#`,`pathname`,`hash`,i)),A(!i.search||!i.search.includes(`#`),ye(`#`,`search`,`hash`,i)));let a=e===``||i.pathname===``,o=a?`/`:i.pathname,s;if(o==null)s=n;else{let e=t.length-1;if(!r&&o.startsWith(`..`)){let t=o.split(`/`);for(;t[0]===`..`;)t.shift(),--e;i.pathname=t.join(`/`)}s=e>=0?t[e]:`/`}let c=_e(i,s),l=o&&o!==`/`&&o.endsWith(`/`),u=(a||o===`.`)&&n.endsWith(`/`);return!c.pathname.endsWith(`/`)&&(l||u)&&(c.pathname+=`/`),c}var Ce=e=>e.replace(/[\\/]{2,}/g,`/`),we=e=>Ce(e.join(`/`)),Te=e=>e.replace(/\/+$/,``),Ee=e=>Te(e).replace(/^\/*/,`/`),De=e=>!e||e===`?`?``:e.startsWith(`?`)?e:`?`+e,Oe=e=>!e||e===`#`?``:e.startsWith(`#`)?e:`#`+e,ke=class{constructor(e,t,n,r=!1){this.status=e,this.statusText=t||``,this.internal=r,n instanceof Error?(this.data=n.toString(),this.error=n):this.data=n}};function Ae(e){return e!=null&&typeof e.status==`number`&&typeof e.statusText==`string`&&typeof e.internal==`boolean`&&`data`in e}function je(e){return we(e.map(e=>e.route.path).filter(Boolean))||`/`}var Me=typeof window<`u`&&window.document!==void 0&&window.document.createElement!==void 0;function Ne(e,t){let n=e;if(typeof n!=`string`||!w.test(n))return{absoluteURL:void 0,isExternal:!1,to:n};let r=n,i=!1;if(Me)try{let e=new URL(window.location.href),r=T.test(n)?new URL(E(n,e.protocol)):new URL(n),a=ge(r.pathname,t);r.origin===e.origin&&a!=null?n=a+r.search+r.hash:i=!0}catch{j(!1,` contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:r,isExternal:i,to:n}}Object.getOwnPropertyNames(Object.prototype).sort().join(`\0`);var Pe=[`POST`,`PUT`,`PATCH`,`DELETE`];new Set(Pe);var Fe=[`GET`,...Pe];new Set(Fe);var Ie=[`about:`,`blob:`,`chrome:`,`chrome-untrusted:`,`content:`,`data:`,`devtools:`,`file:`,`filesystem:`,`javascript:`];function Le(e){try{return Ie.includes(new URL(e).protocol)}catch{return!1}}var Re=v.createContext(null);Re.displayName=`DataRouter`;var ze=v.createContext(null);ze.displayName=`DataRouterState`;var Be=v.createContext(!1);function Ve(){return v.useContext(Be)}var He=v.createContext({isTransitioning:!1});He.displayName=`ViewTransition`;var Ue=v.createContext(new Map);Ue.displayName=`Fetchers`;var We=v.createContext(null);We.displayName=`Await`;var Ge=v.createContext(null);Ge.displayName=`Navigation`;var Ke=v.createContext(null);Ke.displayName=`Location`;var qe=v.createContext({outlet:null,matches:[],isDataRoute:!1});qe.displayName=`Route`;var Je=v.createContext(null);Je.displayName=`RouteError`;var Ye=`REACT_ROUTER_ERROR`,Xe=`REDIRECT`,Ze=`ROUTE_ERROR_RESPONSE`;function Qe(e){if(e.startsWith(`${Ye}:${Xe}:{`))try{let t=JSON.parse(e.slice(28));if(typeof t==`object`&&t&&typeof t.status==`number`&&typeof t.statusText==`string`&&typeof t.location==`string`&&typeof t.reloadDocument==`boolean`&&typeof t.replace==`boolean`)return t}catch{}}function $e(e){if(e.startsWith(`${Ye}:${Ze}:{`))try{let t=JSON.parse(e.slice(40));if(typeof t==`object`&&t&&typeof t.status==`number`&&typeof t.statusText==`string`)return new ke(t.status,t.statusText,t.data)}catch{}}function et(e,{relative:t}={}){A(tt(),`useHref() may be used only in the context of a component.`);let{basename:n,navigator:r}=v.useContext(Ge),{hash:i,pathname:a,search:o}=lt(e,{relative:t}),s=a;return n!==`/`&&(s=a===`/`?n:we([n,a])),r.createHref({pathname:s,search:o,hash:i})}function tt(){return v.useContext(Ke)!=null}function nt(){return A(tt(),`useLocation() may be used only in the context of a component.`),v.useContext(Ke).location}var rt=`You should call navigate() in a React.useEffect(), not when your component is first rendered.`;function it(e){v.useContext(Ge).static||v.useLayoutEffect(e)}function at(){let{isDataRoute:e}=v.useContext(qe);return e?Et():ot()}function ot(){A(tt(),`useNavigate() may be used only in the context of a component.`);let e=v.useContext(Re),{basename:t,navigator:n}=v.useContext(Ge),{matches:r}=v.useContext(qe),{pathname:i}=nt(),a=JSON.stringify(xe(r)),o=v.useRef(!1);return it(()=>{o.current=!0}),v.useCallback((r,s={})=>{if(j(o.current,rt),!o.current)return;if(typeof r==`number`){n.go(r);return}let c=Se(r,JSON.parse(a),i,s.relative===`path`);e==null&&t!==`/`&&(c.pathname=c.pathname===`/`?t:we([t,c.pathname])),(s.replace?n.replace:n.push)(c,s.state,s)},[t,n,a,i,e])}var st=v.createContext(null);function ct(e){let t=v.useContext(qe).outlet;return v.useMemo(()=>t&&v.createElement(st.Provider,{value:e},t),[t,e])}function lt(e,{relative:t}={}){let{matches:n}=v.useContext(qe),{pathname:r}=nt(),i=JSON.stringify(xe(n));return v.useMemo(()=>Se(e,JSON.parse(i),r,t===`path`),[e,i,r,t])}function ut(e,t){return dt(e,t)}function dt(e,t,n){A(tt(),`useRoutes() may be used only in the context of a component.`);let{navigator:r}=v.useContext(Ge),{matches:i}=v.useContext(qe),a=i[i.length-1],o=a?a.params:{},s=a?a.pathname:`/`,c=a?a.pathnameBase:`/`,l=a&&a.route;{let e=l&&l.path||``;Ot(s,!l||e.endsWith(`*`)||e.endsWith(`*?`),`You rendered descendant (or called \`useRoutes()\`) at "${s}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. - -Please change the parent to .`)}let u=nt(),d;if(t){let e=typeof t==`string`?I(t):t;A(c===`/`||e.pathname?.startsWith(c),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${c}" but pathname "${e.pathname}" was given in the \`location\` prop.`),d=e}else d=u;let f=d.pathname||`/`,p=f;if(c!==`/`){let e=c.replace(/^\//,``).split(`/`);p=`/`+f.replace(/^\//,``).split(`/`).slice(e.length).join(`/`)}let m=n&&n.state.matches.length?n.state.matches.map(e=>Object.assign(e,{route:n.manifest[e.route.id]||e.route})):z(e,{pathname:p});j(l||m!=null,`No routes matched location "${d.pathname}${d.search}${d.hash}" `),j(m==null||m[m.length-1].route.element!==void 0||m[m.length-1].route.Component!==void 0||m[m.length-1].route.lazy!==void 0,`Matched leaf route at location "${d.pathname}${d.search}${d.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let h=vt(m&&m.map(e=>Object.assign({},e,{params:Object.assign({},o,e.params),pathname:we([c,r.encodeLocation?r.encodeLocation(e.pathname.replace(/%/g,`%25`).replace(/\?/g,`%3F`).replace(/#/g,`%23`)).pathname:e.pathname]),pathnameBase:e.pathnameBase===`/`?c:we([c,r.encodeLocation?r.encodeLocation(e.pathnameBase.replace(/%/g,`%25`).replace(/\?/g,`%3F`).replace(/#/g,`%23`)).pathname:e.pathnameBase])})),i,n);return t&&h?v.createElement(Ke.Provider,{value:{location:{pathname:`/`,search:``,hash:``,state:null,key:`default`,mask:void 0,...d},navigationType:`POP`}},h):h}function ft(){let e=Tt(),t=Ae(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,r=`rgba(200,200,200, 0.5)`,i={padding:`0.5rem`,backgroundColor:r},a={padding:`2px 4px`,backgroundColor:r},o=null;return console.error(`Error handled by React Router default ErrorBoundary:`,e),o=v.createElement(v.Fragment,null,v.createElement(`p`,null,`💿 Hey developer 👋`),v.createElement(`p`,null,`You can provide a way better UX than this when your app throws errors by providing your own `,v.createElement(`code`,{style:a},`ErrorBoundary`),` or`,` `,v.createElement(`code`,{style:a},`errorElement`),` prop on your route.`)),v.createElement(v.Fragment,null,v.createElement(`h2`,null,`Unexpected Application Error!`),v.createElement(`h3`,{style:{fontStyle:`italic`}},t),n?v.createElement(`pre`,{style:i},n):null,o)}var pt=v.createElement(ft,null),mt=class extends v.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||t.revalidation!==`idle`&&e.revalidation===`idle`?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:e.error===void 0?t.error:e.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){this.props.onError?this.props.onError(e,t):console.error(`React Router caught the following error during render`,e)}render(){let e=this.state.error;if(this.context&&typeof e==`object`&&e&&`digest`in e&&typeof e.digest==`string`){let t=$e(e.digest);t&&(e=t)}let t=e===void 0?this.props.children:v.createElement(qe.Provider,{value:this.props.routeContext},v.createElement(Je.Provider,{value:e,children:this.props.component}));return this.context?v.createElement(gt,{error:e},t):t}};mt.contextType=Be;var ht=new WeakMap;function gt({children:e,error:t}){let{basename:n}=v.useContext(Ge);if(typeof t==`object`&&t&&`digest`in t&&typeof t.digest==`string`){let e=Qe(t.digest);if(e){let r=ht.get(t);if(r)throw r;let i=Ne(e.location,n),a=i.absoluteURL||i.to;if(Le(a))throw Error(`Invalid redirect location`);if(Me&&!ht.get(t)){if(i.isExternal||e.reloadDocument)window.location.href=a;else{let n=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(i.to,{replace:e.replace}));throw ht.set(t,n),n}}return v.createElement(`meta`,{httpEquiv:`refresh`,content:`0;url=${a}`})}}return e}function _t({routeContext:e,match:t,children:n}){let r=v.useContext(Re);return r&&r.static&&r.staticContext&&(t.route.errorElement||t.route.ErrorBoundary)&&(r.staticContext._deepestRenderedBoundaryId=t.route.id),v.createElement(qe.Provider,{value:e},n)}function vt(e,t=[],n){let r=n?.state;if(e==null){if(!r)return null;if(r.errors)e=r.matches;else if(t.length===0&&!r.initialized&&r.matches.length>0)e=r.matches;else return null}let i=e,a=r?.errors;if(a!=null){let e=i.findIndex(e=>e.route.id&&a?.[e.route.id]!==void 0);A(e>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(a).join(`,`)}`),i=i.slice(0,Math.min(i.length,e+1))}let o=!1,s=-1;if(n&&r){o=r.renderFallback;for(let e=0;e=0?i.slice(0,s+1):[i[0]];break}}}}let c=n?.onError,l=r&&c?(e,t)=>{c(e,{location:r.location,params:r.matches?.[0]?.params??{},pattern:je(r.matches),errorInfo:t})}:void 0;return i.reduceRight((e,n,c)=>{let u,d=!1,f=null,p=null;r&&(u=a&&n.route.id?a[n.route.id]:void 0,f=n.route.errorElement||pt,o&&(s<0&&c===0?(Ot(`route-fallback`,!1,"No `HydrateFallback` element provided to render during initial hydration"),d=!0,p=null):s===c&&(d=!0,p=n.route.hydrateFallbackElement||null)));let m=t.concat(i.slice(0,c+1)),h=()=>{let t;return t=u?f:d?p:n.route.Component?v.createElement(n.route.Component,null):n.route.element?n.route.element:e,v.createElement(_t,{match:n,routeContext:{outlet:e,matches:m,isDataRoute:r!=null},children:t})};return r&&(n.route.ErrorBoundary||n.route.errorElement||c===0)?v.createElement(mt,{location:r.location,revalidation:r.revalidation,component:f,error:u,children:h(),routeContext:{outlet:null,matches:m,isDataRoute:!0},onError:l}):h()},null)}function yt(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function bt(e){let t=v.useContext(Re);return A(t,yt(e)),t}function xt(e){let t=v.useContext(ze);return A(t,yt(e)),t}function St(e){let t=v.useContext(qe);return A(t,yt(e)),t}function Ct(e){let t=St(e),n=t.matches[t.matches.length-1];return A(n.route.id,`${e} can only be used on routes that contain a unique "id"`),n.route.id}function wt(){return Ct(`useRouteId`)}function Tt(){let e=v.useContext(Je),t=xt(`useRouteError`),n=Ct(`useRouteError`);return e===void 0?t.errors?.[n]:e}function Et(){let{router:e}=bt(`useNavigate`),t=Ct(`useNavigate`),n=v.useRef(!1);return it(()=>{n.current=!0}),v.useCallback(async(r,i={})=>{j(n.current,rt),n.current&&(typeof r==`number`?await e.navigate(r):await e.navigate(r,{fromRouteId:t,...i}))},[e,t])}var Dt={};function Ot(e,t,n){!t&&!Dt[e]&&(Dt[e]=!0,j(!1,n))}v.memo(kt);function kt({routes:e,manifest:t,future:n,state:r,isStatic:i,onError:a}){return dt(e,void 0,{manifest:t,state:r,isStatic:i,onError:a,future:n})}function At({to:e,replace:t,state:n,relative:r}){A(tt(),` may be used only in the context of a component.`);let{static:i}=v.useContext(Ge);j(!i,` must not be used on the initial render in a . This is a no-op, but you should modify your code so the is only ever rendered in response to some user interaction or state change.`);let{matches:a}=v.useContext(qe),{pathname:o}=nt(),s=at(),c=Se(e,xe(a),o,r===`path`),l=JSON.stringify(c);return v.useEffect(()=>{s(JSON.parse(l),{replace:t,state:n,relative:r})},[s,l,r,t,n]),null}function jt(e){return ct(e.context)}function Mt(e){A(!1,`A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .`)}function Nt({basename:e=`/`,children:t=null,location:n,navigationType:r=`POP`,navigator:i,static:a=!1,useTransitions:o}){A(!tt(),`You cannot render a inside another . You should never have more than one in your app.`);let s=e.replace(/^\/*/,`/`),c=v.useMemo(()=>({basename:s,navigator:i,static:a,useTransitions:o,future:{}}),[s,i,a,o]);typeof n==`string`&&(n=I(n));let{pathname:l=`/`,search:u=``,hash:d=``,state:f=null,key:p=`default`,mask:m}=n,h=v.useMemo(()=>{let e=ge(l,s);return e==null?null:{location:{pathname:e,search:u,hash:d,state:f,key:p,mask:m},navigationType:r}},[s,l,u,d,f,p,r,m]);return j(h!=null,` is not able to match the URL "${l}${u}${d}" because it does not start with the basename, so the won't render anything.`),h==null?null:v.createElement(Ge.Provider,{value:c},v.createElement(Ke.Provider,{children:t,value:h}))}function Pt({children:e,location:t}){return ut(Ft(e),t)}v.Component;function Ft(e,t=[]){let n=[];return v.Children.forEach(e,(e,r)=>{if(!v.isValidElement(e))return;let i=[...t,r];if(e.type===v.Fragment){n.push.apply(n,Ft(e.props.children,i));return}A(e.type===Mt,`[${typeof e.type==`string`?e.type:e.type.name}] is not a component. All component children of must be a or `),A(!e.props.index||!e.props.children,`An index route cannot have child routes.`);let a={id:e.props.id||i.join(`-`),caseSensitive:e.props.caseSensitive,element:e.props.element,Component:e.props.Component,index:e.props.index,path:e.props.path,middleware:e.props.middleware,loader:e.props.loader,action:e.props.action,hydrateFallbackElement:e.props.hydrateFallbackElement,HydrateFallback:e.props.HydrateFallback,errorElement:e.props.errorElement,ErrorBoundary:e.props.ErrorBoundary,hasErrorBoundary:e.props.hasErrorBoundary===!0||e.props.ErrorBoundary!=null||e.props.errorElement!=null,shouldRevalidate:e.props.shouldRevalidate,handle:e.props.handle,lazy:e.props.lazy};e.props.children&&(a.children=Ft(e.props.children,i)),n.push(a)}),n}var It=`get`,Lt=`application/x-www-form-urlencoded`;function Rt(e){return typeof HTMLElement<`u`&&e instanceof HTMLElement}function zt(e){return Rt(e)&&e.tagName.toLowerCase()===`button`}function Bt(e){return Rt(e)&&e.tagName.toLowerCase()===`form`}function Vt(e){return Rt(e)&&e.tagName.toLowerCase()===`input`}function Ht(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function Ut(e,t){return e.button===0&&(!t||t===`_self`)&&!Ht(e)}function Wt(e=``){return new URLSearchParams(typeof e==`string`||Array.isArray(e)||e instanceof URLSearchParams?e:Object.keys(e).reduce((t,n)=>{let r=e[n];return t.concat(Array.isArray(r)?r.map(e=>[n,e]):[[n,r]])},[]))}function Gt(e,t){let n=Wt(e);return t&&t.forEach((e,r)=>{n.has(r)||t.getAll(r).forEach(e=>{n.append(r,e)})}),n}var Kt=null;function qt(){if(Kt===null)try{new FormData(document.createElement(`form`),0),Kt=!1}catch{Kt=!0}return Kt}var Jt=new Set([`application/x-www-form-urlencoded`,`multipart/form-data`,`text/plain`]);function Yt(e){return e!=null&&!Jt.has(e)?(j(!1,`"${e}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${Lt}"`),null):e}function Xt(e,t){let n,r,i,a,o;if(Bt(e)){let o=e.getAttribute(`action`);r=o?ge(o,t):null,n=e.getAttribute(`method`)||It,i=Yt(e.getAttribute(`enctype`))||Lt,a=new FormData(e)}else if(zt(e)||Vt(e)&&(e.type===`submit`||e.type===`image`)){let o=e.form;if(o==null)throw Error(`Cannot submit a