diff --git a/README.md b/README.md index 3619190..f3d8d02 100644 --- a/README.md +++ b/README.md @@ -778,7 +778,16 @@ limits are one reason confidence never rises above Medium. Ordinary input, cache cache-write input, and output are priced separately; requests above the published 272,000-input-token threshold use the corresponding long-context rates where OpenAI publishes them. Unknown models or missing price classes fail -closed as `UNPRICED MODEL MIX` rather than being guessed or treated as free. +closed as `UNPRICED USAGE` rather than being guessed or treated as free. +Codex's context-window bookkeeping updates are recognised only when the window +and both complete, explicit zero-token-class breakdowns match its synthetic fill record. +They rebase the local counter without adding response costs, token totals or +model-call graph pulses. Quota learning restarts as `LOCAL ACCOUNTING REBASED`. +Events with a missing or null cumulative total are ignored without replacing +the current baseline, including inherited child history and historical scans. +Missing input/output usage and inconsistent response/cumulative usage instead +restart as `MISSING RESPONSE USAGE` or `INCONSISTENT RESPONSE USAGE`; no costs +are guessed. Unknown model/tier or unavailable pricing remains `UNPRICED USAGE`. Core, Extended, and DigBench trials use ephemeral threads that intentionally do not appear in normal persisted session telemetry. While a subscription-funded benchmark suite is active, Quota views replace the numeric API-equivalent @@ -831,7 +840,7 @@ used quota falls, the account or window definition changes, finalized counters regress, the quota moves five points without any matching priced local call, or an unknown/unpriced model occurs. The learning readout retains the reason, for example `RESTARTED: WINDOW RESET`, `WINDOW DEFINITION CHANGED`, `LOCAL -ACCOUNTING REBASED`, `UNPRICED MODEL MIX`, or `LOCAL COVERAGE GAP`, while new +ACCOUNTING REBASED`, `UNPRICED USAGE`, or `LOCAL COVERAGE GAP`, while new clean movement accumulates. It never silently returns to `0/5PP`. Even a valid estimate can still vary with reasoning effort, model mix, caching, prompt shape, and backend quota weighting, so compare ranges and sample counts rather @@ -1590,7 +1599,11 @@ dismisses suggestions while retaining the draft and typing focus. Suggestions are fetched asynchronously and filtered locally. They also work in a selected wide terminal session row when space permits. Outside the full-detail composer, press `/` to open the full command browser. Use arrows and Enter to browse, **C** to confirm a -setting change, Left to go back and Escape to return to the session. Buttons +setting change, Left to go back and Escape to return to the session. Speed menus +keep all options visible: Up/Down highlights a choice, **Enter REVIEW** stages it, +and **C CONFIRM** applies it. The separate **CURRENT** marker identifies the +existing speed. Model, reasoning and speed changes are provisional until C; +reviewing or changing the highlight sends no settings update. Buttons are clickable. Writable web mode exposes the same catalogue through **/ COMMANDS**, with the same composer suggestion shortcuts. @@ -1600,7 +1613,7 @@ are clickable. Writable web mode exposes the same catalogue through | `/cwd`, `/pwd` | Read-only aliases showing the running session’s current directory, including during active turns. | | `/rename` | Edit the selected session’s saved name, review it and confirm. Uses Codex’s name API; works during an active turn and does not send a prompt or change model settings. | | `/model` | Advertised models, their descriptions and supported reasoning levels. Choosing a model resets its explicit speed override to the server default. | -| Advertised speed commands, such as `/fast` | Names, IDs and descriptions come from the current model's service-tier catalogue; enable a tier or clear the explicit override. No speed names or tier IDs are assumed. | +| Advertised speed commands, such as `/fast` | Show the default speed and every advertised tier in one list, initially highlighting the current speed. Up/Down selects; **Enter REVIEW** provisionally selects the highlighted choice while keeping the list visible, then **C CONFIRM** applies it. Changing the highlight cancels the pending confirmation. IDs and descriptions come from the current model's catalogue; no tier IDs are guessed. The changed speed is verified before success is reported. | | `/plan` | Advertised collaboration modes, using Codex's built-in mode instructions. | | `/permissions` | Named profiles allowed for the session's directory, with supplied descriptions. Review carefully: this changes future permission behaviour. | | `/skills`, `/apps`, `/mcp`, `/hooks`, `/experimental` | **Browse-only** live inventories and supplied help, including MCP tool descriptions. Invoke/configure these through Codex. | @@ -2446,6 +2459,21 @@ deterministic PASS/FAIL verifier. ## Options +The full-screen terminal UI and browser interface randomly choose one of three +CODEXOMETER entrances: + +- The logo slides in from the right. +- A dot blinks three times, then advances as the name is typed letter by letter. +- Eleven random characters shuffle and resolve into the name in a random order. + +All three use the same full-width block lettering at the vertical centre, then +shrink into the header before the dashboard appears. Data loads during the +roughly two-to-three-second animation. Press any key or left-click to skip. +The browser plays it once per page load, respects reduced-motion preferences, +and uses the same block logo in its header. In the terminal, `q` or Ctrl+C quits +immediately; inline mode and terminals smaller than 68 columns or eight rows +open directly. + ```text --codex PATH path to the Codex CLI (default: codex) --check-auth verify the current Codex login and exit @@ -2608,7 +2636,9 @@ codexometer --web --web-port 8765 1. Run one of these commands and keep that terminal open. 2. Open the private `http://127.0.0.1:PORT/#pair=...` link printed in the terminal - within five minutes. The application does not automatically launch a browser. + within five minutes. Terminal output includes an underlined hyperlink; in + Ghostty on macOS, hold Cmd while clicking. Redirected output remains plain text. + The application does not automatically launch a browser. 3. Pairing exchanges the one-use secret for a temporary browser capability and removes the secret from the visible URL. Do not share the original link. 4. Browse **Quota**, **Sessions**, and **Usage**. Refresh and browser Back/Forward diff --git a/docs/slash-command-support.md b/docs/slash-command-support.md index d571abb..f7f7b74 100644 --- a/docs/slash-command-support.md +++ b/docs/slash-command-support.md @@ -46,7 +46,7 @@ Aliases on one row share the rating and implementation. Lifecycle commands share | `/experimental` | Browse only | P3 | Medium | `experimentalFeature/list`. Enablement changes process/configuration scope, rather than a single session; no mutation UI. Proposed next increment: review an advertised enablement change with its process/configuration scope and refresh/restart effects made clear. | | `/statusline` | Local equivalent | Done | — | Codexometer's own footer picker; also available in read-only web mode. Does not edit the native CLI's status line. | | `/help` | Local catalogue | Done | — | Codexometer catalogue entry; absent from the upstream registry. | -| Advertised speed aliases, e.g. `/fast` | Session adapter | Done | — | Use live model-advertised tier names/IDs, with confirmed session overrides; global defaults remain out of scope. | +| Advertised speed aliases, e.g. `/fast` | Session adapter | Done | — | Show all live model-advertised speeds plus the default in one picker; Up/Down selects, Enter Review stages the choice, and C Confirm commits a verified session override. Global defaults remain out of scope. | `/help` is Codexometer's catalogue entry, absent from this upstream registry. Speed aliases such as `/fast` appear only when the current model advertises that tier; IDs are not guessed. They change session overrides rather than native persistent defaults. diff --git a/go.mod b/go.mod index 03facd8..59def12 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/charmbracelet/colorprofile v0.4.3 github.com/charmbracelet/ultraviolet v0.0.0-20260811164956-006e29f97886 github.com/charmbracelet/x/ansi v0.11.8 + github.com/charmbracelet/x/term v0.2.2 github.com/gorilla/websocket v1.5.3 go.starlark.net v0.0.0-20260930220527-d7438c5a85ac golang.org/x/sys v0.48.0 @@ -17,7 +18,6 @@ require ( require ( github.com/atotto/clipboard v0.1.4 // indirect - github.com/charmbracelet/x/term v0.2.2 // indirect github.com/charmbracelet/x/termios v0.1.1 // indirect github.com/charmbracelet/x/windows v0.2.2 // indirect github.com/clipperhouse/displaywidth v0.11.0 // indirect diff --git a/internal/codex/live_usage.go b/internal/codex/live_usage.go index 544a77a..b4ea1ad 100644 --- a/internal/codex/live_usage.go +++ b/internal/codex/live_usage.go @@ -30,20 +30,26 @@ const ( // kept separate until daemon model resolution or requested-model fallback. // APIEqUSD remains standard cost. Quota learning adds APIEqTierPremiumUSD; // APIEqUnknownTierCalls identifies standard fallbacks with missing tier evidence. +// Missing/inconsistent response usage has separate monotonic counters from +// unavailable pricing. AccountingAdjustments counts recognised synthetic context +// fills; those are excluded from observed tokens, activity and response graphs. type LiveUsageSnapshot struct { - TotalTokens int64 - LastActivity time.Time - SessionCount int - Sessions []LiveUsageSession - CodexStatusKnown bool - CodexUp bool - CodexWorking bool - APIEqUSD float64 - APIEqPricedCalls int64 - APIEqUnpricedCalls int64 - APIEqPendingCalls int64 - APIEqTierPremiumUSD float64 - APIEqUnknownTierCalls int64 + TotalTokens int64 + LastActivity time.Time + SessionCount int + Sessions []LiveUsageSession + CodexStatusKnown bool + CodexUp bool + CodexWorking bool + APIEqUSD float64 + APIEqPricedCalls int64 + APIEqUnpricedCalls int64 + APIEqMissingUsageCalls int64 + APIEqInconsistentUsageCalls int64 + APIEqAccountingAdjustments int64 + APIEqPendingCalls int64 + APIEqTierPremiumUSD float64 + APIEqUnknownTierCalls int64 } // LiveUsageSession is one independently started local Codex session. Token @@ -81,8 +87,9 @@ const ( SessionAttentionComplete ) -// LiveModelCall is the small, content-free usage pulse persisted after one -// upstream model response. +// LiveModelCall is a small, content-free response-usage pulse. APIEqIssue retains +// the reason a response could not be priced. Synthetic context bookkeeping does +// not create pulses. type LiveModelCall struct { Sequence uint64 At time.Time @@ -91,6 +98,7 @@ type LiveModelCall struct { Model string APIEqUSD float64 APIEqKnown bool + APIEqIssue string apiEqFinalized bool RequestedServiceTier string APIEqTierPremiumUSD float64 @@ -117,24 +125,27 @@ type LiveUsageReader struct { WriterLocksRoot string statusProvider sessionStatusProvider - mu sync.Mutex - initialized bool - startedAt time.Time - lastDiscovery time.Time - lastFullDiscovery time.Time - files map[string]*rolloutCursor - totalTokens int64 - apiEqUSD float64 - apiEqTierPremiumUSD float64 - apiEqUnknownTierCalls int64 - apiEqPricedCalls int64 - apiEqUnknownCalls int64 - lastActivity time.Time - nextEventSequence uint64 - daemonObservationSequence uint64 - resolvedObservations []resolvedModelObservation - pendingModelResolutions []pendingModelResolution - daemonSubscribedThreads map[string]struct{} + mu sync.Mutex + initialized bool + startedAt time.Time + lastDiscovery time.Time + lastFullDiscovery time.Time + files map[string]*rolloutCursor + totalTokens int64 + apiEqUSD float64 + apiEqTierPremiumUSD float64 + apiEqUnknownTierCalls int64 + apiEqPricedCalls int64 + apiEqUnknownCalls int64 + apiEqMissingUsageCalls int64 + apiEqInconsistentUsageCalls int64 + apiEqAccountingAdjustments int64 + lastActivity time.Time + nextEventSequence uint64 + daemonObservationSequence uint64 + resolvedObservations []resolvedModelObservation + pendingModelResolutions []pendingModelResolution + daemonSubscribedThreads map[string]struct{} } type rolloutCursor struct { @@ -185,30 +196,37 @@ type rolloutEvent struct { Payload struct { Type string `json:"type"` Info *struct { - TotalTokenUsage rolloutTokenUsage `json:"total_token_usage"` - LastTokenUsage rolloutTokenUsage `json:"last_token_usage"` + TotalTokenUsage rolloutTokenUsage `json:"total_token_usage"` + LastTokenUsage rolloutTokenUsage `json:"last_token_usage"` + ModelContextWindow *int64 `json:"model_context_window"` } `json:"info"` } `json:"payload"` } type rolloutTokenUsage struct { - InputTokens int64 `json:"input_tokens"` - CachedInputTokens int64 `json:"cached_input_tokens"` + InputTokens *int64 `json:"input_tokens"` + CachedInputTokens *int64 `json:"cached_input_tokens"` CacheWriteInputTokens int64 `json:"cache_write_input_tokens"` OutputTokens *int64 `json:"output_tokens"` - ReasoningOutputTokens int64 `json:"reasoning_output_tokens"` - TotalTokens int64 `json:"total_tokens"` + ReasoningOutputTokens *int64 `json:"reasoning_output_tokens"` + TotalTokens *int64 `json:"total_tokens"` } -func (u rolloutTokenUsage) benchmarkUsage() BenchmarkUsage { - output := int64(0) - if u.OutputTokens != nil { - output = *u.OutputTokens +func optionalRolloutTokens(value *int64) int64 { + if value == nil { + return 0 } + return *value +} + +func (u rolloutTokenUsage) benchmarkUsage() BenchmarkUsage { return BenchmarkUsage{ - InputTokens: u.InputTokens, CachedInputTokens: u.CachedInputTokens, - CacheWriteInputTokens: u.CacheWriteInputTokens, OutputTokens: output, - ReasoningOutputTokens: u.ReasoningOutputTokens, TotalTokens: u.TotalTokens, + InputTokens: optionalRolloutTokens(u.InputTokens), + CachedInputTokens: optionalRolloutTokens(u.CachedInputTokens), + CacheWriteInputTokens: u.CacheWriteInputTokens, + OutputTokens: optionalRolloutTokens(u.OutputTokens), + ReasoningOutputTokens: optionalRolloutTokens(u.ReasoningOutputTokens), + TotalTokens: optionalRolloutTokens(u.TotalTokens), } } @@ -397,10 +415,13 @@ func (r *LiveUsageReader) fetchTokenUsage(ctx context.Context, forceFullDiscover CodexUp: codexUp, CodexWorking: codexWorking, APIEqUSD: r.apiEqUSD, APIEqPricedCalls: r.apiEqPricedCalls, - APIEqUnpricedCalls: r.apiEqUnknownCalls, - APIEqPendingCalls: int64(len(r.pendingModelResolutions)), - APIEqTierPremiumUSD: r.apiEqTierPremiumUSD, - APIEqUnknownTierCalls: r.apiEqUnknownTierCalls, + APIEqUnpricedCalls: r.apiEqUnknownCalls, + APIEqMissingUsageCalls: r.apiEqMissingUsageCalls, + APIEqInconsistentUsageCalls: r.apiEqInconsistentUsageCalls, + APIEqAccountingAdjustments: r.apiEqAccountingAdjustments, + APIEqPendingCalls: int64(len(r.pendingModelResolutions)), + APIEqTierPremiumUSD: r.apiEqTierPremiumUSD, + APIEqUnknownTierCalls: r.apiEqUnknownTierCalls, }, nil } @@ -635,6 +656,17 @@ func (r *LiveUsageReader) consume(path string, cursor *rolloutCursor) error { cursor.totalTokens = record.total continue } + // Codex fills its context counter after ContextWindowExceeded. This + // exact, complete zero-breakdown shape is bookkeeping, not a + // response. Rebase the cursor without adding tokens or a graph + // pulse; the separate counter makes quota learning restart safely. + if record.contextWindowFill && record.usage.TotalTokens == max(record.total-cursor.totalTokens, 0) { + if record.total != cursor.totalTokens { + r.apiEqAccountingAdjustments++ + } + cursor.totalTokens = record.total + continue + } if record.total >= cursor.totalTokens { delta := record.total - cursor.totalTokens r.totalTokens += delta @@ -649,7 +681,7 @@ func (r *LiveUsageReader) consume(path string, cursor *rolloutCursor) error { r.nextEventSequence++ model := cursor.currentModel pricingUsage := record.usage - usageMatchesDelta := record.usage.TotalTokens == delta + usageMatchesDelta := record.usageAvailable && record.usage.TotalTokens == delta && validateBenchmarkUsage(record.usage) == "" _, daemonSubscribed := r.daemonSubscribedThreads[cursor.threadID] resolutionPending := daemonSubscribed && cursor.threadID != "" && cursor.currentTurnID != "" resolved := false @@ -668,7 +700,16 @@ func (r *LiveUsageReader) consume(path string, cursor *rolloutCursor) error { r.finalizeCallPricing(&call, pricingUsage) } else if !resolutionDeferred { call.apiEqFinalized = true - r.apiEqUnknownCalls++ + if !record.usageAvailable { + call.APIEqIssue = "missing response input/output usage" + r.apiEqMissingUsageCalls++ + } else { + call.APIEqIssue = validateBenchmarkUsage(record.usage) + if call.APIEqIssue == "" { + call.APIEqIssue = "cumulative token increase does not match last response usage" + } + r.apiEqInconsistentUsageCalls++ + } } cursor.modelCalls = appendBounded(cursor.modelCalls, call, telemetryHistoryMax) if resolutionDeferred { @@ -728,9 +769,9 @@ func (r *LiveUsageReader) reconcilePendingModelResolutions() { for _, pending := range r.pendingModelResolutions { call := r.modelCall(pending.callSequence) if call == nil { - // The call aged out of the bounded per-session history before it - // could be finalized. Preserve fail-closed monotonic accounting. - r.apiEqUnknownCalls++ + // The response aged out before model resolution; the loss is a + // usage-coverage issue, not evidence of an unsupported model. + r.apiEqMissingUsageCalls++ continue } if call.apiEqFinalized { @@ -907,12 +948,14 @@ func tokenTotalRecord(line []byte) (int64, time.Time, *uint64, bool) { } type rolloutTokenRecord struct { - total int64 - outputTokens int64 - outputKnown bool - usage BenchmarkUsage - at time.Time - ordinal *uint64 + usageAvailable bool + contextWindowFill bool + total int64 + outputTokens int64 + outputKnown bool + usage BenchmarkUsage + at time.Time + ordinal *uint64 } func tokenUsageRecord(line []byte) (rolloutTokenRecord, bool) { @@ -924,11 +967,31 @@ func tokenUsageRecord(line []byte) (rolloutTokenRecord, bool) { event.Payload.Type != "token_count" || event.Payload.Info == nil { return rolloutTokenRecord{}, false } - lastUsage := event.Payload.Info.LastTokenUsage.benchmarkUsage() - outputKnown := event.Payload.Info.LastTokenUsage.OutputTokens != nil + info := event.Payload.Info + // An absent cumulative counter is unknown, not a reset to zero. Reject it + // before either live consumption or historical/inherited baseline scans. + if info.TotalTokenUsage.TotalTokens == nil { + return rolloutTokenRecord{}, false + } + lastUsage := info.LastTokenUsage.benchmarkUsage() + outputKnown := info.LastTokenUsage.OutputTokens != nil + // Require every core token field in both breakdowns. Missing legacy + // telemetry must never be mistaken for Codex's synthetic full-context event. + zeroClasses := func(u rolloutTokenUsage) bool { + return u.InputTokens != nil && *u.InputTokens == 0 && + u.OutputTokens != nil && *u.OutputTokens == 0 && + u.CachedInputTokens != nil && *u.CachedInputTokens == 0 && + u.ReasoningOutputTokens != nil && *u.ReasoningOutputTokens == 0 && + u.TotalTokens != nil && u.CacheWriteInputTokens == 0 + } + contextFill := info.ModelContextWindow != nil && *info.ModelContextWindow > 0 && + *info.TotalTokenUsage.TotalTokens == *info.ModelContextWindow && + zeroClasses(info.TotalTokenUsage) && zeroClasses(info.LastTokenUsage) return rolloutTokenRecord{ - total: event.Payload.Info.TotalTokenUsage.TotalTokens, - outputTokens: lastUsage.OutputTokens, outputKnown: outputKnown, + usageAvailable: info.LastTokenUsage.InputTokens != nil && outputKnown, + contextWindowFill: contextFill, + total: *info.TotalTokenUsage.TotalTokens, + outputTokens: lastUsage.OutputTokens, outputKnown: outputKnown, usage: lastUsage, at: event.Timestamp, ordinal: event.Ordinal, }, true diff --git a/internal/codex/live_usage_accounting_test.go b/internal/codex/live_usage_accounting_test.go new file mode 100644 index 0000000..d5b2c61 --- /dev/null +++ b/internal/codex/live_usage_accounting_test.go @@ -0,0 +1,293 @@ +package codex + +import ( + "context" + "encoding/json" + "math" + "strings" + "testing" + "time" +) + +func contextFillLine(t *testing.T, at time.Time, window, delta int64) string { + t.Helper() + zero := func(total int64) map[string]any { + return map[string]any{ + "input_tokens": 0, "cached_input_tokens": 0, "output_tokens": 0, + "reasoning_output_tokens": 0, "total_tokens": total, + } + } + raw, err := json.Marshal(map[string]any{"timestamp": at, "type": "event_msg", "payload": map[string]any{ + "type": "token_count", "info": map[string]any{"total_token_usage": zero(window), "last_token_usage": zero(delta), "model_context_window": window}, + }}) + if err != nil { + t.Fatal(err) + } + return string(raw) +} + +func astraUsageReader(t *testing.T, baseline int64) (*LiveUsageReader, string, time.Time) { + t.Helper() + home := t.TempDir() + now := time.Now().UTC() + path := testRolloutPath(t, home, now.Add(-time.Hour), "astra-accounting") + writeRollout(t, path, sessionMetaLine("astra-accounting", `"cli"`, "/synthetic", nil)+"\n"+ + tierSettingsLine(now, `"fast"`)+"\n"+`{"type":"turn_context","payload":{"model":"gpt-6-astra","effort":"xhigh"}}`+"\n"+tokenCountLine(now, baseline)+"\n") + r, err := NewLiveUsageReader(home) + if err != nil { + t.Fatal(err) + } + if _, err = r.FetchTokenUsage(context.Background()); err != nil { + t.Fatal(err) + } + return r, path, now +} + +func TestContextWindowFillRebasesWithoutResponseTokensOrPricing(t *testing.T) { + r, path, now := astraUsageReader(t, 100) + appendRollout(t, path, richTokenCountLine(now, 1200, 1000, 200, 0, 100)+"\n") + before, err := r.FetchTokenUsage(context.Background()) + if err != nil || before.APIEqPricedCalls != 1 || before.APIEqUnpricedCalls != 0 || before.APIEqTierPremiumUSD != before.APIEqUSD { + t.Fatal("normal Astra xhigh Fast failed", before, err) + } + fill := contextFillLine(t, now.Add(time.Second), 128000, 126800) + appendRollout(t, path, fill+"\n") + after, err := r.FetchTokenUsage(context.Background()) + if err != nil || after.TotalTokens != before.TotalTokens || after.Sessions[0].TotalTokens != before.Sessions[0].TotalTokens || + !after.LastActivity.Equal(before.LastActivity) || after.APIEqPricedCalls != 1 || after.APIEqUSD != before.APIEqUSD || + after.APIEqTierPremiumUSD != before.APIEqTierPremiumUSD || after.APIEqUnpricedCalls != 0 || after.APIEqMissingUsageCalls != 0 || + after.APIEqInconsistentUsageCalls != 0 || after.APIEqPendingCalls != 0 || after.APIEqAccountingAdjustments != 1 || len(after.Sessions[0].ModelCalls) != 1 { + t.Fatalf("bookkeeping became usage, price error, activity or graph pulse: %+v %v", after, err) + } + // Repeated notification and polling must not adjust twice. + appendRollout(t, path, fill+"\n") + repeated, _ := r.FetchTokenUsage(context.Background()) + if repeated.APIEqAccountingAdjustments != 1 || repeated.TotalTokens != before.TotalTokens { + t.Fatal("duplicate adjustment", repeated) + } + appendRollout(t, path, richTokenCountLine(now.Add(2*time.Second), 129100, 1000, 200, 0, 100)+"\n") + resumed, err := r.FetchTokenUsage(context.Background()) + if err != nil || resumed.TotalTokens != 2200 || resumed.APIEqPricedCalls != 2 || resumed.APIEqUnpricedCalls != 0 || resumed.APIEqMissingUsageCalls != 0 || + resumed.APIEqInconsistentUsageCalls != 0 || resumed.APIEqAccountingAdjustments != 1 || len(resumed.Sessions[0].ModelCalls) != 2 || + math.Abs(resumed.APIEqUSD-2*before.APIEqUSD) > 1e-12 || resumed.APIEqTierPremiumUSD != resumed.APIEqUSD { + t.Fatal("normal response after rebase lost/double counted", resumed, err) + } +} + +func TestContextWindowFillCanReduceCounterWithoutLosingNextResponse(t *testing.T) { + r, path, now := astraUsageReader(t, 200000) + appendRollout(t, path, contextFillLine(t, now, 128000, 0)+"\n"+richTokenCountLine(now, 129100, 1000, 0, 0, 100)+"\n") + usage, err := r.FetchTokenUsage(context.Background()) + if err != nil || usage.TotalTokens != 1100 || usage.APIEqAccountingAdjustments != 1 || usage.APIEqPricedCalls != 1 || usage.APIEqUnpricedCalls != 0 { + t.Fatal("downward fill lost next response", usage, err) + } +} + +func TestContextWindowFillRequiresExactExplicitBreakdowns(t *testing.T) { + for _, kind := range []string{"missing-window", "different-window", "missing-input", "missing-output", "missing-cached", "missing-reasoning", "missing-last-total", "nonzero-input", "nonzero-total-cache", "wrong-delta"} { + t.Run(kind, func(t *testing.T) { + r, path, now := astraUsageReader(t, 100) + var event map[string]any + if err := json.Unmarshal([]byte(contextFillLine(t, now, 128000, 127900)), &event); err != nil { + t.Fatal(err) + } + info := event["payload"].(map[string]any)["info"].(map[string]any) + total, last := info["total_token_usage"].(map[string]any), info["last_token_usage"].(map[string]any) + switch kind { + case "missing-window": + delete(info, "model_context_window") + case "different-window": + info["model_context_window"] = 256000 + case "missing-input": + delete(last, "input_tokens") + case "missing-output": + delete(total, "output_tokens") + case "missing-cached": + delete(last, "cached_input_tokens") + case "missing-reasoning": + delete(total, "reasoning_output_tokens") + case "missing-last-total": + delete(last, "total_tokens") + case "nonzero-input": + last["input_tokens"] = 10 + case "nonzero-total-cache": + total["cached_input_tokens"] = 1 + case "wrong-delta": + last["total_tokens"] = 127000 + } + raw, _ := json.Marshal(event) + appendRollout(t, path, string(raw)+"\n") + usage, err := r.FetchTokenUsage(context.Background()) + if err != nil || usage.APIEqAccountingAdjustments != 0 || usage.APIEqPricedCalls != 0 || usage.APIEqUnpricedCalls != 0 || + usage.APIEqMissingUsageCalls+usage.APIEqInconsistentUsageCalls != 1 || usage.TotalTokens != 127900 { + t.Fatal("incomplete/ambiguous record silently discarded", usage, err) + } + }) + } +} + +func TestResponseUsageProblemsAreSeparateFromUnknownPrices(t *testing.T) { + for _, kind := range []string{"missing-input", "missing-output", "cumulative-mismatch", "invalid-breakdown", "unknown-model", "unsupported-tier"} { + t.Run(kind, func(t *testing.T) { + r, path, now := astraUsageReader(t, 100) + event := richTokenCountLine(now, 1200, 1000, 0, 0, 100) + switch kind { + case "missing-input": + event = strings.Replace(event, `"input_tokens":1000,`, "", 1) + case "missing-output": + event = strings.Replace(event, `"output_tokens":100,`, "", 1) + case "cumulative-mismatch": + event = richTokenCountLine(now, 5000, 1000, 0, 0, 100) + case "invalid-breakdown": + event = richTokenCountLine(now, 1200, 1000, 1001, 0, 100) + case "unknown-model": + event = turnContextLine(now, "gpt-6-astra-preview") + "\n" + event + case "unsupported-tier": + event = tierSettingsLine(now, `"flex"`) + "\n" + turnContextLine(now, "gpt-6-astra") + "\n" + event + } + appendRollout(t, path, event+"\n") + usage, err := r.FetchTokenUsage(context.Background()) + if err != nil || usage.APIEqPricedCalls != 0 || usage.APIEqUSD != 0 || usage.APIEqTierPremiumUSD != 0 || usage.APIEqPendingCalls != 0 || usage.APIEqAccountingAdjustments != 0 { + t.Fatal("unsafe cost or wrong accounting", usage, err) + } + missing, inconsistent, unpriced := int64(0), int64(0), int64(0) + switch kind { + case "missing-input", "missing-output": + missing = 1 + case "cumulative-mismatch", "invalid-breakdown": + inconsistent = 1 + default: + unpriced = 1 + } + if usage.APIEqMissingUsageCalls != missing || usage.APIEqInconsistentUsageCalls != inconsistent || usage.APIEqUnpricedCalls != unpriced || + len(usage.Sessions[0].ModelCalls) != 1 || usage.Sessions[0].ModelCalls[0].APIEqIssue == "" { + t.Fatalf("cause lost: %+v", usage) + } + again, _ := r.FetchTokenUsage(context.Background()) + if again.APIEqMissingUsageCalls != missing || again.APIEqInconsistentUsageCalls != inconsistent || again.APIEqUnpricedCalls != unpriced { + t.Fatal("double counted", again) + } + }) + } +} + +func incompleteCumulativeUsageLine(t *testing.T, at time.Time, shape string) string { + t.Helper() + var event map[string]any + if err := json.Unmarshal([]byte(richTokenCountLine(at, 1200, 1000, 0, 0, 100)), &event); err != nil { + t.Fatal(err) + } + info := event["payload"].(map[string]any)["info"].(map[string]any) + switch shape { + case "missing-counter": + delete(info["total_token_usage"].(map[string]any), "total_tokens") + case "null-counter": + info["total_token_usage"].(map[string]any)["total_tokens"] = nil + case "missing-total": + delete(info, "total_token_usage") + case "null-total": + info["total_token_usage"] = nil + default: + t.Fatalf("unknown incomplete usage shape %q", shape) + } + raw, err := json.Marshal(event) + if err != nil { + t.Fatal(err) + } + return string(raw) +} + +func TestMissingCumulativeTotalLeavesLiveCursorUnchanged(t *testing.T) { + for _, shape := range []string{"missing-counter", "null-counter", "missing-total", "null-total"} { + t.Run(shape, func(t *testing.T) { + r, path, now := astraUsageReader(t, 100) + appendRollout(t, path, richTokenCountLine(now, 1200, 1000, 0, 0, 100)+"\n") + before, err := r.FetchTokenUsage(context.Background()) + if err != nil { + t.Fatal(err) + } + appendRollout(t, path, incompleteCumulativeUsageLine(t, now.Add(time.Second), shape)+"\n") + after, err := r.FetchTokenUsage(context.Background()) + if err != nil || r.files[path].totalTokens != 1200 || after.TotalTokens != before.TotalTokens || + after.APIEqUSD != before.APIEqUSD || after.APIEqPricedCalls != before.APIEqPricedCalls || + !after.LastActivity.Equal(before.LastActivity) || len(after.Sessions[0].ModelCalls) != 1 { + t.Fatal("incomplete cumulative usage changed accounting or activity", after, err) + } + appendRollout(t, path, richTokenCountLine(now.Add(2*time.Second), 2200, 900, 0, 0, 100)+"\n") + next, err := r.FetchTokenUsage(context.Background()) + if err != nil || next.TotalTokens != 2100 || next.Sessions[0].TotalTokens != 2100 || + next.APIEqPricedCalls != 2 || next.APIEqInconsistentUsageCalls != 0 || next.APIEqMissingUsageCalls != 0 || len(next.Sessions[0].ModelCalls) != 2 { + t.Fatal("next response was counted from an incorrect baseline", next, err) + } + }) + } +} + +func TestMissingCumulativeTotalDoesNotReplaceHistoricalBaseline(t *testing.T) { + for _, shape := range []string{"missing-counter", "null-counter", "missing-total", "null-total"} { + t.Run(shape, func(t *testing.T) { + home := t.TempDir() + now := time.Now().UTC() + path := testRolloutPath(t, home, now.Add(-time.Hour), "incomplete-baseline") + writeRollout(t, path, tokenCountLine(now, 1000)+"\n"+incompleteCumulativeUsageLine(t, now, shape)+"\n") + r, err := NewLiveUsageReader(home) + if err != nil { + t.Fatal(err) + } + before, err := r.FetchTokenUsage(context.Background()) + if err != nil || r.files[path].totalTokens != 1000 || before.TotalTokens != 0 { + t.Fatal("incomplete tail replaced the historical baseline", before, err) + } + appendRollout(t, path, richTokenCountLine(now.Add(time.Second), 1200, 150, 0, 0, 50)+"\n") + after, err := r.FetchTokenUsage(context.Background()) + if err != nil || after.TotalTokens != 200 || len(after.Sessions[0].ModelCalls) != 1 { + t.Fatal("historical usage was counted again", after, err) + } + }) + } +} + +func TestMissingInheritedCumulativeTotalPreservesChildBaseline(t *testing.T) { + for _, ordinal := range []bool{false, true} { + name := "legacy" + if ordinal { + name = "ordinal" + } + t.Run(name, func(t *testing.T) { + home := t.TempDir() + now := time.Now().UTC() + parent := testRolloutPath(t, home, now.Add(-time.Hour), "parent") + writeRollout(t, parent, sessionMetaLine("parent", `"cli"`, "/work", nil)+"\n"+tokenCountLine(now, 1000)+"\n") + r, err := NewLiveUsageReader(home) + if err != nil { + t.Fatal(err) + } + if _, err := r.FetchTokenUsage(context.Background()); err != nil { + t.Fatal(err) + } + var boundary *uint64 + inherited := tokenCountLine(now.Add(-time.Minute), 1000) + owned := tokenCountLine(now.Add(time.Second), 1200) + incomplete := incompleteCumulativeUsageLine(t, now.Add(-time.Minute), "missing-counter") + if ordinal { + start := uint64(3) + boundary = &start + inherited = tokenCountLineWithOrdinal(now, 1000, 1) + owned = tokenCountLineWithOrdinal(now.Add(time.Second), 1200, 3) + incomplete = strings.Replace(incomplete, `"type":"event_msg"`, `"ordinal":1,"type":"event_msg"`, 1) + } + child := testRolloutPath(t, home, now.Add(2*time.Second), "child") + writeRollout(t, child, sessionMetaLineAt("child", threadSpawnSource("parent"), "/work", boundary, now)+"\n"+inherited+"\n"+incomplete+"\n") + r.lastDiscovery = time.Time{} + before, err := r.FetchTokenUsage(context.Background()) + if err != nil || before.TotalTokens != 0 || r.files[child].totalTokens != 1000 || len(r.files[child].modelCalls) != 0 { + t.Fatal("incomplete inherited event reset the child baseline", before, err) + } + appendRollout(t, child, owned+"\n") + after, err := r.FetchTokenUsage(context.Background()) + if err != nil || after.TotalTokens != 200 || len(r.files[child].modelCalls) != 1 || after.Sessions[0].ID != "parent" { + t.Fatal("first owned child usage counted inherited tokens", after, err) + } + }) + } +} diff --git a/internal/codex/live_usage_test.go b/internal/codex/live_usage_test.go index fcb26ee..d254bfd 100644 --- a/internal/codex/live_usage_test.go +++ b/internal/codex/live_usage_test.go @@ -701,7 +701,7 @@ func TestLiveUsageReaderRerouteCorrectionFailsClosedOnUnpriceableUsage(t *testin reader.reconcilePendingModelResolutions() call := reader.files["rollout"].modelCalls[0] if call.Model != "gpt-5.6-terra" || call.APIEqKnown || reader.apiEqUSD != 0 || - reader.apiEqPricedCalls != 0 || reader.apiEqUnknownCalls != 1 { + reader.apiEqPricedCalls != 0 || reader.apiEqUnknownCalls != 0 || reader.apiEqInconsistentUsageCalls != 1 || call.APIEqIssue == "" { t.Fatalf("unpriceable reroute correction = call %#v reader %#v", call, reader) } } diff --git a/internal/codex/service_tier.go b/internal/codex/service_tier.go index 51ad480..39d3168 100644 --- a/internal/codex/service_tier.go +++ b/internal/codex/service_tier.go @@ -65,9 +65,18 @@ func rolloutTierRecord(line []byte) (string, *uint64, time.Time, bool) { } func (r *LiveUsageReader) finalizeCallPricing(call *LiveModelCall, usage BenchmarkUsage) { - call.APIEqUSD, call.APIEqKnown, _ = EstimateStandardAPIEqCost(call.Model, usage) + if issue := validateBenchmarkUsage(usage); issue != "" { + call.APIEqIssue = issue + call.apiEqFinalized = true + r.apiEqInconsistentUsageCalls++ + return + } + call.APIEqUSD, call.APIEqKnown, call.APIEqIssue = EstimateStandardAPIEqCost(call.Model, usage) if call.APIEqKnown { call.APIEqTierPremiumUSD, call.APIEqKnown = requestedTierPremium(call.Model, call.RequestedServiceTier, call.APIEqUSD) + if !call.APIEqKnown { + call.APIEqIssue = "unavailable pricing for requested service tier" + } } call.apiEqFinalized = true if call.APIEqKnown { diff --git a/internal/codex/session_commands.go b/internal/codex/session_commands.go index dbf6711..2dd8bab 100644 --- a/internal/codex/session_commands.go +++ b/internal/codex/session_commands.go @@ -63,11 +63,13 @@ type SessionCommandChoice struct { } type SessionCommandMenu struct { - Title string `json:"title"` - Help string `json:"help"` - Path string `json:"path"` - Revision string `json:"revision"` - Multiple bool `json:"multiple,omitempty"` + Title string `json:"title"` + Help string `json:"help"` + Path string `json:"path"` + Revision string `json:"revision"` + Multiple bool `json:"multiple,omitempty"` + // Picker keeps choices visible; the highlighted option is applied explicitly. + Picker bool `json:"picker,omitempty"` Input bool `json:"input,omitempty"` InputLabel string `json:"inputLabel,omitempty"` InputLimit int `json:"inputLimit,omitempty"` diff --git a/internal/codex/session_commands_unix.go b/internal/codex/session_commands_unix.go index 21988f6..3653d89 100644 --- a/internal/codex/session_commands_unix.go +++ b/internal/codex/session_commands_unix.go @@ -285,15 +285,31 @@ func (p *daemonStatusProvider) commandMenu(ctx context.Context, t commandThread, return m, nil } if parts[0] == "tier" && model.Model == current.Model { + advertised := false for _, tier := range model.ServiceTiers { - if tier.ID == target { + if tier.ID == target && commandSafe(tier.ID) && commandSafe(tier.Name) { m.Title = "/" + strings.ToLower(tier.Name) - m.Help = tier.Description - setting("Enable "+tier.Name, tier.Description, map[string]any{"serviceTier": tier.ID}) - setting("Use default tier", "Clear this session's explicit speed override.", map[string]any{"serviceTier": nil}) - return m, nil + advertised = true + break } } + if !advertised { + return m, ErrSessionCommand + } + m.Picker = true + m.Help = "Choose a speed for this session. Changes apply to subsequent turns; automatic quota thresholds may later replace them." + setting("Standard (default)", "Clear this session's explicit speed override and use the server default.", map[string]any{"serviceTier": nil}) + m.Choices[len(m.Choices)-1].Selected = canonicalQuotaSessionTier(current.Tier) == "default" + seen := map[string]bool{"default": true} + for _, tier := range model.ServiceTiers { + if !commandSafe(tier.ID) || !commandSafe(tier.Name) || seen[tier.ID] { + continue + } + seen[tier.ID] = true + setting(tier.Name, tier.Description, map[string]any{"serviceTier": tier.ID}) + m.Choices[len(m.Choices)-1].Selected = canonicalQuotaSessionTier(current.Tier) == canonicalQuotaTier(tier.ID) + } + return m, nil } } case "permissions": @@ -415,6 +431,17 @@ func (p *daemonStatusProvider) ExecuteSessionCommand(ctx context.Context, id, pa } } } + if m.Picker { + current, err := p.readQuotaSession(ctx, id) + if err != nil { + return err + } + current.Tier = nil + if tier, ok := o.params["serviceTier"].(string); ok { + current.Tier = &tier + } + return p.writeQuotaSettings(ctx, current, o.params) + } return p.requestOn(ctx, conn, o.method, o.params, nil) } } diff --git a/internal/codex/session_commands_unix_test.go b/internal/codex/session_commands_unix_test.go index cc50d53..24c486d 100644 --- a/internal/codex/session_commands_unix_test.go +++ b/internal/codex/session_commands_unix_test.go @@ -8,6 +8,7 @@ import ( "net/url" "strings" "testing" + "time" ) func TestSessionCommandsModelAndTier(t *testing.T) { @@ -33,10 +34,10 @@ func TestSessionCommandsModelAndTier(t *testing.T) { } f.mu.Unlock() m, err = p.SessionCommands(ctx, "one", "fast") - if err != nil || len(m.Choices) != 2 { + if err != nil || len(m.Choices) != 3 || !m.Picker { t.Fatalf("tier alias: %+v %v", m, err) } - if err = p.ExecuteSessionCommand(ctx, "one", m.Path, m.Revision, m.Choices[0].ID); err != nil { + if err = p.ExecuteSessionCommand(ctx, "one", m.Path, m.Revision, m.Choices[1].ID); err != nil { t.Fatal(err) } f.mu.Lock() @@ -45,7 +46,7 @@ func TestSessionCommandsModelAndTier(t *testing.T) { } f.commandStatus = "active" f.mu.Unlock() - if err = p.ExecuteSessionCommand(ctx, "one", m.Path, m.Revision, m.Choices[1].ID); err == nil { + if err = p.ExecuteSessionCommand(ctx, "one", m.Path, m.Revision, m.Choices[0].ID); err == nil { t.Fatal("accepted action after turn started") } if _, err = p.SessionCommands(ctx, "closed", "model"); err == nil { @@ -216,3 +217,68 @@ func TestSessionRenameMaximumUnicodeInput(t *testing.T) { } } } + +func TestSessionSpeedPickerListsCurrentAndAppliesAdvertisedIDs(t *testing.T) { + p, f := newQuotaDaemon(t) + tier := "priority" + f.mu.Lock() + f.sessions["one"] = QuotaSession{ID: "one", Model: "small", Effort: "medium", Tier: &tier} + f.mu.Unlock() + ctx := context.Background() + menu, err := p.SessionCommands(ctx, "one", "fast") + if err != nil || !menu.Picker || len(menu.Choices) != 3 { + t.Fatalf("incomplete picker: %+v %v", menu, err) + } + for i, label := range []string{"Standard (default)", "Fast", "Slow"} { + if menu.Choices[i].Label != label || !menu.Choices[i].Action || menu.Choices[i].Selected != (i == 2) { + t.Fatalf("wrong label/action/current speed: %+v", menu.Choices) + } + } + if err := p.ExecuteSessionCommand(ctx, "one", menu.Path, menu.Revision, menu.Choices[1].ID); err != nil { + t.Fatal(err) + } + f.mu.Lock() + current := f.sessions["one"] + if len(f.writes) != 1 || f.writes[0]["serviceTier"] != "flex" || len(f.writes[0]) != 2 || current.Model != "small" || current.Effort != "medium" || f.sessions["two"].Tier != nil { + t.Errorf("speed change altered other settings/session or guessed the tier: %+v %+v", f.writes, current) + } + f.mu.Unlock() + menu, err = p.SessionCommands(ctx, "one", "fast") + if err != nil || !menu.Choices[1].Selected || menu.Choices[2].Selected { + t.Fatalf("applied speed not reflected on reopen: %+v %v", menu, err) + } + if err := p.ExecuteSessionCommand(ctx, "one", menu.Path, menu.Revision, menu.Choices[0].ID); err != nil { + t.Fatal(err) + } + f.mu.Lock() + if len(f.writes) != 2 || f.writes[1]["serviceTier"] != nil || f.sessions["one"].Tier != nil { + t.Error("default did not clear the override") + } + f.mu.Unlock() + menu, err = p.SessionCommands(ctx, "one", "fast") + if err != nil || !menu.Choices[0].Selected { + t.Fatal("default speed not marked current", err) + } +} + +func TestSessionSpeedPickerDoesNotTreatQueuedWriteAsApplied(t *testing.T) { + p, f := newQuotaDaemon(t) + f.mu.Lock() + f.sessions["one"] = QuotaSession{ID: "one", Model: "small", Effort: "medium"} + f.queued = true + f.mu.Unlock() + ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond) + defer cancel() + menu, err := p.SessionCommands(ctx, "one", "fast") + if err != nil { + t.Fatal(err) + } + if err = p.ExecuteSessionCommand(ctx, "one", menu.Path, menu.Revision, menu.Choices[1].ID); err == nil { + t.Fatal("queued speed update reported as applied") + } + f.mu.Lock() + defer f.mu.Unlock() + if len(f.writes) != 1 || f.sessions["one"].Tier != nil { + t.Fatal("unconfirmed write was retried or changed running speed") + } +} diff --git a/internal/ui/model.go b/internal/ui/model.go index 3e3b160..0c31655 100644 --- a/internal/ui/model.go +++ b/internal/ui/model.go @@ -41,6 +41,7 @@ type BenchmarkTaskProvider interface { } type Model struct { + startup startupState // Only populated on a local copy during a read-only render/hit-test pass. geometry *monitorGeometryCache scheduleUI scheduleUI @@ -428,6 +429,7 @@ func New(fetcher Fetcher, refreshEvery time.Duration) Model { refreshEvery = time.Minute } model := Model{ + startup: startupState{pending: true}, resetThreshold: 80, resetWarningHours: 72, fetcher: fetcher, @@ -460,6 +462,9 @@ func New(fetcher Fetcher, refreshEvery time.Duration) Model { // buffer instead of Bubble Tea's alternate screen. func (m *Model) SetInline(inline bool) { m.inline = inline + if inline { + m.startup = startupState{} + } } type initialViewMsg struct{} @@ -469,6 +474,11 @@ func (m Model) Init() tea.Cmd { } func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) { + if next, cmd, handled := m.updateStartup(message); handled { + return next, cmd + } else { + m = next + } if next, cmd, handled := m.updateMonitorHistory(message); handled { return next, cmd } else { @@ -1044,6 +1054,7 @@ func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) { m.width = message.Width m.height = message.Height m.prepareBenchmarkDetailTranscript() + return m, m.beginStartup(time.Now()) case accountHistoryMsg: if message.sequence != m.history.sequence { return m, nil diff --git a/internal/ui/monitor_commands.go b/internal/ui/monitor_commands.go index 261926a..29b782e 100644 --- a/internal/ui/monitor_commands.go +++ b/internal/ui/monitor_commands.go @@ -21,6 +21,7 @@ type monitorCommandsState struct { menu codex.SessionCommandMenu selected, scroll, hover int notice string + reviewedChoice string until time.Time footerHover string input textinput.Model @@ -36,6 +37,8 @@ type monitorCommandsResult struct { func (m *Model) loadMonitorCommands(path string) tea.Cmd { p := &m.monitorCommands + p.reviewedChoice = "" + p.until = time.Time{} path = strings.TrimPrefix(strings.TrimSpace(path), "/") if strings.TrimPrefix(strings.TrimSpace(path), "/") == "statusline" { p.busy = false @@ -116,6 +119,14 @@ func (m Model) chooseMonitorCommand() (Model, tea.Cmd, bool) { cmd := m.loadMonitorCommands(o.Next) return m, cmd, true } + if m.monitorCommands.menu.Picker { + p := &m.monitorCommands + if o.Action && p.notice == "" { + p.reviewedChoice = o.ID + p.until = time.Now().Add(30 * time.Second) + } + return m, nil, true + } if m.monitorCommands.menu.Multiple { p := &m.monitorCommands p.menu.Choices = append([]codex.SessionCommandChoice(nil), p.menu.Choices...) @@ -140,10 +151,15 @@ func (m Model) confirmMonitorCommand() (Model, tea.Cmd, bool) { return m, nil, true } o, ok := m.commandChoice() - if !p.detail || !ok || !o.Action || p.busy || p.notice != "" { + reviewed := p.detail || (p.menu.Picker && p.reviewedChoice == o.ID) + if !reviewed || !ok || !o.Action || p.busy || p.notice != "" { return m, nil, true } if time.Now().After(p.until) { + if p.menu.Picker { + p.reviewedChoice = "" + return m, nil, true + } p.notice = "Confirmation expired; reopen the option." return m, nil, true } @@ -214,6 +230,7 @@ func (m Model) monitorCommandRows(width, height int) []commandDisplayRow { } else if strings.HasPrefix(p.menu.Path, "cd/") { text += "\nOnly this session’s directory changes; project configuration is not reloaded. Confirm after reviewing both paths." } else { + text += "\nNo change sent yet. C confirms the selected setting. Model, reasoning and speed settings can change token usage and cost." text += "\nChanges apply to subsequent turns of this session. They are not global defaults. Confirm only after reviewing the option. Automatic quota thresholds may later supersede model settings." } } @@ -228,6 +245,9 @@ func (m Model) monitorCommandRows(width, height int) []commandDisplayRow { for i := start; i < min(start+capacity, len(p.menu.Choices)); i++ { o := p.menu.Choices[i] label := o.Label + if p.menu.Picker && o.Selected { + label += " // CURRENT" + } if p.menu.Multiple { mark := "[ ] " if o.Selected { @@ -244,6 +264,28 @@ func (m Model) monitorCommandRows(width, height int) []commandDisplayRow { if len(rows) == 0 { rows = append(rows, commandDisplayRow{text: "No available options. Use Codex for unsupported commands.", choice: -1}) } + if p.menu.Picker { + if o, ok := m.commandChoice(); ok && p.reviewedChoice == o.ID { + message := "REVIEW // " + o.Label + " — no change yet; C confirms." + if time.Now().After(p.until) { + message = "Review expired. Press Enter to review again." + } + if len(rows) < capacity { + rows = append(rows, commandDisplayRow{text: ansi.Truncate(message, w, "…"), choice: -1}) + } + } + } + if p.menu.Picker && len(rows) < capacity { + if o, ok := m.commandChoice(); ok { + rows = append(rows, commandDisplayRow{choice: -1}) + for _, line := range strings.Split(ansi.Hardwrap(o.Help, w, true), "\n") { + if len(rows) >= capacity { + break + } + rows = append(rows, commandDisplayRow{text: line, choice: -1}) + } + } + } if p.menu.Multiple { preview := statusline.Text(statusLineSelection(p.menu), m.monitorStatusValues()) if preview == "" { @@ -261,8 +303,12 @@ func (m Model) commandFooter(width int) []commandDisplayRow { buttons = append(buttons, commandDisplayRow{text: "[ ENTER REVIEW ]", action: "review"}) } else if p.menu.Multiple { buttons = append(buttons, commandDisplayRow{text: "[ C APPLY ]", action: "confirm"}) - } else if o, ok := m.commandChoice(); ok && p.detail && o.Action && !p.busy && p.notice == "" { - buttons = append(buttons, commandDisplayRow{text: "[ C CONFIRM ]", action: "confirm"}) + } else if o, ok := m.commandChoice(); ok && o.Action && !p.busy && p.notice == "" { + if p.detail || (p.menu.Picker && p.reviewedChoice == o.ID && time.Now().Before(p.until)) { + buttons = append(buttons, commandDisplayRow{text: "[ C CONFIRM ]", action: "confirm"}) + } else { + buttons = append(buttons, commandDisplayRow{text: "[ ENTER REVIEW ]", action: "stage"}) + } } buttons = append(buttons, commandDisplayRow{text: "[ ← BACK ]", action: "back"}, commandDisplayRow{text: "[ ESC CLOSE ]", action: "close"}) used := 0 @@ -284,10 +330,20 @@ func (m Model) renderMonitorCommands(width, height int, colors palette) string { break } style := colors.label() - if r.action != "" && (r.choice == p.selected || r.choice == p.hover) { - style = style.Foreground(colors.primary).Bold(true) + text := r.text + if r.action != "" { + style = colors.dimmed() + if r.choice == p.selected { + style = style.Foreground(colors.background).Background(colors.primary).Bold(true) + text = "> " + text + } else { + text = " " + text + if r.choice == p.hover { + style = style.Underline(true) + } + } } - body[i] = style.Render(ansi.Truncate(r.text, max(width-4, 1), "")) + body[i] = style.Render(ansi.Truncate(text, max(width-4, 1), "")) } var footer []string for _, b := range m.commandFooter(width) { @@ -301,6 +357,10 @@ func (m Model) renderMonitorCommands(width, height int, colors palette) string { hint := "↑/↓ select or scroll • Enter open • / root" if p.menu.Input { hint = "Enter review • Escape cancel" + } else if p.menu.Picker { + hint = "↑/↓ select • Enter review • C confirm • Esc cancel" + } else if p.detail { + hint = "↑/↓ scroll • C confirm • ← back • Escape cancel" } else if p.menu.Multiple { hint = "↑/↓ select • Space toggle • ←/→ order • C apply" } @@ -332,6 +392,7 @@ func (m Model) updateMonitorCommands(msg tea.Msg) (Model, tea.Cmd, bool) { p.notice = "Command unavailable or unconfirmed. Check Codex before retrying." } } else if r.applied { + speed := p.menu.Picker // Applying settings invalidates both the option revision and // the root suggestion catalogue. Reopen only from a fresh read. m.monitorCommands = monitorCommandsState{request: p.request + 1} @@ -346,6 +407,9 @@ func (m Model) updateMonitorCommands(msg tea.Msg) (Model, tea.Cmd, bool) { cmd := m.focusMonitorPrompt() m.monitorPrompt.session = r.session m.monitorPrompt.notice = "Change requested. Codex will apply it to subsequent turns." + if speed { + m.monitorPrompt.notice = "Session speed changed. Applies to subsequent turns." + } if strings.HasPrefix(r.path, "rename/") { m.monitorPrompt.notice = "Session renamed." } else if strings.HasPrefix(r.path, "cd/") { @@ -354,6 +418,14 @@ func (m Model) updateMonitorCommands(msg tea.Msg) (Model, tea.Cmd, bool) { return m, cmd, true } else { p.menu = r.menu + if p.menu.Picker { + for i, o := range p.menu.Choices { + if o.Selected { + p.selected = i + break + } + } + } if p.menu.Input { p.input = textinput.New() p.input.CharLimit = p.menu.InputLimit @@ -463,6 +535,9 @@ func (m Model) updateMonitorCommands(msg tea.Msg) (Model, tea.Cmd, bool) { cmd := m.loadMonitorCommands("") return m, cmd, true case "enter", "right": + if p.menu.Picker && key.String() == "right" { + return m, nil, true + } if !p.detail { return m.chooseMonitorCommand() } @@ -479,7 +554,11 @@ func (m Model) updateMonitorCommands(msg tea.Msg) (Model, tea.Cmd, bool) { if p.detail { p.scroll = max(0, p.scroll+d) } else { + before := p.selected p.selected = min(max(0, p.selected+d), max(len(p.menu.Choices)-1, 0)) + if before != p.selected { + p.reviewedChoice = "" + } } } return m, nil, true @@ -499,7 +578,11 @@ func (m Model) updateMonitorCommands(msg tea.Msg) (Model, tea.Cmd, bool) { if p.detail { p.scroll = max(0, p.scroll+d) } else { + before := p.selected p.selected = min(max(0, p.selected+d), max(len(p.menu.Choices)-1, 0)) + if before != p.selected { + p.reviewedChoice = "" + } } return m, nil, true } @@ -510,6 +593,13 @@ func (m Model) updateMonitorCommands(msg tea.Msg) (Model, tea.Cmd, bool) { if y < len(rows) && rows[y].action == "choose" { p.hover = rows[y].choice if click && v.Button == tea.MouseLeft { + if p.menu.Picker { + if p.selected != p.hover { + p.reviewedChoice = "" + } + p.selected = p.hover + return m, nil, true + } p.selected = p.hover return m.chooseMonitorCommand() } @@ -523,6 +613,8 @@ func (m Model) updateMonitorCommands(msg tea.Msg) (Model, tea.Cmd, bool) { switch b.action { case "review": return m.reviewMonitorCommandInput() + case "stage": + return m.chooseMonitorCommand() case "confirm": return m.confirmMonitorCommand() case "back": diff --git a/internal/ui/monitor_commands_test.go b/internal/ui/monitor_commands_test.go index 0aef908..79defff 100644 --- a/internal/ui/monitor_commands_test.go +++ b/internal/ui/monitor_commands_test.go @@ -3,13 +3,16 @@ package ui import ( "context" "errors" + "image" "net/url" + "reflect" "strconv" "strings" "testing" "time" tea "charm.land/bubbletea/v2" + uv "github.com/charmbracelet/ultraviolet" "github.com/charmbracelet/x/ansi" "github.com/merefield/codexometer/internal/codex" ) @@ -374,3 +377,210 @@ func TestMonitorDirectoryEditorReviewAndConfirmation(t *testing.T) { t.Fatal("directory change lost draft or notice") } } + +type speedCommandTestClient struct { + *commandTestClient + choice string +} + +func (f *speedCommandTestClient) SessionCommands(context.Context, string, string) (codex.SessionCommandMenu, error) { + return codex.SessionCommandMenu{Title: "/fast", Path: "tier/flex", Revision: "speed-revision", Picker: true, Choices: []codex.SessionCommandChoice{ + {ID: "default", Label: "Standard (default)", Help: "Server default speed", Action: true}, + {ID: "fast", Label: "Fast", Help: "Advertised faster speed", Action: true}, + {ID: "slow", Label: "Slow", Help: "Advertised slower speed", Action: true, Selected: true}, + }}, nil +} +func (f *speedCommandTestClient) ExecuteSessionCommand(_ context.Context, _, path, revision, choice string) error { + if path != "tier/flex" || revision != "speed-revision" { + return codex.ErrSessionCommand + } + f.calls++ + f.choice = choice + return nil +} +func speedCommandTestModel(t *testing.T) (Model, *speedCommandTestClient) { + t.Helper() + m, base := commandTestModel(t) + f := &speedCommandTestClient{commandTestClient: base} + m.fetcher = f + m, cmd, _ := m.openMonitorCommands("fast") + next, _ := m.Update(cmd()) + return next.(Model), f +} + +func TestMonitorSpeedPickerEnterReviewThenCConfirm(t *testing.T) { + m, f := speedCommandTestModel(t) + m.monitorPrompt.input.SetValue("/fast") + view := ansi.Strip(m.render()) + for _, text := range []string{"Standard (default)", "Fast", "> Slow // CURRENT", "[ ENTER REVIEW ]", "Enter review", "C confirm"} { + if !strings.Contains(view, text) { + t.Fatalf("missing %q in %s", text, view) + } + } + if m.monitorCommands.selected != 2 || m.monitorCommands.detail { + t.Fatal("current speed not highlighted in the list") + } + next, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyUp}) + m = next.(Model) + if cmd != nil || f.calls != 0 || m.monitorCommands.selected != 1 || m.monitorCommands.detail || !strings.Contains(ansi.Strip(m.render()), "> Fast") { + t.Fatal("Up from bottom did not highlight the preceding speed") + } + next, cmd = m.Update(key('c')) + m = next.(Model) + if cmd != nil || f.calls != 0 { + t.Fatal("C applied an unreviewed speed") + } + for _, code := range []rune{tea.KeyEnter, tea.KeyEnter, tea.KeyRight} { + next, cmd = m.Update(tea.KeyPressMsg{Code: code}) + m = next.(Model) + if cmd != nil || f.calls != 0 || m.monitorCommands.detail { + t.Fatal("Enter/Right left the list or applied without C") + } + } + if m.monitorCommands.reviewedChoice != "fast" || !strings.Contains(ansi.Strip(m.render()), "[ C CONFIRM ]") || !strings.Contains(ansi.Strip(m.render()), "Standard (default)") { + t.Fatal("Enter did not provisionally review the speed while retaining all choices") + } + next, cmd = m.Update(key('c')) + m = next.(Model) + if cmd == nil || !m.monitorCommands.busy { + t.Fatal("C Confirm did not send the reviewed speed") + } + _, duplicate := m.Update(key('c')) + if duplicate != nil { + t.Fatal("busy picker allowed duplicate C Apply") + } + next, _ = m.Update(cmd()) + m = next.(Model) + if f.calls != 1 || f.choice != "fast" || m.monitorCommands.open || m.monitorPrompt.input.Value() != "" || !strings.Contains(m.monitorPrompt.notice, "Session speed changed.") { + t.Fatal("wrong applied speed, draft or success state") + } +} + +func TestMonitorSpeedPickerFooterReviewsThenConfirms(t *testing.T) { + for _, width := range []int{60, 120} { + m, f := speedCommandTestModel(t) + m.width = width + x, y := renderedTextStart(t, m, "Standard (default)") + next, cmd := m.Update(tea.MouseClickMsg{X: x, Y: y, Button: tea.MouseLeft}) + m = next.(Model) + if cmd != nil || f.calls != 0 || m.monitorCommands.selected != 0 || m.monitorCommands.detail { + t.Fatal("click applied speed or hid the list") + } + x, y = renderedTextStart(t, m, "[ ENTER REVIEW ]") + next, cmd = m.Update(tea.MouseClickMsg{X: x, Y: y, Button: tea.MouseLeft}) + m = next.(Model) + if cmd != nil || f.calls != 0 || m.monitorCommands.reviewedChoice != "default" { + t.Fatal("footer review applied or failed to stage the choice") + } + x, y = renderedTextStart(t, m, "[ C CONFIRM ]") + next, cmd = m.Update(tea.MouseClickMsg{X: x, Y: y, Button: tea.MouseLeft}) + m = next.(Model) + if cmd == nil { + t.Fatal("footer Apply is not clickable") + } + cmd() + if f.calls != 1 || f.choice != "default" { + t.Fatal("footer applied the wrong highlighted speed") + } + } +} + +func TestMonitorSlashListsHaveOneVisibleSelection(t *testing.T) { + m, _ := commandTestModel(t) + m.monitorCommands.menu.Choices = append(m.monitorCommands.menu.Choices, codex.SessionCommandChoice{ID: "high", Label: "High", Action: true}) + if !strings.Contains(ansi.Strip(m.render()), "> Medium") { + t.Fatal("selected option has no visible highlight marker") + } + next, _ := m.Update(tea.KeyPressMsg{Code: tea.KeyDown}) + m = next.(Model) + view := ansi.Strip(m.render()) + if !strings.Contains(view, "> High") || strings.Contains(view, "> Medium") { + t.Fatal("selection highlight did not follow Down") + } +} + +func TestMonitorSlashSelectionUsesHighlightColors(t *testing.T) { + m, _ := commandTestModel(t) + m.monitorCommands.menu.Choices = append(m.monitorCommands.menu.Choices, codex.SessionCommandChoice{ID: "high", Label: "High", Action: true}) + for _, selected := range []int{0, 1} { + m.monitorCommands.selected = selected + view := m.render() + cells := uv.NewScreenBuffer(m.width, m.height) + uv.NewStyledString(view).Draw(&cells, image.Rect(0, 0, m.width, m.height)) + expected := uv.NewScreenBuffer(2, 1) + colors := paletteFor(m.theme) + uv.NewStyledString(colors.label().Foreground(colors.background).Background(colors.primary).Render("x")+colors.dimmed().Render("x")).Draw(&expected, image.Rect(0, 0, 2, 1)) + for index, label := range []string{"Medium", "High"} { + x, y := renderedTextStart(t, m, label) + got := cells.CellAt(x, y).Style + want := expected.CellAt(1, 0).Style + if index == selected { + want = expected.CellAt(0, 0).Style + } + if !reflect.DeepEqual(got.Fg, want.Fg) || (index == selected && !reflect.DeepEqual(got.Bg, want.Bg)) { + t.Fatalf("%s did not use the appropriate selected/subdued colours", label) + } + } + } +} + +func TestMonitorSpeedReviewIsBoundToChoiceAndExpires(t *testing.T) { + m, f := speedCommandTestModel(t) + next, _ := m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + m = next.(Model) + if m.monitorCommands.reviewedChoice != "slow" { + t.Fatal("Enter did not stage current choice") + } + next, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyUp}) + m = next.(Model) + if m.monitorCommands.reviewedChoice != "" { + t.Fatal("changing highlight retained old review") + } + _, cmd := m.Update(key('c')) + if cmd != nil || f.calls != 0 { + t.Fatal("different choice applied without review") + } + next, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + m = next.(Model) + m.monitorCommands.until = time.Now().Add(-time.Second) + next, cmd = m.Update(key('c')) + m = next.(Model) + if cmd != nil || f.calls != 0 || m.monitorCommands.reviewedChoice != "" { + t.Fatal("expired review applied") + } + next, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + m = next.(Model) + _, cmd = m.Update(key('c')) + if cmd == nil { + t.Fatal("fresh review could not confirm") + } + cmd() + if f.calls != 1 || f.choice != "fast" { + t.Fatal("confirmed wrong choice") + } +} + +func TestMonitorModelReasoningEnterReviewBeforeC(t *testing.T) { + m, f := commandTestModel(t) + _, cmd := m.Update(key('c')) + if cmd != nil || f.calls != 0 { + t.Fatal("unreviewed model/reasoning applied") + } + next, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + m = next.(Model) + if cmd != nil || f.calls != 0 || !m.monitorCommands.detail || !strings.Contains(ansi.Strip(m.render()), "[ C CONFIRM ]") { + t.Fatal("Enter did not provisionally review reasoning") + } + _, cmd = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + if cmd != nil || f.calls != 0 { + t.Fatal("second Enter confirmed") + } + _, cmd = m.Update(key('c')) + if cmd == nil { + t.Fatal("C did not confirm reviewed model/reasoning") + } + cmd() + if f.calls != 1 { + t.Fatal("incorrect mutation count") + } +} diff --git a/internal/ui/quota_api_eq.go b/internal/ui/quota_api_eq.go index e95024d..2464bea 100644 --- a/internal/ui/quota_api_eq.go +++ b/internal/ui/quota_api_eq.go @@ -27,6 +27,9 @@ func quotaAPIAccountingEqual(left, right codex.LiveUsageSnapshot) bool { left.APIEqUnknownTierCalls == right.APIEqUnknownTierCalls && left.APIEqPricedCalls == right.APIEqPricedCalls && left.APIEqUnpricedCalls == right.APIEqUnpricedCalls && + left.APIEqMissingUsageCalls == right.APIEqMissingUsageCalls && + left.APIEqInconsistentUsageCalls == right.APIEqInconsistentUsageCalls && + left.APIEqAccountingAdjustments == right.APIEqAccountingAdjustments && left.APIEqPendingCalls == right.APIEqPendingCalls } @@ -46,21 +49,26 @@ type quotaAPISample struct { } type quotaAPIAnchor struct { - usedPercent int - resetAt int64 - costUSD float64 - premiumUSD float64 - unknownTierCalls int64 - pricedCalls int64 - unpricedCalls int64 - restartReason string + usedPercent int + resetAt int64 + costUSD float64 + premiumUSD float64 + unknownTierCalls int64 + pricedCalls int64 + unpricedCalls int64 + missingUsageCalls int64 + inconsistentUsageCalls int64 + accountingAdjustments int64 + restartReason string } const ( quotaAPIRestartAccountChanged = "ACCOUNT CHANGED" quotaAPIRestartAccountingRebased = "LOCAL ACCOUNTING REBASED" quotaAPIRestartCoverageGap = "LOCAL COVERAGE GAP" - quotaAPIRestartUnpricedModel = "UNPRICED MODEL MIX" + quotaAPIRestartUnpricedUsage = "UNPRICED USAGE" + quotaAPIRestartMissingUsage = "MISSING RESPONSE USAGE" + quotaAPIRestartInconsistentUsage = "INCONSISTENT RESPONSE USAGE" quotaAPIRestartWindowChanged = "WINDOW DEFINITION CHANGED" quotaAPIRestartWindowReset = "WINDOW RESET" ) @@ -113,6 +121,9 @@ func (m *Model) observeQuotaAPIEq(snapshot codex.Snapshot, usage codex.LiveUsage resetAt: optionalUnix(meter.Window.ResetsAt), costUSD: usage.APIEqUSD + usage.APIEqTierPremiumUSD, premiumUSD: usage.APIEqTierPremiumUSD, unknownTierCalls: usage.APIEqUnknownTierCalls, pricedCalls: usage.APIEqPricedCalls, unpricedCalls: usage.APIEqUnpricedCalls, + missingUsageCalls: usage.APIEqMissingUsageCalls, + inconsistentUsageCalls: usage.APIEqInconsistentUsageCalls, + accountingAdjustments: usage.APIEqAccountingAdjustments, } anchor, exists := m.quotaAPIAnchors[key] if !exists { @@ -134,7 +145,10 @@ func (m *Model) observeQuotaAPIEq(snapshot codex.Snapshot, usage codex.LiveUsage } if current.costUSD < anchor.costUSD || current.pricedCalls < anchor.pricedCalls || current.unpricedCalls < anchor.unpricedCalls || current.premiumUSD < anchor.premiumUSD || - current.unknownTierCalls < anchor.unknownTierCalls { + current.unknownTierCalls < anchor.unknownTierCalls || + current.missingUsageCalls < anchor.missingUsageCalls || + current.inconsistentUsageCalls < anchor.inconsistentUsageCalls || + current.accountingAdjustments < anchor.accountingAdjustments { current.restartReason = quotaAPIRestartAccountingRebased m.quotaAPIAnchors[key] = current m.quotaAPIIssues[key] = "" @@ -149,7 +163,20 @@ func (m *Model) observeQuotaAPIEq(snapshot codex.Snapshot, usage codex.LiveUsage m.quotaAPIAnchors[key] = anchor } if current.unpricedCalls > anchor.unpricedCalls { - current.restartReason = quotaAPIRestartUnpricedModel + current.restartReason = quotaAPIRestartUnpricedUsage + m.quotaAPIAnchors[key] = current + m.quotaAPIIssues[key] = "" + continue + } + switch { + case current.missingUsageCalls > anchor.missingUsageCalls: + current.restartReason = quotaAPIRestartMissingUsage + case current.inconsistentUsageCalls > anchor.inconsistentUsageCalls: + current.restartReason = quotaAPIRestartInconsistentUsage + case current.accountingAdjustments > anchor.accountingAdjustments: + current.restartReason = quotaAPIRestartAccountingRebased + } + if current.restartReason != "" { m.quotaAPIAnchors[key] = current m.quotaAPIIssues[key] = "" continue @@ -451,8 +478,12 @@ func shortQuotaAPIRestartReason(reason string) string { return "REBASED" case quotaAPIRestartCoverageGap: return "COVERAGE GAP" - case quotaAPIRestartUnpricedModel: + case quotaAPIRestartUnpricedUsage: return "UNPRICED" + case quotaAPIRestartMissingUsage: + return "USAGE MISSING" + case quotaAPIRestartInconsistentUsage: + return "USAGE INCONSISTENT" case quotaAPIRestartWindowChanged: return "WINDOW CHANGED" case quotaAPIRestartWindowReset: diff --git a/internal/ui/quota_api_eq_test.go b/internal/ui/quota_api_eq_test.go index 5fd44fc..883f559 100644 --- a/internal/ui/quota_api_eq_test.go +++ b/internal/ui/quota_api_eq_test.go @@ -17,7 +17,9 @@ func TestShortQuotaAPIRestartReasonsStayCanonical(t *testing.T) { quotaAPIRestartAccountChanged: "ACCOUNT", quotaAPIRestartAccountingRebased: "REBASED", quotaAPIRestartCoverageGap: "COVERAGE GAP", - quotaAPIRestartUnpricedModel: "UNPRICED", + quotaAPIRestartUnpricedUsage: "UNPRICED", + quotaAPIRestartMissingUsage: "USAGE MISSING", + quotaAPIRestartInconsistentUsage: "USAGE INCONSISTENT", quotaAPIRestartWindowChanged: "WINDOW CHANGED", quotaAPIRestartWindowReset: "RESET", "FUTURE REASON": "FUTURE REASON", @@ -72,7 +74,7 @@ func TestQuotaAPIEstimatorRequiresCleanPricedMovement(t *testing.T) { t.Fatalf("unpriced interval produced evidence: %#v", model.quotaAPIEvidence) } line := model.quotaAPILine(apiEqSnapshot(25, reset).Meters()[0], 80) - if !strings.Contains(line, "UNPRICED MODEL MIX") { + if !strings.Contains(line, "UNPRICED USAGE") { t.Fatalf("unpriced learning state = %q", line) } @@ -363,3 +365,71 @@ func apiEqSnapshot(used int, reset int64) codex.Snapshot { Primary: &codex.Window{UsedPercent: used, WindowDurationMins: &duration, ResetsAt: &reset}, }} } + +func TestQuotaUsageIssuesRestartWithSpecificReasonsAndRecover(t *testing.T) { + for _, kind := range []string{"missing", "inconsistent", "adjusted", "unpriced"} { + t.Run(kind, func(t *testing.T) { + now := time.Now() + reset := now.Add(4 * time.Hour).Unix() + model := New(nil, time.Minute) + base := apiEqSnapshot(10, reset) + plan := "pro_500" + base.RateLimits.PlanType = &plan + model.observeQuotaAPIEq(base, codex.LiveUsageSnapshot{}, now) + issue := codex.LiveUsageSnapshot{APIEqUSD: 1, APIEqPricedCalls: 1} + reason := "" + switch kind { + case "missing": + issue.APIEqMissingUsageCalls = 1 + reason = quotaAPIRestartMissingUsage + case "inconsistent": + issue.APIEqInconsistentUsageCalls = 1 + reason = quotaAPIRestartInconsistentUsage + case "adjusted": + issue.APIEqAccountingAdjustments = 1 + reason = quotaAPIRestartAccountingRebased + case "unpriced": + issue.APIEqUnpricedCalls = 1 + reason = quotaAPIRestartUnpricedUsage + } + observe := func(percent int, usage codex.LiveUsageSnapshot) { + snap := apiEqSnapshot(percent, reset) + snap.RateLimits.PlanType = &plan + model.snapshot = snap + model.observeQuotaAPIEq(snap, usage, now.Add(time.Duration(percent)*time.Minute)) + } + observe(15, issue) + if len(model.quotaAPIEvidence) != 0 || !strings.Contains(model.quotaAPILine(model.snapshot.Meters()[0], 120), reason) { + t.Fatal("unsafe learning or incorrect cause", model.quotaAPIEvidence, model.quotaAPIAnchors) + } + clean := issue + clean.APIEqUSD = 2 + clean.APIEqPricedCalls = 2 + observe(17, clean) + if !strings.Contains(model.quotaAPILine(model.snapshot.Meters()[0], 120), "2/5PP") { + t.Fatal("unchanged error counter kept restarting") + } + clean.APIEqUSD = 3 + clean.APIEqPricedCalls = 3 + observe(20, clean) + if len(model.quotaAPIEvidence) != 1 { + t.Fatal("clean learning did not resume", model.quotaAPIAnchors) + } + }) + } +} + +func TestQuotaAccountingBracketIncludesUsageIssuesAndAdjustments(t *testing.T) { + before := codex.LiveUsageSnapshot{APIEqUSD: 1, APIEqPricedCalls: 1} + for _, mutate := range []func(*codex.LiveUsageSnapshot){ + func(s *codex.LiveUsageSnapshot) { s.APIEqMissingUsageCalls++ }, + func(s *codex.LiveUsageSnapshot) { s.APIEqInconsistentUsageCalls++ }, + func(s *codex.LiveUsageSnapshot) { s.APIEqAccountingAdjustments++ }, + } { + after := before + mutate(&after) + if quotaAPIAccountingEqual(before, after) { + t.Fatal("changed usage accounting was accepted") + } + } +} diff --git a/internal/ui/startup.go b/internal/ui/startup.go new file mode 100644 index 0000000..18cd9a5 --- /dev/null +++ b/internal/ui/startup.go @@ -0,0 +1,322 @@ +package ui + +import ( + "math" + "math/rand/v2" + "strings" + "time" + + tea "charm.land/bubbletea/v2" +) + +const ( + startupFrameInterval = time.Second / 30 + startupEntrance = 900 * time.Millisecond + startupHold = 180 * time.Millisecond + startupDock = 800 * time.Millisecond + startupDuration = startupEntrance + startupHold + startupDock + startupBlinkHalf = 180 * time.Millisecond + startupBlinks = 3 + startupTypingStep = 90 * time.Millisecond + startupShuffleStep = 110 * time.Millisecond + startupShuffleFrame = 70 * time.Millisecond + startupTitle = "CODEXOMETER" + startupAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +) + +type startupAnimation int + +const ( + startupSlide startupAnimation = iota + startupTyping + startupShuffle + startupAnimationCount +) + +type startupState struct { + pending bool + started time.Time + elapsed time.Duration + animation startupAnimation + seed uint64 + order [len(startupTitle)]int +} + +func (s startupState) active() bool { return !s.started.IsZero() } + +type startupFrameMsg struct{ started, at time.Time } + +func startupTick(started time.Time) tea.Cmd { + return tea.Tick(startupFrameInterval, func(now time.Time) tea.Msg { + return startupFrameMsg{started: started, at: now} + }) +} + +func (m *Model) beginStartup(now time.Time) tea.Cmd { + // Resize recomputes geometry, but never restarts or adds a second tick chain. + if m.inline || m.width < 68 || m.height < 8 { + if m.width > 0 && m.height > 0 { + m.startup = startupState{} + } + return nil + } + if !m.startup.pending { + return nil + } + m.startup = newStartupState(now, startupAnimation(rand.IntN(int(startupAnimationCount))), rand.Uint64()) + return startupTick(now) +} + +func (m Model) updateStartup(message tea.Msg) (Model, tea.Cmd, bool) { + if tick, ok := message.(startupFrameMsg); ok { + if !m.startup.active() || tick.started != m.startup.started { + return m, nil, true + } + // Wall-clock progress catches up after slow frames instead of delaying startup. + m.startup.elapsed = max(m.startup.elapsed, tick.at.Sub(m.startup.started)) + if m.startup.elapsed >= m.startup.duration() { + m.startup = startupState{} + return m, nil, true + } + return m, startupTick(m.startup.started), true + } + if !m.startup.active() { + return m, nil, false + } + switch message := message.(type) { + case tea.KeyPressMsg: + m.startup = startupState{} + if message.String() == "ctrl+c" || strings.EqualFold(message.String(), "q") { + m.cancelBenchmark() + return m, tea.Quit, true + } + // Consume the skip key so it cannot activate an unseen dashboard action. + return m, nil, true + case tea.PasteMsg: + m.startup = startupState{} + return m, nil, true + case tea.MouseMsg: + if click, ok := message.(tea.MouseClickMsg); ok && click.Mouse().Button == tea.MouseLeft { + m.startup = startupState{} + } + return m, nil, true + } + // Fetches, telemetry, timers and initial view loading keep their normal handlers. + return m, nil, false +} + +func newStartupState(now time.Time, animation startupAnimation, seed uint64) startupState { + s := startupState{started: now, animation: animation, seed: seed} + random := rand.New(rand.NewPCG(seed, ^seed)) + copy(s.order[:], random.Perm(len(startupTitle))) + return s +} + +func (s startupState) entranceDuration() time.Duration { + switch s.animation { + case startupTyping: + return startupBlinkHalf*2*startupBlinks + startupTypingStep*time.Duration(len(startupTitle)) + case startupShuffle: + return startupShuffleStep * time.Duration(len(startupTitle)) + default: + return startupEntrance + } +} + +func (s startupState) duration() time.Duration { + return s.entranceDuration() + startupHold + startupDock +} + +// Keep random content stable within a frame and across redraws/resizes. Only +// elapsed time advances the effect; rendering never consumes global randomness. +func (s startupState) lettering() (text string, cursor int, dot bool) { + elapsed := max(s.elapsed, 0) + switch s.animation { + case startupTyping: + blinkDuration := startupBlinkHalf * 2 * startupBlinks + typed := 0 + if elapsed < blinkDuration { + dot = (elapsed/startupBlinkHalf)%2 == 0 + } else { + typed = min(int((elapsed-blinkDuration)/startupTypingStep)+1, len(startupTitle)) + dot = typed < len(startupTitle) + } + return startupTitle[:typed] + strings.Repeat(" ", len(startupTitle)-typed), typed, dot + case startupShuffle: + locked := min(int(elapsed/startupShuffleStep), len(startupTitle)) + characters := []byte(startupTitle) + random := rand.New(rand.NewPCG(s.seed, uint64(elapsed/startupShuffleFrame))) + for index := range characters { + character := byte(0) + // Unresolved slots stay visibly unresolved, even when a random draw happens + // to equal their target. Locked positions never change again. + for character == 0 || character == startupTitle[index] { + character = startupAlphabet[random.IntN(len(startupAlphabet))] + } + characters[index] = character + } + for _, position := range s.order[:locked] { + characters[position] = startupTitle[position] + } + return string(characters), -1, false + default: + return startupTitle, -1, false + } +} + +// The header font uses three columns per letter except its five-column M. +// Reserve these same slots for every variant so typing and random glyphs cannot +// change the wordmark's scale, spacing or final geometry. +type startupLetterSlot struct{ start, width int } + +var startupLetterSlots = func() (slots []startupLetterSlot) { + column := 0 + for _, letter := range startupTitle { + width := 3 + if letter == 'M' { + width = 5 + } + slots = append(slots, startupLetterSlot{column, width}) + column += width + 1 + } + return slots +}() + +var startupRandomGlyphs = map[byte][4]string{ + 'A': {"010", "101", "111", "101"}, 'B': {"110", "111", "101", "110"}, + 'C': {"111", "100", "100", "111"}, 'D': {"110", "101", "101", "110"}, + 'E': {"111", "100", "110", "111"}, 'F': {"111", "100", "110", "100"}, + 'G': {"111", "100", "101", "111"}, 'H': {"101", "101", "111", "101"}, + 'I': {"111", "010", "010", "111"}, 'J': {"001", "001", "101", "111"}, + 'K': {"101", "110", "110", "101"}, 'L': {"100", "100", "100", "111"}, + 'M': {"101", "111", "101", "101"}, 'N': {"101", "111", "111", "101"}, + 'O': {"111", "101", "101", "111"}, 'P': {"111", "101", "111", "100"}, + 'Q': {"111", "101", "111", "001"}, 'R': {"111", "101", "110", "101"}, + 'S': {"111", "100", "001", "111"}, 'T': {"111", "010", "010", "010"}, + 'U': {"101", "101", "101", "111"}, 'V': {"101", "101", "101", "010"}, + 'W': {"101", "101", "111", "101"}, 'X': {"101", "010", "010", "101"}, + 'Y': {"101", "101", "010", "010"}, 'Z': {"111", "001", "100", "111"}, + '0': {"111", "101", "101", "111"}, '1': {"110", "010", "010", "111"}, + '2': {"111", "001", "110", "111"}, '3': {"111", "011", "001", "111"}, + '4': {"101", "101", "111", "001"}, '5': {"111", "110", "001", "111"}, + '6': {"100", "111", "101", "111"}, '7': {"111", "001", "010", "010"}, + '8': {"111", "111", "101", "111"}, '9': {"111", "101", "111", "001"}, +} + +func (s startupState) bitmap() [][]bool { + text, cursor, dot := s.lettering() + if text == startupTitle && !dot { + return startupLogoBitmap + } + pixels := make([][]bool, len(startupLogoBitmap)) + for row := range pixels { + pixels[row] = make([]bool, len(startupLogoBitmap[row])) + for position, slot := range startupLetterSlots { + for col := range slot.width { + if text[position] == startupTitle[position] { + pixels[row][slot.start+col] = startupLogoBitmap[row][slot.start+col] + } else if text[position] != ' ' { + glyph := startupRandomGlyphs[text[position]] + pixels[row][slot.start+col] = glyph[row][col*len(glyph[row])/slot.width] == '1' + } + } + } + } + if dot && cursor < len(startupLetterSlots) { + slot := startupLetterSlots[cursor] + pixels[len(pixels)-1][slot.start+slot.width/2] = true + } + return pixels +} + +// Decode the existing two-row half-block lettering into four pixel rows. The +// same bitmap scales up for the entrance and returns exactly to the header art. +func startupLogoPixels() [][]bool { + width := len([]rune(headerLogo[0])) + pixels := make([][]bool, len(headerLogo)*2) + for row, line := range headerLogo { + pixels[row*2], pixels[row*2+1] = make([]bool, width), make([]bool, width) + for col, cell := range []rune(line) { + pixels[row*2][col] = cell == '█' || cell == '▀' + pixels[row*2+1][col] = cell == '█' || cell == '▄' + } + } + return pixels +} + +var startupLogoBitmap = startupLogoPixels() + +type startupGeometry struct{ x, y, width, pixelHeight int } + +func (m Model) startupGeometry() startupGeometry { + width := max(m.width, 1) + baseWidth := len(startupLogoBitmap[0]) + pixels := min(max(int(math.Round(float64(len(startupLogoBitmap))*float64(width)/float64(baseWidth))), 4), max((m.height-2)*2, 4)) + g := startupGeometry{width: width, pixelHeight: pixels, y: max((m.height-(pixels+1)/2)/2, 0)} + elapsed := m.startup.elapsed + entrance := m.startup.entranceDuration() + if elapsed < entrance { + if m.startup.animation == startupSlide { + progress := float64(max(elapsed, 0)) / float64(entrance) + g.x = int(math.Round(float64(width) * math.Pow(1-progress, 3))) + } + } else if elapsed > entrance+startupHold { + progress := min(float64(elapsed-entrance-startupHold)/float64(startupDock), 1) + eased := progress * progress * (3 - 2*progress) + g.width = int(math.Round(float64(width) + (float64(baseWidth)-float64(width))*eased)) + g.pixelHeight = int(math.Round(float64(pixels) + (float64(len(startupLogoBitmap))-float64(pixels))*eased)) + g.x = int(math.Round(2 * eased)) + g.y = int(math.Round(float64(g.y) + (1-float64(g.y))*eased)) + } + return g +} + +func scaledStartupLogo(width, pixelHeight int) []string { + return scaledStartupBitmap(startupLogoBitmap, width, pixelHeight) +} + +func scaledStartupBitmap(bitmap [][]bool, width, pixelHeight int) []string { + width, pixelHeight = max(width, 1), max(pixelHeight, 1) + sourceHeight, sourceWidth := len(bitmap), len(bitmap[0]) + rows := make([]string, (pixelHeight+1)/2) + for row := range rows { + var line strings.Builder + for col := range width { + sourceX := col * sourceWidth / width + top := bitmap[(row*2)*sourceHeight/pixelHeight][sourceX] + bottom := row*2+1 < pixelHeight && bitmap[(row*2+1)*sourceHeight/pixelHeight][sourceX] + switch { + case top && bottom: + line.WriteRune('█') + case top: + line.WriteRune('▀') + case bottom: + line.WriteRune('▄') + default: + line.WriteByte(' ') + } + } + rows[row] = line.String() + } + return rows +} + +func (m Model) renderStartup() string { + g := m.startupGeometry() + logo := scaledStartupBitmap(m.startup.bitmap(), g.width, g.pixelHeight) + rows := make([]string, max(m.height, 1)) + colors := paletteFor(m.theme) + for row := range rows { + rows[row] = strings.Repeat(" ", max(m.width, 1)) + logoRow := row - g.y + if logoRow < 0 || logoRow >= len(logo) || g.x >= m.width { + continue + } + // Each block occupies one terminal cell. Clip before styling to prevent any + // incoming frame wrapping onto the next row or leaving stray background cells. + cells := []rune(logo[logoRow]) + visible := min(len(cells), m.width-g.x) + rows[row] = strings.Repeat(" ", g.x) + colors.header().Render(string(cells[:visible])) + strings.Repeat(" ", m.width-g.x-visible) + } + return strings.Join(rows, "\n") +} diff --git a/internal/ui/startup_test.go b/internal/ui/startup_test.go new file mode 100644 index 0000000..6fc1b51 --- /dev/null +++ b/internal/ui/startup_test.go @@ -0,0 +1,403 @@ +package ui + +import ( + "errors" + "reflect" + "strings" + "testing" + "time" + + tea "charm.land/bubbletea/v2" + "charm.land/lipgloss/v2" + "github.com/charmbracelet/x/ansi" + "github.com/merefield/codexometer/internal/codex" +) + +func startupTestModel(t *testing.T, width, height int) Model { + t.Helper() + m := New(stubFetcher{snapshot: codex.DemoSnapshot()}, time.Minute) + next, cmd := m.Update(tea.WindowSizeMsg{Width: width, Height: height}) + m = next.(Model) + if !m.startup.active() || cmd == nil { + t.Fatal("startup did not start after terminal size arrived") + } + m.startup = newStartupState(m.startup.started, startupSlide, 42) + return m +} + +func TestStartupLogoReturnsToOriginalHeader(t *testing.T) { + if got := scaledStartupLogo(len([]rune(headerLogo[0])), len(headerLogo)*2); !reflect.DeepEqual(got, headerLogo) { + t.Fatalf("scaled logo changed header lettering: %#v", got) + } + for _, size := range [][2]int{{68, 8}, {80, 24}, {120, 40}, {240, 60}, {320, 8}} { + m := startupTestModel(t, size[0], size[1]) + previousX := m.width + for elapsed := time.Duration(0); elapsed <= startupEntrance; elapsed += 30 * time.Millisecond { + m.startup.elapsed = elapsed + g := m.startupGeometry() + if g.x > previousX || g.width != m.width { + t.Fatal("entrance did not move left at full width", g) + } + previousX = g.x + } + m.startup.elapsed = startupEntrance + startupHold/2 + g := m.startupGeometry() + if g.x != 0 || g.width != m.width || g.y != (m.height-(g.pixelHeight+1)/2)/2 { + t.Fatal("logo did not settle across the full centred screen", g) + } + previousWidth, previousY := g.width, g.y + for elapsed := startupEntrance + startupHold; elapsed <= startupDuration; elapsed += 20 * time.Millisecond { + m.startup.elapsed = elapsed + g := m.startupGeometry() + if g.width > previousWidth || g.y > previousY { + t.Fatal("docking did not shrink toward the header", g) + } + previousWidth, previousY = g.width, g.y + lines := strings.Split(ansi.Strip(m.View().Content), "\n") + if len(lines) != m.height { + t.Fatalf("frame height=%d want %d", len(lines), m.height) + } + for _, line := range lines { + if lipgloss.Width(line) != m.width { + t.Fatal("animation frame wrapped or left stale cells") + } + } + } + m.startup.elapsed = startupDuration + lines := strings.Split(ansi.Strip(m.renderStartup()), "\n") + for row, logo := range headerLogo { + if got := lines[row+1]; got != " "+logo+strings.Repeat(" ", m.width-2-lipgloss.Width(logo)) { + t.Fatal("logo did not dock at normal header coordinates", got) + } + } + } +} + +func TestStartupLoadsDataWithoutShowingDashboardEarly(t *testing.T) { + m := New(stubFetcher{snapshot: codex.DemoSnapshot()}, time.Minute) + if m.View().Content != "" { + t.Fatal("dashboard flashed before the first terminal size") + } + commands, ok := m.Init()().(tea.BatchMsg) + if !ok || len(commands) < 1 { + t.Fatal("startup did not schedule initial data loading") + } + next, _ := m.Update(tea.WindowSizeMsg{Width: 120, Height: 40}) + m = next.(Model) + started := m.startup.started + next, _ = m.Update(commands[0]()) + m = next.(Model) + if m.loading || len(m.snapshot.Meters()) != 2 || !m.startup.active() { + t.Fatal("quota load was blocked by the animation") + } + next, cmd := m.Update(secondMsg(started.Add(time.Second))) + m = next.(Model) + if cmd == nil || m.phase != 1 { + t.Fatal("background timers stopped during animation") + } + m.startup.elapsed = m.startup.entranceDuration() + if strings.Contains(ansi.Strip(m.View().Content), "VERSION") { + t.Fatal("dashboard rendered before the logo docked") + } + next, cmd = m.Update(startupFrameMsg{started: started, at: started.Add(m.startup.duration())}) + m = next.(Model) + if m.startup.active() || m.startup.pending || cmd != nil || !strings.Contains(ansi.Strip(m.View().Content), "VERSION") { + t.Fatal("animation did not reveal the loaded dashboard") + } + next, cmd = m.Update(tea.WindowSizeMsg{Width: 140, Height: 50}) + m = next.(Model) + if m.startup.active() || cmd != nil { + t.Fatal("resize replayed the startup animation") + } + next, _ = m.Update(refreshMsg(time.Now())) + if next.(Model).startup.active() { + t.Fatal("refresh replayed the startup animation") + } +} + +func TestStartupRestoredViewAndErrorsStillUpdate(t *testing.T) { + m := NewWithPreferences(historyStub{}, time.Minute, &memoryPreferenceStore{preferences: Preferences{MainTab: "usage"}}) + next, _ := m.Update(tea.WindowSizeMsg{Width: 120, Height: 40}) + m = next.(Model) + next, cmd := m.Update(initialViewMsg{}) + m = next.(Model) + if cmd == nil || !m.history.loading || !m.startup.active() { + t.Fatal("saved view did not load during animation") + } + next, _ = m.Update(cmd()) + m = next.(Model) + if m.history.loading { + t.Fatal("saved view results were not applied during animation") + } + next, _ = m.Update(fetchedMsg{err: errors.New("offline")}) + m = next.(Model) + if m.err == nil || !m.startup.active() { + t.Fatal("fetch errors were not processed during animation") + } +} + +func TestStartupSkipDoesNotActivateHiddenControls(t *testing.T) { + for _, message := range []tea.Msg{key('t'), tea.PasteMsg{Content: "do not send"}, specialKey(tea.KeyEnter), specialKey(tea.KeyEscape), tea.MouseClickMsg{X: 3, Y: 1, Button: tea.MouseLeft}} { + m := startupTestModel(t, 120, 40) + m.meterView = viewMonitor + started := m.startup.started + next, cmd := m.Update(message) + m = next.(Model) + if m.startup.active() || cmd != nil || m.theme != themeHacker || m.meterView != viewMonitor { + t.Fatal("skip activated an unseen control") + } + next, cmd = m.Update(startupFrameMsg{started: started, at: started.Add(time.Second)}) + if next.(Model).startup.active() || cmd != nil { + t.Fatal("queued frame resurrected skipped animation") + } + } + for _, message := range []tea.Msg{key('q'), modifiedKey('c', tea.ModCtrl)} { + m := startupTestModel(t, 120, 40) + next, cmd := m.Update(message) + if next.(Model).startup.active() || cmd == nil { + t.Fatal("quit blocked by startup") + } + if _, ok := cmd().(tea.QuitMsg); !ok { + t.Fatal("startup quit did not stop program") + } + } + m := startupTestModel(t, 120, 40) + next, cmd := m.Update(tea.MouseMotionMsg{X: 3, Y: 1}) + if !next.(Model).startup.active() || cmd != nil { + t.Fatal("hover skipped startup or reached a hidden control") + } +} + +func TestStartupResizeAndDelayedFrames(t *testing.T) { + m := startupTestModel(t, 80, 24) + started := m.startup.started + next, cmd := m.Update(startupFrameMsg{started: started, at: started.Add(500 * time.Millisecond)}) + m = next.(Model) + if cmd == nil || m.startup.elapsed != 500*time.Millisecond { + t.Fatal("delayed frame did not use elapsed time") + } + next, cmd = m.Update(tea.WindowSizeMsg{Width: 160, Height: 40}) + m = next.(Model) + if cmd != nil || m.startup.started != started || m.startup.elapsed != 500*time.Millisecond || m.startupGeometry().width != 160 { + t.Fatal("resize restarted timing or failed to rescale") + } + next, cmd = m.Update(startupFrameMsg{started: started.Add(time.Second), at: started.Add(time.Second)}) + if cmd != nil || next.(Model).startup.elapsed != m.startup.elapsed { + t.Fatal("stale animation changed progress") + } + next, _ = m.Update(startupFrameMsg{started: started, at: started.Add(200 * time.Millisecond)}) + m = next.(Model) + if m.startup.elapsed != 500*time.Millisecond { + t.Fatal("out-of-order frame moved animation backwards") + } + next, cmd = m.Update(tea.WindowSizeMsg{Width: 50, Height: 10}) + if next.(Model).startup.active() || cmd != nil { + t.Fatal("resize to a compact terminal did not finish animation") + } +} + +func TestStartupCompactAndInlineFallback(t *testing.T) { + for _, size := range [][2]int{{40, 24}, {80, 7}, {1, 1}} { + m := New(nil, time.Minute) + next, cmd := m.Update(tea.WindowSizeMsg{Width: size[0], Height: size[1]}) + m = next.(Model) + if m.startup.active() || m.startup.pending || cmd != nil || m.render() == "" { + t.Fatal("compact startup did not show the normal UI") + } + } + m := New(nil, time.Minute) + next, cmd := m.Update(tea.WindowSizeMsg{}) + if !next.(Model).startup.pending || cmd != nil { + t.Fatal("animation started without terminal dimensions") + } + m.SetInline(true) + next, cmd = m.Update(tea.WindowSizeMsg{Width: 120, Height: 40}) + m = next.(Model) + if m.startup.active() || m.startup.pending || cmd != nil || m.View().AltScreen { + t.Fatal("inline mode used the fullscreen animation") + } +} + +func TestStartupTypingThreeBlinksAndMovingDot(t *testing.T) { + s := newStartupState(time.Now(), startupTyping, 42) + for blink := 0; blink < 3; blink++ { + for _, on := range []bool{true, false} { + s.elapsed = time.Duration(blink*2) * startupBlinkHalf + if !on { + s.elapsed += startupBlinkHalf + } + text, cursor, dot := s.lettering() + if text != strings.Repeat(" ", 11) || cursor != 0 || dot != on { + t.Fatal("expected three blank-word cursor blinks before typing", blink, on, text, cursor, dot) + } + count := 0 + for _, row := range s.bitmap() { + for _, pixel := range row { + if pixel { + count++ + } + } + } + if on && count != 1 || !on && count != 0 { + t.Fatal("blink drew something other than the dot") + } + } + } + for typed := 1; typed <= 11; typed++ { + s.elapsed = 6*startupBlinkHalf + time.Duration(typed-1)*startupTypingStep + text, cursor, dot := s.lettering() + if text != startupTitle[:typed]+strings.Repeat(" ", 11-typed) || cursor != typed || dot != (typed < 11) { + t.Fatal("typing did not reveal the next character and move its cursor", typed, text, cursor, dot) + } + pixels := s.bitmap() + for position := 0; position < typed; position++ { + slot := startupLetterSlots[position] + for row := range pixels { + for col := range slot.width { + if pixels[row][slot.start+col] != startupLogoBitmap[row][slot.start+col] { + t.Fatal("typing changed the original letter shape") + } + } + } + } + if dot { + slot := startupLetterSlots[cursor] + if !pixels[len(pixels)-1][slot.start+slot.width/2] { + t.Fatal("dot was not in the next letter's position") + } + } + } + if !reflect.DeepEqual(s.bitmap(), startupLogoBitmap) { + t.Fatal("typing did not finish with the exact original logo") + } +} + +func TestStartupShuffleLocksCharactersInRandomOrder(t *testing.T) { + s := newStartupState(time.Now(), startupShuffle, 42) + initial, _, _ := s.lettering() + if len(initial) != 11 { + t.Fatal("shuffle changed the character count") + } + for index, character := range initial { + if !strings.ContainsRune(startupAlphabet, character) || byte(character) == startupTitle[index] { + t.Fatal("initial shuffle did not begin with random unresolved characters") + } + } + s.elapsed = startupShuffleFrame + changed, _, _ := s.lettering() + if changed == initial { + t.Fatal("unresolved characters did not shuffle") + } + s.elapsed = 0 + same, _, _ := s.lettering() + if same != initial { + t.Fatal("a redraw changed the random frame") + } + previous := make(map[int]bool) + var sequence []int + for count := 1; count <= 11; count++ { + s.elapsed = time.Duration(count) * startupShuffleStep + text, cursor, dot := s.lettering() + if len(text) != 11 || cursor != -1 || dot { + t.Fatal("shuffle added a typing cursor or changed character count") + } + correct := 0 + for index := range text { + if previous[index] && text[index] != startupTitle[index] { + t.Fatal("resolved character became random again") + } + if text[index] == startupTitle[index] { + correct++ + if !previous[index] { + sequence = append(sequence, index) + } + previous[index] = true + } + } + if correct != count { + t.Fatalf("resolved %d characters, want %d", correct, count) + } + } + if reflect.DeepEqual(sequence, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { + t.Fatal("shuffle resolved from left to right instead of the seeded random order") + } + if !reflect.DeepEqual(s.bitmap(), startupLogoBitmap) { + t.Fatal("shuffle did not end with the original logo") + } + other := newStartupState(s.started, startupShuffle, 24) + if reflect.DeepEqual(s.order, other.order) { + t.Fatal("shuffle order did not vary with launch seed") + } + for _, character := range startupAlphabet { + glyph, exists := startupRandomGlyphs[byte(character)] + if !exists { + t.Fatalf("missing random glyph %c", character) + } + for _, row := range glyph { + if len(row) != 3 { + t.Fatal("random glyph did not fit the shared font") + } + } + } +} + +func TestStartupVariantsShareFullSizeAndDocking(t *testing.T) { + for _, size := range [][2]int{{68, 8}, {80, 24}, {120, 40}, {240, 60}, {320, 8}} { + reference := startupTestModel(t, size[0], size[1]) + for _, animation := range []startupAnimation{startupSlide, startupTyping, startupShuffle} { + m := reference + m.startup = newStartupState(m.startup.started, animation, 42) + for _, dockProgress := range []time.Duration{0, startupDock / 4, startupDock / 2, startupDock} { + m.startup.elapsed = m.startup.entranceDuration() + startupHold + dockProgress + reference.startup.elapsed = reference.startup.entranceDuration() + startupHold + dockProgress + if m.startupGeometry() != reference.startupGeometry() || m.renderStartup() != reference.renderStartup() { + t.Fatalf("variant %d changed full-size lettering or shared docking", animation) + } + } + for elapsed := time.Duration(0); elapsed < m.startup.duration(); elapsed += 70 * time.Millisecond { + m.startup.elapsed = elapsed + frame := strings.Split(ansi.Strip(m.View().Content), "\n") + if len(frame) != m.height { + t.Fatal("variant overflowed terminal height") + } + for _, line := range frame { + if lipgloss.Width(line) != m.width { + t.Fatal("variant overflowed terminal width") + } + } + } + next, cmd := m.Update(startupFrameMsg{started: m.startup.started, at: m.startup.started.Add(m.startup.duration())}) + if next.(Model).startup.active() || cmd != nil { + t.Fatal("variant did not finish at its own duration") + } + m.startup = newStartupState(reference.startup.started, animation, 42) + next, cmd = m.Update(key('t')) + if next.(Model).startup.active() || cmd != nil || next.(Model).theme != themeHacker { + t.Fatal("variant skip triggered a hidden control") + } + } + } +} + +func TestStartupRandomChoiceSurvivesRedrawAndResize(t *testing.T) { + m := New(nil, time.Minute) + next, cmd := m.Update(tea.WindowSizeMsg{Width: 120, Height: 40}) + m = next.(Model) + if cmd == nil || m.startup.animation < startupSlide || m.startup.animation >= startupAnimationCount { + t.Fatal("startup did not choose one of the three animations") + } + selected := m.startup + before := m.renderStartup() + if m.renderStartup() != before { + t.Fatal("redraw changed the chosen animation or its random characters") + } + next, cmd = m.Update(tea.WindowSizeMsg{Width: 160, Height: 50}) + m = next.(Model) + if cmd != nil || m.startup != selected { + t.Fatal("resize reselected the animation or random seed") + } + next, _ = m.Update(tea.WindowSizeMsg{Width: 120, Height: 40}) + if next.(Model).renderStartup() != before { + t.Fatal("resize changed the chosen animation's current frame") + } +} diff --git a/internal/ui/view.go b/internal/ui/view.go index 5644067..4406add 100644 --- a/internal/ui/view.go +++ b/internal/ui/view.go @@ -30,6 +30,12 @@ func (m Model) View() tea.View { } func (m Model) render() string { + if m.startup.active() { + return m.renderStartup() + } + if m.startup.pending && !m.inline && (m.width <= 0 || m.height <= 0) { + return "" + } m.geometry = &monitorGeometryCache{} colors := paletteFor(m.theme) layout := m.dashboardLayout() diff --git a/internal/web/control.go b/internal/web/control.go index edb052a..4540a62 100644 --- a/internal/web/control.go +++ b/internal/web/control.go @@ -86,6 +86,7 @@ type actionRequest struct { } type preparedAction struct { + speed bool request actionRequest id string until time.Time diff --git a/internal/web/control_commands.go b/internal/web/control_commands.go index a2e9590..14c0e46 100644 --- a/internal/web/control_commands.go +++ b/internal/web/control_commands.go @@ -52,6 +52,9 @@ func (c *control) handleCommands(w http.ResponseWriter, r *http.Request, body ac return } message := "Change requested. Codex will apply it to subsequent turns." + if p.speed { + message = "Session speed changed. Applies to subsequent turns." + } if strings.HasPrefix(q.Path, "rename/") { message = "Session renamed." } else if strings.HasPrefix(q.Path, "cd/") { @@ -75,7 +78,7 @@ func (c *control) handleCommands(w http.ResponseWriter, r *http.Request, body ac } for _, o := range m.Choices { if o.ID == q.Choice && o.Action { - p := &preparedAction{request: body, id: rand.Text(), until: time.Now().Add(30 * time.Second)} + p := &preparedAction{request: body, speed: m.Picker, id: rand.Text(), until: time.Now().Add(30 * time.Second)} c.mu.Lock() c.pending = p c.mu.Unlock() diff --git a/internal/web/control_commands_test.go b/internal/web/control_commands_test.go index 7b4caa6..062fc5c 100644 --- a/internal/web/control_commands_test.go +++ b/internal/web/control_commands_test.go @@ -17,12 +17,13 @@ type commandsFake struct { lists int listedPath, committedPath string revision string + picker bool } func (f *commandsFake) SessionCommands(_ context.Context, _ string, path string) (codex.SessionCommandMenu, error) { f.lists++ f.listedPath = path - return codex.SessionCommandMenu{Revision: f.revision, Choices: []codex.SessionCommandChoice{{ID: "choice", Label: "Medium", Help: "Dynamic help", Action: true}}}, nil + return codex.SessionCommandMenu{Revision: f.revision, Picker: f.picker, Choices: []codex.SessionCommandChoice{{ID: "choice", Label: "Medium", Help: "Dynamic help", Action: true}}}, nil } func (f *commandsFake) ExecuteSessionCommand(_ context.Context, id, path, revision, choice string) error { if id != "parent" || revision != f.revision || choice != "choice" { @@ -154,3 +155,25 @@ func TestCommandsRejectInvalidDecodedInputBeforeAdapter(t *testing.T) { } } } + +func TestSpeedPickerUsesBoundCommitAndReportsVerifiedSuccess(t *testing.T) { + s, _, token := controlServer(t) + f := &commandsFake{revision: "current", picker: true} + s.control.commands = f + b := actionRequest{Session: "parent", Command: &commandRequest{Mode: "prepare", Path: "fast", Revision: "current", Choice: "choice"}} + w := actionCall(s, token, "commands", b) + var prepared struct{ Confirmation string } + if w.Code != 200 || json.Unmarshal(w.Body.Bytes(), &prepared) != nil || prepared.Confirmation == "" || f.calls != 0 { + t.Fatal("prepare sent or lost the highlighted speed", w.Body) + } + b.Confirmation = prepared.Confirmation + b.Command = &commandRequest{Mode: "commit"} + w = actionCall(s, token, "commands", b) + var result struct{ Message string } + if w.Code != 200 || json.Unmarshal(w.Body.Bytes(), &result) != nil || result.Message != "Session speed changed. Applies to subsequent turns." || f.calls != 1 { + t.Fatal("speed commit missing or success still pending", w.Body) + } + if w = actionCall(s, token, "commands", b); w.Code != 409 || f.calls != 1 { + t.Fatal("speed token could be replayed") + } +} diff --git a/internal/web/dist/assets/index-BHlnMEGS.js b/internal/web/dist/assets/index-BHlnMEGS.js new file mode 100644 index 0000000..a8bb917 --- /dev/null +++ b/internal/web/dist/assets/index-BHlnMEGS.js @@ -0,0 +1,46 @@ +(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 e=Array.isArray,t=Array.prototype.indexOf,n=Array.prototype.includes,r=Array.from,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyDescriptors,s=Object.prototype,c=Array.prototype,l=Object.getPrototypeOf,u=Object.isExtensible;function d(e){return typeof e==`function`}var f=()=>{};function p(e){return e()}function m(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}function g(e,t,n=!1){return e===void 0?n?t():t:e}function _(e,t){if(Array.isArray(e))return e;if(t===void 0||!(Symbol.iterator in e))return Array.from(e);let n=[];for(let r of e)if(n.push(r),n.length===t)break;return n}var v=1024,y=2048,b=4096,x=8192,S=16384,C=32768,w=1<<25,ee=65536,T=1<<19,te=1<<20,E=1<<25,D=1<<21,O=1<<22,ne=1<<23,k=Symbol(`$state`),re=Symbol(`component`),ie=Symbol(`legacy props`),ae=Symbol(``),oe=Symbol(`attributes`),se=Symbol(`class`),ce=Symbol(`style`),le=Symbol(`text`),ue=Symbol(`form reset`),de=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},fe=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`),pe={},me=Symbol(`uninitialized`),he=`http://www.w3.org/1999/xhtml`;function ge(){console.warn(`https://svelte.dev/e/derived_inert`)}function _e(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function ve(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function ye(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var A=!1;function be(e){A=e}var j;function xe(e){if(e===null)throw _e(),pe;return j=e}function Se(){return xe(cn(j))}function M(e){if(A){if(cn(j)!==null)throw _e(),pe;j=e}}function Ce(e=1){if(A){for(var t=e,n=j;t--;)n=cn(n);j=n}}function we(e=!0){for(var t=0,n=j;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=cn(n);e&&n.remove(),n=i}}function Te(e){if(!e||e.nodeType!==8)throw _e(),pe;return e.data}function Ee(e){return e===this.v}function De(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Oe(e){return!De(e,this.v)}function ke(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ae(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function je(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Me(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function Ne(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Pe(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Fe(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function Ie(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Le(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function Re(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function ze(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Be(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var Ve=!1;function He(){Ve=!0}var Ue=null;function We(e){Ue=e}function Ge(e,t=!1,n){Ue={p:Ue,i:!1,c:null,e:null,s:e,x:null,r:U,l:Ve&&!t?{s:null,u:null,$:[]}:null}}function Ke(e){var t=Ue,n=t.e;if(n!==null){t.e=null;for(var r of n)xn(r)}return e!==void 0&&(t.x=e),t.i=!0,Ue=t.p,qe(e)}function qe(e={}){return i(e,re,{value:!0}),e}function Je(){return!Ve||Ue!==null&&Ue.l===null}var Ye=[];function Xe(){var e=Ye;Ye=[],m(e)}function Ze(e){if(Ye.length===0&&!Tt){var t=Ye;queueMicrotask(()=>{t===Ye&&Xe()})}Ye.push(e)}function Qe(){for(;Ye.length>0;)Xe()}var $e=~(y|b|v);function et(e,t){e.f=e.f&$e|t}function tt(e){e.f&512||e.deps===null?et(e,v):et(e,b)}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),et(e,v)}function rt(e){A&&sn(e)!==null&&ln(e)}var it=!1;function at(){it||(it=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[ue]?.()})},{capture:!0}))}function ot(e){var t=H,n=U;Gn(null),Kn(null);try{return e()}finally{Gn(t),Kn(n)}}function st(e,t,n,r=n){e.addEventListener(t,()=>ot(n));let i=e[ue];e[ue]=i?()=>{i(),r(!0)}:()=>r(!0),at()}function ct(e,t,n,r){let i=Je()?ft:ht;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=U,c=lt(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){mn(e,s)}ut()}}var d=dt();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>mt(e))).then(u).catch(e=>mn(e,s)).finally(d)}l?l.then(()=>{c(),f(),ut()}):f()}function lt(){var e=U,t=H,n=Ue,r=P;return function(i=!0){Kn(e),Gn(t),We(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function ut(e=!0){Kn(null),Gn(null),We(null),e&&P?.deactivate()}function dt(){var e=U,t=e.b,n=P,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function ft(e){var t=2|y;return U!==null&&(U.f|=T),{ctx:Ue,deps:null,effects:null,equals:Ee,f:t,fn:e,reactions:null,rv:0,v:me,wv:0,parent:U,ac:null}}var pt=Symbol(`obsolete`);function mt(e,t,n){let r=U;r===null&&Ae();var i=void 0,a=Ht(me),o=!H,s=new Set;return Tn(()=>{var t=U,n=h();i=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==de&&n.reject(e)}).finally(ut)}catch(e){n.reject(e),ut()}var c=P;if(o){if(t.f&32768)var l=dt();if(r.b?.is_rendered())c.async_deriveds.get(t)?.reject(pt);else for(let e of s.values())e.reject(pt);s.add(n),c.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),s.delete(n),t!==pt&&(c.activate(),t?(a.f|=ne,Kt(a,t)):(a.f&8388608&&(a.f^=ne),Kt(a,e)),c.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),yn(()=>{for(let e of s)e.reject(pt)}),new Promise(e=>{function t(n){function r(){n===i?e(a):t(i)}n.then(r,r)}t(i)})}function N(e){let t=ft(e);return Jn(t),t}function ht(e){let t=ft(e);return t.equals=Oe,t}function gt(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(de),t.ac=null}),t.fn!==null&&(t.teardown=f),lr(t,0),An(t))}function bt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&ur(t)}var xt=null,P=null,St=null,Ct=null,wt=null,Tt=!1,Et=!1,Dt=null,Ot=null,kt=0,At=1,jt=class e{id=At++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){xt===null?xt=this:(xt.#n=this,this.#t=xt),xt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)et(r,y),t(r);for(r of n.m)et(r,b),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=v}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),et(e,y),this.schedule(e);for(let e of this.#d)et(e,b),this.schedule(e);this.apply();for(var t=Dt=[],n=[],r=Ot=[];this.#c.length>0;){kt++>1e3&&(this.#S(),Nt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Rt(e),this.#h()||this.discard(),t}}if(P=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Dt=null,Ot=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)Lt(e,t);r.length>0&&P.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),St=this,Ft(n),Ft(t),St=null,this.#s?.resolve();var o=P;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(Bt.clear(),o.#_())}#v(e,t,n){e.f^=v;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=v:i&4?t.push(r):ir(r)&&(i&16&&this.#d.add(r),ur(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),et(i,y),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),P=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=h()).promise}static ensure(){if(P===null){let t=P=new e;!Et&&!Tt&&Ze(()=>{t.#e||t.flush()})}return P}apply(){Ct=null}schedule(e){if(wt=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?xt=e:t.#t=e,this.linked=!1}}};function Mt(e){var t=Tt;Tt=!0;try{var n;for(e&&(P!==null&&!P.is_fork&&P.flush(),n=e());;){if(Qe(),P===null)return n;P.flush()}}finally{Tt=t}}function Nt(){try{Fe()}catch(e){mn(e,wt)}}var Pt=null;function Ft(e){var t=e.length;if(t!==0){for(var n=0;n0)){Bt.clear();for(let e of Pt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Pt.has(n)&&(Pt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||ur(n)}}Pt.clear()}}Pt=null}}function It(e){P.schedule(e)}function Lt(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),et(e,v);for(var n=e.first;n!==null;)Lt(n,t),n=n.next}}function Rt(e){et(e,v);for(var t=e.first;t!==null;)Rt(t),t=t.next}var zt=new Set,Bt=new Map,Vt=!1;function Ht(e,t){return{f:0,v:e,reactions:null,equals:Ee,rv:0,wv:0}}function F(e,t){let n=Ht(e,t);return Jn(n),n}function Ut(e,t=!1,n=!0){let r=Ht(e);return t||(r.equals=Oe),Ve&&n&&Ue!==null&&Ue.l!==null&&(Ue.l.s??=[]).push(r),r}function I(e,t,n=!1){return H!==null&&(!Wn||H.f&131072)&&Je()&&H.f&4325394&&(qn===null||!qn.has(e))&&ze(),Kt(e,n?Zt(t):t,Ot)}var Wt=null,Gt=0;function Kt(e,t,n=null){if(!e.equals(t)){Hn?Bt.set(e,t):Bt.has(e)||Bt.set(e,e.v);var r=jt.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&&_t(t),Ct===null&&tt(t)}e.wv=rr(),Wt=null,Gt=0,Xt(e,y,n),Wt=null,Je()&&U!==null&&U.f&1024&&!(U.f&96)&&(Zn===null?Qn([e]):Zn.push(e)),!r.is_fork&&zt.size>0&&!Vt&&qt()}return t}function qt(){Vt=!1;for(let e of zt){e.f&1024&&et(e,b);let t;try{t=ir(e)}catch{t=!0}t&&ur(e)}zt.clear()}function Jt(e,t=1){var n=W(e),r=t===1?n++:n--;return I(e,n),r}function Yt(e){I(e,e.v+1)}function Xt(e,t,n){var r=e.reactions;if(r!==null){var i=Je(),a=r.length;if(Gt+=a,Gt>1e5&&Wt===null&&(Wt=new Set),Wt!==null){if(Wt.has(e))return;Wt.add(e)}for(var o=0;o{if(tr===d)return e();var t=H,n=tr;Gn(null),nr(d);var r=e();return Gn(t),nr(n),r};return i&&r.set(`length`,F(t.length,u)),new Proxy(t,{defineProperty(e,t,n){(!(`value`in n)||n.configurable===!1||n.enumerable===!1||n.writable===!1)&&Le();var i=r.get(t);return i===void 0?f(()=>{var e=F(n.value,u);return r.set(t,e),e}):I(i,n.value,!0),!0},deleteProperty(e,t){var n=r.get(t);if(n===void 0){if(t in e){let e=f(()=>F(me,u));r.set(t,e),Yt(o)}}else I(n,me),Yt(o);return!0},get(e,n,i){if(n===k)return t;var o=r.get(n),s=n in e;if(o===void 0&&(!s||a(e,n)?.writable)&&(o=f(()=>F(Zt(s?e[n]:me),u)),r.set(n,o)),o!==void 0){var c=W(o);return c===me?void 0:c}return Reflect.get(e,n,i)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var n=Reflect.getOwnPropertyDescriptor(e,t),i=r.get(t);if(i!==void 0){var a=W(i);if(a===me)return;if(n&&`value`in n)n.value=a;else return{enumerable:!0,configurable:!0,value:a,writable:!0}}return n},has(e,t){if(t===k)return!0;var n=r.get(t),i=n!==void 0&&n.v!==me||Reflect.has(e,t);return(n!==void 0||U!==null&&(!i||a(e,t)?.writable))&&(n===void 0&&(n=f(()=>F(i?Zt(e[t]):me,u)),r.set(t,n)),W(n)===me)?!1:i},set(e,t,n,s){var c=r.get(t),l=t in e;if(i&&t===`length`)for(var d=n;dF(me,u)),r.set(d+``,p)):I(p,me)}if(c===void 0)(!l||a(e,t)?.writable)&&(c=f(()=>F(void 0,u)),I(c,Zt(n)),r.set(t,c));else{l=c.v!==me;var m=f(()=>Zt(n));I(c,m)}var h=Reflect.getOwnPropertyDescriptor(e,t);if(h?.set&&h.set.call(s,n),!l){if(i&&typeof t==`string`){var g=r.get(`length`),_=Number(t);Number.isInteger(_)&&_>=g.v&&I(g,_+1)}Yt(o)}return!0},ownKeys(e){W(o);var t=Reflect.ownKeys(e).filter(e=>{var t=r.get(e);return t===void 0||t.v!==me});for(var[n,i]of r)i.v!==me&&!(n in e)&&t.push(n);return t},setPrototypeOf(){Re()}})}function Qt(e){try{if(typeof e==`object`&&e&&k in e)return e[k]}catch{}return e}function $t(e,t){return Object.is(Qt(e),Qt(t))}var en,tn,nn,rn;function an(){if(en===void 0){en=window,tn=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;nn=a(t,`firstChild`).get,rn=a(t,`nextSibling`).get,u(e)&&(e[se]=void 0,e[oe]=null,e[ce]=void 0,e.__e=void 0),u(n)&&(n[le]=void 0)}}function on(e=``){return document.createTextNode(e)}function sn(e){return nn.call(e)}function cn(e){return rn.call(e)}function L(e,t){if(!A)return sn(e);var n=sn(j);if(n===null)n=j.appendChild(on());else if(t&&n.nodeType!==3){var r=on();return n?.before(r),xe(r),r}return t&&fn(n),xe(n),n}function R(e,t=!1){if(!A){var n=sn(e);return n instanceof Comment&&n.data===``?cn(n):n}if(t){if(j?.nodeType!==3){var r=on();return j?.before(r),xe(r),r}fn(j)}return j}function z(e,t=!1){if(!A)return sn(e);var n=L(e,t);return M(e),n}function B(e,t=1,n=!1){let r=A?j:e;for(var i;t--;)i=r,r=cn(r);if(!A)return r;if(n){if(r?.nodeType!==3){var a=on();return r===null?i?.after(a):r.before(a),xe(a),a}fn(r)}return xe(r),r}function ln(e){e.textContent=``}function un(){return!1}function dn(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function fn(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function pn(e){var t=U;if(t===null)return H.f|=ne,e;if(!(t.f&32768)&&!(t.f&4))throw e;mn(e,t)}function mn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function hn(e){U===null&&(H===null&&Pe(e),Ne()),Hn&&Me(e)}function gn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function _n(e,t){var n=U;n!==null&&n.f&8192&&(e|=x);var r={ctx:Ue,deps:null,nodes:null,f:e|y|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};P?.register_created_effect(r);var i=r;if(e&4)Dt===null?jt.ensure().schedule(r):Dt.push(r);else if(t!==null){try{ur(r)}catch(e){throw Mn(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=ee))}if(i!==null&&(i.parent=n,n!==null&&gn(i,n),H!==null&&H.f&2&&!(e&64))){var a=H;(a.effects??=[]).push(i)}return r}function vn(){return H!==null&&!Wn}function yn(e){let t=_n(8,null);return et(t,v),t.teardown=e,t}function bn(e){hn(`$effect`);var t=U.f;if(!H&&t&32&&Ue!==null&&!Ue.i){var n=Ue;(n.e??=[]).push(e)}else return xn(e)}function xn(e){return _n(4|te,e)}function Sn(e){return hn(`$effect.pre`),_n(8|te,e)}function Cn(e){jt.ensure();let t=_n(64|T,e);return(e={})=>new Promise(n=>{e.outro?Fn(t,()=>{Mn(t),n(void 0)}):(Mn(t),n(void 0))})}function wn(e){return _n(4,e)}function Tn(e){return _n(O|T,e)}function En(e,t=0){return _n(8|t,e)}function V(e,t=[],n=[],r=[]){ct(r,t,n,t=>{_n(8,()=>{e(...t.map(W))})})}function Dn(e,t=0){return _n(16|t,e)}function On(e){return _n(32|T,e)}function kn(e){var t=e.teardown;if(t!==null){let n=Hn,r=H;Un(!0),Gn(null);try{t.call(null)}catch(t){mn(t,e.parent)}finally{Un(n),Gn(r)}}}function An(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&ot(()=>{e.abort(de)});var r=n.next;n.f&64?n.parent=null:Mn(n,t),n=r}}function jn(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||Mn(t),t=n}}function Mn(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(Nn(e.nodes.start,e.nodes.end),n=!0),e.f|=w,An(e,t&&!n),lr(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();kn(e),e.f^=w,e.f|=S;var i=e.parent;i!==null&&i.first!==null&&Pn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Nn(e,t){for(;e!==null;){var n=e===t?null:cn(e);e.remove(),e=n}}function Pn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Fn(e,t,n=!0){var r=[];e.f|=256,In(e,r,!0);var i=()=>{n&&Mn(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function In(e,t,n){if(!(e.f&8192)){e.f^=x;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);In(i,t,o?n:!1)}i=a}}}function Ln(e){e.f&=-257,Rn(e,!0)}function Rn(e,t){if(!(e.f&256)&&e.f&8192){e.f^=x,e.f&1024||(et(e,y),jt.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);Rn(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function zn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:cn(n);t.append(n),n=i}}var Bn=null,Vn=!1,Hn=!1;function Un(e){Hn=e}var H=null,Wn=!1;function Gn(e){H=e}var U=null;function Kn(e){U=e}var qn=null;function Jn(e){H!==null&&(H.f&2097152||H.f&2)&&(qn??=new Set).add(e)}var Yn=null,Xn=0,Zn=null;function Qn(e){Zn=e}var $n=1,er=0,tr=er;function nr(e){tr=e}function rr(){return++$n}function ir(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&Ct===null&&et(e,v)}return!1}function ar(e,t,n=!0){var r=e.reactions;if(r!==null&&!(qn!==null&&qn.has(e)))for(var i=0;i{e.ac.abort(de)}),e.ac=null);try{e.f|=D;var u=e.fn,d=u();e.f|=C;var f=sr(e);if(Je()&&Zn!==null&&!Wn&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=Xn+Yn.length,r=0;r{s.ac.abort(de),s.ac=null,et(s,y)}),yt(s),lr(s,0)}}function lr(e,t){var n=e.deps;if(n!==null)for(var r=t;rn?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?(i.__removed=!1,Ze(()=>{i.__removed||t.addEventListener(e,i,r)})):t.addEventListener(e,i,r),i}function Cr(e,t,n,r,i){var a={capture:r,passive:i},o=Sr(e,t,n,a);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&yn(()=>{o.__removed=!0,t.removeEventListener(e,o,a)})}function G(e,t,n){(t[yr]??={})[e]=n}function wr(e){for(var t=0;t{Er=!1,Tr=null}));var s=0,c=Tr===e&&e[yr];if(c){var l=a.indexOf(c);if(l!==-1&&(t===document||t===window)){e[yr]=t;return}var u=a.indexOf(t);if(u===-1)return;l<=u&&(s=l)}if(o=a[s]||e.target,o!==t){i(e,`currentTarget`,{configurable:!0,get(){return o||n}});var d=H,f=U;Gn(null),Kn(null);try{for(var p,m=[];o!==null&&o!==t;){try{var h=o[yr]?.[r];h!=null&&(!o.disabled||e.target===o)&&h.call(o,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;s++,o=s{throw e});throw p}}finally{e[yr]=t,delete e.currentTarget,Gn(d),Kn(f)}}}var Or=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function kr(e){return Or?.createHTML(e)??e}function Ar(e){var t=dn(`template`);return t.innerHTML=kr(e.replaceAll(``,``)),t.content}function jr(e,t){var n=U;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function K(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(A)return jr(j,null),j;i===void 0&&(i=Ar(a?e:``+e),n||(i=sn(i)));var t=r||tn?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=sn(t),s=t.lastChild;jr(o,s)}else jr(t,t);return t}}function Mr(e,t,n=`svg`){var r=!e.startsWith(``),i=!!(t&1),a=`<${n}>${r?e:``+e}`,o;return()=>{if(A)return jr(j,null),j;if(!o){var e=sn(Ar(a));if(i)for(o=document.createDocumentFragment();sn(e);)o.appendChild(sn(e));else o=sn(e)}var t=o.cloneNode(!0);if(i){var n=sn(t),r=t.lastChild;jr(n,r)}else jr(t,t);return t}}function Nr(e,t){return Mr(e,t,`svg`)}function Pr(e=``){if(!A){var t=on(e+``);return jr(t,t),t}var n=j;return n.nodeType===3?fn(n):(n.before(n=on()),xe(n)),jr(n,n),n}function Fr(){if(A)return jr(j,null),j;var e=document.createDocumentFragment(),t=document.createComment(``),n=on();return e.append(t,n),jr(t,n),e}function q(e,t){if(A){var n=U;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=j),Se();return}e!==null&&e.before(t)}function Ir(){if(A&&j&&j.nodeType===8&&j.textContent?.startsWith(`$`)){let e=j.textContent.substring(1);return Se(),e}return(window.__svelte??={}).uid??=1,`c${window.__svelte.uid++}`}function Lr(e){let t=0,n=Ht(0),r;return()=>{vn()&&(W(n),En(()=>(t===0&&(r=mr(()=>e(()=>Yt(n)))),t+=1,()=>{Ze(()=>{--t,t===0&&(r?.(),r=void 0,Yt(n))})})))}}var Rr=ee|T;function zr(e,t,n,r){new Br(e,t,n,r)}var Br=class{parent;is_pending=!1;transform_error;#e;#t=A?j:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Lr(()=>(this.#m=Ht(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=U;t.b=this,t.f|=128,n(e)},this.parent=U.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=Dn(()=>{if(A){let e=this.#t;Se();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Rr),A&&(this.#e=j)}#g(){try{this.#a=On(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Ze(r),t&&(this.#s=On(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){ye();return}t=!0,n&&Be(),this.#s!==null&&Fn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){mn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=On(()=>e(this.#e)),Ze(()=>{var e=this.#c=document.createDocumentFragment(),t=on(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return On(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){mn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(P);return}this.#u===0&&(this.#e.before(e),this.#c=null,Fn(this.#o,()=>{this.#o=null}),this.#x(P))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=On(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();zn(this.#a,e);let t=this.#n.pending;this.#o=On(()=>t(this.#e))}else this.#x(P)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=U,n=H,r=Ue;Kn(this.#i),Gn(this.#i),We(this.#i.ctx);try{return jt.ensure(),e()}finally{Kn(t),Gn(n),We(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Fn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Ze(()=>{this.#d=!1,this.#m&&Kt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),W(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;P?.is_fork?(this.#a&&P.skip_effect(this.#a),this.#o&&P.skip_effect(this.#o),this.#s&&P.skip_effect(this.#s),P.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(Mn(this.#a),null),this.#o&&=(Mn(this.#o),null),this.#s&&=(Mn(this.#s),null),A&&(xe(this.#t),Ce(),xe(we()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return On(()=>{var r=U;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return mn(e,this.#i.parent),null}}))};Ze(()=>{var t;try{t=this.transform_error(e)}catch(e){mn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>mn(e,this.#i&&this.#i.parent)):n(t)})}};function J(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[le]??=e.nodeValue)&&(e[le]=n,e.nodeValue=`${n}`)}function Vr(e,t){return Ur(e,t)}var Hr=new Map;function Ur(e,{target:t,anchor:n,props:i={},events:a,context:o,intro:s=!0,transformError:c}){an();var l=void 0,u=Cn(()=>{var s=n??t.appendChild(on());zr(s,{pending:()=>{}},t=>{Ge({});var n=Ue;if(o&&(n.c=o),a&&(i.$$events=a),A&&jr(t,null),l=e(t,i)||qe(),A&&(U.nodes.end=j,j===null||j.nodeType!==8||j.data!==`]`))throw _e(),pe;Ke()},c);var u=new Set,d=e=>{for(var n=0;n{for(var e of u)for(let n of[t,document]){var r=Hr.get(n),i=r.get(e);--i==0?(n.removeEventListener(e,Dr),r.delete(e),r.size===0&&Hr.delete(n)):r.set(e,i)}xr.delete(d),s!==n&&s.parentNode?.removeChild(s)}});return Wr.set(l,u),l}var Wr=new WeakMap,Gr=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)Ln(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(Ln(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(Mn(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();zn(r,t),t.append(on()),this.#n.set(e,{effect:r,fragment:t})}else Mn(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Fn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(Mn(n.effect),this.#n.delete(e))};ensure(e,t){var n=P,r=un();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=on();i.append(a),this.#n.set(e,{effect:On(()=>t(a)),fragment:i})}else this.#t.set(e,On(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else A&&(this.anchor=j),this.#a(n)}};function Y(e,t,n=!1){var r;A&&(r=j,Se());var i=new Gr(e),a=n?ee:0;function o(e,t){if(A){var n=Te(r);if(e!==parseInt(n.substring(1))){var a=we();xe(a),i.anchor=a,be(!1),i.ensure(e,t),be(!0);return}}i.ensure(e,t)}Dn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Kr=Symbol(`NaN`);function qr(e,t,n){A&&Se();var r=new Gr(e),i=!Je();Dn(()=>{var e=t();e!==e&&(e=Kr),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function Jr(e,t){return t}function Yr(e,t,n){for(var i=[],a=t.length,o,s=t.length,c=0;c{if(o){if(o.pending.delete(n),o.done.add(n),o.pending.size===0){var t=e.outrogroups;Xr(e,r(o.done)),t.delete(o),t.size===0&&(e.outrogroups=null)}}else--s},!1)}if(s===0){var l=i.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;ln(d),d.append(u),e.items.clear()}Xr(e,t,!l)}else o={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(o)}function Xr(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var t=i();return e(t)?t:t==null?[]:r(t)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,$r(v,p,c,n,a),d!==null&&(p.length===0?d.f&33554432?(d.f^=E,ti(d,null,c)):Ln(d):Fn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:Dn(()=>{p=W(f);var e=p.length;let t=!1;A&&Te(c)===`[!`!=(e===0)&&(c=we(),xe(c),be(!1),t=!0);for(var r=new Set,u=P,v=un(),y=0;ys(c)):(d=On(()=>s(Zr??=on())),d.f|=E)),e>r.size&&je(``,``,``),A&&e>0&&xe(we()),!h){if(m.set(u,r),v){for(let[e,t]of l)r.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}t&&be(!0),W(f)}),flags:n,items:l,pending:m,outrogroups:null,fallback:d};h=!1,A&&(c=j)}function Qr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function $r(e,t,n,i,a){var o=!!(i&8),s=t.length,c=e.items,l=Qr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(o)for(v=0;v0){var T=i&4&&s===0?n:null;if(o){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function ei(e,t,n,r,i,a,o,s){var c=o&1?o&16?Ht(n):Ut(n,!1,!1):null,l=o&2?Ht(i):null;return{v:c,i:l,e:On(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function ti(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=cn(r);if(a.before(r),r===i)return;r=o}}function ni(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function ri(e,t,n){var r;A&&(r=j,Se());var i=new Gr(e);Dn(()=>{var e=t()??null;if(A&&Te(r)===`[`!=(e!==null)){var a=we();xe(a),i.anchor=a,be(!1),i.ensure(e,e&&(t=>n(t,e))),be(!0);return}i.ensure(e,e&&(t=>n(t,e)))},ee)}var ii=[...` +\r\f\xA0\v`];function ai(e,t,n){var r=e==null?``:``+e;if(t&&(r=r?r+` `+t:t),n){for(var i of Object.keys(n))if(n[i])r=r?r+` `+i:i;else if(r.length)for(var a=i.length,o=0;(o=r.indexOf(i,o))>=0;){var s=o+a;(o===0||ii.includes(r[o-1]))&&(s===r.length||ii.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function oi(e,t=!1){var n=t?` !important;`:`;`,r=``;for(var i of Object.keys(e)){var a=e[i];a!=null&&a!==``&&(r+=` `+i+`: `+a+n)}return r}function si(e){return e[0]!==`-`||e[1]!==`-`?e.toLowerCase():e}function ci(e,t){if(t){var n=``,r,i;if(Array.isArray(t)?(r=t[0],i=t[1]):r=t,e){e=String(e).replaceAll(/\/\*.*?\*\//g,``).trim();var a=!1,o=0,s=!1,c=[];r&&c.push(...Object.keys(r).map(si)),i&&c.push(...Object.keys(i).map(si));var l=0,u=-1;let t=e.length;for(var d=0;d{t.every(vi)||(`__defaultValue`in e&&pi(e,!1),`__value`in e&&mi(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),yn(()=>{t.disconnect()})}function gi(e,t,n=t){var r=new WeakSet,i=!0;st(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),_i);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&_i(o)}n(a),e.__value=a,P!==null&&r.add(P)}),wn(()=>{var a=t();if(e===document.activeElement){var o=P;if(r.has(o))return}if(mi(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=_i(s),n(a))}e.__value=a,i=!1})}function _i(e){return`__value`in e?e.__value:e.value}function vi(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var yi=Symbol(`is custom element`),bi=Symbol(`is html`),xi=fe?`link`:`LINK`;function Si(e){if(A){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Z(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Z(e,`checked`,null),e.checked=r}}};e[ue]=n,Ze(n),at()}}function Ci(e,t){var n=wi(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Z(e,t,n,r){var i=wi(e);A&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===xi)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[ae]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&Ei(e).has(t)?e[t]=n:e.setAttribute(t,n))}function wi(e){return e[oe]??={[yi]:e.nodeName.includes(`-`),[bi]:e.namespaceURI===he}}var Ti=new Map;function Ei(e){var t=e.getAttribute(`is`)||e.nodeName,n=Ti.get(t);if(n)return n;Ti.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var s in r=o(i),r)r[s].set&&s!==`innerHTML`&&s!==`textContent`&&s!==`innerText`&&n.add(s);i=l(i)}return n}function Di(e,t,n=t){var r=new WeakSet;st(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=Mi(e)?Ni(a):a,n(a),P!==null&&r.add(P),await dr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(A&&e.defaultValue!==e.value||mr(t)==null&&e.value)&&(n(Mi(e)?Ni(e.value):e.value),P!==null&&r.add(P)),En(()=>{var n=t();if(e===document.activeElement){var i=P;if(r.has(i))return}Mi(e)&&n===Ni(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}var Oi=new Set;function ki(e,t,n,r,i=r){var a=n.getAttribute(`type`)===`checkbox`,o=e;let s=!1;if(t!==null)for(var c of t)o=o[c]??=[];o.push(n),st(n,`change`,()=>{var e=n.__value;a&&(e=ji(o,e,n.checked)),i(e)},()=>i(a?[]:null)),En(()=>{var e=r();if(A&&n.defaultChecked!==n.checked){s=!0;return}a?(e||=[],n.checked=e.includes(n.__value)):n.checked=$t(n.__value,e)}),yn(()=>{var e=o.indexOf(n);e!==-1&&o.splice(e,1)}),Oi.has(o)||(Oi.add(o),Ze(()=>{o.sort((e,t)=>e.compareDocumentPosition(t)===4?-1:1),Oi.delete(o)})),Ze(()=>{if(s){var e=a?ji(o,e,n.checked):o.find(e=>e.checked)?.__value;i(e)}})}function Ai(e,t,n=t){st(e,`change`,t=>{n(t?e.defaultChecked:e.checked)}),(A&&e.defaultChecked!==e.checked||mr(t)==null)&&n(e.checked),En(()=>{e.checked=!!t()})}function ji(e,t,n){for(var r=new Set,i=0;i{var n=this.#e.get(e);n.delete(t),n.size===0&&(this.#e.delete(e),this.#t.unobserve(e))}}#r(){return this.#t??=new ResizeObserver(t=>{for(var n of t){e.entries.set(n.target,n);for(var r of this.#e.get(n.target)||[])r(n)}})}}({box:`border-box`});function Fi(e,t,n){var r=Pi.observe(e,()=>n(e[t]));wn(()=>(mr(()=>n(e[t])),r))}function Ii(e,t){return e===t||e?.[k]===t}function Li(e=qe(),t,n,r){var i=Ue.r,a=U;return wn(()=>{var o,s;return En(()=>{o=s,s=r?.()||[],mr(()=>{Ii(n(...s),e)||(t(e,...s),o&&Ii(n(...o),e)&&t(null,...o))})}),()=>{let r=a;for(;r!==i&&r.parent!==null&&r.parent.f&33554432;)r=r.parent;let o=()=>{s&&Ii(n(...s),e)&&t(null,...s)},c=r.teardown;r.teardown=()=>{o(),c?.()}}}),e}function Ri(e,t,n,r,i){var a=()=>{r(n[e])};n.addEventListener(t,a),i?En(()=>{n[e]=i()}):a(),(n===document.body||n===window||n===document)&&yn(()=>{n.removeEventListener(t,a)})}function zi(e=!1){let t=Ue,n=t.l.u;if(!n)return;let r=()=>hr(t.s);if(e){let e=0,n={},i=ft(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>W(i)}n.b.length&&Sn(()=>{Bi(t,r),m(n.b)}),bn(()=>{let e=mr(()=>n.m.map(p));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&bn(()=>{Bi(t,r),m(n.a)})}function Bi(e,t){if(e.l.s)for(let t of e.l.s)W(t);t()}var Vi=!1;function Hi(e){var t=Vi;try{return Vi=!1,[e(),Vi]}finally{Vi=t}}var Ui={get(e,t){let n=e.props.length;for(;n--;){let r=e.props[n];if(d(r)&&(r=r()),typeof r==`object`&&r&&t in r)return r[t]}},set(e,t,n){let r=e.props.length;for(;r--;){let i=e.props[r];d(i)&&(i=i());let o=a(i,t);if(o&&o.set)return o.set(n),!0}return!1},getOwnPropertyDescriptor(e,t){let n=e.props.length;for(;n--;){let r=e.props[n];if(d(r)&&(r=r()),typeof r==`object`&&r&&t in r){let e=a(r,t);return e&&!e.configurable&&(e.configurable=!0),e}}},has(e,t){if(t===k||t===ie)return!1;for(let n of e.props)if(d(n)&&(n=n()),n!=null&&t in n)return!0;return!1},ownKeys(e){let t=[];for(let n of e.props)if(d(n)&&(n=n()),n){for(let e in n)t.includes(e)||t.push(e);for(let e of Object.getOwnPropertySymbols(n))t.includes(e)||t.push(e)}return t}};function Wi(...e){return new Proxy({props:e},Ui)}function Q(e,t,n,r){var i=!Ve||!!(n&2),o=!!(n&8),s=!!(n&16),c=r,l=!0,u=void 0,d=()=>s&&i?(u??=ft(r),W(u)):(l&&(l=!1,c=s?mr(r):r),c);let f;if(o){var p=k in e||ie in e;f=a(e,t)?.set??(p&&t in e?n=>e[t]=n:void 0)}var m,h=!1;o?[m,h]=Hi(()=>e[t]):m=e[t],m===void 0&&r!==void 0&&(m=d(),f&&(i&&Ie(t),f(m)));var g=i?()=>{var n=e[t];return n===void 0?d():(l=!0,n)}:()=>{var n=e[t];return n!==void 0&&(c=void 0),n===void 0?c:n};if(i&&!(n&4))return g;if(f){var _=e.$$legacy;return(function(e,t){return arguments.length>0?((!i||!t||_||h)&&f(t?g():e),e):g()})}var v=!1,y=(n&1?ft:ht)(()=>(v=!1,g()));o&&W(y);var b=U;return(function(e,t){if(arguments.length>0){let n=t?W(y):i&&o?Zt(e):e;return I(y,n),v=!0,c!==void 0&&(c=n),e}return Hn&&v||b.f&16384?y.v:W(y)})}function Gi(e){Ue===null&&ke(`onMount`),Ve&&Ue.l!==null?qi(Ue).m.push(e):bn(()=>{let t=mr(e);if(typeof t==`function`)return t})}function Ki(e){Ue===null&&ke(`onDestroy`),Gi(()=>()=>mr(e))}function qi(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);function Ji(e,t){if(e instanceof RegExp)return{keys:!1,pattern:e};var n,r,i,a,o=[],s=``,c=e.split(`/`);for(c[0]||c.shift();i=c.shift();)n=i[0],n===`*`?(o.push(`wild`),s+=`/(.*)`):n===`:`?(r=i.indexOf(`?`,1),a=i.indexOf(`.`,1),o.push(i.substring(1,~r?r:~a?a:i.length)),s+=~r&&!~a?`(?:/([^/]+?))?`:`/([^/]+?)`,~a&&(s+=(~r?`?`:``)+`\\`+i.substring(a))):s+=`/`+i;return{keys:o,pattern:RegExp(`^`+s+(t?`(?=$|/)`:`/?$`),`i`)}}var Yi=new class{#e=F(Xi());get _loc(){return W(this.#e)}set _loc(e){I(this.#e,e)}#t=N(()=>this._loc.location);get _location(){return W(this.#t)}set _location(e){I(this.#t,e)}#n=N(()=>this._loc.querystring);get _querystring(){return W(this.#n)}set _querystring(e){I(this.#n,e)}#r=F(void 0);get _params(){return W(this.#r)}set _params(e){I(this.#r,e)}get loc(){return this._loc}get location(){return this._location}get querystring(){return this._querystring}get params(){return this._params}constructor(){typeof window<`u`?window.addEventListener(`hashchange`,()=>{this._loc=Xi()}):console.warn(`[svelte-spa-router] window 'window' is not defined, skipping initiation`)}};function Xi(){let e=typeof window<`u`?window.location.href:``,t=e.indexOf(`#/`),n=t>-1?e.substr(t+1):`/`,r=n.indexOf(`?`),i=``;return r>-1&&(i=n.substr(r+1),n=n.substr(0,r)),{location:n,querystring:i}}async function Zi(e){if(!e||e.length<1||e.charAt(0)!=`/`&&e.indexOf(`#/`)!==0)throw Error(`Invalid parameter location`);await dr();let t=(e.charAt(0)==`#`?``:`#`)+e;try{let e={...history.state};delete e.__svelte_spa_router_scrollX,delete e.__svelte_spa_router_scrollY,window.history.replaceState(e,``,t)}catch{console.warn("Caught exception while replacing the current page. If you're running this in the Svelte REPL, please note that the `replace` method might not work in this environment.")}window.dispatchEvent(new Event(`hashchange`))}function Qi(e){e?window.scrollTo(e.__svelte_spa_router_scrollX||0,e.__svelte_spa_router_scrollY||0):window.scrollTo(0,0)}function $i(e,t){Ge(t,!0);let n=Q(t,`routes`,19,()=>({})),r=Q(t,`prefix`,3,``),i=Q(t,`restoreScrollState`,3,!1),a=Q(t,`onConditionsFailed`,3,()=>{}),o=Q(t,`onRouteLoaded`,3,()=>{}),s=Q(t,`onRouteLoading`,3,()=>{}),c=Q(t,`onRouteEvent`,3,()=>{});class l{path;component;conditions;userData;props;_pattern;_keys;constructor(e,t){let n=e=>typeof e==`object`&&!!e&&e._sveltesparouter===!0;if(!t||typeof t!=`function`&&!n(t))throw Error(`Invalid component object`);if(!e||typeof e==`string`&&(e.length<1||e.charAt(0)!=`/`&&e.charAt(0)!=`*`)||typeof e==`object`&&!(e instanceof RegExp))throw Error(`Invalid value for "path" argument - strings must start with / or *`);let r=Ji(e);if(this.path=e,n(t)){let e=t;this.component=e.component,this.conditions=e.conditions||[],this.userData=e.userData,this.props=e.props||{}}else{let e=t;this.component=()=>Promise.resolve(e),this.conditions=[],this.props={}}this._pattern=r.pattern,this._keys=r.keys}match(e){if(r()){if(typeof r()==`string`){if(e.startsWith(r()))e=e.substr(r().length)||`/`;else return null}else if(r()instanceof RegExp){let t=e.match(r());if(t&&t[0])e=e.substr(t[0].length)||`/`;else return null}}let t=this._pattern.exec(e);if(t===null)return null;if(this._keys===!1)return t;let n={},i=0;for(;i{u.push(new l(t,e))}):Object.keys(n()).forEach(e=>{let t=n();u.push(new l(e,t[e]))});let d=F(null),f=F(null),p=F({}),m=null,h=null;bn(()=>{history.scrollRestoration=i()?`manual`:`auto`}),bn(()=>{if(!i())return;let e=e=>{m=e.state&&(e.state.__svelte_spa_router_scrollY||e.state.__svelte_spa_router_scrollX)?e.state:null};return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)});async function g(e,t){await dr(),e(t)}bn(()=>{let e=Yi.loc,t=!1;return mr(async()=>{let n=0;for(;n{t=!0}});function _(e){return e&&typeof e==`object`&&Object.keys(e).length?e:null}var v=Fr(),y=R(v),b=e=>{let t=N(()=>W(d));var n=Fr(),r=R(n),i=e=>{var n=Fr();ri(R(n),()=>W(t),(e,t)=>{t(e,Wi({get params(){return W(f)},get onRouteEvent(){return c()}},()=>W(p)))}),q(e,n)},a=e=>{var n=Fr();ri(R(n),()=>W(t),(e,t)=>{t(e,Wi({get onRouteEvent(){return c()}},()=>W(p)))}),q(e,n)};Y(r,e=>{W(f)?e(i):e(a,-1)}),q(e,n)};Y(y,e=>{W(d)&&e(b)}),q(e,v),Ke()}var ea=[`bars`,`pace`,`pie`,`fuel`,`resets`,`thresholds`];function ta(e){return e===`zone`||e===`consumption-pace`?`pace`:typeof e==`string`&&ea.includes(e)?e:`bars`}var na=`codexometer.web.preferences.v1`,ra={statusLine:null,tab:`quota`,view:`bars`,selected:``,defaultDetail:0,layouts:[]};function ia(){try{let e=JSON.parse(localStorage.getItem(na)||`null`);return!e||typeof e!=`object`?ra:{statusLine:Array.isArray(e.statusLine)?[...new Set(e.statusLine.filter(e=>typeof e==`string`&&e.length<64))].slice(0,32):null,tab:[`quota`,`sessions`,`usage`].includes(e.tab)?e.tab:`quota`,view:ta(e.view),selected:typeof e.selected==`string`?e.selected.slice(0,256):``,defaultDetail:+(e.defaultDetail===1),layouts:Array.isArray(e.layouts)?e.layouts.filter(e=>{if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.id==`string`&&t.id.length<=256&&[0,1,2].includes(t.level)}).slice(-100):[]}}catch{return ra}}var aa=Zt(ia()),oa=Zt({open:!1});function sa(){let e=JSON.stringify(aa);try{localStorage.setItem(na,e)}catch{}}function ca(){return aa.tab===`quota`?`/quota/`+aa.view:`/`+aa.tab}function la(e){return aa.layouts.find(t=>t.id===e)?.level??aa.defaultDetail}function ua(e){aa.defaultDetail=e,aa.layouts=[]}function da(e,t){aa.layouts=[...aa.layouts.filter(t=>t.id!==e),{id:e,level:Math.max(0,Math.min(2,t))}].slice(-100)}var $=Zt({data:null,connected:!1,error:``}),fa=e=>e==null?`Unavailable`:e.toLocaleString(`en-GB`),pa=e=>!e||String(e).startsWith(`0001-`)?`Not yet available`:new Date(typeof e==`number`?e*1e3:e).toLocaleString(`en-GB`),ma=`codexometer.web.session`,ha=class extends Error{},ga;async function _a(e,t,n){if(!ga||!$.connected||!$.data?.control||$.data.sessionsError)throw Error(`Session controls unavailable. Check the live connection.`);return await ga(e,t,n)}function va(){let e=new AbortController,t,n=``;ga=async(t,r,i)=>{let a=await fetch(`/api/control/`+t,{method:`POST`,headers:{Authorization:`Bearer ${n}`,"Content-Type":`application/json`},body:JSON.stringify(r),signal:AbortSignal.any([e.signal,AbortSignal.timeout(8e3),...i?[i]:[]]),cache:`no-store`});if(!a.ok)throw a.status===502?Error(`Outcome uncertain. Check Codex before taking another action; nothing was retried.`):new ha(`Action rejected, expired or changed. Refresh and check Codex; nothing was retried.`);return a.json()};try{n=sessionStorage.getItem(ma)||``}catch{}let r=location.hash,i=r.startsWith(`#pair=`)?r.slice(6):``;(i||!r||r===`#`)&&(history.replaceState(null,``,`/#`+ca()),window.dispatchEvent(new HashChangeEvent(`hashchange`)));async function a(){if(i)try{let t=await fetch(`/api/pair`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({secret:i}),signal:e.signal});if(!t.ok)throw Error(`Pairing link expired or already used. Restart --web for a fresh link.`);n=(await t.json()).token;try{sessionStorage.setItem(ma,n)}catch{}}catch(t){e.signal.aborted||($.error=t instanceof Error?t.message:`Pairing failed`);return}if(!n){$.error=`Open the private pairing link printed by codexometer --web in your terminal.`;return}await o()}async function o(){try{let t=await fetch(`/api/events`,{headers:{Authorization:`Bearer ${n}`},signal:e.signal,cache:`no-store`});if(t.status===401){try{sessionStorage.removeItem(ma)}catch{}$.error=`Browser session expired. Restart --web and open its new pairing link.`;return}if(!t.ok||!t.body)throw Error(`Live connection unavailable`);let r=t.body.getReader(),i=new TextDecoder,a=``;for(;!e.signal.aborted;){let{done:e,value:t}=await r.read();if(e)break;a+=i.decode(t,{stream:!0});let n;for(;(n=a.indexOf(` + +`))>=0;){let e=a.slice(0,n);a=a.slice(n+2),e.startsWith(`data: `)&&($.data=JSON.parse(e.slice(6)),$.connected=!0,$.error=``)}}}catch{}finally{$.connected=!1}e.signal.aborted||($.error=`Connection lost — showing last observation. Reconnecting…`,t=setTimeout(o,2500))}return a(),()=>{ga=void 0,e.abort(),clearTimeout(t),$.connected=!1}}He();var ya=K(`

Quota refresh failed. Policy state is based on the last successful + observation.

`),ba=K(`

`),xa=K(`

The longest Codex quota window selects one active model profile. ASK + creates a per-session review; AUTO applies the profile on the next + eligible check.

`,1),Sa=K(`

No threshold-based model steps were configured at launch.

`);function Ca(e,t){Ge(t,!1),zi();var n=Fr(),r=R(n),i=e=>{var t=xa(),n=R(t),r=z(n),i=B(n,2),a=e=>{q(e,ya())};Y(i,e=>{$.data.quotaError&&e(a)});var o=B(i,2),s=L(o),c=z(s),l=B(s,4);X(l,5,()=>$.data.thresholds,Jr,(e,t)=>{var n=ba();let r;var i=L(n),a=z(i),o=B(i,2),s=L(o),c=z(s,!0),l=z(B(s,2));M(o);var u=B(o,2),d=z(u,!0),f=z(B(u,2));M(n),V((e,i)=>{r=li(n,1,``,null,r,{active:W(t).state===`ACTIVE`,next:W(t).state===`NEXT`}),J(a,`${W(t).threshold??``}%`),J(c,W(t).model),J(l,`${e??``} REASONING // ${i??``} + SPEED`),J(d,W(t).mode),J(f,`${W(t).state??``}${W(t).state===`NEXT`?` // ${W(t).remaining||0} PP TO GO`:``}`)},[()=>W(t).effort.toUpperCase(),()=>W(t).speed.toUpperCase()]),q(e,n)}),M(l),M(o),V(e=>{J(r,`MODEL STEP POLICY // OBSERVED ${e??``}`),J(c,`THRESHOLDS // ${$.data.thresholds.length??``} CONFIGURED`)},[()=>pa($.data.quotaAt)]),q(e,t)},a=e=>{q(e,Sa())};Y(r,e=>{$.data?.thresholds?.length?e(i):e(a,-1)}),q(e,n),Ke()}var wa=Nr(` `,1),Ta=Nr(` `,1),Ea=Nr(` `,1),Da=Nr(` `,1),Oa=K(`
`,1),ka=K(` `),Aa=K(`
Quota observations
Observed atPeriod elapsedConsumedTrail segment
`),ja=K(`
OBSERVATION DATA
`),Ma=K(`
CONSUMPTIONQUOTA PERIOD ELAPSED
`,1);function Na(e,t){let n=Ir();Ge(t,!0);let r=Q(t,`trail`,19,()=>[]),i=F(!1),a=F(!0),o=F(`window`),s=[0,25,50,75,100],c={off:0,halfHour:30,hour:60,day:1440,window:0},l=F(400),u=F(240),d=N(()=>W(l)-24),f=N(()=>W(u)-48),p=N(()=>Math.max(1,W(d)-48)),m=N(()=>Math.max(1,W(f)-20)),h=N(()=>48+Math.max(0,Math.min(100,t.elapsed))/100*W(p));function g(e){return W(f)-(e-0)/100*W(m)}let _=N(()=>g(t.used)),v=N(()=>t.used-t.elapsed),y=N(()=>r().map((e,t)=>`${t===0||e.break?`M`:`L`}${48+e.elapsed/100*W(p)} ${g(e.used)}`).join(` `)),b=N(()=>{let e=0;for(let t=r().length-1;t>0;t--)if(r()[t].break){e=t;break}return r().slice(e)}),x=N(()=>W(b).length<2?0:Math.max(0,Date.parse(W(b).at(-1).at)-Date.parse(W(b)[0].at)));function S(e){if(W(x)Date.parse(e.at)>=t).length>=2}let C=N(()=>({off:!0,halfHour:S(18e5),hour:S(36e5),day:S(864e5),window:Number.isFinite(t.elapsed)&&t.elapsed>0&&Number.isFinite(t.used)}));bn(()=>{W(o)!==`window`&&!W(C)[W(o)]&&I(o,`window`)});function w(e){if(e===`off`||!W(C)[e])return[];let t=c[e]*6e4,n=Date.parse(W(b).at(-1).at)-t,r=W(b).findIndex(e=>Date.parse(e.at)>=n);return r<0?[]:W(b).slice(r)}function ee(e){if(e.length<2)return null;let t=e.reduce((e,t)=>e+t.elapsed,0)/e.length,n=e.reduce((e,t)=>e+t.used,0)/e.length,r=0,i=0;for(let a of e)r+=(a.elapsed-t)*(a.used-n),i+=(a.elapsed-t)**2;return i>0?Math.max(0,r/i):null}let T=N(()=>{let e=W(o)===`window`&&W(C).window?t.used/t.elapsed:ee(w(W(o)));if(e===null||!t.duration)return null;let n=t.used+e*(100-t.elapsed),r=W(o)===`window`?0:t.used-e*t.elapsed,i=e,a=Math.max(0,t.elapsed-c[W(o)]/t.duration*100);W(o)===`window`&&(a=0);let s=100;if(i!==0){let e=(0-r)/i,t=(100-r)/i;a=Math.max(a,Math.min(e,t)),s=Math.min(100,Math.max(e,t))}if(a>s)return null;let l=r+i*a,u=r+i*s,d=Math.hypot((s-a)/100*W(p),(u-l)/100*W(m)),f=Math.max(3,Math.min(9,Math.floor(d/65)+2));return{path:`M${Array.from({length:f},(e,t)=>{let n=a+(s-a)*t/(f-1),r=l+(u-l)*t/(f-1);return`${48+n/100*W(p)} ${g(r)}`}).join(` L`)}`,projected:n,safe:n<=100}}),te=N(()=>{if(W(b).length===0)return`NO OBSERVATIONS`;if(W(b).length===1)return`ONE OBSERVATION`;let e=Math.floor(W(x)/6e4);if(e<60)return`${e} MIN CONTINUOUS`;let t=Math.floor(e/60);return t<24?`${t}H CONTINUOUS`:`${Math.floor(t/24)}D ${t%24}H CONTINUOUS`});var E=Ma(),D=R(E),O=L(D);let ne;var k=L(O),re=L(k),ie=B(re);M(k);var ae=B(k),oe=B(ae);X(oe,17,()=>s,Jr,(e,t)=>{var n=wa(),r=R(n),i=B(r),a=z(i);V(()=>{Z(r,`x1`,48+W(t)/100*W(p)),Z(r,`x2`,48+W(t)/100*W(p)),Z(r,`y2`,W(f)),Z(i,`x`,48+W(t)/100*W(p)),Z(i,`y`,W(f)+20),J(a,`${W(t)??``}%`)}),q(e,n)});var se=B(oe);X(se,17,()=>s,Jr,(e,t)=>{var n=Ta(),r=R(n),i=B(r),a=z(i);V((e,n,o)=>{Z(r,`y1`,e),Z(r,`x2`,W(d)),Z(r,`y2`,n),Z(i,`y`,o),J(a,`${W(t)??``}%`)},[()=>g(W(t)),()=>g(W(t)),()=>g(W(t))+4]),q(e,n)});var ce=B(se),le=B(ce),ue=B(le),de=e=>{var t=Ea(),n=R(t),i=B(n),a=B(i),o=z(L(a));M(a),V((e,t)=>{Z(n,`d`,W(y)),Z(i,`d`,W(y)),Z(a,`cx`,48+r()[0].elapsed/100*W(p)),Z(a,`cy`,e),J(o,`First observation: ${t??``}`)},[()=>g(r()[0].used),()=>pa(r()[0].at)]),q(e,t)};Y(ue,e=>{W(a)&&r().length&&e(de)});var fe=B(ue),pe=e=>{var t=Da(),r=R(t),i=B(r),a=z(L(i));M(i),V(e=>{Z(r,`d`,W(T).path),Z(i,`d`,W(T).path),Z(i,`marker-mid`,`url(#${n}-trend-arrow)`),J(a,`${W(o)===`window`?`Window-average pace`:`Recent consumption trajectory`} projects ${e??``}% at reset if continued`)},[()=>W(T).projected.toFixed(1)]),q(e,t)};Y(fe,e=>{W(T)&&e(pe)});var me=B(fe,2),he=B(me),ge=B(he),_e=z(L(ge));M(ge),M(O),M(D);var ve=B(D,2),ye=L(ve),A=L(ye),be=B(A,3),j=z(be,!0),xe=B(be,2),Se=e=>{var t=Oa(),n=z(B(R(t)));V(e=>J(n,`TREND // ${e??``}`),[()=>W(T).projected>=100?`QUOTA EXHAUSTION PROJECTED`:`${W(T).projected.toFixed(1)}% PROJECTED AT RESET`]),q(e,t)};Y(xe,e=>{W(T)&&e(Se)}),M(ye);var we=B(ye,2),Te=L(we),Ee=L(Te);Si(Ee),Ce(),M(Te);var De=B(Te,2),Oe=B(L(De),2),ke=L(Oe);ke.value=ke.__value=`off`;var Ae=B(ke),je=z(Ae);Ae.value=Ae.__value=`halfHour`;var Me=B(Ae),Ne=z(Me);Me.value=Me.__value=`hour`;var Pe=B(Me),Fe=z(Pe);Pe.value=Pe.__value=`day`;var Ie=B(Pe),Le=z(Ie);Ie.value=Ie.__value=`window`,M(Oe),hi(Oe),M(De);var Re=z(B(De,2));M(we),M(ve);var ze=B(ve,2),Be=e=>{var t=ja(),n=B(L(t),2),a=e=>{var t=Aa(),n=L(t),i=B(L(n),2);X(i,21,r,Jr,(e,t,n)=>{var r=ka(),i=L(r),a=L(i),o=z(a,!0);M(i);var s=B(i),c=z(s),l=B(s),u=z(l),d=z(B(l),!0);M(r),V((e,r)=>{Z(a,`datetime`,W(t).at),J(o,e),J(c,`${r??``}%`),J(u,`${W(t).used??``}%`),J(d,n===0?`First observation`:W(t).break?`Gap before this observation`:`Connected to previous observation`)},[()=>pa(W(t).at),()=>W(t).elapsed.toFixed(1)]),q(e,r)}),M(i),M(n),M(t),q(e,t)};Y(n,e=>{W(i)&&e(a)}),M(t),Ri(`open`,`toggle`,t,e=>I(i,e),()=>W(i)),q(e,t)};Y(ze,e=>{r().length&&e(Be)}),V((e,r,i,a,o,s)=>{Z(O,`viewBox`,`0 0 ${W(l)} ${W(u)}`),Z(O,`aria-label`,e),ne=di(O,``,ne,{"--trend-color":W(T)?.safe?`#115b35`:`#7f1825`}),Z(re,`id`,n),Z(ie,`id`,n+`-trend-arrow`),Z(ae,`width`,W(p)),Z(ae,`height`,W(m)),Z(ae,`fill`,`url(#${n})`),Z(ce,`d`,`M48 20 V${W(f)} H${W(d)}`),Z(le,`y1`,r),Z(le,`x2`,W(d)),Z(le,`y2`,i),Z(me,`x`,48+W(p)/2),Z(me,`y`,W(u)-5),Z(he,`cx`,W(h)),Z(he,`cy`,W(_)),Z(ge,`cx`,W(h)),Z(ge,`cy`,W(_)),J(_e,`${t.used??``}% consumed // ${a??``}% of period elapsed`),J(A,`${t.used??``}% USED // ${o??``}% TIME ELAPSED`),J(j,s),Ae.disabled=!W(C).halfHour,J(je,`LAST 30 MINUTES${W(C).halfHour?``:` — UNAVAILABLE`}`),Me.disabled=!W(C).hour,J(Ne,`LAST HOUR${W(C).hour?``:` — UNAVAILABLE`}`),Pe.disabled=!W(C).day,J(Fe,`LAST 24 HOURS${W(C).day?``:` — UNAVAILABLE`}`),Ie.disabled=!W(C).window,J(Le,`FROM WINDOW START${W(C).window?``:` — NO TIME ELAPSED`}`),J(Re,`HISTORY // ${W(te)??``}`)},[()=>`Pace: ${t.used}% consumed, ${t.elapsed.toFixed(1)}% of quota period elapsed. ${W(v)>0?`Above`:W(v)<0?`Below`:`On`} the steady-consumption line.${W(a)&&r().length?` Trace path contains ${r().length} ${r().length===1?`observation`:`observations`}; gaps are not interpolated.`:``}`,()=>g(0),()=>g(100),()=>t.elapsed.toFixed(1),()=>t.elapsed.toFixed(1),()=>Math.abs(W(v))<.05?`ON PACE`:W(v)>0?`ABOVE THE LINE — CONSUMING FASTER THAN TIME`:`BELOW THE LINE — WITHIN PACE`]),Fi(D,`clientWidth`,e=>I(l,e)),Fi(D,`clientHeight`,e=>I(u,e)),Ai(Ee,()=>W(a),e=>I(a,e)),gi(Oe,()=>W(o),e=>I(o,e)),q(e,E),Ke()}var Pa=K(` `),Fa=K(`

Quota refresh failed. Values below are the last successful observation.

`),Ia=K(`

Expiry details unavailable. No listed expiry does not mean no expiry.

`),La=K(`

`),Ra=K(`

Read-only preview. Use the terminal to redeem a reset.

The backend may return only some credits. This list does not establish + redemption order.

`),za=K(` `,1),Ba=Nr(``),Va=Nr(``),Ha=K(`
`),Ua=K(`

Cycle duration or reset date unavailable — position cannot be + plotted.

`),Wa=K(`
EMPTYFULL
`),Ga=K(`

`,1),Ka=K(`
`,1),qa=K(`

`),Ja=K(`

`),Ya=K(`

No quota windows reported yet.

`),Xa=K(`
`,1),Za=K(`

All reported windows are shown. API-equivalent learning and quota status + scoring remain in the terminal for this first preview.

`,1),Qa=K(` `,1);function $a(e,t){Ge(t,!0);let n=Q(t,`params`,19,()=>({})),r=N(()=>ea.filter(e=>e!==`thresholds`||!!$.data?.thresholds?.length)),i=F(Zt(Date.now())),a=N(()=>W(r).includes(ta(n().view))?ta(n().view):`bars`);Gi(()=>{let e=setInterval(()=>I(i,Date.now(),!0),1e3);return()=>clearInterval(e)});function o(e){return!e.duration||e.duration<=0||!e.reset?null:Math.max(0,Math.min(100,100*(1-(e.reset*1e3-W(i))/(e.duration*6e4))))}bn(()=>{aa.view=W(a),(n().view===`zone`||n().view===`consumption-pace`)&&Zi(`/quota/pace`)});function s(e){let t=e/100*Math.PI*2;return`M60 60 L60 14 A46 46 0 ${+(e>50)} 1 ${60+46*Math.sin(t)} ${60-46*Math.cos(t)} Z`}var c=Qa(),l=R(c);X(l,21,()=>W(r),Jr,(e,t)=>{var n=Pa();let r;var i=z(n,!0);V(e=>{Z(n,`href`,`#/quota/`+W(t)),Z(n,`aria-current`,W(a)===W(t)?`page`:void 0),r=li(n,1,``,null,r,{active:W(a)===W(t)}),J(i,e)},[()=>W(t)===`fuel`?`FUEL TANK`:W(t).toUpperCase()]),q(e,n)}),M(l);var u=B(l,2),d=e=>{var t=Za(),n=R(t),r=e=>{q(e,Fa())};Y(n,e=>{$.data.quotaError&&e(r)});var i=B(n,2),c=z(i),l=B(i,2),u=e=>{Ca(e,{})},d=e=>{var t=Ra(),n=L(t),r=z(n),i=B(n,4),a=e=>{q(e,Ia())};Y(i,e=>{$.data.credits.length||e(a)}),X(B(i,2),17,()=>$.data.credits,Jr,(e,t)=>{var n=La(),r=L(n),i=z(r),a=z(B(r,2),!0);M(n),V(e=>{J(i,`${(W(t).title||`Quota reset`)??``} // ${W(t).status??``}`),J(a,e)},[()=>W(t).expiryKnown?W(t).expires?`EXPIRES `+pa(W(t).expires):`Does not expire`:`Expiry information unavailable`]),q(e,n)}),Ce(2),M(t),V(()=>J(r,`RESET INVENTORY // ${$.data.creditCount??``} AVAILABLE`)),q(e,t)},f=e=>{var t=Xa(),n=R(t);let r;X(n,21,()=>$.data.meters,Jr,(e,t)=>{let n=N(()=>o(W(t)));var r=Ja(),i=L(r),c=z(i,!0),l=B(i,2),u=L(l),d=e=>{var n=za(),r=R(n),i=z(r),a=z(B(r));V(()=>{J(i,`FREE ${100-W(t).used}%`),J(a,`USED ${W(t).used??``}%`)}),q(e,n)},f=e=>{var n=za(),r=R(n),i=z(r),a=z(B(r));V(()=>{J(i,`USED ${W(t).used??``}%`),J(a,`FREE ${100-W(t).used}%`)}),q(e,n)};Y(u,e=>{W(a)===`fuel`?e(d):e(f,-1)}),M(l);var p=B(l,2);let m;var h=L(p),g=e=>{var n=Ha(),r=L(n),i=B(L(r)),a=e=>{q(e,Ba())},o=e=>{var n=Va();V(e=>Z(n,`d`,e),[()=>s(W(t).used)]),q(e,n)};Y(i,e=>{W(t).used>=100?e(a):W(t).used>0&&e(o,1)}),M(r),M(n),V(()=>Z(r,`aria-label`,`${W(t).used}% quota used`)),q(e,n)},_=e=>{var r=Fr(),i=R(r),a=e=>{{let r=N(()=>W(t).trail||[]);Na(e,{get used(){return W(t).used},get elapsed(){return W(n)},get trail(){return W(r)},get duration(){return W(t).duration}})}},o=e=>{q(e,Ua())};Y(i,e=>{W(n)===null?e(o,-1):e(a)}),q(e,r)},v=e=>{var r=Ka(),i=R(r),o=L(i);let s;M(i);var c=B(i,2),l=e=>{q(e,Wa())};Y(c,e=>{W(a)===`fuel`&&e(l)});var u=B(c,2),d=e=>{var t=Ga(),r=R(t),i=z(r),o=B(r,2),s=L(o);let c;M(o),V(e=>{J(i,`RESET CYCLE // ${e??``}% ELAPSED`),c=di(s,``,c,{width:`${W(a)===`fuel`?100-W(n):W(n)}%`})},[()=>Math.floor(W(n))]),q(e,t)};Y(u,e=>{W(n)!==null&&e(d)}),V(()=>{Z(i,`aria-label`,W(a)===`fuel`?`Fuel remaining`:`Quota used`),Z(i,`aria-valuenow`,W(a)===`fuel`?100-W(t).used:W(t).used),s=di(o,``,s,{width:`${W(a)===`fuel`?100-W(t).used:W(t).used}%`})}),q(e,r)};Y(h,e=>{W(a)===`pie`?e(g):W(a)===`pace`?e(_,1):e(v,-1)}),M(p);var y=B(p,2),b=z(y),x=B(y,2),S=e=>{var n=qa(),r=z(n,!0);V(()=>J(r,W(t).details)),q(e,n)};Y(x,e=>{W(t).details&&e(S)}),M(r),V(e=>{J(c,W(t).name),m=li(p,1,`meter-graphic`,null,m,{"bar-graphic":W(a)===`bars`||W(a)===`fuel`}),J(b,`RESETS // ${e??``}`)},[()=>pa(W(t).reset)]),q(e,r)}),M(n);var i=B(n,2),c=e=>{q(e,Ya())};Y(i,e=>{$.data.meters.length||e(c)}),V(()=>r=li(n,1,`quota-grid`,null,r,{radial:W(a)===`pie`,zone:W(a)===`pace`})),q(e,t)};Y(l,e=>{W(a)===`thresholds`?e(u):W(a)===`resets`?e(d,1):e(f,-1)}),Ce(2),V(e=>J(c,`QUOTA // OBSERVED ${e??``}`),[()=>pa($.data.quotaAt)]),q(e,t)};Y(u,e=>{$.data&&e(d)}),q(e,c),Ke()}var eo=K(`
`),to=K(`

`,1);function no(e,t){Ge(t,!0);let n=Q(t,`values`,19,()=>[]),r=Q(t,`label`,3,`Token activity`),i=Q(t,`capacity`,3,0),a=N(()=>Math.max(0,...n())),o=N(()=>i()>n().length?[...Array(i()-n().length).fill(0),...n()]:n());var s=to(),c=R(s),l=z(c),u=B(c,2);X(u,21,()=>W(o),Jr,(e,t)=>{var n=eo();let r;V((e,t)=>{Z(n,`title`,e),r=di(n,``,r,{height:t})},[()=>W(t).toLocaleString(`en-GB`)+` tokens`,()=>`${100*W(t)/Math.max(1,W(a))}%`]),q(e,n)}),M(u),V((e,t)=>{J(l,`SCALE // 0 — ${e??``} TOKENS`),Z(u,`aria-label`,t)},[()=>W(a).toLocaleString(`en-GB`),()=>`${r()}. Peak ${W(a).toLocaleString(`en-GB`)} tokens.`]),q(e,s),Ke()}var ro=K(` `),io=K(``),ao=K(`

`),oo=K(`

`,1),so=K(`

`),co=K(`

Loading / sending…

`),lo=K(`
`),uo=K(`

Confirmation expired. Reopen the option.

`),fo=K(`

`,1),po=K(`

 
`),mo=K(`
`),ho=K(`
`),go=K(`

Review expired. Press Enter to review again.

`),_o=K(`

`,1),vo=K(`

↑/↓ select · Enter review · C confirm · Escape cancel

`,1),yo=K(`

No available matching commands.

`),bo=K(`

↑/↓ select · Tab complete · Enter options · Escape dismiss

`),xo=K(` `,1),So=K(` `,1),Co=K(``),wo=K(` `,1),To=K(`
`),Eo=K(`
`);function Do(e,t){let n=Ir();Ge(t,!0);let r=e=>{var t=Fr();X(R(t),19,()=>W(E),e=>e.id,(e,t,r)=>{var i=io();let a;var o=L(i),s=z(o,!0),l=B(o),d=z(l),f=B(l),p=e=>{var n=ro(),r=z(n,!0);V(()=>J(r,W(t).help)),q(e,n)};Y(f,e=>{(W(T)&&W(u)?.path===``||W(C))&&e(p)}),M(i),V(()=>{Z(i,`id`,`${n}-${W(r)}`),Z(i,`role`,W(C)?`option`:void 0),Z(i,`aria-selected`,W(C)?W(_)===W(r):void 0),Z(i,`tabindex`,W(C)?-1:void 0),i.disabled=W(c)||W(O),Z(i,`title`,W(t).help),Z(i,`aria-label`,W(t).label+(W(C)&&W(t).selected?` // CURRENT`:``)+(W(t).next?` →`:W(t).action?``:` // HELP`)),a=li(i,1,`svelte-7bfwr9`,null,a,{suggested:W(_)===W(r)}),J(s,W(t).label),J(d,`${W(C)&&W(t).selected?` // CURRENT`:``}${W(t).next?` →`:W(t).action?``:` // HELP`}`)}),Cr(`focus`,i,()=>{W(C)||k(W(r))}),G(`click`,i,()=>{k(W(r)),oe(W(t))}),q(e,i)}),q(e,t)},i=Q(t,`query`,3,``),a=Q(t,`onchange`,3,()=>{}),o=Q(t,`oncomplete`,3,e=>{}),s=F(!1),c=F(!1),l=F(``),u=F(null),d=F(null),f=F(``),p=F(0),m=F(Zt(Date.now())),h=F(``),g=0,_=F(0),v=F(null),y=F(void 0),b=F(void 0),x=F(void 0),S=F(``),C=N(()=>W(s)&&!!W(u)?.picker&&!W(d)),w=N(()=>W(s)&&!W(u)?.input&&!W(d)),ee=setInterval(()=>{I(m,Date.now(),!0)},1e3);Ki(()=>{clearInterval(ee),g++});let T=N(()=>i().trim().startsWith(`/`)&&!i().trim().startsWith(`//`)),te=N(()=>W(u)?.path===``&&W(T)?i().trim().slice(1).toLowerCase():``),E=N(()=>(W(u)?.choices||[]).filter(e=>!W(te)||e.label.toLowerCase().startsWith(`/`+W(te)))),D=N(()=>W(E).find(e=>e.id===W(S))),O=N(()=>!$.connected||!!$.data?.sessionsError),ne=N(()=>W(s)&&W(T)&&(!W(u)||W(u).path===``)&&!W(d));bn(()=>{i(),I(_,0)}),bn(()=>{let e=W(_);W(E),W(w)&&dr().then(()=>{let t=W(y)?.children[e];if(!t||!W(y))return;let n=W(y).getBoundingClientRect(),r=t.getBoundingClientRect();r.topn.bottom&&(W(y).scrollTop+=r.bottom-n.bottom)})});function k(e){W(_)!==e&&W(u)?.picker&&(I(S,``),I(f,``),I(p,0),I(l,``),g++),I(_,e,!0)}function re(){g++,I(c,!1),I(s,!1),I(v,i()),I(f,``),I(S,``),I(p,0),o()(i())}function ie(e){let t=!!W(b)?.contains(e.target);if(!W(s)||W(u)?.input||!W(w)&&!W(d)||!W(T)&&!t||W(O)||e.isComposing||e.ctrlKey||e.metaKey||e.altKey)return;let n=e.key.toLowerCase()===`c`;if(n&&!t||![`ArrowUp`,`ArrowDown`,`Tab`,`Enter`,`Escape`].includes(e.key)&&!n||e.key===`Tab`&&(W(d)||W(u)?.path!==``||e.shiftKey)||e.key===`Enter`&&e.target instanceof Element&&e.target.closest(`button`)&&!W(y)?.contains(e.target)&&!e.target.closest(`[data-command-confirm]`)||W(d)&&!t||W(d)&&[`ArrowUp`,`ArrowDown`].includes(e.key))return;if(e.preventDefault(),e.stopPropagation(),e.key===`Escape`){re();return}if(W(c)||e.repeat&&n)return;if(n){(W(d)?.action||W(C)&&W(S))&&ce();return}if(W(d))return;if(e.key===`ArrowUp`||e.key===`ArrowDown`){k(Math.min(Math.max(W(_)+(e.key===`ArrowUp`?-1:1),0),Math.max(W(E).length-1,0))),!W(C)&&W(y)?.contains(e.target)&&dr().then(()=>(W(y)?.children[W(_)])?.focus());return}let r=W(E)[W(_)];r&&(e.key===`Tab`?o()(r.label):W(C)?se(r):oe(r))}bn(()=>{W(O)&&(g++,I(c,!1),I(f,``),I(S,``),W(s)&&(I(s,!1),I(v,i())),I(u,null),I(d,null))}),bn(()=>{W(T)&&!W(s)&&i()!==W(v)&&!W(O)&&(I(s,!0),ae(``))});async function ae(e){let n=++g;I(c,!0),I(d,null),I(S,``),I(f,``),I(p,0),I(l,``);try{let r=await _a(`commands`,{session:t.session,command:{mode:`list`,path:e}});n===g&&((e===``||e===`help`)&&r.choices.push({id:`local-statusline`,label:`/statusline`,help:`Choose multiple fields for Codexometer’s detail footer.`,next:`statusline`}),I(u,r,!0),I(_,r.picker?Math.max(r.choices.findIndex(e=>e.selected),0):0,!0),r.picker&&dr().then(()=>W(y)?.focus()),I(h,r.value||``,!0),(r.path.startsWith(`rename/`)||r.path.startsWith(`cd/`))&&r.choices.length===1&&(I(c,!1),oe(r.choices[0])))}catch{n===g&&(I(l,`Command catalogue unavailable. Use Codex.`),(e===``||e===`help`)&&I(u,{title:`/ COMMANDS`,help:`Local display settings remain available.`,path:``,revision:``,choices:[{id:`local-statusline`,label:`/statusline`,help:`Choose multiple fields for Codexometer’s detail footer.`,next:`statusline`}]},!0))}finally{n===g&&I(c,!1)}}async function oe(e){if(W(u)?.picker){k(W(E).findIndex(t=>t.id===e.id)),W(y)?.focus();return}if(e.next===`statusline`){oa.open=!0,I(s,!1),I(v,i());return}if(e.next){await ae(e.next);return}await se(e)}async function se(e){if(W(c)||W(O)||!W(u)||(W(u).picker?I(S,e.id,!0):(I(d,e,!0),dr().then(()=>W(x)?.focus())),I(f,``),I(p,0),I(l,``),!e.action))return;let n=++g;I(c,!0);try{let r=await _a(`commands`,{session:t.session,command:{mode:`prepare`,path:W(u).path,revision:W(u).revision,choice:e.id}});if(n===g){let e=Date.parse(r.expires);if(!r.confirmation||!Number.isFinite(e)||Date.now()>=e)throw Error(`Review confirmation unavailable`);I(f,r.confirmation,!0),I(p,e,!0)}}catch{n===g&&I(l,`Option changed or unavailable. Refresh before retrying.`)}finally{n===g&&I(c,!1)}}async function ce(){if(!W(f)||W(O)||W(c)||Date.now()>=W(p)||!W(u)||W(u).picker&&W(S)!==W(E)[W(_)]?.id)return;let e=W(f);I(f,``),I(c,!0);let n=++g;try{let r=await _a(`commands`,{session:t.session,confirmation:e,command:{mode:`commit`,path:W(u).path}});n===g&&(I(l,r.message,!0),I(s,!1),I(u,null),I(d,null),I(S,``),I(p,0),I(v,null),a()())}catch{n===g&&I(l,`Change unconfirmed. Check Codex before retrying.`)}finally{n===g&&I(c,!1)}}var le={handleKey:ie},ue=Eo(),de=L(ue),fe=B(de,2),pe=e=>{var t=ao(),n=z(t,!0);V(()=>J(n,W(l))),q(e,t)};Y(fe,e=>{W(l)&&!W(s)&&e(pe)});var me=B(fe,2),he=e=>{var i=To();let a;var o=L(i),s=e=>{var t=oo(),n=R(t),r=z(n,!0),i=z(B(n,2),!0);V(()=>{J(r,W(u)?.title||`/ COMMANDS`),J(i,W(u)?.help||`Loading live options…`)}),q(e,t)};Y(o,e=>{W(ne)||e(s)});var v=B(o,2),w=e=>{var t=so(),n=z(t,!0);V(()=>J(n,W(l))),q(e,t)};Y(v,e=>{W(l)&&e(w)});var ee=B(v,2),te=e=>{q(e,co())};Y(ee,e=>{W(c)&&e(te)});var k=B(ee,2),oe=e=>{var t=lo(),n=L(t),r=L(n),i=B(r);Si(i),M(n);var a=B(n,2),o=z(a);M(t),V((e,t)=>{J(r,`${e??``} `),Z(i,`aria-label`,W(u).inputLabel||`Session name`),Z(i,`maxlength`,W(u).inputLimit||512),i.disabled=W(c)||W(O),a.disabled=t,J(o,`REVIEW ${W(u).path===`cd`?`DIRECTORY`:`RENAME`}`)},[()=>(W(u).inputLabel||`Session name`).toUpperCase(),()=>W(c)||W(O)||!W(h).trim()]),Cr(`submit`,t,e=>{e.preventDefault(),W(u)&&W(h).trim()&&!W(c)&&!W(O)&&ae(W(u).path+`/`+encodeURIComponent(W(h).trim()))}),Di(i,()=>W(h),e=>I(h,e)),q(e,t)},le=e=>{var n=po(),r=L(n),i=z(r,!0),a=B(r,2),o=z(a,!0),s=B(a,2),l=e=>{var n=fo(),r=R(n),i=L(r),a=B(i),o=e=>{q(e,Pr(`Only this session's saved + name changes.`))},s=N(()=>W(u)?.path.startsWith(`rename/`)),l=e=>{q(e,Pr(`Only this session’s + directory changes; project configuration is not reloaded.`))},d=N(()=>W(u)?.path.startsWith(`cd/`)),h=e=>{q(e,Pr(`No change sent yet. C confirms the selected setting. Model, + reasoning and speed settings can change token usage and cost. + Changes affect subsequent turns of this session, not global + defaults. Automatic quota thresholds may later supersede model + settings.`))};Y(a,e=>{W(s)?e(o):W(d)?e(l,1):e(h,-1)}),M(r);var g=B(r,2),_=B(g,2),v=e=>{q(e,uo())};Y(_,e=>{W(f)&&W(m)>=W(p)&&e(v)}),V(()=>{J(i,`TARGET // ${t.session??``}. `),g.disabled=W(c)||W(O)||!W(f)||W(m)>=W(p)}),G(`click`,g,ce),q(e,n)};Y(s,e=>{W(d).action&&e(l)});var h=B(s,2);M(n),Li(n,e=>I(x,e),()=>W(x)),V(()=>{J(i,W(d).label),J(o,W(d).help||`No additional help supplied by Codex.`),h.disabled=W(c)}),G(`click`,h,()=>{g++,I(c,!1),I(d,null),I(f,``),dr().then(()=>W(y)?.focus())}),q(e,n)},ue=e=>{var t=xo(),i=R(t),a=e=>{var t=mo(),i=L(t);r(i),M(t),Li(t,e=>I(y,e),()=>W(y)),V(()=>Z(t,`aria-activedescendant`,`${n}-${W(_)}`)),q(e,t)},o=e=>{var t=ho();let n;var i=L(t);r(i),M(t),Li(t,e=>I(y,e),()=>W(y)),V(()=>n=li(t,1,`command-options svelte-7bfwr9`,null,n,{vertical:W(ne)})),q(e,t)};Y(i,e=>{W(C)?e(a):e(o,-1)});var s=B(i,2),l=e=>{var t=vo(),n=R(t),r=z(n,!0),i=B(n,4),a=e=>{var t=_o(),n=R(t),r=z(n),i=B(n,2),a=e=>{q(e,go())};Y(i,e=>{W(f)&&W(m)>=W(p)&&e(a)}),V(()=>J(r,`REVIEW // ${W(D).label??``}. No change sent yet; C confirms.`)),q(e,t)};Y(i,e=>{W(D)&&e(a)}),V(()=>J(r,W(E)[W(_)]?.help)),q(e,t)};Y(s,e=>{W(C)&&e(l)});var d=B(s,2),h=e=>{q(e,yo())};Y(d,e=>{!W(c)&&!W(E).length&&e(h)});var g=B(d,2),v=e=>{q(e,bo())};Y(g,e=>{W(T)&&W(u)?.path===``&&e(v)}),q(e,t)};Y(k,e=>{W(u)?.input?e(oe):W(d)?e(le,1):e(ue,-1)});var de=B(k,2),fe=e=>{var t=wo(),n=R(t),r=e=>{var t=So(),n=R(t),r=B(n,2);V(()=>{n.disabled=W(c)||W(O)||!W(E)[W(_)]?.action||!!W(l),r.disabled=W(c)||W(O)||!W(f)||W(S)!==W(E)[W(_)]?.id||W(m)>=W(p)||!!W(l)}),G(`click`,n,()=>{let e=W(E)[W(_)];e&&se(e)}),G(`click`,r,ce),q(e,t)};Y(n,e=>{W(C)&&e(r)});var i=B(n,2),a=B(i,2),o=B(a,2),s=e=>{var t=Co();V(()=>t.disabled=W(c)),G(`click`,t,()=>{re()}),q(e,t)};Y(o,e=>{W(C)&&e(s)}),V(()=>{i.disabled=W(c),a.disabled=W(c)}),G(`click`,i,()=>ae(``)),G(`click`,a,()=>ae(W(u)?.path||``)),q(e,t)};Y(de,e=>{W(ne)||e(fe)}),M(i),Li(i,e=>I(b,e),()=>W(b)),V(()=>{Z(i,`aria-label`,W(ne)?`Slash command suggestions`:`Session command options`),a=li(i,1,`svelte-7bfwr9`,null,a,{"command-popup":W(ne)})}),G(`keydown`,i,ie),q(e,i)};return Y(me,e=>{W(s)&&e(he)}),M(ue),V(()=>de.disabled=W(O)||W(c)),G(`click`,de,()=>{I(s,!W(s)),I(v,W(s)?null:i(),!0),I(f,``),W(s)&&ae(``)}),q(e,ue),Ke(le)}wr([`click`,`keydown`]);var Oo=K(` `,1),ko=K(`
`),Ao=K(`
/
`);function jo(e,t){Ge(t,!0);let n=N(()=>t.lines.filter(e=>e.kind===`addition`).length),r=N(()=>t.lines.filter(e=>e.kind===`removal`).length);var i=Ao(),a=L(i),o=L(a),s=z(o),c=z(B(o,2));M(a),X(B(a,2),17,()=>t.lines,Jr,(e,t)=>{var n=ko();let r;var i=L(n),a=e=>{var n=Oo(),r=R(n),i=z(r,!0),a=z(B(r),!0);V(()=>{J(i,W(t).old||``),J(a,W(t).new||``)}),q(e,n)};Y(i,e=>{W(t).kind!==`heading`&&W(t).kind!==`metadata`&&e(a)});var o=z(B(i),!0);M(n),V(()=>{r=li(n,1,`diff-line svelte-x4gtb6`,null,r,{added:W(t).kind===`addition`,removed:W(t).kind===`removal`,heading:W(t).kind===`heading`}),J(o,W(t).text)}),q(e,n)}),M(i),V(()=>{Z(a,`aria-label`,`${W(n)} lines added, ${W(r)} lines removed`),J(s,`+${W(n)??``}`),J(c,`−${W(r)??``}`)}),q(e,i),Ke()}var Mo=K(`

`),No=K(`

Send this saved prompt now? This starts Codex work and consumes quota.

`,1),Po=K(``),Fo=K(`

 

In memory only. Follows session model, reasoning and speed. Closing + Codexometer cancels pending work.

`),Io=K(`

CURRENT PROFILE

MODEL / REASONING LEVEL / SPEED

 

PROPOSED PROFILE

MODEL / REASONING LEVEL / SPEED

 

Applied settings remain after Codexometer closes.

`,1),Lo=K(`

`),Ro=K(`

 
`,1),zo=K(`

Command unavailable from this observation. Open Codex to inspect the + request.

`),Bo=K(`

Session controls temporarily unavailable. Check Codex for current state.

`),Vo=K(`

Checking session controls…

`),Ho=K(`

`),Uo=K(`
About browser controls

Controls require a supported live request from a connected shared + app-server session. Local observations alone cannot provide them.

`),Wo=K(` `,1),Go=K(` `),Ko=K(`Grants permission beyond this one action. Check the scope + carefully.`),qo=K(``),Jo=K(``),Yo=K(``),Xo=K(``),Zo=K(`

Not before this time; when quota and idle allow.

`),Qo=K(`

`,1),$o=K(`

The existing trigger remains active until saved or deleted. If + it changes or sends while you edit, saving is blocked.

`),es=K(` `,1),ts=K(``),ns=K(``),rs=K(``),is=K(` `,1),as=K(`
  • `),os=K(`
    View fixed choices

    Type one of these choices exactly. Your answer stays masked.

      `),ss=K(` `,1),cs=K(`

      `),ls=K(`

      `,1),us=K(` `,1),ds=K(``),fs=K(`

      `,1),ps=K(`

      `);function ms(e,t){Ge(t,!0);let n=[],r=Q(t,`observedCommand`,3,``),i=Q(t,`observedFileChanges`,19,()=>[]),a=Q(t,`review`,3,``),o=Q(t,`suspended`,3,!1),s=Q(t,`openScheduler`,3,!1),c=Q(t,`onProtectedChange`,3,e=>{}),l=F(null),u=F(Zt([])),d=F(null),f=F(``),p=F(0),m=F(Zt(Date.now())),h=F(!1),g=F(``),_=F(!1),v=F(!1),y=F(``),b=F(!1),x=F(``),S=F(!1),C=F(``),w=F(`now`),ee=F(60),T=F(``),te=F(!1),E=F(``),D=F(Zt([])),O=N(()=>W(D).find(e=>e.status!==`sent`)),ne=F(void 0),k=F(void 0);function re(e){if(W(b)){dr().then(()=>W(ne)?.focus());return}if(e.status!==`pending`||W(h)||!W(l)?.id||W(l).kind!==`prompt`||W(l).questions?.length)return;I(b,!0),I(x,e.id,!0),dr().then(()=>W(ne)?.focus()),I(u,[e.text],!0),I(w,e.trigger===`quota`?`quota`:`at`,!0),I(f,``),I(C,``);let t=new Date(e.at);e.trigger!==`quota`&&I(T,new Date(t.getTime()-t.getTimezoneOffset()*6e4).toISOString().slice(0,16),!0)}bn(()=>{s()&&W(O)&&!W(S)&&W(l)?.id&&W(l).kind===`prompt`&&!W(l).questions?.length&&(I(S,!0),re(W(O)))});async function ie(){if(!a())try{I(D,await _a(`schedules`,{session:t.session},ye.signal),!0),W(b)&&!W(D).some(e=>e.id===W(x)&&e.status===`pending`)&&I(f,``),W(C)&&!W(D).some(e=>e.id===W(C)&&e.status===`pending`)&&(I(f,``),I(C,``))}catch{}}async function ae(e){try{I(D,await _a(`schedules`,{session:t.session,cancelId:e},ye.signal),!0),I(b,!1),I(f,``),I(C,``),I(u,[],!0)}catch(e){I(g,e instanceof Error?e.message:`Unable to cancel trigger.`,!0)}}bn(()=>(c()(W(h)||W(u).some(e=>e.length>0)),()=>c()(!1)));let oe=N(()=>$.data?.sessions.find(e=>e.id===t.session)?.status),se=N(()=>!$.connected||!!$.data?.sessionsError),ce=N(()=>a()!==`profile`&&W(l)?.kind===`prompt`&&!W(l).questions?.length&&!!$.data?.profiles?.some(e=>e.session===t.session&&e.pending)),le=N(()=>!W(se)&&!W(v)&&!!W(l)?.id&&W(l).kind===`approval`&&W(y)!==W(l).id),ue=N(()=>W(le)?W(l).command:r()),de=N(()=>!!W(f)&&W(m)W(l)?.questions?.length?W(l).questions:[{text:`Follow-up message`,secret:!1,freeText:!0,options:[]}]),pe=N(()=>W(l)?.kind===`approval`||W(l)?.kind===`profile`?W(d)!==null:W(u).length===W(fe).length&&W(u).every((e,t)=>e.trim().length>0&&(W(fe)[t].freeText||W(fe)[t].options?.includes(e)))),me=N(()=>W(l)?.kind===`prompt`&&!W(l).questions?.length&&W(w)!==`now`),he=N(()=>W(w)===`delay`?W(m)+W(ee)*6e4:Date.parse(W(T))),ge=N(()=>{if(!W(me))return``;if(W(b)&&!W(D).some(e=>e.id===W(x)&&e.status===`pending`))return`Trigger changed, removed or already sent. Go back and reopen it; your draft has not been saved.`;if(!W(u)[0]?.trim())return`Enter a message to schedule.`;if(W(w)===`quota`)return``;if(!Number.isFinite(W(he))||W(he)<=W(m))return`Choose a future date/time or a delay greater than zero.`;let e=new Date(W(m));return e.setFullYear(e.getFullYear()+1),W(he)>=e.getTime()?`Choose a time within one year.`:``});function _e(e){return new Date(e).toLocaleString([],{timeZoneName:`short`})}function ve(e){if(e.status!==`pending`)return e.status;if(e.trigger===`at`&&Date.parse(e.at)>W(m))return`Due in `+Math.ceil((Date.parse(e.at)-W(m))/6e4)+` min`;let t=W(se)?`waiting for a fresh connection`:e.waitingReason||(e.canSend?`ready; awaiting dispatch`:`waiting for quota and an eligible idle session`);return(e.trigger===`at`?`Time reached — `:``)+t}bn(()=>{(W(se)||o()||W(ce)||W(m)>=W(p))&&I(f,``)});let ye=new AbortController;Gi(()=>{let e=e=>{e.ctrlKey&&e.key.toLowerCase()===`s`&&!a()&&W(O)&&(e.preventDefault(),re(W(O)))};window.addEventListener(`keydown`,e);let n,r=setInterval(()=>{I(m,Date.now(),!0)},1e3);async function i(){await ie();try{let e=await _a(`offer`,{session:t.session,...a()?{review:a()}:{}},ye.signal);if(ye.signal.aborted)return;I(v,!1),W(l)?.id!==e.id&&(I(f,``),I(d,null),I(u,Array(Math.max(1,e.questions?.length||0)).fill(``),!0)),I(l,e,!0),W(_)&&e.id&&e.id!==W(y)&&(I(g,``),I(_,!1))}catch{ye.signal.aborted||(I(l,null),I(v,!0),I(f,``))}finally{ye.signal.aborted||(n=setTimeout(i,2e3))}}return i(),()=>{window.removeEventListener(`keydown`,e),ye.abort(),clearTimeout(n),clearInterval(r)}});async function A(e){if(!e&&W(l)?.kind===`prompt`&&!W(l).questions?.length&&W(u)[0]?.trim().startsWith(`/`)&&!W(u)[0]?.trim().startsWith(`//`)){I(g,`Choose a slash command from COMMANDS; use // for literal slash text.`);return}if(!W(l)?.id||W(h)||W(se)||o()||W(ce)||(e?!e.canSend:!W(pe)||W(ge)))return;let n=W(l).id;I(h,!0),I(g,``),I(_,!1),I(f,``),I(C,e?.id||``,!0);try{let r=!e&&W(l).kind===`prompt`&&!W(l).questions?.length&&W(w)!==`now`?{trigger:W(w)===`quota`?`quota`:`at`,zone:Intl.DateTimeFormat().resolvedOptions().timeZone,...W(w)===`quota`?{}:{at:(W(w)===`delay`?new Date(Date.now()+W(ee)*6e4):new Date(W(T))).toISOString()}}:void 0;I(te,!!r),I(E,r?r.trigger===`quota`?`After fresh quota becomes available, when the session is idle`:_e(r.at)+` — not before this time; when quota and idle allow`:``,!0);let i=await _a(`prepare`,{session:t.session,...a()?{review:a()}:{},offer:n,...e?{sendId:e.id}:{},...r?{schedule:r}:{},...r&&W(b)?{editId:W(x)}:{},...W(l).kind===`approval`||W(l).kind===`profile`?{choice:W(d)}:{answers:e?[e.text]:[...W(u)]}},ye.signal);W(l)?.id===n&&!ye.signal.aborted&&!W(se)&&!o()&&!W(ce)&&(I(f,i.confirmation,!0),I(p,Date.parse(i.expires),!0))}catch(e){ye.signal.aborted||I(g,e instanceof Error?e.message:`Unable to prepare action.`,!0)}finally{I(h,!1)}}async function be(){if(!W(l)?.id||W(h)||o()||W(ce)||!W(de))return;let e=W(l).id,n=W(l).kind,r=W(f);I(f,``),I(h,!0),I(y,e,!0);try{await _a(`commit`,{session:t.session,...a()?{review:a()}:{},offer:e,confirmation:r},ye.signal),I(_,!0),I(b,!1),W(te)&&(I(y,``),await ie()),I(g,W(te)?`Trigger set. Closing Codexometer cancels it.`:n===`profile`?`Profile decision completed.`:n===`approval`?`Decision sent.`:`Text sent.`,!0)}catch(e){I(g,e instanceof ha?e.message:`Outcome uncertain. Check Codex before taking another action; nothing was retried.`,!0)}finally{I(h,!1),I(C,``),await ie(),I(u,[],!0),I(d,null)}}var j=Wo(),xe=R(j),Se=e=>{var t=Fr();X(R(t),17,()=>W(D).filter(e=>e.status!==`sent`),e=>e.id,(e,t)=>{var n=Fo(),r=L(n),i=z(r,!0),a=B(r,2),s=z(a),c=B(a,2),u=z(c,!0),d=B(c,2),p=z(d,!0),m=B(d,2),_=z(m,!0),v=B(m,2),y=e=>{var t=Mo(),n=z(t,!0);V(()=>J(n,W(g))),q(e,t)};Y(v,e=>{W(g)&&e(y)});var b=B(v,4),x=B(b,2),S=B(x,2),w=e=>{var n=No(),r=B(R(n),2),i=B(r,2);V(()=>r.disabled=W(h)||W(se)||!W(t).canSend),G(`click`,r,be),G(`click`,i,()=>{I(f,``),I(C,``)}),q(e,n)},ee=e=>{var n=Po();V(()=>n.disabled=W(h)||W(se)||!W(t).canSend||o()),G(`click`,n,()=>A(W(t))),q(e,n)};Y(S,e=>{W(de)&&W(C)===W(t).id?e(w):e(ee,-1)}),M(n),V((e,n)=>{J(i,W(t).status===`uncertain`?`CHECK TRIGGER — outcome uncertain; not retried`:`SCHEDULED FOLLOW-UP`),J(s,`TARGET // ${W(t).session??``}`),J(u,e),J(p,n),J(_,W(t).text),b.disabled=W(h)||!W(l)?.id||W(l).kind!==`prompt`||!!W(l).questions?.length||W(t).status!==`pending`,x.disabled=W(h)||W(t).status===`sending`},[()=>W(t).trigger===`quota`?`After confirmed quota recovery, when idle`:`Not before ${_e(W(t).at)}`,()=>ve(W(t))]),G(`click`,b,()=>re(W(t))),G(`click`,x,()=>ae(W(t).id)),q(e,n)}),q(e,t)},we=N(()=>!a()&&W(l)?.kind!==`approval`&&!W(l)?.questions?.length&&![`APPROVAL NEEDED`,`INPUT NEEDED`].includes(W(oe)||``));Y(xe,e=>{W(we)&&e(Se)});var Te=B(xe,2),Ee=e=>{var r=ps(),o=L(r),s=z(o,!0),c=B(o,2),x=e=>{var t=Io(),n=R(t),r=z(n),i=B(n,6),a=z(i,!0),o=z(B(i,6),!0);Ce(2),V(()=>{J(r,`Your ${W(l).profile.threshold??``}% quota threshold has been reached. Review + the profile for subsequent turns.`),J(a,W(l).profile.current),J(o,W(l).profile.proposed)}),q(e,t)};Y(c,e=>{W(l)?.profile&&!W(se)&&!W(v)&&e(x)});var S=B(c,2),C=e=>{var t=Lo();let n;var r=z(t,!0);V(()=>{n=li(t,1,`svelte-1oupzfc`,null,n,{notice:!W(_),sent:W(_)}),J(r,W(g))}),q(e,t)};Y(S,e=>{W(g)&&e(C)});var D=B(S,2),O=e=>{jo(e,{get lines(){return W(l).fileChanges}})},re=e=>{jo(e,{get lines(){return i()}})},ie=e=>{var t=Ro(),n=R(t),r=z(n,!0),i=z(B(n,2),!0);V(()=>{J(r,W(le)?`EXACT COMMAND`:`LAST OBSERVED COMMAND`),J(i,W(ue))}),q(e,t)},ae=e=>{q(e,zo())};Y(D,e=>{W(l)?.fileChanges?.length&&!W(se)&&!W(v)?e(O):i().length?e(re,1):W(ue)?e(ie,2):a()!==`profile`&&W(oe)===`APPROVAL NEEDED`&&!W(_)&&e(ae,3)});var ce=B(D,2),me=e=>{q(e,Bo())},ve=e=>{q(e,Vo())},ye=e=>{var t=Wo(),n=R(t),r=e=>{var t=Ho(),n=z(t,!0);V(e=>J(n,e),[()=>W(oe)===`WORKING`?`Codex is working — nothing to respond to.`:[`APPROVAL NEEDED`,`INPUT NEEDED`].includes(W(oe)||``)?`Respond in Codex for this request.`:`Nothing needs a response right now.`]),q(e,t)};Y(n,e=>{(W(oe)===`WORKING`||!W(_)&&!W(h))&&e(r)});var i=B(n,2),a=e=>{q(e,Uo())},o=N(()=>[`APPROVAL NEEDED`,`INPUT NEEDED`].includes(W(oe)||``)&&!W(_));Y(i,e=>{W(o)&&e(a)}),q(e,t)},j=e=>{var r=fs(),i=R(r),o=z(i),s=B(i,2),c=L(s),g=z(c,!0),_=B(c,2),v=e=>{var r=Fr();X(R(r),17,()=>W(l).choices||[],Jr,(e,r,i)=>{var o=qo(),s=L(o);Si(s),s.value=s.__value=i;var c=B(s),l=B(c),u=e=>{var t=Go(),n=z(t,!0);V(()=>J(n,W(r).detail)),q(e,t)};Y(l,e=>{W(r).detail&&e(u)});var f=B(l,2),p=e=>{q(e,Ko())};Y(f,e=>{W(r).persistent&&e(p)}),M(o),V(()=>{Z(s,`name`,`decision-`+t.session+`-`+a()),J(c,` ${W(r).label??``} `)}),ki(n,[],s,()=>W(d),e=>I(d,e)),q(e,o)}),q(e,r)},y=e=>{var n=Wo(),r=R(n),i=e=>{var t=es(),n=R(t),r=B(L(n)),i=L(r),a=e=>{var t=Jo();t.value=t.__value=`now`,q(e,t)};Y(i,e=>{W(b)||e(a)});var o=B(i);o.value=o.__value=`quota`;var s=B(o);s.value=s.__value=`delay`;var c=B(s);c.value=c.__value=`at`,M(r),hi(r),M(n);var l=B(n,2),u=e=>{var t=Yo(),n=B(L(t));Si(n),M(t),Di(n,()=>W(ee),e=>I(ee,e)),q(e,t)};Y(l,e=>{W(w)===`delay`&&e(u)});var d=B(l,2),f=e=>{var t=Xo(),n=B(L(t));Si(n),M(t),Di(n,()=>W(T),e=>I(T,e)),q(e,t)};Y(d,e=>{W(w)===`at`&&e(f)});var p=B(d,2),m=e=>{var t=Qo(),n=R(t),r=z(n),i=B(n,2),a=e=>{q(e,Zo())};Y(i,e=>{W(w)!==`quota`&&e(a)});var o=B(i,2);o.textContent=`Timezone: ${Intl.DateTimeFormat().resolvedOptions().timeZone??``}. + One pending request per session; saving replaces it. Memory + only; Codexometer must stay running. Uses session model, + reasoning and speed.`,V(e=>J(r,`Will send: ${e??``}`),[()=>W(w)===`quota`?`when quota is available and this session is idle`:Number.isFinite(W(he))?_e(W(he)):`choose a date and time`]),q(e,t)};Y(p,e=>{W(w)!==`now`&&e(m)});var h=B(p,2),g=e=>{q(e,$o())};Y(h,e=>{W(b)&&e(g)}),gi(r,()=>W(w),e=>I(w,e)),q(e,t)};Y(r,e=>{W(l).questions?.length||e(i)}),X(B(r,2),17,()=>W(fe),Jr,(e,n,r)=>{var i=ss(),o=R(i),s=e=>{{let n=N(()=>W(u)[0]||``);Li(Do(e,{get session(){return t.session},get query(){return W(n)},oncomplete:e=>{I(u,[e],!0),dr().then(()=>W(ne)?.focus())},onchange:()=>{I(u,[``],!0),dr().then(()=>W(ne)?.focus())}}),e=>I(k,e,!0),()=>W(k))}};Y(o,e=>{r===0&&a()!==`profile`&&W(l).kind===`prompt`&&!W(l).questions?.length&&e(s)});var c=B(o,2),d=L(c),f=B(d),p=e=>{var t=ts();Si(t),Di(t,()=>W(u)[r],e=>W(u)[r]=e),q(e,t)},m=e=>{var t=rs(),i=L(t);i.value=i.__value=``,X(B(i),17,()=>W(n).options||[],Jr,(e,t)=>{var n=ns(),r=z(n,!0),i={};V(()=>{J(r,W(t)),i!==(i=W(t))&&(n.value=(n.__value=i)??``)}),q(e,n)}),M(t),hi(t),gi(t,()=>W(u)[r],e=>W(u)[r]=e),q(e,t)},h=e=>{var t=is(),i=R(t);rt(i),Li(i,e=>I(ne,e),()=>W(ne));var a=B(i,2),o=e=>{var t=Ho(),r=z(t);V(e=>J(r,`Suggested answers: ${e??``}`),[()=>W(n).options.join(` · `)]),q(e,t)};Y(a,e=>{W(n).options?.length&&e(o)}),G(`keydown`,i,e=>{W(l)?.kind===`prompt`&&!W(l).questions?.length&&W(k)?.handleKey(e)}),Di(i,()=>W(u)[r],e=>W(u)[r]=e),q(e,t)};Y(f,e=>{W(n).secret?e(p):W(n).freeText?e(h,-1):e(m,1)}),M(c);var g=B(c,2),_=e=>{var t=os(),r=B(L(t),4);X(r,21,()=>W(n).options||[],Jr,(e,t)=>{var n=as(),r=z(n,!0);V(()=>J(r,W(t))),q(e,n)}),M(r),M(t),q(e,t)};Y(g,e=>{W(n).secret&&!W(n).freeText&&e(_)}),V(()=>J(d,`${W(n).text??``} `)),q(e,i)}),q(e,n)};Y(_,e=>{W(l).kind===`approval`||W(l).kind===`profile`?e(v):e(y,-1)}),M(s);var x=B(s,2),S=e=>{var t=ls(),n=R(t),r=e=>{var t=cs(),n=z(t);V(()=>J(n,`TRIGGER // ${W(E)??``}`)),q(e,t)};Y(n,e=>{W(te)&&e(r)});var i=B(n,2),a=z(i),o=B(i,2),s=z(o),c=B(o,2);V(e=>{J(a,`Check the target and ${W(l).kind===`profile`?`profile above`:W(l).kind===`approval`?W(l).fileChanges?.length?`proposed file changes above`:`exact command and permission scope`:`message above`}. ${W(te)?`This schedules a follow-up that will send automatically when eligible`:`This will send to Codex`}; it may start work using your quota. + Confirmation expires in ${e??``}s.`),o.disabled=W(h)||W(se),J(s,`CONFIRM ${(W(l).kind===`approval`||W(l).kind===`profile`?W(l).choices?.[W(d)]?.label:W(te)?W(b)?`SAVE CHANGES`:`SCHEDULE`:`SEND`)??``}`)},[()=>Math.max(0,Math.ceil((W(p)-W(m))/1e3))]),G(`click`,o,be),G(`click`,c,()=>{I(f,``)}),q(e,t)},C=e=>{var t=us(),n=R(t),r=e=>{var t=Mo(),n=z(t,!0);V(()=>J(n,W(ge))),q(e,t)};Y(n,e=>{W(ge)&&e(r)});var i=B(n,2),a=z(i,!0);V(()=>{i.disabled=W(h)||W(se)||!W(pe)||!!W(ge),J(a,W(h)?`SENDING…`:W(b)?`REVIEW CHANGES`:`REVIEW BEFORE SENDING`)}),G(`click`,i,()=>A()),q(e,t)};Y(x,e=>{W(de)?e(S):e(C,-1)});var D=B(x,2),O=e=>{var t=ds();G(`click`,t,()=>{I(b,!1),I(f,``),I(u,[],!0)}),q(e,t)};Y(D,e=>{W(b)&&e(O)}),V(()=>{J(o,`TARGET // ${W(l).thread??``} // ${(W(l).directory||`Directory unavailable`)??``}`),s.disabled=W(h)||W(de)||W(se),J(g,W(l).kind===`approval`||W(l).kind===`profile`?`Choose a decision`:W(b)?`EDIT SCHEDULED FOLLOW-UP`:`Reply to this session`)}),q(e,r)};Y(ce,e=>{W(se)||W(v)?e(me):W(l)?W(l).id?W(y)!==W(l).id&&e(j,3):e(ye,2):e(ve,1)}),M(r),V(()=>J(s,a()===`profile`?`QUOTA THRESHOLD`:`SESSION CONTROL // EXPERIMENTAL`)),q(e,r)};Y(Te,e=>{!W(ce)&&(!W(O)||W(b)||a()||W(l)?.kind!==`prompt`||W(l).questions?.length)&&e(Ee)}),q(e,j),Ke()}wr([`click`,`keydown`]);var hs=K(` `,1),gs=K(`
      `),_s=K(`

      STATUS LINE // CODEXOMETER

      Choose multiple fields and their order. These display preferences do not + change Codex CLI configuration. Missing values are omitted.

      `),vs=K(`

      `,1);function ys(e,t){Ge(t,!0);let n=Q(t,`stale`,3,!1),r=F(Zt([])),i=F(!1),a=N(()=>$.data?.statusLineFields||[]),o=e=>[...new Set(e??W(a).filter(e=>e.default).map(e=>e.id))].filter(e=>W(a).some(t=>t.id===e)),s=e=>o(e).map(e=>e===`run-state`&&(n()||!$.connected||$.data?.sessionsError)?`STALE`:t.session.statusLine?.[e]).filter(Boolean).join(` · `),c=N(()=>[...W(r).map(e=>W(a).find(t=>t.id===e)).filter(Boolean),...W(a).filter(e=>!W(r).includes(e.id))]);bn(()=>{oa.open&&!W(i)?(I(r,o(aa.statusLine),!0),I(i,!0)):oa.open||I(i,!1)});function l(e,t){I(r,t?[...W(r),e]:W(r).filter(t=>t!==e),!0)}function u(e,t){let n=W(r).indexOf(e),i=n+t;if(n<0||i<0||i>=W(r).length)return;let a=[...W(r)];[a[n],a[i]]=[a[i],a[n]],I(r,a,!0)}var d=vs(),f=R(d),p=e=>{var t=_s(),n=B(L(t),4);X(n,21,()=>W(c),e=>e.id,(e,t)=>{var n=gs(),i=L(n),a=L(i);Si(a);var o=B(a,1,!0),s=z(B(o),!0);M(i);var c=B(i,2),d=e=>{var n=hs(),i=R(n),a=B(i,2);V((e,n)=>{Z(i,`aria-label`,`Move `+W(t).label+` earlier`),i.disabled=e,Z(a,`aria-label`,`Move `+W(t).label+` later`),a.disabled=n},[()=>W(r).indexOf(W(t).id)===0,()=>W(r).indexOf(W(t).id)===W(r).length-1]),G(`click`,i,()=>u(W(t).id,-1)),G(`click`,a,()=>u(W(t).id,1)),q(e,n)},f=N(()=>W(r).includes(W(t).id));Y(c,e=>{W(f)&&e(d)}),M(n),V(e=>{Z(i,`title`,W(t).help),Ci(a,e),J(o,W(t).label),J(s,W(t).help)},[()=>W(r).includes(W(t).id)]),G(`change`,a,e=>l(W(t).id,e.currentTarget.checked)),q(e,n)}),M(n);var i=B(n,2),a=z(i),o=B(i,2),d=B(o,2);M(t),V(e=>J(a,`Preview // ${e??``}`),[()=>s(W(r))||`—`]),G(`click`,o,()=>{aa.statusLine=[...W(r)],sa(),oa.open=!1}),G(`click`,d,()=>{oa.open=!1}),q(e,t)};Y(f,e=>{oa.open&&e(p)});var m=B(f,2),h=L(m),g=z(h,!0),_=B(h,2);M(m),V((e,t)=>{Z(h,`title`,e),J(g,t)},[()=>s(aa.statusLine),()=>s(aa.statusLine)]),G(`click`,_,()=>{oa.open=!oa.open}),q(e,d),Ke()}wr([`change`,`click`]);var bs=K(`
      `);function xs(e,t){Ge(t,!0);let n=Q(t,`active`,3,!1),r=N(()=>t.session.text||t.session.contextKind===`LAST ACTIVITY`&&t.session.workingCommand||``),i=N(()=>[`LAST REPLY`,`LAST ACTIVITY`].includes(t.session.contextKind)&&!!W(r).trim()),a=F(!1),o=F(``),s=F(!1);bn(()=>{if(!W(s))return;let e=setTimeout(()=>{I(s,!1)},150);return()=>clearTimeout(e)}),bn(()=>{W(r),t.session.status,I(o,``)});async function c(){if(!W(i)||W(a))return;let e=W(r);I(a,!0),I(s,!0),I(o,``);try{await navigator.clipboard.writeText(e),W(r)===e&&I(o,`Copied.`)}catch{W(r)===e&&I(o,`Clipboard unavailable. Select the visible text and copy it manually.`)}finally{I(a,!1)}}function l(e){!n()||!W(i)||e.defaultPrevented||e.repeat||e.ctrlKey||e.metaKey||e.altKey||e.key.toLowerCase()!==`c`||e.target instanceof Element&&e.target.closest(`input, textarea, select, [contenteditable]`)||(e.preventDefault(),c())}var u=Fr();Cr(`keydown`,en,l);var d=R(u),f=e=>{var t=bs(),n=L(t),r=z(n,!0),i=B(n,2);let l;M(t),V(()=>{J(r,W(o)),i.disabled=W(a),l=li(i,1,`svelte-543j00`,null,l,{flashed:W(s)})}),G(`click`,i,c),q(e,t)};Y(d,e=>{W(i)&&e(f)}),q(e,u),Ke()}wr([`click`]);var Ss=K(`

       
      `,1),Cs=K(`

      LATEST GUIDANCE

       
      `,1),ws=K(`

      CONTEXT

       

      `,1),Ts=K(`

      `),Es=K(`

      `),Ds=K(`
       
      `),Os=K(`

       
      `,1),ks=K(`
       
      `),As=K(`

      Command unavailable from this observation. Open Codex to inspect the + request.

      `),js=K(`

      `,1),Ms=K(`

      `),Ns=K(` `,1),Ps=K(`
      `),Fs=K(`

      Session connection or refresh unavailable. Context and telemetry may be + stale.

      `),Is=K(`

      Some quota profile checks are unavailable. Only sessions with freshly + verified quota and settings can be updated; previous outcome notices remain + visible.

      `),Ls=K(` `),Rs=K(` `),zs=K(` `),Bs=K(``),Vs=K(`

      Read only — reply or approve in Codex.

      `),Hs=K(`

      `),Us=K(`QUOTA THRESHOLD // REVIEW PROFILE ↗`),Ws=K(`
      `),Gs=K(`
      `),Ks=K(`

      This session is no longer in the current observation. Return to sessions.

      `),qs=K(`

      `),Js=K(``),Ys=K(`

      `),Xs=K(` `),Zs=K(`

      `),Qs=K(`QUOTA THRESHOLD // REVIEW PROFILE ↗`),$s=K(` `,1),ec=K(`

      `),tc=K(`

      TOKEN ACTIVITY // 30 SECOND SAMPLES

      `),nc=K(`

      TOKENS

      FULL DETAIL →
      `),rc=K(`

      No locally observed sessions yet. Keep Codex running alongside + Codexometer.

      `),ic=K(`

      ↑ ↓ SELECT SESSION // ← LESS DETAIL // → MORE DETAIL // ESC BACK

      `,1),ac=K(`

      SESSION TOTALS

      `,1);function oc(e,t){Ge(t,!0);let n=(e,t=f,n,r)=>{let i=ht(()=>g(n?.(),!0)),a=ht(()=>g(r?.(),!1));var o=Ns(),s=R(o),c=e=>{var n=Ss(),r=R(n),i=z(r,!0),a=z(B(r,2),!0);V(()=>{J(i,t().contextKind===`LAST REPLY`&&!t().streaming?`TASK`:`CURRENT TASK`),J(a,t().currentTask)}),q(e,n)};Y(s,e=>{t().currentTask&&e(c)});var l=B(s,2),u=e=>{var n=Cs(),r=z(B(R(n),2),!0);V(()=>J(r,t().latestGuidance)),q(e,n)};Y(l,e=>{t().latestGuidance&&e(u)});var d=B(l,2),p=e=>{var n=ws(),r=z(B(R(n),2),!0);Ce(2),V(()=>J(r,t().approvalContext)),q(e,n)};Y(d,e=>{W(a)&&t().contextKind===`APPROVAL REQUEST`&&t().approvalContext&&e(p)});var m=B(d,2),_=e=>{var n=Ts();let r;var i=z(n,!0);V(e=>{r=li(n,1,`attention-note`,null,r,{inferred:t().status===`CHECK SESSION`}),J(i,e)},[()=>C(t())]),q(e,n)},v=N(()=>C(t())&&(!W(a)||W(h)||t().status===`CHECK SESSION`));Y(m,e=>{W(v)&&e(_)});var y=B(m,2),b=e=>{var n=Es(),r=z(n,!0);V(e=>J(r,e),[()=>w(t())]),q(e,n)};Y(y,e=>{W(i)&&e(b)});var x=B(y,2),S=e=>{var n=Ds(),r=z(n,!0);V(()=>J(r,t().text||`No session context available.`)),q(e,n)};Y(x,e=>{(t().text||!t().workingCommand)&&e(S)});var ee=B(x,2),T=e=>{var n=Os(),r=B(R(n),2),i=L(r),a=B(i),o=e=>{var n=Pr();V(()=>J(n,`// +${(t().runningCommands||0)-+(t().commandStatus===`running`)} RUNNING`)),q(e,n)};Y(a,e=>{!W(h)&&(t().runningCommands||0)>+(t().commandStatus===`running`)&&e(o)});var s=B(a,2),c=e=>{q(e,Pr(`+`))};Y(s,e=>{t().runningLimited&&e(c)}),M(r);var l=z(B(r,2),!0);V(e=>{J(i,`COMMAND // ${e??``} `),J(l,t().workingCommand)},[()=>W(h)?`UNKNOWN`:(t().commandStatus||`unknown`).toUpperCase()]),q(e,n)};Y(ee,e=>{t().workingCommand&&e(T)});var te=B(ee,2),E=e=>{jo(e,{get lines(){return t().fileChanges}})},D=e=>{var n=js(),r=B(R(n),2),i=z(r,!0),a=B(r,2),o=e=>{var n=ks(),r=z(n,!0);V(()=>J(r,t().command)),q(e,n)},s=e=>{q(e,As())};Y(a,e=>{t().command?e(o):e(s,-1)}),V(()=>J(i,W(h)||t().status!==`APPROVAL NEEDED`?`LAST OBSERVED COMMAND`:$.data?.control?`COMMAND REQUEST`:`COMMAND TO APPROVE IN CODEX`)),q(e,n)};Y(te,e=>{(!W(a)||!$.data?.control)&&t().fileChanges?.length?e(E):(!W(a)||!$.data?.control)&&(t().command||t().status===`APPROVAL NEEDED`)&&e(D,1)});var O=B(te,2),ne=e=>{var n=Ms(),r=z(n);V(()=>J(r,`CONTEXT SOURCE // ${(t().source||`LOCAL`)??``} // OBSERVATION`)),q(e,n)};Y(O,e=>{W(a)||e(ne)}),q(e,o)},r=Q(t,`params`,19,()=>({})),i=N(()=>$.data?.sessions||[]),a=N(()=>$.data?.control&&$.data.profiles||[]),o=N(()=>($.data?.triggers||[]).filter(e=>W(i).some(t=>t.id===e.session)));function s(e,t){if(r().id&&r().id!==t&&W(l)){e.preventDefault();return}b(t)}function c(e){return[`TURN COMPLETE`,`IDLE`,`ACTIVE`].includes(e.status)&&W(o).some(t=>t.session===e.id)?`TRIGGER SET`:e.status}let l=F(!1);function u(e,t){if(r().id&&r().id!==t&&W(l)){e.preventDefault();return}b(t)}bn(()=>{let e=r().id;e&&mr(()=>{aa.selected=e,da(e,2)})});let d=N(()=>W(i).find(e=>e.id===r().id)),p=N(()=>new URLSearchParams(Yi.querystring).get(`review`)===`profile`&&W(a).some(e=>e.session===r().id&&e.pending)),m=N(()=>W(i).some(e=>e.id===aa.selected)?aa.selected:W(i)[0]?.id),h=N(()=>!$.connected||!!$.data?.sessionsError),v=N(()=>[[`OBSERVED TOKENS`,fa(W(i).reduce((e,t)=>e+t.tokens,0))],[`LISTED SESSIONS`,fa(W(i).length)],...[[`WORKING`,`WORKING`],[`AWAITING APPROVAL`,`APPROVAL NEEDED`],[`AWAITING INPUT`,`INPUT NEEDED`],[`CHECK · INFERRED`,`CHECK SESSION`]].map(([e,t])=>[e,W(h)?`—`:fa(W(i).filter(e=>e.status===t).length)])]),y=N(()=>W(i).filter(e=>[`INPUT NEEDED`,`APPROVAL NEEDED`,`CHECK SESSION`].includes(e.status)));function b(e){aa.selected=e,dr().then(()=>document.querySelector(`.session-row.selected`)?.scrollIntoView({block:`nearest`}))}function x(e,t){if(b(e),r().id){t<0&&(da(e,2),location.hash=`/sessions`);return}let n=la(e)+t;n>2?location.hash=`/sessions/`+encodeURIComponent(e):da(e,n)}function S(e){let t=e.target;if(!(e.altKey||e.ctrlKey||e.metaKey||e.shiftKey||t.closest(`input, textarea, select, [contenteditable="true"]`))){if(e.key===`Escape`&&r().id)e.preventDefault(),x(r().id,-1);else if([`ArrowLeft`,`ArrowRight`].includes(e.key)&&(r().id||W(m)))e.preventDefault(),x(r().id||W(m),e.key===`ArrowLeft`?-1:1);else if(!r().id&&[`ArrowUp`,`ArrowDown`].includes(e.key)&&W(i).length){e.preventDefault();let t=W(i).findIndex(e=>e.id===W(m));b(W(i)[Math.max(0,Math.min(W(i).length-1,t+(e.key===`ArrowUp`?-1:1)))].id)}}}function C(e){return W(h)?`Last observation only — refresh unavailable. Check Codex for current state.`:e.status===`CHECK SESSION`?`INFERRED INACTIVITY — a quiet session, not a confirmed input or approval request. Local tools may still be running.`:$.data?.control&&[`APPROVAL NEEDED`,`INPUT NEEDED`].includes(e.status)?`Open full detail for supported live controls; otherwise reply in Codex.`:e.status===`APPROVAL NEEDED`?`OBSERVED APPROVAL SIGNAL — approve or decline in Codex.`:e.status===`INPUT NEEDED`?`OBSERVED INPUT SIGNAL — reply in Codex.`:e.status===`TURN COMPLETE`?`OBSERVED TURN COMPLETION — informational, not an approval request.`:``}function w(e){return e.streaming&&e.contextKind===`LAST REPLY`?`REPLY // STREAMING`:e.contextKind||`LAST ACTIVITY`}var ee=ac();Cr(`keydown`,en,S);var T=R(ee),te=z(B(L(T),2));M(T);var E=B(T,2);let D;X(E,21,()=>W(v),Jr,(e,t)=>{var n=N(()=>_(W(t),2));let r=()=>W(n)[0],i=()=>W(n)[1];var a=Ps(),o=L(a),s=z(o,!0),c=z(B(o,2),!0);M(a),V(()=>{J(s,r()),J(c,i())}),q(e,a)}),M(E);var O=B(E,2),ne=z(O),k=B(O,2),re=e=>{q(e,Fs())};Y(k,e=>{W(h)&&e(re)});var ie=B(k,2),ae=e=>{q(e,Is())};Y(ie,e=>{$.data?.profileError&&e(ae)});var oe=B(ie,2),se=e=>{var t=Bs(),n=L(t);X(n,17,()=>W(y),Jr,(e,t)=>{var n=Ls();let r;var i=z(n);V((e,a)=>{r=li(n,1,`button`,null,r,{approval:W(t).status===`APPROVAL NEEDED`}),Z(n,`href`,e),J(i,`${W(t).status??``} + ${a??``} // ${(W(t).name||W(t).directory||W(t).id)??``}`)},[()=>`#/sessions/`+encodeURIComponent(W(t).id),()=>Array.from(W(t).id).slice(-5).join(``).toUpperCase()]),G(`click`,n,()=>b(W(t).id)),q(e,n)});var c=B(n,2);X(c,17,()=>W(a).filter(e=>e.pending&&W(i).some(t=>t.id===e.session)),Jr,(e,t)=>{var n=Rs(),a=z(n);V((e,i,o)=>{Z(n,`title`,r().id&&r().id!==W(t).session&&W(l)?`Finish sending or clear your current draft before switching sessions.`:`Review this session’s quota threshold`),Z(n,`href`,e),J(a,`QUOTA THRESHOLD ${i??``} // ${o??``}`)},[()=>`#/sessions/`+encodeURIComponent(W(t).session)+`?review=profile`,()=>Array.from(W(t).session).slice(-5).join(``).toUpperCase(),()=>W(i).find(e=>e.id===W(t).session)?.name||W(i).find(e=>e.id===W(t).session)?.directory||W(t).session]),G(`click`,n,e=>u(e,W(t).session)),q(e,n)}),X(B(c,2),17,()=>W(o),Jr,(e,t)=>{var n=zs(),r=z(n);V((e,t,i)=>{Z(n,`href`,e),J(r,`TRIGGER SET ${t??``} // ${i??``}`)},[()=>`#/sessions/`+encodeURIComponent(W(t).session),()=>Array.from(W(t).session).slice(-5).join(``).toUpperCase(),()=>W(i).find(e=>e.id===W(t).session)?.name||W(i).find(e=>e.id===W(t).session)?.directory]),G(`click`,n,e=>s(e,W(t).session)),q(e,n)}),M(t),q(e,t)},ce=N(()=>(W(y).length||W(a).some(e=>e.pending)||W(o).length)&&!W(h));Y(oe,e=>{W(ce)&&e(se)});var le=B(oe,2),ue=e=>{var t=Fr(),i=R(t),o=e=>{var t=Gs(),i=L(t),o=L(i),s=L(o);let u;var f=B(s);M(o);var m=B(o,2);M(i);var g=B(i,2),_=e=>{var t=Ms(),n=z(t,!0);V(()=>J(n,W(d).directory)),q(e,t)};Y(g,e=>{W(d).name&&W(d).directory&&e(_)});var v=B(g,2),y=z(v),x=B(v,2);let S;var C=L(x),w=L(C);n(w,()=>W(d),()=>!0,()=>!0),M(C);var ee=B(C,2),T=e=>{var t=Fr();qr(R(t),()=>W(d).id,e=>{{let t=N(()=>new URLSearchParams(Yi.querystring).get(`schedule`)===`edit`),n=N(()=>W(d).fileChanges||[]);ms(e,{get session(){return W(d).id},get openScheduler(){return W(t)},get observedCommand(){return W(d).command},get observedFileChanges(){return W(n)},get suspended(){return W(p)},onProtectedChange:e=>{I(l,e,!0)}})}}),q(e,t)},te=e=>{q(e,Vs())};Y(ee,e=>{$.data?.control?e(T):e(te,-1)}),M(x);var E=B(x,2);X(E,17,()=>W(a).filter(e=>e.session===W(d).id),e=>e.session,(e,t)=>{var n=Ws(),r=L(n),i=e=>{var n=Hs(),r=z(n,!0);V(()=>J(r,W(t).notice)),q(e,n)};Y(r,e=>{W(t).notice&&e(i)});var a=B(r,2),o=e=>{ms(e,{get session(){return W(d).id},review:`profile`})},s=e=>{var t=Us();V(e=>Z(t,`href`,e),[()=>`#/sessions/`+encodeURIComponent(W(d).id)+`?review=profile`]),q(e,t)};Y(a,e=>{W(t).pending&&W(p)?e(o):W(t).pending&&e(s,1)}),M(n),V(()=>Z(n,`id`,`quota-profile-`+W(d).id)),q(e,n)});var D=B(E,2),O=e=>{var t=Fr();qr(R(t),()=>W(d).id,e=>{xs(e,{get session(){return W(d)},active:!0})}),q(e,t)};Y(D,e=>{W(p)||e(O)}),qr(B(D,2),()=>W(d).id,e=>{ys(e,{get session(){return W(d)},get stale(){return W(h)}})}),M(t),V((e,t)=>{u=li(s,1,`lamp lit`,null,u,{working:W(d).status===`WORKING`&&!W(h)}),J(f,`${e??``} // ${(W(d).name||W(d).directory)??``}`),J(y,`${t??``} TOKENS // ${W(d).id??``} // CONTEXT SOURCE // ${(W(d).source||`LOCAL`)??``}`),S=di(x,``,S,{display:W(p)?`none`:void 0})},[()=>W(h)?`STALE`:W(p)?`QUOTA THRESHOLD`:c(W(d)),()=>fa(W(d).tokens)]),G(`click`,m,()=>{b(r().id),da(r().id,2)}),q(e,t)},s=e=>{q(e,Ks())};Y(i,e=>{W(d)?e(o):e(s,-1)}),q(e,t)},de=e=>{var t=ic(),r=R(t),o=B(L(r),2),s=L(o),l=B(s,2);M(o),M(r);var d=B(r,2);X(d,17,()=>W(i),e=>e.id,(e,t)=>{let r=N(()=>la(W(t).id));var i=nc();let o;var s=L(i),l=L(s),d=e=>{var n=qs(),r=L(n),i=z(r);M(n),V(e=>{Z(r,`aria-pressed`,W(m)===W(t).id),J(i,`${e??``} // ${W(t).name??``}`)},[()=>Array.from(W(t).id).slice(-5).join(``).toUpperCase()]),G(`click`,r,()=>b(W(t).id)),q(e,n)};Y(l,e=>{W(t).name&&e(d)});var f=B(l,2),p=L(f),g=L(p);let _;var v=B(g,1,!0);M(p),M(f);var y=B(f,2),S=e=>{var n=Js(),r=z(n,!0);V(()=>{Z(n,`aria-pressed`,W(m)===W(t).id),J(r,W(t).directory||W(t).id)}),G(`click`,n,()=>b(W(t).id)),q(e,n)};Y(y,e=>{W(t).name||e(S)});var C=B(y,2),ee=L(C);Ce(),M(C);var T=B(C,2),te=e=>{var n=Ms(),r=z(n,!0);V(()=>J(r,W(t).directory)),q(e,n)};Y(T,e=>{W(t).name&&W(t).directory&&e(te)});var E=B(T,2),D=z(E),O=B(E,2),ne=z(O),k=B(O,2),re=L(k),ie=B(re,2),ae=z(ie,!0),oe=B(ie,2);M(k);var se=B(k,2),ce=B(se,2),le=e=>{var n=Ys(),r=z(n,!0);V(()=>J(r,W(t).status===`CHECK SESSION`?`INFERRED INACTIVITY`:$.data?.control?`OPEN FULL DETAIL OR REPLY IN CODEX`:W(t).status===`APPROVAL NEEDED`?`APPROVE OR DECLINE IN CODEX`:`REPLY IN CODEX`)),q(e,n)},ue=N(()=>!W(h)&&[`APPROVAL NEEDED`,`INPUT NEEDED`,`CHECK SESSION`].includes(W(t).status));Y(ce,e=>{W(ue)&&e(le)}),M(s);var de=B(s,2),fe=e=>{var r=ec(),i=L(r),o=L(i),s=z(o,!0),c=B(o,2),l=e=>{var n=Xs(),r=z(n,!0);V(e=>{Z(n,`href`,e),J(r,$.data?.control?`REVIEW IN FULL DETAIL ↗`:`APPROVAL IN CODEX ↗`)},[()=>`#/sessions/`+encodeURIComponent(W(t).id)]),q(e,n)};Y(c,e=>{!W(h)&&W(t).status===`APPROVAL NEEDED`&&e(l)}),M(i);var d=B(i,2);n(d,()=>W(t),()=>!1);var f=B(d,2);X(f,17,()=>W(a).filter(e=>e.session===W(t).id),Jr,(e,n)=>{var r=$s(),i=R(r),a=e=>{var t=Zs(),r=z(t,!0);V(()=>J(r,W(n).notice)),q(e,t)};Y(i,e=>{W(n).notice&&e(a)});var o=B(i,2),s=e=>{var n=Qs();V(e=>Z(n,`href`,e),[()=>`#/sessions/`+encodeURIComponent(W(t).id)+`?review=profile`]),G(`click`,n,e=>u(e,W(t).id)),q(e,n)};Y(o,e=>{W(n).pending&&e(s)}),q(e,r)});var p=B(f,2);{let e=N(()=>W(m)===W(t).id);xs(p,{get session(){return W(t)},get active(){return W(e)}})}M(r),V(e=>J(s,e),[()=>w(W(t))]),q(e,r)};Y(de,e=>{W(r)>0&&e(fe)});var pe=B(de,2),me=e=>{var n=tc(),r=B(L(n),2);{let e=N(()=>(W(t).samples||[]).map(e=>e.tokens));no(r,{get values(){return W(e)},capacity:120})}var i=z(B(r,2));M(n),V(()=>J(i,`LAST ${(W(t).samples?.length||0)??``} SAMPLES // AUTO SCALE`)),q(e,n)};Y(pe,e=>{W(r)<2&&e(me)}),M(i),V((e,n,a,s,c)=>{o=li(i,1,`session-row`,null,o,{selected:W(m)===W(t).id,wide:W(r)===2,split:W(r)===1}),Z(i,`aria-label`,`Session `+(W(t).name||W(t).directory||W(t).id)),Z(p,`href`,e),_=li(g,1,`lamp lit`,null,_,{working:W(t).status===`WORKING`&&!W(h)}),J(v,n),J(ee,`${a??``} `),J(D,`${W(t).agents??``} LINKED AGENTS`),J(ne,`ACTIVE // ${s??``}`),re.disabled=W(r)===0,Z(ie,`aria-expanded`,W(r)>0),J(ae,W(r)?`HIDE DETAIL`:`SHOW DETAIL`),Z(se,`href`,c)},[()=>`#/sessions/`+encodeURIComponent(W(t).id),()=>W(h)?`STALE`:c(W(t)),()=>fa(W(t).tokens),()=>pa(W(t).activity),()=>`#/sessions/`+encodeURIComponent(W(t).id)]),G(`click`,p,()=>b(W(t).id)),G(`click`,re,()=>x(W(t).id,-1)),G(`click`,ie,()=>{b(W(t).id),da(W(t).id,+!W(r))}),G(`click`,oe,()=>x(W(t).id,1)),G(`click`,se,()=>b(W(t).id)),q(e,i)});var f=B(d,2),p=e=>{q(e,rc())};Y(f,e=>{W(i).length||e(p)}),G(`click`,s,()=>ua(1)),G(`click`,l,()=>ua(0)),q(e,t)};Y(le,e=>{r().id?e(ue):e(de,-1)}),V(e=>{J(te,`OBSERVED ${e??``}`),D=li(E,1,`session-totals`,null,D,{stale:W(h)}),J(ne,`${W(h)?`LAST KNOWN TOTALS — live state counts unavailable. `:``}Tokens + observed since this server started for currently listed sessions; linked + agents are already included. Totals can decrease when a session leaves the + list. History samples every 30 seconds. Not account-wide totals.`)},[()=>pa($.data?.sessionsAt)]),q(e,ee),Ke()}wr([`click`]);var sc=K(`

      `),cc=K(`

      Relative usage/credits are not tokens or quota percentages. Groupings + describe the same usage, not additional consumption.

      `,1),lc=K(`// APPROXIMATE`),uc=K(`

      Partial accounting: totals may still change.

      `),dc=K(`

      Percentage of this period's historical allowance, not today's limit. May + exceed 100%. This is not a manual/automatic reset log.

      `,1),fc=K(`

      `,1),pc=K(`

      This optional account report is unavailable. Token history remains + available; missing data does not mean zero usage.

      `),mc=K(`
      `),hc=K(`
      `),gc=K(`

      No usage reported for this day.

      `),_c=K(`

      Selected breakdown unavailable; missing does not mean zero.

      `),vc=K(`

      `,1);function yc(e,t){Ge(t,!0);let n=F(`surface`),r=F(0),i=N(()=>t.reports?.daily),a=N(()=>t.reports?.plan),o=N(()=>t.mode===`breakdown`?W(i)?.days.length||0:W(a)?.periods.length||0),s=N(()=>Math.min(W(r),Math.max(0,W(o)-1))),c=N(()=>W(i)?.days[(W(i)?.days.length||0)-1-W(s)]),l=N(()=>W(a)?.periods[W(s)]),u=N(()=>t.mode===`breakdown`?W(i)?.units||``:`%`),d=N(()=>{let e=Object.create(null);if(t.mode===`breakdown`)e=W(c)?.groups[W(n)]||{};else{let t=W(n)===`feature`?`thread_source`:W(n)===`task start`?`turn_trigger`:W(n);for(let n of W(l)?.breakdowns||[])if(n.dimension===t)for(let t of n.rows)e[t.key]=(e[t.key]||0)+t.basis_points/100}return Object.entries(e).sort((e,t)=>t[1]-e[1]||e[0].localeCompare(t[0]))}),f=N(()=>t.mode===`breakdown`?W(c)?.total:W(l)?.used_basis_points==null?null:W(l).used_basis_points/100),p=N(()=>Math.max(1,W(f)||0,...W(d).map(([,e])=>e))),m=e=>e==null?`UNKNOWN`:e.toLocaleString(`en-GB`,{maximumFractionDigits:2});var h=vc(),g=R(h),v=L(g),y=B(L(v)),b=L(y);b.value=b.__value=`surface`;var x=B(b);x.value=x.__value=`model`;var S=B(x);S.value=S.__value=`feature`;var C=B(S);C.value=C.__value=`task start`,M(y),hi(y),M(v);var w=B(v,2),ee=B(w,2),T=z(B(ee,2),!0);M(g);var te=B(g,2),E=L(te),D=z(E,!0),O=B(E,2),ne=z(O),k=B(O,2),re=e=>{var t=cc(),n=R(t),r=z(n),a=B(n,2),o=e=>{var t=sc(),n=z(t);V(()=>J(n,`Data as of ${W(i).dataAsOf??``}`)),q(e,t)};Y(a,e=>{W(i).dataAsOf&&e(o)});var s=z(B(a,2));Ce(2),V((e,t)=>{J(r,`Latest fetch ${e??``} // queried ${W(i).from??``} → ${W(i).through??``}. + Reports are fetched from OpenAI and are not saved between runs.`),J(s,`${(W(c)?.date||`No daily buckets reported`)??``} // ${t??``} + ${W(u)??``}`)},[()=>pa(W(i).fetchedAt),()=>m(W(f))]),q(e,t)},ie=e=>{var t=fc(),n=R(t),r=L(n);let i;var o=z(r,!0),s=B(r),c=e=>{q(e,lc())};Y(s,e=>{W(a).approximate&&e(c)});var u=B(s);M(n);var d=B(n,2),p=L(d),h=B(p),g=e=>{var t=Pr();V(()=>J(t,`// Boundary tolerance ${W(a).boundary_tolerance_seconds??``}s`)),q(e,t)};Y(h,e=>{W(a).boundary_tolerance_seconds!=null&&e(g)}),M(d);var _=B(d,2),v=e=>{var t=dc(),n=R(t),r=L(n),i=B(r);let a;var o=z(i);M(n);var s=B(n,2),c=z(s),u=B(s,2),d=e=>{q(e,uc())};Y(u,e=>{W(l).accounting_complete||e(d)}),Ce(2),V(e=>{J(r,`${W(l).window_minutes??``} MIN // ${W(l).plan_type??``} // `),a=li(i,1,`svelte-1fbui7h`,null,a,{muted:W(f)==null,"quota-near":W(f)!=null&&W(f)>=80&&W(f)<100,"quota-full":W(f)!=null&&W(f)>=100}),J(o,`USED ${e??``}${W(f)==null?``:`%`}`),J(c,`${W(l).starts_at??``} → ${W(l).ends_at??``}`)},[()=>m(W(f))]),q(e,t)};Y(_,e=>{W(l)&&e(v)}),V(e=>{i=li(r,1,`svelte-1fbui7h`,null,i,{"quality-warning":!W(a).coverage_complete}),J(o,W(a).coverage_complete?`Reported range complete`:`Partial coverage`),J(u,` // Latest fetch ${e??``}`),J(p,`Coverage starts ${(W(a).coverage_start||`unknown`)??``} // Data as of ${(W(a).data_as_of||`unknown`)??``}`)},[()=>pa(W(a).fetchedAt)]),q(e,t)},ae=e=>{q(e,pc())};Y(k,e=>{t.mode===`breakdown`&&W(i)?e(re):t.mode===`periods`&&W(a)?e(ie,1):e(ae,-1)});var oe=B(k,2),se=e=>{var n=hc(),r=L(n),i=z(B(L(r),2),!0);M(r),X(B(r,2),17,()=>W(d),Jr,(e,n,r)=>{var i=N(()=>_(W(n),2));let a=()=>W(i)[0],o=()=>W(i)[1];var s=mc();let c;var l=L(s),u=z(l,!0),d=B(l,2),f=L(d);let h;M(d);var g=z(B(d,2));M(s),V((e,n,i)=>{c=li(s,1,`report-row svelte-1fbui7h`,null,c,{unknown:e,alternate:r%2==1}),Z(l,`title`,a()),J(u,a()),h=di(f,``,h,{width:n}),J(g,`${i??``}${t.mode===`periods`?`%`:``}`)},[()=>a().trim().toLowerCase()===`unknown`,()=>`${Math.max(0,100*o()/W(p))}%`,()=>m(o())]),q(e,s)}),M(n),V(()=>J(i,t.mode===`breakdown`?W(u):`QUOTA USED`)),q(e,n)},ce=e=>{q(e,gc())},le=e=>{q(e,_c())};Y(oe,e=>{W(d).length?e(se):t.mode===`breakdown`&&W(c)?.total===0&&W(c)?.groups[W(n)]!=null?e(ce,1):(W(i)&&t.mode===`breakdown`||W(a)&&t.mode===`periods`)&&e(le,2)}),M(te),V(()=>{w.disabled=W(s)>=W(o)-1,ee.disabled=W(s)===0,J(T,W(o)?`${W(s)+1} / ${W(o)}`:`NO DATA`),J(D,t.mode===`breakdown`?`DAILY BREAKDOWN`:`QUOTA WINDOWS`),J(ne,`${(t.mode===`breakdown`?t.reports?.dailyStatus||`UNAVAILABLE`:t.reports?.planStatus||`UNAVAILABLE`)??``} // UTC`)}),gi(y,()=>W(n),e=>I(n,e)),G(`click`,w,()=>I(r,W(s)+1)),G(`click`,ee,()=>I(r,W(s)-1)),q(e,h),Ke()}wr([`click`]);var bc=864e5;function xc(e,t){let n=new Date(e);n.setUTCDate(1),n.setUTCMonth(n.getUTCMonth()+t);let r=new Date(Date.UTC(n.getUTCFullYear(),n.getUTCMonth()+1,0)).getUTCDate();return n.setUTCDate(Math.min(e.getUTCDate(),r)),n}function Sc(e,t,n){let r=new Date(Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()));for(let e=0;;e++){let i=xc(r,-t),a=new Date(i.getTime()+bc);if(e===n)return{start:a,end:r};r=i}}var Cc=K(``),wc=K(``,1),Tc=K(`

      History refresh failed. Any displayed history is the last successful + observation.

      `),Ec=K(`

      Token history unavailable; other reports may still be available.

      `),Dc=K(``),Oc=K(`
      `),kc=K(`

      LESS ░ ▒ ▓ █ MORE // HOVER FOR DATE AND TOKENS

      `,1),Ac=K(`
      `,1),jc=K(` `),Mc=K(`

      LIFETIME TOKENS

      PEAK DAY

      CURRENT STREAK

      DAYS

      LONGEST TURN

      SECONDS

      LONGEST STREAK

      DAYS

      OPENAI // UTC

      Accessible data table
      Date (UTC)Tokens

      `,1),Nc=K(`

      History unavailable or awaiting a matching account observation. Missing + history is not treated as zero usage.

      `),Pc=K(`

      USAGE // ACCOUNT HISTORY

      Account-wide history reported by Codex, not the local Sessions counter. Dates + use UTC. Allowance history is approximate where indicated; reset causes are + not provided.

      `,1);function Fc(e,t){Ge(t,!0);let n=F(`daily`),r=F(12),i=F(0),a=N(()=>{let e=$.data?.usage?.dailyUsageBuckets;if(!e)return[];let{start:t,end:n}=Sc(new Date,W(r),W(i)),a=new Map;for(let t of e)!Number.isFinite(t.tokens)||t.tokens<0||a.set(t.startDate,(a.get(t.startDate)||0)+t.tokens);let o=[];for(let e=t;e<=n;e=new Date(e.getTime()+864e5)){let t=e.toISOString().slice(0,10);o.push({date:t,tokens:a.get(t)||0})}return o}),o=N(()=>Math.max(1,...W(a).map(e=>e.tokens))),s=N(()=>W(a).length?new Date(W(a)[0].date+`T00:00:00Z`).getUTCDay():0),c=N(()=>{if(W(n)===`monthly`){let e=new Map;for(let t of W(a))e.set(t.date.slice(0,7),(e.get(t.date.slice(0,7))||0)+t.tokens);return[...e].map(([e,t])=>({date:e,tokens:t}))}let e=0;return W(a).map(t=>({date:t.date,tokens:e+=t.tokens}))});var l=Pc(),u=B(R(l),4);X(u,20,()=>[[`daily`,`Daily`],[`monthly`,`Monthly`],[`cumulative`,`Cumulative`],[`breakdown`,`Breakdown`],[`periods`,`Windows`]],Jr,(e,t)=>{var r=N(()=>_(t,2));let a=()=>W(r)[0],o=()=>W(r)[1];var s=Cc();let c;var l=z(s,!0);V(()=>{Z(s,`aria-pressed`,W(n)===a()),c=li(s,1,``,null,c,{active:W(n)===a()}),J(l,o())}),G(`click`,s,()=>{I(n,a(),!0),I(i,0)}),q(e,s)}),M(u);var d=B(u,2),f=L(d),p=e=>{var t=wc(),n=R(t),a=B(L(n)),o=L(a);o.value=o.__value=6;var s=B(o);s.value=s.__value=12,M(a),hi(a),M(n);var c=B(n),l=B(c);V(()=>l.disabled=W(i)===0),G(`change`,a,()=>I(i,0)),gi(a,()=>W(r),e=>I(r,e)),G(`click`,c,()=>Jt(i)),G(`click`,l,()=>Jt(i,-1)),q(e,t)};Y(f,e=>{W(n)!==`breakdown`&&W(n)!==`periods`&&e(p)}),M(d);var m=B(d,2),h=e=>{q(e,Tc())};Y(m,e=>{$.data?.usageError&&e(h)});var g=B(m,2),v=e=>{q(e,Ec())};Y(g,e=>{$.data?.usage?.tokenStatus===`UNAVAILABLE`&&W(n)!==`breakdown`&&W(n)!==`periods`&&e(v)});var y=B(g,2),b=e=>{var t=Fr();qr(R(t),()=>W(n),e=>{{let t=N(()=>$.data?.usage?.reports);yc(e,{get reports(){return W(t)},get mode(){return W(n)}})}}),q(e,t)},x=e=>{var t=Mc(),r=R(t),i=L(r),l=z(B(L(i),2),!0);M(i);var u=B(i,2),d=z(B(L(u),2),!0);M(u);var f=B(u,2),p=B(L(f),2),m=L(p);Ce(),M(p),M(f);var h=B(f,2),g=B(L(h),2),_=L(g);Ce(),M(g),M(h);var v=B(h,2),y=B(L(v),2),b=L(y);Ce(),M(y),M(v),M(r);var x=B(r,4),S=L(x),C=z(S),w=B(S,2),ee=e=>{var t=kc(),n=R(t),r=L(n),i=L(r);X(i,17,()=>Array(W(s)),Jr,(e,t)=>{q(e,Dc())}),X(B(i,2),17,()=>W(a),Jr,(e,t)=>{var n=Oc();let r,i;V(e=>{r=li(n,1,`heat-cell`,null,r,{zero:W(t).tokens===0}),Z(n,`title`,e),i=di(n,``,i,{opacity:W(t).tokens?.25+.75*W(t).tokens/W(o):1})},[()=>`${W(t).date}: ${fa(W(t).tokens)} tokens`]),q(e,n)}),M(r),M(n),Ce(2),q(e,t)},T=e=>{var t=Ac(),r=R(t);{let e=N(()=>W(c).map(e=>e.tokens)),t=N(()=>W(n)+` usage`);no(r,{get values(){return W(e)},get label(){return W(t)}})}var i=B(r,2),a=L(i),o=z(a,!0),s=z(B(a),!0);M(i),V(e=>{J(o,W(c)[0]?.date),J(s,e)},[()=>W(c).at(-1)?.date]),q(e,t)};Y(w,e=>{W(n)===`daily`?e(ee):e(T,-1)});var te=B(w,2),E=B(L(te),2),D=L(E),O=B(L(D));X(O,21,()=>W(n)===`daily`?W(a):W(c),Jr,(e,t)=>{var n=jc(),r=L(n),i=z(r,!0),a=z(B(r),!0);M(n),V(e=>{J(i,W(t).date),J(a,e)},[()=>fa(W(t).tokens)]),q(e,n)}),M(O),M(D),M(E),M(te),M(x);var ne=z(B(x,2));V((e,t,n,r,i,o,s)=>{J(l,e),J(d,t),J(m,`${n??``} `),J(_,`${r??``} `),J(b,`${i??``} `),J(C,`${W(a)[0]?.date??``} — ${o??``}`),J(ne,`OBSERVED ${s??``} // CUMULATIVE IS FOR THE SELECTED PERIOD`)},[()=>fa($.data.usage.summary.lifetimeTokens),()=>fa($.data.usage.summary.peakDailyTokens),()=>fa($.data.usage.summary.currentStreakDays),()=>fa($.data.usage.summary.longestRunningTurnSec),()=>fa($.data.usage.summary.longestStreakDays),()=>W(a).at(-1)?.date,()=>pa($.data.usageAt)]),q(e,t)},S=e=>{q(e,Nc())};Y(y,e=>{W(n)===`breakdown`||W(n)===`periods`?e(b):$.data?.usage&&$.data.usage.dailyUsageBuckets!==null?e(x,1):e(S,-1)}),q(e,l),Ke()}wr([`click`,`change`]);var Ic=K(`

      Page not found

      Return to Quota

      `,1);function Lc(e){var t=Ic();Ce(2),q(e,t)}var Rc=`CODEXOMETER`,zc=[`█▀▀ █▀█ █▀▄ █▀▀ ▀▄▀ █▀█ █▀▄▀█ █▀▀ ▀█▀ █▀▀ █▀█`,`█▄▄ █▄█ █▄▀ ██▄ █ █ █▄█ █ ▀ █ ██▄ █ ██▄ █▀▄`].flatMap(e=>[[...e].map(e=>e===`█`||e===`▀`),[...e].map(e=>e===`█`||e===`▄`)]),Bc=zc[0].length,Vc=zc.length,Hc=0,Uc=[...Rc].map(e=>{let t=e===`M`?5:3,n={start:Hc,width:t};return Hc+=t+1,n}),Wc=`ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`,Gc=`010 101 111 101.110 111 101 110.111 100 100 111.110 101 101 110.111 100 110 111.111 100 110 100.111 100 101 111.101 101 111 101.111 010 010 111.001 001 101 111.101 110 110 101.100 100 100 111.101 111 101 101.101 111 111 101.111 101 101 111.111 101 111 100.111 101 111 001.111 101 110 101.111 100 001 111.111 010 010 010.101 101 101 111.101 101 101 010.101 101 111 101.101 010 010 101.101 101 010 010.111 001 100 111.111 101 101 111.110 010 010 111.111 001 110 111.111 011 001 111.101 101 111 001.111 110 001 111.100 111 101 111.111 001 010 010.111 111 101 111.111 101 111 001`.split(`.`).map(e=>e.split(` `));function Kc(e=Rc,t=-1,n=!1){let r=``;for(let i=0;i{e|=0,e=e+1831565813|0;let t=Math.imul(e^e>>>15,1|e);return t^=t+Math.imul(t^t>>>7,61|t),((t^t>>>14)>>>0)/4294967296}}function Xc(){let e=qc[Math.floor(Math.random()*qc.length)],t=Math.floor(Math.random()*4294967296),n=Yc(t),r=[...Rc].map((e,t)=>t);for(let e=r.length-1;e>0;e--){let t=Math.floor(n()*(e+1));[r[e],r[t]]=[r[t],r[e]]}return{variant:e,seed:t,order:r}}function Zc(e,t){if(e.variant===`typing`){let e=t<1080,n=e?0:Math.min(Math.floor((t-1080)/90)+1,11);return{text:Rc.slice(0,n).padEnd(11),cursor:n,dot:e?Math.floor(t/180)%2==0:n<11}}if(e.variant===`shuffle`){let n=Yc(e.seed^Math.floor(t/70)),r=[...Rc].map(e=>{let t;do t=Wc[Math.floor(n()*36)];while(t===e);return t}),i=Math.min(Math.floor(t/110),11);for(let t of e.order.slice(0,i))r[t]=Rc[t];return{text:r.join(``),cursor:-1,dot:!1}}return{text:Rc,cursor:-1,dot:!1}}var Qc=Nr(``);function $c(e,t){Ge(t,!0);let n=Q(t,`text`,3,Rc),r=Q(t,`cursor`,19,()=>-1),i=Q(t,`dot`,3,!1),a=N(()=>Kc(n(),r(),i()));var o=Qc(),s=z(o);V(()=>{Z(o,`viewBox`,`0 0 ${Bc} ${Vc}`),Z(o,`width`,Bc),Z(o,`height`,Vc),Z(s,`d`,W(a))}),q(e,o),Ke()}var el=K(`
      `),tl=K(`
      `);function nl(e,t){Ge(t,!0);let n=F(void 0),r=F(0),i=F(Zt({width:0,height:0,pixelRatio:1})),a=F(Zt({x:0,y:0,width:0})),o=N(()=>W(n)?Zc(W(n),W(r)):void 0),s=N(()=>{if(!W(n))return{x:0,y:0,width:0};let e=Jc[W(n).variant],t=Math.min(Math.max((W(r)-e-180)/800,0),1),o=t*t*(3-2*t),s=W(i).width+(W(a).width-W(i).width)*o,c=Math.max((W(i).height-W(i).width*Vc/Bc)/2,0),l=(W(n).variant===`slide`&&W(r)Math.round(e*W(i).pixelRatio)/W(i).pixelRatio;return{x:d(l),y:d(u),width:d(s)}});Gi(()=>{let e=matchMedia(`(prefers-reduced-motion: reduce)`);if(e.matches){t.onfinish();return}I(n,Xc(),!0);let o=Jc[W(n).variant]+180+800,s=0,c=!1,l=performance.now(),u=()=>{let e=t.target?.getBoundingClientRect();I(i,{width:window.innerWidth,height:window.innerHeight,pixelRatio:window.devicePixelRatio||1},!0),e&&I(a,{x:e.x,y:e.y,width:e.width},!0)},d=()=>{c||(c=!0,cancelAnimationFrame(s),t.onfinish())},f=e=>{e instanceof PointerEvent&&e.button!==0||e instanceof KeyboardEvent&&(e.metaKey||e.ctrlKey||e.altKey||e.shiftKey)||e instanceof KeyboardEvent&&[`Shift`,`Control`,`Alt`,`Meta`].includes(e.key)||(e.preventDefault(),e.stopImmediatePropagation(),d())},p=()=>{e.matches&&d()},m=e=>{I(r,Math.max(e-l,0),!0),W(r)>=o?d():s=requestAnimationFrame(m)},h=new ResizeObserver(u);if(t.target){h.observe(t.target),t.target.parentElement&&h.observe(t.target.parentElement);let e=t.target.closest(`header`);e&&h.observe(e)}return u(),window.addEventListener(`resize`,u),document.addEventListener(`keydown`,f,!0),document.addEventListener(`pointerdown`,f,!0),e.addEventListener(`change`,p),s=requestAnimationFrame(m),()=>{c=!0,cancelAnimationFrame(s),h.disconnect(),window.removeEventListener(`resize`,u),document.removeEventListener(`keydown`,f,!0),document.removeEventListener(`pointerdown`,f,!0),e.removeEventListener(`change`,p)}});var c=tl(),l=L(c),u=e=>{var t=el();let n;$c(L(t),{get text(){return W(o).text},get cursor(){return W(o).cursor},get dot(){return W(o).dot}}),M(t),V(()=>{Z(t,`data-text`,W(o).text),Z(t,`data-cursor`,W(o).dot?W(o).cursor:void 0),n=di(t,``,n,{left:`${W(s).x}px`,top:`${W(s).y}px`,width:`${W(s).width}px`})}),q(e,t)};Y(l,e=>{W(o)&&e(u)});var d=B(l,2);M(c),V(()=>Z(c,`data-entrance`,W(n)?.variant)),G(`click`,d,function(...e){t.onfinish?.apply(this,e)}),q(e,c),Ke()}wr([`click`]);var rl=K(` `),il=K(`

      `),al=K(`

      Connecting to your local Codexometer…

      `),ol=K(``),sl=K(`

      Your quota. Your sessions. Your command centre.

      `);function cl(e,t){Ge(t,!0);let n={"/":$a,"/quota/:view?":$a,"/sessions/:id?":oc,"/usage":Fc,"*":Lc},r={quota:/^(?:\/|\/quota(?:\/[^/]+)?\/?)$/,sessions:/^\/sessions(?:\/[^/]+)?\/?$/,usage:/^\/usage\/?$/},i=F(`hacker`),a=F(!0),o=F(void 0),s=[`hacker`,`rust`,`blue-steel`,`ultraviolet`,`nightshade`];bn(()=>{sa()}),bn(()=>{if($.data)for(let[e,t]of Object.entries(r))t.test(Yi.location)&&(aa.tab=e)}),Gi(()=>{try{let e=localStorage.getItem(`codexometer.web.theme`);e&&s.includes(e)&&I(i,e,!0)}catch{}return va()});function c(){try{localStorage.setItem(`codexometer.web.theme`,W(i))}catch{}}var l=sl(),u=L(l),d=L(u),f=L(d);$c(L(f),{}),M(f),Li(f,e=>I(o,e),()=>W(o)),Ce(2),M(d);var p=B(d,2),m=L(p);let h;var g=B(m,1,!0),v=z(B(g));M(p),M(u);var y=B(u,2);X(y,21,()=>Object.entries(r),Jr,(e,t)=>{var n=N(()=>_(W(t),2));let r=()=>W(n)[0],i=()=>W(n)[1],a=N(()=>i().test(Yi.location));var o=rl();let s;var c=z(o,!0);V(e=>{Z(o,`href`,r()===`quota`?`#/quota/`+aa.view:`#/`+r()),Z(o,`aria-current`,W(a)?`page`:void 0),s=li(o,1,``,null,s,{active:W(a)}),J(c,e)},[()=>r().toUpperCase()]),q(e,o)}),M(y);var b=B(y,2),x=L(b),S=e=>{var t=il(),n=z(t,!0);V(()=>J(n,$.error)),q(e,t)};Y(x,e=>{$.error&&e(S)});var C=B(x,2),w=e=>{$i(e,{get routes(){return n}})},ee=e=>{q(e,al())};Y(C,e=>{$.data?e(w):$.error||e(ee,1)}),M(b);var T=B(b,2),te=L(T),E=z(te),D=B(te,2),O=z(D,!0),ne=B(D,2),k=B(L(ne));X(k,21,()=>s,Jr,(e,t)=>{var n=ol(),r=z(n,!0),i={};V(e=>{J(r,e),i!==(i=W(t))&&(n.value=(n.__value=i)??``)},[()=>W(t).replace(`-`,` `).toUpperCase()]),q(e,n)}),M(k),hi(k),M(ne),M(T);var re=B(T,2),ie=e=>{nl(e,{get target(){return W(o)},onfinish:()=>I(a,!1)})};Y(re,e=>{W(a)&&e(ie)}),M(l),V(()=>{Z(l,`data-theme`,W(i)),Z(l,`data-starting`,W(a)),u.inert=W(a),h=li(m,1,`lamp`,null,h,{lit:$.connected}),J(g,$.connected?`CONNECTED`:`OFFLINE`),J(v,`EXPERIMENTAL // ${$.data?.control?`SESSION CONTROL`:`READ ONLY`}`),y.inert=W(a),b.inert=W(a),T.inert=W(a),J(E,`v${($.data?.version||`…`)??``} // LOCAL ONLY`),J(O,$.data?.control?`Session control enabled. Stop the server to revoke access.`:`No actions can be sent from this preview.`)}),G(`change`,k,c),gi(k,()=>W(i),e=>I(i,e)),q(e,l),Ke()}wr([`change`]),Vr(cl,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/internal/web/dist/assets/index-ED7GoxW0.css b/internal/web/dist/assets/index-ED7GoxW0.css deleted file mode 100644 index b31a55d..0000000 --- a/internal/web/dist/assets/index-ED7GoxW0.css +++ /dev/null @@ -1 +0,0 @@ -.observation-details.svelte-1vgdi83{font-size:12px}summary.svelte-1vgdi83{cursor:pointer;color:var(--accent)}.observation-table.svelte-1vgdi83{max-height:240px;overflow:auto}.zone-canvas.svelte-1vgdi83{flex:1;min-height:170px;margin-top:6px;position:relative}svg.svelte-1vgdi83{width:100%;height:100%;display:block;position:absolute;inset:0}text.svelte-1vgdi83{fill:var(--ink);font:14px Cascadia Code,Consolas,monospace}.axis-title.svelte-1vgdi83{letter-spacing:.04em;font-size:12px}.grid.svelte-1vgdi83{stroke:#fff;stroke-opacity:.14;stroke-width:1px}.axes.svelte-1vgdi83{stroke:var(--ink);stroke-width:1.5px;fill:none}.pace-line.svelte-1vgdi83{stroke:#fff;stroke-width:2px;stroke-dasharray:6 4}.position-halo.svelte-1vgdi83{fill:#101820;stroke:#fff;stroke-width:1.5px}.position-dot.svelte-1vgdi83{fill:#fff}.observation-trail.svelte-1vgdi83{fill:none;stroke:#101820;stroke-width:2.5px;stroke-linejoin:round}.observation-trail-casing.svelte-1vgdi83{fill:none;stroke:#ffffff73;stroke-width:4.5px;stroke-linejoin:round}.trail-start.svelte-1vgdi83{fill:#101820;stroke:#ffffffb3;stroke-width:2px}.trend-line.svelte-1vgdi83{fill:none;stroke:var(--trend-color);stroke-width:2px;stroke-dasharray:3 6}.trend-arrow.svelte-1vgdi83{fill:var(--trend-color);stroke:#ffffff73;stroke-width:.5px}.trend-casing.svelte-1vgdi83{fill:none;stroke:#ffffff73;stroke-width:4px;stroke-dasharray:3 6}.zone-caption.svelte-1vgdi83{min-width:0;color:var(--accent);flex:auto;margin:0}.trend-summary.svelte-1vgdi83{color:var(--ink)}.zone-footer.svelte-1vgdi83{justify-content:space-between;align-items:flex-start;gap:12px;margin-top:4px;display:flex}.zone-controls.svelte-1vgdi83{color:var(--muted);text-align:right;flex-direction:column;align-items:flex-end;gap:5px;font-size:11px;display:flex}.trace-control.svelte-1vgdi83,.trend-control.svelte-1vgdi83{cursor:pointer;white-space:nowrap}.trace-control.svelte-1vgdi83 input:where(.svelte-1vgdi83){accent-color:var(--accent)}.trend-control.svelte-1vgdi83 select:where(.svelte-1vgdi83){border:1px solid var(--edge);background:var(--panel);max-width:min(230px,45vw);color:var(--ink);font:inherit}.trend-control.svelte-1vgdi83 option:where(.svelte-1vgdi83):disabled{color:var(--muted)}.zone-controls.svelte-1vgdi83 small:where(.svelte-1vgdi83){font-size:10px}@container (width<=560px){.zone-footer.svelte-1vgdi83{flex-direction:column}.zone-controls.svelte-1vgdi83{align-self:stretch;align-items:flex-end}}.slash-commands.svelte-7bfwr9{margin:.4rem 0;position:relative}.command-popup.svelte-7bfwr9{z-index:5;box-sizing:border-box;border:1px solid var(--accent);background:var(--panel);width:min(40rem,100%);padding:.35rem;position:absolute;bottom:100%;left:0;box-shadow:0 .3rem 1rem #0006}.command-popup.svelte-7bfwr9 p:where(.svelte-7bfwr9){margin:.3rem 0;font-size:.8em}.command-options.vertical.svelte-7bfwr9{flex-flow:column;gap:.15rem;display:flex}.vertical.svelte-7bfwr9 button:where(.svelte-7bfwr9){text-align:left;white-space:nowrap;width:100%;min-width:0;color:var(--muted);background:var(--panel);border-color:#0000;flex:none;align-items:baseline;gap:.4rem;display:flex}.vertical.svelte-7bfwr9 .command-name:where(.svelte-7bfwr9),.vertical.svelte-7bfwr9 .command-tail:where(.svelte-7bfwr9){flex:none}.vertical.svelte-7bfwr9 .command-name:where(.svelte-7bfwr9){text-overflow:ellipsis;max-width:100%;overflow:hidden}.vertical.svelte-7bfwr9 button.suggested:where(.svelte-7bfwr9){outline:none}.vertical.svelte-7bfwr9 button.suggested:where(.svelte-7bfwr9) .command-name:where(.svelte-7bfwr9),.vertical.svelte-7bfwr9 button:where(.svelte-7bfwr9):hover .command-name:where(.svelte-7bfwr9){background:var(--accent);color:var(--bg)}.vertical.svelte-7bfwr9 .command-help:where(.svelte-7bfwr9){min-width:0;max-width:none;color:var(--muted);flex:1}.command-options.svelte-7bfwr9{flex-wrap:wrap;gap:.35rem;max-height:35vh;display:flex;overflow:auto}.command-options.svelte-7bfwr9 .suggested:where(.svelte-7bfwr9){outline:1px solid var(--accent)}.command-help.svelte-7bfwr9{text-overflow:ellipsis;white-space:nowrap;opacity:.75;max-width:32ch;font-size:.8em;display:block;overflow:hidden}pre.svelte-7bfwr9{white-space:pre-wrap;overflow-wrap:anywhere;max-height:35vh;overflow:auto}.file-diff.svelte-x4gtb6{border:1px solid var(--muted);max-height:60vh;padding:.5rem;font-family:monospace;overflow:auto}.diff-line.svelte-x4gtb6{white-space:pre;min-height:1.3em;display:flex}.line-number.svelte-x4gtb6{text-align:right;opacity:.75;-webkit-user-select:none;user-select:none;flex:0 0 5ch;padding-right:1ch;display:inline-block}.added.svelte-x4gtb6{color:#67d391;background:#67d39112}.removed.svelte-x4gtb6{color:#ff6b83;background:#ff6b8312}.heading.svelte-x4gtb6{padding:.5rem 0;font-weight:700}.saved-prompt.svelte-1oupzfc{white-space:pre-wrap;overflow-wrap:anywhere}fieldset.svelte-1oupzfc{border:1px solid;margin:.5rem 0;padding:.35rem .6rem}.decision.svelte-1oupzfc,.answer.svelte-1oupzfc{margin:.3rem 0;display:block}.decision.svelte-1oupzfc code:where(.svelte-1oupzfc),.decision.svelte-1oupzfc span:where(.svelte-1oupzfc){overflow-wrap:anywhere;margin:.3rem 0 .3rem 1.5rem;display:block}textarea.svelte-1oupzfc,.answer.svelte-1oupzfc input:where(.svelte-1oupzfc),.answer.svelte-1oupzfc select:where(.svelte-1oupzfc){box-sizing:border-box;background:var(--bg);width:100%;color:inherit;font:inherit;border:1px solid;margin-top:.4rem;padding:.5rem;display:block}textarea.svelte-1oupzfc{resize:vertical;background:color-mix(in srgb, currentColor 8%, var(--bg))}button.svelte-1oupzfc{margin:.3rem .5rem .3rem 0}h3.svelte-1oupzfc,p.svelte-1oupzfc{margin-block:.4rem}.sent.svelte-1oupzfc{color:var(--accent)}.status-line.svelte-gprptn{align-items:center;gap:.5rem;min-width:0;margin-top:.5rem;display:flex}.status-line.svelte-gprptn p:where(.svelte-gprptn){white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;margin:0;font-size:.8rem;overflow:hidden}.configure.svelte-gprptn{padding:.15rem .35rem;font-size:.75rem}.status-picker.svelte-gprptn{border:1px solid var(--edge);margin:.5rem 0;padding:.6rem}.fields.svelte-gprptn{max-height:35vh;overflow:auto}.field.svelte-gprptn{align-items:center;gap:.35rem;display:flex}.field.svelte-gprptn label:where(.svelte-gprptn){flex:1;align-items:center;gap:.4rem;display:flex}.field.svelte-gprptn label:where(.svelte-gprptn) span:where(.svelte-gprptn){font-size:.75rem}.field.svelte-gprptn button:where(.svelte-gprptn){padding:.1rem .35rem}.session-copy.svelte-543j00{justify-content:flex-end;align-items:center;gap:8px;margin-top:auto;padding-top:6px;display:flex}span.svelte-543j00{color:var(--muted);overflow-wrap:anywhere;font-size:12px}button.svelte-543j00{color:var(--accent);flex-shrink:0;padding:3px 7px}button.svelte-543j00:hover,button.flashed.svelte-543j00{color:var(--bg);background:var(--accent)}h3.svelte-1fbui7h,.window-dates.svelte-1fbui7h,.report-value.svelte-1fbui7h{color:var(--accent);font-weight:700}.quality-warning.svelte-1fbui7h,.quota-near.svelte-1fbui7h{color:var(--approval)}.quota-full.svelte-1fbui7h{color:#ff6680}.alternate.svelte-1fbui7h .report-fill:where(.svelte-1fbui7h){background:radial-gradient(circle, var(--panel) .7px, transparent .9px) 0 0 / 3px 3px, var(--accent)}.unknown.svelte-1fbui7h,.unknown.svelte-1fbui7h .report-value:where(.svelte-1fbui7h){color:var(--muted)}.unknown.svelte-1fbui7h .report-fill:where(.svelte-1fbui7h){background:var(--muted);opacity:1}.report-bars.svelte-1fbui7h{grid-template-columns:minmax(5rem,1fr) minmax(2rem,3fr) auto;gap:.6rem;display:grid}.report-row.svelte-1fbui7h,.report-head.svelte-1fbui7h{grid-column:1/-1;grid-template-columns:subgrid;align-items:center;gap:.6rem;display:grid}.report-head.svelte-1fbui7h{color:var(--muted);font-size:.85rem}.report-head.svelte-1fbui7h>span:where(.svelte-1fbui7h):last-child,.report-value.svelte-1fbui7h{text-align:right}.report-row.svelte-1fbui7h>span:where(.svelte-1fbui7h):first-child{overflow-wrap:anywhere}.report-track.svelte-1fbui7h{background:var(--panel);height:1rem}.report-fill.svelte-1fbui7h{background:var(--accent);height:100%}:root{font-synthesis:none;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;background:#080e0c;font-family:Cascadia Code,SFMono-Regular,Consolas,monospace}*{box-sizing:border-box}body{margin:0}.shell{--accent:#80edac;--approval:#ffca58;--muted:#98aaa0;--edge:#31473a;--panel:#101c16;--bg:#080e0c;--ink:#e2eee5;background:var(--bg);height:100dvh;color:var(--ink);grid-template-rows:auto auto minmax(0,1fr) auto;padding:clamp(10px,1.2vw,20px);display:grid}.shell[data-theme=rust]{--approval:#ff8a3d;--accent:#ffbc75;--muted:#bfac95;--edge:#59412c;--panel:#261b14;--bg:#140e0a}.shell[data-theme=blue-steel]{--approval:#e8c46a;--accent:#85c8ff;--muted:#9cacc4;--edge:#334d69;--panel:#142235;--bg:#0b1421}.shell[data-theme=ultraviolet]{--approval:#f9a8d4;--accent:#c5adff;--muted:#b3a6c9;--edge:#4b3c68;--panel:#241a35;--bg:#140d22}.shell[data-theme=nightshade]{--approval:#8f7cff;--accent:#e59bff;--muted:#c6a7cf;--edge:#673976;--panel:#301739;--bg:#1c0b24}header,.spread,footer,.controls{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;display:flex}header{margin-bottom:10px}.brand{letter-spacing:.08em;color:var(--accent);text-shadow:0 0 22px color-mix(in srgb, var(--accent) 20%, transparent);font-size:clamp(22px,2.5vw,32px);font-weight:800;text-decoration:none}header p{color:var(--muted);margin:3px 0 0;font-size:13px}.connection{color:var(--accent);text-align:right}small,.eyebrow{letter-spacing:.04em;font-size:12px}.connection small{color:var(--muted);margin-top:4px;display:block}.lamp{background:var(--muted);border-radius:50%;width:9px;height:9px;margin-right:9px;display:inline-block}.lamp.lit{background:var(--accent);box-shadow:0 0 8px color-mix(in srgb, var(--accent) 40%, transparent)}.working{animation:1.5s ease-in-out infinite pulse}@keyframes pulse{50%{opacity:.3}}nav{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}nav a,button,.button,select{font:inherit;color:var(--accent);border:1px solid var(--edge);background:var(--panel);cursor:pointer;border-radius:3px;padding:7px 11px;font-size:13px;text-decoration:none}button:hover,.button:hover,nav a:hover{border-color:var(--accent)}nav a.active,nav button.active{background:var(--accent);color:var(--bg);border-color:var(--accent)}:focus-visible{outline:2px solid var(--accent);outline-offset:4px}.secondary{margin-bottom:8px}.secondary a,.secondary button{padding:6px 10px;font-size:12px}main{scrollbar-gutter:stable;flex-direction:column;min-width:0;min-height:0;display:flex;overflow:auto}main>*{flex-shrink:0}.panel{border:1px solid var(--edge);border-top:2px solid var(--accent);background:var(--panel);border-radius:4px;min-width:0;padding:clamp(10px,1vw,16px)}h1{color:var(--accent);font-size:20px}h2{letter-spacing:.04em;color:var(--accent);overflow-wrap:anywhere;margin:0 0 10px;font-size:13px}h3{overflow-wrap:anywhere;font-size:13px}p{margin:6px 0;font-size:13px;line-height:1.45}a{color:var(--accent)}.muted,.eyebrow{color:var(--muted)}.notice{color:#ffe0aa;background:#332611;border-left:3px solid #ffc26e;padding:12px 16px}.empty{color:var(--muted);padding:35px 0}.quota-grid{flex:1 0 auto;grid-auto-rows:minmax(220px,1fr);gap:10px;display:grid}.quota-card{flex-direction:column;min-height:0;display:flex;container-type:inline-size}.quota-card>:not(.meter-graphic){flex-shrink:0}.meter-graphic{flex-direction:column;flex:1;min-height:100px;display:flex}.bar-graphic{min-height:65px}.quota-card .gauge{flex:2;height:auto;min-height:16px;margin:7px 0}.quota-card .timeline{flex:1;min-height:8px}.quota-grid.zone{grid-template-columns:repeat(auto-fit,minmax(min(100%,420px),1fr));grid-auto-rows:minmax(330px,1fr)}.quota-grid.radial{grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr));grid-auto-rows:minmax(260px,1fr)}.gauge{background:var(--edge);height:35px;margin:15px 0;overflow:hidden}.gauge>div{background:repeating-linear-gradient(90deg, var(--accent) 0, var(--accent) 9px, transparent 9px, transparent 12px);height:100%;transition:width .5s}.gauge.timeline{height:14px}.pie-wrap{flex:1;min-height:100px;margin:6px 0;position:relative}.pie-wrap svg{width:100%;height:100%;position:absolute;inset:0}.pie-base{fill:var(--edge);stroke:var(--accent);stroke-width:1px}.pie-fill{fill:var(--accent)}.readout{overflow-wrap:anywhere;color:var(--accent);margin:18px 0;font-size:clamp(22px,3vw,34px)}.credit+.credit{border-top:1px solid var(--edge);padding-top:20px}.credit{margin-top:24px}.session-row{grid-template-columns:minmax(210px,1.1fr) minmax(0,1.2fr) minmax(0,1.5fr);align-items:stretch;gap:14px;margin:18px 0;display:grid}.session-totals{grid-template-columns:repeat(auto-fit,minmax(min(145px,100%),1fr));gap:8px;margin:8px 0;display:grid}.session-totals>div{border:1px solid var(--edge);min-width:0;padding:10px}.session-totals dt{color:var(--muted);font-size:11px}.session-totals dd{color:var(--accent);overflow-wrap:anywhere;margin:6px 0 0;font-size:24px}.session-totals.stale dd{color:var(--muted)}.session-row.selected{outline:1px solid var(--accent);outline-offset:4px}.session-row.wide .context{grid-column:2/-1}.detail-controls{flex-wrap:wrap;gap:6px;display:flex}.attention-summary{flex-wrap:wrap;gap:8px;margin-block:8px;display:flex}.attention-summary .approval{color:var(--approval);border-color:var(--approval)}.attention-summary .approval:hover{background:color-mix(in srgb, var(--approval) 12%, var(--panel))}.attention-summary .approval:focus-visible{outline-color:var(--approval)}.attention-note,.attention-badge{color:var(--accent);border-left:3px solid;padding-left:8px}.attention-note.inferred{color:var(--muted)}.attention-badge{font-size:12px}.session-select{text-align:left;overflow-wrap:anywhere;max-width:100%}.status-detail{color:inherit;text-decoration:none}.status-detail:hover{text-decoration:underline}button:disabled{opacity:.4;cursor:default}.session-row .graph-panel{flex-direction:column;grid-column:2/-1;display:flex}.session-row.split .graph-panel{grid-column:auto}.session-row .button,.session-row button{margin-top:8px;display:inline-block}.context pre{max-height:230px;overflow:auto}.session-row .context{flex-direction:column;display:flex}pre{font:inherit;white-space:pre-wrap;overflow-wrap:anywhere;font-size:13px;line-height:1.7}.full-detail{margin-top:6px}.detail-heading{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:6px 12px;display:flex}.detail-heading h2{flex:250px;min-width:0;margin:0}.detail-metadata{overflow-wrap:anywhere}.detail-workspace{border-top:1px solid var(--edge);flex-direction:column;gap:10px;margin-top:8px;padding-top:4px;display:flex}.detail-context,.detail-workspace .session-actions{min-width:0}.detail-workspace .session-actions{border-top:1px solid var(--edge);padding-top:6px}.full-detail h3{margin-block:8px}.full-detail pre{margin-block:8px;line-height:1.5}.full-detail hr{margin-block:12px}.full-detail .command,.full-detail .notice{padding:8px 10px}.command{background:var(--bg);border-left:3px solid var(--accent);padding:16px}hr{border:0;border-top:1px solid var(--edge);margin:24px 0}.chart{border-bottom:1px solid var(--muted);align-items:flex-end;gap:2px;height:clamp(120px,22vh,320px);margin-top:18px;display:flex}.chart-bar{background:var(--accent);flex:1;min-width:0;max-height:100%}.controls{justify-content:flex-start;margin:20px 0}.summary-grid{grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));gap:16px;margin:20px 0;display:grid}.thresholds-panel{padding:clamp(14px,2vw,22px)}.thresholds-panel h1{margin-top:0}.threshold-list{gap:8px;margin-top:16px;display:grid}.threshold-list article{border:1px solid var(--edge);grid-template-columns:minmax(64px,.5fr) minmax(220px,3fr) minmax(64px,.6fr) minmax(120px,1.2fr);align-items:center;gap:12px;padding:10px 12px;display:grid}.threshold-list article.active{border-color:var(--accent);box-shadow:inset 3px 0 0 var(--accent)}.threshold-list article.next{border-color:var(--approval);box-shadow:inset 3px 0 0 var(--approval)}.threshold-list p{color:var(--muted);margin:3px 0 0;font-size:12px}.threshold-trigger{color:var(--accent);font-size:22px;font-weight:800}.threshold-mode,.threshold-state{color:var(--muted);font-size:12px;font-weight:700}.threshold-list article.active .threshold-state{color:var(--accent)}.threshold-list article.next .threshold-state{color:var(--approval)}.heat-scroll{overflow-x:auto}.heatmap{grid-template-rows:repeat(7,13px);grid-auto-columns:minmax(9px,1fr);grid-auto-flow:column;gap:4px;min-width:620px;margin:25px 0;display:grid}.heat-cell{background:var(--accent);border-radius:2px}.heat-cell.zero{background:var(--edge)}.table-scroll{max-height:280px;overflow:auto}table{border-collapse:collapse;width:100%;margin-top:15px}th,td{text-align:left;border-bottom:1px solid var(--edge);padding:8px}summary{cursor:pointer;color:var(--accent);padding-top:14px}footer{border-top:1px solid var(--edge);color:var(--muted);margin-top:8px;padding-top:8px;font-size:11px}footer select{padding:6px}@media (width<=850px){.session-row{grid-template-columns:minmax(0,1fr)}.session-row.wide .context,.session-row .graph-panel{grid-column:auto}.connection{text-align:left}.threshold-list article{grid-template-columns:64px minmax(0,1fr)}.threshold-mode,.threshold-state{grid-column:2}}@media (prefers-reduced-motion:reduce){*,:before,:after{transition:none!important;animation:none!important}} diff --git a/internal/web/dist/assets/index-KFu7j0xL.js b/internal/web/dist/assets/index-KFu7j0xL.js deleted file mode 100644 index 0173a34..0000000 --- a/internal/web/dist/assets/index-KFu7j0xL.js +++ /dev/null @@ -1,44 +0,0 @@ -(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 e=Array.isArray,t=Array.prototype.indexOf,n=Array.prototype.includes,r=Array.from,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyDescriptors,s=Object.prototype,c=Array.prototype,l=Object.getPrototypeOf,u=Object.isExtensible;function d(e){return typeof e==`function`}var f=()=>{};function p(e){return e()}function m(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}function g(e,t,n=!1){return e===void 0?n?t():t:e}function _(e,t){if(Array.isArray(e))return e;if(t===void 0||!(Symbol.iterator in e))return Array.from(e);let n=[];for(let r of e)if(n.push(r),n.length===t)break;return n}var v=1024,y=2048,b=4096,x=8192,S=16384,C=32768,w=1<<25,T=65536,E=1<<19,ee=1<<20,D=1<<25,O=1<<21,k=1<<22,te=1<<23,A=Symbol(`$state`),ne=Symbol(`component`),re=Symbol(`legacy props`),ie=Symbol(``),ae=Symbol(`attributes`),oe=Symbol(`class`),se=Symbol(`style`),ce=Symbol(`text`),le=Symbol(`form reset`),ue=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},de=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`),fe={},pe=Symbol(`uninitialized`),me=`http://www.w3.org/1999/xhtml`;function he(){console.warn(`https://svelte.dev/e/derived_inert`)}function ge(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function _e(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function ve(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var j=!1;function ye(e){j=e}var M;function be(e){if(e===null)throw ge(),fe;return M=e}function xe(){return be(sn(M))}function N(e){if(j){if(sn(M)!==null)throw ge(),fe;M=e}}function Se(e=1){if(j){for(var t=e,n=M;t--;)n=sn(n);M=n}}function Ce(e=!0){for(var t=0,n=M;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=sn(n);e&&n.remove(),n=i}}function we(e){if(!e||e.nodeType!==8)throw ge(),fe;return e.data}function Te(e){return e===this.v}function Ee(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function De(e){return!Ee(e,this.v)}function Oe(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function ke(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Ae(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function je(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function Me(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Ne(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function Pe(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function Fe(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ie(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function Le(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Re(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function ze(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var Be=!1;function Ve(){Be=!0}var He=null;function Ue(e){He=e}function We(e,t=!1,n){He={p:He,i:!1,c:null,e:null,s:e,x:null,r:W,l:Be&&!t?{s:null,u:null,$:[]}:null}}function Ge(e){var t=He,n=t.e;if(n!==null){t.e=null;for(var r of n)bn(r)}return e!==void 0&&(t.x=e),t.i=!0,He=t.p,Ke(e)}function Ke(e={}){return i(e,ne,{value:!0}),e}function qe(){return!Be||He!==null&&He.l===null}var Je=[];function Ye(){var e=Je;Je=[],m(e)}function Xe(e){if(Je.length===0&&!wt){var t=Je;queueMicrotask(()=>{t===Je&&Ye()})}Je.push(e)}function Ze(){for(;Je.length>0;)Ye()}var Qe=~(y|b|v);function $e(e,t){e.f=e.f&Qe|t}function et(e){e.f&512||e.deps===null?$e(e,v):$e(e,b)}function tt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),$e(e,v)}function nt(e){j&&on(e)!==null&&cn(e)}var rt=!1;function it(){rt||(rt=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[le]?.()})},{capture:!0}))}function at(e){var t=U,n=W;Wn(null),Gn(null);try{return e()}finally{Wn(t),Gn(n)}}function ot(e,t,n,r=n){e.addEventListener(t,()=>at(n));let i=e[le];e[le]=i?()=>{i(),r(!0)}:()=>r(!0),it()}function st(e,t,n,r){let i=qe()?dt:mt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=W,c=ct(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){pn(e,s)}lt()}}var d=ut();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>pt(e))).then(u).catch(e=>pn(e,s)).finally(d)}l?l.then(()=>{c(),f(),lt()}):f()}function ct(){var e=W,t=U,n=He,r=F;return function(i=!0){Gn(e),Wn(t),Ue(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function lt(e=!0){Gn(null),Wn(null),Ue(null),e&&F?.deactivate()}function ut(){var e=W,t=e.b,n=F,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function dt(e){var t=2|y;return W!==null&&(W.f|=E),{ctx:He,deps:null,effects:null,equals:Te,f:t,fn:e,reactions:null,rv:0,v:pe,wv:0,parent:W,ac:null}}var ft=Symbol(`obsolete`);function pt(e,t,n){let r=W;r===null&&ke();var i=void 0,a=Vt(pe),o=!U,s=new Set;return wn(()=>{var t=W,n=h();i=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==ue&&n.reject(e)}).finally(lt)}catch(e){n.reject(e),lt()}var c=F;if(o){if(t.f&32768)var l=ut();if(r.b?.is_rendered())c.async_deriveds.get(t)?.reject(ft);else for(let e of s.values())e.reject(ft);s.add(n),c.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),s.delete(n),t!==ft&&(c.activate(),t?(a.f|=te,Gt(a,t)):(a.f&8388608&&(a.f^=te),Gt(a,e)),c.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),vn(()=>{for(let e of s)e.reject(ft)}),new Promise(e=>{function t(n){function r(){n===i?e(a):t(i)}n.then(r,r)}t(i)})}function P(e){let t=dt(e);return qn(t),t}function mt(e){let t=dt(e);return t.equals=De,t}function ht(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(ue),t.ac=null}),t.fn!==null&&(t.teardown=f),cr(t,0),kn(t))}function yt(e){if(e.effects!==null)for(let t of e.effects)t.teardown&&t.fn!==null&&lr(t)}var bt=null,F=null,xt=null,St=null,Ct=null,wt=!1,Tt=!1,Et=null,Dt=null,Ot=0,kt=1,At=class e{id=kt++;#e=!1;linked=!0;#t=null;#n=null;async_deriveds=new Map;current=new Map;previous=new Map;#r=new Set;#i=new Set;#a=0;#o=new Map;#s=null;#c=[];#l=[];#u=new Set;#d=new Set;#f=new Map;#p=new Set;is_fork=!1;#m=!1;constructor(){bt===null?bt=this:(bt.#n=this,this.#t=bt),bt=this}#h(){if(this.is_fork)return!0;for(let n of this.#o.keys()){for(var e=n,t=!1;e.parent!==null;){if(this.#f.has(e)){t=!0;break}e=e.parent}if(!t)return!0}return!1}skip_effect(e){this.#f.has(e)||this.#f.set(e,{d:[],m:[]}),this.#p.delete(e)}unskip_effect(e,t=e=>this.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)$e(r,y),t(r);for(r of n.m)$e(r,b),t(r)}this.#p.add(e)}#g(){var e=[];for(let i of this.#c)if(!(i.f&16384||!(i.f&6144))){for(var t=i,n=!1;t.parent!==null;){t=t.parent;var r=t.f;if(r&96){if(!(r&1024)){n=!0;break}t.f^=v}}n||e.push(t)}return this.#c=[],e}#_(){this.#e=!0;for(let e of this.#u)this.#d.delete(e),$e(e,y),this.schedule(e);for(let e of this.#d)$e(e,b),this.schedule(e);this.apply();for(var t=Et=[],n=[],r=Dt=[];this.#c.length>0;){Ot++>1e3&&(this.#S(),Mt());for(let e of this.#g())try{this.#v(e,t,n)}catch(t){throw Lt(e),this.#h()||this.discard(),t}}if(F=null,r.length>0){var i=e.ensure();for(let e of r)i.schedule(e)}if(Et=null,Dt=null,this.#h()){this.#x(n),this.#x(t);for(let[e,t]of this.#f)It(e,t);r.length>0&&F.#_();return}let a=this.#y();if(a){this.#x(n),this.#x(t),a.#b(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),xt=this,Pt(n),Pt(t),xt=null,this.#s?.resolve();var o=F;if(this.#a===0&&(this.#c.length===0||o!==null)&&this.#S(),this.#c.length>0){if(o!==null){for(let e of this.#c)o.#c.push(e);this.#c=[]}else o=this}o!==null&&(zt.clear(),o.#_())}#v(e,t,n){e.f^=v;for(var r=e.first;r!==null;){var i=r.f,a=!!(i&96);if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=v:i&4?t.push(r):rr(r)&&(i&16&&this.#d.add(r),lr(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#y(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#b(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null&&!(e.f&2&&!(e.f&6144)))for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),$e(i,y),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),F=this,this.#_()}#x(e){for(var t=0;t{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=h()).promise}static ensure(){if(F===null){let t=F=new e;!Tt&&!wt&&Xe(()=>{t.#e||t.flush()})}return F}apply(){St=null}schedule(e){if(Ct=e,e.b?.is_pending&&e.f&16777228&&!(e.f&32768)){e.b.defer_effect(e);return}this.#c.push(e)}#S(){if(this.linked){var e=this.#t,t=this.#n;e===null||(e.#n=t),t===null?bt=e:t.#t=e,this.linked=!1}}};function jt(e){var t=wt;wt=!0;try{var n;for(e&&(F!==null&&!F.is_fork&&F.flush(),n=e());;){if(Ze(),F===null)return n;F.flush()}}finally{wt=t}}function Mt(){try{Pe()}catch(e){pn(e,Ct)}}var Nt=null;function Pt(e){var t=e.length;if(t!==0){for(var n=0;n0)){zt.clear();for(let e of Nt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Nt.has(n)&&(Nt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||lr(n)}}Nt.clear()}}Nt=null}}function Ft(e){F.schedule(e)}function It(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),$e(e,v);for(var n=e.first;n!==null;)It(n,t),n=n.next}}function Lt(e){$e(e,v);for(var t=e.first;t!==null;)Lt(t),t=t.next}var Rt=new Set,zt=new Map,Bt=!1;function Vt(e,t){return{f:0,v:e,reactions:null,equals:Te,rv:0,wv:0}}function I(e,t){let n=Vt(e,t);return qn(n),n}function Ht(e,t=!1,n=!0){let r=Vt(e);return t||(r.equals=De),Be&&n&&He!==null&&He.l!==null&&(He.l.s??=[]).push(r),r}function L(e,t,n=!1){return U!==null&&(!Un||U.f&131072)&&qe()&&U.f&4325394&&(Kn===null||!Kn.has(e))&&Re(),Gt(e,n?Xt(t):t,Dt)}var Ut=null,Wt=0;function Gt(e,t,n=null){if(!e.equals(t)){Vn?zt.set(e,t):zt.has(e)||zt.set(e,e.v);var r=At.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&>(t),St===null&&et(t)}e.wv=nr(),Ut=null,Wt=0,Yt(e,y,n),Ut=null,qe()&&W!==null&&W.f&1024&&!(W.f&96)&&(Xn===null?Zn([e]):Xn.push(e)),!r.is_fork&&Rt.size>0&&!Bt&&Kt()}return t}function Kt(){Bt=!1;for(let e of Rt){e.f&1024&&$e(e,b);let t;try{t=rr(e)}catch{t=!0}t&&lr(e)}Rt.clear()}function qt(e,t=1){var n=G(e),r=t===1?n++:n--;return L(e,n),r}function Jt(e){L(e,e.v+1)}function Yt(e,t,n){var r=e.reactions;if(r!==null){var i=qe(),a=r.length;if(Wt+=a,Wt>1e5&&Ut===null&&(Ut=new Set),Ut!==null){if(Ut.has(e))return;Ut.add(e)}for(var o=0;o{if(er===d)return e();var t=U,n=er;Wn(null),tr(d);var r=e();return Wn(t),tr(n),r};return i&&r.set(`length`,I(t.length,u)),new Proxy(t,{defineProperty(e,t,n){(!(`value`in n)||n.configurable===!1||n.enumerable===!1||n.writable===!1)&&Ie();var i=r.get(t);return i===void 0?f(()=>{var e=I(n.value,u);return r.set(t,e),e}):L(i,n.value,!0),!0},deleteProperty(e,t){var n=r.get(t);if(n===void 0){if(t in e){let e=f(()=>I(pe,u));r.set(t,e),Jt(o)}}else L(n,pe),Jt(o);return!0},get(e,n,i){if(n===A)return t;var o=r.get(n),s=n in e;if(o===void 0&&(!s||a(e,n)?.writable)&&(o=f(()=>I(Xt(s?e[n]:pe),u)),r.set(n,o)),o!==void 0){var c=G(o);return c===pe?void 0:c}return Reflect.get(e,n,i)},getOwnPropertyDescriptor(e,t){this.has?.(e,t);var n=Reflect.getOwnPropertyDescriptor(e,t),i=r.get(t);if(i!==void 0){var a=G(i);if(a===pe)return;if(n&&`value`in n)n.value=a;else return{enumerable:!0,configurable:!0,value:a,writable:!0}}return n},has(e,t){if(t===A)return!0;var n=r.get(t),i=n!==void 0&&n.v!==pe||Reflect.has(e,t);return(n!==void 0||W!==null&&(!i||a(e,t)?.writable))&&(n===void 0&&(n=f(()=>I(i?Xt(e[t]):pe,u)),r.set(t,n)),G(n)===pe)?!1:i},set(e,t,n,s){var c=r.get(t),l=t in e;if(i&&t===`length`)for(var d=n;dI(pe,u)),r.set(d+``,p)):L(p,pe)}if(c===void 0)(!l||a(e,t)?.writable)&&(c=f(()=>I(void 0,u)),L(c,Xt(n)),r.set(t,c));else{l=c.v!==pe;var m=f(()=>Xt(n));L(c,m)}var h=Reflect.getOwnPropertyDescriptor(e,t);if(h?.set&&h.set.call(s,n),!l){if(i&&typeof t==`string`){var g=r.get(`length`),_=Number(t);Number.isInteger(_)&&_>=g.v&&L(g,_+1)}Jt(o)}return!0},ownKeys(e){G(o);var t=Reflect.ownKeys(e).filter(e=>{var t=r.get(e);return t===void 0||t.v!==pe});for(var[n,i]of r)i.v!==pe&&!(n in e)&&t.push(n);return t},setPrototypeOf(){Le()}})}function Zt(e){try{if(typeof e==`object`&&e&&A in e)return e[A]}catch{}return e}function Qt(e,t){return Object.is(Zt(e),Zt(t))}var $t,en,tn,nn;function rn(){if($t===void 0){$t=window,en=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;tn=a(t,`firstChild`).get,nn=a(t,`nextSibling`).get,u(e)&&(e[oe]=void 0,e[ae]=null,e[se]=void 0,e.__e=void 0),u(n)&&(n[ce]=void 0)}}function an(e=``){return document.createTextNode(e)}function on(e){return tn.call(e)}function sn(e){return nn.call(e)}function R(e,t){if(!j)return on(e);var n=on(M);if(n===null)n=M.appendChild(an());else if(t&&n.nodeType!==3){var r=an();return n?.before(r),be(r),r}return t&&dn(n),be(n),n}function z(e,t=!1){if(!j){var n=on(e);return n instanceof Comment&&n.data===``?sn(n):n}if(t){if(M?.nodeType!==3){var r=an();return M?.before(r),be(r),r}dn(M)}return M}function B(e,t=!1){if(!j)return on(e);var n=R(e,t);return N(e),n}function V(e,t=1,n=!1){let r=j?M:e;for(var i;t--;)i=r,r=sn(r);if(!j)return r;if(n){if(r?.nodeType!==3){var a=an();return r===null?i?.after(a):r.before(a),be(a),a}dn(r)}return be(r),r}function cn(e){e.textContent=``}function ln(){return!1}function un(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function dn(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function fn(e){var t=W;if(t===null)return U.f|=te,e;if(!(t.f&32768)&&!(t.f&4))throw e;pn(e,t)}function pn(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128&&!(t.f&33570816)){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}function mn(e){W===null&&(U===null&&Ne(e),Me()),Vn&&je(e)}function hn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function gn(e,t){var n=W;n!==null&&n.f&8192&&(e|=x);var r={ctx:He,deps:null,nodes:null,f:e|y|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};F?.register_created_effect(r);var i=r;if(e&4)Et===null?At.ensure().schedule(r):Et.push(r);else if(t!==null){try{lr(r)}catch(e){throw jn(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=T))}if(i!==null&&(i.parent=n,n!==null&&hn(i,n),U!==null&&U.f&2&&!(e&64))){var a=U;(a.effects??=[]).push(i)}return r}function _n(){return U!==null&&!Un}function vn(e){let t=gn(8,null);return $e(t,v),t.teardown=e,t}function yn(e){mn(`$effect`);var t=W.f;if(!U&&t&32&&He!==null&&!He.i){var n=He;(n.e??=[]).push(e)}else return bn(e)}function bn(e){return gn(4|ee,e)}function xn(e){return mn(`$effect.pre`),gn(8|ee,e)}function Sn(e){At.ensure();let t=gn(64|E,e);return(e={})=>new Promise(n=>{e.outro?Pn(t,()=>{jn(t),n(void 0)}):(jn(t),n(void 0))})}function Cn(e){return gn(4,e)}function wn(e){return gn(k|E,e)}function Tn(e,t=0){return gn(8|t,e)}function H(e,t=[],n=[],r=[]){st(r,t,n,t=>{gn(8,()=>{e(...t.map(G))})})}function En(e,t=0){return gn(16|t,e)}function Dn(e){return gn(32|E,e)}function On(e){var t=e.teardown;if(t!==null){let n=Vn,r=U;Hn(!0),Wn(null);try{t.call(null)}catch(t){pn(t,e.parent)}finally{Hn(n),Wn(r)}}}function kn(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&at(()=>{e.abort(ue)});var r=n.next;n.f&64?n.parent=null:jn(n,t),n=r}}function An(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||jn(t),t=n}}function jn(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(Mn(e.nodes.start,e.nodes.end),n=!0),e.f|=w,kn(e,t&&!n),cr(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();On(e),e.f^=w,e.f|=S;var i=e.parent;i!==null&&i.first!==null&&Nn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Mn(e,t){for(;e!==null;){var n=e===t?null:sn(e);e.remove(),e=n}}function Nn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Pn(e,t,n=!0){var r=[];e.f|=256,Fn(e,r,!0);var i=()=>{n&&jn(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Fn(e,t,n){if(!(e.f&8192)){e.f^=x;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=!!(i.f&65536)||!!(i.f&32)&&!!(e.f&16);Fn(i,t,o?n:!1)}i=a}}}function In(e){e.f&=-257,Ln(e,!0)}function Ln(e,t){if(!(e.f&256)&&e.f&8192){e.f^=x,e.f&1024||($e(e,y),At.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=!!(n.f&65536)||!!(n.f&32);Ln(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Rn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:sn(n);t.append(n),n=i}}var zn=null,Bn=!1,Vn=!1;function Hn(e){Vn=e}var U=null,Un=!1;function Wn(e){U=e}var W=null;function Gn(e){W=e}var Kn=null;function qn(e){U!==null&&(U.f&2097152||U.f&2)&&(Kn??=new Set).add(e)}var Jn=null,Yn=0,Xn=null;function Zn(e){Xn=e}var Qn=1,$n=0,er=$n;function tr(e){er=e}function nr(){return++Qn}function rr(e){var t=e.f;if(t&2048)return!0;if(t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&St===null&&$e(e,v)}return!1}function ir(e,t,n=!0){var r=e.reactions;if(r!==null&&!(Kn!==null&&Kn.has(e)))for(var i=0;i{e.ac.abort(ue)}),e.ac=null);try{e.f|=O;var u=e.fn,d=u();e.f|=C;var f=or(e);if(qe()&&Xn!==null&&!Un&&f!==null&&!(e.f&6146))for(var p=0;p0)for(t.length=Yn+Jn.length,r=0;r{s.ac.abort(ue),s.ac=null,$e(s,y)}),vt(s),cr(s,0)}}function cr(e,t){var n=e.deps;if(n!==null)for(var r=t;rn?.call(this,e))}return e.startsWith(`pointer`)||e.startsWith(`touch`)||e===`wheel`?(i.__removed=!1,Xe(()=>{i.__removed||t.addEventListener(e,i,r)})):t.addEventListener(e,i,r),i}function Sr(e,t,n,r,i){var a={capture:r,passive:i},o=xr(e,t,n,a);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&vn(()=>{o.__removed=!0,t.removeEventListener(e,o,a)})}function K(e,t,n){(t[vr]??={})[e]=n}function Cr(e){for(var t=0;t{Tr=!1,wr=null}));var s=0,c=wr===e&&e[vr];if(c){var l=a.indexOf(c);if(l!==-1&&(t===document||t===window)){e[vr]=t;return}var u=a.indexOf(t);if(u===-1)return;l<=u&&(s=l)}if(o=a[s]||e.target,o!==t){i(e,`currentTarget`,{configurable:!0,get(){return o||n}});var d=U,f=W;Wn(null),Gn(null);try{for(var p,m=[];o!==null&&o!==t;){try{var h=o[vr]?.[r];h!=null&&(!o.disabled||e.target===o)&&h.call(o,e)}catch(e){p?m.push(e):p=e}if(e.cancelBubble)break;s++,o=s{throw e});throw p}}finally{e[vr]=t,delete e.currentTarget,Wn(d),Gn(f)}}}var Dr=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function Or(e){return Dr?.createHTML(e)??e}function kr(e){var t=un(`template`);return t.innerHTML=Or(e.replaceAll(``,``)),t.content}function Ar(e,t){var n=W;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function q(e,t){var n=!!(t&1),r=!!(t&2),i,a=!e.startsWith(``);return()=>{if(j)return Ar(M,null),M;i===void 0&&(i=kr(a?e:``+e),n||(i=on(i)));var t=r||en?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=on(t),s=t.lastChild;Ar(o,s)}else Ar(t,t);return t}}function jr(e,t,n=`svg`){var r=!e.startsWith(``),i=!!(t&1),a=`<${n}>${r?e:``+e}`,o;return()=>{if(j)return Ar(M,null),M;if(!o){var e=on(kr(a));if(i)for(o=document.createDocumentFragment();on(e);)o.appendChild(on(e));else o=on(e)}var t=o.cloneNode(!0);if(i){var n=on(t),r=t.lastChild;Ar(n,r)}else Ar(t,t);return t}}function Mr(e,t){return jr(e,t,`svg`)}function Nr(e=``){if(!j){var t=an(e+``);return Ar(t,t),t}var n=M;return n.nodeType===3?dn(n):(n.before(n=an()),be(n)),Ar(n,n),n}function Pr(){if(j)return Ar(M,null),M;var e=document.createDocumentFragment(),t=document.createComment(``),n=an();return e.append(t,n),Ar(t,n),e}function J(e,t){if(j){var n=W;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=M),xe();return}e!==null&&e.before(t)}function Fr(){if(j&&M&&M.nodeType===8&&M.textContent?.startsWith(`$`)){let e=M.textContent.substring(1);return xe(),e}return(window.__svelte??={}).uid??=1,`c${window.__svelte.uid++}`}function Ir(e){let t=0,n=Vt(0),r;return()=>{_n()&&(G(n),Tn(()=>(t===0&&(r=pr(()=>e(()=>Jt(n)))),t+=1,()=>{Xe(()=>{--t,t===0&&(r?.(),r=void 0,Jt(n))})})))}}var Lr=T|E;function Rr(e,t,n,r){new zr(e,t,n,r)}var zr=class{parent;is_pending=!1;transform_error;#e;#t=j?M:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=Ir(()=>(this.#m=Vt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=W;t.b=this,t.f|=128,n(e)},this.parent=W.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=En(()=>{if(j){let e=this.#t;xe();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#y():this.#g()}else this.#b()},Lr),j&&(this.#e=M)}#g(){try{this.#a=Dn(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed,{reset:n,invoke_onerror:r}=this.#v(e);Xe(r),t&&(this.#s=Dn(()=>{t(this.#e,()=>e,()=>n)}))}#v(e){var t=!1,n=!1;let r=()=>{if(t){ve();return}t=!0,n&&ze(),this.#s!==null&&Pn(this.#s,()=>{this.#s=null}),this.#S(()=>{this.#b()})};return{reset:r,invoke_onerror:()=>{try{n=!0,this.#n.onerror?.(e,r),n=!1}catch(e){pn(e,this.#i&&this.#i.parent)}}}}#y(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=Dn(()=>e(this.#e)),Xe(()=>{var e=this.#c=document.createDocumentFragment(),t=an(),n=!1;if(e.append(t),this.#a=this.#S(()=>{try{return Dn(()=>this.#r(t))}catch(e){try{this.error(e),n=!0}catch(e){pn(e,this.#i.parent)}return null}}),this.#a===null){this.#c=null,n&&this.#x(F);return}this.#u===0&&(this.#e.before(e),this.#c=null,Pn(this.#o,()=>{this.#o=null}),this.#x(F))}))}#b(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=Dn(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Rn(this.#a,e);let t=this.#n.pending;this.#o=Dn(()=>t(this.#e))}else this.#x(F)}catch(e){this.error(e)}}#x(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){tt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#S(e){var t=W,n=U,r=He;Gn(this.#i),Wn(this.#i),Ue(this.#i.ctx);try{return At.ensure(),e()}finally{Gn(t),Wn(n),Ue(r)}}#C(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#C(e,t);return}this.#u+=e,this.#u===0&&(this.#x(t),this.#o&&Pn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#C(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Xe(()=>{this.#d=!1,this.#m&&Gt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),G(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;F?.is_fork?(this.#a&&F.skip_effect(this.#a),this.#o&&F.skip_effect(this.#o),this.#s&&F.skip_effect(this.#s),F.oncommit(()=>{this.#w(e)})):this.#w(e)}#w(e){this.#a&&=(jn(this.#a),null),this.#o&&=(jn(this.#o),null),this.#s&&=(jn(this.#s),null),j&&(be(this.#t),Se(),be(Ce()));let t=this.#n.failed,n=e=>{let{reset:n,invoke_onerror:r}=this.#v(e);r(),t&&(this.#s=this.#S(()=>{try{return Dn(()=>{var r=W;r.b=this,r.f|=128,t(this.#e,()=>e,()=>n)})}catch(e){return pn(e,this.#i.parent),null}}))};Xe(()=>{var t;try{t=this.transform_error(e)}catch(e){pn(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(n,e=>pn(e,this.#i&&this.#i.parent)):n(t)})}};function Y(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[ce]??=e.nodeValue)&&(e[ce]=n,e.nodeValue=`${n}`)}function Br(e,t){return Hr(e,t)}var Vr=new Map;function Hr(e,{target:t,anchor:n,props:i={},events:a,context:o,intro:s=!0,transformError:c}){rn();var l=void 0,u=Sn(()=>{var s=n??t.appendChild(an());Rr(s,{pending:()=>{}},t=>{We({});var n=He;if(o&&(n.c=o),a&&(i.$$events=a),j&&Ar(t,null),l=e(t,i)||Ke(),j&&(W.nodes.end=M,M===null||M.nodeType!==8||M.data!==`]`))throw ge(),fe;Ge()},c);var u=new Set,d=e=>{for(var n=0;n{for(var e of u)for(let n of[t,document]){var r=Vr.get(n),i=r.get(e);--i==0?(n.removeEventListener(e,Er),r.delete(e),r.size===0&&Vr.delete(n)):r.set(e,i)}br.delete(d),s!==n&&s.parentNode?.removeChild(s)}});return Ur.set(l,u),l}var Ur=new WeakMap,Wr=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)In(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(In(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(jn(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Rn(r,t),t.append(an()),this.#n.set(e,{effect:r,fragment:t})}else jn(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Pn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(jn(n.effect),this.#n.delete(e))};ensure(e,t){var n=F,r=ln();if(t&&!this.#t.has(e)&&!this.#n.has(e)){if(r){var i=document.createDocumentFragment(),a=an();i.append(a),this.#n.set(e,{effect:Dn(()=>t(a)),fragment:i})}else this.#t.set(e,Dn(()=>t(this.anchor)))}if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else j&&(this.anchor=M),this.#a(n)}};function X(e,t,n=!1){var r;j&&(r=M,xe());var i=new Wr(e),a=n?T:0;function o(e,t){if(j){var n=we(r);if(e!==parseInt(n.substring(1))){var a=Ce();be(a),i.anchor=a,ye(!1),i.ensure(e,t),ye(!0);return}}i.ensure(e,t)}En(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Gr=Symbol(`NaN`);function Kr(e,t,n){j&&xe();var r=new Wr(e),i=!qe();En(()=>{var e=t();e!==e&&(e=Gr),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function qr(e,t){return t}function Jr(e,t,n){for(var i=[],a=t.length,o,s=t.length,c=0;c{if(o){if(o.pending.delete(n),o.done.add(n),o.pending.size===0){var t=e.outrogroups;Yr(e,r(o.done)),t.delete(o),t.size===0&&(e.outrogroups=null)}}else--s},!1)}if(s===0){var l=i.length===0&&n!==null&&e.pending.size===0;if(l){var u=n,d=u.parentNode;cn(d),d.append(u),e.items.clear()}Yr(e,t,!l)}else o={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(o)}function Yr(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var t=i();return e(t)?t:t==null?[]:r(t)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Qr(v,p,c,n,a),d!==null&&(p.length===0?d.f&33554432?(d.f^=D,ei(d,null,c)):In(d):Pn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:En(()=>{p=G(f);var e=p.length;let t=!1;j&&we(c)===`[!`!=(e===0)&&(c=Ce(),be(c),ye(!1),t=!0);for(var r=new Set,u=F,v=ln(),y=0;ys(c)):(d=Dn(()=>s(Xr??=an())),d.f|=D)),e>r.size&&Ae(``,``,``),j&&e>0&&be(Ce()),!h){if(m.set(u,r),v){for(let[e,t]of l)r.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u)}t&&ye(!0),G(f)}),flags:n,items:l,pending:m,outrogroups:null,fallback:d};h=!1,j&&(c=M)}function Zr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Qr(e,t,n,i,a){var o=!!(i&8),s=t.length,c=e.items,l=Zr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(o)for(v=0;v0){var E=i&4&&s===0?n:null;if(o){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function $r(e,t,n,r,i,a,o,s){var c=o&1?o&16?Vt(n):Ht(n,!1,!1):null,l=o&2?Vt(i):null;return{v:c,i:l,e:Dn(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function ei(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=sn(r);if(a.before(r),r===i)return;r=o}}function ti(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function ni(e,t,n){var r;j&&(r=M,xe());var i=new Wr(e);En(()=>{var e=t()??null;if(j&&we(r)===`[`!=(e!==null)){var a=Ce();be(a),i.anchor=a,ye(!1),i.ensure(e,e&&(t=>n(t,e))),ye(!0);return}i.ensure(e,e&&(t=>n(t,e)))},T)}var ri=[...` -\r\f\xA0\v`];function ii(e,t,n){var r=e==null?``:``+e;if(t&&(r=r?r+` `+t:t),n){for(var i of Object.keys(n))if(n[i])r=r?r+` `+i:i;else if(r.length)for(var a=i.length,o=0;(o=r.indexOf(i,o))>=0;){var s=o+a;(o===0||ri.includes(r[o-1]))&&(s===r.length||ri.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function ai(e,t=!1){var n=t?` !important;`:`;`,r=``;for(var i of Object.keys(e)){var a=e[i];a!=null&&a!==``&&(r+=` `+i+`: `+a+n)}return r}function oi(e){return e[0]!==`-`||e[1]!==`-`?e.toLowerCase():e}function si(e,t){if(t){var n=``,r,i;if(Array.isArray(t)?(r=t[0],i=t[1]):r=t,e){e=String(e).replaceAll(/\/\*.*?\*\//g,``).trim();var a=!1,o=0,s=!1,c=[];r&&c.push(...Object.keys(r).map(oi)),i&&c.push(...Object.keys(i).map(oi));var l=0,u=-1;let t=e.length;for(var d=0;d{t.every(_i)||(`__defaultValue`in e&&fi(e,!1),`__value`in e&&pi(e,e.__value))});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),vn(()=>{t.disconnect()})}function hi(e,t,n=t){var r=new WeakSet,i=!0;ot(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),gi);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&gi(o)}n(a),e.__value=a,F!==null&&r.add(F)}),Cn(()=>{var a=t();if(e===document.activeElement){var o=F;if(r.has(o))return}if(pi(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=gi(s),n(a))}e.__value=a,i=!1})}function gi(e){return`__value`in e?e.__value:e.value}function _i(e){if(e.target.closest(`selectedcontent`)!==null)return!0;if(e.type===`childList`){var t=[...e.addedNodes,...e.removedNodes];return t.length>0&&t.every(e=>e.nodeName===`SELECTEDCONTENT`)}return!1}var vi=Symbol(`is custom element`),yi=Symbol(`is html`),bi=de?`link`:`LINK`;function xi(e){if(j){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;Q(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;Q(e,`checked`,null),e.checked=r}}};e[le]=n,Xe(n),it()}}function Si(e,t){var n=Ci(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function Q(e,t,n,r){var i=Ci(e);j&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===bi)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[ie]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&Ti(e).has(t)?e[t]=n:e.setAttribute(t,n))}function Ci(e){return e[ae]??={[vi]:e.nodeName.includes(`-`),[yi]:e.namespaceURI===me}}var wi=new Map;function Ti(e){var t=e.getAttribute(`is`)||e.nodeName,n=wi.get(t);if(n)return n;wi.set(t,n=new Set);for(var r,i=e,a=Element.prototype;a!==i;){for(var s in r=o(i),r)r[s].set&&s!==`innerHTML`&&s!==`textContent`&&s!==`innerText`&&n.add(s);i=l(i)}return n}function Ei(e,t,n=t){var r=new WeakSet;ot(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=ji(e)?Mi(a):a,n(a),F!==null&&r.add(F),await ur(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(j&&e.defaultValue!==e.value||pr(t)==null&&e.value)&&(n(ji(e)?Mi(e.value):e.value),F!==null&&r.add(F)),Tn(()=>{var n=t();if(e===document.activeElement){var i=F;if(r.has(i))return}ji(e)&&n===Mi(e.value)||(e.type!==`date`||n||e.value)&&n!==e.value&&(e.value=n??``)})}var Di=new Set;function Oi(e,t,n,r,i=r){var a=n.getAttribute(`type`)===`checkbox`,o=e;let s=!1;if(t!==null)for(var c of t)o=o[c]??=[];o.push(n),ot(n,`change`,()=>{var e=n.__value;a&&(e=Ai(o,e,n.checked)),i(e)},()=>i(a?[]:null)),Tn(()=>{var e=r();if(j&&n.defaultChecked!==n.checked){s=!0;return}a?(e||=[],n.checked=e.includes(n.__value)):n.checked=Qt(n.__value,e)}),vn(()=>{var e=o.indexOf(n);e!==-1&&o.splice(e,1)}),Di.has(o)||(Di.add(o),Xe(()=>{o.sort((e,t)=>e.compareDocumentPosition(t)===4?-1:1),Di.delete(o)})),Xe(()=>{if(s){var e=a?Ai(o,e,n.checked):o.find(e=>e.checked)?.__value;i(e)}})}function ki(e,t,n=t){ot(e,`change`,t=>{n(t?e.defaultChecked:e.checked)}),(j&&e.defaultChecked!==e.checked||pr(t)==null)&&n(e.checked),Tn(()=>{e.checked=!!t()})}function Ai(e,t,n){for(var r=new Set,i=0;i{var n=this.#e.get(e);n.delete(t),n.size===0&&(this.#e.delete(e),this.#t.unobserve(e))}}#r(){return this.#t??=new ResizeObserver(t=>{for(var n of t){e.entries.set(n.target,n);for(var r of this.#e.get(n.target)||[])r(n)}})}}({box:`border-box`});function Pi(e,t,n){var r=Ni.observe(e,()=>n(e[t]));Cn(()=>(pr(()=>n(e[t])),r))}function Fi(e,t){return e===t||e?.[A]===t}function Ii(e=Ke(),t,n,r){var i=He.r,a=W;return Cn(()=>{var o,s;return Tn(()=>{o=s,s=r?.()||[],pr(()=>{Fi(n(...s),e)||(t(e,...s),o&&Fi(n(...o),e)&&t(null,...o))})}),()=>{let r=a;for(;r!==i&&r.parent!==null&&r.parent.f&33554432;)r=r.parent;let o=()=>{s&&Fi(n(...s),e)&&t(null,...s)},c=r.teardown;r.teardown=()=>{o(),c?.()}}}),e}function Li(e,t,n,r,i){var a=()=>{r(n[e])};n.addEventListener(t,a),i?Tn(()=>{n[e]=i()}):a(),(n===document.body||n===window||n===document)&&vn(()=>{n.removeEventListener(t,a)})}function Ri(e=!1){let t=He,n=t.l.u;if(!n)return;let r=()=>mr(t.s);if(e){let e=0,n={},i=dt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>G(i)}n.b.length&&xn(()=>{zi(t,r),m(n.b)}),yn(()=>{let e=pr(()=>n.m.map(p));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&yn(()=>{zi(t,r),m(n.a)})}function zi(e,t){if(e.l.s)for(let t of e.l.s)G(t);t()}var Bi=!1;function Vi(e){var t=Bi;try{return Bi=!1,[e(),Bi]}finally{Bi=t}}var Hi={get(e,t){let n=e.props.length;for(;n--;){let r=e.props[n];if(d(r)&&(r=r()),typeof r==`object`&&r&&t in r)return r[t]}},set(e,t,n){let r=e.props.length;for(;r--;){let i=e.props[r];d(i)&&(i=i());let o=a(i,t);if(o&&o.set)return o.set(n),!0}return!1},getOwnPropertyDescriptor(e,t){let n=e.props.length;for(;n--;){let r=e.props[n];if(d(r)&&(r=r()),typeof r==`object`&&r&&t in r){let e=a(r,t);return e&&!e.configurable&&(e.configurable=!0),e}}},has(e,t){if(t===A||t===re)return!1;for(let n of e.props)if(d(n)&&(n=n()),n!=null&&t in n)return!0;return!1},ownKeys(e){let t=[];for(let n of e.props)if(d(n)&&(n=n()),n){for(let e in n)t.includes(e)||t.push(e);for(let e of Object.getOwnPropertySymbols(n))t.includes(e)||t.push(e)}return t}};function Ui(...e){return new Proxy({props:e},Hi)}function Wi(e,t,n,r){var i=!Be||!!(n&2),o=!!(n&8),s=!!(n&16),c=r,l=!0,u=void 0,d=()=>s&&i?(u??=dt(r),G(u)):(l&&(l=!1,c=s?pr(r):r),c);let f;if(o){var p=A in e||re in e;f=a(e,t)?.set??(p&&t in e?n=>e[t]=n:void 0)}var m,h=!1;o?[m,h]=Vi(()=>e[t]):m=e[t],m===void 0&&r!==void 0&&(m=d(),f&&(i&&Fe(t),f(m)));var g=i?()=>{var n=e[t];return n===void 0?d():(l=!0,n)}:()=>{var n=e[t];return n!==void 0&&(c=void 0),n===void 0?c:n};if(i&&!(n&4))return g;if(f){var _=e.$$legacy;return(function(e,t){return arguments.length>0?((!i||!t||_||h)&&f(t?g():e),e):g()})}var v=!1,y=(n&1?dt:mt)(()=>(v=!1,g()));o&&G(y);var b=W;return(function(e,t){if(arguments.length>0){let n=t?G(y):i&&o?Xt(e):e;return L(y,n),v=!0,c!==void 0&&(c=n),e}return Vn&&v||b.f&16384?y.v:G(y)})}function Gi(e){He===null&&Oe(`onMount`),Be&&He.l!==null?qi(He).m.push(e):yn(()=>{let t=pr(e);if(typeof t==`function`)return t})}function Ki(e){He===null&&Oe(`onDestroy`),Gi(()=>()=>pr(e))}function qi(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`);function Ji(e,t){if(e instanceof RegExp)return{keys:!1,pattern:e};var n,r,i,a,o=[],s=``,c=e.split(`/`);for(c[0]||c.shift();i=c.shift();)n=i[0],n===`*`?(o.push(`wild`),s+=`/(.*)`):n===`:`?(r=i.indexOf(`?`,1),a=i.indexOf(`.`,1),o.push(i.substring(1,~r?r:~a?a:i.length)),s+=~r&&!~a?`(?:/([^/]+?))?`:`/([^/]+?)`,~a&&(s+=(~r?`?`:``)+`\\`+i.substring(a))):s+=`/`+i;return{keys:o,pattern:RegExp(`^`+s+(t?`(?=$|/)`:`/?$`),`i`)}}var Yi=new class{#e=I(Xi());get _loc(){return G(this.#e)}set _loc(e){L(this.#e,e)}#t=P(()=>this._loc.location);get _location(){return G(this.#t)}set _location(e){L(this.#t,e)}#n=P(()=>this._loc.querystring);get _querystring(){return G(this.#n)}set _querystring(e){L(this.#n,e)}#r=I(void 0);get _params(){return G(this.#r)}set _params(e){L(this.#r,e)}get loc(){return this._loc}get location(){return this._location}get querystring(){return this._querystring}get params(){return this._params}constructor(){typeof window<`u`?window.addEventListener(`hashchange`,()=>{this._loc=Xi()}):console.warn(`[svelte-spa-router] window 'window' is not defined, skipping initiation`)}};function Xi(){let e=typeof window<`u`?window.location.href:``,t=e.indexOf(`#/`),n=t>-1?e.substr(t+1):`/`,r=n.indexOf(`?`),i=``;return r>-1&&(i=n.substr(r+1),n=n.substr(0,r)),{location:n,querystring:i}}async function Zi(e){if(!e||e.length<1||e.charAt(0)!=`/`&&e.indexOf(`#/`)!==0)throw Error(`Invalid parameter location`);await ur();let t=(e.charAt(0)==`#`?``:`#`)+e;try{let e={...history.state};delete e.__svelte_spa_router_scrollX,delete e.__svelte_spa_router_scrollY,window.history.replaceState(e,``,t)}catch{console.warn("Caught exception while replacing the current page. If you're running this in the Svelte REPL, please note that the `replace` method might not work in this environment.")}window.dispatchEvent(new Event(`hashchange`))}function Qi(e){e?window.scrollTo(e.__svelte_spa_router_scrollX||0,e.__svelte_spa_router_scrollY||0):window.scrollTo(0,0)}function $i(e,t){We(t,!0);let n=Wi(t,`routes`,19,()=>({})),r=Wi(t,`prefix`,3,``),i=Wi(t,`restoreScrollState`,3,!1),a=Wi(t,`onConditionsFailed`,3,()=>{}),o=Wi(t,`onRouteLoaded`,3,()=>{}),s=Wi(t,`onRouteLoading`,3,()=>{}),c=Wi(t,`onRouteEvent`,3,()=>{});class l{path;component;conditions;userData;props;_pattern;_keys;constructor(e,t){let n=e=>typeof e==`object`&&!!e&&e._sveltesparouter===!0;if(!t||typeof t!=`function`&&!n(t))throw Error(`Invalid component object`);if(!e||typeof e==`string`&&(e.length<1||e.charAt(0)!=`/`&&e.charAt(0)!=`*`)||typeof e==`object`&&!(e instanceof RegExp))throw Error(`Invalid value for "path" argument - strings must start with / or *`);let r=Ji(e);if(this.path=e,n(t)){let e=t;this.component=e.component,this.conditions=e.conditions||[],this.userData=e.userData,this.props=e.props||{}}else{let e=t;this.component=()=>Promise.resolve(e),this.conditions=[],this.props={}}this._pattern=r.pattern,this._keys=r.keys}match(e){if(r()){if(typeof r()==`string`){if(e.startsWith(r()))e=e.substr(r().length)||`/`;else return null}else if(r()instanceof RegExp){let t=e.match(r());if(t&&t[0])e=e.substr(t[0].length)||`/`;else return null}}let t=this._pattern.exec(e);if(t===null)return null;if(this._keys===!1)return t;let n={},i=0;for(;i{u.push(new l(t,e))}):Object.keys(n()).forEach(e=>{let t=n();u.push(new l(e,t[e]))});let d=I(null),f=I(null),p=I({}),m=null,h=null;yn(()=>{history.scrollRestoration=i()?`manual`:`auto`}),yn(()=>{if(!i())return;let e=e=>{m=e.state&&(e.state.__svelte_spa_router_scrollY||e.state.__svelte_spa_router_scrollX)?e.state:null};return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)});async function g(e,t){await ur(),e(t)}yn(()=>{let e=Yi.loc,t=!1;return pr(async()=>{let n=0;for(;n{t=!0}});function _(e){return e&&typeof e==`object`&&Object.keys(e).length?e:null}var v=Pr(),y=z(v),b=e=>{let t=P(()=>G(d));var n=Pr(),r=z(n),i=e=>{var n=Pr();ni(z(n),()=>G(t),(e,t)=>{t(e,Ui({get params(){return G(f)},get onRouteEvent(){return c()}},()=>G(p)))}),J(e,n)},a=e=>{var n=Pr();ni(z(n),()=>G(t),(e,t)=>{t(e,Ui({get onRouteEvent(){return c()}},()=>G(p)))}),J(e,n)};X(r,e=>{G(f)?e(i):e(a,-1)}),J(e,n)};X(y,e=>{G(d)&&e(b)}),J(e,v),Ge()}var ea=[`bars`,`pace`,`pie`,`fuel`,`resets`,`thresholds`];function ta(e){return e===`zone`||e===`consumption-pace`?`pace`:typeof e==`string`&&ea.includes(e)?e:`bars`}var na=`codexometer.web.preferences.v1`,ra={statusLine:null,tab:`quota`,view:`bars`,selected:``,defaultDetail:0,layouts:[]};function ia(){try{let e=JSON.parse(localStorage.getItem(na)||`null`);return!e||typeof e!=`object`?ra:{statusLine:Array.isArray(e.statusLine)?[...new Set(e.statusLine.filter(e=>typeof e==`string`&&e.length<64))].slice(0,32):null,tab:[`quota`,`sessions`,`usage`].includes(e.tab)?e.tab:`quota`,view:ta(e.view),selected:typeof e.selected==`string`?e.selected.slice(0,256):``,defaultDetail:+(e.defaultDetail===1),layouts:Array.isArray(e.layouts)?e.layouts.filter(e=>{if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.id==`string`&&t.id.length<=256&&[0,1,2].includes(t.level)}).slice(-100):[]}}catch{return ra}}var aa=Xt(ia()),oa=Xt({open:!1});function sa(){let e=JSON.stringify(aa);try{localStorage.setItem(na,e)}catch{}}function ca(){return aa.tab===`quota`?`/quota/`+aa.view:`/`+aa.tab}function la(e){return aa.layouts.find(t=>t.id===e)?.level??aa.defaultDetail}function ua(e){aa.defaultDetail=e,aa.layouts=[]}function da(e,t){aa.layouts=[...aa.layouts.filter(t=>t.id!==e),{id:e,level:Math.max(0,Math.min(2,t))}].slice(-100)}var $=Xt({data:null,connected:!1,error:``}),fa=e=>e==null?`Unavailable`:e.toLocaleString(`en-GB`),pa=e=>!e||String(e).startsWith(`0001-`)?`Not yet available`:new Date(typeof e==`number`?e*1e3:e).toLocaleString(`en-GB`),ma=`codexometer.web.session`,ha=class extends Error{},ga;async function _a(e,t,n){if(!ga||!$.connected||!$.data?.control||$.data.sessionsError)throw Error(`Session controls unavailable. Check the live connection.`);return await ga(e,t,n)}function va(){let e=new AbortController,t,n=``;ga=async(t,r,i)=>{let a=await fetch(`/api/control/`+t,{method:`POST`,headers:{Authorization:`Bearer ${n}`,"Content-Type":`application/json`},body:JSON.stringify(r),signal:AbortSignal.any([e.signal,AbortSignal.timeout(8e3),...i?[i]:[]]),cache:`no-store`});if(!a.ok)throw a.status===502?Error(`Outcome uncertain. Check Codex before taking another action; nothing was retried.`):new ha(`Action rejected, expired or changed. Refresh and check Codex; nothing was retried.`);return a.json()};try{n=sessionStorage.getItem(ma)||``}catch{}let r=location.hash,i=r.startsWith(`#pair=`)?r.slice(6):``;(i||!r||r===`#`)&&(history.replaceState(null,``,`/#`+ca()),window.dispatchEvent(new HashChangeEvent(`hashchange`)));async function a(){if(i)try{let t=await fetch(`/api/pair`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({secret:i}),signal:e.signal});if(!t.ok)throw Error(`Pairing link expired or already used. Restart --web for a fresh link.`);n=(await t.json()).token;try{sessionStorage.setItem(ma,n)}catch{}}catch(t){e.signal.aborted||($.error=t instanceof Error?t.message:`Pairing failed`);return}if(!n){$.error=`Open the private pairing link printed by codexometer --web in your terminal.`;return}await o()}async function o(){try{let t=await fetch(`/api/events`,{headers:{Authorization:`Bearer ${n}`},signal:e.signal,cache:`no-store`});if(t.status===401){try{sessionStorage.removeItem(ma)}catch{}$.error=`Browser session expired. Restart --web and open its new pairing link.`;return}if(!t.ok||!t.body)throw Error(`Live connection unavailable`);let r=t.body.getReader(),i=new TextDecoder,a=``;for(;!e.signal.aborted;){let{done:e,value:t}=await r.read();if(e)break;a+=i.decode(t,{stream:!0});let n;for(;(n=a.indexOf(` - -`))>=0;){let e=a.slice(0,n);a=a.slice(n+2),e.startsWith(`data: `)&&($.data=JSON.parse(e.slice(6)),$.connected=!0,$.error=``)}}}catch{}finally{$.connected=!1}e.signal.aborted||($.error=`Connection lost — showing last observation. Reconnecting…`,t=setTimeout(o,2500))}return a(),()=>{ga=void 0,e.abort(),clearTimeout(t),$.connected=!1}}Ve();var ya=q(`

      Quota refresh failed. Policy state is based on the last successful - observation.

      `),ba=q(`

      `),xa=q(`

      The longest Codex quota window selects one active model profile. ASK - creates a per-session review; AUTO applies the profile on the next - eligible check.

      `,1),Sa=q(`

      No threshold-based model steps were configured at launch.

      `);function Ca(e,t){We(t,!1),Ri();var n=Pr(),r=z(n),i=e=>{var t=xa(),n=z(t),r=B(n),i=V(n,2),a=e=>{J(e,ya())};X(i,e=>{$.data.quotaError&&e(a)});var o=V(i,2),s=R(o),c=B(s),l=V(s,4);Z(l,5,()=>$.data.thresholds,qr,(e,t)=>{var n=ba();let r;var i=R(n),a=B(i),o=V(i,2),s=R(o),c=B(s,!0),l=B(V(s,2));N(o);var u=V(o,2),d=B(u,!0),f=B(V(u,2));N(n),H((e,i)=>{r=ci(n,1,``,null,r,{active:G(t).state===`ACTIVE`,next:G(t).state===`NEXT`}),Y(a,`${G(t).threshold??``}%`),Y(c,G(t).model),Y(l,`${e??``} REASONING // ${i??``} - SPEED`),Y(d,G(t).mode),Y(f,`${G(t).state??``}${G(t).state===`NEXT`?` // ${G(t).remaining||0} PP TO GO`:``}`)},[()=>G(t).effort.toUpperCase(),()=>G(t).speed.toUpperCase()]),J(e,n)}),N(l),N(o),H(e=>{Y(r,`MODEL STEP POLICY // OBSERVED ${e??``}`),Y(c,`THRESHOLDS // ${$.data.thresholds.length??``} CONFIGURED`)},[()=>pa($.data.quotaAt)]),J(e,t)},a=e=>{J(e,Sa())};X(r,e=>{$.data?.thresholds?.length?e(i):e(a,-1)}),J(e,n),Ge()}var wa=Mr(` `,1),Ta=Mr(` `,1),Ea=Mr(` `,1),Da=Mr(` `,1),Oa=q(`
      `,1),ka=q(` `),Aa=q(`
      Quota observations
      Observed atPeriod elapsedConsumedTrail segment
      `),ja=q(`
      OBSERVATION DATA
      `),Ma=q(`
      CONSUMPTIONQUOTA PERIOD ELAPSED
      `,1);function Na(e,t){let n=Fr();We(t,!0);let r=Wi(t,`trail`,19,()=>[]),i=I(!1),a=I(!0),o=I(`window`),s=[0,25,50,75,100],c={off:0,halfHour:30,hour:60,day:1440,window:0},l=I(400),u=I(240),d=P(()=>G(l)-24),f=P(()=>G(u)-48),p=P(()=>Math.max(1,G(d)-48)),m=P(()=>Math.max(1,G(f)-20)),h=P(()=>48+Math.max(0,Math.min(100,t.elapsed))/100*G(p));function g(e){return G(f)-(e-0)/100*G(m)}let _=P(()=>g(t.used)),v=P(()=>t.used-t.elapsed),y=P(()=>r().map((e,t)=>`${t===0||e.break?`M`:`L`}${48+e.elapsed/100*G(p)} ${g(e.used)}`).join(` `)),b=P(()=>{let e=0;for(let t=r().length-1;t>0;t--)if(r()[t].break){e=t;break}return r().slice(e)}),x=P(()=>G(b).length<2?0:Math.max(0,Date.parse(G(b).at(-1).at)-Date.parse(G(b)[0].at)));function S(e){if(G(x)Date.parse(e.at)>=t).length>=2}let C=P(()=>({off:!0,halfHour:S(18e5),hour:S(36e5),day:S(864e5),window:Number.isFinite(t.elapsed)&&t.elapsed>0&&Number.isFinite(t.used)}));yn(()=>{G(o)!==`window`&&!G(C)[G(o)]&&L(o,`window`)});function w(e){if(e===`off`||!G(C)[e])return[];let t=c[e]*6e4,n=Date.parse(G(b).at(-1).at)-t,r=G(b).findIndex(e=>Date.parse(e.at)>=n);return r<0?[]:G(b).slice(r)}function T(e){if(e.length<2)return null;let t=e.reduce((e,t)=>e+t.elapsed,0)/e.length,n=e.reduce((e,t)=>e+t.used,0)/e.length,r=0,i=0;for(let a of e)r+=(a.elapsed-t)*(a.used-n),i+=(a.elapsed-t)**2;return i>0?Math.max(0,r/i):null}let E=P(()=>{let e=G(o)===`window`&&G(C).window?t.used/t.elapsed:T(w(G(o)));if(e===null||!t.duration)return null;let n=t.used+e*(100-t.elapsed),r=G(o)===`window`?0:t.used-e*t.elapsed,i=e,a=Math.max(0,t.elapsed-c[G(o)]/t.duration*100);G(o)===`window`&&(a=0);let s=100;if(i!==0){let e=(0-r)/i,t=(100-r)/i;a=Math.max(a,Math.min(e,t)),s=Math.min(100,Math.max(e,t))}if(a>s)return null;let l=r+i*a,u=r+i*s,d=Math.hypot((s-a)/100*G(p),(u-l)/100*G(m)),f=Math.max(3,Math.min(9,Math.floor(d/65)+2));return{path:`M${Array.from({length:f},(e,t)=>{let n=a+(s-a)*t/(f-1),r=l+(u-l)*t/(f-1);return`${48+n/100*G(p)} ${g(r)}`}).join(` L`)}`,projected:n,safe:n<=100}}),ee=P(()=>{if(G(b).length===0)return`NO OBSERVATIONS`;if(G(b).length===1)return`ONE OBSERVATION`;let e=Math.floor(G(x)/6e4);if(e<60)return`${e} MIN CONTINUOUS`;let t=Math.floor(e/60);return t<24?`${t}H CONTINUOUS`:`${Math.floor(t/24)}D ${t%24}H CONTINUOUS`});var D=Ma(),O=z(D),k=R(O);let te;var A=R(k),ne=R(A),re=V(ne);N(A);var ie=V(A),ae=V(ie);Z(ae,17,()=>s,qr,(e,t)=>{var n=wa(),r=z(n),i=V(r),a=B(i);H(()=>{Q(r,`x1`,48+G(t)/100*G(p)),Q(r,`x2`,48+G(t)/100*G(p)),Q(r,`y2`,G(f)),Q(i,`x`,48+G(t)/100*G(p)),Q(i,`y`,G(f)+20),Y(a,`${G(t)??``}%`)}),J(e,n)});var oe=V(ae);Z(oe,17,()=>s,qr,(e,t)=>{var n=Ta(),r=z(n),i=V(r),a=B(i);H((e,n,o)=>{Q(r,`y1`,e),Q(r,`x2`,G(d)),Q(r,`y2`,n),Q(i,`y`,o),Y(a,`${G(t)??``}%`)},[()=>g(G(t)),()=>g(G(t)),()=>g(G(t))+4]),J(e,n)});var se=V(oe),ce=V(se),le=V(ce),ue=e=>{var t=Ea(),n=z(t),i=V(n),a=V(i),o=B(R(a));N(a),H((e,t)=>{Q(n,`d`,G(y)),Q(i,`d`,G(y)),Q(a,`cx`,48+r()[0].elapsed/100*G(p)),Q(a,`cy`,e),Y(o,`First observation: ${t??``}`)},[()=>g(r()[0].used),()=>pa(r()[0].at)]),J(e,t)};X(le,e=>{G(a)&&r().length&&e(ue)});var de=V(le),fe=e=>{var t=Da(),r=z(t),i=V(r),a=B(R(i));N(i),H(e=>{Q(r,`d`,G(E).path),Q(i,`d`,G(E).path),Q(i,`marker-mid`,`url(#${n}-trend-arrow)`),Y(a,`${G(o)===`window`?`Window-average pace`:`Recent consumption trajectory`} projects ${e??``}% at reset if continued`)},[()=>G(E).projected.toFixed(1)]),J(e,t)};X(de,e=>{G(E)&&e(fe)});var pe=V(de,2),me=V(pe),he=V(me),ge=B(R(he));N(he),N(k),N(O);var _e=V(O,2),ve=R(_e),j=R(ve),ye=V(j,3),M=B(ye,!0),be=V(ye,2),xe=e=>{var t=Oa(),n=B(V(z(t)));H(e=>Y(n,`TREND // ${e??``}`),[()=>G(E).projected>=100?`QUOTA EXHAUSTION PROJECTED`:`${G(E).projected.toFixed(1)}% PROJECTED AT RESET`]),J(e,t)};X(be,e=>{G(E)&&e(xe)}),N(ve);var Ce=V(ve,2),we=R(Ce),Te=R(we);xi(Te),Se(),N(we);var Ee=V(we,2),De=V(R(Ee),2),Oe=R(De);Oe.value=Oe.__value=`off`;var ke=V(Oe),Ae=B(ke);ke.value=ke.__value=`halfHour`;var je=V(ke),Me=B(je);je.value=je.__value=`hour`;var Ne=V(je),Pe=B(Ne);Ne.value=Ne.__value=`day`;var Fe=V(Ne),Ie=B(Fe);Fe.value=Fe.__value=`window`,N(De),mi(De),N(Ee);var Le=B(V(Ee,2));N(Ce),N(_e);var Re=V(_e,2),ze=e=>{var t=ja(),n=V(R(t),2),a=e=>{var t=Aa(),n=R(t),i=V(R(n),2);Z(i,21,r,qr,(e,t,n)=>{var r=ka(),i=R(r),a=R(i),o=B(a,!0);N(i);var s=V(i),c=B(s),l=V(s),u=B(l),d=B(V(l),!0);N(r),H((e,r)=>{Q(a,`datetime`,G(t).at),Y(o,e),Y(c,`${r??``}%`),Y(u,`${G(t).used??``}%`),Y(d,n===0?`First observation`:G(t).break?`Gap before this observation`:`Connected to previous observation`)},[()=>pa(G(t).at),()=>G(t).elapsed.toFixed(1)]),J(e,r)}),N(i),N(n),N(t),J(e,t)};X(n,e=>{G(i)&&e(a)}),N(t),Li(`open`,`toggle`,t,e=>L(i,e),()=>G(i)),J(e,t)};X(Re,e=>{r().length&&e(ze)}),H((e,r,i,a,o,s)=>{Q(k,`viewBox`,`0 0 ${G(l)} ${G(u)}`),Q(k,`aria-label`,e),te=ui(k,``,te,{"--trend-color":G(E)?.safe?`#115b35`:`#7f1825`}),Q(ne,`id`,n),Q(re,`id`,n+`-trend-arrow`),Q(ie,`width`,G(p)),Q(ie,`height`,G(m)),Q(ie,`fill`,`url(#${n})`),Q(se,`d`,`M48 20 V${G(f)} H${G(d)}`),Q(ce,`y1`,r),Q(ce,`x2`,G(d)),Q(ce,`y2`,i),Q(pe,`x`,48+G(p)/2),Q(pe,`y`,G(u)-5),Q(me,`cx`,G(h)),Q(me,`cy`,G(_)),Q(he,`cx`,G(h)),Q(he,`cy`,G(_)),Y(ge,`${t.used??``}% consumed // ${a??``}% of period elapsed`),Y(j,`${t.used??``}% USED // ${o??``}% TIME ELAPSED`),Y(M,s),ke.disabled=!G(C).halfHour,Y(Ae,`LAST 30 MINUTES${G(C).halfHour?``:` — UNAVAILABLE`}`),je.disabled=!G(C).hour,Y(Me,`LAST HOUR${G(C).hour?``:` — UNAVAILABLE`}`),Ne.disabled=!G(C).day,Y(Pe,`LAST 24 HOURS${G(C).day?``:` — UNAVAILABLE`}`),Fe.disabled=!G(C).window,Y(Ie,`FROM WINDOW START${G(C).window?``:` — NO TIME ELAPSED`}`),Y(Le,`HISTORY // ${G(ee)??``}`)},[()=>`Pace: ${t.used}% consumed, ${t.elapsed.toFixed(1)}% of quota period elapsed. ${G(v)>0?`Above`:G(v)<0?`Below`:`On`} the steady-consumption line.${G(a)&&r().length?` Trace path contains ${r().length} ${r().length===1?`observation`:`observations`}; gaps are not interpolated.`:``}`,()=>g(0),()=>g(100),()=>t.elapsed.toFixed(1),()=>t.elapsed.toFixed(1),()=>Math.abs(G(v))<.05?`ON PACE`:G(v)>0?`ABOVE THE LINE — CONSUMING FASTER THAN TIME`:`BELOW THE LINE — WITHIN PACE`]),Pi(O,`clientWidth`,e=>L(l,e)),Pi(O,`clientHeight`,e=>L(u,e)),ki(Te,()=>G(a),e=>L(a,e)),hi(De,()=>G(o),e=>L(o,e)),J(e,D),Ge()}var Pa=q(` `),Fa=q(`

      Quota refresh failed. Values below are the last successful observation.

      `),Ia=q(`

      Expiry details unavailable. No listed expiry does not mean no expiry.

      `),La=q(`

      `),Ra=q(`

      Read-only preview. Use the terminal to redeem a reset.

      The backend may return only some credits. This list does not establish - redemption order.

      `),za=q(` `,1),Ba=Mr(``),Va=Mr(``),Ha=q(`
      `),Ua=q(`

      Cycle duration or reset date unavailable — position cannot be - plotted.

      `),Wa=q(`
      EMPTYFULL
      `),Ga=q(`

      `,1),Ka=q(`
      `,1),qa=q(`

      `),Ja=q(`

      `),Ya=q(`

      No quota windows reported yet.

      `),Xa=q(`
      `,1),Za=q(`

      All reported windows are shown. API-equivalent learning and quota status - scoring remain in the terminal for this first preview.

      `,1),Qa=q(` `,1);function $a(e,t){We(t,!0);let n=Wi(t,`params`,19,()=>({})),r=P(()=>ea.filter(e=>e!==`thresholds`||!!$.data?.thresholds?.length)),i=I(Xt(Date.now())),a=P(()=>G(r).includes(ta(n().view))?ta(n().view):`bars`);Gi(()=>{let e=setInterval(()=>L(i,Date.now(),!0),1e3);return()=>clearInterval(e)});function o(e){return!e.duration||e.duration<=0||!e.reset?null:Math.max(0,Math.min(100,100*(1-(e.reset*1e3-G(i))/(e.duration*6e4))))}yn(()=>{aa.view=G(a),(n().view===`zone`||n().view===`consumption-pace`)&&Zi(`/quota/pace`)});function s(e){let t=e/100*Math.PI*2;return`M60 60 L60 14 A46 46 0 ${+(e>50)} 1 ${60+46*Math.sin(t)} ${60-46*Math.cos(t)} Z`}var c=Qa(),l=z(c);Z(l,21,()=>G(r),qr,(e,t)=>{var n=Pa();let r;var i=B(n,!0);H(e=>{Q(n,`href`,`#/quota/`+G(t)),Q(n,`aria-current`,G(a)===G(t)?`page`:void 0),r=ci(n,1,``,null,r,{active:G(a)===G(t)}),Y(i,e)},[()=>G(t)===`fuel`?`FUEL TANK`:G(t).toUpperCase()]),J(e,n)}),N(l);var u=V(l,2),d=e=>{var t=Za(),n=z(t),r=e=>{J(e,Fa())};X(n,e=>{$.data.quotaError&&e(r)});var i=V(n,2),c=B(i),l=V(i,2),u=e=>{Ca(e,{})},d=e=>{var t=Ra(),n=R(t),r=B(n),i=V(n,4),a=e=>{J(e,Ia())};X(i,e=>{$.data.credits.length||e(a)}),Z(V(i,2),17,()=>$.data.credits,qr,(e,t)=>{var n=La(),r=R(n),i=B(r),a=B(V(r,2),!0);N(n),H(e=>{Y(i,`${(G(t).title||`Quota reset`)??``} // ${G(t).status??``}`),Y(a,e)},[()=>G(t).expiryKnown?G(t).expires?`EXPIRES `+pa(G(t).expires):`Does not expire`:`Expiry information unavailable`]),J(e,n)}),Se(2),N(t),H(()=>Y(r,`RESET INVENTORY // ${$.data.creditCount??``} AVAILABLE`)),J(e,t)},f=e=>{var t=Xa(),n=z(t);let r;Z(n,21,()=>$.data.meters,qr,(e,t)=>{let n=P(()=>o(G(t)));var r=Ja(),i=R(r),c=B(i,!0),l=V(i,2),u=R(l),d=e=>{var n=za(),r=z(n),i=B(r),a=B(V(r));H(()=>{Y(i,`FREE ${100-G(t).used}%`),Y(a,`USED ${G(t).used??``}%`)}),J(e,n)},f=e=>{var n=za(),r=z(n),i=B(r),a=B(V(r));H(()=>{Y(i,`USED ${G(t).used??``}%`),Y(a,`FREE ${100-G(t).used}%`)}),J(e,n)};X(u,e=>{G(a)===`fuel`?e(d):e(f,-1)}),N(l);var p=V(l,2);let m;var h=R(p),g=e=>{var n=Ha(),r=R(n),i=V(R(r)),a=e=>{J(e,Ba())},o=e=>{var n=Va();H(e=>Q(n,`d`,e),[()=>s(G(t).used)]),J(e,n)};X(i,e=>{G(t).used>=100?e(a):G(t).used>0&&e(o,1)}),N(r),N(n),H(()=>Q(r,`aria-label`,`${G(t).used}% quota used`)),J(e,n)},_=e=>{var r=Pr(),i=z(r),a=e=>{{let r=P(()=>G(t).trail||[]);Na(e,{get used(){return G(t).used},get elapsed(){return G(n)},get trail(){return G(r)},get duration(){return G(t).duration}})}},o=e=>{J(e,Ua())};X(i,e=>{G(n)===null?e(o,-1):e(a)}),J(e,r)},v=e=>{var r=Ka(),i=z(r),o=R(i);let s;N(i);var c=V(i,2),l=e=>{J(e,Wa())};X(c,e=>{G(a)===`fuel`&&e(l)});var u=V(c,2),d=e=>{var t=Ga(),r=z(t),i=B(r),o=V(r,2),s=R(o);let c;N(o),H(e=>{Y(i,`RESET CYCLE // ${e??``}% ELAPSED`),c=ui(s,``,c,{width:`${G(a)===`fuel`?100-G(n):G(n)}%`})},[()=>Math.floor(G(n))]),J(e,t)};X(u,e=>{G(n)!==null&&e(d)}),H(()=>{Q(i,`aria-label`,G(a)===`fuel`?`Fuel remaining`:`Quota used`),Q(i,`aria-valuenow`,G(a)===`fuel`?100-G(t).used:G(t).used),s=ui(o,``,s,{width:`${G(a)===`fuel`?100-G(t).used:G(t).used}%`})}),J(e,r)};X(h,e=>{G(a)===`pie`?e(g):G(a)===`pace`?e(_,1):e(v,-1)}),N(p);var y=V(p,2),b=B(y),x=V(y,2),S=e=>{var n=qa(),r=B(n,!0);H(()=>Y(r,G(t).details)),J(e,n)};X(x,e=>{G(t).details&&e(S)}),N(r),H(e=>{Y(c,G(t).name),m=ci(p,1,`meter-graphic`,null,m,{"bar-graphic":G(a)===`bars`||G(a)===`fuel`}),Y(b,`RESETS // ${e??``}`)},[()=>pa(G(t).reset)]),J(e,r)}),N(n);var i=V(n,2),c=e=>{J(e,Ya())};X(i,e=>{$.data.meters.length||e(c)}),H(()=>r=ci(n,1,`quota-grid`,null,r,{radial:G(a)===`pie`,zone:G(a)===`pace`})),J(e,t)};X(l,e=>{G(a)===`thresholds`?e(u):G(a)===`resets`?e(d,1):e(f,-1)}),Se(2),H(e=>Y(c,`QUOTA // OBSERVED ${e??``}`),[()=>pa($.data.quotaAt)]),J(e,t)};X(u,e=>{$.data&&e(d)}),J(e,c),Ge()}var eo=q(`
      `),to=q(`

      `,1);function no(e,t){We(t,!0);let n=Wi(t,`values`,19,()=>[]),r=Wi(t,`label`,3,`Token activity`),i=Wi(t,`capacity`,3,0),a=P(()=>Math.max(0,...n())),o=P(()=>i()>n().length?[...Array(i()-n().length).fill(0),...n()]:n());var s=to(),c=z(s),l=B(c),u=V(c,2);Z(u,21,()=>G(o),qr,(e,t)=>{var n=eo();let r;H((e,t)=>{Q(n,`title`,e),r=ui(n,``,r,{height:t})},[()=>G(t).toLocaleString(`en-GB`)+` tokens`,()=>`${100*G(t)/Math.max(1,G(a))}%`]),J(e,n)}),N(u),H((e,t)=>{Y(l,`SCALE // 0 — ${e??``} TOKENS`),Q(u,`aria-label`,t)},[()=>G(a).toLocaleString(`en-GB`),()=>`${r()}. Peak ${G(a).toLocaleString(`en-GB`)} tokens.`]),J(e,s),Ge()}var ro=q(`

      `),io=q(`

      `,1),ao=q(`

      `),oo=q(`

      Loading / sending…

      `),so=q(`
      `),co=q(`

      Confirmation expired. Reopen the option.

      `),lo=q(`

      `,1),uo=q(`

       
      `,1),fo=q(` `),po=q(``),mo=q(`

      No available matching commands.

      `),ho=q(`

      ↑/↓ select · Tab complete · Enter options · Escape dismiss

      `),go=q(`
      `,1),_o=q(` `,1),vo=q(`
      `),yo=q(`
      `);function bo(e,t){We(t,!0);let n=Wi(t,`query`,3,``),r=Wi(t,`onchange`,3,()=>{}),i=Wi(t,`oncomplete`,3,e=>{}),a=I(!1),o=I(!1),s=I(``),c=I(null),l=I(null),u=I(``),d=I(0),f=I(Xt(Date.now())),p=I(``),m=0,h=I(0),g=I(null),_=I(void 0),v=setInterval(()=>{L(f,Date.now(),!0)},1e3);Ki(()=>{clearInterval(v),m++});let y=P(()=>n().trim().startsWith(`/`)&&!n().trim().startsWith(`//`)),b=P(()=>G(c)?.path===``&&G(y)?n().trim().slice(1).toLowerCase():``),x=P(()=>(G(c)?.choices||[]).filter(e=>!G(b)||e.label.toLowerCase().startsWith(`/`+G(b)))),S=P(()=>!$.connected||!!$.data?.sessionsError),C=P(()=>G(a)&&G(y)&&(!G(c)||G(c).path===``)&&!G(l));yn(()=>{n(),L(h,0)}),yn(()=>{let e=G(h);G(x),G(C)&&ur().then(()=>{let t=G(_)?.children[e];if(!t||!G(_))return;let n=G(_).getBoundingClientRect(),r=t.getBoundingClientRect();r.topn.bottom&&(G(_).scrollTop+=r.bottom-n.bottom)})});function w(e){if(!G(y)||!G(a)||G(c)&&G(c).path!==``||G(l)||G(S)||e.isComposing||e.ctrlKey||e.metaKey||e.altKey||![`ArrowUp`,`ArrowDown`,`Tab`,`Enter`,`Escape`].includes(e.key)||e.key===`Tab`&&e.shiftKey)return;if(e.preventDefault(),e.key===`Escape`){L(a,!1),L(g,n()),L(u,``);return}if(e.key===`ArrowUp`||e.key===`ArrowDown`){L(h,Math.min(Math.max(G(h)+(e.key===`ArrowUp`?-1:1),0),Math.max(G(x).length-1,0)),!0);return}let t=G(x)[G(h)];t&&!G(o)&&(e.key===`Tab`?i()(t.label):E(t))}yn(()=>{G(S)&&(m++,L(o,!1),L(u,``),G(a)&&(L(a,!1),L(g,n())),L(c,null),L(l,null))}),yn(()=>{G(y)&&!G(a)&&n()!==G(g)&&!G(S)&&(L(a,!0),T(``))});async function T(e){let n=++m;L(o,!0),L(l,null),L(u,``),L(s,``);try{let r=await _a(`commands`,{session:t.session,command:{mode:`list`,path:e}});n===m&&((e===``||e===`help`)&&r.choices.push({id:`local-statusline`,label:`/statusline`,help:`Choose multiple fields for Codexometer’s detail footer.`,next:`statusline`}),L(c,r,!0),L(p,r.value||``,!0),(r.path.startsWith(`rename/`)||r.path.startsWith(`cd/`))&&r.choices.length===1&&E(r.choices[0]))}catch{n===m&&(L(s,`Command catalogue unavailable. Use Codex.`),(e===``||e===`help`)&&L(c,{title:`/ COMMANDS`,help:`Local display settings remain available.`,path:``,revision:``,choices:[{id:`local-statusline`,label:`/statusline`,help:`Choose multiple fields for Codexometer’s detail footer.`,next:`statusline`}]},!0))}finally{n===m&&L(o,!1)}}async function E(e){if(e.next===`statusline`){oa.open=!0,L(a,!1),L(g,n());return}if(e.next){await T(e.next);return}if(L(l,e,!0),L(u,``),L(s,``),!e.action||!G(c))return;let r=++m;L(o,!0);try{let n=await _a(`commands`,{session:t.session,command:{mode:`prepare`,path:G(c).path,revision:G(c).revision,choice:e.id}});r===m&&(L(u,n.confirmation,!0),L(d,Date.parse(n.expires),!0))}catch{r===m&&L(s,`Option changed or unavailable. Refresh before retrying.`)}finally{r===m&&L(o,!1)}}async function ee(){if(!G(u)||G(S)||G(o)||G(f)>=G(d)||!G(c))return;let e=G(u);L(u,``),L(o,!0);let n=++m;try{let i=await _a(`commands`,{session:t.session,confirmation:e,command:{mode:`commit`,path:G(c).path}});n===m&&(L(s,i.message,!0),L(a,!1),L(c,null),L(l,null),L(d,0),L(g,null),r()())}catch{n===m&&L(s,`Change unconfirmed. Check Codex before retrying.`)}finally{n===m&&L(o,!1)}}var D={handleKey:w},O=yo(),k=R(O),te=V(k,2),A=e=>{var t=ro(),n=B(t,!0);H(()=>Y(n,G(s))),J(e,t)};X(te,e=>{G(s)&&!G(a)&&e(A)});var ne=V(te,2),re=e=>{var n=vo();let r;var i=R(n),a=e=>{var t=io(),n=z(t),r=B(n,!0),i=B(V(n,2),!0);H(()=>{Y(r,G(c)?.title||`/ COMMANDS`),Y(i,G(c)?.help||`Loading live options…`)}),J(e,t)};X(i,e=>{G(C)||e(a)});var m=V(i,2),g=e=>{var t=ao(),n=B(t,!0);H(()=>Y(n,G(s))),J(e,t)};X(m,e=>{G(s)&&e(g)});var v=V(m,2),b=e=>{J(e,oo())};X(v,e=>{G(o)&&e(b)});var w=V(v,2),D=e=>{var t=so(),n=R(t),r=R(n),i=V(r);xi(i),N(n);var a=V(n,2),s=B(a);N(t),H((e,t)=>{Y(r,`${e??``} `),Q(i,`aria-label`,G(c).inputLabel||`Session name`),Q(i,`maxlength`,G(c).inputLimit||512),i.disabled=G(o)||G(S),a.disabled=t,Y(s,`REVIEW ${G(c).path===`cd`?`DIRECTORY`:`RENAME`}`)},[()=>(G(c).inputLabel||`Session name`).toUpperCase(),()=>G(o)||G(S)||!G(p).trim()]),Sr(`submit`,t,e=>{e.preventDefault(),G(c)&&G(p).trim()&&!G(o)&&!G(S)&&T(G(c).path+`/`+encodeURIComponent(G(p).trim()))}),Ei(i,()=>G(p),e=>L(p,e)),J(e,t)},O=e=>{var n=uo(),r=z(n),i=B(r,!0),a=V(r,2),s=B(a,!0),p=V(a,2),m=e=>{var n=lo(),r=z(n),i=R(r),a=V(i),s=e=>{J(e,Nr(`Only this session's saved name - changes.`))},l=P(()=>G(c)?.path.startsWith(`rename/`)),p=e=>{J(e,Nr(`Only this session’s directory - changes; project configuration is not reloaded.`))},m=P(()=>G(c)?.path.startsWith(`cd/`)),h=e=>{J(e,Nr(`Changes affect subsequent turns of this session, not global - defaults. Automatic quota thresholds may later supersede model - settings.`))};X(a,e=>{G(l)?e(s):G(m)?e(p,1):e(h,-1)}),N(r);var g=V(r,2),_=V(g,2),v=e=>{J(e,co())};X(_,e=>{G(u)&&G(f)>=G(d)&&e(v)}),H(()=>{Y(i,`TARGET // ${t.session??``}. `),g.disabled=G(o)||G(S)||!G(u)||G(f)>=G(d)}),K(`click`,g,ee),J(e,n)};X(p,e=>{G(l).action&&e(m)});var h=V(p,2);H(()=>{Y(i,G(l).label),Y(s,G(l).help||`No additional help supplied by Codex.`),h.disabled=G(o)}),K(`click`,h,()=>{L(l,null),L(u,``)}),J(e,n)},k=e=>{var t=go(),n=z(t);let r;Z(n,23,()=>G(x),e=>e.id,(e,t,n)=>{var r=po();let i;var a=R(r),s=B(a,!0),l=V(a),u=B(l,!0),d=V(l),f=e=>{var n=fo(),r=B(n,!0);H(()=>Y(r,G(t).help)),J(e,n)};X(d,e=>{G(y)&&G(c)?.path===``&&e(f)}),N(r),H(()=>{r.disabled=G(o)||G(S),Q(r,`title`,G(t).help),Q(r,`aria-label`,G(t).label+(G(t).next?` →`:G(t).action?``:` // HELP`)),i=ci(r,1,`svelte-7bfwr9`,null,i,{suggested:G(y)&&G(c)?.path===``&&G(h)===G(n)}),Y(s,G(t).label),Y(u,G(t).next?` →`:G(t).action?``:` // HELP`)}),K(`click`,r,()=>E(G(t))),J(e,r)}),N(n),Ii(n,e=>L(_,e),()=>G(_));var i=V(n,2),a=e=>{J(e,mo())};X(i,e=>{!G(o)&&!G(x).length&&e(a)});var s=V(i,2),l=e=>{J(e,ho())};X(s,e=>{G(y)&&G(c)?.path===``&&e(l)}),H(()=>r=ci(n,1,`command-options svelte-7bfwr9`,null,r,{vertical:G(C)})),J(e,t)};X(w,e=>{G(c)?.input?e(D):G(l)?e(O,1):e(k,-1)});var te=V(w,2),A=e=>{var t=_o(),n=z(t),r=V(n,2);H(()=>{n.disabled=G(o),r.disabled=G(o)}),K(`click`,n,()=>T(``)),K(`click`,r,()=>T(G(c)?.path||``)),J(e,t)};X(te,e=>{G(C)||e(A)}),N(n),H(()=>{Q(n,`aria-label`,G(C)?`Slash command suggestions`:void 0),r=ci(n,1,`svelte-7bfwr9`,null,r,{"command-popup":G(C)})}),J(e,n)};return X(ne,e=>{G(a)&&e(re)}),N(O),H(()=>k.disabled=G(S)||G(o)),K(`click`,k,()=>{L(a,!G(a)),L(g,G(a)?null:n(),!0),L(u,``),G(a)&&T(``)}),J(e,O),Ge(D)}Cr([`click`]);var xo=q(` `,1),So=q(`
      `),Co=q(`
      /
      `);function wo(e,t){We(t,!0);let n=P(()=>t.lines.filter(e=>e.kind===`addition`).length),r=P(()=>t.lines.filter(e=>e.kind===`removal`).length);var i=Co(),a=R(i),o=R(a),s=B(o),c=B(V(o,2));N(a),Z(V(a,2),17,()=>t.lines,qr,(e,t)=>{var n=So();let r;var i=R(n),a=e=>{var n=xo(),r=z(n),i=B(r,!0),a=B(V(r),!0);H(()=>{Y(i,G(t).old||``),Y(a,G(t).new||``)}),J(e,n)};X(i,e=>{G(t).kind!==`heading`&&G(t).kind!==`metadata`&&e(a)});var o=B(V(i),!0);N(n),H(()=>{r=ci(n,1,`diff-line svelte-x4gtb6`,null,r,{added:G(t).kind===`addition`,removed:G(t).kind===`removal`,heading:G(t).kind===`heading`}),Y(o,G(t).text)}),J(e,n)}),N(i),H(()=>{Q(a,`aria-label`,`${G(n)} lines added, ${G(r)} lines removed`),Y(s,`+${G(n)??``}`),Y(c,`−${G(r)??``}`)}),J(e,i),Ge()}var To=q(`

      `),Eo=q(`

      Send this saved prompt now? This starts Codex work and consumes quota.

      `,1),Do=q(``),Oo=q(`

       

      In memory only. Follows session model, reasoning and speed. Closing - Codexometer cancels pending work.

      `),ko=q(`

      CURRENT PROFILE

      MODEL / REASONING LEVEL / SPEED

       

      PROPOSED PROFILE

      MODEL / REASONING LEVEL / SPEED

       

      Applied settings remain after Codexometer closes.

      `,1),Ao=q(`

      `),jo=q(`

       
      `,1),Mo=q(`

      Command unavailable from this observation. Open Codex to inspect the - request.

      `),No=q(`

      Session controls temporarily unavailable. Check Codex for current state.

      `),Po=q(`

      Checking session controls…

      `),Fo=q(`

      `),Io=q(`
      About browser controls

      Controls require a supported live request from a connected shared - app-server session. Local observations alone cannot provide them.

      `),Lo=q(` `,1),Ro=q(` `),zo=q(`Grants permission beyond this one action. Check the scope - carefully.`),Bo=q(``),Vo=q(``),Ho=q(``),Uo=q(``),Wo=q(`

      Not before this time; when quota and idle allow.

      `),Go=q(`

      `,1),Ko=q(`

      The existing trigger remains active until saved or deleted. If - it changes or sends while you edit, saving is blocked.

      `),qo=q(` `,1),Jo=q(``),Yo=q(``),Xo=q(``),Zo=q(` `,1),Qo=q(`
    • `),$o=q(`
      View fixed choices

      Type one of these choices exactly. Your answer stays masked.

        `),es=q(` `,1),ts=q(`

        `),ns=q(`

        `,1),rs=q(` `,1),is=q(``),as=q(`

        `,1),os=q(`

        `);function ss(e,t){We(t,!0);let n=[],r=Wi(t,`observedCommand`,3,``),i=Wi(t,`observedFileChanges`,19,()=>[]),a=Wi(t,`review`,3,``),o=Wi(t,`suspended`,3,!1),s=Wi(t,`openScheduler`,3,!1),c=Wi(t,`onProtectedChange`,3,e=>{}),l=I(null),u=I(Xt([])),d=I(null),f=I(``),p=I(0),m=I(Xt(Date.now())),h=I(!1),g=I(``),_=I(!1),v=I(!1),y=I(``),b=I(!1),x=I(``),S=I(!1),C=I(``),w=I(`now`),T=I(60),E=I(``),ee=I(!1),D=I(``),O=I(Xt([])),k=P(()=>G(O).find(e=>e.status!==`sent`)),te=I(void 0),A=I(void 0);function ne(e){if(G(b)){ur().then(()=>G(te)?.focus());return}if(e.status!==`pending`||G(h)||!G(l)?.id||G(l).kind!==`prompt`||G(l).questions?.length)return;L(b,!0),L(x,e.id,!0),ur().then(()=>G(te)?.focus()),L(u,[e.text],!0),L(w,e.trigger===`quota`?`quota`:`at`,!0),L(f,``),L(C,``);let t=new Date(e.at);e.trigger!==`quota`&&L(E,new Date(t.getTime()-t.getTimezoneOffset()*6e4).toISOString().slice(0,16),!0)}yn(()=>{s()&&G(k)&&!G(S)&&G(l)?.id&&G(l).kind===`prompt`&&!G(l).questions?.length&&(L(S,!0),ne(G(k)))});async function re(){if(!a())try{L(O,await _a(`schedules`,{session:t.session},ve.signal),!0),G(b)&&!G(O).some(e=>e.id===G(x)&&e.status===`pending`)&&L(f,``),G(C)&&!G(O).some(e=>e.id===G(C)&&e.status===`pending`)&&(L(f,``),L(C,``))}catch{}}async function ie(e){try{L(O,await _a(`schedules`,{session:t.session,cancelId:e},ve.signal),!0),L(b,!1),L(f,``),L(C,``),L(u,[],!0)}catch(e){L(g,e instanceof Error?e.message:`Unable to cancel trigger.`,!0)}}yn(()=>(c()(G(h)||G(u).some(e=>e.length>0)),()=>c()(!1)));let ae=P(()=>$.data?.sessions.find(e=>e.id===t.session)?.status),oe=P(()=>!$.connected||!!$.data?.sessionsError),se=P(()=>a()!==`profile`&&G(l)?.kind===`prompt`&&!G(l).questions?.length&&!!$.data?.profiles?.some(e=>e.session===t.session&&e.pending)),ce=P(()=>!G(oe)&&!G(v)&&!!G(l)?.id&&G(l).kind===`approval`&&G(y)!==G(l).id),le=P(()=>G(ce)?G(l).command:r()),ue=P(()=>!!G(f)&&G(m)G(l)?.questions?.length?G(l).questions:[{text:`Follow-up message`,secret:!1,freeText:!0,options:[]}]),fe=P(()=>G(l)?.kind===`approval`||G(l)?.kind===`profile`?G(d)!==null:G(u).length===G(de).length&&G(u).every((e,t)=>e.trim().length>0&&(G(de)[t].freeText||G(de)[t].options?.includes(e)))),pe=P(()=>G(l)?.kind===`prompt`&&!G(l).questions?.length&&G(w)!==`now`),me=P(()=>G(w)===`delay`?G(m)+G(T)*6e4:Date.parse(G(E))),he=P(()=>{if(!G(pe))return``;if(G(b)&&!G(O).some(e=>e.id===G(x)&&e.status===`pending`))return`Trigger changed, removed or already sent. Go back and reopen it; your draft has not been saved.`;if(!G(u)[0]?.trim())return`Enter a message to schedule.`;if(G(w)===`quota`)return``;if(!Number.isFinite(G(me))||G(me)<=G(m))return`Choose a future date/time or a delay greater than zero.`;let e=new Date(G(m));return e.setFullYear(e.getFullYear()+1),G(me)>=e.getTime()?`Choose a time within one year.`:``});function ge(e){return new Date(e).toLocaleString([],{timeZoneName:`short`})}function _e(e){if(e.status!==`pending`)return e.status;if(e.trigger===`at`&&Date.parse(e.at)>G(m))return`Due in `+Math.ceil((Date.parse(e.at)-G(m))/6e4)+` min`;let t=G(oe)?`waiting for a fresh connection`:e.waitingReason||(e.canSend?`ready; awaiting dispatch`:`waiting for quota and an eligible idle session`);return(e.trigger===`at`?`Time reached — `:``)+t}yn(()=>{(G(oe)||o()||G(se)||G(m)>=G(p))&&L(f,``)});let ve=new AbortController;Gi(()=>{let e=e=>{e.ctrlKey&&e.key.toLowerCase()===`s`&&!a()&&G(k)&&(e.preventDefault(),ne(G(k)))};window.addEventListener(`keydown`,e);let n,r=setInterval(()=>{L(m,Date.now(),!0)},1e3);async function i(){await re();try{let e=await _a(`offer`,{session:t.session,...a()?{review:a()}:{}},ve.signal);if(ve.signal.aborted)return;L(v,!1),G(l)?.id!==e.id&&(L(f,``),L(d,null),L(u,Array(Math.max(1,e.questions?.length||0)).fill(``),!0)),L(l,e,!0),G(_)&&e.id&&e.id!==G(y)&&(L(g,``),L(_,!1))}catch{ve.signal.aborted||(L(l,null),L(v,!0),L(f,``))}finally{ve.signal.aborted||(n=setTimeout(i,2e3))}}return i(),()=>{window.removeEventListener(`keydown`,e),ve.abort(),clearTimeout(n),clearInterval(r)}});async function j(e){if(!e&&G(l)?.kind===`prompt`&&!G(l).questions?.length&&G(u)[0]?.trim().startsWith(`/`)&&!G(u)[0]?.trim().startsWith(`//`)){L(g,`Choose a slash command from COMMANDS; use // for literal slash text.`);return}if(!G(l)?.id||G(h)||G(oe)||o()||G(se)||(e?!e.canSend:!G(fe)||G(he)))return;let n=G(l).id;L(h,!0),L(g,``),L(_,!1),L(f,``),L(C,e?.id||``,!0);try{let r=!e&&G(l).kind===`prompt`&&!G(l).questions?.length&&G(w)!==`now`?{trigger:G(w)===`quota`?`quota`:`at`,zone:Intl.DateTimeFormat().resolvedOptions().timeZone,...G(w)===`quota`?{}:{at:(G(w)===`delay`?new Date(Date.now()+G(T)*6e4):new Date(G(E))).toISOString()}}:void 0;L(ee,!!r),L(D,r?r.trigger===`quota`?`After fresh quota becomes available, when the session is idle`:ge(r.at)+` — not before this time; when quota and idle allow`:``,!0);let i=await _a(`prepare`,{session:t.session,...a()?{review:a()}:{},offer:n,...e?{sendId:e.id}:{},...r?{schedule:r}:{},...r&&G(b)?{editId:G(x)}:{},...G(l).kind===`approval`||G(l).kind===`profile`?{choice:G(d)}:{answers:e?[e.text]:[...G(u)]}},ve.signal);G(l)?.id===n&&!ve.signal.aborted&&!G(oe)&&!o()&&!G(se)&&(L(f,i.confirmation,!0),L(p,Date.parse(i.expires),!0))}catch(e){ve.signal.aborted||L(g,e instanceof Error?e.message:`Unable to prepare action.`,!0)}finally{L(h,!1)}}async function ye(){if(!G(l)?.id||G(h)||o()||G(se)||!G(ue))return;let e=G(l).id,n=G(l).kind,r=G(f);L(f,``),L(h,!0),L(y,e,!0);try{await _a(`commit`,{session:t.session,...a()?{review:a()}:{},offer:e,confirmation:r},ve.signal),L(_,!0),L(b,!1),G(ee)&&(L(y,``),await re()),L(g,G(ee)?`Trigger set. Closing Codexometer cancels it.`:n===`profile`?`Profile decision completed.`:n===`approval`?`Decision sent.`:`Text sent.`,!0)}catch(e){L(g,e instanceof ha?e.message:`Outcome uncertain. Check Codex before taking another action; nothing was retried.`,!0)}finally{L(h,!1),L(C,``),await re(),L(u,[],!0),L(d,null)}}var M=Lo(),be=z(M),xe=e=>{var t=Pr();Z(z(t),17,()=>G(O).filter(e=>e.status!==`sent`),e=>e.id,(e,t)=>{var n=Oo(),r=R(n),i=B(r,!0),a=V(r,2),s=B(a),c=V(a,2),u=B(c,!0),d=V(c,2),p=B(d,!0),m=V(d,2),_=B(m,!0),v=V(m,2),y=e=>{var t=To(),n=B(t,!0);H(()=>Y(n,G(g))),J(e,t)};X(v,e=>{G(g)&&e(y)});var b=V(v,4),x=V(b,2),S=V(x,2),w=e=>{var n=Eo(),r=V(z(n),2),i=V(r,2);H(()=>r.disabled=G(h)||G(oe)||!G(t).canSend),K(`click`,r,ye),K(`click`,i,()=>{L(f,``),L(C,``)}),J(e,n)},T=e=>{var n=Do();H(()=>n.disabled=G(h)||G(oe)||!G(t).canSend||o()),K(`click`,n,()=>j(G(t))),J(e,n)};X(S,e=>{G(ue)&&G(C)===G(t).id?e(w):e(T,-1)}),N(n),H((e,n)=>{Y(i,G(t).status===`uncertain`?`CHECK TRIGGER — outcome uncertain; not retried`:`SCHEDULED FOLLOW-UP`),Y(s,`TARGET // ${G(t).session??``}`),Y(u,e),Y(p,n),Y(_,G(t).text),b.disabled=G(h)||!G(l)?.id||G(l).kind!==`prompt`||!!G(l).questions?.length||G(t).status!==`pending`,x.disabled=G(h)||G(t).status===`sending`},[()=>G(t).trigger===`quota`?`After confirmed quota recovery, when idle`:`Not before ${ge(G(t).at)}`,()=>_e(G(t))]),K(`click`,b,()=>ne(G(t))),K(`click`,x,()=>ie(G(t).id)),J(e,n)}),J(e,t)},Ce=P(()=>!a()&&G(l)?.kind!==`approval`&&!G(l)?.questions?.length&&![`APPROVAL NEEDED`,`INPUT NEEDED`].includes(G(ae)||``));X(be,e=>{G(Ce)&&e(xe)});var we=V(be,2),Te=e=>{var r=os(),o=R(r),s=B(o,!0),c=V(o,2),x=e=>{var t=ko(),n=z(t),r=B(n),i=V(n,6),a=B(i,!0),o=B(V(i,6),!0);Se(2),H(()=>{Y(r,`Your ${G(l).profile.threshold??``}% quota threshold has been reached. Review - the profile for subsequent turns.`),Y(a,G(l).profile.current),Y(o,G(l).profile.proposed)}),J(e,t)};X(c,e=>{G(l)?.profile&&!G(oe)&&!G(v)&&e(x)});var S=V(c,2),C=e=>{var t=Ao();let n;var r=B(t,!0);H(()=>{n=ci(t,1,`svelte-1oupzfc`,null,n,{notice:!G(_),sent:G(_)}),Y(r,G(g))}),J(e,t)};X(S,e=>{G(g)&&e(C)});var O=V(S,2),k=e=>{wo(e,{get lines(){return G(l).fileChanges}})},ne=e=>{wo(e,{get lines(){return i()}})},re=e=>{var t=jo(),n=z(t),r=B(n,!0),i=B(V(n,2),!0);H(()=>{Y(r,G(ce)?`EXACT COMMAND`:`LAST OBSERVED COMMAND`),Y(i,G(le))}),J(e,t)},ie=e=>{J(e,Mo())};X(O,e=>{G(l)?.fileChanges?.length&&!G(oe)&&!G(v)?e(k):i().length?e(ne,1):G(le)?e(re,2):a()!==`profile`&&G(ae)===`APPROVAL NEEDED`&&!G(_)&&e(ie,3)});var se=V(O,2),pe=e=>{J(e,No())},_e=e=>{J(e,Po())},ve=e=>{var t=Lo(),n=z(t),r=e=>{var t=Fo(),n=B(t,!0);H(e=>Y(n,e),[()=>G(ae)===`WORKING`?`Codex is working — nothing to respond to.`:[`APPROVAL NEEDED`,`INPUT NEEDED`].includes(G(ae)||``)?`Respond in Codex for this request.`:`Nothing needs a response right now.`]),J(e,t)};X(n,e=>{(G(ae)===`WORKING`||!G(_)&&!G(h))&&e(r)});var i=V(n,2),a=e=>{J(e,Io())},o=P(()=>[`APPROVAL NEEDED`,`INPUT NEEDED`].includes(G(ae)||``)&&!G(_));X(i,e=>{G(o)&&e(a)}),J(e,t)},M=e=>{var r=as(),i=z(r),o=B(i),s=V(i,2),c=R(s),g=B(c,!0),_=V(c,2),v=e=>{var r=Pr();Z(z(r),17,()=>G(l).choices||[],qr,(e,r,i)=>{var o=Bo(),s=R(o);xi(s),s.value=s.__value=i;var c=V(s),l=V(c),u=e=>{var t=Ro(),n=B(t,!0);H(()=>Y(n,G(r).detail)),J(e,t)};X(l,e=>{G(r).detail&&e(u)});var f=V(l,2),p=e=>{J(e,zo())};X(f,e=>{G(r).persistent&&e(p)}),N(o),H(()=>{Q(s,`name`,`decision-`+t.session+`-`+a()),Y(c,` ${G(r).label??``} `)}),Oi(n,[],s,()=>G(d),e=>L(d,e)),J(e,o)}),J(e,r)},y=e=>{var n=Lo(),r=z(n),i=e=>{var t=qo(),n=z(t),r=V(R(n)),i=R(r),a=e=>{var t=Vo();t.value=t.__value=`now`,J(e,t)};X(i,e=>{G(b)||e(a)});var o=V(i);o.value=o.__value=`quota`;var s=V(o);s.value=s.__value=`delay`;var c=V(s);c.value=c.__value=`at`,N(r),mi(r),N(n);var l=V(n,2),u=e=>{var t=Ho(),n=V(R(t));xi(n),N(t),Ei(n,()=>G(T),e=>L(T,e)),J(e,t)};X(l,e=>{G(w)===`delay`&&e(u)});var d=V(l,2),f=e=>{var t=Uo(),n=V(R(t));xi(n),N(t),Ei(n,()=>G(E),e=>L(E,e)),J(e,t)};X(d,e=>{G(w)===`at`&&e(f)});var p=V(d,2),m=e=>{var t=Go(),n=z(t),r=B(n),i=V(n,2),a=e=>{J(e,Wo())};X(i,e=>{G(w)!==`quota`&&e(a)});var o=V(i,2);o.textContent=`Timezone: ${Intl.DateTimeFormat().resolvedOptions().timeZone??``}. - One pending request per session; saving replaces it. Memory - only; Codexometer must stay running. Uses session model, - reasoning and speed.`,H(e=>Y(r,`Will send: ${e??``}`),[()=>G(w)===`quota`?`when quota is available and this session is idle`:Number.isFinite(G(me))?ge(G(me)):`choose a date and time`]),J(e,t)};X(p,e=>{G(w)!==`now`&&e(m)});var h=V(p,2),g=e=>{J(e,Ko())};X(h,e=>{G(b)&&e(g)}),hi(r,()=>G(w),e=>L(w,e)),J(e,t)};X(r,e=>{G(l).questions?.length||e(i)}),Z(V(r,2),17,()=>G(de),qr,(e,n,r)=>{var i=es(),o=z(i),s=e=>{{let n=P(()=>G(u)[0]||``);Ii(bo(e,{get session(){return t.session},get query(){return G(n)},oncomplete:e=>{L(u,[e],!0),ur().then(()=>G(te)?.focus())},onchange:()=>{L(u,[``],!0),ur().then(()=>G(te)?.focus())}}),e=>L(A,e,!0),()=>G(A))}};X(o,e=>{r===0&&a()!==`profile`&&G(l).kind===`prompt`&&!G(l).questions?.length&&e(s)});var c=V(o,2),d=R(c),f=V(d),p=e=>{var t=Jo();xi(t),Ei(t,()=>G(u)[r],e=>G(u)[r]=e),J(e,t)},m=e=>{var t=Xo(),i=R(t);i.value=i.__value=``,Z(V(i),17,()=>G(n).options||[],qr,(e,t)=>{var n=Yo(),r=B(n,!0),i={};H(()=>{Y(r,G(t)),i!==(i=G(t))&&(n.value=(n.__value=i)??``)}),J(e,n)}),N(t),mi(t),hi(t,()=>G(u)[r],e=>G(u)[r]=e),J(e,t)},h=e=>{var t=Zo(),i=z(t);nt(i),Ii(i,e=>L(te,e),()=>G(te));var a=V(i,2),o=e=>{var t=Fo(),r=B(t);H(e=>Y(r,`Suggested answers: ${e??``}`),[()=>G(n).options.join(` · `)]),J(e,t)};X(a,e=>{G(n).options?.length&&e(o)}),K(`keydown`,i,e=>{G(l)?.kind===`prompt`&&!G(l).questions?.length&&G(A)?.handleKey(e)}),Ei(i,()=>G(u)[r],e=>G(u)[r]=e),J(e,t)};X(f,e=>{G(n).secret?e(p):G(n).freeText?e(h,-1):e(m,1)}),N(c);var g=V(c,2),_=e=>{var t=$o(),r=V(R(t),4);Z(r,21,()=>G(n).options||[],qr,(e,t)=>{var n=Qo(),r=B(n,!0);H(()=>Y(r,G(t))),J(e,n)}),N(r),N(t),J(e,t)};X(g,e=>{G(n).secret&&!G(n).freeText&&e(_)}),H(()=>Y(d,`${G(n).text??``} `)),J(e,i)}),J(e,n)};X(_,e=>{G(l).kind===`approval`||G(l).kind===`profile`?e(v):e(y,-1)}),N(s);var x=V(s,2),S=e=>{var t=ns(),n=z(t),r=e=>{var t=ts(),n=B(t);H(()=>Y(n,`TRIGGER // ${G(D)??``}`)),J(e,t)};X(n,e=>{G(ee)&&e(r)});var i=V(n,2),a=B(i),o=V(i,2),s=B(o),c=V(o,2);H(e=>{Y(a,`Check the target and ${G(l).kind===`profile`?`profile above`:G(l).kind===`approval`?G(l).fileChanges?.length?`proposed file changes above`:`exact command and permission scope`:`message above`}. ${G(ee)?`This schedules a follow-up that will send automatically when eligible`:`This will send to Codex`}; it may start work using your quota. - Confirmation expires in ${e??``}s.`),o.disabled=G(h)||G(oe),Y(s,`CONFIRM ${(G(l).kind===`approval`||G(l).kind===`profile`?G(l).choices?.[G(d)]?.label:G(ee)?G(b)?`SAVE CHANGES`:`SCHEDULE`:`SEND`)??``}`)},[()=>Math.max(0,Math.ceil((G(p)-G(m))/1e3))]),K(`click`,o,ye),K(`click`,c,()=>{L(f,``)}),J(e,t)},C=e=>{var t=rs(),n=z(t),r=e=>{var t=To(),n=B(t,!0);H(()=>Y(n,G(he))),J(e,t)};X(n,e=>{G(he)&&e(r)});var i=V(n,2),a=B(i,!0);H(()=>{i.disabled=G(h)||G(oe)||!G(fe)||!!G(he),Y(a,G(h)?`SENDING…`:G(b)?`REVIEW CHANGES`:`REVIEW BEFORE SENDING`)}),K(`click`,i,()=>j()),J(e,t)};X(x,e=>{G(ue)?e(S):e(C,-1)});var O=V(x,2),k=e=>{var t=is();K(`click`,t,()=>{L(b,!1),L(f,``),L(u,[],!0)}),J(e,t)};X(O,e=>{G(b)&&e(k)}),H(()=>{Y(o,`TARGET // ${G(l).thread??``} // ${(G(l).directory||`Directory unavailable`)??``}`),s.disabled=G(h)||G(ue)||G(oe),Y(g,G(l).kind===`approval`||G(l).kind===`profile`?`Choose a decision`:G(b)?`EDIT SCHEDULED FOLLOW-UP`:`Reply to this session`)}),J(e,r)};X(se,e=>{G(oe)||G(v)?e(pe):G(l)?G(l).id?G(y)!==G(l).id&&e(M,3):e(ve,2):e(_e,1)}),N(r),H(()=>Y(s,a()===`profile`?`QUOTA THRESHOLD`:`SESSION CONTROL // EXPERIMENTAL`)),J(e,r)};X(we,e=>{!G(se)&&(!G(k)||G(b)||a()||G(l)?.kind!==`prompt`||G(l).questions?.length)&&e(Te)}),J(e,M),Ge()}Cr([`click`,`keydown`]);var cs=q(` `,1),ls=q(`
        `),us=q(`

        STATUS LINE // CODEXOMETER

        Choose multiple fields and their order. These display preferences do not - change Codex CLI configuration. Missing values are omitted.

        `),ds=q(`

        `,1);function fs(e,t){We(t,!0);let n=Wi(t,`stale`,3,!1),r=I(Xt([])),i=I(!1),a=P(()=>$.data?.statusLineFields||[]),o=e=>[...new Set(e??G(a).filter(e=>e.default).map(e=>e.id))].filter(e=>G(a).some(t=>t.id===e)),s=e=>o(e).map(e=>e===`run-state`&&(n()||!$.connected||$.data?.sessionsError)?`STALE`:t.session.statusLine?.[e]).filter(Boolean).join(` · `),c=P(()=>[...G(r).map(e=>G(a).find(t=>t.id===e)).filter(Boolean),...G(a).filter(e=>!G(r).includes(e.id))]);yn(()=>{oa.open&&!G(i)?(L(r,o(aa.statusLine),!0),L(i,!0)):oa.open||L(i,!1)});function l(e,t){L(r,t?[...G(r),e]:G(r).filter(t=>t!==e),!0)}function u(e,t){let n=G(r).indexOf(e),i=n+t;if(n<0||i<0||i>=G(r).length)return;let a=[...G(r)];[a[n],a[i]]=[a[i],a[n]],L(r,a,!0)}var d=ds(),f=z(d),p=e=>{var t=us(),n=V(R(t),4);Z(n,21,()=>G(c),e=>e.id,(e,t)=>{var n=ls(),i=R(n),a=R(i);xi(a);var o=V(a,1,!0),s=B(V(o),!0);N(i);var c=V(i,2),d=e=>{var n=cs(),i=z(n),a=V(i,2);H((e,n)=>{Q(i,`aria-label`,`Move `+G(t).label+` earlier`),i.disabled=e,Q(a,`aria-label`,`Move `+G(t).label+` later`),a.disabled=n},[()=>G(r).indexOf(G(t).id)===0,()=>G(r).indexOf(G(t).id)===G(r).length-1]),K(`click`,i,()=>u(G(t).id,-1)),K(`click`,a,()=>u(G(t).id,1)),J(e,n)},f=P(()=>G(r).includes(G(t).id));X(c,e=>{G(f)&&e(d)}),N(n),H(e=>{Q(i,`title`,G(t).help),Si(a,e),Y(o,G(t).label),Y(s,G(t).help)},[()=>G(r).includes(G(t).id)]),K(`change`,a,e=>l(G(t).id,e.currentTarget.checked)),J(e,n)}),N(n);var i=V(n,2),a=B(i),o=V(i,2),d=V(o,2);N(t),H(e=>Y(a,`Preview // ${e??``}`),[()=>s(G(r))||`—`]),K(`click`,o,()=>{aa.statusLine=[...G(r)],sa(),oa.open=!1}),K(`click`,d,()=>{oa.open=!1}),J(e,t)};X(f,e=>{oa.open&&e(p)});var m=V(f,2),h=R(m),g=B(h,!0),_=V(h,2);N(m),H((e,t)=>{Q(h,`title`,e),Y(g,t)},[()=>s(aa.statusLine),()=>s(aa.statusLine)]),K(`click`,_,()=>{oa.open=!oa.open}),J(e,d),Ge()}Cr([`change`,`click`]);var ps=q(`
        `);function ms(e,t){We(t,!0);let n=Wi(t,`active`,3,!1),r=P(()=>t.session.text||t.session.contextKind===`LAST ACTIVITY`&&t.session.workingCommand||``),i=P(()=>[`LAST REPLY`,`LAST ACTIVITY`].includes(t.session.contextKind)&&!!G(r).trim()),a=I(!1),o=I(``),s=I(!1);yn(()=>{if(!G(s))return;let e=setTimeout(()=>{L(s,!1)},150);return()=>clearTimeout(e)}),yn(()=>{G(r),t.session.status,L(o,``)});async function c(){if(!G(i)||G(a))return;let e=G(r);L(a,!0),L(s,!0),L(o,``);try{await navigator.clipboard.writeText(e),G(r)===e&&L(o,`Copied.`)}catch{G(r)===e&&L(o,`Clipboard unavailable. Select the visible text and copy it manually.`)}finally{L(a,!1)}}function l(e){!n()||!G(i)||e.defaultPrevented||e.repeat||e.ctrlKey||e.metaKey||e.altKey||e.key.toLowerCase()!==`c`||e.target instanceof Element&&e.target.closest(`input, textarea, select, [contenteditable]`)||(e.preventDefault(),c())}var u=Pr();Sr(`keydown`,$t,l);var d=z(u),f=e=>{var t=ps(),n=R(t),r=B(n,!0),i=V(n,2);let l;N(t),H(()=>{Y(r,G(o)),i.disabled=G(a),l=ci(i,1,`svelte-543j00`,null,l,{flashed:G(s)})}),K(`click`,i,c),J(e,t)};X(d,e=>{G(i)&&e(f)}),J(e,u),Ge()}Cr([`click`]);var hs=q(`

         
        `,1),gs=q(`

        LATEST GUIDANCE

         
        `,1),_s=q(`

        CONTEXT

         

        `,1),vs=q(`

        `),ys=q(`

        `),bs=q(`
         
        `),xs=q(`

         
        `,1),Ss=q(`
         
        `),Cs=q(`

        Command unavailable from this observation. Open Codex to inspect the - request.

        `),ws=q(`

        `,1),Ts=q(`

        `),Es=q(` `,1),Ds=q(`
        `),Os=q(`

        Session connection or refresh unavailable. Context and telemetry may be - stale.

        `),ks=q(`

        Some quota profile checks are unavailable. Only sessions with freshly - verified quota and settings can be updated; previous outcome notices remain - visible.

        `),As=q(` `),js=q(` `),Ms=q(` `),Ns=q(``),Ps=q(`

        Read only — reply or approve in Codex.

        `),Fs=q(`

        `),Is=q(`QUOTA THRESHOLD // REVIEW PROFILE ↗`),Ls=q(`
        `),Rs=q(`
        `),zs=q(`

        This session is no longer in the current observation. Return to sessions.

        `),Bs=q(`

        `),Vs=q(``),Hs=q(`

        `),Us=q(` `),Ws=q(`

        `),Gs=q(`QUOTA THRESHOLD // REVIEW PROFILE ↗`),Ks=q(` `,1),qs=q(`

        `),Js=q(`

        TOKEN ACTIVITY // 30 SECOND SAMPLES

        `),Ys=q(`

        TOKENS

        FULL DETAIL →
        `),Xs=q(`

        No locally observed sessions yet. Keep Codex running alongside - Codexometer.

        `),Zs=q(`

        ↑ ↓ SELECT SESSION // ← LESS DETAIL // → MORE DETAIL // ESC BACK

        `,1),Qs=q(`

        SESSION TOTALS

        `,1);function $s(e,t){We(t,!0);let n=(e,t=f,n,r)=>{let i=mt(()=>g(n?.(),!0)),a=mt(()=>g(r?.(),!1));var o=Es(),s=z(o),c=e=>{var n=hs(),r=z(n),i=B(r,!0),a=B(V(r,2),!0);H(()=>{Y(i,t().contextKind===`LAST REPLY`&&!t().streaming?`TASK`:`CURRENT TASK`),Y(a,t().currentTask)}),J(e,n)};X(s,e=>{t().currentTask&&e(c)});var l=V(s,2),u=e=>{var n=gs(),r=B(V(z(n),2),!0);H(()=>Y(r,t().latestGuidance)),J(e,n)};X(l,e=>{t().latestGuidance&&e(u)});var d=V(l,2),p=e=>{var n=_s(),r=B(V(z(n),2),!0);Se(2),H(()=>Y(r,t().approvalContext)),J(e,n)};X(d,e=>{G(a)&&t().contextKind===`APPROVAL REQUEST`&&t().approvalContext&&e(p)});var m=V(d,2),_=e=>{var n=vs();let r;var i=B(n,!0);H(e=>{r=ci(n,1,`attention-note`,null,r,{inferred:t().status===`CHECK SESSION`}),Y(i,e)},[()=>C(t())]),J(e,n)},v=P(()=>C(t())&&(!G(a)||G(h)||t().status===`CHECK SESSION`));X(m,e=>{G(v)&&e(_)});var y=V(m,2),b=e=>{var n=ys(),r=B(n,!0);H(e=>Y(r,e),[()=>w(t())]),J(e,n)};X(y,e=>{G(i)&&e(b)});var x=V(y,2),S=e=>{var n=bs(),r=B(n,!0);H(()=>Y(r,t().text||`No session context available.`)),J(e,n)};X(x,e=>{(t().text||!t().workingCommand)&&e(S)});var T=V(x,2),E=e=>{var n=xs(),r=V(z(n),2),i=R(r),a=V(i),o=e=>{var n=Nr();H(()=>Y(n,`// +${(t().runningCommands||0)-+(t().commandStatus===`running`)} RUNNING`)),J(e,n)};X(a,e=>{!G(h)&&(t().runningCommands||0)>+(t().commandStatus===`running`)&&e(o)});var s=V(a,2),c=e=>{J(e,Nr(`+`))};X(s,e=>{t().runningLimited&&e(c)}),N(r);var l=B(V(r,2),!0);H(e=>{Y(i,`COMMAND // ${e??``} `),Y(l,t().workingCommand)},[()=>G(h)?`UNKNOWN`:(t().commandStatus||`unknown`).toUpperCase()]),J(e,n)};X(T,e=>{t().workingCommand&&e(E)});var ee=V(T,2),D=e=>{wo(e,{get lines(){return t().fileChanges}})},O=e=>{var n=ws(),r=V(z(n),2),i=B(r,!0),a=V(r,2),o=e=>{var n=Ss(),r=B(n,!0);H(()=>Y(r,t().command)),J(e,n)},s=e=>{J(e,Cs())};X(a,e=>{t().command?e(o):e(s,-1)}),H(()=>Y(i,G(h)||t().status!==`APPROVAL NEEDED`?`LAST OBSERVED COMMAND`:$.data?.control?`COMMAND REQUEST`:`COMMAND TO APPROVE IN CODEX`)),J(e,n)};X(ee,e=>{(!G(a)||!$.data?.control)&&t().fileChanges?.length?e(D):(!G(a)||!$.data?.control)&&(t().command||t().status===`APPROVAL NEEDED`)&&e(O,1)});var k=V(ee,2),te=e=>{var n=Ts(),r=B(n);H(()=>Y(r,`CONTEXT SOURCE // ${(t().source||`LOCAL`)??``} // OBSERVATION`)),J(e,n)};X(k,e=>{G(a)||e(te)}),J(e,o)},r=Wi(t,`params`,19,()=>({})),i=P(()=>$.data?.sessions||[]),a=P(()=>$.data?.control&&$.data.profiles||[]),o=P(()=>($.data?.triggers||[]).filter(e=>G(i).some(t=>t.id===e.session)));function s(e,t){if(r().id&&r().id!==t&&G(l)){e.preventDefault();return}b(t)}function c(e){return[`TURN COMPLETE`,`IDLE`,`ACTIVE`].includes(e.status)&&G(o).some(t=>t.session===e.id)?`TRIGGER SET`:e.status}let l=I(!1);function u(e,t){if(r().id&&r().id!==t&&G(l)){e.preventDefault();return}b(t)}yn(()=>{let e=r().id;e&&pr(()=>{aa.selected=e,da(e,2)})});let d=P(()=>G(i).find(e=>e.id===r().id)),p=P(()=>new URLSearchParams(Yi.querystring).get(`review`)===`profile`&&G(a).some(e=>e.session===r().id&&e.pending)),m=P(()=>G(i).some(e=>e.id===aa.selected)?aa.selected:G(i)[0]?.id),h=P(()=>!$.connected||!!$.data?.sessionsError),v=P(()=>[[`OBSERVED TOKENS`,fa(G(i).reduce((e,t)=>e+t.tokens,0))],[`LISTED SESSIONS`,fa(G(i).length)],...[[`WORKING`,`WORKING`],[`AWAITING APPROVAL`,`APPROVAL NEEDED`],[`AWAITING INPUT`,`INPUT NEEDED`],[`CHECK · INFERRED`,`CHECK SESSION`]].map(([e,t])=>[e,G(h)?`—`:fa(G(i).filter(e=>e.status===t).length)])]),y=P(()=>G(i).filter(e=>[`INPUT NEEDED`,`APPROVAL NEEDED`,`CHECK SESSION`].includes(e.status)));function b(e){aa.selected=e,ur().then(()=>document.querySelector(`.session-row.selected`)?.scrollIntoView({block:`nearest`}))}function x(e,t){if(b(e),r().id){t<0&&(da(e,2),location.hash=`/sessions`);return}let n=la(e)+t;n>2?location.hash=`/sessions/`+encodeURIComponent(e):da(e,n)}function S(e){let t=e.target;if(!(e.altKey||e.ctrlKey||e.metaKey||e.shiftKey||t.closest(`input, textarea, select, [contenteditable="true"]`))){if(e.key===`Escape`&&r().id)e.preventDefault(),x(r().id,-1);else if([`ArrowLeft`,`ArrowRight`].includes(e.key)&&(r().id||G(m)))e.preventDefault(),x(r().id||G(m),e.key===`ArrowLeft`?-1:1);else if(!r().id&&[`ArrowUp`,`ArrowDown`].includes(e.key)&&G(i).length){e.preventDefault();let t=G(i).findIndex(e=>e.id===G(m));b(G(i)[Math.max(0,Math.min(G(i).length-1,t+(e.key===`ArrowUp`?-1:1)))].id)}}}function C(e){return G(h)?`Last observation only — refresh unavailable. Check Codex for current state.`:e.status===`CHECK SESSION`?`INFERRED INACTIVITY — a quiet session, not a confirmed input or approval request. Local tools may still be running.`:$.data?.control&&[`APPROVAL NEEDED`,`INPUT NEEDED`].includes(e.status)?`Open full detail for supported live controls; otherwise reply in Codex.`:e.status===`APPROVAL NEEDED`?`OBSERVED APPROVAL SIGNAL — approve or decline in Codex.`:e.status===`INPUT NEEDED`?`OBSERVED INPUT SIGNAL — reply in Codex.`:e.status===`TURN COMPLETE`?`OBSERVED TURN COMPLETION — informational, not an approval request.`:``}function w(e){return e.streaming&&e.contextKind===`LAST REPLY`?`REPLY // STREAMING`:e.contextKind||`LAST ACTIVITY`}var T=Qs();Sr(`keydown`,$t,S);var E=z(T),ee=B(V(R(E),2));N(E);var D=V(E,2);let O;Z(D,21,()=>G(v),qr,(e,t)=>{var n=P(()=>_(G(t),2));let r=()=>G(n)[0],i=()=>G(n)[1];var a=Ds(),o=R(a),s=B(o,!0),c=B(V(o,2),!0);N(a),H(()=>{Y(s,r()),Y(c,i())}),J(e,a)}),N(D);var k=V(D,2),te=B(k),A=V(k,2),ne=e=>{J(e,Os())};X(A,e=>{G(h)&&e(ne)});var re=V(A,2),ie=e=>{J(e,ks())};X(re,e=>{$.data?.profileError&&e(ie)});var ae=V(re,2),oe=e=>{var t=Ns(),n=R(t);Z(n,17,()=>G(y),qr,(e,t)=>{var n=As();let r;var i=B(n);H((e,a)=>{r=ci(n,1,`button`,null,r,{approval:G(t).status===`APPROVAL NEEDED`}),Q(n,`href`,e),Y(i,`${G(t).status??``} - ${a??``} // ${(G(t).name||G(t).directory||G(t).id)??``}`)},[()=>`#/sessions/`+encodeURIComponent(G(t).id),()=>Array.from(G(t).id).slice(-5).join(``).toUpperCase()]),K(`click`,n,()=>b(G(t).id)),J(e,n)});var c=V(n,2);Z(c,17,()=>G(a).filter(e=>e.pending&&G(i).some(t=>t.id===e.session)),qr,(e,t)=>{var n=js(),a=B(n);H((e,i,o)=>{Q(n,`title`,r().id&&r().id!==G(t).session&&G(l)?`Finish sending or clear your current draft before switching sessions.`:`Review this session’s quota threshold`),Q(n,`href`,e),Y(a,`QUOTA THRESHOLD ${i??``} // ${o??``}`)},[()=>`#/sessions/`+encodeURIComponent(G(t).session)+`?review=profile`,()=>Array.from(G(t).session).slice(-5).join(``).toUpperCase(),()=>G(i).find(e=>e.id===G(t).session)?.name||G(i).find(e=>e.id===G(t).session)?.directory||G(t).session]),K(`click`,n,e=>u(e,G(t).session)),J(e,n)}),Z(V(c,2),17,()=>G(o),qr,(e,t)=>{var n=Ms(),r=B(n);H((e,t,i)=>{Q(n,`href`,e),Y(r,`TRIGGER SET ${t??``} // ${i??``}`)},[()=>`#/sessions/`+encodeURIComponent(G(t).session),()=>Array.from(G(t).session).slice(-5).join(``).toUpperCase(),()=>G(i).find(e=>e.id===G(t).session)?.name||G(i).find(e=>e.id===G(t).session)?.directory]),K(`click`,n,e=>s(e,G(t).session)),J(e,n)}),N(t),J(e,t)},se=P(()=>(G(y).length||G(a).some(e=>e.pending)||G(o).length)&&!G(h));X(ae,e=>{G(se)&&e(oe)});var ce=V(ae,2),le=e=>{var t=Pr(),i=z(t),o=e=>{var t=Rs(),i=R(t),o=R(i),s=R(o);let u;var f=V(s);N(o);var m=V(o,2);N(i);var g=V(i,2),_=e=>{var t=Ts(),n=B(t,!0);H(()=>Y(n,G(d).directory)),J(e,t)};X(g,e=>{G(d).name&&G(d).directory&&e(_)});var v=V(g,2),y=B(v),x=V(v,2);let S;var C=R(x),w=R(C);n(w,()=>G(d),()=>!0,()=>!0),N(C);var T=V(C,2),E=e=>{var t=Pr();Kr(z(t),()=>G(d).id,e=>{{let t=P(()=>new URLSearchParams(Yi.querystring).get(`schedule`)===`edit`),n=P(()=>G(d).fileChanges||[]);ss(e,{get session(){return G(d).id},get openScheduler(){return G(t)},get observedCommand(){return G(d).command},get observedFileChanges(){return G(n)},get suspended(){return G(p)},onProtectedChange:e=>{L(l,e,!0)}})}}),J(e,t)},ee=e=>{J(e,Ps())};X(T,e=>{$.data?.control?e(E):e(ee,-1)}),N(x);var D=V(x,2);Z(D,17,()=>G(a).filter(e=>e.session===G(d).id),e=>e.session,(e,t)=>{var n=Ls(),r=R(n),i=e=>{var n=Fs(),r=B(n,!0);H(()=>Y(r,G(t).notice)),J(e,n)};X(r,e=>{G(t).notice&&e(i)});var a=V(r,2),o=e=>{ss(e,{get session(){return G(d).id},review:`profile`})},s=e=>{var t=Is();H(e=>Q(t,`href`,e),[()=>`#/sessions/`+encodeURIComponent(G(d).id)+`?review=profile`]),J(e,t)};X(a,e=>{G(t).pending&&G(p)?e(o):G(t).pending&&e(s,1)}),N(n),H(()=>Q(n,`id`,`quota-profile-`+G(d).id)),J(e,n)});var O=V(D,2),k=e=>{var t=Pr();Kr(z(t),()=>G(d).id,e=>{ms(e,{get session(){return G(d)},active:!0})}),J(e,t)};X(O,e=>{G(p)||e(k)}),Kr(V(O,2),()=>G(d).id,e=>{fs(e,{get session(){return G(d)},get stale(){return G(h)}})}),N(t),H((e,t)=>{u=ci(s,1,`lamp lit`,null,u,{working:G(d).status===`WORKING`&&!G(h)}),Y(f,`${e??``} // ${(G(d).name||G(d).directory)??``}`),Y(y,`${t??``} TOKENS // ${G(d).id??``} // CONTEXT SOURCE // ${(G(d).source||`LOCAL`)??``}`),S=ui(x,``,S,{display:G(p)?`none`:void 0})},[()=>G(h)?`STALE`:G(p)?`QUOTA THRESHOLD`:c(G(d)),()=>fa(G(d).tokens)]),K(`click`,m,()=>{b(r().id),da(r().id,2)}),J(e,t)},s=e=>{J(e,zs())};X(i,e=>{G(d)?e(o):e(s,-1)}),J(e,t)},ue=e=>{var t=Zs(),r=z(t),o=V(R(r),2),s=R(o),l=V(s,2);N(o),N(r);var d=V(r,2);Z(d,17,()=>G(i),e=>e.id,(e,t)=>{let r=P(()=>la(G(t).id));var i=Ys();let o;var s=R(i),l=R(s),d=e=>{var n=Bs(),r=R(n),i=B(r);N(n),H(e=>{Q(r,`aria-pressed`,G(m)===G(t).id),Y(i,`${e??``} // ${G(t).name??``}`)},[()=>Array.from(G(t).id).slice(-5).join(``).toUpperCase()]),K(`click`,r,()=>b(G(t).id)),J(e,n)};X(l,e=>{G(t).name&&e(d)});var f=V(l,2),p=R(f),g=R(p);let _;var v=V(g,1,!0);N(p),N(f);var y=V(f,2),S=e=>{var n=Vs(),r=B(n,!0);H(()=>{Q(n,`aria-pressed`,G(m)===G(t).id),Y(r,G(t).directory||G(t).id)}),K(`click`,n,()=>b(G(t).id)),J(e,n)};X(y,e=>{G(t).name||e(S)});var C=V(y,2),T=R(C);Se(),N(C);var E=V(C,2),ee=e=>{var n=Ts(),r=B(n,!0);H(()=>Y(r,G(t).directory)),J(e,n)};X(E,e=>{G(t).name&&G(t).directory&&e(ee)});var D=V(E,2),O=B(D),k=V(D,2),te=B(k),A=V(k,2),ne=R(A),re=V(ne,2),ie=B(re,!0),ae=V(re,2);N(A);var oe=V(A,2),se=V(oe,2),ce=e=>{var n=Hs(),r=B(n,!0);H(()=>Y(r,G(t).status===`CHECK SESSION`?`INFERRED INACTIVITY`:$.data?.control?`OPEN FULL DETAIL OR REPLY IN CODEX`:G(t).status===`APPROVAL NEEDED`?`APPROVE OR DECLINE IN CODEX`:`REPLY IN CODEX`)),J(e,n)},le=P(()=>!G(h)&&[`APPROVAL NEEDED`,`INPUT NEEDED`,`CHECK SESSION`].includes(G(t).status));X(se,e=>{G(le)&&e(ce)}),N(s);var ue=V(s,2),de=e=>{var r=qs(),i=R(r),o=R(i),s=B(o,!0),c=V(o,2),l=e=>{var n=Us(),r=B(n,!0);H(e=>{Q(n,`href`,e),Y(r,$.data?.control?`REVIEW IN FULL DETAIL ↗`:`APPROVAL IN CODEX ↗`)},[()=>`#/sessions/`+encodeURIComponent(G(t).id)]),J(e,n)};X(c,e=>{!G(h)&&G(t).status===`APPROVAL NEEDED`&&e(l)}),N(i);var d=V(i,2);n(d,()=>G(t),()=>!1);var f=V(d,2);Z(f,17,()=>G(a).filter(e=>e.session===G(t).id),qr,(e,n)=>{var r=Ks(),i=z(r),a=e=>{var t=Ws(),r=B(t,!0);H(()=>Y(r,G(n).notice)),J(e,t)};X(i,e=>{G(n).notice&&e(a)});var o=V(i,2),s=e=>{var n=Gs();H(e=>Q(n,`href`,e),[()=>`#/sessions/`+encodeURIComponent(G(t).id)+`?review=profile`]),K(`click`,n,e=>u(e,G(t).id)),J(e,n)};X(o,e=>{G(n).pending&&e(s)}),J(e,r)});var p=V(f,2);{let e=P(()=>G(m)===G(t).id);ms(p,{get session(){return G(t)},get active(){return G(e)}})}N(r),H(e=>Y(s,e),[()=>w(G(t))]),J(e,r)};X(ue,e=>{G(r)>0&&e(de)});var fe=V(ue,2),pe=e=>{var n=Js(),r=V(R(n),2);{let e=P(()=>(G(t).samples||[]).map(e=>e.tokens));no(r,{get values(){return G(e)},capacity:120})}var i=B(V(r,2));N(n),H(()=>Y(i,`LAST ${(G(t).samples?.length||0)??``} SAMPLES // AUTO SCALE`)),J(e,n)};X(fe,e=>{G(r)<2&&e(pe)}),N(i),H((e,n,a,s,c)=>{o=ci(i,1,`session-row`,null,o,{selected:G(m)===G(t).id,wide:G(r)===2,split:G(r)===1}),Q(i,`aria-label`,`Session `+(G(t).name||G(t).directory||G(t).id)),Q(p,`href`,e),_=ci(g,1,`lamp lit`,null,_,{working:G(t).status===`WORKING`&&!G(h)}),Y(v,n),Y(T,`${a??``} `),Y(O,`${G(t).agents??``} LINKED AGENTS`),Y(te,`ACTIVE // ${s??``}`),ne.disabled=G(r)===0,Q(re,`aria-expanded`,G(r)>0),Y(ie,G(r)?`HIDE DETAIL`:`SHOW DETAIL`),Q(oe,`href`,c)},[()=>`#/sessions/`+encodeURIComponent(G(t).id),()=>G(h)?`STALE`:c(G(t)),()=>fa(G(t).tokens),()=>pa(G(t).activity),()=>`#/sessions/`+encodeURIComponent(G(t).id)]),K(`click`,p,()=>b(G(t).id)),K(`click`,ne,()=>x(G(t).id,-1)),K(`click`,re,()=>{b(G(t).id),da(G(t).id,+!G(r))}),K(`click`,ae,()=>x(G(t).id,1)),K(`click`,oe,()=>b(G(t).id)),J(e,i)});var f=V(d,2),p=e=>{J(e,Xs())};X(f,e=>{G(i).length||e(p)}),K(`click`,s,()=>ua(1)),K(`click`,l,()=>ua(0)),J(e,t)};X(ce,e=>{r().id?e(le):e(ue,-1)}),H(e=>{Y(ee,`OBSERVED ${e??``}`),O=ci(D,1,`session-totals`,null,O,{stale:G(h)}),Y(te,`${G(h)?`LAST KNOWN TOTALS — live state counts unavailable. `:``}Tokens - observed since this server started for currently listed sessions; linked - agents are already included. Totals can decrease when a session leaves the - list. History samples every 30 seconds. Not account-wide totals.`)},[()=>pa($.data?.sessionsAt)]),J(e,T),Ge()}Cr([`click`]);var ec=q(`

        `),tc=q(`

        Relative usage/credits are not tokens or quota percentages. Groupings - describe the same usage, not additional consumption.

        `,1),nc=q(`// APPROXIMATE`),rc=q(`

        Partial accounting: totals may still change.

        `),ic=q(`

        Percentage of this period's historical allowance, not today's limit. May - exceed 100%. This is not a manual/automatic reset log.

        `,1),ac=q(`

        `,1),oc=q(`

        This optional account report is unavailable. Token history remains - available; missing data does not mean zero usage.

        `),sc=q(`
        `),cc=q(`
        `),lc=q(`

        No usage reported for this day.

        `),uc=q(`

        Selected breakdown unavailable; missing does not mean zero.

        `),dc=q(`

        `,1);function fc(e,t){We(t,!0);let n=I(`surface`),r=I(0),i=P(()=>t.reports?.daily),a=P(()=>t.reports?.plan),o=P(()=>t.mode===`breakdown`?G(i)?.days.length||0:G(a)?.periods.length||0),s=P(()=>Math.min(G(r),Math.max(0,G(o)-1))),c=P(()=>G(i)?.days[(G(i)?.days.length||0)-1-G(s)]),l=P(()=>G(a)?.periods[G(s)]),u=P(()=>t.mode===`breakdown`?G(i)?.units||``:`%`),d=P(()=>{let e=Object.create(null);if(t.mode===`breakdown`)e=G(c)?.groups[G(n)]||{};else{let t=G(n)===`feature`?`thread_source`:G(n)===`task start`?`turn_trigger`:G(n);for(let n of G(l)?.breakdowns||[])if(n.dimension===t)for(let t of n.rows)e[t.key]=(e[t.key]||0)+t.basis_points/100}return Object.entries(e).sort((e,t)=>t[1]-e[1]||e[0].localeCompare(t[0]))}),f=P(()=>t.mode===`breakdown`?G(c)?.total:G(l)?.used_basis_points==null?null:G(l).used_basis_points/100),p=P(()=>Math.max(1,G(f)||0,...G(d).map(([,e])=>e))),m=e=>e==null?`UNKNOWN`:e.toLocaleString(`en-GB`,{maximumFractionDigits:2});var h=dc(),g=z(h),v=R(g),y=V(R(v)),b=R(y);b.value=b.__value=`surface`;var x=V(b);x.value=x.__value=`model`;var S=V(x);S.value=S.__value=`feature`;var C=V(S);C.value=C.__value=`task start`,N(y),mi(y),N(v);var w=V(v,2),T=V(w,2),E=B(V(T,2),!0);N(g);var ee=V(g,2),D=R(ee),O=B(D,!0),k=V(D,2),te=B(k),A=V(k,2),ne=e=>{var t=tc(),n=z(t),r=B(n),a=V(n,2),o=e=>{var t=ec(),n=B(t);H(()=>Y(n,`Data as of ${G(i).dataAsOf??``}`)),J(e,t)};X(a,e=>{G(i).dataAsOf&&e(o)});var s=B(V(a,2));Se(2),H((e,t)=>{Y(r,`Latest fetch ${e??``} // queried ${G(i).from??``} → ${G(i).through??``}. - Reports are fetched from OpenAI and are not saved between runs.`),Y(s,`${(G(c)?.date||`No daily buckets reported`)??``} // ${t??``} - ${G(u)??``}`)},[()=>pa(G(i).fetchedAt),()=>m(G(f))]),J(e,t)},re=e=>{var t=ac(),n=z(t),r=R(n);let i;var o=B(r,!0),s=V(r),c=e=>{J(e,nc())};X(s,e=>{G(a).approximate&&e(c)});var u=V(s);N(n);var d=V(n,2),p=R(d),h=V(p),g=e=>{var t=Nr();H(()=>Y(t,`// Boundary tolerance ${G(a).boundary_tolerance_seconds??``}s`)),J(e,t)};X(h,e=>{G(a).boundary_tolerance_seconds!=null&&e(g)}),N(d);var _=V(d,2),v=e=>{var t=ic(),n=z(t),r=R(n),i=V(r);let a;var o=B(i);N(n);var s=V(n,2),c=B(s),u=V(s,2),d=e=>{J(e,rc())};X(u,e=>{G(l).accounting_complete||e(d)}),Se(2),H(e=>{Y(r,`${G(l).window_minutes??``} MIN // ${G(l).plan_type??``} // `),a=ci(i,1,`svelte-1fbui7h`,null,a,{muted:G(f)==null,"quota-near":G(f)!=null&&G(f)>=80&&G(f)<100,"quota-full":G(f)!=null&&G(f)>=100}),Y(o,`USED ${e??``}${G(f)==null?``:`%`}`),Y(c,`${G(l).starts_at??``} → ${G(l).ends_at??``}`)},[()=>m(G(f))]),J(e,t)};X(_,e=>{G(l)&&e(v)}),H(e=>{i=ci(r,1,`svelte-1fbui7h`,null,i,{"quality-warning":!G(a).coverage_complete}),Y(o,G(a).coverage_complete?`Reported range complete`:`Partial coverage`),Y(u,` // Latest fetch ${e??``}`),Y(p,`Coverage starts ${(G(a).coverage_start||`unknown`)??``} // Data as of ${(G(a).data_as_of||`unknown`)??``}`)},[()=>pa(G(a).fetchedAt)]),J(e,t)},ie=e=>{J(e,oc())};X(A,e=>{t.mode===`breakdown`&&G(i)?e(ne):t.mode===`periods`&&G(a)?e(re,1):e(ie,-1)});var ae=V(A,2),oe=e=>{var n=cc(),r=R(n),i=B(V(R(r),2),!0);N(r),Z(V(r,2),17,()=>G(d),qr,(e,n,r)=>{var i=P(()=>_(G(n),2));let a=()=>G(i)[0],o=()=>G(i)[1];var s=sc();let c;var l=R(s),u=B(l,!0),d=V(l,2),f=R(d);let h;N(d);var g=B(V(d,2));N(s),H((e,n,i)=>{c=ci(s,1,`report-row svelte-1fbui7h`,null,c,{unknown:e,alternate:r%2==1}),Q(l,`title`,a()),Y(u,a()),h=ui(f,``,h,{width:n}),Y(g,`${i??``}${t.mode===`periods`?`%`:``}`)},[()=>a().trim().toLowerCase()===`unknown`,()=>`${Math.max(0,100*o()/G(p))}%`,()=>m(o())]),J(e,s)}),N(n),H(()=>Y(i,t.mode===`breakdown`?G(u):`QUOTA USED`)),J(e,n)},se=e=>{J(e,lc())},ce=e=>{J(e,uc())};X(ae,e=>{G(d).length?e(oe):t.mode===`breakdown`&&G(c)?.total===0&&G(c)?.groups[G(n)]!=null?e(se,1):(G(i)&&t.mode===`breakdown`||G(a)&&t.mode===`periods`)&&e(ce,2)}),N(ee),H(()=>{w.disabled=G(s)>=G(o)-1,T.disabled=G(s)===0,Y(E,G(o)?`${G(s)+1} / ${G(o)}`:`NO DATA`),Y(O,t.mode===`breakdown`?`DAILY BREAKDOWN`:`QUOTA WINDOWS`),Y(te,`${(t.mode===`breakdown`?t.reports?.dailyStatus||`UNAVAILABLE`:t.reports?.planStatus||`UNAVAILABLE`)??``} // UTC`)}),hi(y,()=>G(n),e=>L(n,e)),K(`click`,w,()=>L(r,G(s)+1)),K(`click`,T,()=>L(r,G(s)-1)),J(e,h),Ge()}Cr([`click`]);var pc=864e5;function mc(e,t){let n=new Date(e);n.setUTCDate(1),n.setUTCMonth(n.getUTCMonth()+t);let r=new Date(Date.UTC(n.getUTCFullYear(),n.getUTCMonth()+1,0)).getUTCDate();return n.setUTCDate(Math.min(e.getUTCDate(),r)),n}function hc(e,t,n){let r=new Date(Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()));for(let e=0;;e++){let i=mc(r,-t),a=new Date(i.getTime()+pc);if(e===n)return{start:a,end:r};r=i}}var gc=q(``),_c=q(``,1),vc=q(`

        History refresh failed. Any displayed history is the last successful - observation.

        `),yc=q(`

        Token history unavailable; other reports may still be available.

        `),bc=q(``),xc=q(`
        `),Sc=q(`

        LESS ░ ▒ ▓ █ MORE // HOVER FOR DATE AND TOKENS

        `,1),Cc=q(`
        `,1),wc=q(` `),Tc=q(`

        LIFETIME TOKENS

        PEAK DAY

        CURRENT STREAK

        DAYS

        LONGEST TURN

        SECONDS

        LONGEST STREAK

        DAYS

        OPENAI // UTC

        Accessible data table
        Date (UTC)Tokens

        `,1),Ec=q(`

        History unavailable or awaiting a matching account observation. Missing - history is not treated as zero usage.

        `),Dc=q(`

        USAGE // ACCOUNT HISTORY

        Account-wide history reported by Codex, not the local Sessions counter. Dates - use UTC. Allowance history is approximate where indicated; reset causes are - not provided.

        `,1);function Oc(e,t){We(t,!0);let n=I(`daily`),r=I(12),i=I(0),a=P(()=>{let e=$.data?.usage?.dailyUsageBuckets;if(!e)return[];let{start:t,end:n}=hc(new Date,G(r),G(i)),a=new Map;for(let t of e)!Number.isFinite(t.tokens)||t.tokens<0||a.set(t.startDate,(a.get(t.startDate)||0)+t.tokens);let o=[];for(let e=t;e<=n;e=new Date(e.getTime()+864e5)){let t=e.toISOString().slice(0,10);o.push({date:t,tokens:a.get(t)||0})}return o}),o=P(()=>Math.max(1,...G(a).map(e=>e.tokens))),s=P(()=>G(a).length?new Date(G(a)[0].date+`T00:00:00Z`).getUTCDay():0),c=P(()=>{if(G(n)===`monthly`){let e=new Map;for(let t of G(a))e.set(t.date.slice(0,7),(e.get(t.date.slice(0,7))||0)+t.tokens);return[...e].map(([e,t])=>({date:e,tokens:t}))}let e=0;return G(a).map(t=>({date:t.date,tokens:e+=t.tokens}))});var l=Dc(),u=V(z(l),4);Z(u,20,()=>[[`daily`,`Daily`],[`monthly`,`Monthly`],[`cumulative`,`Cumulative`],[`breakdown`,`Breakdown`],[`periods`,`Windows`]],qr,(e,t)=>{var r=P(()=>_(t,2));let a=()=>G(r)[0],o=()=>G(r)[1];var s=gc();let c;var l=B(s,!0);H(()=>{Q(s,`aria-pressed`,G(n)===a()),c=ci(s,1,``,null,c,{active:G(n)===a()}),Y(l,o())}),K(`click`,s,()=>{L(n,a(),!0),L(i,0)}),J(e,s)}),N(u);var d=V(u,2),f=R(d),p=e=>{var t=_c(),n=z(t),a=V(R(n)),o=R(a);o.value=o.__value=6;var s=V(o);s.value=s.__value=12,N(a),mi(a),N(n);var c=V(n),l=V(c);H(()=>l.disabled=G(i)===0),K(`change`,a,()=>L(i,0)),hi(a,()=>G(r),e=>L(r,e)),K(`click`,c,()=>qt(i)),K(`click`,l,()=>qt(i,-1)),J(e,t)};X(f,e=>{G(n)!==`breakdown`&&G(n)!==`periods`&&e(p)}),N(d);var m=V(d,2),h=e=>{J(e,vc())};X(m,e=>{$.data?.usageError&&e(h)});var g=V(m,2),v=e=>{J(e,yc())};X(g,e=>{$.data?.usage?.tokenStatus===`UNAVAILABLE`&&G(n)!==`breakdown`&&G(n)!==`periods`&&e(v)});var y=V(g,2),b=e=>{var t=Pr();Kr(z(t),()=>G(n),e=>{{let t=P(()=>$.data?.usage?.reports);fc(e,{get reports(){return G(t)},get mode(){return G(n)}})}}),J(e,t)},x=e=>{var t=Tc(),r=z(t),i=R(r),l=B(V(R(i),2),!0);N(i);var u=V(i,2),d=B(V(R(u),2),!0);N(u);var f=V(u,2),p=V(R(f),2),m=R(p);Se(),N(p),N(f);var h=V(f,2),g=V(R(h),2),_=R(g);Se(),N(g),N(h);var v=V(h,2),y=V(R(v),2),b=R(y);Se(),N(y),N(v),N(r);var x=V(r,4),S=R(x),C=B(S),w=V(S,2),T=e=>{var t=Sc(),n=z(t),r=R(n),i=R(r);Z(i,17,()=>Array(G(s)),qr,(e,t)=>{J(e,bc())}),Z(V(i,2),17,()=>G(a),qr,(e,t)=>{var n=xc();let r,i;H(e=>{r=ci(n,1,`heat-cell`,null,r,{zero:G(t).tokens===0}),Q(n,`title`,e),i=ui(n,``,i,{opacity:G(t).tokens?.25+.75*G(t).tokens/G(o):1})},[()=>`${G(t).date}: ${fa(G(t).tokens)} tokens`]),J(e,n)}),N(r),N(n),Se(2),J(e,t)},E=e=>{var t=Cc(),r=z(t);{let e=P(()=>G(c).map(e=>e.tokens)),t=P(()=>G(n)+` usage`);no(r,{get values(){return G(e)},get label(){return G(t)}})}var i=V(r,2),a=R(i),o=B(a,!0),s=B(V(a),!0);N(i),H(e=>{Y(o,G(c)[0]?.date),Y(s,e)},[()=>G(c).at(-1)?.date]),J(e,t)};X(w,e=>{G(n)===`daily`?e(T):e(E,-1)});var ee=V(w,2),D=V(R(ee),2),O=R(D),k=V(R(O));Z(k,21,()=>G(n)===`daily`?G(a):G(c),qr,(e,t)=>{var n=wc(),r=R(n),i=B(r,!0),a=B(V(r),!0);N(n),H(e=>{Y(i,G(t).date),Y(a,e)},[()=>fa(G(t).tokens)]),J(e,n)}),N(k),N(O),N(D),N(ee),N(x);var te=B(V(x,2));H((e,t,n,r,i,o,s)=>{Y(l,e),Y(d,t),Y(m,`${n??``} `),Y(_,`${r??``} `),Y(b,`${i??``} `),Y(C,`${G(a)[0]?.date??``} — ${o??``}`),Y(te,`OBSERVED ${s??``} // CUMULATIVE IS FOR THE SELECTED PERIOD`)},[()=>fa($.data.usage.summary.lifetimeTokens),()=>fa($.data.usage.summary.peakDailyTokens),()=>fa($.data.usage.summary.currentStreakDays),()=>fa($.data.usage.summary.longestRunningTurnSec),()=>fa($.data.usage.summary.longestStreakDays),()=>G(a).at(-1)?.date,()=>pa($.data.usageAt)]),J(e,t)},S=e=>{J(e,Ec())};X(y,e=>{G(n)===`breakdown`||G(n)===`periods`?e(b):$.data?.usage&&$.data.usage.dailyUsageBuckets!==null?e(x,1):e(S,-1)}),J(e,l),Ge()}Cr([`click`,`change`]);var kc=q(`

        Page not found

        Return to Quota

        `,1);function Ac(e){var t=kc();Se(2),J(e,t)}var jc=q(` `),Mc=q(`

        `),Nc=q(`

        Connecting to your local Codexometer…

        `),Pc=q(``),Fc=q(`
        CODEXOMETER

        Your quota. Your sessions. Your command centre.

        `);function Ic(e,t){We(t,!0);let n={"/":$a,"/quota/:view?":$a,"/sessions/:id?":$s,"/usage":Oc,"*":Ac},r={quota:/^(?:\/|\/quota(?:\/[^/]+)?\/?)$/,sessions:/^\/sessions(?:\/[^/]+)?\/?$/,usage:/^\/usage\/?$/},i=I(`hacker`),a=[`hacker`,`rust`,`blue-steel`,`ultraviolet`,`nightshade`];yn(()=>{sa()}),yn(()=>{if($.data)for(let[e,t]of Object.entries(r))t.test(Yi.location)&&(aa.tab=e)}),Gi(()=>{try{let e=localStorage.getItem(`codexometer.web.theme`);e&&a.includes(e)&&L(i,e,!0)}catch{}return va()});function o(){try{localStorage.setItem(`codexometer.web.theme`,G(i))}catch{}}var s=Fc(),c=R(s),l=V(R(c),2),u=R(l);let d;var f=V(u,1,!0),p=B(V(f));N(l),N(c);var m=V(c,2);Z(m,21,()=>Object.entries(r),qr,(e,t)=>{var n=P(()=>_(G(t),2));let r=()=>G(n)[0],i=()=>G(n)[1],a=P(()=>i().test(Yi.location));var o=jc();let s;var c=B(o,!0);H(e=>{Q(o,`href`,r()===`quota`?`#/quota/`+aa.view:`#/`+r()),Q(o,`aria-current`,G(a)?`page`:void 0),s=ci(o,1,``,null,s,{active:G(a)}),Y(c,e)},[()=>r().toUpperCase()]),J(e,o)}),N(m);var h=V(m,2),g=R(h),v=e=>{var t=Mc(),n=B(t,!0);H(()=>Y(n,$.error)),J(e,t)};X(g,e=>{$.error&&e(v)});var y=V(g,2),b=e=>{$i(e,{get routes(){return n}})},x=e=>{J(e,Nc())};X(y,e=>{$.data?e(b):$.error||e(x,1)}),N(h);var S=V(h,2),C=R(S),w=B(C),T=V(C,2),E=B(T,!0),ee=V(T,2),D=V(R(ee));Z(D,21,()=>a,qr,(e,t)=>{var n=Pc(),r=B(n,!0),i={};H(e=>{Y(r,e),i!==(i=G(t))&&(n.value=(n.__value=i)??``)},[()=>G(t).replace(`-`,` `).toUpperCase()]),J(e,n)}),N(D),mi(D),N(ee),N(S),N(s),H(()=>{Q(s,`data-theme`,G(i)),d=ci(u,1,`lamp`,null,d,{lit:$.connected}),Y(f,$.connected?`CONNECTED`:`OFFLINE`),Y(p,`EXPERIMENTAL // ${$.data?.control?`SESSION CONTROL`:`READ ONLY`}`),Y(w,`v${($.data?.version||`…`)??``} // LOCAL ONLY`),Y(E,$.data?.control?`Session control enabled. Stop the server to revoke access.`:`No actions can be sent from this preview.`)}),K(`change`,D,o),hi(D,()=>G(i),e=>L(i,e)),J(e,s),Ge()}Cr([`change`]),Br(Ic,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/internal/web/dist/assets/index-l8TP2B-n.css b/internal/web/dist/assets/index-l8TP2B-n.css new file mode 100644 index 0000000..c9a602c --- /dev/null +++ b/internal/web/dist/assets/index-l8TP2B-n.css @@ -0,0 +1 @@ +.observation-details.svelte-1vgdi83{font-size:12px}summary.svelte-1vgdi83{cursor:pointer;color:var(--accent)}.observation-table.svelte-1vgdi83{max-height:240px;overflow:auto}.zone-canvas.svelte-1vgdi83{flex:1;min-height:170px;margin-top:6px;position:relative}svg.svelte-1vgdi83{width:100%;height:100%;display:block;position:absolute;inset:0}text.svelte-1vgdi83{fill:var(--ink);font:14px Cascadia Code,Consolas,monospace}.axis-title.svelte-1vgdi83{letter-spacing:.04em;font-size:12px}.grid.svelte-1vgdi83{stroke:#fff;stroke-opacity:.14;stroke-width:1px}.axes.svelte-1vgdi83{stroke:var(--ink);stroke-width:1.5px;fill:none}.pace-line.svelte-1vgdi83{stroke:#fff;stroke-width:2px;stroke-dasharray:6 4}.position-halo.svelte-1vgdi83{fill:#101820;stroke:#fff;stroke-width:1.5px}.position-dot.svelte-1vgdi83{fill:#fff}.observation-trail.svelte-1vgdi83{fill:none;stroke:#101820;stroke-width:2.5px;stroke-linejoin:round}.observation-trail-casing.svelte-1vgdi83{fill:none;stroke:#ffffff73;stroke-width:4.5px;stroke-linejoin:round}.trail-start.svelte-1vgdi83{fill:#101820;stroke:#ffffffb3;stroke-width:2px}.trend-line.svelte-1vgdi83{fill:none;stroke:var(--trend-color);stroke-width:2px;stroke-dasharray:3 6}.trend-arrow.svelte-1vgdi83{fill:var(--trend-color);stroke:#ffffff73;stroke-width:.5px}.trend-casing.svelte-1vgdi83{fill:none;stroke:#ffffff73;stroke-width:4px;stroke-dasharray:3 6}.zone-caption.svelte-1vgdi83{min-width:0;color:var(--accent);flex:auto;margin:0}.trend-summary.svelte-1vgdi83{color:var(--ink)}.zone-footer.svelte-1vgdi83{justify-content:space-between;align-items:flex-start;gap:12px;margin-top:4px;display:flex}.zone-controls.svelte-1vgdi83{color:var(--muted);text-align:right;flex-direction:column;align-items:flex-end;gap:5px;font-size:11px;display:flex}.trace-control.svelte-1vgdi83,.trend-control.svelte-1vgdi83{cursor:pointer;white-space:nowrap}.trace-control.svelte-1vgdi83 input:where(.svelte-1vgdi83){accent-color:var(--accent)}.trend-control.svelte-1vgdi83 select:where(.svelte-1vgdi83){border:1px solid var(--edge);background:var(--panel);max-width:min(230px,45vw);color:var(--ink);font:inherit}.trend-control.svelte-1vgdi83 option:where(.svelte-1vgdi83):disabled{color:var(--muted)}.zone-controls.svelte-1vgdi83 small:where(.svelte-1vgdi83){font-size:10px}@container (width<=560px){.zone-footer.svelte-1vgdi83{flex-direction:column}.zone-controls.svelte-1vgdi83{align-self:stretch;align-items:flex-end}}.slash-commands.svelte-7bfwr9{margin:.4rem 0;position:relative}.command-popup.svelte-7bfwr9{z-index:5;box-sizing:border-box;border:1px solid var(--accent);background:var(--panel);width:min(40rem,100%);padding:.35rem;position:absolute;bottom:100%;left:0;box-shadow:0 .3rem 1rem #0006}.command-popup.svelte-7bfwr9 p:where(.svelte-7bfwr9){margin:.3rem 0;font-size:.8em}.command-options.vertical.svelte-7bfwr9{flex-flow:column;gap:.15rem;display:flex}.vertical.svelte-7bfwr9 button:where(.svelte-7bfwr9){text-align:left;white-space:nowrap;width:100%;min-width:0;color:var(--muted);background:var(--panel);border-color:#0000;flex:none;align-items:baseline;gap:.4rem;display:flex}.vertical.svelte-7bfwr9 .command-name:where(.svelte-7bfwr9),.vertical.svelte-7bfwr9 .command-tail:where(.svelte-7bfwr9){flex:none}.vertical.svelte-7bfwr9 .command-name:where(.svelte-7bfwr9){text-overflow:ellipsis;max-width:100%;overflow:hidden}.vertical.svelte-7bfwr9 button.suggested:where(.svelte-7bfwr9){outline:none}.command-options.svelte-7bfwr9 button.suggested:where(.svelte-7bfwr9) .command-name:where(.svelte-7bfwr9){background:var(--accent);color:var(--bg);font-weight:700}.command-options.svelte-7bfwr9 button:where(.svelte-7bfwr9):not(.suggested):hover .command-name:where(.svelte-7bfwr9){text-decoration:underline}.command-options.svelte-7bfwr9 button:where(.svelte-7bfwr9){color:var(--muted)}.vertical.svelte-7bfwr9 .command-help:where(.svelte-7bfwr9){min-width:0;max-width:none;color:var(--muted);flex:1}.command-options.svelte-7bfwr9{flex-wrap:wrap;gap:.35rem;max-height:35vh;display:flex;overflow:auto}.command-options.svelte-7bfwr9 .suggested:where(.svelte-7bfwr9){outline:1px solid var(--accent)}.command-help.svelte-7bfwr9{text-overflow:ellipsis;white-space:nowrap;opacity:.75;max-width:32ch;font-size:.8em;display:block;overflow:hidden}pre.svelte-7bfwr9{white-space:pre-wrap;overflow-wrap:anywhere;max-height:35vh;overflow:auto}.file-diff.svelte-x4gtb6{border:1px solid var(--muted);max-height:60vh;padding:.5rem;font-family:monospace;overflow:auto}.diff-line.svelte-x4gtb6{white-space:pre;min-height:1.3em;display:flex}.line-number.svelte-x4gtb6{text-align:right;opacity:.75;-webkit-user-select:none;user-select:none;flex:0 0 5ch;padding-right:1ch;display:inline-block}.added.svelte-x4gtb6{color:#67d391;background:#67d39112}.removed.svelte-x4gtb6{color:#ff6b83;background:#ff6b8312}.heading.svelte-x4gtb6{padding:.5rem 0;font-weight:700}.saved-prompt.svelte-1oupzfc{white-space:pre-wrap;overflow-wrap:anywhere}fieldset.svelte-1oupzfc{border:1px solid;margin:.5rem 0;padding:.35rem .6rem}.decision.svelte-1oupzfc,.answer.svelte-1oupzfc{margin:.3rem 0;display:block}.decision.svelte-1oupzfc code:where(.svelte-1oupzfc),.decision.svelte-1oupzfc span:where(.svelte-1oupzfc){overflow-wrap:anywhere;margin:.3rem 0 .3rem 1.5rem;display:block}textarea.svelte-1oupzfc,.answer.svelte-1oupzfc input:where(.svelte-1oupzfc),.answer.svelte-1oupzfc select:where(.svelte-1oupzfc){box-sizing:border-box;background:var(--bg);width:100%;color:inherit;font:inherit;border:1px solid;margin-top:.4rem;padding:.5rem;display:block}textarea.svelte-1oupzfc{resize:vertical;background:color-mix(in srgb, currentColor 8%, var(--bg))}button.svelte-1oupzfc{margin:.3rem .5rem .3rem 0}h3.svelte-1oupzfc,p.svelte-1oupzfc{margin-block:.4rem}.sent.svelte-1oupzfc{color:var(--accent)}.status-line.svelte-gprptn{align-items:center;gap:.5rem;min-width:0;margin-top:.5rem;display:flex}.status-line.svelte-gprptn p:where(.svelte-gprptn){white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;margin:0;font-size:.8rem;overflow:hidden}.configure.svelte-gprptn{padding:.15rem .35rem;font-size:.75rem}.status-picker.svelte-gprptn{border:1px solid var(--edge);margin:.5rem 0;padding:.6rem}.fields.svelte-gprptn{max-height:35vh;overflow:auto}.field.svelte-gprptn{align-items:center;gap:.35rem;display:flex}.field.svelte-gprptn label:where(.svelte-gprptn){flex:1;align-items:center;gap:.4rem;display:flex}.field.svelte-gprptn label:where(.svelte-gprptn) span:where(.svelte-gprptn){font-size:.75rem}.field.svelte-gprptn button:where(.svelte-gprptn){padding:.1rem .35rem}.session-copy.svelte-543j00{justify-content:flex-end;align-items:center;gap:8px;margin-top:auto;padding-top:6px;display:flex}span.svelte-543j00{color:var(--muted);overflow-wrap:anywhere;font-size:12px}button.svelte-543j00{color:var(--accent);flex-shrink:0;padding:3px 7px}button.svelte-543j00:hover,button.flashed.svelte-543j00{color:var(--bg);background:var(--accent)}h3.svelte-1fbui7h,.window-dates.svelte-1fbui7h,.report-value.svelte-1fbui7h{color:var(--accent);font-weight:700}.quality-warning.svelte-1fbui7h,.quota-near.svelte-1fbui7h{color:var(--approval)}.quota-full.svelte-1fbui7h{color:#ff6680}.alternate.svelte-1fbui7h .report-fill:where(.svelte-1fbui7h){background:radial-gradient(circle, var(--panel) .7px, transparent .9px) 0 0 / 3px 3px, var(--accent)}.unknown.svelte-1fbui7h,.unknown.svelte-1fbui7h .report-value:where(.svelte-1fbui7h){color:var(--muted)}.unknown.svelte-1fbui7h .report-fill:where(.svelte-1fbui7h){background:var(--muted);opacity:1}.report-bars.svelte-1fbui7h{grid-template-columns:minmax(5rem,1fr) minmax(2rem,3fr) auto;gap:.6rem;display:grid}.report-row.svelte-1fbui7h,.report-head.svelte-1fbui7h{grid-column:1/-1;grid-template-columns:subgrid;align-items:center;gap:.6rem;display:grid}.report-head.svelte-1fbui7h{color:var(--muted);font-size:.85rem}.report-head.svelte-1fbui7h>span:where(.svelte-1fbui7h):last-child,.report-value.svelte-1fbui7h{text-align:right}.report-row.svelte-1fbui7h>span:where(.svelte-1fbui7h):first-child{overflow-wrap:anywhere}.report-track.svelte-1fbui7h{background:var(--panel);height:1rem}.report-fill.svelte-1fbui7h{background:var(--accent);height:100%}svg.svelte-1jpy949{fill:currentColor;width:100%;height:auto;display:block}.startup.svelte-19xbbtg{z-index:100;background:var(--bg);color:var(--accent);position:fixed;inset:0;overflow:hidden}.wordmark.svelte-19xbbtg{position:absolute}button.svelte-19xbbtg{color:var(--muted);font:inherit;white-space:nowrap;background:0 0;border:0;font-size:12px;position:absolute;bottom:24px;left:50%;transform:translate(-50%)}:root{font-synthesis:none;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;background:#080e0c;font-family:Cascadia Code,SFMono-Regular,Consolas,monospace}*{box-sizing:border-box}body{margin:0}.shell{--accent:#80edac;--approval:#ffca58;--muted:#98aaa0;--edge:#31473a;--panel:#101c16;--bg:#080e0c;--ink:#e2eee5;background:var(--bg);height:100dvh;color:var(--ink);grid-template-rows:auto auto minmax(0,1fr) auto;padding:clamp(10px,1.2vw,20px);display:grid}.shell[data-theme=rust]{--approval:#ff8a3d;--accent:#ffbc75;--muted:#bfac95;--edge:#59412c;--panel:#261b14;--bg:#140e0a}.shell[data-theme=blue-steel]{--approval:#e8c46a;--accent:#85c8ff;--muted:#9cacc4;--edge:#334d69;--panel:#142235;--bg:#0b1421}.shell[data-theme=ultraviolet]{--approval:#f9a8d4;--accent:#c5adff;--muted:#b3a6c9;--edge:#4b3c68;--panel:#241a35;--bg:#140d22}.shell[data-theme=nightshade]{--approval:#8f7cff;--accent:#e59bff;--muted:#c6a7cf;--edge:#673976;--panel:#301739;--bg:#1c0b24}header,.spread,footer,.controls{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;display:flex}header{margin-bottom:10px}.shell[data-starting=true]>header,.shell[data-starting=true]>nav,.shell[data-starting=true]>main,.shell[data-starting=true]>footer{visibility:hidden}.brand{width:clamp(240px,32vw,405px);max-width:calc(100vw - 40px);color:var(--accent);text-decoration:none;display:block}header p{color:var(--muted);margin:3px 0 0;font-size:13px}.connection{color:var(--accent);text-align:right}small,.eyebrow{letter-spacing:.04em;font-size:12px}.connection small{color:var(--muted);margin-top:4px;display:block}.lamp{background:var(--muted);border-radius:50%;width:9px;height:9px;margin-right:9px;display:inline-block}.lamp.lit{background:var(--accent);box-shadow:0 0 8px color-mix(in srgb, var(--accent) 40%, transparent)}.working{animation:1.5s ease-in-out infinite pulse}@keyframes pulse{50%{opacity:.3}}nav{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}nav a,button,.button,select{font:inherit;color:var(--accent);border:1px solid var(--edge);background:var(--panel);cursor:pointer;border-radius:3px;padding:7px 11px;font-size:13px;text-decoration:none}button:hover,.button:hover,nav a:hover{border-color:var(--accent)}nav a.active,nav button.active{background:var(--accent);color:var(--bg);border-color:var(--accent)}:focus-visible{outline:2px solid var(--accent);outline-offset:4px}.secondary{margin-bottom:8px}.secondary a,.secondary button{padding:6px 10px;font-size:12px}main{scrollbar-gutter:stable;flex-direction:column;min-width:0;min-height:0;display:flex;overflow:auto}main>*{flex-shrink:0}.panel{border:1px solid var(--edge);border-top:2px solid var(--accent);background:var(--panel);border-radius:4px;min-width:0;padding:clamp(10px,1vw,16px)}h1{color:var(--accent);font-size:20px}h2{letter-spacing:.04em;color:var(--accent);overflow-wrap:anywhere;margin:0 0 10px;font-size:13px}h3{overflow-wrap:anywhere;font-size:13px}p{margin:6px 0;font-size:13px;line-height:1.45}a{color:var(--accent)}.muted,.eyebrow{color:var(--muted)}.notice{color:#ffe0aa;background:#332611;border-left:3px solid #ffc26e;padding:12px 16px}.empty{color:var(--muted);padding:35px 0}.quota-grid{flex:1 0 auto;grid-auto-rows:minmax(220px,1fr);gap:10px;display:grid}.quota-card{flex-direction:column;min-height:0;display:flex;container-type:inline-size}.quota-card>:not(.meter-graphic){flex-shrink:0}.meter-graphic{flex-direction:column;flex:1;min-height:100px;display:flex}.bar-graphic{min-height:65px}.quota-card .gauge{flex:2;height:auto;min-height:16px;margin:7px 0}.quota-card .timeline{flex:1;min-height:8px}.quota-grid.zone{grid-template-columns:repeat(auto-fit,minmax(min(100%,420px),1fr));grid-auto-rows:minmax(330px,1fr)}.quota-grid.radial{grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr));grid-auto-rows:minmax(260px,1fr)}.gauge{background:var(--edge);height:35px;margin:15px 0;overflow:hidden}.gauge>div{background:repeating-linear-gradient(90deg, var(--accent) 0, var(--accent) 9px, transparent 9px, transparent 12px);height:100%;transition:width .5s}.gauge.timeline{height:14px}.pie-wrap{flex:1;min-height:100px;margin:6px 0;position:relative}.pie-wrap svg{width:100%;height:100%;position:absolute;inset:0}.pie-base{fill:var(--edge);stroke:var(--accent);stroke-width:1px}.pie-fill{fill:var(--accent)}.readout{overflow-wrap:anywhere;color:var(--accent);margin:18px 0;font-size:clamp(22px,3vw,34px)}.credit+.credit{border-top:1px solid var(--edge);padding-top:20px}.credit{margin-top:24px}.session-row{grid-template-columns:minmax(210px,1.1fr) minmax(0,1.2fr) minmax(0,1.5fr);align-items:stretch;gap:14px;margin:18px 0;display:grid}.session-totals{grid-template-columns:repeat(auto-fit,minmax(min(145px,100%),1fr));gap:8px;margin:8px 0;display:grid}.session-totals>div{border:1px solid var(--edge);min-width:0;padding:10px}.session-totals dt{color:var(--muted);font-size:11px}.session-totals dd{color:var(--accent);overflow-wrap:anywhere;margin:6px 0 0;font-size:24px}.session-totals.stale dd{color:var(--muted)}.session-row.selected{outline:1px solid var(--accent);outline-offset:4px}.session-row.wide .context{grid-column:2/-1}.detail-controls{flex-wrap:wrap;gap:6px;display:flex}.attention-summary{flex-wrap:wrap;gap:8px;margin-block:8px;display:flex}.attention-summary .approval{color:var(--approval);border-color:var(--approval)}.attention-summary .approval:hover{background:color-mix(in srgb, var(--approval) 12%, var(--panel))}.attention-summary .approval:focus-visible{outline-color:var(--approval)}.attention-note,.attention-badge{color:var(--accent);border-left:3px solid;padding-left:8px}.attention-note.inferred{color:var(--muted)}.attention-badge{font-size:12px}.session-select{text-align:left;overflow-wrap:anywhere;max-width:100%}.status-detail{color:inherit;text-decoration:none}.status-detail:hover{text-decoration:underline}button:disabled{opacity:.4;cursor:default}.session-row .graph-panel{flex-direction:column;grid-column:2/-1;display:flex}.session-row.split .graph-panel{grid-column:auto}.session-row .button,.session-row button{margin-top:8px;display:inline-block}.context pre{max-height:230px;overflow:auto}.session-row .context{flex-direction:column;display:flex}pre{font:inherit;white-space:pre-wrap;overflow-wrap:anywhere;font-size:13px;line-height:1.7}.full-detail{margin-top:6px}.detail-heading{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:6px 12px;display:flex}.detail-heading h2{flex:250px;min-width:0;margin:0}.detail-metadata{overflow-wrap:anywhere}.detail-workspace{border-top:1px solid var(--edge);flex-direction:column;gap:10px;margin-top:8px;padding-top:4px;display:flex}.detail-context,.detail-workspace .session-actions{min-width:0}.detail-workspace .session-actions{border-top:1px solid var(--edge);padding-top:6px}.full-detail h3{margin-block:8px}.full-detail pre{margin-block:8px;line-height:1.5}.full-detail hr{margin-block:12px}.full-detail .command,.full-detail .notice{padding:8px 10px}.command{background:var(--bg);border-left:3px solid var(--accent);padding:16px}hr{border:0;border-top:1px solid var(--edge);margin:24px 0}.chart{border-bottom:1px solid var(--muted);align-items:flex-end;gap:2px;height:clamp(120px,22vh,320px);margin-top:18px;display:flex}.chart-bar{background:var(--accent);flex:1;min-width:0;max-height:100%}.controls{justify-content:flex-start;margin:20px 0}.summary-grid{grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));gap:16px;margin:20px 0;display:grid}.thresholds-panel{padding:clamp(14px,2vw,22px)}.thresholds-panel h1{margin-top:0}.threshold-list{gap:8px;margin-top:16px;display:grid}.threshold-list article{border:1px solid var(--edge);grid-template-columns:minmax(64px,.5fr) minmax(220px,3fr) minmax(64px,.6fr) minmax(120px,1.2fr);align-items:center;gap:12px;padding:10px 12px;display:grid}.threshold-list article.active{border-color:var(--accent);box-shadow:inset 3px 0 0 var(--accent)}.threshold-list article.next{border-color:var(--approval);box-shadow:inset 3px 0 0 var(--approval)}.threshold-list p{color:var(--muted);margin:3px 0 0;font-size:12px}.threshold-trigger{color:var(--accent);font-size:22px;font-weight:800}.threshold-mode,.threshold-state{color:var(--muted);font-size:12px;font-weight:700}.threshold-list article.active .threshold-state{color:var(--accent)}.threshold-list article.next .threshold-state{color:var(--approval)}.heat-scroll{overflow-x:auto}.heatmap{grid-template-rows:repeat(7,13px);grid-auto-columns:minmax(9px,1fr);grid-auto-flow:column;gap:4px;min-width:620px;margin:25px 0;display:grid}.heat-cell{background:var(--accent);border-radius:2px}.heat-cell.zero{background:var(--edge)}.table-scroll{max-height:280px;overflow:auto}table{border-collapse:collapse;width:100%;margin-top:15px}th,td{text-align:left;border-bottom:1px solid var(--edge);padding:8px}summary{cursor:pointer;color:var(--accent);padding-top:14px}footer{border-top:1px solid var(--edge);color:var(--muted);margin-top:8px;padding-top:8px;font-size:11px}footer select{padding:6px}@media (width<=850px){.session-row{grid-template-columns:minmax(0,1fr)}.session-row.wide .context,.session-row .graph-panel{grid-column:auto}.connection{text-align:left}.threshold-list article{grid-template-columns:64px minmax(0,1fr)}.threshold-mode,.threshold-state{grid-column:2}}@media (prefers-reduced-motion:reduce){*,:before,:after{transition:none!important;animation:none!important}} diff --git a/internal/web/dist/index.html b/internal/web/dist/index.html index db1119a..aeac37e 100644 --- a/internal/web/dist/index.html +++ b/internal/web/dist/index.html @@ -5,8 +5,8 @@ Codexometer // Experimental web - - + +
        diff --git a/internal/web/server.go b/internal/web/server.go index 0b6029b..d8da586 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -13,11 +13,15 @@ import ( "mime" "net" "net/http" + "os" "strconv" "strings" "sync" "time" + "github.com/charmbracelet/x/ansi" + "github.com/charmbracelet/x/term" + "github.com/merefield/codexometer/internal/codex" ) @@ -70,7 +74,8 @@ func Run(ctx context.Context, source Source, refresh time.Duration, port int, ou wait := s.store.collect(ctx, source, refresh) defer func() { cancel(); wait() }() httpServer := newHTTPServer(ctx, s.handler()) - fmt.Fprintf(output, "Experimental web interface // %s\nOpen this private, one-use link within 5 minutes:\nhttp://%s/#pair=%s\nKeep this terminal open. Ctrl+C stops the server. Do not share the link.\n", mode, s.host, s.pairSecret) + url := fmt.Sprintf("http://%s/#pair=%s", s.host, s.pairSecret) + fmt.Fprintf(output, "Experimental web interface // %s\nOpen this private, one-use link within 5 minutes:\n%s\nKeep this terminal open. Ctrl+C stops the server. Do not share the link.\n", mode, launchLink(url, terminalOutput(output))) stop := context.AfterFunc(ctx, func() { _ = httpServer.Close() }) defer stop() err = httpServer.Serve(listener) @@ -81,6 +86,22 @@ func Run(ctx context.Context, source Source, refresh time.Duration, port int, ou return err } +// Keep pipes, files and non-terminal writers free of terminal control sequences. +func terminalOutput(output io.Writer) bool { + file, ok := output.(interface{ Fd() uintptr }) + return ok && os.Getenv("TERM") != "dumb" && term.IsTerminal(file.Fd()) +} + +// The target and visible text both retain the complete one-use pairing fragment. +// Explicit OSC 8 metadata avoids relying on terminal URL detection (including +// wrapped URLs). The URL is generated locally, never supplied by session text. +func launchLink(url string, terminal bool) string { + if !terminal { + return url + } + return ansi.SetHyperlink(url) + "\x1b[4m" + url + "\x1b[24m" + ansi.ResetHyperlink() +} + func newHTTPServer(ctx context.Context, handler http.Handler) *http.Server { // Bound slow ordinary responses too. SSE renews its own shorter per-write // deadline, so a healthy event stream can outlive this response timeout. diff --git a/internal/web/server_test.go b/internal/web/server_test.go index 2c5c958..2f6a76d 100644 --- a/internal/web/server_test.go +++ b/internal/web/server_test.go @@ -9,11 +9,14 @@ import ( "io" "net/http" "net/http/httptest" + "os" "strings" "sync" "testing" "time" + "github.com/charmbracelet/x/ansi" + "github.com/merefield/codexometer/internal/codex" ) @@ -277,7 +280,7 @@ func TestRunStopsCollectorsAndServer(t *testing.T) { if err != nil { t.Fatal(err) } - if !strings.Contains(string(buffer[:n]), "http://127.0.0.1:") || !strings.Contains(string(buffer[:n]), "#pair=") { + if !strings.Contains(string(buffer[:n]), "http://127.0.0.1:") || !strings.Contains(string(buffer[:n]), "#pair=") || strings.ContainsRune(string(buffer[:n]), '\x1b') { t.Fatalf("unsafe/missing launch link: %s", buffer[:n]) } cancel() @@ -390,3 +393,33 @@ func TestSessionStatus(t *testing.T) { } } } + +func TestLaunchLinkPreservesCompletePairingURL(t *testing.T) { + url := "http://127.0.0.1:32123/#pair=ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" + plain := launchLink(url, false) + if plain != url { + t.Fatal("plain output changed the pairing URL") + } + linked := launchLink(url, true) + if ansi.Strip(linked) != url { + t.Fatal("terminal output changed the visible pairing URL") + } + if !strings.HasPrefix(linked, ansi.SetHyperlink(url)+"\x1b[4m") || + !strings.HasSuffix(linked, "\x1b[24m"+ansi.ResetHyperlink()) { + t.Fatal("missing full hyperlink target, underline, or terminal-state reset") + } +} + +func TestTerminalOutputExcludesRedirectedWriters(t *testing.T) { + if terminalOutput(io.Discard) || terminalOutput(new(strings.Builder)) { + t.Fatal("non-terminal writer classified as a terminal") + } + file, err := os.CreateTemp(t.TempDir(), "launch-output") + if err != nil { + t.Fatal(err) + } + defer file.Close() + if terminalOutput(file) { + t.Fatal("redirected file classified as a terminal") + } +} diff --git a/main_test.go b/main_test.go index fba6d77..2f70c5a 100644 --- a/main_test.go +++ b/main_test.go @@ -291,6 +291,9 @@ func TestRunResetWarningHours(t *testing.T) { func sameDemoAccounting(left, right codex.LiveUsageSnapshot) bool { return left.APIEqUSD == right.APIEqUSD && left.APIEqPricedCalls == right.APIEqPricedCalls && left.APIEqUnpricedCalls == right.APIEqUnpricedCalls && + left.APIEqMissingUsageCalls == right.APIEqMissingUsageCalls && + left.APIEqInconsistentUsageCalls == right.APIEqInconsistentUsageCalls && + left.APIEqAccountingAdjustments == right.APIEqAccountingAdjustments && left.APIEqPendingCalls == right.APIEqPendingCalls } diff --git a/web/src/App.svelte b/web/src/App.svelte index d4a00f6..1cd32cd 100644 --- a/web/src/App.svelte +++ b/web/src/App.svelte @@ -6,6 +6,8 @@ import Sessions from './Sessions.svelte'; import Usage from './Usage.svelte'; import Missing from './Missing.svelte'; + import BlockLogo from './BlockLogo.svelte'; + import Startup from './Startup.svelte'; import { preferences, savePreferences } from './preferences.svelte'; const routes = { @@ -21,6 +23,8 @@ usage: /^\/usage\/?$/, }; let theme = $state('hacker'); + let starting = $state(true); + let brand = $state(); const themes = ['hacker', 'rust', 'blue-steel', 'ultraviolet', 'nightshade']; $effect(() => { savePreferences(); @@ -50,10 +54,15 @@ } -
        -
        + diff --git a/web/src/BlockLogo.svelte b/web/src/BlockLogo.svelte new file mode 100644 index 0000000..3eb552c --- /dev/null +++ b/web/src/BlockLogo.svelte @@ -0,0 +1,31 @@ + + + + + diff --git a/web/src/SessionCommands.svelte b/web/src/SessionCommands.svelte index 47f55c8..6b368ad 100644 --- a/web/src/SessionCommands.svelte +++ b/web/src/SessionCommands.svelte @@ -19,6 +19,7 @@ help: string; next?: string; action?: boolean; + selected?: boolean; } interface Menu { title: string; @@ -27,6 +28,7 @@ revision: string; choices: Choice[]; input?: boolean; + picker?: boolean; inputLabel?: string; inputLimit?: number; value?: string; @@ -44,6 +46,12 @@ let selectedIndex = $state(0); let dismissedQuery = $state(null); let optionList: HTMLDivElement | undefined = $state(); + let commandPanel: HTMLDivElement | undefined = $state(); + let reviewPanel: HTMLDivElement | undefined = $state(); + let stagedChoice = $state(''); + const listID = $props.id(); + const picking = $derived(open && !!menu?.picker && !selected); + const browsing = $derived(open && !menu?.input && !selected); const clock = setInterval(() => { now = Date.now(); }, 1000); @@ -62,6 +70,9 @@ (o) => !filter || o.label.toLowerCase().startsWith('/' + filter), ), ); + const stagedSpeed = $derived( + choices.find((choice) => choice.id === stagedChoice), + ); let unavailable = $derived(!live.connected || !!live.data?.sessionsError); let suggesting = $derived( open && slash && (!menu || menu.path === '') && !selected, @@ -73,7 +84,7 @@ $effect(() => { const index = selectedIndex; choices; - if (suggesting) + if (browsing) void tick().then(() => { const row = optionList?.children[index] as HTMLElement | undefined; if (!row || !optionList) return; @@ -85,12 +96,33 @@ optionList.scrollTop += rowRect.bottom - listRect.bottom; }); }); + function highlight(index: number) { + if (selectedIndex !== index && menu?.picker) { + stagedChoice = ''; + confirmation = ''; + expires = 0; + notice = ''; + request++; + } + selectedIndex = index; + } + function closeCommands() { + request++; + busy = false; + open = false; + dismissedQuery = query; + confirmation = ''; + stagedChoice = ''; + expires = 0; + oncomplete(query); + } export function handleKey(event: KeyboardEvent) { + const inside = !!commandPanel?.contains(event.target as Node); if ( - !slash || !open || - (menu && menu.path !== '') || - selected || + menu?.input || + (!browsing && !selected) || + (!slash && !inside) || unavailable || event.isComposing || event.ctrlKey || @@ -98,33 +130,68 @@ event.altKey ) return; - if (!['ArrowUp', 'ArrowDown', 'Tab', 'Enter', 'Escape'].includes(event.key)) + const confirmKey = event.key.toLowerCase() === 'c'; + if (confirmKey && !inside) return; // Never consume composer typing. + if ( + !['ArrowUp', 'ArrowDown', 'Tab', 'Enter', 'Escape'].includes(event.key) && + !confirmKey + ) return; - if (event.key === 'Tab' && event.shiftKey) return; + if ( + event.key === 'Tab' && + (selected || menu?.path !== '' || event.shiftKey) + ) + return; + if ( + event.key === 'Enter' && + event.target instanceof Element && + event.target.closest('button') && + !optionList?.contains(event.target) && + !event.target.closest('[data-command-confirm]') + ) + return; + if (selected && !inside) return; + if (selected && ['ArrowUp', 'ArrowDown'].includes(event.key)) return; event.preventDefault(); + event.stopPropagation(); if (event.key === 'Escape') { - open = false; - dismissedQuery = query; - confirmation = ''; + closeCommands(); return; } + if (busy || (event.repeat && confirmKey)) return; + if (confirmKey) { + if (selected?.action || (picking && stagedChoice)) void commit(); + return; + } + if (selected) return; // Repeated Enter never confirms a reviewed change. if (event.key === 'ArrowUp' || event.key === 'ArrowDown') { - selectedIndex = Math.min( - Math.max(selectedIndex + (event.key === 'ArrowUp' ? -1 : 1), 0), - Math.max(choices.length - 1, 0), + highlight( + Math.min( + Math.max(selectedIndex + (event.key === 'ArrowUp' ? -1 : 1), 0), + Math.max(choices.length - 1, 0), + ), ); + if (!picking && optionList?.contains(event.target as Node)) + void tick().then(() => + ( + optionList?.children[selectedIndex] as HTMLElement | undefined + )?.focus(), + ); return; } const option = choices[selectedIndex]; - if (!option || busy) return; + if (!option) return; if (event.key === 'Tab') oncomplete(option.label); + else if (picking) void reviewChoice(option); else void choose(option); } + $effect(() => { if (unavailable) { request++; busy = false; confirmation = ''; + stagedChoice = ''; if (open) { open = false; dismissedQuery = query; @@ -143,7 +210,9 @@ const seq = ++request; busy = true; selected = null; + stagedChoice = ''; confirmation = ''; + expires = 0; notice = ''; try { const result = await controlRequest('commands', { @@ -159,13 +228,22 @@ next: 'statusline', }); menu = result; + selectedIndex = result.picker + ? Math.max( + result.choices.findIndex((choice) => choice.selected), + 0, + ) + : 0; + if (result.picker) void tick().then(() => optionList?.focus()); name = result.value || ''; if ( (result.path.startsWith('rename/') || result.path.startsWith('cd/')) && result.choices.length === 1 - ) + ) { + busy = false; // The catalogue is ready before preparing its sole choice. void choose(result.choices[0]); + } } } catch { if (seq === request) { @@ -191,6 +269,11 @@ } } async function choose(o: Choice) { + if (menu?.picker) { + highlight(choices.findIndex((choice) => choice.id === o.id)); + optionList?.focus(); + return; + } if (o.next === 'statusline') { statusLineUI.open = true; open = false; @@ -201,10 +284,19 @@ await load(o.next); return; } - selected = o; + await reviewChoice(o); + } + async function reviewChoice(o: Choice) { + if (busy || unavailable || !menu) return; + if (menu.picker) stagedChoice = o.id; + else { + selected = o; + void tick().then(() => reviewPanel?.focus()); + } confirmation = ''; + expires = 0; notice = ''; - if (!o.action || !menu) return; + if (!o.action) return; const seq = ++request; busy = true; try { @@ -221,8 +313,15 @@ }, }); if (seq === request) { + const deadline = Date.parse(result.expires); + if ( + !result.confirmation || + !Number.isFinite(deadline) || + Date.now() >= deadline + ) + throw new Error('Review confirmation unavailable'); confirmation = result.confirmation; - expires = Date.parse(result.expires); + expires = deadline; } } catch { if (seq === request) @@ -231,8 +330,17 @@ if (seq === request) busy = false; } } + async function commit() { - if (!confirmation || unavailable || busy || now >= expires || !menu) return; + if ( + !confirmation || + unavailable || + busy || + Date.now() >= expires || + !menu || + (menu.picker && stagedChoice !== choices[selectedIndex]?.id) + ) + return; const token = confirmation; confirmation = ''; busy = true; @@ -248,6 +356,7 @@ open = false; menu = null; selected = null; + stagedChoice = ''; expires = 0; dismissedQuery = null; onchange(); @@ -261,6 +370,37 @@ } +{#snippet options()} + {#each choices as o, index (o.id)}{/each} +{/snippet} +
        - {#if confirmation && now >= expires}

        - Confirmation expired. Reopen the option. -

        {/if} - {/if} - - {:else}
        - {#each choices as o, index (o.id)}{/each} +

        {selected.label}

        +
        {selected.help || 'No additional help supplied by Codex.'}
        + {#if selected.action}

        + TARGET // {session}. + {#if menu?.path.startsWith('rename/')}Only this session's saved + name changes. + {:else if menu?.path.startsWith('cd/')}Only this session’s + directory changes; project configuration is not reloaded. + {:else}No change sent yet. C confirms the selected setting. Model, + reasoning and speed settings can change token usage and cost. + Changes affect subsequent turns of this session, not global + defaults. Automatic quota thresholds may later supersede model + settings.{/if} +

        + + {#if confirmation && now >= expires}

        + Confirmation expired. Reopen the option. +

        {/if} + {/if} +
        + {:else} + {#if picking} +
        + {@render options()} +
        + {:else} + + {/if} + {#if picking}

        {choices[selectedIndex]?.help}

        +

        + ↑/↓ select · Enter review · C confirm · Escape cancel +

        + {#if stagedSpeed}

        + REVIEW // {stagedSpeed.label}. No change sent yet; C confirms. +

        + {#if confirmation && now >= expires}

        + Review expired. Press Enter to review again. +

        {/if} + {/if} + {/if} {#if !busy && !choices.length}

        No available matching commands.

        {/if} @@ -365,10 +538,37 @@

        {/if} {/if} {#if !suggesting} + {#if picking} + + {/if} + {#if picking}{/if} {/if}
        {/if} @@ -426,10 +626,16 @@ .vertical button.suggested { outline: none; } - .vertical button.suggested .command-name, - .vertical button:hover .command-name { + .command-options button.suggested .command-name { background: var(--accent); color: var(--bg); + font-weight: 700; + } + .command-options button:not(.suggested):hover .command-name { + text-decoration: underline; + } + .command-options button { + color: var(--muted); } .vertical .command-help { flex: 1; diff --git a/web/src/Startup.svelte b/web/src/Startup.svelte new file mode 100644 index 0000000..e38e691 --- /dev/null +++ b/web/src/Startup.svelte @@ -0,0 +1,180 @@ + + +
        + {#if letters} +
        + +
        + {/if} + +
        + + diff --git a/web/src/style.css b/web/src/style.css index b0b28fa..7a93fe4 100644 --- a/web/src/style.css +++ b/web/src/style.css @@ -70,14 +70,20 @@ footer, header { margin-bottom: 10px; } +.shell[data-starting='true'] > header, +.shell[data-starting='true'] > nav, +.shell[data-starting='true'] > main, +.shell[data-starting='true'] > footer { + visibility: hidden; +} .brand { - font-size: clamp(22px, 2.5vw, 32px); - font-weight: 800; - letter-spacing: 0.08em; + display: block; + width: clamp(240px, 32vw, 405px); + max-width: calc(100vw - 40px); color: var(--accent); text-decoration: none; - text-shadow: 0 0 22px color-mix(in srgb, var(--accent) 20%, transparent); } + header p { margin: 3px 0 0; color: var(--muted); diff --git a/web/src/wordmark.ts b/web/src/wordmark.ts new file mode 100644 index 0000000..278a766 --- /dev/null +++ b/web/src/wordmark.ts @@ -0,0 +1,147 @@ +// Keep the browser wordmark identical to internal/ui/header_actions.go. Decode +// the two terminal half-block rows into a four-row bitmap, independent of fonts. +export const title = 'CODEXOMETER'; +const header = [ + '█▀▀ █▀█ █▀▄ █▀▀ ▀▄▀ █▀█ █▀▄▀█ █▀▀ ▀█▀ █▀▀ █▀█', + '█▄▄ █▄█ █▄▀ ██▄ █ █ █▄█ █ ▀ █ ██▄ █ ██▄ █▀▄', +]; +const bitmap = header.flatMap((line) => [ + [...line].map((cell) => cell === '█' || cell === '▀'), + [...line].map((cell) => cell === '█' || cell === '▄'), +]); +export const logoWidth = bitmap[0].length; +export const logoHeight = bitmap.length; +let column = 0; +const slots = [...title].map((letter) => { + const width = letter === 'M' ? 5 : 3; + const slot = { start: column, width }; + column += width + 1; + return slot; +}); +const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; +// Random glyphs share the TUI's tiny font; resolved letters use the header art. +const glyphs = [ + '010 101 111 101', + '110 111 101 110', + '111 100 100 111', + '110 101 101 110', + '111 100 110 111', + '111 100 110 100', + '111 100 101 111', + '101 101 111 101', + '111 010 010 111', + '001 001 101 111', + '101 110 110 101', + '100 100 100 111', + '101 111 101 101', + '101 111 111 101', + '111 101 101 111', + '111 101 111 100', + '111 101 111 001', + '111 101 110 101', + '111 100 001 111', + '111 010 010 010', + '101 101 101 111', + '101 101 101 010', + '101 101 111 101', + '101 010 010 101', + '101 101 010 010', + '111 001 100 111', + '111 101 101 111', + '110 010 010 111', + '111 001 110 111', + '111 011 001 111', + '101 101 111 001', + '111 110 001 111', + '100 111 101 111', + '111 001 010 010', + '111 111 101 111', + '111 101 111 001', +].map((glyph) => glyph.split(' ')); + +export function wordmarkPath(text = title, cursor = -1, dot = false): string { + let path = ''; + for (let row = 0; row < logoHeight; row++) { + for (let position = 0; position < slots.length; position++) { + const slot = slots[position]; + const character = text[position] || ' '; + const glyph = glyphs[alphabet.indexOf(character)]; + for (let col = 0; col < slot.width; col++) { + const lit = + character === title[position] + ? bitmap[row][slot.start + col] + : glyph?.[row][Math.floor((col * 3) / slot.width)] === '1'; + const cursorPixel = + dot && + position === cursor && + row === logoHeight - 1 && + col === Math.floor(slot.width / 2); + if (lit || cursorPixel) path += `M${slot.start + col} ${row}h1v1h-1z`; + } + } + } + return path; +} + +export type Entrance = 'slide' | 'typing' | 'shuffle'; +export const entrances: Entrance[] = ['slide', 'typing', 'shuffle']; +export const entranceDuration = { slide: 900, typing: 2070, shuffle: 1210 }; +export const holdDuration = 180; +export const dockDuration = 800; + +function randomSource(seed: number): () => number { + return () => { + seed |= 0; + seed = (seed + 0x6d2b79f5) | 0; + let value = Math.imul(seed ^ (seed >>> 15), 1 | seed); + value ^= value + Math.imul(value ^ (value >>> 7), 61 | value); + return ((value ^ (value >>> 14)) >>> 0) / 4294967296; + }; +} + +export function createEntrance() { + const variant = entrances[Math.floor(Math.random() * entrances.length)]; + const seed = Math.floor(Math.random() * 4294967296); + const random = randomSource(seed); + const order = [...title].map((_, index) => index); + for (let index = order.length - 1; index > 0; index--) { + const other = Math.floor(random() * (index + 1)); + [order[index], order[other]] = [order[other], order[index]]; + } + return { variant, seed, order }; +} + +export function lettering( + state: ReturnType, + elapsed: number, +) { + if (state.variant === 'typing') { + const blinking = elapsed < 1080; + const typed = blinking + ? 0 + : Math.min(Math.floor((elapsed - 1080) / 90) + 1, title.length); + return { + text: title.slice(0, typed).padEnd(title.length), + cursor: typed, + dot: blinking + ? Math.floor(elapsed / 180) % 2 === 0 + : typed < title.length, + }; + } + if (state.variant === 'shuffle') { + // Redraws and resizes do not consume randomness or undo resolved letters. + const random = randomSource(state.seed ^ Math.floor(elapsed / 70)); + const characters = [...title].map((target) => { + let character; + do { + character = alphabet[Math.floor(random() * alphabet.length)]; + } while (character === target); + return character; + }); + const locked = Math.min(Math.floor(elapsed / 110), title.length); + for (const index of state.order.slice(0, locked)) + characters[index] = title[index]; + return { text: characters.join(''), cursor: -1, dot: false }; + } + return { text: title, cursor: -1, dot: false }; +} diff --git a/web/tests/browser.spec.ts b/web/tests/browser.spec.ts index a86fea2..62419ad 100644 --- a/web/tests/browser.spec.ts +++ b/web/tests/browser.spec.ts @@ -1,6 +1,7 @@ import { test as base, expect } from '@playwright/test'; import { spawn } from 'node:child_process'; import { resolve } from 'node:path'; +import { readFileSync } from 'node:fs'; import type { Page } from '@playwright/test'; // Keep the stream open so tests can distinguish fresh signals from disconnected @@ -9,7 +10,10 @@ async function mockStream(page: Page, snapshot: object) { await page.addInitScript((snapshot) => { const original = window.fetch.bind(window); window.fetch = (input, init) => { - if (input === '/api/events') + if (input === '/api/events') { + document.documentElement.dataset.testStreams = String( + Number(document.documentElement.dataset.testStreams || 0) + 1, + ); return Promise.resolve( new Response( new ReadableStream({ @@ -21,6 +25,11 @@ async function mockStream(page: Page, snapshot: object) { ), ); send(snapshot); + window.addEventListener( + 'test-disconnect', + () => controller.error(new Error('Test stream closed')), + { once: true }, + ); window.addEventListener('test-snapshot', (event) => send((event as CustomEvent).detail), ); @@ -29,6 +38,7 @@ async function mockStream(page: Page, snapshot: object) { { headers: { 'Content-Type': 'text/event-stream' } }, ), ); + } return original(input, init); }; }, snapshot); @@ -97,6 +107,9 @@ const test = base.extend<{ }, }); +// Existing dashboard tests open directly; startup motion is exercised separately. +test.use({ reducedMotion: 'reduce' }); + test.describe('quota profile reviews', () => { test.use({ controlMode: true, quotaMode: 'ask' }); test('profile and native approval coexist and confirm independently', async ({ @@ -1397,13 +1410,46 @@ test('slash commands discover help and require a separate confirmation', async ( panel.getByRole('button', { name: '/model →', exact: true }), ).toBeEnabled(); await text.press('Enter'); - await panel.getByRole('button', { name: 'Medium', exact: true }).click(); + const medium = panel.getByRole('button', { name: 'Medium', exact: true }); + const high = panel.getByRole('button', { name: 'High', exact: true }); + await expect(medium).toHaveClass(/suggested/); + await expect(high).not.toHaveClass(/suggested/); + const selectedStyle = await medium + .locator('.command-name') + .evaluate((node) => ({ + color: getComputedStyle(node).color, + background: getComputedStyle(node).backgroundColor, + weight: getComputedStyle(node).fontWeight, + })); + const unselectedStyle = await high + .locator('.command-name') + .evaluate((node) => ({ + color: getComputedStyle(node).color, + background: getComputedStyle(node).backgroundColor, + })); + expect(selectedStyle.background).not.toBe(unselectedStyle.background); + expect(selectedStyle.color).not.toBe(unselectedStyle.color); + expect(Number(selectedStyle.weight)).toBeGreaterThanOrEqual(700); + await text.press('ArrowDown'); + await expect(high).toHaveClass(/suggested/); + await expect(medium).not.toHaveClass(/suggested/); + await medium.focus(); + await medium.press('c'); + expect( + commands.filter((c) => c === 'prepare' || c === 'commit'), + ).toHaveLength(0); + await medium.press('Enter'); await expect(panel).toContainText('Server effort description'); await expect( panel.getByRole('button', { name: 'CONFIRM CHANGE' }), ).toBeEnabled(); expect(commands.filter((c) => c === 'commit')).toHaveLength(0); - await panel.getByRole('button', { name: 'CONFIRM CHANGE' }).click(); + const review = panel.getByRole('group', { name: 'Review command change' }); + await expect(review).toBeFocused(); + await review.press('Enter'); + await review.press('Enter'); + expect(commands.filter((c) => c === 'commit')).toHaveLength(0); + await review.press('c'); await expect(panel).toContainText('Change requested.'); expect(commands.filter((c) => c === 'commit')).toHaveLength(1); expect(calls.filter((c) => c.action === 'commit')).toHaveLength(0); @@ -1563,6 +1609,271 @@ for (const command of ['rename', 'cd']) { ); } +async function mockSpeedCommands(page: Page) { + await mockActions(page, 'prompt'); + const calls: { mode: string; path: string; choice?: string }[] = []; + let reject = false; + await page.route('**/api/control/commands', async (route) => { + const body = route.request().postDataJSON(); + const command = body.command; + calls.push(command); + if (reject && command.mode === 'commit') { + await route.fulfill({ status: 409, json: { error: 'Changed' } }); + return; + } + await route.fulfill({ + json: + command.mode === 'prepare' + ? { + confirmation: 'speed-token', + expires: new Date(Date.now() + 30000).toISOString(), + } + : command.mode === 'commit' + ? { message: 'Session speed changed. Applies to subsequent turns.' } + : command.path === '' + ? { + title: '/ COMMANDS', + help: 'Live commands', + path: '', + revision: 'r', + choices: [ + { + id: 'speed', + label: '/fast', + help: 'Choose speed', + next: 'tier/flex', + }, + ], + } + : { + title: '/fast', + help: 'Choose session speed', + path: 'tier/flex', + revision: 'r', + picker: true, + choices: [ + { + id: 'default', + label: 'Standard (default)', + help: 'Use the server default', + action: true, + }, + { + id: 'flex', + label: 'Fast', + help: 'Advertised faster tier', + action: true, + }, + { + id: 'priority', + label: 'Slow', + help: 'Advertised slower tier', + action: true, + selected: true, + }, + ], + }, + }); + }); + return { + calls, + rejectCommit: () => { + reject = true; + }, + }; +} + +async function openSpeedCommands(page: Page, pairingURL: string) { + await page.goto(pairingURL); + await page.evaluate(() => { + location.hash = '/sessions/parent'; + }); + const text = page + .locator('.detail-workspace') + .getByRole('textbox', { name: 'Follow-up message' }); + await text.fill('/fast'); + const panel = page.getByRole('region', { name: 'Session slash commands' }); + await expect( + panel.getByRole('button', { name: '/fast →', exact: true }), + ).toBeEnabled(); + await text.press('Enter'); + const list = panel.getByRole('listbox', { name: 'Session speed' }); + await expect(list).toBeFocused(); + await expect(list.getByRole('option')).toHaveCount(3); + return { text, panel, list }; +} + +test('speed picker keeps choices visible while Enter reviews and C confirms', async ({ + page, + pairingURL, +}) => { + const { calls } = await mockSpeedCommands(page); + const { text, panel, list } = await openSpeedCommands(page, pairingURL); + const slow = list.getByRole('option', { + name: 'Slow // CURRENT', + exact: true, + }); + const fast = list.getByRole('option', { name: 'Fast', exact: true }); + await expect(slow).toHaveAttribute('aria-selected', 'true'); + await list.press('ArrowDown'); + await expect(slow).toHaveAttribute('aria-selected', 'true'); + await list.press('ArrowUp'); + await expect(fast).toHaveAttribute('aria-selected', 'true'); + await expect(slow).toHaveAttribute('aria-selected', 'false'); + await expect(slow).toContainText('CURRENT'); + await list.press('c'); + expect(calls.every((c) => c.mode === 'list')).toBe(true); + await expect( + panel.getByRole('button', { name: 'C CONFIRM CHANGE', exact: true }), + ).toBeDisabled(); + await list.press('Enter'); + await expect(panel).toContainText('REVIEW // Fast'); + await expect(list).toBeVisible(); + await expect(list.getByRole('option')).toHaveCount(3); + await expect(text).toHaveValue('/fast'); + expect(calls.filter((c) => c.mode === 'commit')).toHaveLength(0); + await expect( + panel.getByRole('button', { name: 'C CONFIRM CHANGE', exact: true }), + ).toBeEnabled(); + await list.press('c'); + await expect(list).toHaveCount(0); + await expect(panel).toContainText('Session speed changed.'); + await expect(text).toHaveValue(''); + await expect(text).toBeFocused(); + expect(calls.filter((c) => c.mode === 'prepare')).toEqual([ + { mode: 'prepare', path: 'tier/flex', revision: 'r', choice: 'flex' }, + ]); + expect(calls.filter((c) => c.mode === 'commit')).toHaveLength(1); +}); + +test('speed picker footer reviews then confirms and Escape preserves the draft', async ({ + page, + pairingURL, +}) => { + const { calls } = await mockSpeedCommands(page); + const { text, panel, list } = await openSpeedCommands(page, pairingURL); + await list + .getByRole('option', { name: 'Standard (default)', exact: true }) + .click(); + await expect( + list.getByRole('option', { name: 'Standard (default)', exact: true }), + ).toHaveAttribute('aria-selected', 'true'); + expect(calls.every((c) => c.mode === 'list')).toBe(true); + await list.press('Escape'); + await expect(list).toHaveCount(0); + await expect(text).toHaveValue('/fast'); + await expect(text).toBeFocused(); + await text.fill('/fas'); + await expect( + panel.getByRole('button', { name: '/fast →', exact: true }), + ).toBeEnabled(); + await text.press('Enter'); + await expect(list).toBeFocused(); + await list + .getByRole('option', { name: 'Standard (default)', exact: true }) + .click(); + await panel + .getByRole('button', { name: 'ENTER REVIEW', exact: true }) + .click(); + await expect( + panel.getByRole('button', { name: 'C CONFIRM CHANGE', exact: true }), + ).toBeEnabled(); + expect(calls.filter((c) => c.mode === 'commit')).toHaveLength(0); + await panel + .getByRole('button', { name: 'C CONFIRM CHANGE', exact: true }) + .click(); + await expect(list).toHaveCount(0); + expect( + calls.filter((c) => c.mode === 'prepare').map((c) => c.choice), + ).toEqual(['default']); + expect(calls.filter((c) => c.mode === 'commit')).toHaveLength(1); +}); + +test('changing a provisional speed cancels its confirmation', async ({ + page, + pairingURL, +}) => { + const { calls } = await mockSpeedCommands(page); + const { panel, list } = await openSpeedCommands(page, pairingURL); + const confirm = panel.getByRole('button', { + name: 'C CONFIRM CHANGE', + exact: true, + }); + await list.press('Enter'); + await expect(confirm).toBeEnabled(); + await list.press('ArrowUp'); + await expect(confirm).toBeDisabled(); + await expect(panel).not.toContainText('REVIEW // Slow'); + await list.press('c'); + expect(calls.filter((c) => c.mode === 'commit')).toHaveLength(0); + await list.press('Enter'); + await expect(confirm).toBeEnabled(); + // Enter on the confirmation button must still only review, never commit. + await confirm.press('Enter'); + await expect(confirm).toBeEnabled(); + expect(calls.filter((c) => c.mode === 'commit')).toHaveLength(0); + await confirm.press('c'); + await expect(list).toHaveCount(0); + expect( + calls.filter((c) => c.mode === 'prepare').map((c) => c.choice), + ).toEqual(['priority', 'flex', 'flex']); + expect(calls.filter((c) => c.mode === 'commit')).toHaveLength(1); +}); + +test('expired or cancelled speed reviews cannot commit', async ({ + page, + pairingURL, +}) => { + const { calls } = await mockSpeedCommands(page); + const { panel, list, text } = await openSpeedCommands(page, pairingURL); + const confirm = panel.getByRole('button', { + name: 'C CONFIRM CHANGE', + exact: true, + }); + await list.press('Enter'); + await expect(confirm).toBeEnabled(); + await page.clock.setFixedTime(new Date(Date.now() + 60000)); + await list.press('c'); + expect(calls.filter((c) => c.mode === 'commit')).toHaveLength(0); + await expect(confirm).toBeDisabled(); + await expect(panel).toContainText('Review expired'); + await page.clock.setFixedTime(new Date(Date.now())); + await list.press('Enter'); + await expect(confirm).toBeEnabled(); + await list.press('Escape'); + await expect(list).toHaveCount(0); + await expect(text).toHaveValue('/fast'); + await panel.getByRole('button', { name: '/ COMMANDS', exact: true }).click(); + await panel.getByRole('button', { name: '/fast →', exact: true }).click(); + await expect(list).toBeFocused(); + await expect(confirm).toBeDisabled(); + await list.press('c'); + expect(calls.filter((c) => c.mode === 'commit')).toHaveLength(0); +}); + +test('unconfirmed speed changes preserve the list and draft without retrying', async ({ + page, + pairingURL, +}) => { + const { calls, rejectCommit } = await mockSpeedCommands(page); + rejectCommit(); + const { text, panel, list } = await openSpeedCommands(page, pairingURL); + await list.press('ArrowUp'); + await list.press('Enter'); + await expect( + panel.getByRole('button', { name: 'C CONFIRM CHANGE', exact: true }), + ).toBeEnabled(); + await list.press('c'); + await expect(panel).toContainText('Change unconfirmed.'); + await expect(list.getByRole('option')).toHaveCount(3); + await expect(text).toHaveValue('/fast'); + await expect( + panel.getByRole('button', { name: 'C CONFIRM CHANGE', exact: true }), + ).toBeDisabled(); + await list.press('c'); + expect(calls.filter((c) => c.mode === 'commit')).toHaveLength(1); +}); + test('unavailable slash commands close without losing the draft and recover with a fresh catalogue', async ({ page, pairingURL, @@ -2000,7 +2311,7 @@ test('pairing, all quota views, navigation and refresh', async ({ await expect(page).toHaveURL(/#\/quota\/bars$/); await expect(page.getByRole('meter').first()).toBeVisible(); await page.getByRole('link', { name: 'PIE', exact: true }).click(); - await expect(page.locator('svg')).toHaveCount(2); + await expect(page.locator('main svg')).toHaveCount(2); await expect( page.getByRole('link', { name: 'ZONE', exact: true }), ).toHaveCount(0); @@ -3556,3 +3867,340 @@ test('cwd shows the running directory without preparing a mutation', async ({ ).toHaveCount(0); expect(modes.every((mode) => mode === 'list')).toBe(true); }); + +test.describe('browser startup', () => { + test.use({ + reducedMotion: 'no-preference', + controlMode: true, + deviceScaleFactor: 2, + }); + + async function freezeStartup(page: Page, choice: number) { + await page.addInitScript((value) => { + Math.random = () => value; + }, choice); + const time = new Date('2026-10-10T12:00:00Z'); + await page.clock.install({ time }); + await page.clock.pauseAt(new Date(time.getTime() + 1000)); + } + + // Assert parity against the terminal source, rather than a second browser font. + const terminalRows = [ + ...readFileSync( + resolve('../internal/ui/header_actions.go'), + 'utf8', + ).matchAll(/"([█▀▄ ][█▀▄ ]+)"/g), + ] + .slice(0, 2) + .map((match) => [...match[1]]); + const expectedPixels: string[] = []; + terminalRows.forEach((line, row) => + line.forEach((cell, col) => { + if (cell === '█' || cell === '▀') + expectedPixels.push(`${col},${row * 2}`); + if (cell === '█' || cell === '▄') + expectedPixels.push(`${col},${row * 2 + 1}`); + }), + ); + + for (const [variant, choice, duration] of [ + ['slide', 0.1, 900], + ['typing', 0.4, 2070], + ['shuffle', 0.8, 1210], + ] as const) { + test(`${variant} uses the terminal font and docks while pairing loads`, async ({ + page, + pairingURL, + }) => { + const errors: string[] = []; + page.on('pageerror', (error) => errors.push(error.message)); + await freezeStartup(page, choice); + await page.goto(pairingURL); + const intro = page.locator('.startup'); + const wordmark = intro.locator('.wordmark'); + await expect(intro).toHaveAttribute('data-entrance', variant); + await page.clock.runFor(32); + const large = (await wordmark.boundingBox())!; + expect(large.width).toBeCloseTo(1280, 0); + expect(large.y + large.height / 2).toBeCloseTo(360, 0); + if (variant === 'slide') expect(large.x).toBeGreaterThan(0); + else expect(large.x).toBeCloseTo(0, 0); + const initial = (await wordmark.getAttribute('data-text'))!; + expect(initial.length).toBe(11); + if (variant === 'typing') expect(initial.trim()).toBe(''); + if (variant === 'shuffle') { + expect(initial).toMatch(/^[A-Z0-9]{11}$/); + [...initial].forEach((letter, index) => + expect(letter).not.toBe('CODEXOMETER'[index]), + ); + } + await expect(page.locator('.connection .lamp')).toHaveClass(/lit/); + await expect(page.locator('footer')).toContainText( + 'Session control enabled', + ); + await expect(page.locator('header')).toHaveAttribute('inert', ''); + await expect(page.locator('main')).toBeHidden(); + await page.clock.runFor(duration + 30 - 32); + await expect(wordmark).toHaveAttribute('data-text', 'CODEXOMETER'); + const path = await wordmark.locator('path').getAttribute('d'); + expect( + [...path!.matchAll(/M(\d+) (\d+)/g)] + .map((match) => `${match[1]},${match[2]}`) + .sort(), + ).toEqual([...expectedPixels].sort()); + expect(await wordmark.locator('path').getAttribute('d')).toBe( + await page.locator('.brand path').getAttribute('d'), + ); + const full = (await wordmark.boundingBox())!; + expect(full.x).toBeCloseTo(0, 0); + expect(full.width).toBeCloseTo(large.width, 0); + // The SVG must paint at its displayed size, rather than magnifying a + // small compositor raster. This also exercises Retina-scale rendering. + const paintWidth = await wordmark + .locator('svg') + .evaluate((svg) => svg.clientWidth); + expect(paintWidth).toBeCloseTo(full.width, 0); + await page.clock.runFor(949); + const near = (await wordmark.boundingBox())!; + const target = (await page.locator('.brand').boundingBox())!; + // The last animation frame may be up to 16 ms before its exact endpoint. + expect(Math.abs(near.x - target.x)).toBeLessThan(2); + expect(Math.abs(near.y - target.y)).toBeLessThan(2); + expect(Math.abs(near.width - target.width)).toBeLessThan(2); + await page.clock.runFor(100); + await expect(intro).toHaveCount(0); + await expect( + page.getByRole('link', { name: 'CODEXOMETER', exact: true }), + ).toBeVisible(); + await expect(page.locator('header')).not.toHaveAttribute('inert', ''); + await page.getByRole('link', { name: 'SESSIONS', exact: true }).click(); + await expect(page.locator('.session-row')).toHaveCount(2); + await page + .getByRole('link', { name: 'CODEXOMETER', exact: true }) + .click(); + await expect(page).toHaveURL(/#\/quota\/bars$/); + await page.clock.runFor(4000); + await expect(intro).toHaveCount(0); + await page.reload(); + await expect(intro).toHaveAttribute('data-entrance', variant); + expect(errors).toEqual([]); + }); + } + + test('typing blinks three times and advances its dot through the fixed slots', async ({ + page, + pairingURL, + }) => { + await freezeStartup(page, 0.4); + await page.goto(pairingURL); + const wordmark = page.locator('.startup .wordmark'); + let elapsed = 0; + for (const at of [16, 216, 376, 576, 736, 936]) { + await page.clock.runFor(at - elapsed); + elapsed = at; + await expect(wordmark).toHaveAttribute('data-text', ' '.repeat(11)); + if ([16, 376, 736].includes(at)) + await expect(wordmark).toHaveAttribute('data-cursor', '0'); + else await expect(wordmark).not.toHaveAttribute('data-cursor'); + } + for (let count = 1; count <= 11; count++) { + const at = 1096 + (count - 1) * 90; + await page.clock.runFor(at - elapsed); + elapsed = at; + await expect(wordmark).toHaveAttribute( + 'data-text', + 'CODEXOMETER'.slice(0, count).padEnd(11), + ); + if (count < 11) + await expect(wordmark).toHaveAttribute('data-cursor', String(count)); + else await expect(wordmark).not.toHaveAttribute('data-cursor'); + } + }); + + test('shuffle resolves letters permanently in a random order', async ({ + page, + pairingURL, + }) => { + await freezeStartup(page, 0.8); + await page.goto(pairingURL); + const wordmark = page.locator('.startup .wordmark'); + let previous: number[] = []; + let elapsed = 0; + const order: number[] = []; + for (let count = 0; count <= 11; count++) { + const at = count * 110 + 32; + await page.clock.runFor(at - elapsed); + elapsed = at; + const text = (await wordmark.getAttribute('data-text'))!; + const locked = [...text].flatMap((letter, index) => + letter === 'CODEXOMETER'[index] ? [index] : [], + ); + expect(locked.length).toBe(count); + for (const index of previous) expect(locked).toContain(index); + order.push(...locked.filter((index) => !previous.includes(index))); + previous = locked; + } + expect(order).not.toEqual([...Array(11).keys()]); + }); + + test('resizing preserves the entrance and fits the final mobile header', async ({ + page, + pairingURL, + }) => { + await freezeStartup(page, 0.8); + await page.goto(pairingURL); + await page.clock.runFor(600); + const intro = page.locator('.startup'); + const wordmark = intro.locator('.wordmark'); + const before = await wordmark.getAttribute('data-text'); + await page.setViewportSize({ width: 360, height: 640 }); + await page.clock.runFor(16); + await expect(intro).toHaveAttribute('data-entrance', 'shuffle'); + await expect(wordmark).toHaveAttribute('data-text', before!); + const box = (await wordmark.boundingBox())!; + expect(box.width).toBeCloseTo(360, 0); + expect(box.y + box.height / 2).toBeCloseTo(320, 0); + expect( + await page.evaluate(() => document.documentElement.scrollWidth), + ).toBe(360); + await page.clock.runFor(1700); + await expect(intro).toHaveCount(0); + await expect( + page.getByRole('link', { name: 'CODEXOMETER', exact: true }), + ).toBeInViewport(); + await expect(page.getByLabel('Theme', { exact: true })).toBeInViewport(); + }); + + for (const input of ['key', 'click'] as const) { + test(`${input} skips without forwarding an action to the dashboard`, async ({ + page, + pairingURL, + }) => { + await freezeStartup(page, 0.4); + await page.goto(pairingURL); + await expect(page.locator('.startup')).toBeVisible(); + await expect(page.locator('.connection .lamp')).toHaveClass(/lit/); + await page.evaluate(() => { + location.hash = '#/sessions'; + }); + await expect(page.locator('.session-row')).toHaveCount(2); + if (input === 'key') await page.keyboard.press('ArrowDown'); + else await page.mouse.click(40, 30); // The hidden header link lies underneath. + await expect(page.locator('.startup')).toHaveCount(0); + await expect(page.locator('main')).toBeVisible(); + await expect(page).toHaveURL(/#\/sessions$/); + await expect(page.locator('.session-row').first()).toHaveClass( + /selected/, + ); + await page.clock.runFor(5000); + await expect(page.locator('.startup')).toHaveCount(0); + }); + } + + test('modified startup shortcuts pass through without skipping the intro', async ({ + page, + pairingURL, + }) => { + await freezeStartup(page, 0.4); + await page.goto(pairingURL); + const intro = page.locator('.startup'); + await expect(intro).toBeVisible(); + await page.evaluate(() => { + document.addEventListener('keydown', (event) => { + document.documentElement.dataset.shortcutKey = event.key; + document.documentElement.dataset.shortcutPrevented = String( + event.defaultPrevented, + ); + }); + }); + for (const shortcut of [ + 'Shift+Tab', + 'Control+ArrowLeft', + 'Alt+ArrowRight', + 'Meta+k', + 'Shift', + ]) { + await page.keyboard.press(shortcut); + await expect(intro).toBeVisible(); + await expect(page.locator('html')).toHaveAttribute( + 'data-shortcut-prevented', + 'false', + ); + } + // An unmodified key still skips and is consumed before reaching the app. + await page.keyboard.press('ArrowDown'); + await expect(intro).toHaveCount(0); + await expect(page.locator('html')).toHaveAttribute( + 'data-shortcut-key', + 'Shift', + ); + }); + + test('reduced motion opens directly and can also cancel an active intro', async ({ + page, + pairingURL, + }) => { + await freezeStartup(page, 0.4); + await page.emulateMedia({ reducedMotion: 'reduce' }); + await page.goto(pairingURL); + await expect(page.locator('.startup')).toHaveCount(0); + await expect( + page.getByRole('link', { name: 'CODEXOMETER', exact: true }), + ).toBeVisible(); + await page.emulateMedia({ reducedMotion: 'no-preference' }); + await expect(page.locator('.startup')).toHaveCount(0); + await page.reload(); + await expect(page.locator('.startup')).toBeVisible(); + await page.emulateMedia({ reducedMotion: 'reduce' }); + await expect(page.locator('.startup')).toHaveCount(0); + }); + + test('the intro and header use the saved theme and survive stream reconnection', async ({ + page, + pairingURL, + }) => { + await freezeStartup(page, 0.4); + await page.addInitScript(() => + localStorage.setItem('codexometer.web.theme', 'rust'), + ); + const snapshot = { + version: 'startup-test', + meters: [], + credits: [], + sessions: [], + }; + await mockStream(page, snapshot); + await page.goto(pairingURL); + await expect(page.locator('.shell')).toHaveAttribute('data-theme', 'rust'); + const accent = await page + .locator('.brand') + .evaluate((element) => getComputedStyle(element).color); + expect( + await page + .locator('.startup') + .evaluate((element) => getComputedStyle(element).color), + ).toBe(accent); + await expect(page.locator('.connection .lamp')).toHaveClass(/lit/); + await page.evaluate( + (snapshot) => + window.dispatchEvent( + new CustomEvent('test-snapshot', { detail: snapshot }), + ), + { ...snapshot, version: 'fresh-during-intro' }, + ); + await expect(page.locator('footer')).toContainText('fresh-during-intro'); + await page.keyboard.press('Escape'); + await expect(page.locator('footer')).toBeVisible(); + await page.evaluate(() => + window.dispatchEvent(new Event('test-disconnect')), + ); + await expect(page.locator('.connection .lamp')).not.toHaveClass(/lit/); + await page.clock.runFor(2600); + await expect(page.locator('html')).toHaveAttribute( + 'data-test-streams', + '2', + ); + await expect(page.locator('.connection .lamp')).toHaveClass(/lit/); + await expect(page.locator('.startup')).toHaveCount(0); + }); +});