detectkit 0.53.0__tar.gz → 0.53.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {detectkit-0.53.0/detectkit.egg-info → detectkit-0.53.2}/PKG-INFO +1 -1
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/__init__.py +1 -1
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/rules/cli.md +4 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/rules/detectors.md +3 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/skills/dtk-tune/SKILL.md +11 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/reporting/assets/report.js +1 -1
- detectkit-0.53.2/detectkit/tuning/assets/tune.js +187 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/tuning/payload.py +5 -1
- {detectkit-0.53.0 → detectkit-0.53.2/detectkit.egg-info}/PKG-INFO +1 -1
- detectkit-0.53.0/detectkit/tuning/assets/tune.js +0 -187
- {detectkit-0.53.0 → detectkit-0.53.2}/LICENSE +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/MANIFEST.in +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/README.md +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/channels/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/channels/base.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/channels/branding.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/channels/email.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/channels/factory.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/channels/mattermost.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/channels/slack.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/channels/telegram.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/channels/webhook.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/orchestrator/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/orchestrator/_base.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/orchestrator/_decision.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/orchestrator/_recovery.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/orchestrator/_replay.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/orchestrator/_types.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/_base.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/_types.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/autotuner.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/axis_spec.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/config_emitter.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/crossval.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/detector_select.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/distribution.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/grid_search.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/labels.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/result.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/runner.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/scoring.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/seasonality_search.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/settings.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/autotune/window_select.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/_output.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/CLAUDE.section.md +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/rules/alerting.md +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/rules/autotune.md +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/rules/metrics.md +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/rules/overview.md +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/rules/project.md +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/commands/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/commands/autotune.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/commands/clean.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/commands/init.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/commands/init_claude.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/commands/osi.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/commands/run.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/commands/test_alert.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/commands/tune.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/commands/ui.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/commands/unlock.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/cli/main.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/config/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/config/metric_config.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/config/metric_io.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/config/profile.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/config/project_config.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/config/validator.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/core/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/core/interval.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/core/models.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/_sql_manager.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/clickhouse_manager.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/internal_tables/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/internal_tables/_alert_states.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/internal_tables/_autotune_runs.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/internal_tables/_base.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/internal_tables/_datapoints.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/internal_tables/_detections.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/internal_tables/_maintenance.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/internal_tables/_metrics.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/internal_tables/_schema.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/internal_tables/_tasks.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/internal_tables/manager.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/manager.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/mysql_manager.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/postgres_manager.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/database/tables.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/detectors/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/detectors/base.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/detectors/factory.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/detectors/seasonality.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/detectors/statistical/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/detectors/statistical/_windowed.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/detectors/statistical/autoreg.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/detectors/statistical/iqr.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/detectors/statistical/mad.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/detectors/statistical/manual_bounds.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/detectors/statistical/zscore.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/loaders/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/loaders/metric_loader.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/loaders/query_template.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/orchestration/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/orchestration/error_dispatch.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/orchestration/task_manager/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/orchestration/task_manager/_base.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/orchestration/task_manager/_detect_step.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/orchestration/task_manager/_load_step.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/orchestration/task_manager/_types.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/orchestration/task_manager/manager.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/reporting/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/reporting/builder.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/reporting/html_report.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/semantic/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/semantic/errors.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/semantic/exporter.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/semantic/importer.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/semantic/osi_model.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/semantic/query_gen.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/tuning/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/tuning/config_writer.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/tuning/html.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/tuning/server.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/ui/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/ui/assets/ui.js +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/ui/html.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/ui/jobs.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/ui/metric_files.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/ui/overview.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/ui/server.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/utils/__init__.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/utils/datetime_utils.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/utils/env_interpolation.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/utils/json_utils.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit/utils/stats.py +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit.egg-info/SOURCES.txt +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit.egg-info/dependency_links.txt +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit.egg-info/entry_points.txt +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit.egg-info/requires.txt +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/detectkit.egg-info/top_level.txt +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/pyproject.toml +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/requirements.txt +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/setup.cfg +0 -0
- {detectkit-0.53.0 → detectkit-0.53.2}/setup.py +0 -0
|
@@ -4,7 +4,7 @@ detectk - Anomaly Detection for Time-Series Metrics
|
|
|
4
4
|
A Python library for data analysts and engineers to monitor metrics with automatic anomaly detection.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "0.53.
|
|
7
|
+
__version__ = "0.53.2"
|
|
8
8
|
|
|
9
9
|
from detectkit.core.interval import Interval
|
|
10
10
|
from detectkit.core.models import ColumnDefinition, TableModel
|
|
@@ -99,6 +99,10 @@ the **Points shown** data window, the alert rule (**direction** + **consecutive
|
|
|
99
99
|
anomalies** + the **anomaly_window**/**min_anomaly_share** pair) and the
|
|
100
100
|
**y = 0** toggle — stay visible in every mode. The chart is
|
|
101
101
|
**zoomable** (scroll/drag + navigator strip) with a **"Points shown"** trim slider.
|
|
102
|
+
Trim it below what the detector needs to warm up (stabilization roughly doubles
|
|
103
|
+
it — Autoreg needs `2·window_size + lags`) and the chart dims completely with an
|
|
104
|
+
explanation plus an inline warning naming the shortfall and the fixes (raise
|
|
105
|
+
Points shown / lower window size / disable stabilization).
|
|
102
106
|
Clicking **Apply** writes the chosen
|
|
103
107
|
config back into the metric YAML **in place** (autotune, by contrast, writes a new
|
|
104
108
|
`__tuned_<id>.yml` and never edits the original). Apply **merges** — it rewrites
|
|
@@ -236,6 +236,9 @@ recency weighting gives an ongoing incident's points more weight), `detrend`,
|
|
|
236
236
|
`smoothing`, `input_type` and seasonality groups, and is near-neutral on clean
|
|
237
237
|
series. Enabling it is a hashed change — it produces a new `detector_id` and
|
|
238
238
|
recomputes on the next run; existing configs that don't set it keep their ids.
|
|
239
|
+
It also adds one extra `window_size` of warm-up context (`mad`/`zscore`/`iqr`
|
|
240
|
+
and `autoreg` alike) — the `dtk tune` cockpit's visible band respects this,
|
|
241
|
+
so with `clamp` on the band starts one window later than without it.
|
|
239
242
|
|
|
240
243
|
## Feature compatibility
|
|
241
244
|
|
|
@@ -93,6 +93,17 @@ alerts): **Points shown**, the alert rule (**direction** + **consecutive
|
|
|
93
93
|
anomalies** + the **anomaly_window**/**min_anomaly_share** fraction pair), and
|
|
94
94
|
the **y = 0** reference-line toggle.
|
|
95
95
|
|
|
96
|
+
**Warm-up matters when Points shown is trimmed low.** The band and anomaly
|
|
97
|
+
dots only appear once the detector has enough trailing history to warm up —
|
|
98
|
+
if you trim **Points shown** below that, the whole chart dims with an
|
|
99
|
+
explanation ("all shown points are detector warm-up — nothing to score yet")
|
|
100
|
+
and an inline warning names the exact point count still needed, with three
|
|
101
|
+
fixes: raise **Points shown**, lower **Window size**, or turn
|
|
102
|
+
**Stabilization** off. **Stabilization** roughly doubles the requirement —
|
|
103
|
+
Autoreg needs close to `2 · window_size + lags` points at its defaults (over
|
|
104
|
+
400) — so watch for this especially right after switching to Autoreg or
|
|
105
|
+
turning stabilization on with a short **Points shown**.
|
|
106
|
+
|
|
96
107
|
## Step 3 — The four modes (autotune is one of them)
|
|
97
108
|
|
|
98
109
|
Walk the user to the mode that fits what they want to do:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";(()=>{var ye={"--term-bg":"#211e1a","--clay":"#d15b36","--st-anomaly":"#d63232","--st-recovery":"#36a64f","--st-nodata":"#f0ad4e","--st-error":"#5a7a8c","--faint":"#9a9384","--muted":"#6e675b","--border":"#332f29","--term-border":"#332f29"};function E(e){return getComputedStyle(document.documentElement).getPropertyValue(e).trim()||ye[e]||"#888"}function we(e){let t=e.replace("#","").trim();t.length===3&&(t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]);let n=parseInt(t,16);return t.length!==6||Number.isNaN(n)?[209,91,54]:[n>>16&255,n>>8&255,n&255]}function D(e,t){let[n,i,s]=we(e);return`rgba(${n},${i},${s},${t})`}function ee(e){let t=Math.max(1,window.devicePixelRatio||1),n=e.clientWidth||e.offsetWidth||0,i=e.clientHeight||e.offsetHeight||0;return e.width=Math.round(n*t),e.height=Math.round(i*t),t}function te(e,t,n,i){let s=()=>e.width-(t.l+t.r)*i,v=()=>e.height-(t.t+t.b)*i,r=()=>n.tmax-n.tmin||1,a=()=>n.vmax-n.vmin||1;return{px:u=>t.l*i+(u-n.tmin)/r()*s(),py:u=>e.height-t.b*i-(u-n.vmin)/a()*v(),tAt:u=>n.tmin+(u-t.l*i)/(s()||1)*r(),vAt:u=>n.vmin+(e.height-t.b*i-u)/(v()||1)*a(),plotW:s,plotH:v,tspan:r}}var F=Number.isFinite;function ne(e,t,n,i,s,v,r,a,b,f,h,d){let u=t.length,A=Math.max(1,Math.round(r)),S=s-i||1,z=0;for(let g=0;g<u;g++){let m=n[g];!F(m)||t[g]<i||t[g]>s||z++}if(e.strokeStyle=f,e.lineWidth=h*d,e.lineJoin="round",e.beginPath(),z<=A){let g=!1;for(let m=0;m<u;m++){let p=n[m],$=t[m];if(!F(p)||$<i||$>s){g=!1;continue}let C=a($),P=b(p);g?e.lineTo(C,P):(e.moveTo(C,P),g=!0)}}else{let g=new Array(A).fill(null),m=new Array(A).fill(null);for(let $=0;$<u;$++){let C=n[$],P=t[$];if(!F(C)||P<i||P>s)continue;let T=Math.floor((P-i)/S*(A-1));T=T<0?0:T>A-1?A-1:T,(g[T]===null||C<g[T])&&(g[T]=C),(m[T]===null||C>m[T])&&(m[T]=C)}let p=!1;for(let $=0;$<A;$++){if(m[$]===null){p=!1;continue}let C=v+$,P=b(m[$]),T=b(g[$]);p?e.lineTo(C,P):(e.moveTo(C,P),p=!0),e.lineTo(C,T)}}e.stroke()}function re(e){let t=[],n=-1;for(let i=0;i<e.length;i++){let s=e[i];s.lo!==null&&s.hi!==null&&F(s.lo)&&F(s.hi)?n===-1&&(n=i):n!==-1&&(t.push([n,i-1]),n=-1)}return n!==-1&&t.push([n,e.length-1]),t}function oe(e,t,n,i,s,v,r,a,b){e.fillStyle=D(v,r);for(let[f,h]of n){e.beginPath(),e.moveTo(i(t[f].t),s(t[f].hi));for(let d=f+1;d<=h;d++)e.lineTo(i(t[d].t),s(t[d].hi));for(let d=h;d>=f;d--)e.lineTo(i(t[d].t),s(t[d].lo));e.closePath(),e.fill()}e.strokeStyle=D(v,a),e.lineWidth=1*b;for(let[f,h]of n)for(let d of["hi","lo"]){e.beginPath();for(let u=f;u<=h;u++){let A=i(t[u].t),S=s(t[u][d]);u===f?e.moveTo(A,S):e.lineTo(A,S)}e.stroke()}}function ie(e,t,n,i,s,v,r,a){for(let b=0;b<t.length;b++){let f=t[b];if(!F(f.v)||f.t<n||f.t>i)continue;let h=s(f.t),d=v(f.v);e.fillStyle=D(r,.18),e.beginPath(),e.arc(h,d,6*a,0,Math.PI*2),e.fill(),e.fillStyle=r,e.beginPath(),e.arc(h,d,3*a,0,Math.PI*2),e.fill()}}function se(e,t,n,i,s,v,r,a,b,f,h){e.font=`${11*h}px ui-monospace, 'JetBrains Mono', monospace`,e.textBaseline="middle";for(let u=0;u<=4;u++){let A=i.vmin+(i.vmax-i.vmin)*u/4,S=v(A);e.strokeStyle=D(b,.1),e.lineWidth=1*h,e.beginPath(),e.moveTo(n.l*h,S),e.lineTo(t.width-n.r*h,S),e.stroke(),e.fillStyle=f,e.textAlign="right",e.fillText(W(A),(n.l-8)*h,S)}e.textBaseline="top";let d=a-r||1;for(let u=0;u<=5;u++){let A=r+d*u/5,S=s(A);e.fillStyle=f,e.textAlign=u===0?"left":u===5?"right":"center",e.fillText($e(A,d),S,(t.height-n.b+7)*h)}}function W(e){let t=Math.abs(e);return t>=1e3?e.toFixed(0):t>=10?e.toFixed(1):t>=1?e.toFixed(2):e.toFixed(3)}function $e(e,t){let n=new Date(e).toISOString();return t<2*864e5?n.slice(5,16).replace("T"," "):n.slice(5,10)}function X(e){return new Date(e).toISOString().slice(0,19).replace("T"," ")}function ae(e){let t=Math.round(e/6e4);if(t<60)return t+"m";let n=Math.floor(t/60),i=t%60;if(n<24)return n+"h"+(i?" "+i+"m":"");let s=Math.floor(n/24),v=n%24;return s+"d"+(v?" "+v+"h":"")}function le(e,t,n){return{left:t.l*n,top:t.t*n,right:e.width-t.r*n,bottom:e.height-t.b*n}}function ce(e,t,n,i,s,v,r){let a=le(t,n,i),b=Math.max(a.left,Math.min(s(v),a.right));b<=a.left+.5||(e.save(),e.fillStyle="rgba(17,15,13,0.42)",e.fillRect(a.left,a.top,b-a.left,a.bottom-a.top),e.strokeStyle=D(E("--faint"),.7),e.lineWidth=1*i,e.setLineDash([4*i,4*i]),e.beginPath(),e.moveTo(b,a.top),e.lineTo(b,a.bottom),e.stroke(),e.setLineDash([]),e.fillStyle=D(E("--faint"),.95),e.font=`${10*i}px ui-monospace, monospace`,e.textAlign="left",e.textBaseline="top",e.fillText(r,b+6*i,a.top+5*i),e.restore())}function de(e,t,n,i,s,v,r){let a=le(t,n,i),b=5*i;e.save();for(let f=0;f<v.length;f++){let h=v[f],d=s(h.t);if(d<a.left-1||d>a.right+1)continue;let u=r(h.kind);e.strokeStyle=D(u,.45),e.lineWidth=1*i,e.beginPath(),e.moveTo(d,a.top),e.lineTo(d,a.bottom),e.stroke(),e.fillStyle=u,e.beginPath(),e.moveTo(d-b,a.top),e.lineTo(d+b,a.top),e.lineTo(d,a.top+b*1.4),e.closePath(),e.fill()}e.restore()}var L={l:56,r:16,t:14,b:28},Me=300*1e3,c="dtk-report";function G(e){return e==="recovery"?E("--st-recovery"):e==="no_data"?E("--st-nodata"):E("--st-anomaly")}function Ae(e){return e==="recovery"?"recovery":e==="no_data"?"no-data":"anomaly"}var M=e=>String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""),K=(e,t,n)=>Math.max(t,Math.min(n,e)),ue=["--clay","--st-error","--st-recovery","--st-nodata"];function Te(e,t){Re(),t.classList.add(c),t.innerHTML="";let n=document.createElement("div");n.className="dtk-report-root",t.appendChild(n);let i=new Map;for(let m of e.points)m.v!==null&&i.set(m.t,m.v);let s=e.detectors.map((m,p)=>{let $=m.effective_start,C=m.points.map(T=>$!==null&&T.t<$?{t:T.t,lo:null,hi:null}:{t:T.t,lo:T.lo,hi:T.hi}),P=[];for(let T of m.points)if(T.a===1&&($===null||T.t>=$)){let I=i.get(T.t);I!==void 0&&P.push({t:T.t,v:I})}return{det:m,band:C,anomalies:P,effectiveStart:$,color:E(ue[p%ue.length]),shown:p===0}});s.length===1&&(s[0].shown=!0),n.appendChild(Se(e));let v=null;s.length>1&&(v=Ce(s,()=>d.repaint()),n.appendChild(v));let r=document.createElement("div");r.className="dtk-bar",n.appendChild(r);let a=document.createElement("div");a.className="dtk-presets",r.appendChild(a);let b=document.createElement("div");b.className="dtk-readout",b.textContent="hover the chart for a point readout",r.appendChild(b);let f=document.createElement("div");f.className="dtk-chart";let h=document.createElement("canvas");f.appendChild(h),n.appendChild(f);let d=Pe(h,e,s,i,m=>{b.innerHTML=m}),u=[{label:"24h",ms:24*3600*1e3},{label:"7d",ms:7*86400*1e3},{label:"30d",ms:30*86400*1e3},{label:"All",ms:null}];for(let m of u){let p=document.createElement("button");p.className="dtk-preset",p.textContent=m.label,p.onclick=()=>{m.ms===null?d.resetView():d.setView(e.period.end-m.ms,e.period.end),Le(a,p)},a.appendChild(p)}let A=document.createElement("label");A.className="dtk-zero",A.title="Draw a horizontal line at y = 0 and scale the chart to include zero.";let S=document.createElement("input");S.type="checkbox",S.onchange=()=>d.setZeroLine(S.checked),A.appendChild(S),A.appendChild(document.createTextNode(" y = 0")),a.appendChild(A),n.appendChild(Ee(e,m=>d.focusAlert(m))),d.resize();let z=0,g=()=>{z||(z=requestAnimationFrame(()=>{z=0,d.resize()}))};window.addEventListener("resize",g)}function Se(e){let t=document.createElement("div");t.className="dtk-header";let n=e.interval_seconds/60,i=e.interval_seconds>=86400?e.interval_seconds/86400+"d":e.interval_seconds>=3600?e.interval_seconds/3600+"h":n>=1?n+"min":e.interval_seconds+"s",s=e.project?`${M(e.project)} \xB7 ${M(e.metric)}`:M(e.metric),v=e.summary;return t.innerHTML=`<div class="dtk-h-top"><h1 class="dtk-title">${s}</h1><div class="dtk-meta">${M(X(e.period.start))} \u2013 ${M(X(e.period.end))} \xB7 interval ${M(i)}${e.generated_at?` \xB7 generated ${M(e.generated_at)}`:""}</div></div>`+(e.description?`<p class="dtk-desc">${M(e.description)}</p>`:"")+'<div class="dtk-chips">'+Y("anomalies",v.anomalies,"--st-anomaly")+Y("alerts",v.alerts,"--clay")+Y("recoveries",v.recoveries,"--st-recovery")+Y("no-data",v.no_data,"--st-nodata")+"</div>",t}function Y(e,t,n){let i=E(n);return`<span class="dtk-chip"><span class="dtk-dot" style="background:${M(i)}"></span><span class="dtk-chip-n">${t}</span><span class="dtk-chip-l">${M(e)}</span></span>`}function Ce(e,t){let n=document.createElement("div");return n.className="dtk-legend",e.forEach(i=>{let s=document.createElement("button");s.className="dtk-legend-item"+(i.shown?"":" off"),s.innerHTML=`<span class="dtk-swatch" style="background:${M(i.color)}"></span><span class="dtk-legend-name">${M(i.det.name)}</span><span class="dtk-legend-id">${M(i.det.id.slice(0,8))}</span><span class="dtk-legend-n">${i.det.anomaly_count}</span>`,s.onclick=()=>{i.shown=!i.shown,s.classList.toggle("off",!i.shown),t()},n.appendChild(s)}),n}function Ee(e,t){let n=document.createElement("div");n.className="dtk-alerts";let i=document.createElement("div");if(i.className="dtk-alerts-head",i.textContent=`Alerts (${e.alerts.length})`,n.appendChild(i),e.alerts.length===0){let r=document.createElement("div");return r.className="dtk-alerts-empty",r.textContent="No alerts fired in this period.",n.appendChild(r),n}let s=document.createElement("div");s.className="dtk-alerts-list";let v=[...e.alerts].sort((r,a)=>a.t-r.t);for(let r of v){let a=document.createElement("button");a.className="dtk-alert-row";let b=G(r.kind),f=r.direction!=="none"?` \xB7 ${M(r.direction)}`:"",h=r.severity>0?` \xB7 sev ${r.severity.toFixed(2)}`:"",d=r.value!==null?` \xB7 value ${W(r.value)}`:"",u=r.onset!==null&&r.kind!=="no_data"?` \xB7 ${ae(Math.max(0,r.t-r.onset))} (${r.consecutive} pts)`:"";a.innerHTML=`<span class="dtk-alert-time">${M(X(r.t))}</span><span class="dtk-badge" style="background:${M(D(b,.18))};color:${M(b)};border-color:${M(D(b,.5))}">${M(Ae(r.kind))}</span><span class="dtk-alert-body"><span class="dtk-alert-rule">${M(r.rule)}</span><span class="dtk-alert-sub">${M(r.detector)}${f}${h}${d}${M(u)}</span></span>`,a.onclick=()=>t(r),s.appendChild(a)}return n.appendChild(s),n}function Le(e,t){e.querySelectorAll(".dtk-preset").forEach(n=>n.classList.remove("active")),t.classList.add("active")}function Pe(e,t,n,i,s){let v=e.getContext("2d");if(!v)throw new Error("report: 2D context unavailable");let r=v,a=t.points.map(o=>o.t),b=t.points.map(o=>o.v===null?NaN:o.v),f=t.period.start,h=t.period.end,d=h-f||1,u=Math.min(Me,d),A=0,S=1,z=!1;C();let g=f,m=h,p=1,$=null;function C(){let o=1/0,l=-1/0;for(let y of b)Number.isFinite(y)&&(y<o&&(o=y),y>l&&(l=y));for(let y of n)for(let k of y.band)k.lo!==null&&Number.isFinite(k.lo)&&k.lo<o&&(o=k.lo),k.hi!==null&&Number.isFinite(k.hi)&&k.hi>l&&(l=k.hi);(!Number.isFinite(o)||!Number.isFinite(l))&&(o=0,l=1),z&&(o>0&&(o=0),l<0&&(l=0)),l<=o&&(l=o+1);let x=(l-o)*.06;A=o-x,S=l+x}function P(o){z!==o&&(z=o,C(),V())}function T(){for(let o of n)if(o.shown&&o.effectiveStart!==null)return o.effectiveStart;for(let o of n)if(o.effectiveStart!==null)return o.effectiveStart;return null}function I(){return{tmin:g,tmax:m,vmin:A,vmax:S}}function fe(){return te(e,L,I(),p)}function O(o,l){let x=l-o;if(x<u){let y=(o+l)/2;o=y-u/2,l=y+u/2,x=u}x>=d&&(o=f,l=h),o<f&&(l+=f-o,o=f),l>h&&(o-=l-h,l=h),g=K(o,f,h),m=K(l,f,h),V()}function Z(){g=f,m=h,V()}function pe(o){let l=o.onset!==null?Math.min(o.onset,o.t):o.t,x=o.t,k=Math.max(x-l,u)*1.5+u;O(l-k,x+k)}let j=0;function q(){j===0&&(j=requestAnimationFrame(V))}function V(){if(j=0,e.width===0||e.height===0)return;let o=fe(),l=E("--faint"),x=E("--muted"),y=E("--clay");if(r.fillStyle=E("--term-bg"),r.fillRect(0,0,e.width,e.height),a.length===0)return;if(se(r,e,L,I(),o.px,o.py,g,m,l,x,p),z&&A<=0&&S>=0){let w=o.py(0);r.strokeStyle=D(x,.6),r.lineWidth=1.25*p,r.beginPath(),r.moveTo(L.l*p,w),r.lineTo(e.width-L.r*p,w),r.stroke(),r.fillStyle=x,r.textAlign="right",r.textBaseline="middle",r.fillText("0",(L.l-8)*p,w)}r.save(),r.beginPath(),r.rect(L.l*p,L.t*p,o.plotW(),o.plotH()),r.clip();let k=L.t*p,N=o.plotH();for(let w of t.alerts){if(w.onset===null||w.kind==="no_data")continue;let B=Math.min(w.onset,w.t),U=Math.max(w.onset,w.t);if(U<g||B>m)continue;let ke=G(w.kind),Q=o.px(B),ge=o.px(U);r.fillStyle=D(ke,.08),r.fillRect(Q,k,Math.max(ge-Q,1*p),N)}for(let w of n){if(!w.shown)continue;let B=re(w.band);oe(r,w.band,B,o.px,o.py,w.color,.13,.4,p)}ne(r,a,b,g,m,L.l*p,o.plotW(),o.px,o.py,y,1.5,p);for(let w of n)w.shown&&ie(r,w.anomalies,g,m,o.px,o.py,E("--st-anomaly"),p);let R=T();R!==null&&R>g&&ce(r,e,L,p,o.px,R,"detection at full power \u2192");let H=t.alerts.map(w=>({t:w.t,kind:w.kind}));de(r,e,L,p,o.px,H,w=>G(w)),$!==null&&he(o,k,N,l),r.restore()}function be(o){let l=a;if(l.length===0)return-1;let x=0,y=l.length-1;for(;x<y;){let k=x+y>>1;l[k]<o?x=k+1:y=k}return x>0&&o-l[x-1]<l[x]-o&&(x-=1),x}function he(o,l,x,y){let k=be($);if(k<0)return;let N=a[k];if(N<g||N>m){s("hover the chart for a point readout");return}let R=o.px(N);r.strokeStyle=D(y,.45),r.lineWidth=1*p,r.setLineDash([2*p,2*p]),r.beginPath(),r.moveTo(R,l),r.lineTo(R,l+x),r.stroke(),r.setLineDash([]);let H=b[k];if(Number.isFinite(H)){let w=o.py(H);r.fillStyle=E("--term-bg"),r.beginPath(),r.arc(R,w,4*p,0,Math.PI*2),r.fill(),r.strokeStyle=E("--clay"),r.lineWidth=2*p,r.beginPath(),r.arc(R,w,4*p,0,Math.PI*2),r.stroke()}ve(k)}function ve(o){let l=a[o],x=b[o],y=`<span class="dtk-ro-t">${M(X(l))}</span>`;y+=`<span class="dtk-ro-v">value ${Number.isFinite(x)?W(x):"\u2014"}</span>`;for(let k of n){if(!k.shown)continue;let N=k.band[o],R=k.det.points[o];if(N&&N.lo!==null&&N.hi!==null){let H=R&&R.a===1,w=H&&R.sev!==null?` sev ${R.sev.toFixed(2)}`:"",B=H?`<span class="dtk-ro-anom" style="color:${M(E("--st-anomaly"))}">anomaly${M(w)}</span>`:'<span class="dtk-ro-ok">ok</span>';y+=`<span class="dtk-ro-det"><span class="dtk-swatch" style="background:${M(k.color)}"></span>${M(k.det.name)}: [${W(N.lo)}, ${W(N.hi)}] ${B}</span>`}}s(y)}function J(o){let l=e.getBoundingClientRect(),x=(o-l.left-L.l)/(l.width-(L.l+L.r)||1);return g+K(x,0,1)*(m-g)}e.addEventListener("wheel",o=>{o.preventDefault();let l=J(o.clientX),x=m-g,y=K(x*Math.pow(1.0015,o.deltaY),u,d),k=(l-g)/(x||1);O(l-k*y,l-k*y+y)},{passive:!1});let _=null;e.addEventListener("mousedown",o=>{_={x:o.clientX,vMin:g,vMax:m},e.style.cursor="grabbing"}),window.addEventListener("mousemove",o=>{if(!_)return;let l=e.getBoundingClientRect(),x=(_.vMax-_.vMin)/(l.width-(L.l+L.r)||1),y=(o.clientX-_.x)*x;O(_.vMin-y,_.vMax-y)}),window.addEventListener("mouseup",()=>{_&&(_=null,e.style.cursor="crosshair")}),e.addEventListener("mousemove",o=>{_||($=J(o.clientX),q())}),e.addEventListener("mouseleave",()=>{$!==null&&($=null,s("hover the chart for a point readout"),q())}),e.addEventListener("dblclick",()=>Z()),e.style.cursor="crosshair";function xe(){p=ee(e),V()}return{repaint:()=>q(),resize:xe,setView:O,resetView:Z,focusAlert:pe,setZeroLine:P}}var me=!1;function Re(){if(me)return;me=!0;let e=`
|
|
1
|
+
"use strict";(()=>{var ye={"--term-bg":"#211e1a","--clay":"#d15b36","--st-anomaly":"#d63232","--st-recovery":"#36a64f","--st-nodata":"#f0ad4e","--st-error":"#5a7a8c","--faint":"#9a9384","--muted":"#6e675b","--border":"#332f29","--term-border":"#332f29"};function E(e){return getComputedStyle(document.documentElement).getPropertyValue(e).trim()||ye[e]||"#888"}function we(e){let t=e.replace("#","").trim();t.length===3&&(t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]);let n=parseInt(t,16);return t.length!==6||Number.isNaN(n)?[209,91,54]:[n>>16&255,n>>8&255,n&255]}function D(e,t){let[n,i,s]=we(e);return`rgba(${n},${i},${s},${t})`}function ee(e){let t=Math.max(1,window.devicePixelRatio||1),n=e.clientWidth||e.offsetWidth||0,i=e.clientHeight||e.offsetHeight||0;return e.width=Math.round(n*t),e.height=Math.round(i*t),t}function te(e,t,n,i){let s=()=>e.width-(t.l+t.r)*i,v=()=>e.height-(t.t+t.b)*i,r=()=>n.tmax-n.tmin||1,a=()=>n.vmax-n.vmin||1;return{px:u=>t.l*i+(u-n.tmin)/r()*s(),py:u=>e.height-t.b*i-(u-n.vmin)/a()*v(),tAt:u=>n.tmin+(u-t.l*i)/(s()||1)*r(),vAt:u=>n.vmin+(e.height-t.b*i-u)/(v()||1)*a(),plotW:s,plotH:v,tspan:r}}var F=Number.isFinite;function ne(e,t,n,i,s,v,r,a,b,f,h,d){let u=t.length,A=Math.max(1,Math.round(r)),S=s-i||1,z=0;for(let g=0;g<u;g++){let m=n[g];!F(m)||t[g]<i||t[g]>s||z++}if(e.strokeStyle=f,e.lineWidth=h*d,e.lineJoin="round",e.beginPath(),z<=A){let g=!1;for(let m=0;m<u;m++){let p=n[m],$=t[m];if(!F(p)||$<i||$>s){g=!1;continue}let C=a($),R=b(p);g?e.lineTo(C,R):(e.moveTo(C,R),g=!0)}}else{let g=new Array(A).fill(null),m=new Array(A).fill(null);for(let $=0;$<u;$++){let C=n[$],R=t[$];if(!F(C)||R<i||R>s)continue;let T=Math.floor((R-i)/S*(A-1));T=T<0?0:T>A-1?A-1:T,(g[T]===null||C<g[T])&&(g[T]=C),(m[T]===null||C>m[T])&&(m[T]=C)}let p=!1;for(let $=0;$<A;$++){if(m[$]===null){p=!1;continue}let C=v+$,R=b(m[$]),T=b(g[$]);p?e.lineTo(C,R):(e.moveTo(C,R),p=!0),e.lineTo(C,T)}}e.stroke()}function re(e){let t=[],n=-1;for(let i=0;i<e.length;i++){let s=e[i];s.lo!==null&&s.hi!==null&&F(s.lo)&&F(s.hi)?n===-1&&(n=i):n!==-1&&(t.push([n,i-1]),n=-1)}return n!==-1&&t.push([n,e.length-1]),t}function oe(e,t,n,i,s,v,r,a,b){e.fillStyle=D(v,r);for(let[f,h]of n){e.beginPath(),e.moveTo(i(t[f].t),s(t[f].hi));for(let d=f+1;d<=h;d++)e.lineTo(i(t[d].t),s(t[d].hi));for(let d=h;d>=f;d--)e.lineTo(i(t[d].t),s(t[d].lo));e.closePath(),e.fill()}e.strokeStyle=D(v,a),e.lineWidth=1*b;for(let[f,h]of n)for(let d of["hi","lo"]){e.beginPath();for(let u=f;u<=h;u++){let A=i(t[u].t),S=s(t[u][d]);u===f?e.moveTo(A,S):e.lineTo(A,S)}e.stroke()}}function ie(e,t,n,i,s,v,r,a){for(let b=0;b<t.length;b++){let f=t[b];if(!F(f.v)||f.t<n||f.t>i)continue;let h=s(f.t),d=v(f.v);e.fillStyle=D(r,.18),e.beginPath(),e.arc(h,d,6*a,0,Math.PI*2),e.fill(),e.fillStyle=r,e.beginPath(),e.arc(h,d,3*a,0,Math.PI*2),e.fill()}}function se(e,t,n,i,s,v,r,a,b,f,h){e.font=`${11*h}px ui-monospace, 'JetBrains Mono', monospace`,e.textBaseline="middle";for(let u=0;u<=4;u++){let A=i.vmin+(i.vmax-i.vmin)*u/4,S=v(A);e.strokeStyle=D(b,.1),e.lineWidth=1*h,e.beginPath(),e.moveTo(n.l*h,S),e.lineTo(t.width-n.r*h,S),e.stroke(),e.fillStyle=f,e.textAlign="right",e.fillText(W(A),(n.l-8)*h,S)}e.textBaseline="top";let d=a-r||1;for(let u=0;u<=5;u++){let A=r+d*u/5,S=s(A);e.fillStyle=f,e.textAlign=u===0?"left":u===5?"right":"center",e.fillText($e(A,d),S,(t.height-n.b+7)*h)}}function W(e){let t=Math.abs(e);return t>=1e3?e.toFixed(0):t>=10?e.toFixed(1):t>=1?e.toFixed(2):e.toFixed(3)}function $e(e,t){let n=new Date(e).toISOString();return t<2*864e5?n.slice(5,16).replace("T"," "):n.slice(5,10)}function O(e){return new Date(e).toISOString().slice(0,19).replace("T"," ")}function ae(e){let t=Math.round(e/6e4);if(t<60)return t+"m";let n=Math.floor(t/60),i=t%60;if(n<24)return n+"h"+(i?" "+i+"m":"");let s=Math.floor(n/24),v=n%24;return s+"d"+(v?" "+v+"h":"")}function le(e,t,n){return{left:t.l*n,top:t.t*n,right:e.width-t.r*n,bottom:e.height-t.b*n}}function ce(e,t,n,i,s,v,r){let a=le(t,n,i),b=Math.max(a.left,Math.min(s(v),a.right));b<=a.left+.5||(e.save(),e.fillStyle="rgba(17,15,13,0.42)",e.fillRect(a.left,a.top,b-a.left,a.bottom-a.top),e.strokeStyle=D(E("--faint"),.7),e.lineWidth=1*i,e.setLineDash([4*i,4*i]),e.beginPath(),e.moveTo(b,a.top),e.lineTo(b,a.bottom),e.stroke(),e.setLineDash([]),e.fillStyle=D(E("--faint"),.95),e.font=`${10*i}px ui-monospace, monospace`,e.textAlign="left",e.textBaseline="top",e.fillText(r,b+6*i,a.top+5*i),e.restore())}function de(e,t,n,i,s,v,r){let a=le(t,n,i),b=5*i;e.save();for(let f=0;f<v.length;f++){let h=v[f],d=s(h.t);if(d<a.left-1||d>a.right+1)continue;let u=r(h.kind);e.strokeStyle=D(u,.45),e.lineWidth=1*i,e.beginPath(),e.moveTo(d,a.top),e.lineTo(d,a.bottom),e.stroke(),e.fillStyle=u,e.beginPath(),e.moveTo(d-b,a.top),e.lineTo(d+b,a.top),e.lineTo(d,a.top+b*1.4),e.closePath(),e.fill()}e.restore()}var L={l:56,r:16,t:14,b:28},Me=300*1e3,c="dtk-report";function G(e){return e==="recovery"?E("--st-recovery"):e==="no_data"?E("--st-nodata"):E("--st-anomaly")}function Ae(e){return e==="recovery"?"recovery":e==="no_data"?"no-data":"anomaly"}var M=e=>String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""),K=(e,t,n)=>Math.max(t,Math.min(n,e)),ue=["--clay","--st-error","--st-recovery","--st-nodata"];function Te(e,t){Pe(),t.classList.add(c),t.innerHTML="";let n=document.createElement("div");n.className="dtk-report-root",t.appendChild(n);let i=new Map;for(let m of e.points)m.v!==null&&i.set(m.t,m.v);let s=e.detectors.map((m,p)=>{let $=m.effective_start,C=m.points.map(T=>$!==null&&T.t<$?{t:T.t,lo:null,hi:null}:{t:T.t,lo:T.lo,hi:T.hi}),R=[];for(let T of m.points)if(T.a===1&&($===null||T.t>=$)){let I=i.get(T.t);I!==void 0&&R.push({t:T.t,v:I})}return{det:m,band:C,anomalies:R,effectiveStart:$,color:E(ue[p%ue.length]),shown:p===0}});s.length===1&&(s[0].shown=!0),n.appendChild(Se(e));let v=null;s.length>1&&(v=Ce(s,()=>d.repaint()),n.appendChild(v));let r=document.createElement("div");r.className="dtk-bar",n.appendChild(r);let a=document.createElement("div");a.className="dtk-presets",r.appendChild(a);let b=document.createElement("div");b.className="dtk-readout",b.textContent="hover the chart for a point readout",r.appendChild(b);let f=document.createElement("div");f.className="dtk-chart";let h=document.createElement("canvas");f.appendChild(h),n.appendChild(f);let d=Re(h,e,s,i,m=>{b.innerHTML=m}),u=[{label:"24h",ms:24*3600*1e3},{label:"7d",ms:7*86400*1e3},{label:"30d",ms:30*86400*1e3},{label:"All",ms:null}];for(let m of u){let p=document.createElement("button");p.className="dtk-preset",p.textContent=m.label,p.onclick=()=>{m.ms===null?d.resetView():d.setView(e.period.end-m.ms,e.period.end),Le(a,p)},a.appendChild(p)}let A=document.createElement("label");A.className="dtk-zero",A.title="Draw a horizontal line at y = 0 and scale the chart to include zero.";let S=document.createElement("input");S.type="checkbox",S.onchange=()=>d.setZeroLine(S.checked),A.appendChild(S),A.appendChild(document.createTextNode(" y = 0")),a.appendChild(A),n.appendChild(Ee(e,m=>d.focusAlert(m))),d.resize();let z=0,g=()=>{z||(z=requestAnimationFrame(()=>{z=0,d.resize()}))};window.addEventListener("resize",g)}function Se(e){let t=document.createElement("div");t.className="dtk-header";let n=e.interval_seconds/60,i=e.interval_seconds>=86400?e.interval_seconds/86400+"d":e.interval_seconds>=3600?e.interval_seconds/3600+"h":n>=1?n+"min":e.interval_seconds+"s",s=e.project?`${M(e.project)} \xB7 ${M(e.metric)}`:M(e.metric),v=e.summary;return t.innerHTML=`<div class="dtk-h-top"><h1 class="dtk-title">${s}</h1><div class="dtk-meta">${M(O(e.period.start))} \u2013 ${M(O(e.period.end))} \xB7 interval ${M(i)}${e.generated_at?` \xB7 generated ${M(e.generated_at)}`:""}</div></div>`+(e.description?`<p class="dtk-desc">${M(e.description)}</p>`:"")+'<div class="dtk-chips">'+Y("anomalies",v.anomalies,"--st-anomaly")+Y("alerts",v.alerts,"--clay")+Y("recoveries",v.recoveries,"--st-recovery")+Y("no-data",v.no_data,"--st-nodata")+"</div>",t}function Y(e,t,n){let i=E(n);return`<span class="dtk-chip"><span class="dtk-dot" style="background:${M(i)}"></span><span class="dtk-chip-n">${t}</span><span class="dtk-chip-l">${M(e)}</span></span>`}function Ce(e,t){let n=document.createElement("div");return n.className="dtk-legend",e.forEach(i=>{let s=document.createElement("button");s.className="dtk-legend-item"+(i.shown?"":" off"),s.innerHTML=`<span class="dtk-swatch" style="background:${M(i.color)}"></span><span class="dtk-legend-name">${M(i.det.name)}</span><span class="dtk-legend-id">${M(i.det.id.slice(0,8))}</span><span class="dtk-legend-n">${i.det.anomaly_count}</span>`,s.onclick=()=>{i.shown=!i.shown,s.classList.toggle("off",!i.shown),t()},n.appendChild(s)}),n}function Ee(e,t){let n=document.createElement("div");n.className="dtk-alerts";let i=document.createElement("div");if(i.className="dtk-alerts-head",i.textContent=`Alerts (${e.alerts.length})`,n.appendChild(i),e.alerts.length===0){let r=document.createElement("div");return r.className="dtk-alerts-empty",r.textContent="No alerts fired in this period.",n.appendChild(r),n}let s=document.createElement("div");s.className="dtk-alerts-list";let v=[...e.alerts].sort((r,a)=>a.t-r.t);for(let r of v){let a=document.createElement("button");a.className="dtk-alert-row";let b=G(r.kind),f=r.direction!=="none"?` \xB7 ${M(r.direction)}`:"",h=r.severity>0?` \xB7 sev ${r.severity.toFixed(2)}`:"",d=r.value!==null?` \xB7 value ${W(r.value)}`:"",u=r.onset!==null&&r.kind!=="no_data"?` \xB7 ${ae(Math.max(0,r.t-r.onset))} (${r.consecutive} pts)`:"";a.innerHTML=`<span class="dtk-alert-time">${M(O(r.t))}</span><span class="dtk-badge" style="background:${M(D(b,.18))};color:${M(b)};border-color:${M(D(b,.5))}">${M(Ae(r.kind))}</span><span class="dtk-alert-body"><span class="dtk-alert-rule">${M(r.rule)}</span><span class="dtk-alert-sub">${M(r.detector)}${f}${h}${d}${M(u)}</span></span>`,a.onclick=()=>t(r),s.appendChild(a)}return n.appendChild(s),n}function Le(e,t){e.querySelectorAll(".dtk-preset").forEach(n=>n.classList.remove("active")),t.classList.add("active")}function Re(e,t,n,i,s){let v=e.getContext("2d");if(!v)throw new Error("report: 2D context unavailable");let r=v,a=t.points.map(o=>o.t),b=t.points.map(o=>o.v===null?NaN:o.v),f=t.period.start,h=t.period.end,d=h-f||1,u=Math.min(Me,d),A=0,S=1,z=!1;C();let g=f,m=h,p=1,$=null;function C(){let o=1/0,l=-1/0;for(let y of b)Number.isFinite(y)&&(y<o&&(o=y),y>l&&(l=y));for(let y of n)for(let k of y.band)k.lo!==null&&Number.isFinite(k.lo)&&k.lo<o&&(o=k.lo),k.hi!==null&&Number.isFinite(k.hi)&&k.hi>l&&(l=k.hi);(!Number.isFinite(o)||!Number.isFinite(l))&&(o=0,l=1),z&&(o>0&&(o=0),l<0&&(l=0)),l<=o&&(l=o+1);let x=(l-o)*.06;A=o-x,S=l+x}function R(o){z!==o&&(z=o,C(),V())}function T(){for(let o of n)if(o.shown&&o.effectiveStart!==null)return o.effectiveStart;for(let o of n)if(o.effectiveStart!==null)return o.effectiveStart;return null}function I(){return{tmin:g,tmax:m,vmin:A,vmax:S}}function fe(){return te(e,L,I(),p)}function X(o,l){let x=l-o;if(x<u){let y=(o+l)/2;o=y-u/2,l=y+u/2,x=u}x>=d&&(o=f,l=h),o<f&&(l+=f-o,o=f),l>h&&(o-=l-h,l=h),g=K(o,f,h),m=K(l,f,h),V()}function Z(){g=f,m=h,V()}function pe(o){let l=o.onset!==null?Math.min(o.onset,o.t):o.t,x=o.t,k=Math.max(x-l,u)*1.5+u;X(l-k,x+k)}let j=0;function q(){j===0&&(j=requestAnimationFrame(V))}function V(){if(j=0,e.width===0||e.height===0)return;let o=fe(),l=E("--faint"),x=E("--muted"),y=E("--clay");if(r.fillStyle=E("--term-bg"),r.fillRect(0,0,e.width,e.height),a.length===0)return;if(se(r,e,L,I(),o.px,o.py,g,m,l,x,p),z&&A<=0&&S>=0){let w=o.py(0);r.strokeStyle=D(x,.6),r.lineWidth=1.25*p,r.beginPath(),r.moveTo(L.l*p,w),r.lineTo(e.width-L.r*p,w),r.stroke(),r.fillStyle=x,r.textAlign="right",r.textBaseline="middle",r.fillText("0",(L.l-8)*p,w)}r.save(),r.beginPath(),r.rect(L.l*p,L.t*p,o.plotW(),o.plotH()),r.clip();let k=L.t*p,N=o.plotH();for(let w of t.alerts){if(w.onset===null||w.kind==="no_data")continue;let B=Math.min(w.onset,w.t),U=Math.max(w.onset,w.t);if(U<g||B>m)continue;let ke=G(w.kind),Q=o.px(B),ge=o.px(U);r.fillStyle=D(ke,.08),r.fillRect(Q,k,Math.max(ge-Q,1*p),N)}for(let w of n){if(!w.shown)continue;let B=re(w.band);oe(r,w.band,B,o.px,o.py,w.color,.13,.4,p)}ne(r,a,b,g,m,L.l*p,o.plotW(),o.px,o.py,y,1.5,p);for(let w of n)w.shown&&ie(r,w.anomalies,g,m,o.px,o.py,E("--st-anomaly"),p);let P=T();P!==null&&P>g&&ce(r,e,L,p,o.px,P,"detection at full power \u2192");let H=t.alerts.map(w=>({t:w.t,kind:w.kind}));de(r,e,L,p,o.px,H,w=>G(w)),$!==null&&he(o,k,N,l),r.restore()}function be(o){let l=a;if(l.length===0)return-1;let x=0,y=l.length-1;for(;x<y;){let k=x+y>>1;l[k]<o?x=k+1:y=k}return x>0&&o-l[x-1]<l[x]-o&&(x-=1),x}function he(o,l,x,y){let k=be($);if(k<0)return;let N=a[k];if(N<g||N>m){s("hover the chart for a point readout");return}let P=o.px(N);r.strokeStyle=D(y,.45),r.lineWidth=1*p,r.setLineDash([2*p,2*p]),r.beginPath(),r.moveTo(P,l),r.lineTo(P,l+x),r.stroke(),r.setLineDash([]);let H=b[k];if(Number.isFinite(H)){let w=o.py(H);r.fillStyle=E("--term-bg"),r.beginPath(),r.arc(P,w,4*p,0,Math.PI*2),r.fill(),r.strokeStyle=E("--clay"),r.lineWidth=2*p,r.beginPath(),r.arc(P,w,4*p,0,Math.PI*2),r.stroke()}ve(k)}function ve(o){let l=a[o],x=b[o],y=`<span class="dtk-ro-t">${M(O(l))}</span>`;y+=`<span class="dtk-ro-v">value ${Number.isFinite(x)?W(x):"\u2014"}</span>`;for(let k of n){if(!k.shown)continue;let N=k.band[o],P=k.det.points[o];if(N&&N.lo!==null&&N.hi!==null){let H=P&&P.a===1,w=H&&P.sev!==null?` sev ${P.sev.toFixed(2)}`:"",B=H?`<span class="dtk-ro-anom" style="color:${M(E("--st-anomaly"))}">anomaly${M(w)}</span>`:'<span class="dtk-ro-ok">ok</span>';y+=`<span class="dtk-ro-det"><span class="dtk-swatch" style="background:${M(k.color)}"></span>${M(k.det.name)}: [${W(N.lo)}, ${W(N.hi)}] ${B}</span>`}}s(y)}function J(o){let l=e.getBoundingClientRect(),x=(o-l.left-L.l)/(l.width-(L.l+L.r)||1);return g+K(x,0,1)*(m-g)}e.addEventListener("wheel",o=>{o.preventDefault();let l=J(o.clientX),x=m-g,y=K(x*Math.pow(1.0015,o.deltaY),u,d),k=(l-g)/(x||1);X(l-k*y,l-k*y+y)},{passive:!1});let _=null;e.addEventListener("mousedown",o=>{_={x:o.clientX,vMin:g,vMax:m},e.style.cursor="grabbing"}),window.addEventListener("mousemove",o=>{if(!_)return;let l=e.getBoundingClientRect(),x=(_.vMax-_.vMin)/(l.width-(L.l+L.r)||1),y=(o.clientX-_.x)*x;X(_.vMin-y,_.vMax-y)}),window.addEventListener("mouseup",()=>{_&&(_=null,e.style.cursor="crosshair")}),e.addEventListener("mousemove",o=>{_||($=J(o.clientX),q())}),e.addEventListener("mouseleave",()=>{$!==null&&($=null,s("hover the chart for a point readout"),q())}),e.addEventListener("dblclick",()=>Z()),e.style.cursor="crosshair";function xe(){p=ee(e),V()}return{repaint:()=>q(),resize:xe,setView:X,resetView:Z,focusAlert:pe,setZeroLine:R}}var me=!1;function Pe(){if(me)return;me=!0;let e=`
|
|
2
2
|
.${c}{--term-bg:#211e1a;--term-border:#332f29;--term-text:#c9c2b4;
|
|
3
3
|
--clay:#d15b36;--clay-700:#b4471f;--ink:#1b1916;--paper:#f5f1e8;--surface:#fbf9f3;
|
|
4
4
|
--border:#e6e0d4;--muted:#6e675b;--faint:#9a9384;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";(()=>{var jo={"--term-bg":"#211e1a","--clay":"#d15b36","--st-anomaly":"#d63232","--st-recovery":"#36a64f","--st-nodata":"#f0ad4e","--st-error":"#5a7a8c","--faint":"#9a9384","--muted":"#6e675b","--border":"#332f29","--term-border":"#332f29"};function G(n){return getComputedStyle(document.documentElement).getPropertyValue(n).trim()||jo[n]||"#888"}function Vo(n){let l=n.replace("#","").trim();l.length===3&&(l=l[0]+l[0]+l[1]+l[1]+l[2]+l[2]);let g=parseInt(l,16);return l.length!==6||Number.isNaN(g)?[209,91,54]:[g>>16&255,g>>8&255,g&255]}function B(n,l){let[g,t,x]=Vo(n);return`rgba(${g},${t},${x},${l})`}function To(n){let l=Math.max(1,window.devicePixelRatio||1),g=n.clientWidth||n.offsetWidth||0,t=n.clientHeight||n.offsetHeight||0;return n.width=Math.round(g*l),n.height=Math.round(t*l),l}var Xn=Number.isFinite;function Jn(n,l,g,t,x,y,R,b,k,F,H,q){let _e=l.length,le=Math.max(1,Math.round(R)),Z=x-t||1,Je=0;for(let re=0;re<_e;re++){let d=g[re];!Xn(d)||l[re]<t||l[re]>x||Je++}if(n.strokeStyle=F,n.lineWidth=H*q,n.lineJoin="round",n.beginPath(),Je<=le){let re=!1;for(let d=0;d<_e;d++){let w=g[d],L=l[d];if(!Xn(w)||L<t||L>x){re=!1;continue}let Q=b(L),he=k(w);re?n.lineTo(Q,he):(n.moveTo(Q,he),re=!0)}}else{let re=new Array(le).fill(null),d=new Array(le).fill(null);for(let L=0;L<_e;L++){let Q=g[L],he=l[L];if(!Xn(Q)||he<t||he>x)continue;let $=Math.floor((he-t)/Z*(le-1));$=$<0?0:$>le-1?le-1:$,(re[$]===null||Q<re[$])&&(re[$]=Q),(d[$]===null||Q>d[$])&&(d[$]=Q)}let w=!1;for(let L=0;L<le;L++){if(d[L]===null){w=!1;continue}let Q=y+L,he=k(d[L]),$=k(re[L]);w?n.lineTo(Q,he):(n.moveTo(Q,he),w=!0),n.lineTo(Q,$)}}n.stroke()}function Ao(n){let l=Math.abs(n);return l>=1e3?n.toFixed(0):l>=10?n.toFixed(1):l>=1?n.toFixed(2):n.toFixed(3)}function _o(n,l){let g=new Date(n).toISOString();return l<2*864e5?g.slice(5,16).replace("T"," "):g.slice(5,10)}function Zn(n,l,g){return{left:l.l*g,top:l.t*g,right:n.width-l.r*g,bottom:n.height-l.b*g}}function Do(n,l,g,t,x,y,R){let b=Zn(l,g,t),k=Math.max(b.left,Math.min(x(y),b.right));k<=b.left+.5||(n.save(),n.fillStyle="rgba(17,15,13,0.42)",n.fillRect(b.left,b.top,k-b.left,b.bottom-b.top),n.strokeStyle=B(G("--faint"),.7),n.lineWidth=1*t,n.setLineDash([4*t,4*t]),n.beginPath(),n.moveTo(k,b.top),n.lineTo(k,b.bottom),n.stroke(),n.setLineDash([]),n.fillStyle=B(G("--faint"),.95),n.font=`${10*t}px ui-monospace, monospace`,n.textAlign="left",n.textBaseline="top",n.fillText(R,k+6*t,b.top+5*t),n.restore())}function Lo(n,l,g,t,x){let y=Zn(l,g,t);if(y.right<=y.left||y.bottom<=y.top)return;n.save(),n.fillStyle="rgba(17,15,13,0.42)",n.fillRect(y.left,y.top,y.right-y.left,y.bottom-y.top),n.fillStyle=B(G("--faint"),.95),n.font=`${10*t}px ui-monospace, monospace`,n.textAlign="center",n.textBaseline="middle";let R=y.right-y.left-12*t;R>0&&n.fillText(x,(y.left+y.right)/2,y.top+(y.bottom-y.top)*.3,R),n.restore()}function $o(n,l,g,t,x,y,R){let b=Zn(l,g,t),k=5*t;n.save();for(let F=0;F<y.length;F++){let H=y[F],q=x(H.t);if(q<b.left-1||q>b.right+1)continue;let _e=R(H.kind);n.strokeStyle=B(_e,.45),n.lineWidth=1*t,n.beginPath(),n.moveTo(q,b.top),n.lineTo(q,b.bottom),n.stroke(),n.fillStyle=_e,n.beginPath(),n.moveTo(q-k,b.top),n.lineTo(q+k,b.top),n.lineTo(q,b.top+k*1.4),n.closePath(),n.fill()}n.restore()}var qo={mad:1,zscore:2,iqr:4};function Uo(n,l){let g=n.seasonalityData;if(!g||g.length===0)return 0;let t=0;for(let x of l){let y=new Set;for(let R of g)y.add(x.map(b=>{var k;return String((k=R==null?void 0:R[b])!=null?k:"")}).join("|"));t=Math.max(t,y.size)}return t}function Go(n,l){var x;if(l.type==="manual_bounds")return l.inputType!=="values"?1:0;if(l.type==="autoreg"){let y=Math.max(1,Math.round((x=l.lags)!=null?x:5)),R=l.windowSize+y;return l.inputType!=="values"&&(R+=1),l.stabilization==="clamp"&&(R+=l.windowSize),R}let g=Math.max(l.minSamples,qo[l.type]);if(l.smoothing==="sma"&&(g=Math.max(g,l.smoothingWindow-1)),l.smoothing==="ema"&&(g=Math.max(g,Math.ceil(5/l.smoothingAlpha))),l.inputType!=="values"&&(g=Math.max(g,1)),l.seasonalityComponents!==null&&l.seasonalityComponents.length>0&&Array.isArray(n.seasonalityData)&&n.seasonalityData.length>0){let y=Uo(n,l.seasonalityComponents);if(y>0){let R=l.minSamplesPerGroup*y;l.windowSize>=R&&(g=Math.max(g,R))}}return l.stabilization==="clamp"&&(g+=l.windowSize),g}function zn(n,l){return Math.min(Go(n,l),n.timestamps.length)}var D={l:52,r:14,t:14,b:26},Qn=46,Yo=10,Ko=13,Ae=Number.isFinite,Lt=1e3,$t=60*Lt,Pt=60*$t,xe=24*Pt,eo=[Lt,2*Lt,5*Lt,10*Lt,15*Lt,30*Lt,$t,2*$t,5*$t,10*$t,15*$t,30*$t,Pt,2*Pt,3*Pt,6*Pt,12*Pt,xe,2*xe,3*xe,5*xe,7*xe,14*xe];function Po(n,l,g){let t=Math.max(l-n,1);if(t>g*14*xe){let b=[1,2,3,6,12,24,36,60,120,240],k=b[b.length-1];for(let Z of b)if(t/(Z*30.44*xe)<=g){k=Z;break}let F=new Date(n),H=F.getUTCFullYear(),q=F.getUTCMonth();if(k>=12){let Z=k/12;H=Math.floor(H/Z)*Z,q=0}else q=Math.floor(q/k)*k;let _e=[],le=Date.UTC(H,q,1);for(;le<=l;)le>=n&&_e.push(le),q+=k,H+=Math.floor(q/12),q%=12,le=Date.UTC(H,q,1);return{ticks:_e,step:k*30*xe}}let x=eo[eo.length-1];for(let b of eo)if(t/b<=g){x=b;break}let y;if(x%xe===0){let b=new Date(n);y=Date.UTC(b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate());let k=x/xe,F=Math.round(y/xe);y+=(k-F%k)%k*xe}else y=Math.ceil(n/x)*x;let R=[];for(let b=y;b<=l;b+=x)b>=n&&R.push(b);return{ticks:R,step:x}}function to(n,l){let g=new Date(n).toISOString();return l>=320*xe?g.slice(0,4):l>=26*xe?g.slice(0,7):l>=xe?g.slice(5,10):l>=Pt?g.slice(5,16).replace("T"," "):g.slice(5,19).replace("T"," ")}function No(n,l={}){var jt;let g=n.getContext("2d");if(!g)throw new Error("chart: 2D context unavailable");let t=g,x=!!l.navigable,y=x&&l.showNavigator!==!1,R=l.yFit==="data",b=!!l.labeling,k=(jt=l.mode)!=null?jt:"tune",F=()=>b?k:"tune",H=()=>F()!=="label",q=()=>F()==="review"?.3:1,_e=()=>F()==="tune"?1:F()==="review"?.3:.85,le=()=>b&&F()==="label",Z=()=>F()==="label"?1:.55,Je=()=>F()==="tune",re=()=>b&&(F()==="tune"||F()==="review"),d=1,w=null,L=-1,Q=0,he=!!l.showZeroLine,$=[],te=null,W=-1,O=null,ke=!1,pe=!1,bt="above",Nt=0,ze=null,ie=null,Me=null,Se=null,be=null,ge=!1,j=null,U=0,Y=1,De=0,Ge=1,ne=0,de=1,Xt="";function hn(){d=To(n)}let lt=()=>y?(Yo+Qn)*d:0,Ze=()=>n.width-(D.l+D.r)*d,ve=()=>n.height-(D.t+D.b)*d-lt(),Qe=()=>x?ne:U,zt=()=>x?de:Y,Ie=()=>zt()-Qe()||1,Oe=()=>Y-U||1,I=o=>D.l*d+(o-Qe())/Ie()*Ze(),ye=o=>n.height-D.b*d-lt()-(o-De)/(Ge-De||1)*ve(),gt=()=>n.height-Qn*d,Le=()=>(Qn-Ko)*d,dt=()=>n.width-(D.l+D.r)*d,ce=o=>D.l*d+(o-U)/Oe()*dt(),Jt=o=>gt()+Le()-(o-De)/(Ge-De||1)*Le(),ct=(o,r,c)=>{let s=new Array(o.length).fill(!1);if(o.length===0)return s;let a=t.measureText(to(o[0],c)).width+16*d,m=-1/0;for(let f=0;f<o.length;f++){let S=r(o[f]);S-m>=a&&(s[f]=!0,m=S)}return s},ee=(o,r,c)=>Math.max(r,Math.min(c,o)),et=()=>{let o=w==null?void 0:w.series,r=o?o.timestamps.length:0,c=r>1?Oe()/(r-1):1e3;return Math.max(c*8,1e3)};function He(o,r){var a;let c=r-o,s=et();if(c<s){let m=(o+r)/2;o=m-s/2,r=m+s/2,c=s}c>=Oe()&&(o=U,r=Y),o<U&&(r+=U-o,o=U),r>Y&&(o-=r-Y,r=Y),ne=ee(o,U,Y),de=ee(r,U,Y),ke||(a=l.onViewChange)==null||a.call(l,ne,de),pe&&fe(),V()}function Et(o,r){x&&(ke=!0,He(o,r),ke=!1)}let tt=o=>{let r=(o-D.l*d)/(Ze()||1);return Qe()+ee(r,0,1)*Ie()},vt=o=>{let r=(o-D.l*d)/(dt()||1);return U+ee(r,0,1)*Oe()},se=o=>{let r=n.height-D.b*d-lt(),c=ee((r-o)/(ve()||1),0,1);return De+c*(Ge-De)};function Rt(o){let r=w==null?void 0:w.series;if(!r||r.timestamps.length===0)return-1;let c=(o-D.l*d)/(Ze()||1),s=Qe()+c*Ie(),a=r.timestamps,m=0,f=a.length-1;for(;m<f;){let S=m+f>>1;a[S]<s?m=S+1:f=S}return m>0&&s-a[m-1]<a[m]-s&&(m-=1),m}function Be(o,r){let c=o.timestamps;if(U=c[0],Y=c[c.length-1],x){let f=`${c.length}:${U}:${Y}`;f!==Xt?(Xt=f,ne=U,de=Y):(ne=ee(ne,U,Y),de=ee(Math.max(de,ne+et()),U,Y))}let s=1/0,a=-1/0;for(let f of o.values)Ae(f)&&(f<s&&(s=f),f>a&&(a=f));if(!R)for(let f of r)f.scored&&(Ae(f.lower)&&f.lower<s&&(s=f.lower),Ae(f.upper)&&f.upper>a&&(a=f.upper));(!Ae(s)||!Ae(a))&&(s=0,a=1),he&&(s>0&&(s=0),a<0&&(a=0)),a<=s&&(a=s+1);let m=(a-s)*.06;De=s-m,Ge=a+m}function Fe(o,r,c){let s=w.series.timestamps;Jn(t,s,o,Qe(),zt(),D.l*d,Ze(),I,ye,r,c,d)}function ut(o,r){let c=[],s=-1;for(let a=Math.max(0,r);a<o.length;a++){let m=o[a];m.scored&&Ae(m.lower)&&Ae(m.upper)?s===-1&&(s=a):s!==-1&&(c.push([s,a-1]),s=-1)}return s!==-1&&c.push([s,o.length-1]),c}function yt(){if(Q=0,!w||n.width===0||n.height===0)return;let{series:o,scored:r,params:c,alerts:s}=w;if(o.timestamps.length===0){t.fillStyle=G("--term-bg"),t.fillRect(0,0,n.width,n.height);return}let a=G("--clay"),m=G("--st-anomaly"),f=G("--faint"),S=G("--muted");t.fillStyle=G("--term-bg"),t.fillRect(0,0,n.width,n.height),t.font=`${11*d}px ui-monospace, 'JetBrains Mono', monospace`,t.textBaseline="middle";for(let C=0;C<=4;C++){let z=De+(Ge-De)*C/4,N=ye(z);t.strokeStyle=B(f,.1),t.lineWidth=1*d,t.beginPath(),t.moveTo(D.l*d,N),t.lineTo(n.width-D.r*d,N),t.stroke(),t.fillStyle=S,t.textAlign="right",t.fillText(Ao(z),(D.l-8)*d,N)}if(he&&De<=0&&Ge>=0){let C=ye(0);t.strokeStyle=B(S,.6),t.lineWidth=1.25*d,t.beginPath(),t.moveTo(D.l*d,C),t.lineTo(n.width-D.r*d,C),t.stroke(),t.fillStyle=S,t.textAlign="right",t.fillText("0",(D.l-8)*d,C)}t.textBaseline="top";let P=n.height-D.b*d-lt();if(x){let C=Po(Qe(),zt(),7),z=ct(C.ticks,I,C.step);for(let N=0;N<C.ticks.length;N++){let ae=C.ticks[N],Ne=I(ae);t.strokeStyle=B(f,.1),t.lineWidth=1*d,t.beginPath(),t.moveTo(Ne,D.t*d),t.lineTo(Ne,P),t.stroke(),z[N]&&(t.fillStyle=S,t.textAlign=Ne<(D.l+24)*d?"left":Ne>n.width-(D.r+24)*d?"right":"center",t.fillText(to(ae,C.step),Ne,P+7*d))}}else{let C=Ie();for(let z=0;z<=5;z++){let N=U+C*z/5,ae=I(N);t.fillStyle=S,t.textAlign=z===0?"left":z===5?"right":"center",t.fillText(_o(N,C),ae,P+7*d)}}t.save(),t.beginPath(),t.rect(D.l*d,D.t*d,Ze(),ve()),t.clip(),gn(),Wn();let A=r.length,_=Math.min(zn(o,c),A),E=_<A?o.timestamps[_]:void 0,J=q(),Te=H()?ut(r,_):[];t.fillStyle=B(a,.13*J);for(let[C,z]of Te){t.beginPath(),t.moveTo(I(r[C].timestamp),ye(r[C].upper));for(let N=C+1;N<=z;N++)t.lineTo(I(r[N].timestamp),ye(r[N].upper));for(let N=z;N>=C;N--)t.lineTo(I(r[N].timestamp),ye(r[N].lower));t.closePath(),t.fill()}t.strokeStyle=B(a,.4*J),t.lineWidth=1*d;for(let[C,z]of Te)for(let N of["upper","lower"]){t.beginPath();for(let ae=C;ae<=z;ae++){let Ne=I(r[ae].timestamp),_t=ye(r[ae][N]);ae===C?t.moveTo(Ne,_t):t.lineTo(Ne,_t)}t.stroke()}t.strokeStyle=B(f,.55*J),t.lineWidth=1*d,t.setLineDash([3*d,3*d]);for(let[C,z]of Te){t.beginPath();for(let N=C;N<=z;N++){let ae=r[N].center;if(!Ae(ae))continue;let Ne=I(r[N].timestamp),_t=ye(ae);N===C?t.moveTo(Ne,_t):t.lineTo(Ne,_t)}t.stroke()}if(t.setLineDash([]),c.smoothing!=="none"&&(!b||F()==="tune")){Fe(o.values,B(a,.28),1.25);let C=r.map(z=>z.processedValue);Fe(C,a,1.6)}else Fe(o.values,a,1.5);let K=_e();for(let C=_;C<A;C++){let z=r[C];if(!z.scored||!Ae(z.value))continue;let N=I(z.timestamp),ae=ye(z.value);z.isAnomaly?(t.fillStyle=B(m,.18*K),t.beginPath(),t.arc(N,ae,6*d,0,Math.PI*2),t.fill(),t.fillStyle=B(m,K),t.beginPath(),t.arc(N,ae,3*d,0,Math.PI*2),t.fill()):o.truthAnomaly[C]&&(t.strokeStyle=B(S,.7),t.lineWidth=1.25*d,t.beginPath(),t.arc(N,ae,3.5*d,0,Math.PI*2),t.stroke())}(!b||F()==="tune")&&(A>0&&_>=A?Lo(t,n,D,d,"all shown points are detector warm-up \u2014 nothing to score yet"):E!==void 0&&Do(t,n,D,d,I,E,"detection at full power \u2192")),L>=0&&L<r.length&&Je()&&bn(L,c.windowSize,r,o,f),s&&s.length&&$o(t,n,D,d,I,s,C=>C==="anomaly"?G("--st-anomaly"):C==="anomaly-validated"?G("--st-recovery"):C==="anomaly-false"?G("--st-error"):C==="recovery"?G("--st-recovery"):G("--st-nodata")),en(),kt(r,_,A),t.restore(),y&&mt(o,s,a,f,S)}function mt(o,r,c,s,a){let m=gt(),f=m+Le(),S=D.l*d,P=n.width-D.r*d,A=Po(U,Y,5);t.save(),t.beginPath(),t.rect(S,m,dt(),Le()),t.clip(),t.strokeStyle=B(s,.1),t.lineWidth=1*d;for(let K of A.ticks){let C=ce(K);t.beginPath(),t.moveTo(C,m),t.lineTo(C,f),t.stroke()}Jn(t,o.timestamps,o.values,U,Y,S,dt(),ce,Jt,B(c,.7),1.1,d),t.restore();let _=ce(ne),E=ce(de);if(t.fillStyle="rgba(27,25,22,0.55)",t.fillRect(S,m,_-S,Le()),t.fillRect(E,m,P-E,Le()),$.length){let K=G("--st-anomaly");t.fillStyle=B(K,.28);for(let C of $){let z=ce(C.start),N=Math.max(ce(C.end)-z,2*d);t.fillRect(z,m,N,Le())}}if(r&&r.length){let K=G("--st-anomaly");t.fillStyle=B(K,.85);for(let C of r){let z=ce(C.t);t.fillRect(z-1*d,m,2*d,Le())}}t.fillStyle="rgba(245,241,232,0.06)",t.fillRect(_,m,E-_,Le()),t.strokeStyle=c,t.lineWidth=1.5*d,t.strokeRect(_,m+1,E-_,Le()-2),t.fillStyle=c;let J=m+Le()/2-8*d;t.fillRect(_-2*d,J,4*d,16*d),t.fillRect(E-2*d,J,4*d,16*d),t.font=`${10*d}px ui-monospace, monospace`,t.textBaseline="top",t.fillStyle=a;let Te=ct(A.ticks,ce,A.step);for(let K=0;K<A.ticks.length;K++){let C=A.ticks[K],z=ce(C);t.strokeStyle=B(s,.25),t.lineWidth=1*d,t.beginPath(),t.moveTo(z,f),t.lineTo(z,f+3*d),t.stroke(),Te[K]&&(t.textAlign=z<(D.l+26)*d?"left":z>n.width-(D.r+26)*d?"right":"center",t.fillText(to(C,A.step),z,f+5*d))}}function bn(o,r,c,s,a){let m=s.timestamps,f=Math.max(0,o-r),S=o-1,P=D.t*d,A=ve();if(S>=f){let J=s.intervalSeconds*1e3/Ie()*Ze()*.5,Te=I(m[f])-J,K=I(m[S])+J;t.fillStyle="rgba(255,255,255,0.05)",t.fillRect(Te,P,K-Te,A),t.strokeStyle=B(a,.5),t.lineWidth=1*d,t.beginPath(),t.moveTo(Te,P),t.lineTo(Te,P+A),t.moveTo(K,P),t.lineTo(K,P+A),t.stroke()}let _=I(m[o]);t.strokeStyle=B(a,.85),t.lineWidth=1*d,t.setLineDash([2*d,2*d]),t.beginPath(),t.moveTo(_,P),t.lineTo(_,P+A),t.stroke(),t.setLineDash([]);let E=c[o];if(E.scored&&Ae(E.lower)&&Ae(E.upper)){let J=5*d;t.strokeStyle=B(G("--clay"),.85),t.lineWidth=1.5*d;for(let Te of[E.lower,E.upper]){let K=ye(Te);t.beginPath(),t.moveTo(_-J,K),t.lineTo(_+J,K),t.stroke()}}if(Ae(E.value)){let J=ye(E.value);t.fillStyle=G("--term-bg"),t.beginPath(),t.arc(_,J,4*d,0,Math.PI*2),t.fill(),t.strokeStyle=E.isAnomaly?G("--st-anomaly"):G("--clay"),t.lineWidth=2*d,t.beginPath(),t.arc(_,J,4*d,0,Math.PI*2),t.stroke()}}function Zt(o,r,c,s,a){t.beginPath(),t.moveTo(o+a,r),t.arcTo(o+c,r,o+c,r+s,a),t.arcTo(o+c,r+s,o,r+s,a),t.arcTo(o,r+s,o,r,a),t.arcTo(o,r,o+c,r,a),t.closePath()}function Qt(o,r){let c=G("--st-anomaly"),s=14*d,a=3*d,m=o-s-a,f=D.t*d+a;t.fillStyle=r?B(c,.95):"rgba(27,25,22,0.82)",t.strokeStyle=B(c,.9),t.lineWidth=1*d,Zt(m,f,s,s,3*d),t.fill(),t.stroke(),t.strokeStyle=r?"#fff":c,t.lineWidth=1.5*d;let S=4*d;t.beginPath(),t.moveTo(m+S,f+S),t.lineTo(m+s-S,f+s-S),t.moveTo(m+s-S,f+S),t.lineTo(m+S,f+s-S),t.stroke()}let wt=()=>ze!=null?ze:ie,$e=()=>{let o=be||Me;return o?[Math.min(o.a,o.b),Math.max(o.a,o.b)]:[Qe(),zt()]};function nt(){let o=[],r=wt();if(r==null||!w)return o;let c=w.series.timestamps,s=w.series.values,[a,m]=$e(),f=-1,S=-1,P=0;for(let A=0;A<c.length;A++){if(c[A]<a||c[A]>m)continue;let _=s[A];Ae(_)&&(bt==="above"?_>r:_<r)?(f===-1&&(f=c[A]),S=c[A],P=0):f!==-1&&(P++,P>Nt&&(o.push([f,S]),f=-1,P=0))}return f!==-1&&o.push([f,S]),o}function fe(){if(!l.onThresholdChange)return;let[o,r]=$e(),c=be||Me;l.onThresholdChange({value:wt(),locked:ze!=null,runs:nt().length,window:c?{start:Math.min(c.a,c.b),end:Math.max(c.a,c.b)}:null,committed:Me!=null,windowMs:r-o})}function ot(o,r){let c=null;for(let s=$.length-1;s>=0;s--){let a=$[s];o<=a.end&&r>=a.start&&(c===null?(a.start=Math.min(a.start,o),a.end=Math.max(a.end,r),c=a):(c.start=Math.min(c.start,a.start),c.end=Math.max(c.end,a.end),te===a&&(te=c),$.splice(s,1)))}c===null&&$.push({start:o,end:r,label:""})}function gn(){if(!pe||wt()==null)return;let r=D.t*d,c=ve(),s=G("--st-nodata");for(let[a,m]of nt()){let f=I(a),S=Math.max(I(m)-f,2*d);t.fillStyle=B(s,.22),t.fillRect(f,r,S,c),t.strokeStyle=B(s,.6),t.lineWidth=1*d,t.strokeRect(f,r,S,c)}}function en(){if(!pe)return;let o=D.t*d,r=n.height-D.b*d-lt(),c=D.l*d,s=n.width-D.r*d,a=G("--st-nodata"),[m,f]=$e(),S=!!(be||Me),P=ee(I(m),c,s),A=ee(I(f),c,s);S&&(t.fillStyle=B(G("--ink"),.5),t.fillRect(c,o,Math.max(0,P-c),ve()),t.fillRect(A,o,Math.max(0,s-A),ve()),t.strokeStyle=B(a,.7),t.lineWidth=1*d,t.setLineDash([3*d,3*d]),t.beginPath(),t.moveTo(P,o),t.lineTo(P,r),t.moveTo(A,o),t.lineTo(A,r),t.stroke(),t.setLineDash([]));let _=wt();if(_!=null&&_>=De&&_<=Ge){let E=ye(_);t.strokeStyle=a,t.lineWidth=1.5*d,t.setLineDash([6*d,4*d]),t.beginPath(),t.moveTo(P,E),t.lineTo(A,E),t.stroke(),t.setLineDash([])}}let En=()=>w?w.series.intervalSeconds*1e3:1e3;function vn(o,r,c){let s=!1;for(let a=0,m=c.length-1;a<c.length;m=a++){let f=c[a].x,S=c[a].y,P=c[m].x,A=c[m].y;S>r!=A>r&&o<(P-f)*(r-S)/(A-S||1e-9)+f&&(s=!s)}return s}function je(o,r,c){let s=[];if(!j||j.length<3)return s;for(let a=Math.max(0,r);a<c;a++){let m=o[a];!m.scored||!m.isAnomaly||!Ae(m.value)||vn(I(m.timestamp),ye(m.value),j)&&s.push(a)}return s}function Wt(o,r){var A;if(!r.length)return[];let c=En(),a=(Math.max(1,(A=w==null?void 0:w.params.consecutiveAnomalies)!=null?A:1)+1)*c,m=c/2,f=[],S=r[0],P=r[0];for(let _=1;_<r.length;_++){let E=r[_];o[E].timestamp-o[P].timestamp>a&&(f.push([o[S].timestamp-m,o[P].timestamp+m]),S=E),P=E}return f.push([o[S].timestamp-m,o[P].timestamp+m]),f}function xt(o,r,c){if(!l.onLassoChange)return;let s=ge&&j?je(o,r,c):[];l.onLassoChange({active:!!j,anomalies:s.length,incidents:Wt(o,s).length})}function Rn(){if(!w||!j)return;let{scored:o}=w,r=o.length,c=Math.min(zn(w.series,w.params),r),s=Wt(o,je(o,c,r));for(let[a,m]of s)ot(a,m);s.length&&Ye()}function kt(o,r,c){if(!ge||!j||j.length<2)return;let s=G("--clay"),a=G("--st-anomaly");t.beginPath(),t.moveTo(j[0].x,j[0].y);for(let m=1;m<j.length;m++)t.lineTo(j[m].x,j[m].y);t.closePath(),t.fillStyle=B(s,.08),t.fill(),t.strokeStyle=B(s,.9),t.lineWidth=1.5*d,t.setLineDash([5*d,4*d]),t.stroke(),t.setLineDash([]);for(let m of je(o,r,c)){let f=I(o[m].timestamp),S=ye(o[m].value);t.strokeStyle=a,t.lineWidth=2*d,t.beginPath(),t.arc(f,S,7*d,0,Math.PI*2),t.stroke()}}function Wn(){let o=D.t*d,r=ve(),c=G("--st-anomaly"),s=D.l*d,a=n.width-D.r*d,m=le(),f=Z();for(let S=0;S<$.length;S++){let P=$[S],A=I(P.start),_=I(P.end);if(_<s-1||A>a+1)continue;let E=Math.max(_-A,2*d),J=m&&P===te;t.fillStyle=B(c,(J?.3:.16)*f),t.fillRect(A,o,E,r),t.strokeStyle=B(c,(J?.95:.5)*f),t.lineWidth=(J?2:1)*d,t.strokeRect(A,o,E,r),m&&(t.fillStyle=B(c,.95),t.fillRect(A-1.5*d,o,3*d,r),t.fillRect(_-1.5*d,o,3*d,r),(_-A>=22*d||J)&&Qt(_,J||S===W))}if(m&&O&&O.mode==="new"){let S=I(O.a),P=I(O.b);t.fillStyle=B(G("--st-nodata"),.28),t.fillRect(Math.min(S,P),o,Math.abs(P-S),r)}}function tn(o,r){if(!w||!w.alerts||!w.alerts.length||r>(D.t+22)*d)return-1;let c=-1,s=9*d;for(let a=0;a<w.alerts.length;a++){let m=Math.abs(o-I(w.alerts[a].t));m<=s&&(s=m,c=a)}return c}function nn(o){var s;if(!w||!w.alerts)return;let r=w.alerts[o].kind,c=r==="anomaly"?"valid":r==="anomaly-validated"?"false":"unreviewed";(s=l.onAlertReviewChange)==null||s.call(l,w.alerts[o].t,c)}function Ye(o){var r;(r=l.onIncidentsChange)==null||r.call(l,$,o)}function pt(o){let r=$.indexOf(o);r<0||($.splice(r,1),te===o&&(te=null),W=-1,Ye(o),V())}function ft(o,r){let c=D.t*d,s=6*d;for(let a=0;a<$.length;a++){let m=I($[a].end);if(m-I($[a].start)>=22*d||$[a]===te){let f=14*d,S=3*d,P=m-f-S,A=c+S;if(o>=P&&o<=P+f&&r>=A&&r<=A+f)return{i:a,edge:"del"}}}for(let a=0;a<$.length;a++){let m=I($[a].start),f=I($[a].end);if(Math.abs(o-m)<=s)return{i:a,edge:"a"};if(Math.abs(o-f)<=s)return{i:a,edge:"b"}}for(let a=0;a<$.length;a++){let m=I($[a].start),f=I($[a].end);if(o>m+s&&o<f-s)return{i:a,edge:"move"}}return null}let yn=()=>{let o=w==null?void 0:w.series,r=o?o.timestamps.length:0;return r>1?Math.max(Oe()/(r-1),1):1e3};function V(){Q===0&&(Q=requestAnimationFrame(yt))}function X(){if(!w)return;let o=w.scored.length,r=Math.min(zn(w.series,w.params),o);xt(w.scored,r,o)}function Pe(){var c;if(!l.onHover||!w)return;if(L<0){l.onHover(null);return}let o=(c=w.scored[L])!=null?c:null,r={index:L,point:o,windowStart:Math.max(0,L-w.params.windowSize),windowEnd:L-1};l.onHover(r)}let Ce=null,ue=null,Ve=o=>{let r=n.getBoundingClientRect();return{x:(o.clientX-r.left)*d,y:(o.clientY-r.top)*d}},Mt=o=>y&&o>=gt(),Ee=o=>{let r=ce(ne),c=ce(de),s=8*d;return Math.abs(o-r)<=s?"l":Math.abs(o-c)<=s?"r":o>r&&o<c?"move":"out"};function St(o){if(!w)return;let{x:r,y:c}=Ve(o);if(Ce||ue||O)return;if(Mt(c)){L!==-1&&(L=-1,Pe(),V());let m=Ee(r);n.style.cursor=m==="l"||m==="r"?"ew-resize":m==="move"?"grab":"pointer";return}let s=F();if(b&&s==="label"&&ge){L!==-1&&(L=-1,Pe()),n.style.cursor="crosshair";return}if(b&&s==="label"&&pe){L!==-1&&(L=-1,Pe()),ze==null&&!Se&&(ie=se(c),fe(),V()),n.style.cursor="crosshair";return}if(b&&s==="label"){L!==-1&&(L=-1,Pe());let m=ft(r,c),f=m&&m.edge==="del"?m.i:-1;f!==W&&(W=f,V()),n.style.cursor=m?m.edge==="del"?"pointer":m.edge==="move"?"grab":"ew-resize":"crosshair";return}if(b&&s==="review"){L!==-1&&(L=-1,Pe()),n.style.cursor=tn(r,c)>=0?"pointer":"grab";return}x&&(n.style.cursor=b&&tn(r,c)>=0?"pointer":"grab");let a=Rt(r);a!==L&&(L=a,Pe(),V())}function Ke(){L!==-1&&(L=-1,Pe(),V())}function Xe(o){if(!x||!w)return;let{x:r,y:c}=Ve(o);if(y&&c>=gt()){let s=Ee(r);if(s==="l")ue={type:"l",grab:0,vMin:ne,vMax:de};else if(s==="r")ue={type:"r",grab:0,vMin:ne,vMax:de};else if(s==="move")ue={type:"move",grab:vt(r),vMin:ne,vMax:de};else{let a=vt(r),m=Ie();He(a-m/2,a+m/2),ue={type:"move",grab:a,vMin:ne,vMax:de}}n.style.cursor="grabbing",o.preventDefault();return}if(c>D.t*d&&c<n.height-D.b*d-lt()){let s=F();if(re()){let a=tn(r,c);if(a>=0){nn(a),o.preventDefault();return}}if(b&&s==="label"&&ge){j=[{x:r,y:c}],X(),n.style.cursor="crosshair",V(),o.preventDefault();return}if(b&&s==="label"&&pe){Se={x:r,ts:tt(r)},ze==null&&(ie=se(c)),fe(),V(),o.preventDefault();return}if(b&&s==="label"){let a=tt(r),m=ft(r,c);if(m&&m.edge==="del"){pt($[m.i]),o.preventDefault();return}if(m&&m.edge==="move"){let f=$[m.i];te=f,O={mode:"move",iv:f,grab:a,a0:f.start,b0:f.end,moved:!1}}else if(m){let f=$[m.i];te=f,O={mode:"edge",iv:f,edge:m.edge,moved:!1}}else te=null,O={mode:"new",a,b:a,moved:!1};n.style.cursor="crosshair",V(),o.preventDefault();return}Ce={x:r,vMin:ne,vMax:de},n.style.cursor="grabbing",o.preventDefault()}}function on(o){if(!w)return;let{x:r,y:c}=Ve(o);if(ge&&j){j.push({x:r,y:c}),X(),V();return}if(pe&&Se){Math.abs(r-Se.x)>6*d?be={a:Se.ts,b:tt(r)}:(be=null,ze==null&&(ie=se(c))),fe(),V();return}if(O){let s=tt(r);if(O.mode==="new")O.b=s,Math.abs(I(O.b)-I(O.a))>4*d&&(O.moved=!0);else if(O.mode==="edge"){let a=O.iv,m=yn();O.edge==="a"?a.start=ee(Math.min(s,a.end-m),U,Y):a.end=ee(Math.max(s,a.start+m),U,Y),O.moved=!0}else{let a=O.iv,m=O.a0+(s-O.grab),f=O.b0+(s-O.grab);m<U&&(f+=U-m,m=U),f>Y&&(m-=f-Y,f=Y),a.start=ee(m,U,Y),a.end=ee(f,U,Y),O.moved=!0}V();return}if(Ce){let s=(r-Ce.x)*(Ce.vMax-Ce.vMin)/(Ze()||1);He(Ce.vMin-s,Ce.vMax-s)}else if(ue){let s=vt(r);ue.type==="l"?He(Math.min(s,de-et()),de):ue.type==="r"?He(ne,Math.max(s,ne+et())):He(ue.vMin+(s-ue.grab),ue.vMax+(s-ue.grab))}}function ht(o){if(ge&&j){Rn(),j=null,X(),V();return}if(pe&&Se){let{x:r,y:c}=Ve(o);if(Math.abs(r-Se.x)>6*d){let s=Se.ts,a=tt(r);Me={a:Math.min(s,a),b:Math.max(s,a)}}else ze=se(c);Se=null,be=null,fe(),V();return}if(O){let r=O;if(r.mode==="new")if(r.moved){let c=ee(Math.min(r.a,r.b),U,Y),s=ee(Math.max(r.a,r.b),U,Y),a={start:c,end:s,label:""};$.push(a),te=a}else te=null;else if(r.iv.start>r.iv.end){let c=r.iv.start;r.iv.start=r.iv.end,r.iv.end=c}O=null,n.style.cursor="crosshair",r.moved&&Ye(),V();return}(Ce||ue)&&(Ce=null,ue=null,n.style.cursor="grab")}function rt(o){let r=o.target,c=!!r&&(r.tagName==="INPUT"||r.tagName==="TEXTAREA"||r.isContentEditable);if(ge){o.key==="Escape"&&j&&(j=null,X(),V());return}pe||c||!te||(o.key==="Delete"||o.key==="Backspace"?(o.preventDefault(),pt(te)):o.key==="Escape"&&(te=null,V()))}function It(o){if(!x||!w)return;if(ge&&j){o.preventDefault();return}o.preventDefault();let{x:r,y:c}=Ve(o),s=ee(Ie()*Math.pow(1.0015,o.deltaY),et(),Oe());if(c>=gt()){let a=vt(r);He(a-s/2,a+s/2)}else{let a=tt(r),m=(a-ne)/(Ie()||1);He(a-m*s,a-m*s+s)}}function Ht(){x&&He(U,Y)}n.addEventListener("mousemove",St),n.addEventListener("mouseleave",Ke),x&&(n.addEventListener("mousedown",Xe),n.addEventListener("wheel",It,{passive:!1}),n.addEventListener("dblclick",Ht),window.addEventListener("mousemove",on),window.addEventListener("mouseup",ht)),b&&(window.addEventListener("keydown",rt),n.style.cursor=k==="tune"?"grab":"crosshair");function Ct(o){w=o,!b&&o.incidents&&($=o.incidents),Be(o.series,o.scored),L>=o.series.timestamps.length&&(L=-1),V()}function wn(o){he!==o&&(he=o,w&&Be(w.series,w.scored),V())}function rn(o){$=o,te=null,W=-1,V()}function Ot(o){b&&(pe=o,o?(te=null,ge=!1,j=null,X()):(ie=null,Se=null,be=null),n.style.cursor="crosshair",fe(),V())}function sn(o){b&&(ge=o,o?(pe=!1,Se=null,be=null,ie=null,te=null,fe()):j=null,n.style.cursor="crosshair",X(),V())}function an(o){!b||k===o||(k=o,o!=="label"&&(pe=!1,Se=null,be=null,ie=null,ge=!1,j=null,te=null,W=-1,fe(),X()),L=-1,n.style.cursor=o==="tune"?"grab":"crosshair",w&&Be(w.series,w.scored),V())}function In(o){bt=o,fe(),V()}function xn(o){Nt=Math.max(0,Math.floor(o)||0),fe(),V()}function Bt(o){ze=o!=null&&Ae(o)?o:null,fe(),V()}function Ft(){let o=nt(),r=En()/2;for(let[c,s]of o)ot(c-r,s+r);return o.length&&Ye(),fe(),V(),o.length}function ln(){Me=null,be=null,fe(),V()}function Tt(){return Me?{start:Math.min(Me.a,Me.b),end:Math.max(Me.a,Me.b)}:null}function At(o){Me=o?{a:o.start,b:o.end}:null,fe(),V()}function Hn(){hn(),w&&(Be(w.series,w.scored),V())}function it(){n.removeEventListener("mousemove",St),n.removeEventListener("mouseleave",Ke),x&&(n.removeEventListener("mousedown",Xe),n.removeEventListener("wheel",It),n.removeEventListener("dblclick",Ht),window.removeEventListener("mousemove",on),window.removeEventListener("mouseup",ht)),b&&window.removeEventListener("keydown",rt),Q!==0&&cancelAnimationFrame(Q),Q=0,w=null}return hn(),{render:Ct,resize:Hn,setMode:an,setZeroLine:wn,setViewWindow:Et,setIncidents:rn,setThresholdMode:Ot,setThresholdDirection:In,setThresholdGap:xn,setThresholdValue:Bt,applyThreshold:Ft,setLassoMode:sn,clearCaptureWindow:ln,getCaptureWindow:Tt,setCaptureWindow:At,destroy:it}}var Xo={mad:3,zscore:3,iqr:1.5,autoreg:3},Jo={mad:10,zscore:3,iqr:4},Zo="dtk-tune";function p(n,l,g){let t=document.createElement(n);return l&&(t.className=l),g!=null&&(t.textContent=g),t}function Kt(n,l){let g=p("label","dtk-ctl-label",n);if(l){g.title=l;let t=p("span","dtk-ctl-info","\u24D8");t.title=l,g.appendChild(document.createTextNode(" ")),g.appendChild(t)}return g}function Gt(n,l,g,t,x){let y=p("div","dtk-ctl");y.appendChild(Kt(n,x));let R=p("div","dtk-seg"),b=g,k=[],F=()=>{k.forEach(H=>H.classList.toggle("on",H.dataset.v===b))};return l.forEach(H=>{let q=p("button","dtk-seg-btn",H.label);q.type="button",q.dataset.v=H.value,q.onclick=()=>{b=H.value,F(),t(b)},k.push(q),R.appendChild(q)}),F(),y.appendChild(R),{row:y,get:()=>b,set:H=>{b=H,F()}}}function at(n,l,g){var F;let t=p("div","dtk-ctl"),x=p("div","dtk-ctl-head"),y=Kt(n,l.hint),R=p("span","dtk-ctl-val"),b=(F=l.fmt)!=null?F:(H=>String(H));x.appendChild(y),x.appendChild(R),t.appendChild(x);let k=p("input","dtk-range");return k.type="range",k.min=String(l.min),k.max=String(l.max),k.step=String(l.step),k.value=String(l.value),R.textContent=b(l.value),k.oninput=()=>{R.textContent=b(Number(k.value))},k.onchange=()=>{let H=Number(k.value);R.textContent=b(H),g(H)},t.appendChild(k),{row:t,get:()=>Number(k.value),set:H=>{k.value=String(H),R.textContent=b(Number(k.value))},setMax:H=>{k.max=String(H),Number(k.value)>H&&(k.value=String(H),R.textContent=b(H))}}}function oo(n){var g;if(n.type==="manual_bounds"){let t={};return n.lowerBound!=null&&(t.lower_bound=n.lowerBound),n.upperBound!=null&&(t.upper_bound=n.upperBound),n.inputType!=="values"&&(t.input_type=n.inputType),t}if(n.type==="autoreg"){let t=Math.max(1,Math.round((g=n.lags)!=null?g:5)),x={lags:t,threshold:n.threshold,window_size:n.windowSize,min_samples:Math.min(Math.max(n.minSamples,t+2),n.windowSize)};return n.stabilization!=="clamp"&&(x.stabilization=null),n.inputType!=="values"&&(x.input_type=n.inputType),x}let l={threshold:n.threshold,window_size:n.windowSize};return n.windowWeights!=="none"&&(l.window_weights=n.windowWeights,n.windowWeights==="exponential"&&n.halfLife!=null&&(l.half_life=n.halfLife)),n.detrend!=="none"&&(l.detrend=n.detrend),n.stabilization&&n.stabilization!=="none"&&(l.stabilization=n.stabilization),n.smoothing!=="none"&&(l.smoothing=n.smoothing),n.inputType!=="values"&&(l.input_type=n.inputType),n.seasonalityComponents&&n.seasonalityComponents.length&&(l.seasonality_components=n.seasonalityComponents,l.min_samples_per_group=n.minSamplesPerGroup),l}function Qo(n,l,g,t){let x=oo(n),y=[`type: ${n.type}`];for(let[R,b]of Object.entries(x))y.push(`${R}=${typeof b=="object"?JSON.stringify(b):b}`);return n.direction&&n.direction!=="any"&&y.push(`direction=${n.direction}`),y.push(`consecutive_anomalies=${l}`),g!=null&&t!=null&&(y.push(`anomaly_window=${g}p`),y.push(`min_anomaly_share=${t}`)),y.join(" \xB7 ")}function er(n,l){var go,vo,yo,wo,xo,ko,Mo,So,Co;nr(),l.classList.add(Zo),l.innerHTML="";let g=p("div","dtk-tune-root");l.appendChild(g);let t=n.points.length,x={timestamps:n.points.map(e=>e.t),values:n.points.map(e=>e.v==null?NaN:e.v),intervalSeconds:n.interval_seconds,truthAnomaly:new Array(t).fill(!1),seasonalityData:n.seasonality_columns.length?n.seasonality:void 0,seasonalityColumns:n.seasonality_columns.length?n.seasonality_columns:void 0},y=x,R=e=>{let i=Math.max(0,t-e);return i<=0?x:{timestamps:x.timestamps.slice(i),values:x.values.slice(i),intervalSeconds:x.intervalSeconds,truthAnomaly:x.truthAnomaly.slice(i),seasonalityData:x.seasonalityData?x.seasonalityData.slice(i):void 0,seasonalityColumns:x.seasonalityColumns}},b=e=>Date.parse(e.replace(" ","T")+"Z"),k=(n.incidents||[]).map(e=>({start:b(e.start),end:b(e.end),label:e.label||""})).filter(e=>Number.isFinite(e.start)&&Number.isFinite(e.end)).map(e=>({start:Math.min(e.start,e.end),end:Math.max(e.start,e.end),label:e.label})),F=(()=>{let e=(n.capture_windows||[])[0];if(!e)return null;let i=b(e.start),u=b(e.end);return!Number.isFinite(i)||!Number.isFinite(u)?null:{start:Math.min(i,u),end:Math.max(i,u)}})(),H=[],q=[],_e=[],le=[],Z=(n.alert_reviews||[]).map(e=>({start:b(e.start),end:b(e.end),verdict:e.verdict==="false"?"false":"valid"})).filter(e=>Number.isFinite(e.start)&&Number.isFinite(e.end)),Je=()=>n.interval_seconds*1e3/2,re=(e,i)=>{let u=Je();for(let v of Z)if(i>=v.start-u&&e<=v.end+u)return v.verdict;return null},d=(e,i,u)=>{let v=Je();for(let M=Z.length-1;M>=0;M--){let h=Z[M];i>=h.start-v&&e<=h.end+v&&Z.splice(M,1)}u&&Z.push({start:e,end:i,verdict:u})},w=(e,i)=>{let u=Je();return e.end>=i.start-u&&e.start<=i.end+u},L=()=>Z.filter(e=>e.verdict==="valid").map(e=>({start:e.start,end:e.end,label:"confirmed alert"})),Q=()=>L().filter(e=>!k.some(i=>w(e,i))),he=()=>[...k,...Q()],$=e=>{let i=!1;for(let u=Z.length-1;u>=0;u--)Z[u].verdict==="valid"&&w(Z[u],e)&&(Z.splice(u,1),i=!0);return i},te=()=>H.map((e,i)=>{var M;let u=(M=q[i])!=null?M:[e,e],v=re(u[0],u[1]);return{t:e,kind:v==="valid"?"anomaly-validated":v==="false"?"anomaly-false":"anomaly"}}),W=n.detector,O=n.consecutive_anomalies,ke=(go=n.anomaly_window_points)!=null?go:0,pe=(vo=n.min_anomaly_share)!=null?vo:.3,bt=()=>ke>=2?ke:null,Nt=()=>ke>=2?pe:null,ze=n.detectors||[],ie=typeof n.detector_index=="number"?n.detector_index:null,Me=ie,Se=new Set,be=new Map,ge=()=>{ie!=null&&Se.add(ie)},j=x.values.filter(e=>Number.isFinite(e)).sort((e,i)=>e-i),U=j.length?j[0]:0,Y=j.length?j[j.length-1]:1,De=e=>j.length?j[Math.min(j.length-1,Math.max(0,Math.round(e*(j.length-1))))]:0,Ge=Math.max((Y-U)*.05,1e-9),ne=U-Ge,de=Y+Ge,Xt=Math.max((de-ne)/400,1e-9),hn=W.lowerBound!=null?W.lowerBound:De(.05),lt=W.upperBound!=null?W.upperBound:De(.95),Ze=(yo=W.seasonalityComponents)!=null?yo:[],ve=new Map;Ze.forEach((e,i)=>e.forEach(u=>ve.set(u,i+1)));let Qe=()=>{let e=0;ve.forEach(u=>{u>e&&(e=u)});let i=[];for(let u=1;u<=e;u++){let v=n.seasonality_columns.filter(M=>ve.get(M)===u);v.length&&i.push(v)}return i.length?i:null},zt=e=>{if(!e||!n.seasonality.length)return 0;let i=0;for(let u of e){let v=new Set;for(let M of n.seasonality)v.add(u.map(h=>{var T;return String((T=M==null?void 0:M[h])!=null?T:"")}).join("|"));i=Math.max(i,v.size)}return i},Ie=()=>{var e;return{type:K.get(),threshold:N.get(),windowSize:Vt.get(),minSamples:W.minSamples,inputType:W.inputType,smoothing:Sn.get(),smoothingAlpha:W.smoothingAlpha,smoothingWindow:W.smoothingWindow,windowWeights:qt.get(),halfLife:qt.get()==="exponential"?cn.get():null,detrend:Mn.get(),stabilization:un.get(),seasonalityComponents:Qe(),minSamplesPerGroup:(e=Jo[K.get()])!=null?e:W.minSamplesPerGroup,consecutiveAnomalies:O,direction:ao.get(),lowerBound:C.get(),upperBound:z.get(),lags:dn.get()}},Oe=p("div","dtk-tune-header"),I=p("div","dtk-tune-titlerow");I.appendChild(p("h1","dtk-tune-title",n.metric));let ye=p("span","dtk-tune-badge",n.save_url?"manual tuning":"preview");I.appendChild(ye),Oe.appendChild(I);let gt=n.project?`${n.project} \xB7 `:"";Oe.appendChild(p("div","dtk-tune-sub",`${gt}${t} points \xB7 ${tr(n.interval_seconds)} grid`)),n.description&&Oe.appendChild(p("div","dtk-tune-desc",n.description)),g.appendChild(Oe);let Le=p("div","dtk-tune-metrics"),dt=p("div","dtk-tune-cockpit");g.appendChild(dt);let ce=p("div","dtk-tune-stage");dt.appendChild(ce);let Jt=p("div","dtk-tune-hud");Jt.appendChild(Le),ce.appendChild(Jt);let ct=p("div","dtk-tune-stagefoot"),ee=p("div","dtk-tune-rail");dt.appendChild(ee);let et=p("div","dtk-tune-railhead"),He=p("span","dtk-rail-title","Tune \xB7 controls"),Et=p("button","dtk-dock-toggle","\u27E9");Et.type="button",Et.title="Collapse the control rail to give the chart the whole width.",et.appendChild(He),et.appendChild(Et),ee.appendChild(et);let tt=p("div","dtk-tune-controls");ee.appendChild(tt);let vt=p("div","dtk-rail-group"),se=p("div","dtk-rail-group"),Rt=p("div","dtk-rail-group"),Be=p("div","dtk-rail-group"),Fe=p("div","dtk-rail-group"),ut=p("div","dtk-rail-group");Rt.style.display="none",Be.style.display="none",Fe.style.display="none",tt.append(vt,se,Rt,Be,Fe,ut);let yt=p("div","dtk-tune-railfoot");ee.appendChild(yt);let mt=p("button","dtk-rail-open","\u2699");mt.type="button",mt.title="Show the control rail",mt.style.display="none",ce.appendChild(mt);let bn=e=>{ee.style.display=e?"":"none",mt.style.display=e?"none":""};Et.onclick=()=>bn(!1),mt.onclick=()=>bn(!0);let Zt=p("div","dtk-tune-trim"),Qt=p("div","dtk-tune-trim-head");Qt.appendChild(Kt("Points shown","Trim the active sample to the most-recent N points. Fewer points recompute faster (cost grows with points \xD7 window) and make a shorter period easier to read \u2014 handy once you can see a smaller window/period is enough."));let wt=p("span","dtk-tune-trim-val");Qt.appendChild(wt),Zt.appendChild(Qt);let $e=p("input","dtk-range");$e.type="range",$e.min=String(Math.min(t,200)),$e.max=String(t),$e.step=String(Math.max(1,Math.round(t/200))),$e.value=String(t),Zt.appendChild($e),vt.appendChild(Zt);let nt=p("div","dtk-tune-chart"),fe=p("canvas");nt.appendChild(fe);let ot=p("div","dtk-tune-spin");ot.appendChild(p("span","dtk-spin-ring")),ot.appendChild(p("span","dtk-spin-txt","computing\u2026")),nt.appendChild(ot),ce.appendChild(nt),ce.appendChild(ct);let gn=p("div","dtk-tune-modes"),en={};[{v:"tune",label:"Tune",hint:"Steer the band \u2014 the confidence corridor leads; incidents recede to read-only context. Hover a point for its window."},{v:"review",label:"Review alerts",hint:"Confirm the fired alerts \u2014 click a marker to cycle un-reviewed \u2192 valid (green) \u2192 false (slate). The band ghosts so the alerts lead."},{v:"label",label:"Label incidents",hint:"Mark real incidents \u2014 drag a span, lasso the anomaly cloud, or threshold-capture. The band hides so incidents lead."},{v:"autotune",label:"Autotune",hint:"Let the autotune engine search for the best detector server-side, using your marked incidents as ground truth, then re-seed the knobs with the winner. Review the band, then Apply."}].forEach(e=>{let i=p("button","dtk-mode-btn",e.label);i.type="button",i.title=e.hint,i.dataset.v=e.v,i.onclick=()=>xn(e.v),en[e.v]=i,gn.appendChild(i)}),Jt.appendChild(gn);let vn=p("div","dtk-tune-legend"),je=(e,i,u)=>{let v=p("span","dtk-leg-item");v.title=u,v.appendChild(p("span",`dtk-leg-sw ${e}`)),v.appendChild(p("span","dtk-leg-txt",i)),vn.appendChild(v)};je("alert","alert","A fired alert, not yet reviewed \u2014 enough consecutive anomalies to meet the rule."),je("alert-ok","valid alert","An alert you confirmed is real (click a marker in Review mode). Counts toward recall."),je("alert-no","false alarm","An alert you marked a false positive. Stays in the false-alert rate."),je("dot","anomaly","A point the detector flagged as anomalous (outside the band)."),je("line","metric","The metric value over time."),je("band","expected range","The detector's confidence band \u2014 values inside it read as normal."),je("center","band center","The expected value at the middle of the band."),ce.insertBefore(vn,nt);let Wt=p("div","dtk-tune-readout");ct.appendChild(Wt);let xt=p("div","dtk-tune-warn");xt.style.display="none",ct.appendChild(xt);let Rn=e=>{let i=e.seasonalityComponents,u=zt(i),v=e.minSamplesPerGroup*u;i&&u>0&&e.windowSize<v?(xt.textContent=`\u26A0 Seasonality inactive at this window: ${e.windowSize} < ${v} (min_samples_per_group ${e.minSamplesPerGroup} \xD7 ${u} key${u===1?"":"s"}). Each point keeps only ~${Math.floor(e.windowSize/u)} same-key point(s), so the band falls back to global statistics (seasonality has no effect). Raise the window to \u2265 ${v}.`,xt.style.display=""):xt.style.display="none"},kt=p("div","dtk-tune-warn");kt.style.display="none",ct.appendChild(kt);let Wn=(e,i)=>{var T;let u=y.timestamps.length;if(u===0||e<u){kt.style.display="none";return}let v=i.stabilization==="clamp",M=i.type==="autoreg"?` (window ${i.windowSize} + lags ${Math.max(1,Math.round((T=i.lags)!=null?T:5))}${i.inputType!=="values"?" + 1 for change input":""}${v?` + window ${i.windowSize} for stabilization`:""})`:v?` (incl. a full window of ${i.windowSize} stabilization warm-up)`:"",h=[];t>e&&h.push(`raise Points shown above ${e}`),(i.type==="autoreg"||v)&&h.push("lower the Window size"),v&&h.push("turn Stabilization off"),i.type!=="autoreg"&&!v&&h.push("reduce min_samples / smoothing / the seasonality grouping (they set this warm-up)"),kt.textContent=`\u26A0 The whole view is detector warm-up: it needs ${e} pts${M} before full power, but only ${u} are shown \u2014 no band or anomaly dots can be drawn. To see the band: ${h.join(", or ")}.`,kt.style.display=""},tn=e=>{let i=Je(),u=y.timestamps,v=(u.length?u[0]:0)-i,M=(u.length?u[u.length-1]:0)+i,h=We=>We.end>=v&&We.start<=M,T=k.filter(h),me=L().filter(h).filter(We=>!T.some(Ue=>w(We,Ue))),we=[...T,...me],Dt=(We,Ue)=>We[1]>=Ue.start-i&&We[0]<=Ue.end+i,qe=0,oe=0,Re=0;for(let We of e){let Ue=re(We[0],We[1]);Ue&&Re++,Ue==="valid"&&oe++,(Ue==="valid"||Ue!=="false"&&we.some(Fo=>Dt(We,Fo)))&&qe++}let Kn=0;for(let We of we)e.some(Ue=>Dt(Ue,We))&&Kn++;let pn=e.length;return{realIncidents:we.length,caught:Kn,recall:we.length?Kn/we.length:NaN,totalAlerts:pn,correctAlerts:qe,falseAlerts:pn-qe,fdr:pn?(pn-qe)/pn:NaN,validated:oe,reviewed:Re}},nn=e=>Number.isFinite(e)?`${Math.round(e*100)}%`:"\u2014",Ye=typeof n.false_alert_budget=="number"&&n.false_alert_budget>0?n.false_alert_budget:null,pt=(e,i,u,v,M)=>`<span class="dtk-m-chip${M!=null&&M.cls?" "+M.cls:""}"${M!=null&&M.title?` title="${M.title}"`:""}><span class="dtk-m-dot" style="background:${v}"></span><span class="dtk-m-v">${e}</span><span class="dtk-m-l">${i}</span>`+(u?`<span class="dtk-m-sub">${u}</span>`:"")+"</span>";function ft(){let e=tn(q),i=e.realIncidents>0,u=Ye!=null?`${Math.round(Ye*100)}%`:"",v=i&&e.totalAlerts>0&&Ye!=null&&Number.isFinite(e.fdr)&&e.fdr>Ye,M;if(!i)M="mark incidents to measure";else if(e.totalAlerts===0)M="no alerts";else if(e.falseAlerts===0)M=`${nn(e.fdr)} \xB7 all correct`;else{let me=e.totalAlerts/e.falseAlerts,we=me>=9.5?String(Math.round(me)):String(Math.round(me*10)/10);M=`${nn(e.fdr)} \xB7 \u22481 in ${we} false`}v&&(M+=` \xB7 \u25B2 over ${u} budget`);let h=e.totalAlerts===0?"no alerts":`${e.validated} valid${e.totalAlerts>e.reviewed?` \xB7 ${e.totalAlerts-e.reviewed} left`:" \xB7 all reviewed"}`,T=Ye?`Budget: at most ${u} of fired alerts false (false-alert rate / FDR). Set per metric or project via false_alert_budget. Labeling is optional \u2014 this just flags when you exceed the budget.`:"";Le.innerHTML=pt(String(e.realIncidents),"real incidents","","var(--c)")+pt(i?`${e.caught}/${e.realIncidents}`:"\u2014","caught",i?`recall ${nn(e.recall)}`:"mark or confirm","var(--green)")+pt(String(e.totalAlerts),"alerts","","var(--c)")+pt(i?String(e.falseAlerts):"\u2014","false alerts",M,"var(--anom)",{cls:v?"over":"",title:T})+pt(`${e.reviewed}/${e.totalAlerts}`,"reviewed",h,"var(--green)")}let yn=()=>{},V=()=>{},X=No(fe,{navigable:!0,labeling:!0,mode:"tune",yFit:"data",onHover:e=>{if(!e||!e.point||!e.point.scored){Wt.textContent="";return}let i=e.point;Wt.textContent=`t=${no(i.timestamp)} value=${fn(i.value)} band=[${fn(i.lower)}, ${fn(i.upper)}]`+(i.isAnomaly?` \u26A0 ${i.direction} (sev ${i.severity.toFixed(2)})`:"")},onIncidentsChange:(e,i)=>{(i?$(i):!1)?Pe():(Dn(),ft())},onThresholdChange:e=>yn(e),onLassoChange:e=>V(e),onAlertReviewChange:(e,i)=>{let u=H.indexOf(e),v=u>=0?q[u]:[e,e];d(v[0],v[1],i==="unreviewed"?null:i),Pe()}});X.setIncidents(k),F&&X.setCaptureWindow(F);let Pe=()=>{le=te(),r&&X.render({series:y,scored:_e,params:r,alerts:le,incidents:k}),ft(),Dn()},Ce=p("div","dtk-th"),ue=p("div","dtk-th-toggles"),Ve=p("button","dtk-th-toggle","Threshold capture");Ve.type="button",Ve.title="Mark incidents fast: set a horizontal line and grab every contiguous span above (or below) it. Click the chart to set the line, drag across it to limit the capture to a time window.",ue.appendChild(Ve);let Mt=p("button","dtk-th-toggle","Lasso anomalies");Mt.type="button",Mt.title="Draw a freeform loop around a cloud of anomaly dots on the chart below \u2014 each run of consecutive anomalies (small gaps bridged) becomes one incident span. The ideal way to turn what the detector already flags into ground-truth incidents.",ue.appendChild(Mt),Ce.appendChild(ue);let Ee=p("div","dtk-th-bar"),St=p("select","dtk-th-sel");for(let[e,i]of[["above","above the line"],["below","below the line"]]){let u=p("option");u.value=e,u.textContent=i,St.appendChild(u)}let Ke=p("input","dtk-th-num");Ke.type="number",Ke.step="any",Ke.placeholder="hover chart";let Xe=p("input","dtk-th-num");Xe.type="number",Xe.min="0",Xe.step="1",Xe.value="0";let on=p("span","dtk-th-scope"),ht=p("button","dtk-inc-btn","\u21BA whole view");ht.type="button",ht.style.display="none";let rt=p("button","dtk-apply-btn dtk-th-add","Add 0 spans");rt.type="button",rt.disabled=!0;let It=p("button","dtk-inc-btn","Done");It.type="button";let Ht=(e,i)=>{let u=p("label","dtk-th-grp");return u.appendChild(p("span","dtk-th-lbl",e)),u.appendChild(i),u};Ee.appendChild(Ht("grab points",St)),Ee.appendChild(Ht("line value",Ke)),Ee.appendChild(Ht("bridge gaps \u2264",Xe)),Ee.appendChild(on),Ee.appendChild(ht),Ee.appendChild(rt),Ee.appendChild(It),Ee.style.display="none",Ce.appendChild(Ee);let Ct=p("div","dtk-th-bar"),wn=p("span","dtk-th-scope","draw a loop around the anomaly dots on the chart below"),rn=p("button","dtk-inc-btn","Done");rn.type="button",Ct.appendChild(wn),Ct.appendChild(rn),Ct.style.display="none",Ce.appendChild(Ct),Be.appendChild(Ce);let Ot=p("div","dtk-th-bar dtk-tune-reviewbar");Ot.appendChild(p("span","dtk-th-scope","Click a red alert marker to confirm it valid (green) or mark it a false alarm \u2014 cycle un-reviewed \u2192 valid \u2192 false."));let sn=p("button","dtk-apply-btn dtk-th-add","Confirm all unreviewed valid");sn.type="button",sn.onclick=()=>{for(let[e,i]of q)re(e,i)||d(e,i,"valid");Pe()};let an=p("button","dtk-inc-btn","Clear verdicts");an.type="button",an.onclick=()=>{Z.length=0,Pe()},Ot.appendChild(sn),Ot.appendChild(an),Rt.appendChild(Ot);let In={tune:"Tune \xB7 controls",review:"Review \xB7 verdicts",label:"Label \xB7 incidents",autotune:"Autotune \xB7 search"};function xn(e){X.setMode(e==="autotune"?"tune":e),Object.keys(en).forEach(i=>{var u;return(u=en[i])==null?void 0:u.classList.toggle("on",i===e)}),e!=="label"&&(Tt(!1),At(!1)),se.style.display=e==="tune"?"":"none",Rt.style.display=e==="review"?"":"none",Be.style.display=e==="label"?"":"none",Fe.style.display=e==="autotune"?"":"none",yt.style.display=e==="tune"||e==="autotune"?"":"none",He.textContent=In[e]}let Bt=!1,Ft=!1,ln=!1,Tt=e=>{Bt=e,Ve.classList.toggle("on",e),Ee.style.display=e?"flex":"none",X.setThresholdMode(e),e&&Ft&&At(!1)},At=e=>{Ft=e,Mt.classList.toggle("on",e),Ct.style.display=e?"flex":"none",X.setLassoMode(e),e&&Bt&&Tt(!1)};Ve.onclick=()=>Tt(!Bt),It.onclick=()=>Tt(!1),Mt.onclick=()=>At(!Ft),rn.onclick=()=>At(!1),St.onchange=()=>X.setThresholdDirection(St.value),Ke.oninput=()=>{let e=Ke.value.trim();ln=!0,X.setThresholdValue(e!==""&&!isNaN(Number(e))?Number(e):null),ln=!1},Xe.oninput=()=>X.setThresholdGap(Number(Xe.value)||0),ht.onclick=()=>X.clearCaptureWindow(),rt.onclick=()=>{X.applyThreshold()},window.addEventListener("keydown",e=>{e.key==="Escape"&&(Bt?Tt(!1):Ft&&At(!1))}),V=e=>{wn.textContent=e.active?`${e.anomalies} anomal${e.anomalies===1?"y":"ies"} \u2192 ${e.incidents} incident${e.incidents===1?"":"s"} \u2014 release to add`:"draw a loop around the anomaly dots on the chart below"},yn=e=>{rt.textContent=`Add ${e.runs} span${e.runs===1?"":"s"}`,rt.disabled=e.runs===0,e.locked&&e.value!=null&&!ln&&(Ke.value=String(Math.round(e.value*1e3)/1e3)),ht.style.display=e.committed?"":"none",on.textContent=e.window?`scope: ${Yt(e.windowMs)} painted`:"scope: current view \u2014 drag the chart to limit it"};let Hn=URL.createObjectURL(new Blob([`"use strict";(()=>{var re={mad:1,zscore:2,iqr:4};function H(n){let e=0;for(let t of n)e+=t;return e}function le(n){let e=n.map((t,i)=>i);return e.sort((t,i)=>n[t]<n[i]?-1:n[t]>n[i]?1:t-i),e}function ce(n,e,t){let i=e.length;if(i===0)return NaN;if(n<=e[0])return t[0];if(n>=e[i-1])return t[i-1];for(let r=1;r<i;r++)if(n<=e[r]){let o=e[r-1],u=e[r],s=t[r-1],l=t[r];if(u===o)return s;let a=(n-o)/(u-o);return s+a*(l-s)}return t[i-1]}function $(n,e,t){let i=le(n),r=i.map(c=>n[c]),o=i.map(c=>e[c]),u=H(o),s=new Array(o.length),l=0;for(let c=0;c<o.length;c++)l+=o[c],s[c]=(l-.5*o[c])/u;let a=t/100;return ce(a,s,r)}function V(n,e){return $(n,e,50)}function ae(n,e,t){let i=t===void 0?V(n,e):t,r=n.map(o=>Math.abs(o-i));return V(r,e)}function me(n,e){let t=H(e),i=0;for(let r=0;r<n.length;r++)i+=n[r]*(e[r]/t);return i}function fe(n,e,t,i=0){let r=H(e),o=e.map(l=>l/r),u=t===void 0?H(n.map((l,a)=>l*o[a])):t,s=0;for(let l=0;l<n.length;l++)s+=o[l]*(n[l]-u)**2;if(i===1){let l=1-H(o.map(a=>a*a));l>1e-12&&(s/=l)}return Math.sqrt(s)}function de(n,e){return e.smoothing==="none"?n.slice():e.smoothing==="ema"?be(n,e.smoothingAlpha):pe(n,e.smoothingWindow)}function be(n,e){let t=n.length,i=new Array(t).fill(NaN);if(t===0)return i;let r=-1;for(let o=0;o<t;o++)if(!Number.isNaN(n[o])){r=o;break}if(r===-1)return i;i[r]=n[r];for(let o=r+1;o<t;o++)Number.isNaN(n[o])?i[o]=i[o-1]:i[o]=e*n[o]+(1-e)*i[o-1];return i}function pe(n,e){let t=n.length,i=new Array(t).fill(NaN);for(let r=0;r<t;r++){let o=Math.max(0,r-e+1),u=0,s=0;for(let l=o;l<=r;l++)Number.isNaN(n[l])||(u+=n[l],s+=1);i[r]=s>0?u/s:NaN}return i}function ee(n,e){let t=n.length;if(e.inputType==="values")return n.slice();let i=new Array(t).fill(NaN);for(let r=1;r<t;r++){let o=n[r-1],u=n[r];e.inputType==="changes"?i[r]=(u-o)/o:e.inputType==="absolute_changes"?i[r]=u-o:i[r]=Math.log(u+1)-Math.log(o+1)}return i}function ye(n,e){return n.halfLife!==null?n.halfLife:Math.max(n.windowSize/20,e/2,1)}function he(n,e){if(n.windowWeights==="none")return null;let t=n.windowSize,i=new Array(t);if(n.windowWeights==="exponential"){let r=ye(n,e);for(let o=1;o<=t;o++){let u=Math.min(o/r,1e3);i[o-1]=Math.pow(.5,u)}return i}for(let r=1;r<=t;r++)i[r-1]=(t+1-r)/t;return i}function ne(n,e){return e===null?n.map(()=>1):n.map(t=>e[t-1])}function ge(n){let e=n[0];for(let t of n)t>e&&(e=t);return e}function we(n){let e=n[0];for(let t of n)t<e&&(e=t);return e}function Se(n,e,t){if(n.length<4)return 0;let i=(ge(e)+we(e))/2,r=e.map(g=>g>i),o=r.map(g=>!g),u=0,s=0;for(let g=0;g<e.length;g++)r[g]?u++:s++;if(u<2||s<2)return 0;let l=(g,_)=>_.filter((N,R)=>g[R]),a=l(o,n),c=l(r,n),S=l(o,e),p=l(r,e),v=l(o,t),f=l(r,t),y=V(a,v),d=V(c,f),k=V(S,v),h=V(p,f);return h===k?0:(y-d)/(h-k)}var Ae={mad:[["median","center"],["mad","spread"]],zscore:[["mean","center"],["std","spread"]],iqr:[["q1","center"],["q3","center"],["iqr","spread"]]};function te(n,e,t){if(n==="mad"){let o=V(e,t);return{median:o,mad:ae(e,t,o)}}if(n==="zscore"){let o=me(e,t);return{mean:o,std:fe(e,t,o,1)}}let i=$(e,t,25),r=$(e,t,75);return{q1:i,q3:r,iqr:r-i}}function Me(n,e,t){if(n==="mad"){if(e.mad===0)return[e.median-1e-10,e.median+1e-10];let i=t*1.4826*e.mad;return[e.median-i,e.median+i]}return n==="zscore"?e.std===0?[e.mean-1e-10,e.mean+1e-10]:[e.mean-t*e.std,e.mean+t*e.std]:e.iqr===0?[e.q1-1e-10,e.q3+1e-10]:[e.q1-t*e.iqr,e.q3+t*e.iqr]}function Ne(n,e,t){if(n==="mad"){let i=1.4826*e.mad;return i>0?t/i:1/0}return n==="zscore"?e.std>0?t/e.std:1/0:e.iqr>0?t/e.iqr:1/0}function ke(n,e){return n==="mad"?e.median:n==="zscore"?e.mean:(e.q1+e.q3)/2}function J(n,e,t,i,r){return{index:n,timestamp:e,value:t,processedValue:i,scored:!1,isAnomaly:!1,lower:NaN,upper:NaN,center:NaN,direction:null,severity:0,reason:r}}function xe(n,e){var a,c,S;let{timestamps:t,values:i}=n,r=t.length,o=ee(i,e),u=(a=e.lowerBound)!=null?a:null,s=(c=e.upperBound)!=null?c:null,l=[];for(let p=0;p<r;p++){let v=i[p],f=o[p],y=t[p];if(Number.isNaN(f)){l.push(J(p,y,v,f,"missing_data"));continue}let d=!1,k=null,h=0;u!==null&&f<u&&(d=!0,k="below",h=u-f),s!==null&&f>s&&(d=!0,k="above",h=f-s);let g=0;if(d)if(u!==null&&s!==null){let x=s-u;g=x>0?h/x:1/0}else g=h;let _=u!=null?u:-1/0,N=s!=null?s:1/0,R=u!==null&&s!==null?(u+s)/2:(S=u!=null?u:s)!=null?S:NaN;l.push({index:p,timestamp:y,value:v,processedValue:f,scored:!0,isAnomaly:d,lower:_,upper:N,center:R,direction:k,severity:g,reason:"ok"})}return l}function De(n,e){let t=e.length;for(let r=0;r<t;r++){let o=r;for(let s=r+1;s<t;s++)Math.abs(n[s][r])>Math.abs(n[o][r])&&(o=s);o!==r&&([n[r],n[o]]=[n[o],n[r]],[e[r],e[o]]=[e[o],e[r]]);let u=n[r][r];if(u!==0)for(let s=r+1;s<t;s++){let l=n[s][r]/u;if(l!==0){for(let a=r;a<t;a++)n[s][a]-=l*n[r][a];e[s]-=l*e[r]}}}let i=new Array(t).fill(0);for(let r=t-1;r>=0;r--){let o=e[r];for(let u=r+1;u<t;u++)o-=n[r][u]*i[u];i[r]=n[r][r]!==0?o/n[r][r]:0}return i}function ve(n,e){let t=n[0].length,i=Array.from({length:t},()=>new Array(t).fill(0)),r=new Array(t).fill(0);for(let s=0;s<n.length;s++){let l=n[s];for(let a=0;a<t;a++){r[a]+=l[a]*e[s];for(let c=a;c<t;c++)i[a][c]+=l[a]*l[c]}}for(let s=0;s<t;s++)for(let l=0;l<s;l++)i[s][l]=i[l][s];let o=0;for(let s=0;s<t;s++)o+=i[s][s];let u=1e-8*(o/t);for(let s=0;s<t;s++)i[s][s]+=u;return De(i,r)}function Pe(n,e){var v;let{timestamps:t,values:i}=n,r=t.length,o=Math.max(1,Math.round((v=e.lags)!=null?v:5)),u=e.windowSize,s=e.threshold,l=Math.max(e.minSamples,o+2),a=e.stabilization==="clamp",c=ee(i,e),S=a?c.slice():c,p=[];for(let f=0;f<r;f++){let y=i[f],d=c[f],k=t[f];if(Number.isNaN(d)){p.push(J(f,k,y,d,"missing_data"));continue}let h=f>=o;if(h){for(let m=f-o;m<f;m++)if(!Number.isFinite(S[m])){h=!1;break}}if(!h){p.push(J(f,k,y,d,"missing_lags"));continue}let g=Math.max(o,f-u),_=g-o,N=[],R=[];for(let m=g;m<f;m++){let A=S[m];if(!Number.isFinite(A))continue;let w=!0,D=new Array(o);for(let z=0;z<o;z++){let Y=S[m-o+z];if(!Number.isFinite(Y)){w=!1;break}D[z]=Y}w&&(N.push(A),R.push(D))}let x=N.length;if(x<l){p.push(J(f,k,y,d,"insufficient_data"));continue}let P=H(N)/x,O=0;for(let m of N)O+=(m-P)*(m-P);let T=Math.sqrt(O/x);Number.isFinite(P)||(P=0),(!Number.isFinite(T)||T<=0)&&(T=1);let j=new Array(x),I=new Array(x);for(let m=0;m<x;m++){let A=new Array(o+1);A[0]=1;for(let w=0;w<o;w++)A[w+1]=(R[m][w]-P)/T;j[m]=A,I[m]=(N[m]-P)/T}let Q=ve(j,I),U=0;for(let m=0;m<x;m++){let A=0;for(let D=0;D<=o;D++)A+=j[m][D]*Q[D];let w=I[m]-A;U+=w*w}let C=Math.sqrt(U/Math.max(x-(o+1),1))*T,q=Q[0];for(let m=0;m<o;m++)q+=(S[f-o+m]-P)/T*Q[m+1];let F=q*T+P,W=F-s*C,L=F+s*C,E=d<W||d>L;if(a&&E){let m=d<W?W:L,A=1/0,w=-1/0;for(let D=_;D<f;D++){let z=c[D];Number.isFinite(z)&&(z<A&&(A=z),z>w&&(w=z))}A<=w&&(m=Math.min(Math.max(m,A),w)),S[f]=m}let X=null,b=0;if(E){let m;d<W?(X="below",m=W-d):(X="above",m=d-L),b=C>0?m/C:1/0}p.push({index:f,timestamp:k,value:y,processedValue:d,scored:!0,isAnomaly:E,lower:W,upper:L,center:F,direction:X,severity:b,reason:"ok"})}return p}function oe(n,e){if(e.type==="manual_bounds")return xe(n,e);if(e.type==="autoreg")return Pe(n,e);let t=e.type,i=Math.max(e.minSamples,re[t]),r=Ae[t],{timestamps:o,values:u}=n,s=o.length,l=de(u,e),a=ee(l,e),c=e.stabilization==="clamp",S=c?a.slice():a,p=e.seasonalityComponents!==null&&e.seasonalityComponents.length>0&&Array.isArray(n.seasonalityData)&&n.seasonalityData.length>0,v=n.seasonalityData,f=he(e,i),y=[];for(let d=0;d<s;d++){let k=u[d],h=a[d],g=o[d];if(Number.isNaN(h)){y.push(J(d,g,k,h,"missing_data"));continue}let _=Math.max(0,d-e.windowSize),N=d-_,R=S.slice(_,d),x=R.map(b=>!Number.isNaN(b)),P=[];for(let b=0;b<R.length;b++)x[b]&&P.push(R[b]);if(P.length<i){y.push(J(d,g,k,h,"insufficient_data"));continue}let O=new Array(N);for(let b=0;b<N;b++)O[b]=N-b;let T=[];for(let b=0;b<N;b++)x[b]&&T.push(O[b]);let j=ne(T,f),I=0;e.detrend==="linear"&&(I=Se(P,T,j));let Q=I!==0?P.map((b,m)=>b+I*T[m]):P,U=te(t,Q,j),C={...U};if(p&&v){let b=v[d];for(let m of e.seasonalityComponents){let A=new Array(N).fill(!0);for(let M=0;M<N;M++){let B=v[_+M],G=!0;for(let Z of m)if(!B||B[Z]!==(b==null?void 0:b[Z])){G=!1;break}A[M]=G}let w=[],D=[];for(let M=0;M<N;M++)x[M]&&A[M]&&(w.push(R[M]),D.push(O[M]));if(w.length<e.minSamplesPerGroup)continue;let z=ne(D,f),Y=I!==0?w.map((M,B)=>M+I*D[B]):w,se=te(t,Y,z);for(let[M,B]of r){let G=U[M],ue=(B==="spread"?G>0:G!==0)?se[M]/G:1;C[M]*=ue}}}let[q,F]=Me(t,C,e.threshold);q>F&&([q,F]=[F,q]);let W=h<q||h>F,L=null,E=0;if(W){let b;h<q?(L="below",b=q-h):(L="above",b=h-F),E=Ne(t,C,b)}c&&W&&(S[d]=h<q?q:F);let X=ke(t,C);y.push({index:d,timestamp:g,value:k,processedValue:h,scored:!0,isAnomaly:W,lower:q,upper:F,center:X,direction:L,severity:E,reason:"ok"})}return y}function Te(n,e){let t=n.seasonalityData;if(!t||t.length===0)return 0;let i=0;for(let r of e){let o=new Set;for(let u of t)o.add(r.map(s=>{var l;return String((l=u==null?void 0:u[s])!=null?l:"")}).join("|"));i=Math.max(i,o.size)}return i}function ie(n,e){var r;if(e.type==="manual_bounds")return e.inputType!=="values"?1:0;if(e.type==="autoreg"){let o=Math.max(1,Math.round((r=e.lags)!=null?r:5)),u=e.windowSize+o;return e.inputType!=="values"&&(u+=1),e.stabilization==="clamp"&&(u+=e.windowSize),u}let t=Math.max(e.minSamples,re[e.type]);if(e.smoothing==="sma"&&(t=Math.max(t,e.smoothingWindow-1)),e.smoothing==="ema"&&(t=Math.max(t,Math.ceil(5/e.smoothingAlpha))),e.inputType!=="values"&&(t=Math.max(t,1)),e.seasonalityComponents!==null&&e.seasonalityComponents.length>0&&Array.isArray(n.seasonalityData)&&n.seasonalityData.length>0){let o=Te(n,e.seasonalityComponents);if(o>0){let u=e.minSamplesPerGroup*o;e.windowSize>=u&&(t=Math.max(t,u))}}return e.stabilization==="clamp"&&(t+=e.windowSize),t}var K=null;function qe(n,e){if(!e||e==="any")return;let t=e==="up"?"above":"below";for(let i of n)i.isAnomaly&&i.direction!==t&&(i.isAnomaly=!1)}function ze(n,e,t){let i=[],r=n.length,o=0;for(;o<r;){if(!(n[o].scored&&n[o].isAnomaly)){o++;continue}let u=o;for(;u+1<r&&n[u+1].scored&&n[u+1].isAnomaly&&n[u+1].timestamp-n[u].timestamp===e;)u++;u-o+1>=t&&i.push({fire:o+t-1,startTs:n[o].timestamp,endTs:n[u].timestamp}),o=u+1}return i}function Re(n,e,t){let i=n.length,r=new Array(i);for(let c=0;c<i;c++)r[c]=n[c].scored&&n[c].isAnomaly;let o=t*e,u=[],s=null,l=-1/0,a=0;for(let c=0;c<i;c++)if(r[c]&&a++,c-e>=0&&r[c-e]&&a--,!(!r[c]||a<o)){if(s&&c-l<=e)s.endTs=n[c].timestamp;else{s&&u.push(s);let S=c;for(let p=Math.max(0,c-e+1);p<=c;p++)if(r[p]){S=p;break}s={fire:c,startTs:n[S].timestamp,endTs:n[c].timestamp}}l=c}return s&&u.push(s),u}function Fe(n,e){let t=new Set(n.map(r=>r.fire)),i=n.slice();for(let r of e)t.has(r.fire)||i.push(r);return i.sort((r,o)=>r.fire-o.fire),i}self.onmessage=n=>{var t,i;let e=n.data;if(e.type==="series"){K=e.series;return}if(e.type==="run"&&K){let r=e.params,o=oe(K,r);qe(o,r.direction);let u=K.intervalSeconds*1e3,s=ze(o,u,r.consecutiveAnomalies),l=(t=e.anomalyWindowPoints)!=null?t:null,a=(i=e.minAnomalyShare)!=null?i:null;l!==null&&a!==null&&l>=2&&a>0&&(s=Fe(s,Re(o,l,a)));let c=s.map(y=>y.fire),S=s.map(y=>[y.startTs,y.endTs]),p=ie(K,r),v=Math.min(p,K.timestamps.length),f=0;for(let y of o)y.scored&&y.isAnomaly&&f++;self.postMessage({type:"result",id:e.id,scored:o,fires:c,fireSpans:S,eff:v,need:p,flagged:f})}};})();
|
|
2
|
+
`],{type:"text/javascript"})),it,jt=0,o=!1,r=null,c=e=>{let i=e.data;if(i.type!=="result"||i.id!==jt||!r)return;o=!1,ot.classList.remove("on");let u=r;_e=i.scored,H=i.fires.map(v=>y.timestamps[v]),q=i.fireSpans,le=te(),X.render({series:y,scored:i.scored,params:u,alerts:le,incidents:k}),ft(),qn.textContent=`${i.flagged} flagged \xB7 ${i.fires.length} alert${i.fires.length===1?"":"s"} \xB7 warm-up ${i.need} pts`,Rn(u),Wn(i.need,u),Gn.textContent=Qo(u,O,bt(),Nt())},s=()=>{o=!1,ot.classList.remove("on"),qn.textContent="recompute failed \u2014 see the browser console"};function a(){it=new Worker(Hn),it.onmessage=c,it.onerror=s,it.postMessage({type:"series",series:y})}a();let m=()=>{r=Ie(),ie!=null&&be.set(ie,r),jt+=1,o&&(it.terminate(),a()),o=!0,ot.classList.add("on"),it.postMessage({type:"run",id:jt,params:r,anomalyWindowPoints:bt(),minAnomalyShare:Nt()})},f=e=>e>1?Yt(x.timestamps[t-1]-x.timestamps[t-e]):"\u2014",S=e=>{wt.textContent=e>=t?`${t} pts \xB7 full (${f(t)})`:`${e} pts \xB7 ${f(e)}`};S(t);function P(e){y=R(e),it.postMessage({type:"series",series:y}),kn(),_()}$e.oninput=()=>{S(Number($e.value))},$e.onchange=()=>{P(Number($e.value))};let A=0,_=()=>{A&&window.clearTimeout(A),A=window.setTimeout(m,130)},E=()=>{ge(),kn(),_()},J=ze.filter(e=>e.tunable),Te=ze.filter(e=>!e.tunable);if(ze.length>1){let e=p("div","dtk-ctl dtk-tune-detpick");if(e.appendChild(Kt("Tuning detector","This metric has several detectors. Pick which one to tune here; every other detector is preserved unchanged when you Apply \u2014 so a manual_bounds floor or a min_detectors\u22652 quorum keeps working.")),J.length>1){let u=p("div","dtk-seg dtk-detpick-seg"),v=[],M=()=>v.forEach(h=>h.classList.toggle("on",Number(h.dataset.v)===ie));J.forEach(h=>{let T=p("button","dtk-seg-btn",`#${h.index+1} ${h.type}`);T.type="button",T.dataset.v=String(h.index),T.title=h.summary,T.onclick=()=>{Ho(h.index),M()},v.push(T),u.appendChild(T)}),M(),e.appendChild(u)}let i=[];Te.length&&i.push(`Preserved (not tunable here): ${Te.map(u=>u.summary).join("; ")}.`),i.push("Apply rewrites only the detector(s) you tune and keeps the rest verbatim."),e.appendChild(p("div","dtk-tune-note",i.join(" "))),se.appendChild(e)}let K=Gt("Detector",[{label:"MAD",value:"mad"},{label:"Z-Score",value:"zscore"},{label:"IQR",value:"iqr"},{label:"Autoreg",value:"autoreg"},{label:"Manual",value:"manual_bounds"}],W.type,e=>{var i;_t((i=Xo[e])!=null?i:3),ge(),Ln(),kn(),_()},"The statistic for the band: MAD (robust median, default), Z-Score (mean/std) or IQR (quartiles) \u2014 all windowed \u2014 Autoreg (predicts each point from its previous values and flags dynamics breaks) or Manual (fixed lower/upper thresholds, no window/history).");se.appendChild(K.row);let C=at("Lower bound",{min:ne,max:de,step:Xt,value:hn,fmt:e=>fn(e),hint:"Manual bounds: values below this read as anomalous. Drag in from the data range to see how many points fall outside (and how many alerts that yields)."},E);se.appendChild(C.row);let z=at("Upper bound",{min:ne,max:de,step:Xt,value:lt,fmt:e=>fn(e),hint:"Manual bounds: values above this read as anomalous."},E);se.appendChild(z.row);let N=at("Threshold (\u03C3-equivalent)",{min:.5,max:10,step:.1,value:W.threshold,fmt:e=>e.toFixed(1),hint:"Band half-width in \u03C3-equivalents. Lower = tighter band = more flags; higher = wider band = fewer flags."},E);se.appendChild(N.row);let ae=N.row.querySelector("input"),Ne=N.row.querySelector(".dtk-ctl-val"),_t=e=>{ae&&(ae.value=String(e)),Ne&&(Ne.textContent=e.toFixed(1))},ro=(e,i,u,v,M)=>{let h=Math.floor(2*i/3)-(e==="autoreg"?u+(M?1:0):0),T=Math.floor(e==="autoreg"?h/(v?2:1):i/2);return Math.max(50,Math.min(2e3,T))},io=Math.max(ro(W.type,t,Math.max(1,Math.round((wo=W.lags)!=null?wo:5)),((xo=W.stabilization)!=null?xo:"none")==="clamp",W.inputType!=="values"),W.windowSize),Vt=at("Window size (points)",{min:Math.max(1,Math.min(10,W.windowSize)),max:io,step:1,value:W.windowSize,fmt:e=>`${e} \xB7 ${Yt(e*n.interval_seconds*1e3)}`,hint:"How many trailing points form the baseline window for each scored point. Larger = steadier baseline (more history); smaller = adapts faster to shifts."},E);se.appendChild(Vt.row);let dn=at("Lags (AR order)",{min:1,max:24,step:1,value:Math.max(1,Math.round((ko=W.lags)!=null?ko:5)),fmt:e=>String(e),hint:"Autoreg: how many immediately-preceding values predict the current one. More lags capture longer short-range patterns but need more history per fit."},E);se.appendChild(dn.row);let qt=Gt("Recency weighting",[{label:"none",value:"none"},{label:"exponential",value:"exponential"},{label:"linear",value:"linear"}],W.windowWeights,()=>{ge(),Ln(),_()},"Weight recent points in the window more heavily: none (flat), exponential (half-life decay) or linear. Helps the baseline track a drifting level.");se.appendChild(qt.row);let cn=at("Half-life (points)",{min:1,max:io,step:1,value:(Mo=W.halfLife)!=null?Mo:Math.max(5,Math.round(W.windowSize/20)),fmt:e=>`${e} \xB7 ${Yt(e*n.interval_seconds*1e3)}`,hint:"Exponential weighting only: the age (in points) at which a point counts half as much as the newest. Smaller = faster decay = fresher baseline."},E),On=cn.row;On.style.display=W.windowWeights==="exponential"?"":"none",se.appendChild(On);let kn=()=>{var i;let e=Math.max(ro(K.get(),y.timestamps.length,Math.max(1,Math.round(dn.get())),un.get()==="clamp",W.inputType!=="values"),W.windowSize,Vt.get());Vt.setMax(e),cn.setMax(Math.max(e,(i=W.halfLife)!=null?i:0,cn.get()))},Mn=Gt("Detrend",[{label:"none",value:"none"},{label:"linear",value:"linear"}],W.detrend,E,"Remove a robust linear trend from each window before computing the band, so a steadily rising/falling metric is not flagged for the trend itself.");se.appendChild(Mn.row);let un=Gt("Stabilization",[{label:"none",value:"none"},{label:"clamp",value:"clamp"}],(So=W.stabilization)!=null?So:"none",E,"Anomaly-robust baseline: flagged points enter later windows clamped to the bound they violated, so a long incident cannot inflate the band and mask itself mid-incident.");se.appendChild(un.row);let Sn=Gt("Smoothing",[{label:"none",value:"none"},{label:"EMA",value:"ema"},{label:"SMA",value:"sma"}],W.smoothing,E,"Smooth the series before detection (EMA or SMA) so single-point jitter does not flag. The detector judges the smoothed line; the raw values show as a faint ghost.");se.appendChild(Sn.row);let Bn=null,so=()=>{};if(n.seasonality_columns.length){let e=n.seasonality_columns,i=p("div","dtk-ctl");Bn=i,i.appendChild(Kt("Seasonality groups","Condition the band on seasonal keys. Pick a group per column: columns in the SAME group are combined into one key (e.g. dow\xD7hour); separate groups each apply their own correction. Off = ignore that column."));let u=e.length,v=[{label:"\u2014",value:"0"}];for(let h=1;h<=u;h++)v.push({label:`G${h}`,value:String(h)});let M=[];e.forEach(h=>{let T=p("div","dtk-season-row");T.appendChild(p("span","dtk-season-col",h));let me=p("div","dtk-seg dtk-season-seg"),we=[],Dt=()=>{var oe;return(oe=ve.get(h))!=null?oe:0},qe=()=>we.forEach(oe=>oe.classList.toggle("on",Number(oe.dataset.v)===Dt()));M.push(qe),v.forEach(oe=>{let Re=p("button","dtk-seg-btn",oe.label);Re.type="button",Re.dataset.v=oe.value,Re.title=oe.value==="0"?`ignore ${h}`:`put ${h} in group ${oe.value}`,Re.onclick=()=>{ve.set(h,Number(oe.value)),qe(),E()},we.push(Re),me.appendChild(Re)}),qe(),T.appendChild(me),i.appendChild(T)}),se.appendChild(i),so=h=>{ve.clear(),h.forEach((T,me)=>T.forEach(we=>ve.set(we,me+1))),M.forEach(T=>T())}}let ao=Gt("Direction",[{label:"both",value:"any"},{label:"up",value:"up"},{label:"down",value:"down"}],(Co=n.direction)!=null?Co:"any",_,"Which anomalies to show and count toward alerts: both directions, only spikes ABOVE the band (up) or only drops BELOW it (down). A preview filter mirroring the alert direction policy \u2014 it never changes the band itself.");ut.appendChild(ao.row);let lo=at("Alert: consecutive anomalies",{min:1,max:10,step:1,value:O,fmt:e=>String(e),hint:"How many anomalies in a row are required before an alert fires. Higher = fewer, more-confident alerts (the \u25BC markers on the chart)."},e=>{O=e,_()});ut.appendChild(lo.row);let Cn=at("Alert: anomaly window (points)",{min:0,max:Math.max(96,ke),step:1,value:ke,fmt:e=>e>=2?`${e} \xB7 ${Yt(e*n.interval_seconds*1e3)}`:"off",hint:"Fraction rule (OR-ed with the consecutive rule): also fire when at least the share below of the trailing window is anomalous AND the latest point is anomalous. Set to off (< 2) for the classic consecutive-only alert."},e=>{ke=e,jn(),_()});ut.appendChild(Cn.row);let Fn=at("Alert: min share in window",{min:.05,max:1,step:.05,value:pe,fmt:e=>`${Math.round(e*100)}%`,hint:"Fraction rule: the share of the anomaly window that must be flagged for the alert to fire. Missing points count against the share (an outage never makes it easier)."},e=>{pe=e,_()});ut.appendChild(Fn.row);let jn=()=>{Fn.row.style.display=ke>=2?"":"none"};jn();let co=p("div","dtk-ctl"),Tn=p("label","dtk-check"),An=p("input");An.type="checkbox",An.onchange=()=>{X.setZeroLine(An.checked)},Tn.title="Draw a horizontal line at y = 0 and include zero in the scale \u2014 for real-valued metrics best read relative to zero.",Tn.appendChild(An),Tn.appendChild(document.createTextNode(" Show y = 0 line")),co.appendChild(Tn),ut.appendChild(co);let Vn=p("div","dtk-ctl dtk-incidents");Vn.appendChild(Kt("Marked incidents","The real incidents you marked (in Label mode). Edit a label, focus to zoom the chart to it, or remove it. Save the set below to incidents/<metric>/ \u2014 the same store dtk autotune reads."));let _n=p("div","dtk-inc-list");Vn.appendChild(_n),Be.appendChild(Vn);function Eo(e){let i=Math.max((e.end-e.start)*.5,n.interval_seconds*1e3*5);X.setViewWindow(e.start-i,e.end+i)}function Ro(e){let i=k.indexOf(e);i>=0&&k.splice(i,1);let u=$(e);X.setIncidents(k),u?Pe():(Dn(),ft())}function Wo(e){d(e.start,e.end,null),X.setIncidents(k),Pe()}function Dn(){_n.innerHTML="";let e=[...k.map(i=>({iv:i,kind:"manual"})),...Q().map(i=>({iv:i,kind:"alert"}))].sort((i,u)=>i.iv.start-u.iv.start);if(!e.length){_n.appendChild(p("div","dtk-inc-empty","None yet \u2014 switch to Label mode and drag across the chart, or confirm alerts in Review mode."));return}for(let{iv:i,kind:u}of e){let v=p("div","dtk-inc-row"+(u==="alert"?" dtk-inc-fromalert":""));if(v.appendChild(p("span","dtk-inc-span",`${no(i.start)} \u2192 ${no(i.end)}`)),v.appendChild(p("span","dtk-inc-dur",Yt(Math.max(0,i.end-i.start)))),u==="alert"){let T=p("span","dtk-inc-badge","\u2713 confirmed alert");T.title="A fired alert you confirmed valid (in Review mode). It counts as a real incident \u2014 remove it here to un-confirm the alert.",v.appendChild(T)}else{let T=p("input","dtk-inc-label");T.type="text",T.value=i.label||"",T.placeholder="label (optional)",T.oninput=()=>{i.label=T.value},v.appendChild(T)}let M=p("button","dtk-inc-btn","focus");M.type="button",M.onclick=()=>Eo(i),v.appendChild(M);let h=p("button","dtk-inc-btn dtk-inc-del","\u2715");h.type="button",h.title=u==="alert"?"un-confirm this alert":"remove this incident",h.onclick=()=>u==="alert"?Wo(i):Ro(i),v.appendChild(h),_n.appendChild(v)}}let Io=[N.row,Vt.row,un.row],uo=[qt.row,Mn.row,Sn.row];Bn&&uo.push(Bn);function Ln(){let e=K.get(),i=e==="manual_bounds",u=e==="autoreg";for(let v of Io)v.style.display=i?"none":"";for(let v of uo)v.style.display=i||u?"none":"";dn.row.style.display=u?"":"none",C.row.style.display=i?"":"none",z.row.style.display=i?"":"none",On.style.display=!i&&!u&&qt.get()==="exponential"?"":"none"}Ln();function mo(e){var i,u;W=e,K.set(e.type),N.set(e.threshold),kn(),Vt.set(e.windowSize),qt.set(e.windowWeights),e.windowWeights==="exponential"&&e.halfLife!=null&&cn.set(e.halfLife),Mn.set(e.detrend),un.set((i=e.stabilization)!=null?i:"none"),Sn.set(e.smoothing),so(e.seasonalityComponents||[]),e.lowerBound!=null&&C.set(e.lowerBound),e.upperBound!=null&&z.set(e.upperBound),(e.lags!=null||e.type==="autoreg")&&dn.set(Math.max(1,Math.round((u=e.lags)!=null?u:5))),Ln()}function Ho(e){var u;if(e===ie)return;let i=ze.find(v=>v.index===e);!i||!i.tunable||!i.seed||(ie!=null&&be.set(ie,Ie()),ie=e,mo((u=be.get(e))!=null?u:i.seed),_())}let qn=p("div","dtk-tune-stat");ct.appendChild(qn);let Un=p("div","dtk-tune-cfg"),mn=p("button","dtk-tune-cfg-k");mn.type="button",mn.title="Show or hide the exact config that will be written on Apply.";let Gn=p("code","dtk-tune-cfg-v"),po=!1,fo=e=>{po=e,Gn.style.display=e?"":"none",mn.textContent=`${e?"\u25BE":"\u25B8"} // effective config`};if(mn.onclick=()=>fo(!po),Un.appendChild(mn),Un.appendChild(Gn),fo(!1),yt.appendChild(Un),n.save_url){let e=p("div","dtk-tune-apply"),i=p("button","dtk-apply-btn","Apply to metric");i.type="button",i.title="Write this detector config back into the metric YAML (the previous version is archived under metrics/.history/). Trimming the sample does not change what is written.";let u=p("span","dtk-apply-msg");i.onclick=()=>{let v=Ie();ie!=null&&be.set(ie,v);let M=[];if(Me!=null)for(let h of new Set([Me,...Se])){let T=be.get(h);T&&M.push({index:h,type:T.type,params:oo(T)})}else M.push({index:null,type:v.type,params:oo(v)});i.disabled=!0,u.className="dtk-apply-msg info",u.textContent="Applying\u2026",fetch(n.save_url,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detectors:M,consecutive_anomalies:O,anomaly_window:bt()?`${bt()*n.interval_seconds}s`:null,min_anomaly_share:Nt()})}).then(h=>h.ok?h.json():h.text().then(T=>{throw new Error(T||`HTTP ${h.status}`)})).then(h=>{var me;u.className="dtk-apply-msg ok";let T=h.preserved&&h.preserved.length?` Kept: ${h.preserved.join(", ")}.`:"";u.textContent=`Applied \u2192 ${(me=h.saved)!=null?me:"metric"} (previous archived).${T} You can close this tab.`}).catch(h=>{i.disabled=!1,u.className="dtk-apply-msg err",u.textContent=`Apply failed: ${h.message}`})},e.appendChild(i),e.appendChild(u),yt.appendChild(e)}else yt.appendChild(p("div","dtk-tune-note","Static preview \u2014 sliders recompute live, but there is no write-back. Run `dtk tune` (without --no-serve) to apply a config."));let Oo=e=>'"'+e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')+'"',Ut=e=>new Date(e).toISOString().slice(0,19).replace("T"," "),ho=()=>{let e=[`metric: ${n.metric}`,"timezone: UTC"],i=new Set,u=he().sort((M,h)=>M.start-h.start).filter(M=>{let h=`${Math.round(M.start)}:${Math.round(M.end)}`;return i.has(h)?!1:(i.add(h),!0)});if(!u.length)e.push("incidents: []");else{e.push("incidents:");for(let M of u){let h=M.label?`, label: ${Oo(M.label)}`:"";e.push(` - {start: "${Ut(M.start)}", end: "${Ut(M.end)}"${h}}`)}}let v=X.getCaptureWindow();if(v&&(e.push("capture_windows:"),e.push(` - {start: "${Ut(v.start)}", end: "${Ut(v.end)}"}`)),Z.length){e.push("alert_reviews:");for(let M of[...Z].sort((h,T)=>h.start-T.start))e.push(` - {start: "${Ut(M.start)}", end: "${Ut(M.end)}", verdict: ${M.verdict}}`)}return e.join(`
|
|
3
|
+
`)+`
|
|
4
|
+
`},$n=p("div","dtk-tune-apply"),Pn=p("input","dtk-setname");Pn.type="text",Pn.placeholder="name this set (optional)";let Nn=p("button","dtk-apply-btn dtk-labels-btn",n.labels_save_url?"Save incidents":"Download incidents");Nn.type="button",Nn.title="Write the marked incidents to incidents/<metric>/ (versioned) so they also feed the next `dtk autotune`. Saving labels does not end tuning \u2014 keep adjusting and apply the detector when ready.";let st=p("span","dtk-apply-msg");Nn.onclick=()=>{let e=ho();if(!n.labels_save_url){let i=new Blob([e],{type:"text/yaml"}),u=p("a");u.href=URL.createObjectURL(i),u.download=`${n.metric}.yml`,u.click(),URL.revokeObjectURL(u.href),st.className="dtk-apply-msg ok",st.textContent=`Downloaded \u2014 drop it into incidents/${n.metric}/`;return}st.className="dtk-apply-msg info",st.textContent="Saving\u2026",fetch(n.labels_save_url,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:Pn.value,yaml:e})}).then(i=>i.ok?i.json():i.text().then(u=>{throw new Error(u||`HTTP ${i.status}`)})).then(i=>{var u;st.className="dtk-apply-msg ok",st.textContent=`Saved \u2192 ${(u=i.saved)!=null?u:"incidents"} (keep tuning, or Apply the detector)`}).catch(i=>{st.className="dtk-apply-msg err",st.textContent=`Save failed: ${i.message}`})},$n.appendChild(Pn),$n.appendChild(Nn),$n.appendChild(st),Be.appendChild($n);function Bo(e){mo(e.detector),ge(),e.consecutive_anomalies!=null&&(O=e.consecutive_anomalies,lo.set(O)),e.anomaly_window_points!=null&&e.min_anomaly_share!=null?(ke=e.anomaly_window_points,pe=e.min_anomaly_share,Cn.setMax(Math.max(96,ke)),Cn.set(ke),Fn.set(pe)):(ke=0,Cn.set(0)),jn(),_()}if(Fe.appendChild(p("div","dtk-tune-note","Run the full autotune engine server-side over the window shown (the \u2018Points shown\u2019 trim \u2014 the same series you see and score here), using the incidents you\u2019ve marked (and confirmed alerts) as ground truth. It re-seeds the knobs with the winning detector \u2014 review the band, then Apply (here or in Tune). Nothing is written until you Apply; the next `dtk run` is the source of truth.")),n.autotune_url){let e=p("div","dtk-tune-apply"),i=p("button","dtk-apply-btn","Run autotune");i.type="button",i.title="Search for the best detector over the window shown (the \u2018Points shown\u2019 trim). Uses your marked incidents as ground truth when present (supervised), else an unsupervised objective. Can take a few seconds on a long window.";let u=p("span","dtk-apply-msg");e.appendChild(i),e.appendChild(u),Fe.appendChild(e);let v=p("div","dtk-at-result");v.style.display="none",Fe.appendChild(v);let M=h=>{v.innerHTML="",v.style.display="";let T=p("div","dtk-at-head");T.appendChild(p("span","dtk-at-winner",h.winner)),T.appendChild(p("span","dtk-at-score",`${h.scoring_metric} ${Number.isFinite(h.score)?h.score.toFixed(3):"\u2014"} \xB7 ${h.mode}`)),v.appendChild(T),v.appendChild(p("div","dtk-at-meta",`${h.n_candidates} candidate${h.n_candidates===1?"":"s"} \xB7 ${h.n_points} pts`+(h.consecutive_anomalies!=null?` \xB7 consecutive ${h.consecutive_anomalies}`:"")+(h.anomaly_window_points!=null&&h.min_anomaly_share!=null?` \xB7 window ${h.anomaly_window_points}p \xD7 ${h.min_anomaly_share}`:"")+(h.seasonality&&h.seasonality.length?` \xB7 seasonality ${JSON.stringify(h.seasonality)}`:" \xB7 no seasonality")));let me=p("button","dtk-tune-cfg-k");me.type="button";let we=p("div","dtk-at-log"),Dt=!1,qe=oe=>{Dt=oe,we.style.display=oe?"":"none",me.textContent=`${oe?"\u25BE":"\u25B8"} decision log (${h.decision_log.length})`};me.onclick=()=>qe(!Dt);for(let oe of h.decision_log){let Re=p("div","dtk-at-logline");Re.appendChild(p("span","dtk-at-stage",oe.stage)),Re.appendChild(p("span","dtk-at-msg",oe.message)),we.appendChild(Re)}qe(!1),v.appendChild(me),v.appendChild(we)};i.onclick=()=>{i.disabled=!0,u.className="dtk-apply-msg info",u.textContent="Autotuning over the shown window\u2026 this can take a moment.",fetch(n.autotune_url,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({yaml:ho(),window:y.timestamps.length?{start:y.timestamps[0],end:y.timestamps[y.timestamps.length-1]}:null})}).then(h=>h.ok?h.json():h.text().then(T=>{throw new Error(T||`HTTP ${h.status}`)})).then(h=>{i.disabled=!1,Bo(h),M(h);let T=h.mode==="supervised"?" \u2014 supervised by your incidents":" \u2014 unsupervised (mark incidents for a supervised tune)";u.className="dtk-apply-msg ok",u.textContent=`Tuned${T}. Knobs updated; review the band, then Apply.`}).catch(h=>{i.disabled=!1,u.className="dtk-apply-msg err",u.textContent=`Autotune failed: ${h.message}`})}}else Fe.appendChild(p("div","dtk-tune-note","Autotune needs the live server \u2014 run `dtk tune` without --no-serve."));xn("tune"),Dn(),m(),ft();let Yn=0,bo=()=>{Yn&&cancelAnimationFrame(Yn),Yn=requestAnimationFrame(()=>X.resize())};typeof ResizeObserver!="undefined"?new ResizeObserver(bo).observe(nt):window.addEventListener("resize",bo)}function fn(n){if(!Number.isFinite(n))return"\u2014";let l=Math.abs(n);return l!==0&&(l<.01||l>=1e6)?n.toExponential(2):Number(n.toFixed(l<1?4:2)).toString()}function no(n){return new Date(n).toISOString().slice(0,16).replace("T"," ")}function Yt(n){let l=Math.round(n/6e4);if(l<60)return`${l}m`;let g=Math.floor(l/60);if(g<24)return`${g}h`;let t=Math.floor(g/24),x=g%24;return x?`${t}d ${x}h`:`${t}d`}function tr(n){return n%86400===0?`${n/86400}d`:n%3600===0?`${n/3600}h`:n%60===0?`${n/60}min`:`${n}s`}var zo=!1;function nr(){if(zo)return;zo=!0;let n=`
|
|
5
|
+
.dtk-tune{--c:#d15b36;--c7:#b4471f;--ink:#1b1916;--muted:#6e675b;--faint:#9a9384;
|
|
6
|
+
--paper:#f5f1e8;--surface:#fbf9f3;--border:#e6e0d4;--green:#2e9e73;--anom:#d63232;
|
|
7
|
+
--mono:'JetBrains Mono',ui-monospace,Menlo,monospace;
|
|
8
|
+
--sans:'Schibsted Grotesk',system-ui,-apple-system,Segoe UI,Roboto,sans-serif;}
|
|
9
|
+
.dtk-tune-root{max-width:1680px;margin:0 auto;padding:12px 16px;font-family:var(--sans);color:var(--ink);
|
|
10
|
+
height:100dvh;display:flex;flex-direction:column;gap:10px;overflow:hidden;}
|
|
11
|
+
.dtk-tune-header{flex:0 0 auto;}
|
|
12
|
+
.dtk-tune-titlerow{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;}
|
|
13
|
+
.dtk-tune-title{font-size:19px;margin:0;font-weight:700;}
|
|
14
|
+
.dtk-tune-badge{font-family:var(--mono);font-size:10px;text-transform:uppercase;letter-spacing:.06em;
|
|
15
|
+
color:#fff;background:var(--c);border-radius:999px;padding:3px 9px;}
|
|
16
|
+
.dtk-tune-sub{color:var(--muted);font-size:12px;margin-top:2px;font-family:var(--mono);}
|
|
17
|
+
.dtk-tune-desc{color:var(--muted);font-size:12px;margin-top:3px;white-space:pre-wrap;max-height:2.6em;overflow:auto;}
|
|
18
|
+
/* cockpit: chart-windshield (stage) + always-visible mode-aware control rail */
|
|
19
|
+
.dtk-tune-cockpit{display:flex;gap:12px;flex:1;min-height:0;}
|
|
20
|
+
.dtk-tune-stage{position:relative;display:flex;flex-direction:column;gap:8px;flex:1;min-width:0;min-height:0;}
|
|
21
|
+
.dtk-tune-hud{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;flex:0 0 auto;}
|
|
22
|
+
.dtk-tune-stagefoot{flex:0 0 auto;display:flex;flex-direction:column;gap:6px;}
|
|
23
|
+
.dtk-tune-rail{flex:0 0 340px;display:flex;flex-direction:column;min-height:0;background:var(--surface);
|
|
24
|
+
border:1px solid var(--border);border-radius:12px;overflow:hidden;}
|
|
25
|
+
.dtk-tune-railhead{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:8px;
|
|
26
|
+
padding:9px 12px;border-bottom:1px solid var(--border);}
|
|
27
|
+
.dtk-rail-title{font-family:var(--mono);font-size:11px;color:var(--muted);text-transform:uppercase;
|
|
28
|
+
letter-spacing:.06em;flex:1 1 auto;}
|
|
29
|
+
.dtk-tune-railfoot{flex:0 0 auto;display:flex;flex-direction:column;gap:8px;padding:11px 12px;
|
|
30
|
+
border-top:1px solid var(--border);background:var(--paper);}
|
|
31
|
+
.dtk-rail-open{position:absolute;top:50%;right:6px;transform:translateY(-50%);z-index:6;
|
|
32
|
+
border:1px solid var(--border);background:var(--surface);color:var(--ink);border-radius:8px;
|
|
33
|
+
padding:13px 7px;font-size:15px;cursor:pointer;box-shadow:0 1px 6px rgba(27,25,22,.14);}
|
|
34
|
+
.dtk-rail-open:hover{border-color:var(--c);color:var(--c7);}
|
|
35
|
+
.dtk-dock-toggle{flex:0 0 auto;border:1px solid var(--border);background:var(--surface);color:var(--muted);
|
|
36
|
+
border-radius:7px;padding:4px 10px;font-family:var(--sans);font-size:13px;font-weight:700;cursor:pointer;line-height:1;}
|
|
37
|
+
.dtk-dock-toggle:hover{border-color:var(--c);color:var(--c7);}
|
|
38
|
+
.dtk-tune-controls{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:14px;padding:14px;}
|
|
39
|
+
.dtk-rail-group{display:flex;flex-direction:column;gap:14px;}
|
|
40
|
+
.dtk-ctl{display:flex;flex-direction:column;gap:6px;}
|
|
41
|
+
.dtk-ctl-head{display:flex;justify-content:space-between;align-items:baseline;}
|
|
42
|
+
.dtk-ctl-label{font-size:12px;font-weight:600;color:var(--ink);}
|
|
43
|
+
.dtk-ctl-val{font-family:var(--mono);font-size:12px;color:var(--c7);}
|
|
44
|
+
.dtk-seg{display:flex;gap:4px;background:var(--paper);border:1px solid var(--border);border-radius:8px;padding:3px;}
|
|
45
|
+
.dtk-seg.dtk-wrap{flex-wrap:wrap;}
|
|
46
|
+
.dtk-seg-btn{flex:1 1 auto;border:0;background:transparent;color:var(--muted);font-family:var(--sans);
|
|
47
|
+
font-size:12px;padding:5px 8px;border-radius:6px;cursor:pointer;white-space:nowrap;}
|
|
48
|
+
.dtk-seg-btn:hover{color:var(--ink);}
|
|
49
|
+
.dtk-seg-btn.on{background:var(--c);color:#fff;font-weight:600;}
|
|
50
|
+
.dtk-range{width:100%;accent-color:var(--c);cursor:pointer;}
|
|
51
|
+
.dtk-check{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--muted);margin-top:2px;cursor:pointer;}
|
|
52
|
+
.dtk-tune-chart{position:relative;width:100%;flex:1;min-height:220px;background:var(--surface);
|
|
53
|
+
border:1px solid var(--border);border-radius:12px;overflow:hidden;}
|
|
54
|
+
.dtk-tune-chart canvas{width:100%;height:100%;display:block;}
|
|
55
|
+
.dtk-tune-readout{font-family:var(--mono);font-size:12px;color:var(--muted);min-height:18px;}
|
|
56
|
+
.dtk-tune-stat{font-family:var(--mono);font-size:12px;color:var(--ink);}
|
|
57
|
+
.dtk-tune-warn{font-family:var(--mono);font-size:12px;line-height:1.5;color:var(--c7);
|
|
58
|
+
background:rgba(240,173,78,0.13);border:1px solid rgba(240,173,78,0.5);border-radius:8px;padding:8px 11px;}
|
|
59
|
+
.dtk-tune-cfg{background:var(--ink);color:#c9c2b4;border-radius:8px;padding:8px 11px;font-family:var(--mono);
|
|
60
|
+
font-size:12px;overflow-x:auto;}
|
|
61
|
+
.dtk-tune-cfg-k{display:flex;width:100%;border:0;background:transparent;color:var(--faint);
|
|
62
|
+
font-family:var(--mono);font-size:11.5px;cursor:pointer;padding:0;text-align:left;}
|
|
63
|
+
.dtk-tune-cfg-k:hover{color:#e6e0d4;}
|
|
64
|
+
.dtk-tune-cfg-v{display:block;color:#e6e0d4;white-space:pre-wrap;word-break:break-word;margin-top:6px;}
|
|
65
|
+
.dtk-tune-apply{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
|
|
66
|
+
.dtk-apply-btn{background:var(--c);color:#fff;border:0;border-radius:8px;padding:10px 18px;font-family:var(--sans);
|
|
67
|
+
font-size:14px;font-weight:600;cursor:pointer;}
|
|
68
|
+
.dtk-apply-btn:hover{background:var(--c7);}
|
|
69
|
+
.dtk-apply-btn:disabled{opacity:.55;cursor:default;}
|
|
70
|
+
.dtk-apply-msg{font-size:13px;}
|
|
71
|
+
.dtk-apply-msg.ok{color:var(--green);}
|
|
72
|
+
.dtk-apply-msg.err{color:var(--anom);}
|
|
73
|
+
.dtk-apply-msg.info{color:var(--muted);}
|
|
74
|
+
.dtk-tune-note{font-size:13px;color:var(--muted);background:var(--surface);border:1px dashed var(--border);
|
|
75
|
+
border-radius:8px;padding:10px 12px;}
|
|
76
|
+
.dtk-ctl-info{color:var(--faint);font-size:10px;cursor:help;vertical-align:super;}
|
|
77
|
+
.dtk-tune-trim{display:flex;flex-direction:column;gap:6px;background:var(--surface);
|
|
78
|
+
border:1px solid var(--border);border-radius:10px;padding:9px 12px;}
|
|
79
|
+
.dtk-tune-trim-head{display:flex;justify-content:space-between;align-items:baseline;}
|
|
80
|
+
.dtk-tune-trim-val{font-family:var(--mono);font-size:12px;color:var(--c7);}
|
|
81
|
+
.dtk-tune-spin{position:absolute;top:10px;right:12px;display:none;align-items:center;gap:7px;
|
|
82
|
+
background:rgba(27,25,22,0.78);color:#e6e0d4;border:1px solid #332f29;border-radius:999px;
|
|
83
|
+
padding:4px 11px 4px 8px;font-family:var(--mono);font-size:11px;pointer-events:none;}
|
|
84
|
+
.dtk-tune-spin.on{display:inline-flex;}
|
|
85
|
+
.dtk-spin-ring{width:12px;height:12px;border-radius:50%;border:2px solid rgba(245,241,232,0.25);
|
|
86
|
+
border-top-color:var(--c);animation:dtk-spin .7s linear infinite;}
|
|
87
|
+
@keyframes dtk-spin{to{transform:rotate(360deg);}}
|
|
88
|
+
.dtk-tune-legend{flex:0 0 auto;display:flex;align-items:center;flex-wrap:wrap;gap:8px 16px;font-size:12px;
|
|
89
|
+
color:var(--muted);padding:6px 12px;background:var(--surface);border:1px solid var(--border);border-radius:9px;}
|
|
90
|
+
.dtk-leg-item{display:inline-flex;align-items:center;gap:6px;cursor:help;}
|
|
91
|
+
.dtk-leg-sw{display:inline-block;flex:0 0 auto;}
|
|
92
|
+
.dtk-leg-sw.line{width:16px;height:3px;background:var(--c);border-radius:2px;}
|
|
93
|
+
.dtk-leg-sw.band{width:16px;height:11px;background:rgba(209,91,54,0.18);
|
|
94
|
+
border:1px solid rgba(209,91,54,0.5);border-radius:2px;}
|
|
95
|
+
.dtk-leg-sw.center{width:16px;height:2px;
|
|
96
|
+
background:repeating-linear-gradient(90deg,var(--faint) 0 4px,transparent 4px 7px);}
|
|
97
|
+
.dtk-leg-sw.dot{width:9px;height:9px;border-radius:50%;background:var(--anom);}
|
|
98
|
+
.dtk-leg-sw.alert{width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;
|
|
99
|
+
border-top:7px solid var(--anom);}
|
|
100
|
+
.dtk-leg-sw.alert-ok{width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;
|
|
101
|
+
border-top:7px solid var(--green);}
|
|
102
|
+
.dtk-leg-sw.alert-no{width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;
|
|
103
|
+
border-top:7px solid #5a7a8c;}
|
|
104
|
+
.dtk-leg-txt{white-space:nowrap;}
|
|
105
|
+
.dtk-season-row{display:flex;align-items:center;justify-content:space-between;gap:8px;}
|
|
106
|
+
.dtk-season-col{font-family:var(--mono);font-size:11.5px;color:var(--muted);
|
|
107
|
+
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
108
|
+
.dtk-season-seg{flex:0 0 auto;padding:2px;}
|
|
109
|
+
.dtk-season-seg .dtk-seg-btn{flex:0 0 auto;padding:3px 7px;font-family:var(--mono);font-size:11px;}
|
|
110
|
+
.dtk-tune-metrics{display:flex;flex-wrap:wrap;gap:8px;margin:0;flex:0 1 auto;}
|
|
111
|
+
.dtk-m-chip{display:inline-flex;align-items:center;gap:7px;padding:7px 13px;background:var(--surface);
|
|
112
|
+
border:1px solid var(--border);border-radius:10px;font-size:13px;}
|
|
113
|
+
.dtk-m-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;}
|
|
114
|
+
.dtk-m-v{font-family:var(--mono);font-weight:700;font-size:15px;color:var(--ink);}
|
|
115
|
+
.dtk-m-l{color:var(--faint);font-family:var(--mono);font-size:11px;text-transform:uppercase;letter-spacing:.05em;}
|
|
116
|
+
.dtk-m-sub{color:var(--muted);font-family:var(--mono);font-size:11.5px;}
|
|
117
|
+
.dtk-m-chip.over{border-color:var(--anom);box-shadow:inset 0 0 0 1px rgba(214,50,50,.5);}
|
|
118
|
+
.dtk-m-chip.over .dtk-m-sub{color:var(--anom);font-weight:600;}
|
|
119
|
+
.dtk-tune-modes{display:inline-flex;gap:4px;background:var(--ink);border-radius:9px;padding:4px;margin:0;flex:0 0 auto;}
|
|
120
|
+
.dtk-mode-btn{border:0;background:transparent;color:#c9c2b4;font-family:var(--sans);font-size:13px;font-weight:600;
|
|
121
|
+
padding:7px 16px;border-radius:6px;cursor:pointer;transition:background .12s,color .12s;}
|
|
122
|
+
.dtk-mode-btn:hover{color:#fff;}
|
|
123
|
+
.dtk-mode-btn.on{background:var(--c);color:#fff;}
|
|
124
|
+
.dtk-tune-reviewbar{align-items:center;}
|
|
125
|
+
.dtk-tune-reviewbar .dtk-apply-btn{background:var(--green);}
|
|
126
|
+
.dtk-tune-reviewbar .dtk-apply-btn:hover{background:#27815d;}
|
|
127
|
+
.dtk-at-result{display:flex;flex-direction:column;gap:7px;background:var(--surface);
|
|
128
|
+
border:1px solid var(--border);border-radius:10px;padding:11px 13px;}
|
|
129
|
+
.dtk-at-head{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:8px;}
|
|
130
|
+
.dtk-at-winner{font-family:var(--mono);font-size:12.5px;font-weight:700;color:var(--ink);}
|
|
131
|
+
.dtk-at-score{font-family:var(--mono);font-size:11.5px;color:var(--c7);}
|
|
132
|
+
.dtk-at-meta{font-family:var(--mono);font-size:11px;color:var(--muted);word-break:break-word;}
|
|
133
|
+
.dtk-at-log{display:flex;flex-direction:column;gap:5px;max-height:240px;overflow:auto;margin-top:4px;}
|
|
134
|
+
.dtk-at-logline{display:flex;gap:8px;align-items:baseline;}
|
|
135
|
+
.dtk-at-stage{flex:0 0 auto;font-family:var(--mono);font-size:9.5px;text-transform:uppercase;
|
|
136
|
+
letter-spacing:.05em;color:#fff;background:var(--c);border-radius:4px;padding:1px 6px;}
|
|
137
|
+
.dtk-at-msg{font-family:var(--mono);font-size:11px;color:var(--ink);}
|
|
138
|
+
.dtk-th{display:flex;flex-direction:column;gap:8px;margin:2px 0 6px;}
|
|
139
|
+
.dtk-th-toggles{display:flex;gap:8px;flex-wrap:wrap;}
|
|
140
|
+
.dtk-th-toggle{align-self:flex-start;border:1px solid var(--border);background:var(--surface);
|
|
141
|
+
color:var(--muted);border-radius:8px;padding:6px 12px;font-family:var(--sans);font-size:12.5px;cursor:pointer;}
|
|
142
|
+
.dtk-th-toggle:hover{border-color:var(--c);color:var(--c7);}
|
|
143
|
+
.dtk-th-toggle.on{background:var(--c);border-color:var(--c);color:#fff;}
|
|
144
|
+
.dtk-th-bar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:10px 14px;padding:11px 13px;
|
|
145
|
+
background:var(--surface);border:1px solid var(--border);border-radius:10px;}
|
|
146
|
+
.dtk-th-grp{display:flex;flex-direction:column;gap:3px;}
|
|
147
|
+
.dtk-th-lbl{font-family:var(--mono);font-size:10.5px;color:var(--faint);text-transform:uppercase;letter-spacing:.05em;}
|
|
148
|
+
.dtk-th-num,.dtk-th-sel{background:var(--paper);color:var(--ink);border:1px solid var(--border);
|
|
149
|
+
border-radius:6px;padding:5px 8px;font-family:var(--mono);font-size:12px;}
|
|
150
|
+
.dtk-th-num{width:96px;}
|
|
151
|
+
.dtk-th-num:focus,.dtk-th-sel:focus{outline:none;border-color:var(--c);}
|
|
152
|
+
.dtk-th-scope{font-family:var(--mono);font-size:11px;color:var(--muted);align-self:center;flex:1 1 160px;}
|
|
153
|
+
.dtk-th-add{padding:7px 14px;}
|
|
154
|
+
.dtk-th-add:disabled{opacity:.5;cursor:default;}
|
|
155
|
+
.dtk-incidents{gap:8px;}
|
|
156
|
+
.dtk-inc-list{display:flex;flex-direction:column;gap:6px;max-height:240px;overflow:auto;}
|
|
157
|
+
.dtk-inc-empty{font-size:12px;color:var(--faint);font-style:italic;}
|
|
158
|
+
.dtk-inc-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap;background:var(--paper);
|
|
159
|
+
border:1px solid var(--border);border-radius:7px;padding:6px 8px;}
|
|
160
|
+
.dtk-inc-span{font-family:var(--mono);font-size:10.5px;color:var(--ink);}
|
|
161
|
+
.dtk-inc-dur{font-family:var(--mono);font-size:10.5px;color:var(--muted);}
|
|
162
|
+
.dtk-inc-label{flex:1 1 90px;min-width:70px;background:var(--surface);color:var(--ink);
|
|
163
|
+
border:1px solid var(--border);border-radius:5px;padding:4px 7px;font-family:var(--sans);font-size:11.5px;}
|
|
164
|
+
.dtk-inc-label:focus{outline:none;border-color:var(--c);}
|
|
165
|
+
.dtk-inc-btn{border:1px solid var(--border);background:var(--surface);color:var(--muted);border-radius:6px;
|
|
166
|
+
padding:3px 8px;font-size:11px;cursor:pointer;font-family:var(--sans);}
|
|
167
|
+
.dtk-inc-btn:hover{border-color:var(--c);color:var(--c7);}
|
|
168
|
+
.dtk-inc-del{color:var(--anom);}
|
|
169
|
+
.dtk-inc-fromalert{border-color:rgba(46,158,115,.5);background:rgba(46,158,115,.08);}
|
|
170
|
+
.dtk-inc-badge{flex:1 1 90px;min-width:70px;font-family:var(--mono);font-size:10.5px;color:var(--green);
|
|
171
|
+
display:inline-flex;align-items:center;font-weight:600;}
|
|
172
|
+
.dtk-setname{background:var(--surface);color:var(--ink);border:1px solid var(--border);border-radius:8px;
|
|
173
|
+
padding:9px 11px;font-family:var(--sans);font-size:13px;min-width:180px;}
|
|
174
|
+
.dtk-setname::placeholder{color:var(--faint);}
|
|
175
|
+
.dtk-setname:focus{outline:none;border-color:var(--c);}
|
|
176
|
+
.dtk-labels-btn{background:var(--surface);color:var(--ink);border:1px solid var(--border);}
|
|
177
|
+
.dtk-labels-btn:hover{background:var(--paper);border-color:var(--c);color:var(--c7);}
|
|
178
|
+
/* Narrow viewports: drop the cockpit to a scrolling stack (chart over rail). */
|
|
179
|
+
@media (max-width:900px){
|
|
180
|
+
.dtk-tune-root{height:auto;overflow:visible;}
|
|
181
|
+
.dtk-tune-cockpit{flex-direction:column;}
|
|
182
|
+
.dtk-tune-rail{flex:0 0 auto;width:100%;}
|
|
183
|
+
.dtk-tune-controls{overflow:visible;}
|
|
184
|
+
.dtk-tune-chart{flex:0 0 auto;height:54vh;min-height:320px;}
|
|
185
|
+
.dtk-rail-open{display:none!important;}
|
|
186
|
+
}
|
|
187
|
+
`,l=document.createElement("style");l.textContent=n,document.head.appendChild(l)}window.__DTK_TUNE__={render:er};})();
|
|
@@ -56,6 +56,10 @@ def default_window_points(seed_window: int) -> int:
|
|
|
56
56
|
# website demo's DETECTOR_THRESHOLD_DEFAULT). autoreg has no per-group entry —
|
|
57
57
|
# it has no seasonality.
|
|
58
58
|
_THRESHOLD_DEFAULT = {"mad": 3.0, "zscore": 3.0, "iqr": 1.5, "autoreg": 3.0}
|
|
59
|
+
# Per-type window defaults (mirror the detector class defaults: the windowed
|
|
60
|
+
# template's 100, autoreg's 200) — a bare ``type: autoreg`` config must open
|
|
61
|
+
# the cockpit at the window the pipeline actually runs, not the windowed default.
|
|
62
|
+
_WINDOW_SIZE_DEFAULT = {"mad": 100, "zscore": 100, "iqr": 100, "autoreg": 200}
|
|
59
63
|
# Per-type min-samples-per-group defaults (mirror the detector subclasses).
|
|
60
64
|
_MIN_SAMPLES_PER_GROUP_DEFAULT = {"mad": 10, "zscore": 3, "iqr": 4}
|
|
61
65
|
# Detector types the interactive tuner can seed/emit: the windowed statistical
|
|
@@ -136,7 +140,7 @@ def seed_detector_params(dtype: str, params: dict[str, Any]) -> dict[str, Any]:
|
|
|
136
140
|
seed: dict[str, Any] = {
|
|
137
141
|
"type": dtype,
|
|
138
142
|
"threshold": params.get("threshold", _THRESHOLD_DEFAULT.get(dtype, 3.0)),
|
|
139
|
-
"windowSize": params.get("window_size", 100),
|
|
143
|
+
"windowSize": params.get("window_size", _WINDOW_SIZE_DEFAULT.get(dtype, 100)),
|
|
140
144
|
"minSamples": params.get("min_samples", 30),
|
|
141
145
|
"inputType": params.get("input_type", "values"),
|
|
142
146
|
"smoothing": params.get("smoothing") or "none",
|