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