detectkit 0.53.1__tar.gz → 0.53.3__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.1/detectkit.egg-info → detectkit-0.53.3}/PKG-INFO +1 -1
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/__init__.py +1 -1
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/rules/cli.md +4 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/rules/detectors.md +3 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/skills/dtk-tune/SKILL.md +11 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/reporting/assets/report.js +1 -1
- detectkit-0.53.3/detectkit/tuning/assets/tune.js +187 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/tuning/payload.py +5 -1
- {detectkit-0.53.1 → detectkit-0.53.3/detectkit.egg-info}/PKG-INFO +1 -1
- detectkit-0.53.1/detectkit/tuning/assets/tune.js +0 -187
- {detectkit-0.53.1 → detectkit-0.53.3}/LICENSE +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/MANIFEST.in +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/README.md +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/channels/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/channels/base.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/channels/branding.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/channels/email.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/channels/factory.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/channels/mattermost.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/channels/slack.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/channels/telegram.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/channels/webhook.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/orchestrator/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/orchestrator/_base.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/orchestrator/_decision.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/orchestrator/_recovery.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/orchestrator/_replay.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/orchestrator/_types.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/_base.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/_types.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/autotuner.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/axis_spec.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/config_emitter.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/crossval.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/detector_select.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/distribution.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/grid_search.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/labels.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/result.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/runner.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/scoring.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/seasonality_search.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/settings.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/autotune/window_select.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/_output.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/CLAUDE.section.md +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/rules/alerting.md +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/rules/autotune.md +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/rules/metrics.md +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/rules/overview.md +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/rules/project.md +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/commands/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/commands/autotune.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/commands/clean.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/commands/init.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/commands/init_claude.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/commands/osi.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/commands/run.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/commands/test_alert.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/commands/tune.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/commands/ui.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/commands/unlock.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/cli/main.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/config/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/config/metric_config.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/config/metric_io.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/config/profile.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/config/project_config.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/config/validator.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/core/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/core/interval.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/core/models.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/_sql_manager.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/clickhouse_manager.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/internal_tables/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/internal_tables/_alert_states.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/internal_tables/_autotune_runs.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/internal_tables/_base.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/internal_tables/_datapoints.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/internal_tables/_detections.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/internal_tables/_maintenance.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/internal_tables/_metrics.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/internal_tables/_schema.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/internal_tables/_tasks.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/internal_tables/manager.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/manager.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/mysql_manager.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/postgres_manager.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/database/tables.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/detectors/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/detectors/base.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/detectors/factory.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/detectors/seasonality.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/detectors/statistical/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/detectors/statistical/_windowed.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/detectors/statistical/autoreg.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/detectors/statistical/iqr.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/detectors/statistical/mad.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/detectors/statistical/manual_bounds.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/detectors/statistical/zscore.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/loaders/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/loaders/metric_loader.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/loaders/query_template.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/orchestration/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/orchestration/error_dispatch.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/orchestration/task_manager/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/orchestration/task_manager/_base.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/orchestration/task_manager/_detect_step.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/orchestration/task_manager/_load_step.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/orchestration/task_manager/_types.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/orchestration/task_manager/manager.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/reporting/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/reporting/builder.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/reporting/html_report.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/semantic/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/semantic/errors.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/semantic/exporter.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/semantic/importer.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/semantic/osi_model.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/semantic/query_gen.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/tuning/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/tuning/config_writer.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/tuning/html.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/tuning/server.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/ui/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/ui/assets/ui.js +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/ui/html.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/ui/jobs.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/ui/metric_files.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/ui/overview.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/ui/server.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/utils/__init__.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/utils/datetime_utils.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/utils/env_interpolation.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/utils/json_utils.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit/utils/stats.py +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit.egg-info/SOURCES.txt +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit.egg-info/dependency_links.txt +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit.egg-info/entry_points.txt +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit.egg-info/requires.txt +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/detectkit.egg-info/top_level.txt +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/pyproject.toml +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/requirements.txt +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/setup.cfg +0 -0
- {detectkit-0.53.1 → detectkit-0.53.3}/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.3"
|
|
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 Io={"--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 K(n){return getComputedStyle(document.documentElement).getPropertyValue(n).trim()||Io[n]||"#888"}function Ho(n){let i=n.replace("#","").trim();i.length===3&&(i=i[0]+i[0]+i[1]+i[1]+i[2]+i[2]);let h=parseInt(i,16);return i.length!==6||Number.isNaN(h)?[209,91,54]:[h>>16&255,h>>8&255,h&255]}function j(n,i){let[h,e,v]=Ho(n);return`rgba(${h},${e},${v},${i})`}function po(n){let i=Math.max(1,window.devicePixelRatio||1),h=n.clientWidth||n.offsetWidth||0,e=n.clientHeight||n.offsetHeight||0;return n.width=Math.round(h*i),n.height=Math.round(e*i),i}var On=Number.isFinite;function Fn(n,i,h,e,v,w,C,g,x,R,_,I){let ae=i.length,ee=Math.max(1,Math.round(C)),U=v-e||1,fe=0;for(let Q=0;Q<ae;Q++){let a=h[Q];!On(a)||i[Q]<e||i[Q]>v||fe++}if(n.strokeStyle=R,n.lineWidth=_*I,n.lineJoin="round",n.beginPath(),fe<=ee){let Q=!1;for(let a=0;a<ae;a++){let y=h[a],L=i[a];if(!On(y)||L<e||L>v){Q=!1;continue}let te=g(L),ve=x(y);Q?n.lineTo(te,ve):(n.moveTo(te,ve),Q=!0)}}else{let Q=new Array(ee).fill(null),a=new Array(ee).fill(null);for(let L=0;L<ae;L++){let te=h[L],ve=i[L];if(!On(te)||ve<e||ve>v)continue;let $=Math.floor((ve-e)/U*(ee-1));$=$<0?0:$>ee-1?ee-1:$,(Q[$]===null||te<Q[$])&&(Q[$]=te),(a[$]===null||te>a[$])&&(a[$]=te)}let y=!1;for(let L=0;L<ee;L++){if(a[L]===null){y=!1;continue}let te=w+L,ve=x(a[L]),$=x(Q[L]);y?n.lineTo(te,ve):(n.moveTo(te,ve),y=!0),n.lineTo(te,$)}}n.stroke()}function fo(n){let i=Math.abs(n);return i>=1e3?n.toFixed(0):i>=10?n.toFixed(1):i>=1?n.toFixed(2):n.toFixed(3)}function ho(n,i){let h=new Date(n).toISOString();return i<2*864e5?h.slice(5,16).replace("T"," "):h.slice(5,10)}function Bn(n,i,h){return{left:i.l*h,top:i.t*h,right:n.width-i.r*h,bottom:n.height-i.b*h}}function bo(n,i,h,e,v,w,C){let g=Bn(i,h,e),x=Math.max(g.left,Math.min(v(w),g.right));x<=g.left+.5||(n.save(),n.fillStyle="rgba(17,15,13,0.42)",n.fillRect(g.left,g.top,x-g.left,g.bottom-g.top),n.strokeStyle=j(K("--faint"),.7),n.lineWidth=1*e,n.setLineDash([4*e,4*e]),n.beginPath(),n.moveTo(x,g.top),n.lineTo(x,g.bottom),n.stroke(),n.setLineDash([]),n.fillStyle=j(K("--faint"),.95),n.font=`${10*e}px ui-monospace, monospace`,n.textAlign="left",n.textBaseline="top",n.fillText(C,x+6*e,g.top+5*e),n.restore())}function go(n,i,h,e,v){let w=Bn(i,h,e);if(w.right<=w.left||w.bottom<=w.top)return;n.save(),n.fillStyle="rgba(17,15,13,0.42)",n.fillRect(w.left,w.top,w.right-w.left,w.bottom-w.top),n.fillStyle=j(K("--faint"),.95),n.font=`${10*e}px ui-monospace, monospace`,n.textAlign="center",n.textBaseline="middle";let C=w.right-w.left-12*e;C>0&&n.fillText(v,(w.left+w.right)/2,w.top+(w.bottom-w.top)*.3,C),n.restore()}function vo(n,i,h,e,v,w,C){let g=Bn(i,h,e),x=5*e;n.save();for(let R=0;R<w.length;R++){let _=w[R],I=v(_.t);if(I<g.left-1||I>g.right+1)continue;let ae=C(_.kind);n.strokeStyle=j(ae,.45),n.lineWidth=1*e,n.beginPath(),n.moveTo(I,g.top),n.lineTo(I,g.bottom),n.stroke(),n.fillStyle=ae,n.beginPath(),n.moveTo(I-x,g.top),n.lineTo(I+x,g.top),n.lineTo(I,g.top+x*1.4),n.closePath(),n.fill()}n.restore()}var Oo={mad:1,zscore:2,iqr:4};function Fo(n,i){let h=n.seasonalityData;if(!h||h.length===0)return 0;let e=0;for(let v of i){let w=new Set;for(let C of h)w.add(v.map(g=>{var x;return String((x=C==null?void 0:C[g])!=null?x:"")}).join("|"));e=Math.max(e,w.size)}return e}function Bo(n,i){var v;if(i.type==="manual_bounds")return i.inputType!=="values"?1:0;if(i.type==="autoreg"){let w=Math.max(1,Math.round((v=i.lags)!=null?v:5)),C=i.windowSize+w;return i.inputType!=="values"&&(C+=1),i.stabilization==="clamp"&&(C+=i.windowSize),C}let h=Math.max(i.minSamples,Oo[i.type]);if(i.smoothing==="sma"&&(h=Math.max(h,i.smoothingWindow-1)),i.smoothing==="ema"&&(h=Math.max(h,Math.ceil(5/i.smoothingAlpha))),i.inputType!=="values"&&(h=Math.max(h,1)),i.seasonalityComponents!==null&&i.seasonalityComponents.length>0&&Array.isArray(n.seasonalityData)&&n.seasonalityData.length>0){let w=Fo(n,i.seasonalityComponents);if(w>0){let C=i.minSamplesPerGroup*w;i.windowSize>=C&&(h=Math.max(h,C))}}return i.stabilization==="clamp"&&(h+=i.windowSize),h}function Cn(n,i){return Math.min(Bo(n,i),n.timestamps.length)}var P={l:52,r:14,t:14,b:26},jn=46,jo=10,Vo=13,_e=Number.isFinite,Mt=1e3,Ct=60*Mt,Tt=60*Ct,Me=24*Tt,Vn=[Mt,2*Mt,5*Mt,10*Mt,15*Mt,30*Mt,Ct,2*Ct,5*Ct,10*Ct,15*Ct,30*Ct,Tt,2*Tt,3*Tt,6*Tt,12*Tt,Me,2*Me,3*Me,5*Me,7*Me,14*Me];function yo(n,i,h){let e=Math.max(i-n,1);if(e>h*14*Me){let g=[1,2,3,6,12,24,36,60,120,240],x=g[g.length-1];for(let U of g)if(e/(U*30.44*Me)<=h){x=U;break}let R=new Date(n),_=R.getUTCFullYear(),I=R.getUTCMonth();if(x>=12){let U=x/12;_=Math.floor(_/U)*U,I=0}else I=Math.floor(I/x)*x;let ae=[],ee=Date.UTC(_,I,1);for(;ee<=i;)ee>=n&&ae.push(ee),I+=x,_+=Math.floor(I/12),I%=12,ee=Date.UTC(_,I,1);return{ticks:ae,step:x*30*Me}}let v=Vn[Vn.length-1];for(let g of Vn)if(e/g<=h){v=g;break}let w;if(v%Me===0){let g=new Date(n);w=Date.UTC(g.getUTCFullYear(),g.getUTCMonth(),g.getUTCDate());let x=v/Me,R=Math.round(w/Me);w+=(x-R%x)%x*Me}else w=Math.ceil(n/v)*v;let C=[];for(let g=w;g<=i;g+=v)g>=n&&C.push(g);return{ticks:C,step:v}}function Un(n,i){let h=new Date(n).toISOString();return i>=320*Me?h.slice(0,4):i>=26*Me?h.slice(0,7):i>=Me?h.slice(5,10):i>=Tt?h.slice(5,16).replace("T"," "):h.slice(5,19).replace("T"," ")}function wo(n,i={}){var fn;let h=n.getContext("2d");if(!h)throw new Error("chart: 2D context unavailable");let e=h,v=!!i.navigable,w=v&&i.showNavigator!==!1,C=i.yFit==="data",g=!!i.labeling,x=(fn=i.mode)!=null?fn:"tune",R=()=>g?x:"tune",_=()=>R()!=="label",I=()=>R()==="review"?.3:1,ae=()=>R()==="tune"?1:R()==="review"?.3:.85,ee=()=>g&&R()==="label",U=()=>R()==="label"?1:.55,fe=()=>R()==="tune",Q=()=>g&&(R()==="tune"||R()==="review"),a=1,y=null,L=-1,te=0,ve=!!i.showZeroLine,$=[],re=null,H=-1,B=null,Ce=!1,he=!1,pt="above",Pt=0,$e=null,le=null,Te=null,Ae=null,ye=null,we=!1,V=null,G=0,q=1,De=0,je=1,ie=0,ce=1,Vt="";function an(){a=po(n)}let rt=()=>w?(jo+jn)*a:0,qe=()=>n.width-(P.l+P.r)*a,xe=()=>n.height-(P.t+P.b)*a-rt(),Xe=()=>v?ie:G,Lt=()=>v?ce:q,Ee=()=>Lt()-Xe()||1,Ie=()=>q-G||1,O=o=>P.l*a+(o-Xe())/Ee()*qe(),ke=o=>n.height-P.b*a-rt()-(o-De)/(je-De||1)*xe(),ft=()=>n.height-jn*a,Pe=()=>(jn-Vo)*a,it=()=>n.width-(P.l+P.r)*a,ue=o=>P.l*a+(o-G)/Ie()*it(),Ut=o=>ft()+Pe()-(o-De)/(je-De||1)*Pe(),st=(o,r,c)=>{let l=new Array(o.length).fill(!1);if(o.length===0)return l;let d=e.measureText(Un(o[0],c)).width+16*a,u=-1/0;for(let f=0;f<o.length;f++){let S=r(o[f]);S-u>=d&&(l[f]=!0,u=S)}return l},ne=(o,r,c)=>Math.max(r,Math.min(c,o)),Je=()=>{let o=y==null?void 0:y.series,r=o?o.timestamps.length:0,c=r>1?Ie()/(r-1):1e3;return Math.max(c*8,1e3)};function Re(o,r){var d;let c=r-o,l=Je();if(c<l){let u=(o+r)/2;o=u-l/2,r=u+l/2,c=l}c>=Ie()&&(o=G,r=q),o<G&&(r+=G-o,o=G),r>q&&(o-=r-q,r=q),ie=ne(o,G,q),ce=ne(r,G,q),Ce||(d=i.onViewChange)==null||d.call(i,ie,ce),he&&be(),F()}function $t(o,r){v&&(Ce=!0,Re(o,r),Ce=!1)}let Qe=o=>{let r=(o-P.l*a)/(qe()||1);return Xe()+ne(r,0,1)*Ee()},ht=o=>{let r=(o-P.l*a)/(it()||1);return G+ne(r,0,1)*Ie()},de=o=>{let r=n.height-P.b*a-rt(),c=ne((r-o)/(xe()||1),0,1);return De+c*(je-De)};function Nt(o){let r=y==null?void 0:y.series;if(!r||r.timestamps.length===0)return-1;let c=(o-P.l*a)/(qe()||1),l=Xe()+c*Ee(),d=r.timestamps,u=0,f=d.length-1;for(;u<f;){let S=u+f>>1;d[S]<l?u=S+1:f=S}return u>0&&l-d[u-1]<d[u]-l&&(u-=1),u}function He(o,r){let c=o.timestamps;if(G=c[0],q=c[c.length-1],v){let f=`${c.length}:${G}:${q}`;f!==Vt?(Vt=f,ie=G,ce=q):(ie=ne(ie,G,q),ce=ne(Math.max(ce,ie+Je()),G,q))}let l=1/0,d=-1/0;for(let f of o.values)_e(f)&&(f<l&&(l=f),f>d&&(d=f));if(!C)for(let f of r)f.scored&&(_e(f.lower)&&f.lower<l&&(l=f.lower),_e(f.upper)&&f.upper>d&&(d=f.upper));(!_e(l)||!_e(d))&&(l=0,d=1),ve&&(l>0&&(l=0),d<0&&(d=0)),d<=l&&(d=l+1);let u=(d-l)*.06;De=l-u,je=d+u}function Oe(o,r,c){let l=y.series.timestamps;Fn(e,l,o,Xe(),Lt(),P.l*a,qe(),O,ke,r,c,a)}function at(o,r){let c=[],l=-1;for(let d=Math.max(0,r);d<o.length;d++){let u=o[d];u.scored&&_e(u.lower)&&_e(u.upper)?l===-1&&(l=d):l!==-1&&(c.push([l,d-1]),l=-1)}return l!==-1&&c.push([l,o.length-1]),c}function bt(){if(te=0,!y||n.width===0||n.height===0)return;let{series:o,scored:r,params:c,alerts:l}=y;if(o.timestamps.length===0){e.fillStyle=K("--term-bg"),e.fillRect(0,0,n.width,n.height);return}let d=K("--clay"),u=K("--st-anomaly"),f=K("--faint"),S=K("--muted");e.fillStyle=K("--term-bg"),e.fillRect(0,0,n.width,n.height),e.font=`${11*a}px ui-monospace, 'JetBrains Mono', monospace`,e.textBaseline="middle";for(let M=0;M<=4;M++){let z=De+(je-De)*M/4,W=ke(z);e.strokeStyle=j(f,.1),e.lineWidth=1*a,e.beginPath(),e.moveTo(P.l*a,W),e.lineTo(n.width-P.r*a,W),e.stroke(),e.fillStyle=S,e.textAlign="right",e.fillText(fo(z),(P.l-8)*a,W)}if(ve&&De<=0&&je>=0){let M=ke(0);e.strokeStyle=j(S,.6),e.lineWidth=1.25*a,e.beginPath(),e.moveTo(P.l*a,M),e.lineTo(n.width-P.r*a,M),e.stroke(),e.fillStyle=S,e.textAlign="right",e.fillText("0",(P.l-8)*a,M)}e.textBaseline="top";let N=n.height-P.b*a-rt();if(v){let M=yo(Xe(),Lt(),7),z=st(M.ticks,O,M.step);for(let W=0;W<M.ticks.length;W++){let oe=M.ticks[W],ge=O(oe);e.strokeStyle=j(f,.1),e.lineWidth=1*a,e.beginPath(),e.moveTo(ge,P.t*a),e.lineTo(ge,N),e.stroke(),z[W]&&(e.fillStyle=S,e.textAlign=ge<(P.l+24)*a?"left":ge>n.width-(P.r+24)*a?"right":"center",e.fillText(Un(oe,M.step),ge,N+7*a))}}else{let M=Ee();for(let z=0;z<=5;z++){let W=G+M*z/5,oe=O(W);e.fillStyle=S,e.textAlign=z===0?"left":z===5?"right":"center",e.fillText(ho(W,M),oe,N+7*a)}}e.save(),e.beginPath(),e.rect(P.l*a,P.t*a,qe(),xe()),e.clip(),dn(),Pn();let T=r.length,E=Math.min(Cn(o,c),T),Y=E<T?o.timestamps[E]:void 0,X=I(),me=_()?at(r,E):[];e.fillStyle=j(d,.13*X);for(let[M,z]of me){e.beginPath(),e.moveTo(O(r[M].timestamp),ke(r[M].upper));for(let W=M+1;W<=z;W++)e.lineTo(O(r[W].timestamp),ke(r[W].upper));for(let W=z;W>=M;W--)e.lineTo(O(r[W].timestamp),ke(r[W].lower));e.closePath(),e.fill()}e.strokeStyle=j(d,.4*X),e.lineWidth=1*a;for(let[M,z]of me)for(let W of["upper","lower"]){e.beginPath();for(let oe=M;oe<=z;oe++){let ge=O(r[oe].timestamp),Ge=ke(r[oe][W]);oe===M?e.moveTo(ge,Ge):e.lineTo(ge,Ge)}e.stroke()}e.strokeStyle=j(f,.55*X),e.lineWidth=1*a,e.setLineDash([3*a,3*a]);for(let[M,z]of me){e.beginPath();for(let W=M;W<=z;W++){let oe=r[W].center;if(!_e(oe))continue;let ge=O(r[W].timestamp),Ge=ke(oe);W===M?e.moveTo(ge,Ge):e.lineTo(ge,Ge)}e.stroke()}if(e.setLineDash([]),c.smoothing!=="none"&&(!g||R()==="tune")){Oe(o.values,j(d,.28),1.25);let M=r.map(z=>z.processedValue);Oe(M,d,1.6)}else Oe(o.values,d,1.5);let J=ae();for(let M=E;M<T;M++){let z=r[M];if(!z.scored||!_e(z.value))continue;let W=O(z.timestamp),oe=ke(z.value);z.isAnomaly?(e.fillStyle=j(u,.18*J),e.beginPath(),e.arc(W,oe,6*a,0,Math.PI*2),e.fill(),e.fillStyle=j(u,J),e.beginPath(),e.arc(W,oe,3*a,0,Math.PI*2),e.fill()):o.truthAnomaly[M]&&(e.strokeStyle=j(S,.7),e.lineWidth=1.25*a,e.beginPath(),e.arc(W,oe,3.5*a,0,Math.PI*2),e.stroke())}(!g||R()==="tune")&&(T>0&&E>=T?go(e,n,P,a,"all shown points are detector warm-up \u2014 nothing to score yet"):Y!==void 0&&bo(e,n,P,a,O,Y,"detection at full power \u2192")),L>=0&&L<r.length&&fe()&&ln(L,c.windowSize,r,o,f),l&&l.length&&vo(e,n,P,a,O,l,M=>M==="anomaly"?K("--st-anomaly"):M==="anomaly-validated"?K("--st-recovery"):M==="anomaly-false"?K("--st-error"):M==="recovery"?K("--st-recovery"):K("--st-nodata")),Kt(),yt(r,E,T),e.restore(),w&<(o,l,d,f,S)}function lt(o,r,c,l,d){let u=ft(),f=u+Pe(),S=P.l*a,N=n.width-P.r*a,T=yo(G,q,5);e.save(),e.beginPath(),e.rect(S,u,it(),Pe()),e.clip(),e.strokeStyle=j(l,.1),e.lineWidth=1*a;for(let J of T.ticks){let M=ue(J);e.beginPath(),e.moveTo(M,u),e.lineTo(M,f),e.stroke()}Fn(e,o.timestamps,o.values,G,q,S,it(),ue,Ut,j(c,.7),1.1,a),e.restore();let E=ue(ie),Y=ue(ce);if(e.fillStyle="rgba(27,25,22,0.55)",e.fillRect(S,u,E-S,Pe()),e.fillRect(Y,u,N-Y,Pe()),$.length){let J=K("--st-anomaly");e.fillStyle=j(J,.28);for(let M of $){let z=ue(M.start),W=Math.max(ue(M.end)-z,2*a);e.fillRect(z,u,W,Pe())}}if(r&&r.length){let J=K("--st-anomaly");e.fillStyle=j(J,.85);for(let M of r){let z=ue(M.t);e.fillRect(z-1*a,u,2*a,Pe())}}e.fillStyle="rgba(245,241,232,0.06)",e.fillRect(E,u,Y-E,Pe()),e.strokeStyle=c,e.lineWidth=1.5*a,e.strokeRect(E,u+1,Y-E,Pe()-2),e.fillStyle=c;let X=u+Pe()/2-8*a;e.fillRect(E-2*a,X,4*a,16*a),e.fillRect(Y-2*a,X,4*a,16*a),e.font=`${10*a}px ui-monospace, monospace`,e.textBaseline="top",e.fillStyle=d;let me=st(T.ticks,ue,T.step);for(let J=0;J<T.ticks.length;J++){let M=T.ticks[J],z=ue(M);e.strokeStyle=j(l,.25),e.lineWidth=1*a,e.beginPath(),e.moveTo(z,f),e.lineTo(z,f+3*a),e.stroke(),me[J]&&(e.textAlign=z<(P.l+26)*a?"left":z>n.width-(P.r+26)*a?"right":"center",e.fillText(Un(M,T.step),z,f+5*a))}}function ln(o,r,c,l,d){let u=l.timestamps,f=Math.max(0,o-r),S=o-1,N=P.t*a,T=xe();if(S>=f){let X=l.intervalSeconds*1e3/Ee()*qe()*.5,me=O(u[f])-X,J=O(u[S])+X;e.fillStyle="rgba(255,255,255,0.05)",e.fillRect(me,N,J-me,T),e.strokeStyle=j(d,.5),e.lineWidth=1*a,e.beginPath(),e.moveTo(me,N),e.lineTo(me,N+T),e.moveTo(J,N),e.lineTo(J,N+T),e.stroke()}let E=O(u[o]);e.strokeStyle=j(d,.85),e.lineWidth=1*a,e.setLineDash([2*a,2*a]),e.beginPath(),e.moveTo(E,N),e.lineTo(E,N+T),e.stroke(),e.setLineDash([]);let Y=c[o];if(Y.scored&&_e(Y.lower)&&_e(Y.upper)){let X=5*a;e.strokeStyle=j(K("--clay"),.85),e.lineWidth=1.5*a;for(let me of[Y.lower,Y.upper]){let J=ke(me);e.beginPath(),e.moveTo(E-X,J),e.lineTo(E+X,J),e.stroke()}}if(_e(Y.value)){let X=ke(Y.value);e.fillStyle=K("--term-bg"),e.beginPath(),e.arc(E,X,4*a,0,Math.PI*2),e.fill(),e.strokeStyle=Y.isAnomaly?K("--st-anomaly"):K("--clay"),e.lineWidth=2*a,e.beginPath(),e.arc(E,X,4*a,0,Math.PI*2),e.stroke()}}function Gt(o,r,c,l,d){e.beginPath(),e.moveTo(o+d,r),e.arcTo(o+c,r,o+c,r+l,d),e.arcTo(o+c,r+l,o,r+l,d),e.arcTo(o,r+l,o,r,d),e.arcTo(o,r,o+c,r,d),e.closePath()}function Yt(o,r){let c=K("--st-anomaly"),l=14*a,d=3*a,u=o-l-d,f=P.t*a+d;e.fillStyle=r?j(c,.95):"rgba(27,25,22,0.82)",e.strokeStyle=j(c,.9),e.lineWidth=1*a,Gt(u,f,l,l,3*a),e.fill(),e.stroke(),e.strokeStyle=r?"#fff":c,e.lineWidth=1.5*a;let S=4*a;e.beginPath(),e.moveTo(u+S,f+S),e.lineTo(u+l-S,f+l-S),e.moveTo(u+l-S,f+S),e.lineTo(u+S,f+l-S),e.stroke()}let gt=()=>$e!=null?$e:le,Le=()=>{let o=ye||Te;return o?[Math.min(o.a,o.b),Math.max(o.a,o.b)]:[Xe(),Lt()]};function Ze(){let o=[],r=gt();if(r==null||!y)return o;let c=y.series.timestamps,l=y.series.values,[d,u]=Le(),f=-1,S=-1,N=0;for(let T=0;T<c.length;T++){if(c[T]<d||c[T]>u)continue;let E=l[T];_e(E)&&(pt==="above"?E>r:E<r)?(f===-1&&(f=c[T]),S=c[T],N=0):f!==-1&&(N++,N>Pt&&(o.push([f,S]),f=-1,N=0))}return f!==-1&&o.push([f,S]),o}function be(){if(!i.onThresholdChange)return;let[o,r]=Le(),c=ye||Te;i.onThresholdChange({value:gt(),locked:$e!=null,runs:Ze().length,window:c?{start:Math.min(c.a,c.b),end:Math.max(c.a,c.b)}:null,committed:Te!=null,windowMs:r-o})}function et(o,r){let c=null;for(let l=$.length-1;l>=0;l--){let d=$[l];o<=d.end&&r>=d.start&&(c===null?(d.start=Math.min(d.start,o),d.end=Math.max(d.end,r),c=d):(c.start=Math.min(c.start,d.start),c.end=Math.max(c.end,d.end),re===d&&(re=c),$.splice(l,1)))}c===null&&$.push({start:o,end:r,label:""})}function dn(){if(!he||gt()==null)return;let r=P.t*a,c=xe(),l=K("--st-nodata");for(let[d,u]of Ze()){let f=O(d),S=Math.max(O(u)-f,2*a);e.fillStyle=j(l,.22),e.fillRect(f,r,S,c),e.strokeStyle=j(l,.6),e.lineWidth=1*a,e.strokeRect(f,r,S,c)}}function Kt(){if(!he)return;let o=P.t*a,r=n.height-P.b*a-rt(),c=P.l*a,l=n.width-P.r*a,d=K("--st-nodata"),[u,f]=Le(),S=!!(ye||Te),N=ne(O(u),c,l),T=ne(O(f),c,l);S&&(e.fillStyle=j(K("--ink"),.5),e.fillRect(c,o,Math.max(0,N-c),xe()),e.fillRect(T,o,Math.max(0,l-T),xe()),e.strokeStyle=j(d,.7),e.lineWidth=1*a,e.setLineDash([3*a,3*a]),e.beginPath(),e.moveTo(N,o),e.lineTo(N,r),e.moveTo(T,o),e.lineTo(T,r),e.stroke(),e.setLineDash([]));let E=gt();if(E!=null&&E>=De&&E<=je){let Y=ke(E);e.strokeStyle=d,e.lineWidth=1.5*a,e.setLineDash([6*a,4*a]),e.beginPath(),e.moveTo(N,Y),e.lineTo(T,Y),e.stroke(),e.setLineDash([])}}let _n=()=>y?y.series.intervalSeconds*1e3:1e3;function cn(o,r,c){let l=!1;for(let d=0,u=c.length-1;d<c.length;u=d++){let f=c[d].x,S=c[d].y,N=c[u].x,T=c[u].y;S>r!=T>r&&o<(N-f)*(r-S)/(T-S||1e-9)+f&&(l=!l)}return l}function Fe(o,r,c){let l=[];if(!V||V.length<3)return l;for(let d=Math.max(0,r);d<c;d++){let u=o[d];!u.scored||!u.isAnomaly||!_e(u.value)||cn(O(u.timestamp),ke(u.value),V)&&l.push(d)}return l}function Et(o,r){var T;if(!r.length)return[];let c=_n(),d=(Math.max(1,(T=y==null?void 0:y.params.consecutiveAnomalies)!=null?T:1)+1)*c,u=c/2,f=[],S=r[0],N=r[0];for(let E=1;E<r.length;E++){let Y=r[E];o[Y].timestamp-o[N].timestamp>d&&(f.push([o[S].timestamp-u,o[N].timestamp+u]),S=Y),N=Y}return f.push([o[S].timestamp-u,o[N].timestamp+u]),f}function vt(o,r,c){if(!i.onLassoChange)return;let l=we&&V?Fe(o,r,c):[];i.onLassoChange({active:!!V,anomalies:l.length,incidents:Et(o,l).length})}function Dn(){if(!y||!V)return;let{scored:o}=y,r=o.length,c=Math.min(Cn(y.series,y.params),r),l=Et(o,Fe(o,c,r));for(let[d,u]of l)et(d,u);l.length&&xt()}function yt(o,r,c){if(!we||!V||V.length<2)return;let l=K("--clay"),d=K("--st-anomaly");e.beginPath(),e.moveTo(V[0].x,V[0].y);for(let u=1;u<V.length;u++)e.lineTo(V[u].x,V[u].y);e.closePath(),e.fillStyle=j(l,.08),e.fill(),e.strokeStyle=j(l,.9),e.lineWidth=1.5*a,e.setLineDash([5*a,4*a]),e.stroke(),e.setLineDash([]);for(let u of Fe(o,r,c)){let f=O(o[u].timestamp),S=ke(o[u].value);e.strokeStyle=d,e.lineWidth=2*a,e.beginPath(),e.arc(f,S,7*a,0,Math.PI*2),e.stroke()}}function Pn(){let o=P.t*a,r=xe(),c=K("--st-anomaly"),l=P.l*a,d=n.width-P.r*a,u=ee(),f=U();for(let S=0;S<$.length;S++){let N=$[S],T=O(N.start),E=O(N.end);if(E<l-1||T>d+1)continue;let Y=Math.max(E-T,2*a),X=u&&N===re;e.fillStyle=j(c,(X?.3:.16)*f),e.fillRect(T,o,Y,r),e.strokeStyle=j(c,(X?.95:.5)*f),e.lineWidth=(X?2:1)*a,e.strokeRect(T,o,Y,r),u&&(e.fillStyle=j(c,.95),e.fillRect(T-1.5*a,o,3*a,r),e.fillRect(E-1.5*a,o,3*a,r),(E-T>=22*a||X)&&Yt(E,X||S===H))}if(u&&B&&B.mode==="new"){let S=O(B.a),N=O(B.b);e.fillStyle=j(K("--st-nodata"),.28),e.fillRect(Math.min(S,N),o,Math.abs(N-S),r)}}function qt(o,r){if(!y||!y.alerts||!y.alerts.length||r>(P.t+22)*a)return-1;let c=-1,l=9*a;for(let d=0;d<y.alerts.length;d++){let u=Math.abs(o-O(y.alerts[d].t));u<=l&&(l=u,c=d)}return c}function wt(o){var l;if(!y||!y.alerts)return;let r=y.alerts[o].kind,c=r==="anomaly"?"valid":r==="anomaly-validated"?"false":"unreviewed";(l=i.onAlertReviewChange)==null||l.call(i,y.alerts[o].t,c)}function xt(o){var r;(r=i.onIncidentsChange)==null||r.call(i,$,o)}function Xt(o){let r=$.indexOf(o);r<0||($.splice(r,1),re===o&&(re=null),H=-1,xt(o),F())}function se(o,r){let c=P.t*a,l=6*a;for(let d=0;d<$.length;d++){let u=O($[d].end);if(u-O($[d].start)>=22*a||$[d]===re){let f=14*a,S=3*a,N=u-f-S,T=c+S;if(o>=N&&o<=N+f&&r>=T&&r<=T+f)return{i:d,edge:"del"}}}for(let d=0;d<$.length;d++){let u=O($[d].start),f=O($[d].end);if(Math.abs(o-u)<=l)return{i:d,edge:"a"};if(Math.abs(o-f)<=l)return{i:d,edge:"b"}}for(let d=0;d<$.length;d++){let u=O($[d].start),f=O($[d].end);if(o>u+l&&o<f-l)return{i:d,edge:"move"}}return null}let dt=()=>{let o=y==null?void 0:y.series,r=o?o.timestamps.length:0;return r>1?Math.max(Ie()/(r-1),1):1e3};function F(){te===0&&(te=requestAnimationFrame(bt))}function Be(){if(!y)return;let o=y.scored.length,r=Math.min(Cn(y.series,y.params),o);vt(y.scored,r,o)}function Ne(){var c;if(!i.onHover||!y)return;if(L<0){i.onHover(null);return}let o=(c=y.scored[L])!=null?c:null,r={index:L,point:o,windowStart:Math.max(0,L-y.params.windowSize),windowEnd:L-1};i.onHover(r)}let Se=null,Z=null,Ve=o=>{let r=n.getBoundingClientRect();return{x:(o.clientX-r.left)*a,y:(o.clientY-r.top)*a}},tt=o=>w&&o>=ft(),Ue=o=>{let r=ue(ie),c=ue(ce),l=8*a;return Math.abs(o-r)<=l?"l":Math.abs(o-c)<=l?"r":o>r&&o<c?"move":"out"};function Jt(o){if(!y)return;let{x:r,y:c}=Ve(o);if(Se||Z||B)return;if(tt(c)){L!==-1&&(L=-1,Ne(),F());let u=Ue(r);n.style.cursor=u==="l"||u==="r"?"ew-resize":u==="move"?"grab":"pointer";return}let l=R();if(g&&l==="label"&&we){L!==-1&&(L=-1,Ne()),n.style.cursor="crosshair";return}if(g&&l==="label"&&he){L!==-1&&(L=-1,Ne()),$e==null&&!Ae&&(le=de(c),be(),F()),n.style.cursor="crosshair";return}if(g&&l==="label"){L!==-1&&(L=-1,Ne());let u=se(r,c),f=u&&u.edge==="del"?u.i:-1;f!==H&&(H=f,F()),n.style.cursor=u?u.edge==="del"?"pointer":u.edge==="move"?"grab":"ew-resize":"crosshair";return}if(g&&l==="review"){L!==-1&&(L=-1,Ne()),n.style.cursor=qt(r,c)>=0?"pointer":"grab";return}v&&(n.style.cursor=g&&qt(r,c)>=0?"pointer":"grab");let d=Nt(r);d!==L&&(L=d,Ne(),F())}function ct(){L!==-1&&(L=-1,Ne(),F())}function nt(o){if(!v||!y)return;let{x:r,y:c}=Ve(o);if(w&&c>=ft()){let l=Ue(r);if(l==="l")Z={type:"l",grab:0,vMin:ie,vMax:ce};else if(l==="r")Z={type:"r",grab:0,vMin:ie,vMax:ce};else if(l==="move")Z={type:"move",grab:ht(r),vMin:ie,vMax:ce};else{let d=ht(r),u=Ee();Re(d-u/2,d+u/2),Z={type:"move",grab:d,vMin:ie,vMax:ce}}n.style.cursor="grabbing",o.preventDefault();return}if(c>P.t*a&&c<n.height-P.b*a-rt()){let l=R();if(Q()){let d=qt(r,c);if(d>=0){wt(d),o.preventDefault();return}}if(g&&l==="label"&&we){V=[{x:r,y:c}],Be(),n.style.cursor="crosshair",F(),o.preventDefault();return}if(g&&l==="label"&&he){Ae={x:r,ts:Qe(r)},$e==null&&(le=de(c)),be(),F(),o.preventDefault();return}if(g&&l==="label"){let d=Qe(r),u=se(r,c);if(u&&u.edge==="del"){Xt($[u.i]),o.preventDefault();return}if(u&&u.edge==="move"){let f=$[u.i];re=f,B={mode:"move",iv:f,grab:d,a0:f.start,b0:f.end,moved:!1}}else if(u){let f=$[u.i];re=f,B={mode:"edge",iv:f,edge:u.edge,moved:!1}}else re=null,B={mode:"new",a:d,b:d,moved:!1};n.style.cursor="crosshair",F(),o.preventDefault();return}Se={x:r,vMin:ie,vMax:ce},n.style.cursor="grabbing",o.preventDefault()}}function Rt(o){if(!y)return;let{x:r,y:c}=Ve(o);if(we&&V){V.push({x:r,y:c}),Be(),F();return}if(he&&Ae){Math.abs(r-Ae.x)>6*a?ye={a:Ae.ts,b:Qe(r)}:(ye=null,$e==null&&(le=de(c))),be(),F();return}if(B){let l=Qe(r);if(B.mode==="new")B.b=l,Math.abs(O(B.b)-O(B.a))>4*a&&(B.moved=!0);else if(B.mode==="edge"){let d=B.iv,u=dt();B.edge==="a"?d.start=ne(Math.min(l,d.end-u),G,q):d.end=ne(Math.max(l,d.start+u),G,q),B.moved=!0}else{let d=B.iv,u=B.a0+(l-B.grab),f=B.b0+(l-B.grab);u<G&&(f+=G-u,u=G),f>q&&(u-=f-q,f=q),d.start=ne(u,G,q),d.end=ne(f,G,q),B.moved=!0}F();return}if(Se){let l=(r-Se.x)*(Se.vMax-Se.vMin)/(qe()||1);Re(Se.vMin-l,Se.vMax-l)}else if(Z){let l=ht(r);Z.type==="l"?Re(Math.min(l,ce-Je()),ce):Z.type==="r"?Re(ie,Math.max(l,ie+Je())):Re(Z.vMin+(l-Z.grab),Z.vMax+(l-Z.grab))}}function zt(o){if(we&&V){Dn(),V=null,Be(),F();return}if(he&&Ae){let{x:r,y:c}=Ve(o);if(Math.abs(r-Ae.x)>6*a){let l=Ae.ts,d=Qe(r);Te={a:Math.min(l,d),b:Math.max(l,d)}}else $e=de(c);Ae=null,ye=null,be(),F();return}if(B){let r=B;if(r.mode==="new")if(r.moved){let c=ne(Math.min(r.a,r.b),G,q),l=ne(Math.max(r.a,r.b),G,q),d={start:c,end:l,label:""};$.push(d),re=d}else re=null;else if(r.iv.start>r.iv.end){let c=r.iv.start;r.iv.start=r.iv.end,r.iv.end=c}B=null,n.style.cursor="crosshair",r.moved&&xt(),F();return}(Se||Z)&&(Se=null,Z=null,n.style.cursor="grab")}function ut(o){let r=o.target,c=!!r&&(r.tagName==="INPUT"||r.tagName==="TEXTAREA"||r.isContentEditable);if(we){o.key==="Escape"&&V&&(V=null,Be(),F());return}he||c||!re||(o.key==="Delete"||o.key==="Backspace"?(o.preventDefault(),Xt(re)):o.key==="Escape"&&(re=null,F()))}function Qt(o){if(!v||!y)return;if(we&&V){o.preventDefault();return}o.preventDefault();let{x:r,y:c}=Ve(o),l=ne(Ee()*Math.pow(1.0015,o.deltaY),Je(),Ie());if(c>=ft()){let d=ht(r);Re(d-l/2,d+l/2)}else{let d=Qe(r),u=(d-ie)/(Ee()||1);Re(d-u*l,d-u*l+l)}}function Wt(){v&&Re(G,q)}n.addEventListener("mousemove",Jt),n.addEventListener("mouseleave",ct),v&&(n.addEventListener("mousedown",nt),n.addEventListener("wheel",Qt,{passive:!1}),n.addEventListener("dblclick",Wt),window.addEventListener("mousemove",Rt),window.addEventListener("mouseup",zt)),g&&(window.addEventListener("keydown",ut),n.style.cursor=x==="tune"?"grab":"crosshair");function It(o){y=o,!g&&o.incidents&&($=o.incidents),He(o.series,o.scored),L>=o.series.timestamps.length&&(L=-1),F()}function Zt(o){ve!==o&&(ve=o,y&&He(y.series,y.scored),F())}function en(o){$=o,re=null,H=-1,F()}function Ln(o){g&&(he=o,o?(re=null,we=!1,V=null,Be()):(le=null,Ae=null,ye=null),n.style.cursor="crosshair",be(),F())}function un(o){g&&(we=o,o?(he=!1,Ae=null,ye=null,le=null,re=null,be()):V=null,n.style.cursor="crosshair",Be(),F())}function Ht(o){!g||x===o||(x=o,o!=="label"&&(he=!1,Ae=null,ye=null,le=null,we=!1,V=null,re=null,H=-1,be(),Be()),L=-1,n.style.cursor=o==="tune"?"grab":"crosshair",y&&He(y.series,y.scored),F())}function Ot(o){pt=o,be(),F()}function tn(o){Pt=Math.max(0,Math.floor(o)||0),be(),F()}function kt(o){$e=o!=null&&_e(o)?o:null,be(),F()}function St(){let o=Ze(),r=_n()/2;for(let[c,l]of o)et(c-r,l+r);return o.length&&xt(),be(),F(),o.length}function nn(){Te=null,ye=null,be(),F()}function on(){return Te?{start:Math.min(Te.a,Te.b),end:Math.max(Te.a,Te.b)}:null}function ze(o){Te=o?{a:o.start,b:o.end}:null,be(),F()}function mn(){an(),y&&(He(y.series,y.scored),F())}function pn(){n.removeEventListener("mousemove",Jt),n.removeEventListener("mouseleave",ct),v&&(n.removeEventListener("mousedown",nt),n.removeEventListener("wheel",Qt),n.removeEventListener("dblclick",Wt),window.removeEventListener("mousemove",Rt),window.removeEventListener("mouseup",zt)),g&&window.removeEventListener("keydown",ut),te!==0&&cancelAnimationFrame(te),te=0,y=null}return an(),{render:It,resize:mn,setMode:Ht,setZeroLine:Zt,setViewWindow:$t,setIncidents:en,setThresholdMode:Ln,setThresholdDirection:Ot,setThresholdGap:tn,setThresholdValue:kt,applyThreshold:St,setLassoMode:un,clearCaptureWindow:nn,getCaptureWindow:on,setCaptureWindow:ze,destroy:pn}}function Tn(n){var h;if(n.type==="manual_bounds"){let e={};return n.lowerBound!=null&&(e.lower_bound=n.lowerBound),n.upperBound!=null&&(e.upper_bound=n.upperBound),n.inputType!=="values"&&(e.input_type=n.inputType),e}if(n.type==="autoreg"){let e=Math.max(1,Math.round((h=n.lags)!=null?h:5)),v={lags:e,threshold:n.threshold,window_size:n.windowSize,min_samples:Math.min(Math.max(n.minSamples,e+2),n.windowSize)};return n.stabilization!=="clamp"&&(v.stabilization=null),n.inputType!=="values"&&(v.input_type=n.inputType),v}let i={threshold:n.threshold,window_size:n.windowSize};return n.windowWeights!=="none"&&(i.window_weights=n.windowWeights,n.windowWeights==="exponential"&&n.halfLife!=null&&(i.half_life=n.halfLife)),n.detrend!=="none"&&(i.detrend=n.detrend),n.stabilization&&n.stabilization!=="none"&&(i.stabilization=n.stabilization),n.smoothing!=="none"&&(i.smoothing=n.smoothing),n.inputType!=="values"&&(i.input_type=n.inputType),n.seasonalityComponents&&n.seasonalityComponents.length&&(i.seasonality_components=n.seasonalityComponents,i.min_samples_per_group=n.minSamplesPerGroup),i}function xo(n,i,h,e){let v=Tn(n),w=[`type: ${n.type}`];for(let[C,g]of Object.entries(v))w.push(`${C}=${typeof g=="object"?JSON.stringify(g):g}`);return n.direction&&n.direction!=="any"&&w.push(`direction=${n.direction}`),w.push(`consecutive_anomalies=${i}`),h!=null&&e!=null&&(w.push(`anomaly_window=${h}p`),w.push(`min_anomaly_share=${e}`)),w.join(" \xB7 ")}function m(n,i,h){let e=document.createElement(n);return i&&(e.className=i),h!=null&&(e.textContent=h),e}function At(n,i){let h=m("label","dtk-ctl-label",n);if(i){h.title=i;let e=m("span","dtk-ctl-info","\u24D8");e.title=i,h.appendChild(document.createTextNode(" ")),h.appendChild(e)}return h}function _t(n,i,h,e,v){let w=m("div","dtk-ctl");w.appendChild(At(n,v));let C=m("div","dtk-seg"),g=h,x=[],R=()=>{x.forEach(_=>_.classList.toggle("on",_.dataset.v===g))};return i.forEach(_=>{let I=m("button","dtk-seg-btn",_.label);I.type="button",I.dataset.v=_.value,I.onclick=()=>{g=_.value,R(),e(g)},x.push(I),C.appendChild(I)}),R(),w.appendChild(C),{row:w,get:()=>g,set:_=>{g=_,R()}}}function Ke(n,i,h){var R;let e=m("div","dtk-ctl"),v=m("div","dtk-ctl-head"),w=At(n,i.hint),C=m("span","dtk-ctl-val"),g=(R=i.fmt)!=null?R:(_=>String(_));v.appendChild(w),v.appendChild(C),e.appendChild(v);let x=m("input","dtk-range");return x.type="range",x.min=String(i.min),x.max=String(i.max),x.step=String(i.step),x.value=String(i.value),C.textContent=g(i.value),x.oninput=()=>{C.textContent=g(Number(x.value))},x.onchange=()=>{let _=Number(x.value);C.textContent=g(_),h(_)},e.appendChild(x),{row:e,get:()=>Number(x.value),set:_=>{x.value=String(_),C.textContent=g(Number(x.value))},setMax:_=>{x.max=String(_),Number(x.value)>_&&(x.value=String(_),C.textContent=g(_))}}}function jt(n){if(!Number.isFinite(n))return"\u2014";let i=Math.abs(n);return i!==0&&(i<.01||i>=1e6)?n.toExponential(2):Number(n.toFixed(i<1?4:2)).toString()}function An(n){return new Date(n).toISOString().slice(0,16).replace("T"," ")}function Dt(n){let i=Math.round(n/6e4);if(i<60)return`${i}m`;let h=Math.floor(i/60);if(h<24)return`${h}h`;let e=Math.floor(h/24),v=h%24;return v?`${e}d ${v}h`:`${e}d`}function ko(n){return n%86400===0?`${n/86400}d`:n%3600===0?`${n/3600}h`:n%60===0?`${n/60}min`:`${n}s`}function So(n,i){let h=i.tol,e=i.timestamps,v=(e.length?e[0]:0)-h,w=(e.length?e[e.length-1]:0)+h,C=a=>a.end>=v&&a.start<=w,g=(a,y)=>a.end>=y.start-h&&a.start<=y.end+h,x=i.incidents.filter(C),R=i.validatedSpans.filter(C).filter(a=>!x.some(y=>g(a,y))),_=[...x,...R],I=(a,y)=>a[1]>=y.start-h&&a[0]<=y.end+h,ae=0,ee=0,U=0;for(let a of n){let y=i.reviewFor(a[0],a[1]);y&&U++,y==="valid"&&ee++,(y==="valid"||y!=="false"&&_.some(L=>I(a,L)))&&ae++}let fe=0;for(let a of _)n.some(y=>I(y,a))&&fe++;let Q=n.length;return{realIncidents:_.length,caught:fe,recall:_.length?fe/_.length:NaN,totalAlerts:Q,correctAlerts:ae,falseAlerts:Q-ae,fdr:Q?(Q-ae)/Q:NaN,validated:ee,reviewed:U}}function Gn(n){return Number.isFinite(n)?`${Math.round(n*100)}%`:"\u2014"}function sn(n,i,h,e,v){return`<span class="dtk-m-chip${v!=null&&v.cls?" "+v.cls:""}"${v!=null&&v.title?` title="${v.title}"`:""}><span class="dtk-m-dot" style="background:${e}"></span><span class="dtk-m-v">${n}</span><span class="dtk-m-l">${i}</span>`+(h?`<span class="dtk-m-sub">${h}</span>`:"")+"</span>"}function Mo(n,i,h){let e=i.realIncidents>0,v=h!=null?`${Math.round(h*100)}%`:"",w=e&&i.totalAlerts>0&&h!=null&&Number.isFinite(i.fdr)&&i.fdr>h,C;if(!e)C="mark incidents to measure";else if(i.totalAlerts===0)C="no alerts";else if(i.falseAlerts===0)C=`${Gn(i.fdr)} \xB7 all correct`;else{let R=i.totalAlerts/i.falseAlerts,_=R>=9.5?String(Math.round(R)):String(Math.round(R*10)/10);C=`${Gn(i.fdr)} \xB7 \u22481 in ${_} false`}w&&(C+=` \xB7 \u25B2 over ${v} budget`);let g=i.totalAlerts===0?"no alerts":`${i.validated} valid${i.totalAlerts>i.reviewed?` \xB7 ${i.totalAlerts-i.reviewed} left`:" \xB7 all reviewed"}`,x=h?`Budget: at most ${v} 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.`:"";n.innerHTML=sn(String(i.realIncidents),"real incidents","","var(--c)")+sn(e?`${i.caught}/${i.realIncidents}`:"\u2014","caught",e?`recall ${Gn(i.recall)}`:"mark or confirm","var(--green)")+sn(String(i.totalAlerts),"alerts","","var(--c)")+sn(e?String(i.falseAlerts):"\u2014","false alerts",C,"var(--anom)",{cls:w?"over":"",title:x})+sn(`${i.reviewed}/${i.totalAlerts}`,"reviewed",g,"var(--green)")}var Co=!1;function To(){if(Co)return;Co=!0;let n=`
|
|
2
|
+
.dtk-tune{--c:#d15b36;--c7:#b4471f;--ink:#1b1916;--muted:#6e675b;--faint:#9a9384;
|
|
3
|
+
--paper:#f5f1e8;--surface:#fbf9f3;--border:#e6e0d4;--green:#2e9e73;--anom:#d63232;
|
|
4
|
+
--mono:'JetBrains Mono',ui-monospace,Menlo,monospace;
|
|
5
|
+
--sans:'Schibsted Grotesk',system-ui,-apple-system,Segoe UI,Roboto,sans-serif;}
|
|
6
|
+
.dtk-tune-root{max-width:1680px;margin:0 auto;padding:12px 16px;font-family:var(--sans);color:var(--ink);
|
|
7
|
+
height:100dvh;display:flex;flex-direction:column;gap:10px;overflow:hidden;}
|
|
8
|
+
.dtk-tune-header{flex:0 0 auto;}
|
|
9
|
+
.dtk-tune-titlerow{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;}
|
|
10
|
+
.dtk-tune-title{font-size:19px;margin:0;font-weight:700;}
|
|
11
|
+
.dtk-tune-badge{font-family:var(--mono);font-size:10px;text-transform:uppercase;letter-spacing:.06em;
|
|
12
|
+
color:#fff;background:var(--c);border-radius:999px;padding:3px 9px;}
|
|
13
|
+
.dtk-tune-sub{color:var(--muted);font-size:12px;margin-top:2px;font-family:var(--mono);}
|
|
14
|
+
.dtk-tune-desc{color:var(--muted);font-size:12px;margin-top:3px;white-space:pre-wrap;max-height:2.6em;overflow:auto;}
|
|
15
|
+
/* cockpit: chart-windshield (stage) + always-visible mode-aware control rail */
|
|
16
|
+
.dtk-tune-cockpit{display:flex;gap:12px;flex:1;min-height:0;}
|
|
17
|
+
.dtk-tune-stage{position:relative;display:flex;flex-direction:column;gap:8px;flex:1;min-width:0;min-height:0;}
|
|
18
|
+
.dtk-tune-hud{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;flex:0 0 auto;}
|
|
19
|
+
.dtk-tune-stagefoot{flex:0 0 auto;display:flex;flex-direction:column;gap:6px;}
|
|
20
|
+
.dtk-tune-rail{flex:0 0 340px;display:flex;flex-direction:column;min-height:0;background:var(--surface);
|
|
21
|
+
border:1px solid var(--border);border-radius:12px;overflow:hidden;}
|
|
22
|
+
.dtk-tune-railhead{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:8px;
|
|
23
|
+
padding:9px 12px;border-bottom:1px solid var(--border);}
|
|
24
|
+
.dtk-rail-title{font-family:var(--mono);font-size:11px;color:var(--muted);text-transform:uppercase;
|
|
25
|
+
letter-spacing:.06em;flex:1 1 auto;}
|
|
26
|
+
.dtk-tune-railfoot{flex:0 0 auto;display:flex;flex-direction:column;gap:8px;padding:11px 12px;
|
|
27
|
+
border-top:1px solid var(--border);background:var(--paper);}
|
|
28
|
+
.dtk-rail-open{position:absolute;top:50%;right:6px;transform:translateY(-50%);z-index:6;
|
|
29
|
+
border:1px solid var(--border);background:var(--surface);color:var(--ink);border-radius:8px;
|
|
30
|
+
padding:13px 7px;font-size:15px;cursor:pointer;box-shadow:0 1px 6px rgba(27,25,22,.14);}
|
|
31
|
+
.dtk-rail-open:hover{border-color:var(--c);color:var(--c7);}
|
|
32
|
+
.dtk-dock-toggle{flex:0 0 auto;border:1px solid var(--border);background:var(--surface);color:var(--muted);
|
|
33
|
+
border-radius:7px;padding:4px 10px;font-family:var(--sans);font-size:13px;font-weight:700;cursor:pointer;line-height:1;}
|
|
34
|
+
.dtk-dock-toggle:hover{border-color:var(--c);color:var(--c7);}
|
|
35
|
+
.dtk-tune-controls{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:14px;padding:14px;}
|
|
36
|
+
.dtk-rail-group{display:flex;flex-direction:column;gap:14px;}
|
|
37
|
+
.dtk-ctl{display:flex;flex-direction:column;gap:6px;}
|
|
38
|
+
.dtk-ctl-head{display:flex;justify-content:space-between;align-items:baseline;}
|
|
39
|
+
.dtk-ctl-label{font-size:12px;font-weight:600;color:var(--ink);}
|
|
40
|
+
.dtk-ctl-val{font-family:var(--mono);font-size:12px;color:var(--c7);}
|
|
41
|
+
.dtk-seg{display:flex;gap:4px;background:var(--paper);border:1px solid var(--border);border-radius:8px;padding:3px;}
|
|
42
|
+
.dtk-seg.dtk-wrap{flex-wrap:wrap;}
|
|
43
|
+
.dtk-seg-btn{flex:1 1 auto;border:0;background:transparent;color:var(--muted);font-family:var(--sans);
|
|
44
|
+
font-size:12px;padding:5px 8px;border-radius:6px;cursor:pointer;white-space:nowrap;}
|
|
45
|
+
.dtk-seg-btn:hover{color:var(--ink);}
|
|
46
|
+
.dtk-seg-btn.on{background:var(--c);color:#fff;font-weight:600;}
|
|
47
|
+
.dtk-range{width:100%;accent-color:var(--c);cursor:pointer;}
|
|
48
|
+
.dtk-check{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--muted);margin-top:2px;cursor:pointer;}
|
|
49
|
+
.dtk-tune-chart{position:relative;width:100%;flex:1;min-height:220px;background:var(--surface);
|
|
50
|
+
border:1px solid var(--border);border-radius:12px;overflow:hidden;}
|
|
51
|
+
.dtk-tune-chart canvas{width:100%;height:100%;display:block;}
|
|
52
|
+
.dtk-tune-readout{font-family:var(--mono);font-size:12px;color:var(--muted);min-height:18px;}
|
|
53
|
+
.dtk-tune-stat{font-family:var(--mono);font-size:12px;color:var(--ink);}
|
|
54
|
+
.dtk-tune-warn{font-family:var(--mono);font-size:12px;line-height:1.5;color:var(--c7);
|
|
55
|
+
background:rgba(240,173,78,0.13);border:1px solid rgba(240,173,78,0.5);border-radius:8px;padding:8px 11px;}
|
|
56
|
+
.dtk-tune-cfg{background:var(--ink);color:#c9c2b4;border-radius:8px;padding:8px 11px;font-family:var(--mono);
|
|
57
|
+
font-size:12px;overflow-x:auto;}
|
|
58
|
+
.dtk-tune-cfg-k{display:flex;width:100%;border:0;background:transparent;color:var(--faint);
|
|
59
|
+
font-family:var(--mono);font-size:11.5px;cursor:pointer;padding:0;text-align:left;}
|
|
60
|
+
.dtk-tune-cfg-k:hover{color:#e6e0d4;}
|
|
61
|
+
.dtk-tune-cfg-v{display:block;color:#e6e0d4;white-space:pre-wrap;word-break:break-word;margin-top:6px;}
|
|
62
|
+
.dtk-tune-apply{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
|
|
63
|
+
.dtk-apply-btn{background:var(--c);color:#fff;border:0;border-radius:8px;padding:10px 18px;font-family:var(--sans);
|
|
64
|
+
font-size:14px;font-weight:600;cursor:pointer;}
|
|
65
|
+
.dtk-apply-btn:hover{background:var(--c7);}
|
|
66
|
+
.dtk-apply-btn:disabled{opacity:.55;cursor:default;}
|
|
67
|
+
.dtk-apply-msg{font-size:13px;}
|
|
68
|
+
.dtk-apply-msg.ok{color:var(--green);}
|
|
69
|
+
.dtk-apply-msg.err{color:var(--anom);}
|
|
70
|
+
.dtk-apply-msg.info{color:var(--muted);}
|
|
71
|
+
.dtk-tune-note{font-size:13px;color:var(--muted);background:var(--surface);border:1px dashed var(--border);
|
|
72
|
+
border-radius:8px;padding:10px 12px;}
|
|
73
|
+
.dtk-ctl-info{color:var(--faint);font-size:10px;cursor:help;vertical-align:super;}
|
|
74
|
+
.dtk-tune-trim{display:flex;flex-direction:column;gap:6px;background:var(--surface);
|
|
75
|
+
border:1px solid var(--border);border-radius:10px;padding:9px 12px;}
|
|
76
|
+
.dtk-tune-trim-head{display:flex;justify-content:space-between;align-items:baseline;}
|
|
77
|
+
.dtk-tune-trim-val{font-family:var(--mono);font-size:12px;color:var(--c7);}
|
|
78
|
+
.dtk-tune-spin{position:absolute;top:10px;right:12px;display:none;align-items:center;gap:7px;
|
|
79
|
+
background:rgba(27,25,22,0.78);color:#e6e0d4;border:1px solid #332f29;border-radius:999px;
|
|
80
|
+
padding:4px 11px 4px 8px;font-family:var(--mono);font-size:11px;pointer-events:none;}
|
|
81
|
+
.dtk-tune-spin.on{display:inline-flex;}
|
|
82
|
+
.dtk-spin-ring{width:12px;height:12px;border-radius:50%;border:2px solid rgba(245,241,232,0.25);
|
|
83
|
+
border-top-color:var(--c);animation:dtk-spin .7s linear infinite;}
|
|
84
|
+
@keyframes dtk-spin{to{transform:rotate(360deg);}}
|
|
85
|
+
.dtk-tune-legend{flex:0 0 auto;display:flex;align-items:center;flex-wrap:wrap;gap:8px 16px;font-size:12px;
|
|
86
|
+
color:var(--muted);padding:6px 12px;background:var(--surface);border:1px solid var(--border);border-radius:9px;}
|
|
87
|
+
.dtk-leg-item{display:inline-flex;align-items:center;gap:6px;cursor:help;}
|
|
88
|
+
.dtk-leg-sw{display:inline-block;flex:0 0 auto;}
|
|
89
|
+
.dtk-leg-sw.line{width:16px;height:3px;background:var(--c);border-radius:2px;}
|
|
90
|
+
.dtk-leg-sw.band{width:16px;height:11px;background:rgba(209,91,54,0.18);
|
|
91
|
+
border:1px solid rgba(209,91,54,0.5);border-radius:2px;}
|
|
92
|
+
.dtk-leg-sw.center{width:16px;height:2px;
|
|
93
|
+
background:repeating-linear-gradient(90deg,var(--faint) 0 4px,transparent 4px 7px);}
|
|
94
|
+
.dtk-leg-sw.dot{width:9px;height:9px;border-radius:50%;background:var(--anom);}
|
|
95
|
+
.dtk-leg-sw.alert{width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;
|
|
96
|
+
border-top:7px solid var(--anom);}
|
|
97
|
+
.dtk-leg-sw.alert-ok{width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;
|
|
98
|
+
border-top:7px solid var(--green);}
|
|
99
|
+
.dtk-leg-sw.alert-no{width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;
|
|
100
|
+
border-top:7px solid #5a7a8c;}
|
|
101
|
+
.dtk-leg-txt{white-space:nowrap;}
|
|
102
|
+
.dtk-season-row{display:flex;align-items:center;justify-content:space-between;gap:8px;}
|
|
103
|
+
.dtk-season-col{font-family:var(--mono);font-size:11.5px;color:var(--muted);
|
|
104
|
+
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
105
|
+
.dtk-season-seg{flex:0 0 auto;padding:2px;}
|
|
106
|
+
.dtk-season-seg .dtk-seg-btn{flex:0 0 auto;padding:3px 7px;font-family:var(--mono);font-size:11px;}
|
|
107
|
+
.dtk-tune-metrics{display:flex;flex-wrap:wrap;gap:8px;margin:0;flex:0 1 auto;}
|
|
108
|
+
.dtk-m-chip{display:inline-flex;align-items:center;gap:7px;padding:7px 13px;background:var(--surface);
|
|
109
|
+
border:1px solid var(--border);border-radius:10px;font-size:13px;}
|
|
110
|
+
.dtk-m-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;}
|
|
111
|
+
.dtk-m-v{font-family:var(--mono);font-weight:700;font-size:15px;color:var(--ink);}
|
|
112
|
+
.dtk-m-l{color:var(--faint);font-family:var(--mono);font-size:11px;text-transform:uppercase;letter-spacing:.05em;}
|
|
113
|
+
.dtk-m-sub{color:var(--muted);font-family:var(--mono);font-size:11.5px;}
|
|
114
|
+
.dtk-m-chip.over{border-color:var(--anom);box-shadow:inset 0 0 0 1px rgba(214,50,50,.5);}
|
|
115
|
+
.dtk-m-chip.over .dtk-m-sub{color:var(--anom);font-weight:600;}
|
|
116
|
+
.dtk-tune-modes{display:inline-flex;gap:4px;background:var(--ink);border-radius:9px;padding:4px;margin:0;flex:0 0 auto;}
|
|
117
|
+
.dtk-mode-btn{border:0;background:transparent;color:#c9c2b4;font-family:var(--sans);font-size:13px;font-weight:600;
|
|
118
|
+
padding:7px 16px;border-radius:6px;cursor:pointer;transition:background .12s,color .12s;}
|
|
119
|
+
.dtk-mode-btn:hover{color:#fff;}
|
|
120
|
+
.dtk-mode-btn.on{background:var(--c);color:#fff;}
|
|
121
|
+
.dtk-tune-reviewbar{align-items:center;}
|
|
122
|
+
.dtk-tune-reviewbar .dtk-apply-btn{background:var(--green);}
|
|
123
|
+
.dtk-tune-reviewbar .dtk-apply-btn:hover{background:#27815d;}
|
|
124
|
+
.dtk-at-result{display:flex;flex-direction:column;gap:7px;background:var(--surface);
|
|
125
|
+
border:1px solid var(--border);border-radius:10px;padding:11px 13px;}
|
|
126
|
+
.dtk-at-head{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:8px;}
|
|
127
|
+
.dtk-at-winner{font-family:var(--mono);font-size:12.5px;font-weight:700;color:var(--ink);}
|
|
128
|
+
.dtk-at-score{font-family:var(--mono);font-size:11.5px;color:var(--c7);}
|
|
129
|
+
.dtk-at-meta{font-family:var(--mono);font-size:11px;color:var(--muted);word-break:break-word;}
|
|
130
|
+
.dtk-at-log{display:flex;flex-direction:column;gap:5px;max-height:240px;overflow:auto;margin-top:4px;}
|
|
131
|
+
.dtk-at-logline{display:flex;gap:8px;align-items:baseline;}
|
|
132
|
+
.dtk-at-stage{flex:0 0 auto;font-family:var(--mono);font-size:9.5px;text-transform:uppercase;
|
|
133
|
+
letter-spacing:.05em;color:#fff;background:var(--c);border-radius:4px;padding:1px 6px;}
|
|
134
|
+
.dtk-at-msg{font-family:var(--mono);font-size:11px;color:var(--ink);}
|
|
135
|
+
.dtk-th{display:flex;flex-direction:column;gap:8px;margin:2px 0 6px;}
|
|
136
|
+
.dtk-th-toggles{display:flex;gap:8px;flex-wrap:wrap;}
|
|
137
|
+
.dtk-th-toggle{align-self:flex-start;border:1px solid var(--border);background:var(--surface);
|
|
138
|
+
color:var(--muted);border-radius:8px;padding:6px 12px;font-family:var(--sans);font-size:12.5px;cursor:pointer;}
|
|
139
|
+
.dtk-th-toggle:hover{border-color:var(--c);color:var(--c7);}
|
|
140
|
+
.dtk-th-toggle.on{background:var(--c);border-color:var(--c);color:#fff;}
|
|
141
|
+
.dtk-th-bar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:10px 14px;padding:11px 13px;
|
|
142
|
+
background:var(--surface);border:1px solid var(--border);border-radius:10px;}
|
|
143
|
+
.dtk-th-grp{display:flex;flex-direction:column;gap:3px;}
|
|
144
|
+
.dtk-th-lbl{font-family:var(--mono);font-size:10.5px;color:var(--faint);text-transform:uppercase;letter-spacing:.05em;}
|
|
145
|
+
.dtk-th-num,.dtk-th-sel{background:var(--paper);color:var(--ink);border:1px solid var(--border);
|
|
146
|
+
border-radius:6px;padding:5px 8px;font-family:var(--mono);font-size:12px;}
|
|
147
|
+
.dtk-th-num{width:96px;}
|
|
148
|
+
.dtk-th-num:focus,.dtk-th-sel:focus{outline:none;border-color:var(--c);}
|
|
149
|
+
.dtk-th-scope{font-family:var(--mono);font-size:11px;color:var(--muted);align-self:center;flex:1 1 160px;}
|
|
150
|
+
.dtk-th-add{padding:7px 14px;}
|
|
151
|
+
.dtk-th-add:disabled{opacity:.5;cursor:default;}
|
|
152
|
+
.dtk-incidents{gap:8px;}
|
|
153
|
+
.dtk-inc-list{display:flex;flex-direction:column;gap:6px;max-height:240px;overflow:auto;}
|
|
154
|
+
.dtk-inc-empty{font-size:12px;color:var(--faint);font-style:italic;}
|
|
155
|
+
.dtk-inc-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap;background:var(--paper);
|
|
156
|
+
border:1px solid var(--border);border-radius:7px;padding:6px 8px;}
|
|
157
|
+
.dtk-inc-span{font-family:var(--mono);font-size:10.5px;color:var(--ink);}
|
|
158
|
+
.dtk-inc-dur{font-family:var(--mono);font-size:10.5px;color:var(--muted);}
|
|
159
|
+
.dtk-inc-label{flex:1 1 90px;min-width:70px;background:var(--surface);color:var(--ink);
|
|
160
|
+
border:1px solid var(--border);border-radius:5px;padding:4px 7px;font-family:var(--sans);font-size:11.5px;}
|
|
161
|
+
.dtk-inc-label:focus{outline:none;border-color:var(--c);}
|
|
162
|
+
.dtk-inc-btn{border:1px solid var(--border);background:var(--surface);color:var(--muted);border-radius:6px;
|
|
163
|
+
padding:3px 8px;font-size:11px;cursor:pointer;font-family:var(--sans);}
|
|
164
|
+
.dtk-inc-btn:hover{border-color:var(--c);color:var(--c7);}
|
|
165
|
+
.dtk-inc-del{color:var(--anom);}
|
|
166
|
+
.dtk-inc-fromalert{border-color:rgba(46,158,115,.5);background:rgba(46,158,115,.08);}
|
|
167
|
+
.dtk-inc-badge{flex:1 1 90px;min-width:70px;font-family:var(--mono);font-size:10.5px;color:var(--green);
|
|
168
|
+
display:inline-flex;align-items:center;font-weight:600;}
|
|
169
|
+
.dtk-setname{background:var(--surface);color:var(--ink);border:1px solid var(--border);border-radius:8px;
|
|
170
|
+
padding:9px 11px;font-family:var(--sans);font-size:13px;min-width:180px;}
|
|
171
|
+
.dtk-setname::placeholder{color:var(--faint);}
|
|
172
|
+
.dtk-setname:focus{outline:none;border-color:var(--c);}
|
|
173
|
+
.dtk-labels-btn{background:var(--surface);color:var(--ink);border:1px solid var(--border);}
|
|
174
|
+
.dtk-labels-btn:hover{background:var(--paper);border-color:var(--c);color:var(--c7);}
|
|
175
|
+
/* Narrow viewports: drop the cockpit to a scrolling stack (chart over rail). */
|
|
176
|
+
@media (max-width:900px){
|
|
177
|
+
.dtk-tune-root{height:auto;overflow:visible;}
|
|
178
|
+
.dtk-tune-cockpit{flex-direction:column;}
|
|
179
|
+
.dtk-tune-rail{flex:0 0 auto;width:100%;}
|
|
180
|
+
.dtk-tune-controls{overflow:visible;}
|
|
181
|
+
.dtk-tune-chart{flex:0 0 auto;height:54vh;min-height:320px;}
|
|
182
|
+
.dtk-rail-open{display:none!important;}
|
|
183
|
+
}
|
|
184
|
+
`,i=document.createElement("style");i.textContent=n,document.head.appendChild(i)}var Ao={mad:3,zscore:3,iqr:1.5,autoreg:3},_o={mad:10,zscore:3,iqr:4},Do="dtk-tune";function Po(n){let i=URL.createObjectURL(new Blob([n.src],{type:"text/javascript"})),h=n.series,e,v=0,w=!1,C=null,g=0,x=U=>{let fe=U.data;fe.type!=="result"||fe.id!==v||!C||(w=!1,n.onResult(fe,C))},R=()=>{w=!1,n.onError()};function _(){e=new Worker(i),e.onmessage=x,e.onerror=R,e.postMessage({type:"series",series:h})}_();let I=()=>{C=n.getParams(),n.onDispatch(C),v+=1,w&&(e.terminate(),_()),w=!0;let U=n.getShare();e.postMessage({type:"run",id:v,params:C,anomalyWindowPoints:U.windowPoints,minAnomalyShare:U.share})};return{postSeries:U=>{h=U,e.postMessage({type:"series",series:h})},recompute:()=>{g&&window.clearTimeout(g),g=window.setTimeout(I,130)},runNow:I}}function Uo(n,i){var oo,ro,io,so,ao,lo,co,uo,mo;To(),i.classList.add(Do),i.innerHTML="";let h=m("div","dtk-tune-root");i.appendChild(h);let e=n.points.length,v={timestamps:n.points.map(t=>t.t),values:n.points.map(t=>t.v==null?NaN:t.v),intervalSeconds:n.interval_seconds,truthAnomaly:new Array(e).fill(!1),seasonalityData:n.seasonality_columns.length?n.seasonality:void 0,seasonalityColumns:n.seasonality_columns.length?n.seasonality_columns:void 0},w=v,C=t=>{let s=Math.max(0,e-t);return s<=0?v:{timestamps:v.timestamps.slice(s),values:v.values.slice(s),intervalSeconds:v.intervalSeconds,truthAnomaly:v.truthAnomaly.slice(s),seasonalityData:v.seasonalityData?v.seasonalityData.slice(s):void 0,seasonalityColumns:v.seasonalityColumns}},g=t=>Date.parse(t.replace(" ","T")+"Z"),x=(n.incidents||[]).map(t=>({start:g(t.start),end:g(t.end),label:t.label||""})).filter(t=>Number.isFinite(t.start)&&Number.isFinite(t.end)).map(t=>({start:Math.min(t.start,t.end),end:Math.max(t.start,t.end),label:t.label})),R=(()=>{let t=(n.capture_windows||[])[0];if(!t)return null;let s=g(t.start),p=g(t.end);return!Number.isFinite(s)||!Number.isFinite(p)?null:{start:Math.min(s,p),end:Math.max(s,p)}})(),_=[],I=[],ae=[],ee=[],U=(n.alert_reviews||[]).map(t=>({start:g(t.start),end:g(t.end),verdict:t.verdict==="false"?"false":"valid"})).filter(t=>Number.isFinite(t.start)&&Number.isFinite(t.end)),fe=()=>n.interval_seconds*1e3/2,Q=(t,s)=>{let p=fe();for(let k of U)if(s>=k.start-p&&t<=k.end+p)return k.verdict;return null},a=(t,s,p)=>{let k=fe();for(let D=U.length-1;D>=0;D--){let b=U[D];s>=b.start-k&&t<=b.end+k&&U.splice(D,1)}p&&U.push({start:t,end:s,verdict:p})},y=(t,s)=>{let p=fe();return t.end>=s.start-p&&t.start<=s.end+p},L=()=>U.filter(t=>t.verdict==="valid").map(t=>({start:t.start,end:t.end,label:"confirmed alert"})),te=()=>L().filter(t=>!x.some(s=>y(t,s))),ve=()=>[...x,...te()],$=t=>{let s=!1;for(let p=U.length-1;p>=0;p--)U[p].verdict==="valid"&&y(U[p],t)&&(U.splice(p,1),s=!0);return s},re=()=>_.map((t,s)=>{var D;let p=(D=I[s])!=null?D:[t,t],k=Q(p[0],p[1]);return{t,kind:k==="valid"?"anomaly-validated":k==="false"?"anomaly-false":"anomaly"}}),H=n.detector,B=n.consecutive_anomalies,Ce=(oo=n.anomaly_window_points)!=null?oo:0,he=(ro=n.min_anomaly_share)!=null?ro:.3,pt=()=>Ce>=2?Ce:null,Pt=()=>Ce>=2?he:null,$e=n.detectors||[],le=typeof n.detector_index=="number"?n.detector_index:null,Te=le,Ae=new Set,ye=new Map,we=()=>{le!=null&&Ae.add(le)},V=v.values.filter(t=>Number.isFinite(t)).sort((t,s)=>t-s),G=V.length?V[0]:0,q=V.length?V[V.length-1]:1,De=t=>V.length?V[Math.min(V.length-1,Math.max(0,Math.round(t*(V.length-1))))]:0,je=Math.max((q-G)*.05,1e-9),ie=G-je,ce=q+je,Vt=Math.max((ce-ie)/400,1e-9),an=H.lowerBound!=null?H.lowerBound:De(.05),rt=H.upperBound!=null?H.upperBound:De(.95),qe=(io=H.seasonalityComponents)!=null?io:[],xe=new Map;qe.forEach((t,s)=>t.forEach(p=>xe.set(p,s+1)));let Xe=()=>{let t=0;xe.forEach(p=>{p>t&&(t=p)});let s=[];for(let p=1;p<=t;p++){let k=n.seasonality_columns.filter(D=>xe.get(D)===p);k.length&&s.push(k)}return s.length?s:null},Lt=t=>{if(!t||!n.seasonality.length)return 0;let s=0;for(let p of t){let k=new Set;for(let D of n.seasonality)k.add(p.map(b=>{var A;return String((A=D==null?void 0:D[b])!=null?A:"")}).join("|"));s=Math.max(s,k.size)}return s},Ee=()=>{var t;return{type:l.get(),threshold:f.get(),windowSize:X.get(),minSamples:H.minSamples,inputType:H.inputType,smoothing:Ge.get(),smoothingAlpha:H.smoothingAlpha,smoothingWindow:H.smoothingWindow,windowWeights:J.get(),halfLife:J.get()==="exponential"?M.get():null,detrend:oe.get(),stabilization:ge.get(),seasonalityComponents:Xe(),minSamplesPerGroup:(t=_o[l.get()])!=null?t:H.minSamplesPerGroup,consecutiveAnomalies:B,direction:Kn.get(),lowerBound:d.get(),upperBound:u.get(),lags:me.get()}},Ie=m("div","dtk-tune-header"),O=m("div","dtk-tune-titlerow");O.appendChild(m("h1","dtk-tune-title",n.metric));let ke=m("span","dtk-tune-badge",n.save_url?"manual tuning":"preview");O.appendChild(ke),Ie.appendChild(O);let ft=n.project?`${n.project} \xB7 `:"";Ie.appendChild(m("div","dtk-tune-sub",`${ft}${e} points \xB7 ${ko(n.interval_seconds)} grid`)),n.description&&Ie.appendChild(m("div","dtk-tune-desc",n.description)),h.appendChild(Ie);let Pe=m("div","dtk-tune-metrics"),it=m("div","dtk-tune-cockpit");h.appendChild(it);let ue=m("div","dtk-tune-stage");it.appendChild(ue);let Ut=m("div","dtk-tune-hud");Ut.appendChild(Pe),ue.appendChild(Ut);let st=m("div","dtk-tune-stagefoot"),ne=m("div","dtk-tune-rail");it.appendChild(ne);let Je=m("div","dtk-tune-railhead"),Re=m("span","dtk-rail-title","Tune \xB7 controls"),$t=m("button","dtk-dock-toggle","\u27E9");$t.type="button",$t.title="Collapse the control rail to give the chart the whole width.",Je.appendChild(Re),Je.appendChild($t),ne.appendChild(Je);let Qe=m("div","dtk-tune-controls");ne.appendChild(Qe);let ht=m("div","dtk-rail-group"),de=m("div","dtk-rail-group"),Nt=m("div","dtk-rail-group"),He=m("div","dtk-rail-group"),Oe=m("div","dtk-rail-group"),at=m("div","dtk-rail-group");Nt.style.display="none",He.style.display="none",Oe.style.display="none",Qe.append(ht,de,Nt,He,Oe,at);let bt=m("div","dtk-tune-railfoot");ne.appendChild(bt);let lt=m("button","dtk-rail-open","\u2699");lt.type="button",lt.title="Show the control rail",lt.style.display="none",ue.appendChild(lt);let ln=t=>{ne.style.display=t?"":"none",lt.style.display=t?"none":""};$t.onclick=()=>ln(!1),lt.onclick=()=>ln(!0);let Gt=m("div","dtk-tune-trim"),Yt=m("div","dtk-tune-trim-head");Yt.appendChild(At("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 gt=m("span","dtk-tune-trim-val");Yt.appendChild(gt),Gt.appendChild(Yt);let Le=m("input","dtk-range");Le.type="range",Le.min=String(Math.min(e,200)),Le.max=String(e),Le.step=String(Math.max(1,Math.round(e/200))),Le.value=String(e),Gt.appendChild(Le),ht.appendChild(Gt);let Ze=m("div","dtk-tune-chart"),be=m("canvas");Ze.appendChild(be);let et=m("div","dtk-tune-spin");et.appendChild(m("span","dtk-spin-ring")),et.appendChild(m("span","dtk-spin-txt","computing\u2026")),Ze.appendChild(et),ue.appendChild(Ze),ue.appendChild(st);let dn=m("div","dtk-tune-modes"),Kt={};[{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(t=>{let s=m("button","dtk-mode-btn",t.label);s.type="button",s.title=t.hint,s.dataset.v=t.v,s.onclick=()=>un(t.v),Kt[t.v]=s,dn.appendChild(s)}),Ut.appendChild(dn);let cn=m("div","dtk-tune-legend"),Fe=(t,s,p)=>{let k=m("span","dtk-leg-item");k.title=p,k.appendChild(m("span",`dtk-leg-sw ${t}`)),k.appendChild(m("span","dtk-leg-txt",s)),cn.appendChild(k)};Fe("alert","alert","A fired alert, not yet reviewed \u2014 enough consecutive anomalies to meet the rule."),Fe("alert-ok","valid alert","An alert you confirmed is real (click a marker in Review mode). Counts toward recall."),Fe("alert-no","false alarm","An alert you marked a false positive. Stays in the false-alert rate."),Fe("dot","anomaly","A point the detector flagged as anomalous (outside the band)."),Fe("line","metric","The metric value over time."),Fe("band","expected range","The detector's confidence band \u2014 values inside it read as normal."),Fe("center","band center","The expected value at the middle of the band."),ue.insertBefore(cn,Ze);let Et=m("div","dtk-tune-readout");st.appendChild(Et);let vt=m("div","dtk-tune-warn");vt.style.display="none",st.appendChild(vt);let Dn=t=>{let s=t.seasonalityComponents,p=Lt(s),k=t.minSamplesPerGroup*p;s&&p>0&&t.windowSize<k?(vt.textContent=`\u26A0 Seasonality inactive at this window: ${t.windowSize} < ${k} (min_samples_per_group ${t.minSamplesPerGroup} \xD7 ${p} key${p===1?"":"s"}). Each point keeps only ~${Math.floor(t.windowSize/p)} same-key point(s), so the band falls back to global statistics (seasonality has no effect). Raise the window to \u2265 ${k}.`,vt.style.display=""):vt.style.display="none"},yt=m("div","dtk-tune-warn");yt.style.display="none",st.appendChild(yt);let Pn=(t,s)=>{var A;let p=w.timestamps.length;if(p===0||t<p){yt.style.display="none";return}let k=s.stabilization==="clamp",D=s.type==="autoreg"?` (window ${s.windowSize} + lags ${Math.max(1,Math.round((A=s.lags)!=null?A:5))}${s.inputType!=="values"?" + 1 for change input":""}${k?` + window ${s.windowSize} for stabilization`:""})`:k?` (incl. a full window of ${s.windowSize} stabilization warm-up)`:"",b=[];e>t&&b.push(`raise Points shown above ${t}`),(s.type==="autoreg"||k)&&b.push("lower the Window size"),k&&b.push("turn Stabilization off"),s.type!=="autoreg"&&!k&&b.push("reduce min_samples / smoothing / the seasonality grouping (they set this warm-up)"),yt.textContent=`\u26A0 The whole view is detector warm-up: it needs ${t} pts${D} before full power, but only ${p} are shown \u2014 no band or anomaly dots can be drawn. To see the band: ${b.join(", or ")}.`,yt.style.display=""},qt=typeof n.false_alert_budget=="number"&&n.false_alert_budget>0?n.false_alert_budget:null;function wt(){let t=So(I,{timestamps:w.timestamps,tol:fe(),incidents:x,validatedSpans:L(),reviewFor:Q});Mo(Pe,t,qt)}let xt=()=>{},Xt=()=>{},se=wo(be,{navigable:!0,labeling:!0,mode:"tune",yFit:"data",onHover:t=>{if(!t||!t.point||!t.point.scored){Et.textContent="";return}let s=t.point;Et.textContent=`t=${An(s.timestamp)} value=${jt(s.value)} band=[${jt(s.lower)}, ${jt(s.upper)}]`+(s.isAnomaly?` \u26A0 ${s.direction} (sev ${s.severity.toFixed(2)})`:"")},onIncidentsChange:(t,s)=>{(s?$(s):!1)?dt():(yn(),wt())},onThresholdChange:t=>xt(t),onLassoChange:t=>Xt(t),onAlertReviewChange:(t,s)=>{let p=_.indexOf(t),k=p>=0?I[p]:[t,t];a(k[0],k[1],s==="unreviewed"?null:s),dt()}});se.setIncidents(x),R&&se.setCaptureWindow(R);let dt=()=>{ee=re(),nn&&se.render({series:w,scored:ae,params:nn,alerts:ee,incidents:x}),wt(),yn()},F=m("div","dtk-th"),Be=m("div","dtk-th-toggles"),Ne=m("button","dtk-th-toggle","Threshold capture");Ne.type="button",Ne.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.",Be.appendChild(Ne);let Se=m("button","dtk-th-toggle","Lasso anomalies");Se.type="button",Se.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.",Be.appendChild(Se),F.appendChild(Be);let Z=m("div","dtk-th-bar"),Ve=m("select","dtk-th-sel");for(let[t,s]of[["above","above the line"],["below","below the line"]]){let p=m("option");p.value=t,p.textContent=s,Ve.appendChild(p)}let tt=m("input","dtk-th-num");tt.type="number",tt.step="any",tt.placeholder="hover chart";let Ue=m("input","dtk-th-num");Ue.type="number",Ue.min="0",Ue.step="1",Ue.value="0";let Jt=m("span","dtk-th-scope"),ct=m("button","dtk-inc-btn","\u21BA whole view");ct.type="button",ct.style.display="none";let nt=m("button","dtk-apply-btn dtk-th-add","Add 0 spans");nt.type="button",nt.disabled=!0;let Rt=m("button","dtk-inc-btn","Done");Rt.type="button";let zt=(t,s)=>{let p=m("label","dtk-th-grp");return p.appendChild(m("span","dtk-th-lbl",t)),p.appendChild(s),p};Z.appendChild(zt("grab points",Ve)),Z.appendChild(zt("line value",tt)),Z.appendChild(zt("bridge gaps \u2264",Ue)),Z.appendChild(Jt),Z.appendChild(ct),Z.appendChild(nt),Z.appendChild(Rt),Z.style.display="none",F.appendChild(Z);let ut=m("div","dtk-th-bar"),Qt=m("span","dtk-th-scope","draw a loop around the anomaly dots on the chart below"),Wt=m("button","dtk-inc-btn","Done");Wt.type="button",ut.appendChild(Qt),ut.appendChild(Wt),ut.style.display="none",F.appendChild(ut),He.appendChild(F);let It=m("div","dtk-th-bar dtk-tune-reviewbar");It.appendChild(m("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 Zt=m("button","dtk-apply-btn dtk-th-add","Confirm all unreviewed valid");Zt.type="button",Zt.onclick=()=>{for(let[t,s]of I)Q(t,s)||a(t,s,"valid");dt()};let en=m("button","dtk-inc-btn","Clear verdicts");en.type="button",en.onclick=()=>{U.length=0,dt()},It.appendChild(Zt),It.appendChild(en),Nt.appendChild(It);let Ln={tune:"Tune \xB7 controls",review:"Review \xB7 verdicts",label:"Label \xB7 incidents",autotune:"Autotune \xB7 search"};function un(t){se.setMode(t==="autotune"?"tune":t),Object.keys(Kt).forEach(s=>{var p;return(p=Kt[s])==null?void 0:p.classList.toggle("on",s===t)}),t!=="label"&&(kt(!1),St(!1)),de.style.display=t==="tune"?"":"none",Nt.style.display=t==="review"?"":"none",He.style.display=t==="label"?"":"none",Oe.style.display=t==="autotune"?"":"none",bt.style.display=t==="tune"||t==="autotune"?"":"none",Re.textContent=Ln[t]}let Ht=!1,Ot=!1,tn=!1,kt=t=>{Ht=t,Ne.classList.toggle("on",t),Z.style.display=t?"flex":"none",se.setThresholdMode(t),t&&Ot&&St(!1)},St=t=>{Ot=t,Se.classList.toggle("on",t),ut.style.display=t?"flex":"none",se.setLassoMode(t),t&&Ht&&kt(!1)};Ne.onclick=()=>kt(!Ht),Rt.onclick=()=>kt(!1),Se.onclick=()=>St(!Ot),Wt.onclick=()=>St(!1),Ve.onchange=()=>se.setThresholdDirection(Ve.value),tt.oninput=()=>{let t=tt.value.trim();tn=!0,se.setThresholdValue(t!==""&&!isNaN(Number(t))?Number(t):null),tn=!1},Ue.oninput=()=>se.setThresholdGap(Number(Ue.value)||0),ct.onclick=()=>se.clearCaptureWindow(),nt.onclick=()=>{se.applyThreshold()},window.addEventListener("keydown",t=>{t.key==="Escape"&&(Ht?kt(!1):Ot&&St(!1))}),Xt=t=>{Qt.textContent=t.active?`${t.anomalies} anomal${t.anomalies===1?"y":"ies"} \u2192 ${t.incidents} incident${t.incidents===1?"":"s"} \u2014 release to add`:"draw a loop around the anomaly dots on the chart below"},xt=t=>{nt.textContent=`Add ${t.runs} span${t.runs===1?"":"s"}`,nt.disabled=t.runs===0,t.locked&&t.value!=null&&!tn&&(tt.value=String(Math.round(t.value*1e3)/1e3)),ct.style.display=t.committed?"":"none",Jt.textContent=t.window?`scope: ${Dt(t.windowMs)} painted`:"scope: current view \u2014 drag the chart to limit it"};let nn=null,on=Po({src:`"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 fe(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 me(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 he(n,e){return n.halfLife!==null?n.halfLife:Math.max(n.windowSize/20,e/2,1)}function ye(n,e){if(n.windowWeights==="none")return null;let t=n.windowSize,i=new Array(t);if(n.windowWeights==="exponential"){let r=he(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(y=>y>i),o=r.map(y=>!y),u=0,s=0;for(let y=0;y<e.length;y++)r[y]?u++:s++;if(u<2||s<2)return 0;let l=(y,_)=>_.filter((N,z)=>y[z]),a=l(o,n),c=l(r,n),w=l(o,e),p=l(r,e),v=l(o,t),d=l(r,t),A=V(a,v),f=V(c,d),k=V(w,v),h=V(p,d);return h===k?0:(A-f)/(h-k)}var Me={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=fe(e,t);return{mean:o,std:me(e,t,o,1)}}let i=$(e,t,25),r=$(e,t,75);return{q1:i,q3:r,iqr:r-i}}function Ae(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,w;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],d=o[p],A=t[p];if(Number.isNaN(d)){l.push(J(p,A,v,d,"missing_data"));continue}let f=!1,k=null,h=0;u!==null&&d<u&&(f=!0,k="below",h=u-d),s!==null&&d>s&&(f=!0,k="above",h=d-s);let y=0;if(f)if(u!==null&&s!==null){let x=s-u;y=x>0?h/x:1/0}else y=h;let _=u!=null?u:-1/0,N=s!=null?s:1/0,z=u!==null&&s!==null?(u+s)/2:(w=u!=null?u:s)!=null?w:NaN;l.push({index:p,timestamp:A,value:v,processedValue:d,scored:!0,isAnomaly:f,lower:_,upper:N,center:z,direction:k,severity:y,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),w=a?c.slice():c,p=[];for(let d=0;d<r;d++){let A=i[d],f=c[d],k=t[d];if(Number.isNaN(f)){p.push(J(d,k,A,f,"missing_data"));continue}let h=d>=o;if(h){for(let m=d-o;m<d;m++)if(!Number.isFinite(w[m])){h=!1;break}}if(!h){p.push(J(d,k,A,f,"missing_lags"));continue}let y=Math.max(o,d-u),_=y-o,N=[],z=[];for(let m=y;m<d;m++){let S=w[m];if(!Number.isFinite(S))continue;let g=!0,D=new Array(o);for(let R=0;R<o;R++){let Y=w[m-o+R];if(!Number.isFinite(Y)){g=!1;break}D[R]=Y}g&&(N.push(S),z.push(D))}let x=N.length;if(x<l){p.push(J(d,k,A,f,"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 S=new Array(o+1);S[0]=1;for(let g=0;g<o;g++)S[g+1]=(z[m][g]-P)/T;j[m]=S,I[m]=(N[m]-P)/T}let Q=ve(j,I),U=0;for(let m=0;m<x;m++){let S=0;for(let D=0;D<=o;D++)S+=j[m][D]*Q[D];let g=I[m]-S;U+=g*g}let C=Math.sqrt(U/Math.max(x-(o+1),1))*T,q=Q[0];for(let m=0;m<o;m++)q+=(w[d-o+m]-P)/T*Q[m+1];let F=q*T+P,W=F-s*C,L=F+s*C,E=f<W||f>L;if(a&&E){let m=f<W?W:L,S=1/0,g=-1/0;for(let D=_;D<d;D++){let R=c[D];Number.isFinite(R)&&(R<S&&(S=R),R>g&&(g=R))}S<=g&&(m=Math.min(Math.max(m,S),g)),w[d]=m}let X=null,b=0;if(E){let m;f<W?(X="below",m=W-f):(X="above",m=f-L),b=C>0?m/C:1/0}p.push({index:d,timestamp:k,value:A,processedValue:f,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=Me[t],{timestamps:o,values:u}=n,s=o.length,l=de(u,e),a=ee(l,e),c=e.stabilization==="clamp",w=c?a.slice():a,p=e.seasonalityComponents!==null&&e.seasonalityComponents.length>0&&Array.isArray(n.seasonalityData)&&n.seasonalityData.length>0,v=n.seasonalityData,d=ye(e,i),A=[];for(let f=0;f<s;f++){let k=u[f],h=a[f],y=o[f];if(Number.isNaN(h)){A.push(J(f,y,k,h,"missing_data"));continue}let _=Math.max(0,f-e.windowSize),N=f-_,z=w.slice(_,f),x=z.map(b=>!Number.isNaN(b)),P=[];for(let b=0;b<z.length;b++)x[b]&&P.push(z[b]);if(P.length<i){A.push(J(f,y,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,d),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[f];for(let m of e.seasonalityComponents){let S=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}S[M]=G}let g=[],D=[];for(let M=0;M<N;M++)x[M]&&S[M]&&(g.push(z[M]),D.push(O[M]));if(g.length<e.minSamplesPerGroup)continue;let R=ne(D,d),Y=I!==0?g.map((M,B)=>M+I*D[B]):g,se=te(t,Y,R);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]=Ae(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&&(w[f]=h<q?q:F);let X=ke(t,C);A.push({index:f,timestamp:y,value:k,processedValue:h,scored:!0,isAnomaly:W,lower:q,upper:F,center:X,direction:L,severity:E,reason:"ok"})}return A}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 Re(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 ze(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 w=c;for(let p=Math.max(0,c-e+1);p<=c;p++)if(r[p]){w=p;break}s={fire:c,startTs:n[w].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=Re(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,ze(o,l,a)));let c=s.map(f=>f.fire),w=s.map(f=>[f.startTs,f.endTs]),p=ie(K,r),v=Math.min(p,K.timestamps.length),d=0;for(let f of o)f.scored&&f.isAnomaly&&d++;let A={type:"result",id:e.id,scored:o,fires:c,fireSpans:w,eff:v,need:p,flagged:d};self.postMessage(A)}};})();
|
|
185
|
+
`,series:w,getParams:Ee,getShare:()=>({windowPoints:pt(),share:Pt()}),onDispatch:t=>{nn=t,le!=null&&ye.set(le,t),et.classList.add("on")},onResult:(t,s)=>{et.classList.remove("on"),ae=t.scored,_=t.fires.map(p=>w.timestamps[p]),I=t.fireSpans,ee=re(),se.render({series:w,scored:t.scored,params:s,alerts:ee,incidents:x}),wt(),zn.textContent=`${t.flagged} flagged \xB7 ${t.fires.length} alert${t.fires.length===1?"":"s"} \xB7 warm-up ${t.need} pts`,Dn(s),Pn(t.need,s),In.textContent=xo(s,B,pt(),Pt())},onError:()=>{et.classList.remove("on"),zn.textContent="recompute failed \u2014 see the browser console"}}),ze=on.recompute,mn=t=>t>1?Dt(v.timestamps[e-1]-v.timestamps[e-t]):"\u2014",pn=t=>{gt.textContent=t>=e?`${e} pts \xB7 full (${mn(e)})`:`${t} pts \xB7 ${mn(t)}`};pn(e);function fn(t){w=C(t),on.postSeries(w),W(),ze()}Le.oninput=()=>{pn(Number(Le.value))},Le.onchange=()=>{fn(Number(Le.value))};let o=()=>{we(),W(),ze()},r=$e.filter(t=>t.tunable),c=$e.filter(t=>!t.tunable);if($e.length>1){let t=m("div","dtk-ctl dtk-tune-detpick");if(t.appendChild(At("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.")),r.length>1){let p=m("div","dtk-seg dtk-detpick-seg"),k=[],D=()=>k.forEach(b=>b.classList.toggle("on",Number(b.dataset.v)===le));r.forEach(b=>{let A=m("button","dtk-seg-btn",`#${b.index+1} ${b.type}`);A.type="button",A.dataset.v=String(b.index),A.title=b.summary,A.onclick=()=>{Ro(b.index),D()},k.push(A),p.appendChild(A)}),D(),t.appendChild(p)}let s=[];c.length&&s.push(`Preserved (not tunable here): ${c.map(p=>p.summary).join("; ")}.`),s.push("Apply rewrites only the detector(s) you tune and keeps the rest verbatim."),t.appendChild(m("div","dtk-tune-note",s.join(" "))),de.appendChild(t)}let l=_t("Detector",[{label:"MAD",value:"mad"},{label:"Z-Score",value:"zscore"},{label:"IQR",value:"iqr"},{label:"Autoreg",value:"autoreg"},{label:"Manual",value:"manual_bounds"}],H.type,t=>{var s;T((s=Ao[t])!=null?s:3),we(),wn(),W(),ze()},"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).");de.appendChild(l.row);let d=Ke("Lower bound",{min:ie,max:ce,step:Vt,value:an,fmt:t=>jt(t),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)."},o);de.appendChild(d.row);let u=Ke("Upper bound",{min:ie,max:ce,step:Vt,value:rt,fmt:t=>jt(t),hint:"Manual bounds: values above this read as anomalous."},o);de.appendChild(u.row);let f=Ke("Threshold (\u03C3-equivalent)",{min:.5,max:10,step:.1,value:H.threshold,fmt:t=>t.toFixed(1),hint:"Band half-width in \u03C3-equivalents. Lower = tighter band = more flags; higher = wider band = fewer flags."},o);de.appendChild(f.row);let S=f.row.querySelector("input"),N=f.row.querySelector(".dtk-ctl-val"),T=t=>{S&&(S.value=String(t)),N&&(N.textContent=t.toFixed(1))},E=(t,s,p,k,D)=>{let b=Math.floor(2*s/3)-(t==="autoreg"?p+(D?1:0):0),A=Math.floor(t==="autoreg"?b/(k?2:1):s/2);return Math.max(50,Math.min(2e3,A))},Y=Math.max(E(H.type,e,Math.max(1,Math.round((so=H.lags)!=null?so:5)),((ao=H.stabilization)!=null?ao:"none")==="clamp",H.inputType!=="values"),H.windowSize),X=Ke("Window size (points)",{min:Math.max(1,Math.min(10,H.windowSize)),max:Y,step:1,value:H.windowSize,fmt:t=>`${t} \xB7 ${Dt(t*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."},o);de.appendChild(X.row);let me=Ke("Lags (AR order)",{min:1,max:24,step:1,value:Math.max(1,Math.round((lo=H.lags)!=null?lo:5)),fmt:t=>String(t),hint:"Autoreg: how many immediately-preceding values predict the current one. More lags capture longer short-range patterns but need more history per fit."},o);de.appendChild(me.row);let J=_t("Recency weighting",[{label:"none",value:"none"},{label:"exponential",value:"exponential"},{label:"linear",value:"linear"}],H.windowWeights,()=>{we(),wn(),ze()},"Weight recent points in the window more heavily: none (flat), exponential (half-life decay) or linear. Helps the baseline track a drifting level.");de.appendChild(J.row);let M=Ke("Half-life (points)",{min:1,max:Y,step:1,value:(co=H.halfLife)!=null?co:Math.max(5,Math.round(H.windowSize/20)),fmt:t=>`${t} \xB7 ${Dt(t*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."},o),z=M.row;z.style.display=H.windowWeights==="exponential"?"":"none",de.appendChild(z);let W=()=>{var s;let t=Math.max(E(l.get(),w.timestamps.length,Math.max(1,Math.round(me.get())),ge.get()==="clamp",H.inputType!=="values"),H.windowSize,X.get());X.setMax(t),M.setMax(Math.max(t,(s=H.halfLife)!=null?s:0,M.get()))},oe=_t("Detrend",[{label:"none",value:"none"},{label:"linear",value:"linear"}],H.detrend,o,"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.");de.appendChild(oe.row);let ge=_t("Stabilization",[{label:"none",value:"none"},{label:"clamp",value:"clamp"}],(uo=H.stabilization)!=null?uo:"none",o,"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.");de.appendChild(ge.row);let Ge=_t("Smoothing",[{label:"none",value:"none"},{label:"EMA",value:"ema"},{label:"SMA",value:"sma"}],H.smoothing,o,"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.");de.appendChild(Ge.row);let $n=null,Yn=()=>{};if(n.seasonality_columns.length){let t=n.seasonality_columns,s=m("div","dtk-ctl");$n=s,s.appendChild(At("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 p=t.length,k=[{label:"\u2014",value:"0"}];for(let b=1;b<=p;b++)k.push({label:`G${b}`,value:String(b)});let D=[];t.forEach(b=>{let A=m("div","dtk-season-row");A.appendChild(m("span","dtk-season-col",b));let We=m("div","dtk-seg dtk-season-seg"),mt=[],Mn=()=>{var pe;return(pe=xe.get(b))!=null?pe:0},Bt=()=>mt.forEach(pe=>pe.classList.toggle("on",Number(pe.dataset.v)===Mn()));D.push(Bt),k.forEach(pe=>{let Ye=m("button","dtk-seg-btn",pe.label);Ye.type="button",Ye.dataset.v=pe.value,Ye.title=pe.value==="0"?`ignore ${b}`:`put ${b} in group ${pe.value}`,Ye.onclick=()=>{xe.set(b,Number(pe.value)),Bt(),o()},mt.push(Ye),We.appendChild(Ye)}),Bt(),A.appendChild(We),s.appendChild(A)}),de.appendChild(s),Yn=b=>{xe.clear(),b.forEach((A,We)=>A.forEach(mt=>xe.set(mt,We+1))),D.forEach(A=>A())}}let Kn=_t("Direction",[{label:"both",value:"any"},{label:"up",value:"up"},{label:"down",value:"down"}],(mo=n.direction)!=null?mo:"any",ze,"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.");at.appendChild(Kn.row);let qn=Ke("Alert: consecutive anomalies",{min:1,max:10,step:1,value:B,fmt:t=>String(t),hint:"How many anomalies in a row are required before an alert fires. Higher = fewer, more-confident alerts (the \u25BC markers on the chart)."},t=>{B=t,ze()});at.appendChild(qn.row);let hn=Ke("Alert: anomaly window (points)",{min:0,max:Math.max(96,Ce),step:1,value:Ce,fmt:t=>t>=2?`${t} \xB7 ${Dt(t*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."},t=>{Ce=t,En(),ze()});at.appendChild(hn.row);let Nn=Ke("Alert: min share in window",{min:.05,max:1,step:.05,value:he,fmt:t=>`${Math.round(t*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)."},t=>{he=t,ze()});at.appendChild(Nn.row);let En=()=>{Nn.row.style.display=Ce>=2?"":"none"};En();let Xn=m("div","dtk-ctl"),bn=m("label","dtk-check"),gn=m("input");gn.type="checkbox",gn.onchange=()=>{se.setZeroLine(gn.checked)},bn.title="Draw a horizontal line at y = 0 and include zero in the scale \u2014 for real-valued metrics best read relative to zero.",bn.appendChild(gn),bn.appendChild(document.createTextNode(" Show y = 0 line")),Xn.appendChild(bn),at.appendChild(Xn);let Rn=m("div","dtk-ctl dtk-incidents");Rn.appendChild(At("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 vn=m("div","dtk-inc-list");Rn.appendChild(vn),He.appendChild(Rn);function Lo(t){let s=Math.max((t.end-t.start)*.5,n.interval_seconds*1e3*5);se.setViewWindow(t.start-s,t.end+s)}function $o(t){let s=x.indexOf(t);s>=0&&x.splice(s,1);let p=$(t);se.setIncidents(x),p?dt():(yn(),wt())}function No(t){a(t.start,t.end,null),se.setIncidents(x),dt()}function yn(){vn.innerHTML="";let t=[...x.map(s=>({iv:s,kind:"manual"})),...te().map(s=>({iv:s,kind:"alert"}))].sort((s,p)=>s.iv.start-p.iv.start);if(!t.length){vn.appendChild(m("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:s,kind:p}of t){let k=m("div","dtk-inc-row"+(p==="alert"?" dtk-inc-fromalert":""));if(k.appendChild(m("span","dtk-inc-span",`${An(s.start)} \u2192 ${An(s.end)}`)),k.appendChild(m("span","dtk-inc-dur",Dt(Math.max(0,s.end-s.start)))),p==="alert"){let A=m("span","dtk-inc-badge","\u2713 confirmed alert");A.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.",k.appendChild(A)}else{let A=m("input","dtk-inc-label");A.type="text",A.value=s.label||"",A.placeholder="label (optional)",A.oninput=()=>{s.label=A.value},k.appendChild(A)}let D=m("button","dtk-inc-btn","focus");D.type="button",D.onclick=()=>Lo(s),k.appendChild(D);let b=m("button","dtk-inc-btn dtk-inc-del","\u2715");b.type="button",b.title=p==="alert"?"un-confirm this alert":"remove this incident",b.onclick=()=>p==="alert"?No(s):$o(s),k.appendChild(b),vn.appendChild(k)}}let Eo=[f.row,X.row,ge.row],Jn=[J.row,oe.row,Ge.row];$n&&Jn.push($n);function wn(){let t=l.get(),s=t==="manual_bounds",p=t==="autoreg";for(let k of Eo)k.style.display=s?"none":"";for(let k of Jn)k.style.display=s||p?"none":"";me.row.style.display=p?"":"none",d.row.style.display=s?"":"none",u.row.style.display=s?"":"none",z.style.display=!s&&!p&&J.get()==="exponential"?"":"none"}wn();function Qn(t){var s,p;H=t,l.set(t.type),f.set(t.threshold),W(),X.set(t.windowSize),J.set(t.windowWeights),t.windowWeights==="exponential"&&t.halfLife!=null&&M.set(t.halfLife),oe.set(t.detrend),ge.set((s=t.stabilization)!=null?s:"none"),Ge.set(t.smoothing),Yn(t.seasonalityComponents||[]),t.lowerBound!=null&&d.set(t.lowerBound),t.upperBound!=null&&u.set(t.upperBound),(t.lags!=null||t.type==="autoreg")&&me.set(Math.max(1,Math.round((p=t.lags)!=null?p:5))),wn()}function Ro(t){var p;if(t===le)return;let s=$e.find(k=>k.index===t);!s||!s.tunable||!s.seed||(le!=null&&ye.set(le,Ee()),le=t,Qn((p=ye.get(t))!=null?p:s.seed),ze())}let zn=m("div","dtk-tune-stat");st.appendChild(zn);let Wn=m("div","dtk-tune-cfg"),rn=m("button","dtk-tune-cfg-k");rn.type="button",rn.title="Show or hide the exact config that will be written on Apply.";let In=m("code","dtk-tune-cfg-v"),Zn=!1,eo=t=>{Zn=t,In.style.display=t?"":"none",rn.textContent=`${t?"\u25BE":"\u25B8"} // effective config`};if(rn.onclick=()=>eo(!Zn),Wn.appendChild(rn),Wn.appendChild(In),eo(!1),bt.appendChild(Wn),n.save_url){let t=m("div","dtk-tune-apply"),s=m("button","dtk-apply-btn","Apply to metric");s.type="button",s.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 p=m("span","dtk-apply-msg");s.onclick=()=>{let k=Ee();le!=null&&ye.set(le,k);let D=[];if(Te!=null)for(let b of new Set([Te,...Ae])){let A=ye.get(b);A&&D.push({index:b,type:A.type,params:Tn(A)})}else D.push({index:null,type:k.type,params:Tn(k)});s.disabled=!0,p.className="dtk-apply-msg info",p.textContent="Applying\u2026",fetch(n.save_url,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detectors:D,consecutive_anomalies:B,anomaly_window:pt()?`${pt()*n.interval_seconds}s`:null,min_anomaly_share:Pt()})}).then(b=>b.ok?b.json():b.text().then(A=>{throw new Error(A||`HTTP ${b.status}`)})).then(b=>{var We;p.className="dtk-apply-msg ok";let A=b.preserved&&b.preserved.length?` Kept: ${b.preserved.join(", ")}.`:"";p.textContent=`Applied \u2192 ${(We=b.saved)!=null?We:"metric"} (previous archived).${A} You can close this tab.`}).catch(b=>{s.disabled=!1,p.className="dtk-apply-msg err",p.textContent=`Apply failed: ${b.message}`})},t.appendChild(s),t.appendChild(p),bt.appendChild(t)}else bt.appendChild(m("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 zo=t=>'"'+t.replace(/\\/g,"\\\\").replace(/"/g,'\\"')+'"',Ft=t=>new Date(t).toISOString().slice(0,19).replace("T"," "),to=()=>{let t=[`metric: ${n.metric}`,"timezone: UTC"],s=new Set,p=ve().sort((D,b)=>D.start-b.start).filter(D=>{let b=`${Math.round(D.start)}:${Math.round(D.end)}`;return s.has(b)?!1:(s.add(b),!0)});if(!p.length)t.push("incidents: []");else{t.push("incidents:");for(let D of p){let b=D.label?`, label: ${zo(D.label)}`:"";t.push(` - {start: "${Ft(D.start)}", end: "${Ft(D.end)}"${b}}`)}}let k=se.getCaptureWindow();if(k&&(t.push("capture_windows:"),t.push(` - {start: "${Ft(k.start)}", end: "${Ft(k.end)}"}`)),U.length){t.push("alert_reviews:");for(let D of[...U].sort((b,A)=>b.start-A.start))t.push(` - {start: "${Ft(D.start)}", end: "${Ft(D.end)}", verdict: ${D.verdict}}`)}return t.join(`
|
|
186
|
+
`)+`
|
|
187
|
+
`},xn=m("div","dtk-tune-apply"),kn=m("input","dtk-setname");kn.type="text",kn.placeholder="name this set (optional)";let Sn=m("button","dtk-apply-btn dtk-labels-btn",n.labels_save_url?"Save incidents":"Download incidents");Sn.type="button",Sn.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 ot=m("span","dtk-apply-msg");Sn.onclick=()=>{let t=to();if(!n.labels_save_url){let s=new Blob([t],{type:"text/yaml"}),p=m("a");p.href=URL.createObjectURL(s),p.download=`${n.metric}.yml`,p.click(),URL.revokeObjectURL(p.href),ot.className="dtk-apply-msg ok",ot.textContent=`Downloaded \u2014 drop it into incidents/${n.metric}/`;return}ot.className="dtk-apply-msg info",ot.textContent="Saving\u2026",fetch(n.labels_save_url,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:kn.value,yaml:t})}).then(s=>s.ok?s.json():s.text().then(p=>{throw new Error(p||`HTTP ${s.status}`)})).then(s=>{var p;ot.className="dtk-apply-msg ok",ot.textContent=`Saved \u2192 ${(p=s.saved)!=null?p:"incidents"} (keep tuning, or Apply the detector)`}).catch(s=>{ot.className="dtk-apply-msg err",ot.textContent=`Save failed: ${s.message}`})},xn.appendChild(kn),xn.appendChild(Sn),xn.appendChild(ot),He.appendChild(xn);function Wo(t){Qn(t.detector),we(),t.consecutive_anomalies!=null&&(B=t.consecutive_anomalies,qn.set(B)),t.anomaly_window_points!=null&&t.min_anomaly_share!=null?(Ce=t.anomaly_window_points,he=t.min_anomaly_share,hn.setMax(Math.max(96,Ce)),hn.set(Ce),Nn.set(he)):(Ce=0,hn.set(0)),En(),ze()}if(Oe.appendChild(m("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 t=m("div","dtk-tune-apply"),s=m("button","dtk-apply-btn","Run autotune");s.type="button",s.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 p=m("span","dtk-apply-msg");t.appendChild(s),t.appendChild(p),Oe.appendChild(t);let k=m("div","dtk-at-result");k.style.display="none",Oe.appendChild(k);let D=b=>{k.innerHTML="",k.style.display="";let A=m("div","dtk-at-head");A.appendChild(m("span","dtk-at-winner",b.winner)),A.appendChild(m("span","dtk-at-score",`${b.scoring_metric} ${Number.isFinite(b.score)?b.score.toFixed(3):"\u2014"} \xB7 ${b.mode}`)),k.appendChild(A),k.appendChild(m("div","dtk-at-meta",`${b.n_candidates} candidate${b.n_candidates===1?"":"s"} \xB7 ${b.n_points} pts`+(b.consecutive_anomalies!=null?` \xB7 consecutive ${b.consecutive_anomalies}`:"")+(b.anomaly_window_points!=null&&b.min_anomaly_share!=null?` \xB7 window ${b.anomaly_window_points}p \xD7 ${b.min_anomaly_share}`:"")+(b.seasonality&&b.seasonality.length?` \xB7 seasonality ${JSON.stringify(b.seasonality)}`:" \xB7 no seasonality")));let We=m("button","dtk-tune-cfg-k");We.type="button";let mt=m("div","dtk-at-log"),Mn=!1,Bt=pe=>{Mn=pe,mt.style.display=pe?"":"none",We.textContent=`${pe?"\u25BE":"\u25B8"} decision log (${b.decision_log.length})`};We.onclick=()=>Bt(!Mn);for(let pe of b.decision_log){let Ye=m("div","dtk-at-logline");Ye.appendChild(m("span","dtk-at-stage",pe.stage)),Ye.appendChild(m("span","dtk-at-msg",pe.message)),mt.appendChild(Ye)}Bt(!1),k.appendChild(We),k.appendChild(mt)};s.onclick=()=>{s.disabled=!0,p.className="dtk-apply-msg info",p.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:to(),window:w.timestamps.length?{start:w.timestamps[0],end:w.timestamps[w.timestamps.length-1]}:null})}).then(b=>b.ok?b.json():b.text().then(A=>{throw new Error(A||`HTTP ${b.status}`)})).then(b=>{s.disabled=!1,Wo(b),D(b);let A=b.mode==="supervised"?" \u2014 supervised by your incidents":" \u2014 unsupervised (mark incidents for a supervised tune)";p.className="dtk-apply-msg ok",p.textContent=`Tuned${A}. Knobs updated; review the band, then Apply.`}).catch(b=>{s.disabled=!1,p.className="dtk-apply-msg err",p.textContent=`Autotune failed: ${b.message}`})}}else Oe.appendChild(m("div","dtk-tune-note","Autotune needs the live server \u2014 run `dtk tune` without --no-serve."));un("tune"),yn(),on.runNow(),wt();let Hn=0,no=()=>{Hn&&cancelAnimationFrame(Hn),Hn=requestAnimationFrame(()=>se.resize())};typeof ResizeObserver!="undefined"?new ResizeObserver(no).observe(Ze):window.addEventListener("resize",no)}window.__DTK_TUNE__={render:Uo};})();
|
|
@@ -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",
|