hyper-scheduler 1.1.1 → 1.1.3
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.
- package/README.md +5 -1
- package/dist/{devtools-_ah1nNTk.js → devtools-BkwHsmeC.js} +102 -97
- package/dist/devtools-Bnqvx4Qg.cjs +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/index.umd.js +1 -0
- package/dist/task-list.png +0 -0
- package/package.json +1 -1
- package/public/task-list.png +0 -0
- package/dist/devtools-xPwLHcqi.cjs +0 -1
- package/dist/index.umd.cjs +0 -1
package/README.md
CHANGED
|
@@ -19,6 +19,10 @@ A lightweight, dependency-free (core) JavaScript task scheduler supporting Cron
|
|
|
19
19
|
- 🛠 **Debuggable**: Built-in debug panel and CLI output.
|
|
20
20
|
- 📦 **Tiny**: < 20KB gzipped.
|
|
21
21
|
|
|
22
|
+
## DevTools
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
22
26
|
## Quick Start
|
|
23
27
|
|
|
24
28
|
```bash
|
|
@@ -52,4 +56,4 @@ scheduler.start();
|
|
|
52
56
|
// Or start only 'system' namespace: scheduler.start('system');
|
|
53
57
|
```
|
|
54
58
|
|
|
55
|
-
See [Documentation](
|
|
59
|
+
See [Documentation](https://crazymryan.github.io/hyper-scheduler) for more details.
|
|
@@ -407,9 +407,12 @@ class FloatingTrigger extends HTMLElement {
|
|
|
407
407
|
__publicField(this, "_offsetX", 0);
|
|
408
408
|
__publicField(this, "_offsetY", 0);
|
|
409
409
|
__publicField(this, "_isCollapsed", false);
|
|
410
|
+
// 新增:收起状态
|
|
411
|
+
// 内部状态:保存展开时的位置
|
|
412
|
+
__publicField(this, "_savedX", 0);
|
|
413
|
+
__publicField(this, "_savedY", 0);
|
|
410
414
|
this._shadow = this.attachShadow({ mode: "open" });
|
|
411
415
|
}
|
|
412
|
-
// 新增:收起状态
|
|
413
416
|
static get observedAttributes() {
|
|
414
417
|
return ["position", "bg-color", "text-color"];
|
|
415
418
|
}
|
|
@@ -417,7 +420,9 @@ class FloatingTrigger extends HTMLElement {
|
|
|
417
420
|
this.loadState();
|
|
418
421
|
this.render();
|
|
419
422
|
this.addEventListeners();
|
|
420
|
-
|
|
423
|
+
requestAnimationFrame(() => {
|
|
424
|
+
this.applyPosition();
|
|
425
|
+
});
|
|
421
426
|
window.addEventListener("resize", this.onResize.bind(this));
|
|
422
427
|
}
|
|
423
428
|
disconnectedCallback() {
|
|
@@ -429,7 +434,9 @@ class FloatingTrigger extends HTMLElement {
|
|
|
429
434
|
attributeChangedCallback(name, _oldVal, newVal) {
|
|
430
435
|
if (name === "position") {
|
|
431
436
|
this._position = newVal || "bottom-right";
|
|
432
|
-
this.
|
|
437
|
+
if (!this.style.getPropertyValue("--hs-trigger-position-set")) {
|
|
438
|
+
this.resetPositionToDefault();
|
|
439
|
+
}
|
|
433
440
|
} else if (name === "bg-color") {
|
|
434
441
|
this._bgColor = newVal || "";
|
|
435
442
|
this.updateStyles();
|
|
@@ -443,9 +450,11 @@ class FloatingTrigger extends HTMLElement {
|
|
|
443
450
|
const savedPos = localStorage.getItem(STORAGE_KEY_POS);
|
|
444
451
|
if (savedPos) {
|
|
445
452
|
const { x, y } = JSON.parse(savedPos);
|
|
446
|
-
this.
|
|
447
|
-
this.
|
|
453
|
+
this._savedX = parseFloat(x);
|
|
454
|
+
this._savedY = parseFloat(y);
|
|
448
455
|
this.style.setProperty("--hs-trigger-position-set", "true");
|
|
456
|
+
} else {
|
|
457
|
+
this.resetPositionToDefault();
|
|
449
458
|
}
|
|
450
459
|
const savedCollapsed = localStorage.getItem(STORAGE_KEY_COLLAPSED);
|
|
451
460
|
if (savedCollapsed === "true") {
|
|
@@ -453,60 +462,45 @@ class FloatingTrigger extends HTMLElement {
|
|
|
453
462
|
}
|
|
454
463
|
} catch (e) {
|
|
455
464
|
console.warn("[FloatingTrigger] Failed to load state:", e);
|
|
465
|
+
this.resetPositionToDefault();
|
|
456
466
|
}
|
|
457
467
|
}
|
|
468
|
+
resetPositionToDefault() {
|
|
469
|
+
const width = 48;
|
|
470
|
+
const height = 48;
|
|
471
|
+
const padding = 20;
|
|
472
|
+
if (this._position.includes("left")) this._savedX = padding;
|
|
473
|
+
else this._savedX = window.innerWidth - width - padding;
|
|
474
|
+
if (this._position.includes("top")) this._savedY = padding;
|
|
475
|
+
else this._savedY = window.innerHeight - height - padding;
|
|
476
|
+
}
|
|
458
477
|
saveState() {
|
|
459
|
-
|
|
460
|
-
if (!button) return;
|
|
478
|
+
localStorage.setItem(STORAGE_KEY_POS, JSON.stringify({ x: this._savedX, y: this._savedY }));
|
|
461
479
|
localStorage.setItem(STORAGE_KEY_COLLAPSED, String(this._isCollapsed));
|
|
462
|
-
if (!this._isCollapsed) {
|
|
463
|
-
const rect = button.getBoundingClientRect();
|
|
464
|
-
localStorage.setItem(STORAGE_KEY_POS, JSON.stringify({ x: rect.left, y: rect.top }));
|
|
465
|
-
} else {
|
|
466
|
-
try {
|
|
467
|
-
const savedPos = localStorage.getItem(STORAGE_KEY_POS);
|
|
468
|
-
if (savedPos) {
|
|
469
|
-
const { x } = JSON.parse(savedPos);
|
|
470
|
-
const rect = button.getBoundingClientRect();
|
|
471
|
-
localStorage.setItem(STORAGE_KEY_POS, JSON.stringify({ x, y: rect.top }));
|
|
472
|
-
}
|
|
473
|
-
} catch (e) {
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
480
|
}
|
|
477
481
|
applyPosition() {
|
|
478
482
|
const button = this._shadow.querySelector("button");
|
|
479
483
|
if (!button) return;
|
|
480
|
-
const
|
|
481
|
-
const
|
|
484
|
+
const width = this._isCollapsed ? 24 : 48;
|
|
485
|
+
const height = 48;
|
|
486
|
+
const maxX = window.innerWidth - width;
|
|
487
|
+
const maxY = window.innerHeight - height;
|
|
488
|
+
let targetX, targetY;
|
|
482
489
|
if (this._isCollapsed) {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
button.style.right = "0px";
|
|
486
|
-
button.style.left = "auto";
|
|
487
|
-
button.style.top = `${currentY}px`;
|
|
488
|
-
button.style.bottom = "auto";
|
|
489
|
-
this.style.setProperty("--hs-trigger-top", `${currentY}px`);
|
|
490
|
+
targetX = maxX;
|
|
491
|
+
targetY = Math.max(0, Math.min(this._savedY, maxY));
|
|
490
492
|
} else {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
button.style.right = pos.includes("right") ? "20px" : "auto";
|
|
497
|
-
} else {
|
|
498
|
-
let currentX = parseFloat(this.style.getPropertyValue("--hs-trigger-left") || "0");
|
|
499
|
-
let currentY = parseFloat(this.style.getPropertyValue("--hs-trigger-top") || "0");
|
|
500
|
-
currentX = Math.max(0, Math.min(currentX, maxX));
|
|
501
|
-
currentY = Math.max(0, Math.min(currentY, maxY));
|
|
502
|
-
this.style.setProperty("--hs-trigger-left", `${currentX}px`);
|
|
503
|
-
this.style.setProperty("--hs-trigger-top", `${currentY}px`);
|
|
504
|
-
button.style.left = `${currentX}px`;
|
|
505
|
-
button.style.top = `${currentY}px`;
|
|
506
|
-
button.style.right = "auto";
|
|
507
|
-
button.style.bottom = "auto";
|
|
493
|
+
targetX = Math.max(0, Math.min(this._savedX, maxX));
|
|
494
|
+
targetY = Math.max(0, Math.min(this._savedY, maxY));
|
|
495
|
+
if (targetX !== this._savedX || targetY !== this._savedY) {
|
|
496
|
+
this._savedX = targetX;
|
|
497
|
+
this._savedY = targetY;
|
|
508
498
|
}
|
|
509
499
|
}
|
|
500
|
+
button.style.left = `${targetX}px`;
|
|
501
|
+
button.style.top = `${targetY}px`;
|
|
502
|
+
this.style.setProperty("--hs-trigger-left", `${targetX}px`);
|
|
503
|
+
this.style.setProperty("--hs-trigger-top", `${targetY}px`);
|
|
510
504
|
this.updateCollapsedState();
|
|
511
505
|
}
|
|
512
506
|
updateStyles() {
|
|
@@ -564,38 +558,38 @@ class FloatingTrigger extends HTMLElement {
|
|
|
564
558
|
if (e.target.closest(".collapse-btn")) return;
|
|
565
559
|
this._isDragging = true;
|
|
566
560
|
this._wasDragging = false;
|
|
567
|
-
|
|
568
|
-
this.
|
|
561
|
+
const rect = btn.getBoundingClientRect();
|
|
562
|
+
this._offsetX = e.clientX - rect.left;
|
|
563
|
+
this._offsetY = e.clientY - rect.top;
|
|
569
564
|
const startX = e.clientX;
|
|
570
565
|
const startY = e.clientY;
|
|
571
566
|
let hasMoved = false;
|
|
567
|
+
btn.style.transition = "none";
|
|
572
568
|
const onMouseMove = (moveEvent) => {
|
|
573
569
|
if (!this._isDragging) return;
|
|
574
570
|
if (!hasMoved && (Math.abs(moveEvent.clientX - startX) > 2 || Math.abs(moveEvent.clientY - startY) > 2)) {
|
|
575
571
|
hasMoved = true;
|
|
576
572
|
this._wasDragging = true;
|
|
577
573
|
btn.style.cursor = "grabbing";
|
|
578
|
-
btn.style.transition = "none";
|
|
579
574
|
}
|
|
580
575
|
if (hasMoved) {
|
|
581
576
|
let newX = moveEvent.clientX - this._offsetX;
|
|
582
577
|
let newY = moveEvent.clientY - this._offsetY;
|
|
583
|
-
const
|
|
584
|
-
const
|
|
578
|
+
const width = this._isCollapsed ? 24 : 48;
|
|
579
|
+
const height = 48;
|
|
580
|
+
const maxX = window.innerWidth - width;
|
|
581
|
+
const maxY = window.innerHeight - height;
|
|
585
582
|
newX = Math.max(0, Math.min(newX, maxX));
|
|
586
583
|
newY = Math.max(0, Math.min(newY, maxY));
|
|
587
584
|
if (this._isCollapsed) {
|
|
588
|
-
|
|
589
|
-
|
|
585
|
+
newX = window.innerWidth - 24;
|
|
586
|
+
this._savedY = newY;
|
|
590
587
|
} else {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
this.style.setProperty("--hs-trigger-left", `${newX}px`);
|
|
588
|
+
this._savedX = newX;
|
|
589
|
+
this._savedY = newY;
|
|
594
590
|
}
|
|
591
|
+
btn.style.left = `${newX}px`;
|
|
595
592
|
btn.style.top = `${newY}px`;
|
|
596
|
-
btn.style.bottom = "auto";
|
|
597
|
-
this.style.setProperty("--hs-trigger-position-set", "true");
|
|
598
|
-
this.style.setProperty("--hs-trigger-top", `${newY}px`);
|
|
599
593
|
}
|
|
600
594
|
};
|
|
601
595
|
const onMouseUp = () => {
|
|
@@ -620,11 +614,9 @@ class FloatingTrigger extends HTMLElement {
|
|
|
620
614
|
}
|
|
621
615
|
render() {
|
|
622
616
|
const posStyles = `
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
right: var(--hs-trigger-right, ${this._position.includes("right") ? "20px" : "auto"});
|
|
627
|
-
transform: translateX(0);
|
|
617
|
+
left: 0;
|
|
618
|
+
top: 0;
|
|
619
|
+
transform: translateZ(0); /* 开启硬件加速 */
|
|
628
620
|
`;
|
|
629
621
|
const bgStyle = this._bgColor ? `background: ${this._bgColor};` : "";
|
|
630
622
|
const colorStyle = this._textColor ? `color: ${this._textColor};` : "";
|
|
@@ -636,21 +628,27 @@ class FloatingTrigger extends HTMLElement {
|
|
|
636
628
|
${posStyles}
|
|
637
629
|
width: 48px;
|
|
638
630
|
height: 48px;
|
|
639
|
-
border-radius: 12px;
|
|
631
|
+
border-radius: 12px;
|
|
640
632
|
background: var(--hs-primary);
|
|
641
633
|
color: white;
|
|
642
634
|
${bgStyle}
|
|
643
635
|
${colorStyle}
|
|
644
636
|
border: none;
|
|
645
|
-
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
637
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
646
638
|
cursor: pointer;
|
|
647
639
|
display: flex;
|
|
648
640
|
align-items: center;
|
|
649
641
|
justify-content: center;
|
|
650
642
|
font-family: var(--hs-font-family);
|
|
651
643
|
z-index: var(--hs-z-index);
|
|
652
|
-
|
|
653
|
-
|
|
644
|
+
/* 添加平滑过渡动画 */
|
|
645
|
+
transition: width 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),
|
|
646
|
+
left 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),
|
|
647
|
+
top 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),
|
|
648
|
+
border-radius 0.3s,
|
|
649
|
+
background 0.2s,
|
|
650
|
+
box-shadow 0.2s;
|
|
651
|
+
overflow: visible;
|
|
654
652
|
}
|
|
655
653
|
.icon {
|
|
656
654
|
transition: transform 0.2s;
|
|
@@ -679,13 +677,14 @@ class FloatingTrigger extends HTMLElement {
|
|
|
679
677
|
display: flex;
|
|
680
678
|
align-items: center;
|
|
681
679
|
justify-content: center;
|
|
682
|
-
opacity: 1;
|
|
680
|
+
opacity: 1;
|
|
683
681
|
transition: opacity 0.2s, background 0.2s;
|
|
684
682
|
color: white;
|
|
685
683
|
font-weight: bold;
|
|
684
|
+
z-index: 2;
|
|
686
685
|
}
|
|
687
686
|
.collapse-btn::before {
|
|
688
|
-
content: '—';
|
|
687
|
+
content: '—';
|
|
689
688
|
font-size: 12px;
|
|
690
689
|
color: currentColor;
|
|
691
690
|
}
|
|
@@ -701,27 +700,24 @@ class FloatingTrigger extends HTMLElement {
|
|
|
701
700
|
width: 24px;
|
|
702
701
|
height: 48px;
|
|
703
702
|
border-radius: 12px 0 0 12px;
|
|
704
|
-
|
|
705
|
-
opacity: 1;
|
|
706
|
-
right: 0 !important; /* 强制右对齐 */
|
|
707
|
-
left: auto !important;
|
|
703
|
+
/* 不再强制 left/right,由 JS 控制 left 实现动画 */
|
|
708
704
|
}
|
|
709
705
|
button.collapsed:hover {
|
|
710
|
-
width: 28px;
|
|
711
|
-
opacity: 1;
|
|
706
|
+
width: 28px;
|
|
712
707
|
}
|
|
708
|
+
|
|
713
709
|
button.collapsed .icon {
|
|
714
710
|
display: none;
|
|
715
711
|
}
|
|
716
712
|
button.collapsed .collapse-btn {
|
|
717
|
-
position: static;
|
|
713
|
+
position: static;
|
|
718
714
|
width: 100%;
|
|
719
715
|
height: 100%;
|
|
720
716
|
border-radius: 0;
|
|
721
717
|
background: transparent;
|
|
722
718
|
}
|
|
723
719
|
button.collapsed .collapse-btn::before {
|
|
724
|
-
content: '‹';
|
|
720
|
+
content: '‹';
|
|
725
721
|
font-size: 20px;
|
|
726
722
|
line-height: 48px;
|
|
727
723
|
}
|
|
@@ -731,9 +727,6 @@ class FloatingTrigger extends HTMLElement {
|
|
|
731
727
|
<span class="icon">${ICONS.chart}</span>
|
|
732
728
|
</button>
|
|
733
729
|
`;
|
|
734
|
-
this.applyPosition();
|
|
735
|
-
this.updateCollapsedState();
|
|
736
|
-
this.updateStyles();
|
|
737
730
|
}
|
|
738
731
|
}
|
|
739
732
|
customElements.define("hs-floating-trigger", FloatingTrigger);
|
|
@@ -1028,7 +1021,10 @@ class TaskList extends HTMLElement {
|
|
|
1028
1021
|
constructor() {
|
|
1029
1022
|
super();
|
|
1030
1023
|
__publicField(this, "_shadow");
|
|
1031
|
-
__publicField(this, "
|
|
1024
|
+
__publicField(this, "_allTasks", []);
|
|
1025
|
+
// 保存所有任务的原始数据
|
|
1026
|
+
__publicField(this, "_filteredTasks", []);
|
|
1027
|
+
// 当前过滤后的任务
|
|
1032
1028
|
__publicField(this, "_lastExecutionTimes", /* @__PURE__ */ new Map());
|
|
1033
1029
|
__publicField(this, "_expandedNamespaces", /* @__PURE__ */ new Set(["default"]));
|
|
1034
1030
|
this._shadow = this.attachShadow({ mode: "open" });
|
|
@@ -1039,12 +1035,13 @@ class TaskList extends HTMLElement {
|
|
|
1039
1035
|
set tasks(map) {
|
|
1040
1036
|
const newTasks = Array.from(map.values());
|
|
1041
1037
|
newTasks.forEach((task) => {
|
|
1042
|
-
const oldTask = this.
|
|
1038
|
+
const oldTask = this._allTasks.find((t2) => t2.id === task.id);
|
|
1043
1039
|
if (oldTask && task.executionCount > oldTask.executionCount) {
|
|
1044
1040
|
this._lastExecutionTimes.set(task.id, Date.now());
|
|
1045
1041
|
}
|
|
1046
1042
|
});
|
|
1047
|
-
this.
|
|
1043
|
+
this._allTasks = newTasks;
|
|
1044
|
+
this._filteredTasks = newTasks;
|
|
1048
1045
|
this.renderRows();
|
|
1049
1046
|
}
|
|
1050
1047
|
groupTasksByNamespace(tasks) {
|
|
@@ -1059,12 +1056,12 @@ class TaskList extends HTMLElement {
|
|
|
1059
1056
|
return groups;
|
|
1060
1057
|
}
|
|
1061
1058
|
filter(text, map) {
|
|
1062
|
-
|
|
1059
|
+
this._allTasks = Array.from(map.values());
|
|
1063
1060
|
if (!text) {
|
|
1064
|
-
this.
|
|
1061
|
+
this._filteredTasks = this._allTasks;
|
|
1065
1062
|
} else {
|
|
1066
1063
|
const lower = text.toLowerCase();
|
|
1067
|
-
this.
|
|
1064
|
+
this._filteredTasks = this._allTasks.filter(
|
|
1068
1065
|
(t2) => t2.id.toLowerCase().includes(lower) || t2.tags.some((tag) => tag.toLowerCase().includes(lower)) || t2.namespace && t2.namespace.toLowerCase().includes(lower)
|
|
1069
1066
|
);
|
|
1070
1067
|
}
|
|
@@ -1146,7 +1143,7 @@ class TaskList extends HTMLElement {
|
|
|
1146
1143
|
return `
|
|
1147
1144
|
<tr data-id="${task.id}" class="${rowClass} ${nestedClass}">
|
|
1148
1145
|
<td class="col-num">
|
|
1149
|
-
${
|
|
1146
|
+
${index + 1}
|
|
1150
1147
|
</td>
|
|
1151
1148
|
<td class="col-id">
|
|
1152
1149
|
<div class="task-id">
|
|
@@ -1175,15 +1172,10 @@ class TaskList extends HTMLElement {
|
|
|
1175
1172
|
renderRows() {
|
|
1176
1173
|
const tbody = this._shadow.querySelector("tbody");
|
|
1177
1174
|
if (!tbody) return;
|
|
1178
|
-
const groups = this.groupTasksByNamespace(this.
|
|
1175
|
+
const groups = this.groupTasksByNamespace(this._filteredTasks);
|
|
1179
1176
|
let html = "";
|
|
1180
1177
|
let globalIndex = 0;
|
|
1181
|
-
const
|
|
1182
|
-
if (defaultTasks && defaultTasks.length > 0) {
|
|
1183
|
-
html += defaultTasks.map((task) => this.renderTaskRow(task, ++globalIndex, false)).join("");
|
|
1184
|
-
}
|
|
1185
|
-
groups.delete("default");
|
|
1186
|
-
const sortedNamespaces = Array.from(groups.keys()).sort((a, b) => a.localeCompare(b));
|
|
1178
|
+
const sortedNamespaces = Array.from(groups.keys()).filter((ns) => ns !== "default").sort((a, b) => a.localeCompare(b));
|
|
1187
1179
|
sortedNamespaces.forEach((ns) => {
|
|
1188
1180
|
const tasks = groups.get(ns);
|
|
1189
1181
|
const isExpanded = this._expandedNamespaces.has(ns);
|
|
@@ -1199,9 +1191,13 @@ class TaskList extends HTMLElement {
|
|
|
1199
1191
|
</tr>
|
|
1200
1192
|
`;
|
|
1201
1193
|
if (isExpanded) {
|
|
1202
|
-
html += tasks.map((task) => this.renderTaskRow(task,
|
|
1194
|
+
html += tasks.map((task) => this.renderTaskRow(task, globalIndex++, true)).join("");
|
|
1203
1195
|
}
|
|
1204
1196
|
});
|
|
1197
|
+
const defaultTasks = groups.get("default");
|
|
1198
|
+
if (defaultTasks && defaultTasks.length > 0) {
|
|
1199
|
+
html += defaultTasks.map((task) => this.renderTaskRow(task, globalIndex++, false)).join("");
|
|
1200
|
+
}
|
|
1205
1201
|
tbody.innerHTML = html;
|
|
1206
1202
|
}
|
|
1207
1203
|
render() {
|
|
@@ -2427,6 +2423,15 @@ class DevTools extends HTMLElement {
|
|
|
2427
2423
|
break;
|
|
2428
2424
|
}
|
|
2429
2425
|
});
|
|
2426
|
+
this._shadow.addEventListener("resize", (e) => {
|
|
2427
|
+
const { width, height } = e.detail;
|
|
2428
|
+
if (width !== void 0) {
|
|
2429
|
+
this.store.setPanelSize({ width });
|
|
2430
|
+
}
|
|
2431
|
+
if (height !== void 0) {
|
|
2432
|
+
this.store.setPanelSize({ height });
|
|
2433
|
+
}
|
|
2434
|
+
});
|
|
2430
2435
|
}
|
|
2431
2436
|
startLoop() {
|
|
2432
2437
|
const loop = (time) => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var t=Object.defineProperty,e=(e,s,n)=>((e,s,n)=>s in e?t(e,s,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[s]=n)(e,"symbol"!=typeof s?s+"":s,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("./index.cjs"),n={header:{title:"DevTools",searchPlaceholder:"Search IDs/Tags... 🔍",toggleDock:"Toggle Dock Position",toggleTheme:"Toggle Theme",close:"Close"},stats:{loading:"Loading...",fps:"FPS",status:"Status",active:"Active",total:"Total",mainThread:"Main Thread",scheduler:"Scheduler",running:"Running",stopped:"Stopped"},tabs:{tasks:"Tasks List",timeline:"Timeline"},list:{idTags:"ID / Tags",status:"Status",driver:"Driver",driverWorker:"Worker (Web Worker)",driverMain:"Main (setTimeout)",schedule:"Schedule",count:"Count",lastRun:"Last Run",actions:"Actions",noTags:"(No Tags)",tip:"✨ Tip: Click a row for details & history."},detail:{back:"Back",details:"Task Details",config:"Config",history:"Execution History",lastRuns:"Last {n} runs",avgDuration:"Avg Duration",startTime:"Start Time",duration:"Duration",drift:"Drift",status:"Status",noHistory:"No execution history",noTask:"No task selected",success:"Success",failed:"Failed",error:"Error"},timeline:{zoom:"Zoom",timeRange:"Time Range: Last {n}s",legend:"Legend",instant:"Instant",duration:"Duration",workerDriver:"Worker Driver",mainDriver:"Main Driver"},status:{running:"Running",paused:"Paused",stopped:"Stopped",idle:"Scheduled",error:"Error"},actions:{trigger:"Trigger now",start:"Start task",stop:"Stop task",remove:"Remove task"}},i={header:{title:"调试工具",searchPlaceholder:"搜索 ID/标签... 🔍",toggleDock:"切换停靠位置",toggleTheme:"切换主题",close:"关闭"},stats:{loading:"加载中...",fps:"帧率",status:"状态",active:"活跃",total:"总数",mainThread:"主线程",scheduler:"调度器",running:"运行中",stopped:"已停止"},tabs:{tasks:"任务列表",timeline:"时间线"},list:{idTags:"ID / 标签",status:"状态",driver:"驱动",driverWorker:"Worker (Web Worker)",driverMain:"主线程 (setTimeout)",schedule:"调度规则",count:"次数",lastRun:"最后运行",actions:"操作",noTags:"(无标签)",tip:"✨ 提示: 点击行查看详情和历史记录。"},detail:{back:"返回",details:"任务详情",config:"配置",history:"执行历史",lastRuns:"最近 {n} 次运行",avgDuration:"平均耗时",startTime:"开始时间",duration:"耗时",drift:"偏差",status:"状态",noHistory:"暂无执行历史",noTask:"未选择任务",success:"成功",failed:"失败",error:"错误"},timeline:{zoom:"缩放",timeRange:"时间范围: 最近 {n}秒",legend:"图例",instant:"瞬间",duration:"耗时",workerDriver:"Worker 驱动",mainDriver:"主线程 驱动"},status:{running:"执行中",paused:"已暂停",stopped:"已停止",idle:"调度中",error:"错误"},actions:{trigger:"立即触发",start:"启动任务",stop:"停止任务",remove:"删除任务"}};let o=n;function a(t){o="zh"===t?i:n}function r(t,e){const s=t.split(".");let n=o;for(const i of s){if(!n||"object"!=typeof n||!(i in n))return t;n=n[i]}return"string"!=typeof n?t:e?n.replace(/\{(\w+)\}/g,(t,s)=>void 0!==e[s]?String(e[s]):`{${s}}`):n}class h{constructor(){e(this,"state"),e(this,"listeners"),e(this,"scheduler"),this.state={isOpen:!1,activeTab:"tasks",theme:"auto",dockPosition:"right",panelSize:{width:500,height:500},language:"en",filterText:"",selectedTaskId:null,tasks:new Map,history:new Map,fps:0,schedulerRunning:!1},this.listeners=new Map}setScheduler(t){this.scheduler=t}getState(){return this.state}subscribe(t,e){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e),()=>{var s;null==(s=this.listeners.get(t))||s.delete(e)}}notify(t,e){var s;null==(s=this.listeners.get(t))||s.forEach(t=>t(e))}toggle(){this.state.isOpen=!this.state.isOpen,this.notify("isOpen",this.state.isOpen)}setTheme(t){this.state.theme=t,this.notify("theme",this.state.theme)}setLanguageSync(t){a(t),this.state.language=t}setLanguage(t){a(t),this.state.language=t,this.notify("language",this.state.language)}setPanelSize(t){this.state.panelSize={...this.state.panelSize,...t};try{localStorage.setItem("hs-panel-size",JSON.stringify(this.state.panelSize))}catch(e){}this.notify("panelSize",this.state.panelSize)}setTab(t){this.state.activeTab=t,this.notify("activeTab",this.state.activeTab)}setDockPosition(t){this.state.dockPosition=t,this.notify("dockPosition",this.state.dockPosition)}setFilterText(t){this.state.filterText=t,this.notify("filterText",this.state.filterText)}updateTask(t){const e=new Map(this.state.tasks);if(e.set(t.id,t),this.state.tasks=e,this.notify("tasks",this.state.tasks),"history"in t&&Array.isArray(t.history)){const e=new Map(this.state.history);e.set(t.id,t.history),this.state.history=e,this.notify("history",this.state.history)}}selectTask(t){this.state.selectedTaskId=t,this.notify("selectedTaskId",t)}addHistory(t,e){const s=[...this.state.history.get(t)||[]];s.push(e),s.length>50&&s.splice(0,s.length-50);const n=new Map(this.state.history);n.set(t,s),this.state.history=n,this.notify("history",n)}async triggerTask(t){this.scheduler&&await this.scheduler.trigger(t)}stopTask(t){console.log("[DevToolsStore] stopTask:",t),this.scheduler&&this.scheduler.pause(t)}startTask(t){console.log("[DevToolsStore] startTask:",t),this.scheduler&&this.scheduler.resume(t)}removeTask(t){if(this.scheduler){this.scheduler.remove(t);const e=new Map(this.state.tasks);e.delete(t),this.state.tasks=e,this.notify("tasks",this.state.tasks)}}setSchedulerRunning(t){this.state.schedulerRunning=t,this.notify("schedulerRunning",t)}}const l='\n :host {\n --hs-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;\n --hs-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;\n --hs-font-size: 12px;\n --hs-line-height: 1.5;\n --hs-panel-width: 400px;\n --hs-panel-height: 300px;\n \n /* 等宽数字字体 */\n --hs-font-monospaced-num: var(--hs-font-mono);\n\n /* Light Theme (Default) */\n --hs-bg: #ffffff;\n --hs-bg-secondary: #f3f4f6;\n --hs-text: #1f2937;\n --hs-text-secondary: #6b7280;\n --hs-border: #e5e7eb;\n --hs-primary: #3b82f6;\n --hs-primary-hover: #2563eb;\n --hs-danger: #ef4444;\n --hs-danger-hover: #dc2626;\n --hs-success: #10b981;\n --hs-warning: #f59e0b;\n \n --hs-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);\n --hs-radius: 6px;\n --hs-header-height: 40px;\n --hs-z-index: 9999;\n --hs-z-index-overlay: 9998;\n\n /* Default display styles for the host itself */\n background: var(--hs-bg);\n color: var(--hs-text);\n font-family: var(--hs-font-family);\n font-size: var(--hs-font-size);\n line-height: var(--hs-line-height);\n }\n\n :host([theme="dark"]) {\n --hs-bg: #111827;\n --hs-bg-secondary: #1f2937;\n --hs-text: #f9fafb;\n --hs-text-secondary: #9ca3af;\n --hs-border: #374151;\n --hs-primary: #60a5fa;\n --hs-primary-hover: #3b82f6;\n --hs-danger: #f87171;\n --hs-danger-hover: #ef4444;\n --hs-success: #34d399;\n --hs-warning: #fbbf24;\n \n --hs-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.5), 0 2px 4px -1px rgba(0, 0, 0, 0.3);\n }\n\n :host {\n background: var(--hs-bg);\n color: var(--hs-text);\n }\n',d={back:'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>',trigger:'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>',pause:'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg>',resume:'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>',remove:'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>',close:'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>',sun:'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>',moon:'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>',dock:'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="18" rx="2" ry="2"></rect><line x1="2" y1="15" x2="22" y2="15"></line></svg>',dockRight:'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="18" rx="2" ry="2"></rect><line x1="15" y1="3" x2="15" y2="21"></line></svg>',chart:'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"></line><line x1="12" y1="20" x2="12" y2="4"></line><line x1="6" y1="20" x2="6" y2="14"></line></svg>'},c="hs-trigger-position",p="hs-trigger-collapsed";class g extends HTMLElement{constructor(){super(),e(this,"_shadow"),e(this,"_position","bottom-right"),e(this,"_bgColor",""),e(this,"_textColor",""),e(this,"_isDragging",!1),e(this,"_wasDragging",!1),e(this,"_offsetX",0),e(this,"_offsetY",0),e(this,"_isCollapsed",!1),e(this,"_savedX",0),e(this,"_savedY",0),this._shadow=this.attachShadow({mode:"open"})}static get observedAttributes(){return["position","bg-color","text-color"]}connectedCallback(){this.loadState(),this.render(),this.addEventListeners(),requestAnimationFrame(()=>{this.applyPosition()}),window.addEventListener("resize",this.onResize.bind(this))}disconnectedCallback(){window.removeEventListener("resize",this.onResize.bind(this))}onResize(){this.applyPosition()}attributeChangedCallback(t,e,s){"position"===t?(this._position=s||"bottom-right",this.style.getPropertyValue("--hs-trigger-position-set")||this.resetPositionToDefault()):"bg-color"===t?(this._bgColor=s||"",this.updateStyles()):"text-color"===t&&(this._textColor=s||"",this.updateStyles())}loadState(){try{const t=localStorage.getItem(c);if(t){const{x:e,y:s}=JSON.parse(t);this._savedX=parseFloat(e),this._savedY=parseFloat(s),this.style.setProperty("--hs-trigger-position-set","true")}else this.resetPositionToDefault();"true"===localStorage.getItem(p)&&(this._isCollapsed=!0)}catch(t){console.warn("[FloatingTrigger] Failed to load state:",t),this.resetPositionToDefault()}}resetPositionToDefault(){this._position.includes("left")?this._savedX=20:this._savedX=window.innerWidth-48-20,this._position.includes("top")?this._savedY=20:this._savedY=window.innerHeight-48-20}saveState(){localStorage.setItem(c,JSON.stringify({x:this._savedX,y:this._savedY})),localStorage.setItem(p,String(this._isCollapsed))}applyPosition(){const t=this._shadow.querySelector("button");if(!t)return;const e=this._isCollapsed?24:48,s=window.innerWidth-e,n=window.innerHeight-48;let i,o;this._isCollapsed?(i=s,o=Math.max(0,Math.min(this._savedY,n))):(i=Math.max(0,Math.min(this._savedX,s)),o=Math.max(0,Math.min(this._savedY,n)),i===this._savedX&&o===this._savedY||(this._savedX=i,this._savedY=o)),t.style.left=`${i}px`,t.style.top=`${o}px`,this.style.setProperty("--hs-trigger-left",`${i}px`),this.style.setProperty("--hs-trigger-top",`${o}px`),this.updateCollapsedState()}updateStyles(){const t=this._shadow.querySelector("button");t&&(t.style.background=this._bgColor||"var(--hs-primary)",t.style.color=this._textColor||"white",this._bgColor?t.style.setProperty("--hs-trigger-bg-hover",`${this._bgColor}; filter: brightness(1.1);`):t.style.removeProperty("--hs-trigger-bg-hover"))}updateCollapsedState(){const t=this._shadow.querySelector("button");t&&(this._isCollapsed?t.classList.add("collapsed"):t.classList.remove("collapsed"))}addEventListeners(){const t=this._shadow.querySelector("button");if(!t)return;t.addEventListener("click",t=>{if(this._wasDragging)return t.preventDefault(),t.stopPropagation(),void(this._wasDragging=!1);this.dispatchEvent(new CustomEvent("toggle",{bubbles:!0,composed:!0}))});const e=this._shadow.querySelector(".collapse-btn");null==e||e.addEventListener("click",t=>{t.stopPropagation(),t.preventDefault(),this._isCollapsed=!this._isCollapsed,this.applyPosition(),this.saveState()}),t.addEventListener("dblclick",t=>{this._isCollapsed&&(t.stopPropagation(),this._isCollapsed=!1,this.applyPosition(),this.saveState())}),t.addEventListener("mousedown",e=>{if(0!==e.button)return;if(e.target.closest(".collapse-btn"))return;this._isDragging=!0,this._wasDragging=!1;const s=t.getBoundingClientRect();this._offsetX=e.clientX-s.left,this._offsetY=e.clientY-s.top;const n=e.clientX,i=e.clientY;let o=!1;t.style.transition="none";const a=e=>{if(this._isDragging&&(!o&&(Math.abs(e.clientX-n)>2||Math.abs(e.clientY-i)>2)&&(o=!0,this._wasDragging=!0,t.style.cursor="grabbing"),o)){let s=e.clientX-this._offsetX,n=e.clientY-this._offsetY;const i=this._isCollapsed?24:48,o=48,a=window.innerWidth-i,r=window.innerHeight-o;s=Math.max(0,Math.min(s,a)),n=Math.max(0,Math.min(n,r)),this._isCollapsed?(s=window.innerWidth-24,this._savedY=n):(this._savedX=s,this._savedY=n),t.style.left=`${s}px`,t.style.top=`${n}px`}},r=()=>{if(this._isDragging){if(this._isDragging=!1,t.style.cursor="pointer",t.style.transition="",o){this.saveState();const t=t=>{t.stopPropagation(),t.preventDefault()};window.addEventListener("click",t,{capture:!0,once:!0})}window.removeEventListener("mousemove",a),window.removeEventListener("mouseup",r)}};window.addEventListener("mousemove",a),window.addEventListener("mouseup",r)})}render(){const t=this._bgColor?`background: ${this._bgColor};`:"",e=this._textColor?`color: ${this._textColor};`:"";this._shadow.innerHTML=`\n <style>\n ${l}\n button {\n position: fixed;\n \n left: 0;\n top: 0;\n transform: translateZ(0); /* 开启硬件加速 */\n \n width: 48px;\n height: 48px;\n border-radius: 12px;\n background: var(--hs-primary);\n color: white;\n ${t}\n ${e}\n border: none;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: var(--hs-font-family);\n z-index: var(--hs-z-index);\n /* 添加平滑过渡动画 */\n transition: width 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), \n left 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), \n top 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), \n border-radius 0.3s,\n background 0.2s, \n box-shadow 0.2s;\n overflow: visible;\n }\n .icon {\n transition: transform 0.2s;\n display: flex;\n }\n button:hover {\n ${this._bgColor?`background: ${this._bgColor}; filter: brightness(1.1);`:"background: var(--hs-primary-hover);"}\n box-shadow: 0 6px 16px rgba(0,0,0,0.2);\n }\n button:hover .icon {\n transform: scale(1.1);\n }\n button:active .icon {\n transform: scale(0.95);\n }\n\n /* 显式收起按钮 */\n .collapse-btn {\n position: absolute;\n top: 0;\n right: 0;\n width: 20px;\n height: 20px;\n background: rgba(0,0,0,0.1);\n border-top-right-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 1; \n transition: opacity 0.2s, background 0.2s;\n color: white;\n font-weight: bold;\n z-index: 2;\n }\n .collapse-btn::before {\n content: '—'; \n font-size: 12px;\n color: currentColor;\n }\n button:hover .collapse-btn {\n opacity: 1;\n }\n .collapse-btn:hover {\n background: rgba(0,0,0,0.3);\n }\n\n /* 收起状态样式 */\n button.collapsed {\n width: 24px;\n height: 48px;\n border-radius: 12px 0 0 12px;\n /* 不再强制 left/right,由 JS 控制 left 实现动画 */\n }\n button.collapsed:hover {\n width: 28px; \n }\n \n button.collapsed .icon {\n display: none;\n }\n button.collapsed .collapse-btn {\n position: static;\n width: 100%;\n height: 100%;\n border-radius: 0;\n background: transparent;\n }\n button.collapsed .collapse-btn::before {\n content: '‹'; \n font-size: 20px;\n line-height: 48px;\n }\n </style>\n <button title="Toggle Hyper Scheduler DevTools">\n <div class="collapse-btn" title="Minimize"></div>\n <span class="icon">${d.chart}</span>\n </button>\n `}}customElements.define("hs-floating-trigger",g);class u extends HTMLElement{constructor(){super(),e(this,"_shadow"),e(this,"_fps",0),e(this,"_stats",{active:0,total:0}),e(this,"_theme","auto"),e(this,"_activeTab","tasks"),e(this,"_language","en"),e(this,"_schedulerRunning",!1),e(this,"$fps"),e(this,"$stats"),e(this,"$schedulerStatus"),e(this,"$themeIcon"),e(this,"$dockIcon"),e(this,"$tabs"),e(this,"$searchInput"),e(this,"$title"),e(this,"$langBtn"),this._shadow=this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.cacheDom(),this.addEventListeners(),this.updateView()}set fps(t){if(this._fps=Math.round(t),this.$fps){const t=this._fps<30?"var(--hs-danger)":this._fps<50?"var(--hs-warning)":"var(--hs-success)";this.$fps.innerHTML=`⚡ ${r("stats.fps")}: <span style="color:${t}; font-family:var(--hs-font-monospaced-num);">${this._fps}</span> (${r("stats.mainThread")})`}}set stats(t){this._stats=t,this.$stats&&(this.$stats.innerHTML=`📊 ${r("stats.status")}: <span style="color:var(--hs-success)">🟢 ${r("stats.active")}: ${t.active}</span> <span style="margin-left:12px;color:var(--hs-text-secondary)">⚪ ${r("stats.total")}: ${t.total}</span>`)}set schedulerRunning(t){if(this._schedulerRunning=t,this.$schedulerStatus){const e=r(t?"stats.running":"stats.stopped"),s=t?"var(--hs-success)":"var(--hs-danger)",n=t?"▶️":"⏹️";this.$schedulerStatus.innerHTML=`${n} ${r("stats.scheduler")}: <span style="color:${s}">${e}</span>`}}set theme(t){this._theme=t,this.setAttribute("theme",t),this.updateThemeIcon()}set language(t){this._language=t,this.updateTexts()}set dockPosition(t){var e;this.$dockIcon&&(this.$dockIcon.innerHTML="right"===t?d.dock:d.dockRight,null==(e=this.$dockIcon.parentElement)||e.setAttribute("title",r("header.toggleDock")))}set activeTab(t){this._activeTab=t,this.updateTabs()}cacheDom(){this.$fps=this._shadow.querySelector(".fps"),this.$stats=this._shadow.querySelector(".stats"),this.$schedulerStatus=this._shadow.querySelector(".scheduler-status"),this.$themeIcon=this._shadow.querySelector(".theme-btn span"),this.$dockIcon=this._shadow.querySelector(".dock-btn"),this.$tabs=this._shadow.querySelectorAll(".tab"),this.$searchInput=this._shadow.querySelector(".search-input"),this.$title=this._shadow.querySelector(".title"),this.$langBtn=this._shadow.querySelector(".lang-btn")}addEventListeners(){var t,e,s,n,i;null==(t=this._shadow.querySelector(".dock-btn"))||t.addEventListener("click",()=>{this.dispatchEvent(new CustomEvent("dock-toggle"))}),null==(e=this._shadow.querySelector(".theme-btn"))||e.addEventListener("click",()=>{const t="dark"===this._theme?"light":"dark";this.dispatchEvent(new CustomEvent("theme-toggle",{detail:t}))}),null==(s=this._shadow.querySelector(".lang-btn"))||s.addEventListener("click",()=>{const t="en"===this._language?"zh":"en";this.dispatchEvent(new CustomEvent("lang-toggle",{detail:t}))}),null==(n=this._shadow.querySelector(".close-btn"))||n.addEventListener("click",()=>{this.dispatchEvent(new CustomEvent("close"))}),this.$tabs.forEach(t=>{t.addEventListener("click",t=>{const e=t.currentTarget.dataset.tab;this.dispatchEvent(new CustomEvent("tab-change",{detail:e}))})}),null==(i=this.$searchInput)||i.addEventListener("input",t=>{const e=t.target.value;this.dispatchEvent(new CustomEvent("search",{detail:e}))})}updateThemeIcon(){this.$themeIcon&&(this.$themeIcon.innerHTML="dark"===this._theme?d.moon:d.sun)}updateTabs(){this.$tabs.forEach(t=>{t.dataset.tab===this._activeTab?t.classList.add("active"):t.classList.remove("active")})}updateTexts(){this.$title&&(this.$title.innerHTML=`🕒 ${r("header.title")}`),this.$searchInput&&(this.$searchInput.placeholder=r("header.searchPlaceholder")),this.$langBtn&&(this.$langBtn.textContent="en"===this._language?"中":"EN"),this.$tabs.forEach(t=>{const e=t.dataset.tab;"tasks"===e&&(t.innerHTML=`📌 ${r("tabs.tasks")}`),"timeline"===e&&(t.innerHTML=`📈 ${r("tabs.timeline")}`)}),this.stats=this._stats,this.fps=this._fps,this.schedulerRunning=this._schedulerRunning}updateView(){this.updateThemeIcon(),this.updateTabs(),this.updateTexts()}render(){this._shadow.innerHTML=`\n <style>\n ${l}\n :host {\n display: block;\n background: var(--hs-bg);\n border-bottom: 1px solid var(--hs-border);\n padding: 0 16px;\n height: var(--hs-header-height);\n height: auto; \n }\n .top-bar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n height: 40px;\n border-bottom: 1px solid var(--hs-border);\n }\n .title {\n font-weight: 600;\n font-size: 13px;\n color: var(--hs-text);\n display: flex;\n align-items: center;\n gap: 6px;\n }\n .search-box {\n flex: 1;\n max-width: 300px;\n margin: 0px 32px 0 16px;\n }\n .search-input {\n width: 100%;\n background: var(--hs-bg-secondary);\n border: 1px solid var(--hs-border);\n color: var(--hs-text);\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 12px;\n }\n .search-input::placeholder {\n color: var(--hs-text-secondary);\n }\n .controls {\n display: flex;\n align-items: center;\n gap: 4px;\n }\n button {\n background: transparent;\n border: none;\n color: var(--hs-text-secondary);\n cursor: pointer;\n padding: 4px;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n width: 28px;\n height: 28px;\n }\n .theme-btn span {\n margin-top: 4px;\n }\n button:hover {\n background: var(--hs-bg-secondary);\n color: var(--hs-text);\n }\n button svg {\n width: 16px;\n height: 16px;\n }\n .lang-btn {\n font-weight: 600;\n }\n .stats-bar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n height: 30px;\n font-size: 11px;\n color: var(--hs-text-secondary);\n border-bottom: 1px solid var(--hs-border);\n gap: 16px;\n }\n .stats-left {\n display: flex;\n gap: 16px;\n }\n .tabs-bar {\n display: flex;\n height: 36px;\n gap: 0;\n }\n .tab {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--hs-text-secondary);\n cursor: pointer;\n border-bottom: 3px solid transparent;\n padding: 0 16px;\n transition: all 0.2s;\n }\n .tab:hover {\n color: var(--hs-text);\n background: var(--hs-bg-secondary);\n }\n .tab.active {\n color: var(--hs-text);\n font-weight: 600;\n border-bottom-color: var(--hs-primary);\n background: var(--hs-bg-secondary);\n }\n </style>\n \n <div class="top-bar">\n <div class="title"></div>\n <div class="search-box">\n <input type="text" class="search-input">\n </div>\n <div class="controls">\n <button class="lang-btn" title="Switch Language">EN</button>\n <button class="dock-btn" title="Toggle Dock">${d.dock}</button>\n <button class="theme-btn" title="Toggle Theme"><span>${d.sun}</span></button>\n <button class="close-btn" title="Close">${d.close}</button>\n </div>\n </div>\n \n <div class="stats-bar">\n <div class="stats-left">\n <div class="scheduler-status"></div>\n <div class="stats"></div>\n </div>\n <div class="fps"></div>\n </div>\n \n <div class="tabs-bar">\n <div class="tab active" data-tab="tasks"></div>\n <div class="tab" data-tab="timeline"></div>\n </div>\n `}}customElements.define("hs-task-header",u);class b extends HTMLElement{constructor(){super(),e(this,"_shadow"),e(this,"_allTasks",[]),e(this,"_filteredTasks",[]),e(this,"_lastExecutionTimes",new Map),e(this,"_expandedNamespaces",new Set(["default"])),this._shadow=this.attachShadow({mode:"open"})}connectedCallback(){this.render()}set tasks(t){const e=Array.from(t.values());e.forEach(t=>{const e=this._allTasks.find(e=>e.id===t.id);e&&t.executionCount>e.executionCount&&this._lastExecutionTimes.set(t.id,Date.now())}),this._allTasks=e,this._filteredTasks=e,this.renderRows()}groupTasksByNamespace(t){const e=new Map;return t.forEach(t=>{const s=t.namespace||"default";e.has(s)||e.set(s,[]),e.get(s).push(t)}),e}filter(t,e){if(this._allTasks=Array.from(e.values()),t){const e=t.toLowerCase();this._filteredTasks=this._allTasks.filter(t=>t.id.toLowerCase().includes(e)||t.tags.some(t=>t.toLowerCase().includes(e))||t.namespace&&t.namespace.toLowerCase().includes(e))}else this._filteredTasks=this._allTasks;this.renderRows()}updateHeaders(){const t=this._shadow.querySelector("thead");t&&(t.innerHTML=`\n <tr>\n <th style="width:40px">#</th>\n <th style="min-width:150px">${r("list.idTags")}</th>\n <th style="width:150px">${r("list.status")}</th>\n <th style="width:70px">${r("list.driver")}</th>\n <th style="width:100px">${r("list.schedule")}</th>\n <th style="width:60px">${r("list.count")}</th>\n <th style="width:100px">${r("list.lastRun")}</th>\n <th style="width:100px">${r("list.actions")}</th>\n </tr>\n `);const e=this._shadow.querySelector(".tip");e&&(e.textContent=r("list.tip")),this.renderRows()}getStatusIcon(t,e){const n=this._lastExecutionTimes.get(e),i=n&&Date.now()-n<1e3;switch(t){case s.TaskStatus.RUNNING:return`<span style="color:var(--hs-primary)">🔵</span> <strong>${r("status.running")}</strong>`;case s.TaskStatus.STOPPED:return`<span style="color:var(--hs-text-secondary)">⚪</span> ${r("status.stopped")}`;case s.TaskStatus.IDLE:return i?`<span class="status-flash" style="color:var(--hs-success)">🟢</span> ${r("status.idle")}`:`<span style="color:var(--hs-success)">🟢</span> ${r("status.idle")}`;case s.TaskStatus.ERROR:return`<span style="color:var(--hs-warning)">🟠</span> ${r("status.error")}`;default:return t}}formatSchedule(t){return"number"==typeof t?`${t}ms`:t&&(t.includes("*")||t.includes(" "))?t.length>15?t.substring(0,12)+"...":t:t||"-"}formatTime(t){if(!t)return"-";const e=new Date(t);return e.toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"})+"."+e.getMilliseconds().toString().padStart(3,"0")}getDriverBadge(t){return"worker"===(t||"worker")?`<span class="driver-badge worker" title="${r("list.driverWorker")}">W</span>`:`<span class="driver-badge main" title="${r("list.driverMain")}">M</span>`}renderTaskRow(t,e,n=!1){const i=this._lastExecutionTimes.get(t.id),o=i&&Date.now()-i<1e3?"recently-executed":"",a=n?"nested-task":"";return`\n <tr data-id="${t.id}" class="${o} ${a}">\n <td class="col-num">\n ${e+1}\n </td>\n <td class="col-id">\n <div class="task-id">\n ${t.id}\n ${t.namespace&&"default"!==t.namespace&&!n?`<span class="namespace-badge" title="Namespace">${t.namespace}</span>`:""}\n </div>\n <div class="tags">\n ${t.tags&&t.tags.length>0?t.tags.map(t=>`<span class="tag">${t}</span>`).join(""):`<span class="no-tags">${r("list.noTags")}</span>`}\n </div>\n </td>\n <td>${this.getStatusIcon(t.status,t.id)}</td>\n <td>${this.getDriverBadge(t.driver)}</td>\n <td>${this.formatSchedule(t.schedule)}</td>\n <td>${t.executionCount||0}</td>\n <td>${this.formatTime(t.lastRun)}</td>\n <td class="col-actions">\n <div class="action-group">\n <button class="btn-icon" data-action="trigger" title="${r("actions.trigger")}" ${t.status===s.TaskStatus.RUNNING?"disabled":""}>${d.trigger}</button>\n ${t.status===s.TaskStatus.STOPPED||t.status===s.TaskStatus.ERROR?`<button class="btn-icon success" data-action="start" title="${r("actions.start")}">${d.resume}</button>`:`<button class="btn-icon warning" data-action="stop" title="${r("actions.stop")}" ${t.status===s.TaskStatus.RUNNING?"disabled":""}>${d.pause}</button>`}\n <button class="btn-icon danger" data-action="remove" title="${r("actions.remove")}">${d.remove}</button>\n </div>\n </td>\n </tr>\n `}renderRows(){const t=this._shadow.querySelector("tbody");if(!t)return;const e=this.groupTasksByNamespace(this._filteredTasks);let s="",n=0;Array.from(e.keys()).filter(t=>"default"!==t).sort((t,e)=>t.localeCompare(e)).forEach(t=>{const i=e.get(t),o=this._expandedNamespaces.has(t);s+=`\n <tr class="namespace-row ${o?"ns-expanded":""}" data-ns="${t}">\n <td colspan="8">\n <span class="ns-toggle">${o?"▼":"▶"}</span>\n <span class="ns-icon">📂</span>\n <span class="ns-name">${t}</span>\n <span class="ns-count">(${i.length})</span>\n </td>\n </tr>\n `,o&&(s+=i.map(t=>this.renderTaskRow(t,n++,!0)).join(""))});const i=e.get("default");i&&i.length>0&&(s+=i.map(t=>this.renderTaskRow(t,n++,!1)).join("")),t.innerHTML=s}render(){this._shadow.innerHTML=`\n <style>\n ${l}\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--hs-bg);\n }\n .table-container {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n overflow-x: auto;\n position: relative;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--hs-font-size);\n color: var(--hs-text);\n }\n thead {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--hs-bg);\n }\n th {\n text-align: left;\n padding: 8px 12px;\n border-bottom: 2px solid var(--hs-border);\n color: var(--hs-text-secondary);\n font-weight: 600;\n background: var(--hs-bg);\n font-size: 11px;\n text-transform: uppercase;\n white-space: nowrap;\n }\n th:last-child {\n position: sticky;\n right: 0;\n background: var(--hs-bg);\n box-shadow: -2px 0 4px rgba(0,0,0,0.1);\n }\n td {\n padding: 8px 12px;\n border-bottom: 1px solid var(--hs-border);\n vertical-align: middle;\n }\n tr:hover {\n background: var(--hs-bg-secondary);\n cursor: pointer;\n }\n tr.recently-executed {\n animation: flash-row 1s ease-out;\n }\n @keyframes flash-row {\n 0% { background: rgba(34, 197, 94, 0.2); }\n 100% { background: transparent; }\n }\n .status-flash {\n animation: flash-icon 1s ease-out;\n }\n @keyframes flash-icon {\n 0%, 50% { opacity: 1; }\n 25%, 75% { opacity: 0.3; }\n }\n .tip {\n padding: 12px;\n text-align: center;\n font-size: 11px;\n color: var(--hs-text-secondary);\n border-top: 1px solid var(--hs-border);\n background: var(--hs-bg);\n }\n .task-id {\n font-weight: 600;\n }\n .namespace-badge {\n display: inline-block;\n background: var(--hs-bg-secondary);\n color: var(--hs-text-secondary);\n border: 1px solid var(--hs-border);\n border-radius: 4px;\n padding: 0 4px;\n font-size: 9px;\n margin-left: 6px;\n font-family: monospace;\n vertical-align: middle;\n }\n .tags {\n display: flex;\n gap: 4px;\n margin-top: 4px;\n }\n .tag {\n background: var(--hs-bg-secondary);\n border: 1px solid var(--hs-border);\n border-radius: 10px;\n padding: 2px 8px;\n font-size: 10px;\n color: var(--hs-text-secondary);\n }\n .no-tags {\n font-size: 10px;\n color: var(--hs-text-secondary);\n font-style: italic;\n }\n .driver-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n font-family: monospace;\n }\n .driver-badge.worker {\n background: rgba(34, 197, 94, 0.15);\n color: var(--hs-success);\n border: 1px solid var(--hs-success);\n }\n .driver-badge.main {\n background: rgba(245, 158, 11, 0.15);\n color: var(--hs-warning);\n border: 1px solid var(--hs-warning);\n }\n .col-num {\n width: 40px;\n color: var(--hs-text-secondary);\n font-size: 11px;\n }\n .col-actions {\n width: 100px;\n position: sticky;\n right: 0;\n background: var(--hs-bg);\n box-shadow: -2px 0 4px rgba(0,0,0,0.1);\n }\n .action-group {\n display: flex;\n gap: 4px;\n }\n .btn-icon {\n background: transparent;\n border: 1px solid var(--hs-border);\n color: var(--hs-text-secondary);\n border-radius: 4px;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n padding: 0;\n flex-shrink: 0;\n }\n .btn-icon svg {\n width: 14px;\n height: 14px;\n display: block;\n }\n .btn-icon:hover {\n background: var(--hs-primary);\n color: white;\n border-color: var(--hs-primary);\n }\n .btn-trigger:hover {\n background: var(--hs-success);\n border-color: var(--hs-success);\n }\n .btn-pause:hover {\n background: var(--hs-warning);\n border-color: var(--hs-warning);\n }\n .btn-resume:hover {\n background: var(--hs-success);\n border-color: var(--hs-success);\n }\n .btn-remove:hover {\n color: white;\n background: var(--hs-danger);\n border-color: var(--hs-danger);\n }\n .btn-icon:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n .btn-icon:disabled:hover {\n background: transparent;\n color: var(--hs-text-secondary);\n border-color: var(--hs-border);\n }\n /* Namespace Styles */\n .namespace-row {\n background: var(--hs-bg-secondary);\n cursor: pointer;\n font-weight: 600;\n user-select: none;\n }\n .namespace-row:hover {\n background: var(--hs-border);\n }\n .namespace-row td {\n padding: 8px 12px;\n border-bottom: 1px solid var(--hs-border);\n }\n .ns-toggle {\n display: inline-block;\n width: 20px;\n text-align: center;\n font-size: 10px;\n color: var(--hs-text-secondary);\n transition: transform 0.2s;\n }\n .ns-icon {\n margin-right: 4px;\n }\n .ns-count {\n font-weight: normal;\n color: var(--hs-text-secondary);\n font-size: 11px;\n margin-left: 4px;\n }\n .nested-task .col-id {\n padding-left: 32px !important;\n }\n </style>\n <div class="table-container">\n <table>\n <thead>\n <tr>\n <th style="width:40px">#</th>\n <th style="min-width:150px">${r("list.idTags")}</th>\n <th style="width:150px">${r("list.status")}</th>\n <th style="width:70px">${r("list.driver")}</th>\n <th style="width:100px">${r("list.schedule")}</th>\n <th style="width:60px">${r("list.count")}</th>\n <th style="width:100px">${r("list.lastRun")}</th>\n <th style="width:100px">${r("list.actions")}</th>\n </tr>\n </thead>\n <tbody>\n \x3c!-- Rows --\x3e\n </tbody>\n </table>\n </div>\n <div class="tip">\n ${r("list.tip")}\n </div>\n `,this._shadow.addEventListener("click",t=>{const e=t.target,s=e.closest(".namespace-row");if(s){const t=s.dataset.ns;return void(t&&(this._expandedNamespaces.has(t)?this._expandedNamespaces.delete(t):this._expandedNamespaces.add(t),this.renderRows()))}const n=e.closest("button");if(n){const t=n.dataset.action,e=n.closest("tr");if(!t||!e)return;const s=e.dataset.id;return void this.dispatchEvent(new CustomEvent("task-action",{detail:{action:t,id:s},bubbles:!0,composed:!0}))}const i=e.closest("tr");if(i&&!e.closest(".col-actions")&&!i.classList.contains("namespace-row")){const t=i.dataset.id;this.dispatchEvent(new CustomEvent("task-select",{detail:t,bubbles:!0,composed:!0}))}})}}customElements.define("hs-task-list",b);class v extends HTMLElement{constructor(){super(),e(this,"_shadow"),e(this,"_task",null),e(this,"_history",[]),this._shadow=this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this._shadow.addEventListener("click",t=>{t.target.closest(".back-btn")&&this.dispatchEvent(new CustomEvent("back"))})}set task(t){this._task=t,this.renderContent()}set history(t){this._history=t||[],this.renderContent()}updateTexts(){this.renderContent()}renderContent(){const t=this._shadow.querySelector(".content");if(!t)return;if(!this._task)return void(t.innerHTML=`<div class="empty">${r("detail.noTask")}</div>`);const e=this._task,s={id:e.id,schedule:e.schedule,tags:e.tags},n=this._history.length>0?Math.round(this._history.reduce((t,e)=>t+e.duration,0)/this._history.length):0;t.innerHTML=`\n <div class="header">\n <button class="back-btn" title="${r("detail.back")}">${d.back}</button>\n <h2>📂 ${e.id}</h2>\n </div>\n \n <div class="section">\n <div class="config-label">${r("detail.config")}:</div>\n <pre>${JSON.stringify(s,null,2)}</pre>\n </div>\n\n <div class="section">\n <h3>📜 ${r("detail.history")} (${r("detail.lastRuns",{n:this._history.length})}) ${n>0?`- ${r("detail.avgDuration")}: ${n}ms`:""}</h3>\n <table class="history-table">\n <thead>\n <tr>\n <th>#</th>\n <th>${r("detail.startTime")}</th>\n <th>${r("detail.duration")}</th>\n <th>${r("detail.drift")}</th>\n <th>${r("detail.status")}</th>\n </tr>\n </thead>\n <tbody>\n ${0===this._history.length?`<tr><td colspan="5" class="no-data">${r("detail.noHistory")}</td></tr>`:""}\n ${this._history.slice().reverse().map((t,e)=>{const s=t.success?`✅ ${r("detail.success")}`:t.error?`❌ ${r("detail.error")}: ${t.error}`:`⚠️ ${r("detail.failed")}`,n=t.duration>100?"slow":"";return`\n <tr class="${t.success?"success":"error"}">\n <td class="col-num">${this._history.length-e}</td>\n <td>${new Date(t.timestamp).toLocaleTimeString("en-US",{hour12:!1})}</td>\n <td class="${n}">${t.duration}ms</td>\n <td>0ms</td>\n <td class="status-cell">${s}</td>\n </tr>\n `}).join("")}\n </tbody>\n </table>\n </div>\n `}render(){this._shadow.innerHTML=`\n <style>\n ${l}\n :host {\n display: block;\n height: 100%;\n background: var(--hs-bg);\n overflow-y: auto;\n }\n .content {\n padding: 16px;\n }\n .header {\n display: flex;\n align-items: center;\n gap: 16px;\n margin-bottom: 24px;\n padding-bottom: 16px;\n border-bottom: 2px solid var(--hs-border);\n }\n .back-btn {\n background: var(--hs-bg-secondary);\n border: 1px solid var(--hs-border);\n color: var(--hs-text);\n padding: 6px 12px;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n }\n .back-btn:hover {\n background: var(--hs-primary);\n color: white;\n border-color: var(--hs-primary);\n }\n h2 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n }\n h3 {\n font-size: 13px;\n color: var(--hs-text);\n margin-bottom: 12px;\n font-weight: 600;\n }\n .section {\n margin-bottom: 32px;\n }\n .config-label {\n font-size: 12px;\n color: var(--hs-text-secondary);\n margin-bottom: 8px;\n }\n pre {\n background: var(--hs-bg-secondary);\n padding: 12px;\n border-radius: 6px;\n border: 1px solid var(--hs-border);\n font-family: 'Monaco', 'Menlo', monospace;\n font-size: 11px;\n overflow-x: auto;\n line-height: 1.5;\n }\n .history-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 11px;\n }\n .history-table th {\n text-align: left;\n padding: 8px;\n border-bottom: 2px solid var(--hs-border);\n color: var(--hs-text-secondary);\n font-weight: 600;\n background: var(--hs-bg-secondary);\n }\n .history-table td {\n padding: 8px;\n border-bottom: 1px solid var(--hs-border);\n }\n .history-table tr.success {\n background: rgba(34, 197, 94, 0.05);\n }\n .history-table tr.error {\n background: rgba(239, 68, 68, 0.05);\n }\n .history-table tr:hover {\n background: var(--hs-bg-secondary);\n }\n .col-num {\n width: 40px;\n color: var(--hs-text-secondary);\n }\n .slow {\n color: var(--hs-warning);\n font-weight: 600;\n }\n .status-cell {\n font-size: 11px;\n }\n .no-data {\n color: var(--hs-text-secondary);\n font-style: italic;\n text-align: center;\n padding: 24px;\n }\n </style>\n <div class="content"></div>\n `}}customElements.define("hs-task-detail",v);class m extends HTMLElement{constructor(){super(),e(this,"_shadow"),e(this,"_tasks",new Map),e(this,"_history",new Map),e(this,"$canvas"),e(this,"ctx"),e(this,"timeRange",6e4),e(this,"zoom",1),this._shadow=this.attachShadow({mode:"open"})}connectedCallback(){if(this.render(),this.$canvas=this._shadow.querySelector("canvas"),!this.$canvas)return void console.error("[Timeline] Canvas not found");if(this.ctx=this.$canvas.getContext("2d"),!this.ctx)return void console.error("[Timeline] Canvas context not available");this.setupZoom(),this.startLoop();const t=this._shadow.querySelector(".canvas-container");if(t){new ResizeObserver(()=>{requestAnimationFrame(()=>this.draw())}).observe(t)}}set data(t){this._tasks=t.tasks,this._history=t.history}set defaultZoom(t){if(t>=.5&&t<=5){this.zoom=t,this.timeRange=6e4/this.zoom;const e=this._shadow.querySelector(".zoom-slider");e&&(e.value=t.toString()),this.updateZoomLabel()}}updateTexts(){this.updateZoomLabel()}updateZoomLabel(){const t=this._shadow.querySelector(".zoom-label");t&&(t.textContent=`${r("timeline.zoom")}: ${this.zoom}x (${Math.round(this.timeRange/1e3)}s)`)}setupZoom(){const t=this._shadow.querySelector(".zoom-slider"),e=this._shadow.querySelector(".zoom-out"),s=this._shadow.querySelector(".zoom-in"),n=t=>{this.zoom=t,this.timeRange=6e4/this.zoom,this.updateZoomLabel()};null==t||t.addEventListener("input",t=>{const e=parseFloat(t.target.value);n(e)}),null==e||e.addEventListener("click",()=>{const e=Math.max(.5,this.zoom-.5);t&&(t.value=e.toString()),n(e)}),null==s||s.addEventListener("click",()=>{const e=Math.min(5,this.zoom+.5);t&&(t.value=e.toString()),n(e)}),this.updateZoomLabel()}startLoop(){const t=()=>{this.isConnected&&(this.draw(),requestAnimationFrame(t))};requestAnimationFrame(t)}draw(){if(!this.ctx||!this.$canvas)return;const t=this._shadow.querySelector(".canvas-container");if(!t)return;const e=window.devicePixelRatio||1,s=t.clientWidth;if(0===s)return;const n=Array.from(this._tasks.keys()),i=t.clientHeight||300,o=40*n.length+60+40,a=Math.max(i,o);this.$canvas.width=s*e,this.$canvas.height=a*e,this.$canvas.style.width=`${s}px`,this.$canvas.style.height=`${a}px`,this.ctx.setTransform(e,0,0,e,0,0);const r=Date.now(),h=r-this.timeRange,l=getComputedStyle(this),d=l.getPropertyValue("--hs-bg").trim()||"#1e1e1e",c=l.getPropertyValue("--hs-text").trim()||"#fff",p=l.getPropertyValue("--hs-text-secondary").trim()||"#888",g=l.getPropertyValue("--hs-border").trim()||"#333",u=l.getPropertyValue("--hs-success").trim()||"#22c55e",b=l.getPropertyValue("--hs-danger").trim()||"#ef4444";this.ctx.fillStyle=d,this.ctx.fillRect(0,0,s,a),this.drawTimeAxis(s,150,h,p,g),n.forEach((t,e)=>{const n=40*e+60;this.drawTaskRow(t,n,s,150,h,r,c,p,g,u,b)}),this.drawLegend(s,a,p,u)}drawTimeAxis(t,e,s,n,i){const o=this.ctx,a=t-e-20;o.fillStyle=n,o.font="10px monospace",o.textAlign="center";for(let r=0;r<=4;r++){const t=e+a/4*r,n=s+this.timeRange/4*r,h=new Date(n).toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1});o.fillText(h,t,30),o.strokeStyle=i,o.beginPath(),o.moveTo(t,40),o.lineTo(t,this.$canvas.clientHeight-40),o.stroke()}o.textAlign="left",o.fillText(r("timeline.timeRange",{n:Math.round(this.timeRange/1e3)}),10,15)}drawTaskRow(t,e,s,n,i,o,a,r,h,l,d){const c=this.ctx,p=s-n-20;c.fillStyle=a,c.font="11px sans-serif",c.textAlign="left",c.fillText(t,10,e+15);const g=this._tasks.get(t),u="main"===(null==g?void 0:g.driver)?"M":"W",b="W"===u?"#22c55e":"#f59e0b";c.fillStyle=b,c.font="9px monospace",c.fillText(`[${u}]`,10,e+28),c.strokeStyle=h,c.beginPath(),c.moveTo(0,e+35),c.lineTo(s,e+35),c.stroke();(this._history.get(t)||[]).forEach(t=>{if(t.timestamp<i||t.timestamp>o)return;const s=n+(t.timestamp-i)/this.timeRange*p,a=t.duration;if(a<10)c.fillStyle=t.success?l:d,c.beginPath(),c.arc(s,e+15,3,0,2*Math.PI),c.fill();else{const n=Math.max(2,a/this.timeRange*p);c.fillStyle=t.success?l:d,c.globalAlpha=.7,c.fillRect(s,e+5,n,20),c.globalAlpha=1}})}drawLegend(t,e,s,n){const i=this.ctx,o=e-25;i.font="10px sans-serif",i.textAlign="left",i.fillStyle=s;let a=10;i.fillText(`${r("timeline.legend")}:`,a,o),a+=50,i.fillStyle=n,i.beginPath(),i.arc(a,o-4,3,0,2*Math.PI),i.fill(),i.fillStyle=s,i.fillText(r("timeline.instant"),a+10,o),a+=60,i.fillStyle=n,i.globalAlpha=.7,i.fillRect(a,o-8,15,10),i.globalAlpha=1,i.fillStyle=s,i.fillText(r("timeline.duration"),a+20,o),a+=80,i.fillText(`[W] ${r("timeline.workerDriver")}`,a,o),a+=110,i.fillText(`[M] ${r("timeline.mainDriver")}`,a,o)}render(){this._shadow.innerHTML=`\n <style>\n ${l}\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--hs-bg);\n overflow: hidden;\n }\n .controls {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n padding: 12px 16px;\n gap: 8px;\n border-bottom: 1px solid var(--hs-border);\n flex-shrink: 0;\n }\n .zoom-label {\n font-size: 11px;\n color: var(--hs-text-secondary);\n }\n .zoom-btn {\n background: var(--hs-bg-secondary);\n border: 1px solid var(--hs-border);\n color: var(--hs-text);\n width: 24px;\n height: 24px;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .zoom-btn:hover {\n background: var(--hs-primary);\n color: white;\n }\n .zoom-slider {\n width: 100px;\n }\n .canvas-container {\n flex: 1;\n overflow: auto;\n position: relative;\n }\n canvas {\n display: block;\n }\n </style>\n <div class="controls">\n <span class="zoom-label">Zoom:</span>\n <button class="zoom-btn zoom-out">-</button>\n <input type="range" class="zoom-slider" min="0.5" max="5" step="0.5" value="1">\n <button class="zoom-btn zoom-in">+</button>\n </div>\n <div class="canvas-container">\n <canvas></canvas>\n </div>\n `}}customElements.define("hs-timeline",m);class y extends HTMLElement{constructor(){super(),e(this,"_shadow"),e(this,"startX",0),e(this,"startY",0),e(this,"startWidth",0),e(this,"startHeight",0),e(this,"panel",null),e(this,"mode","right"),this._shadow=this.attachShadow({mode:"open"})}isMobile(){return window.innerWidth<=480}connectedCallback(){this.render(),this.addEventListeners(),this.panel=this.closest(".panel"),this.panel&&this.panel.classList.contains("dock-bottom")&&(this.mode="bottom")}addEventListeners(){const t=this._shadow.querySelector(".handle"),e=t=>{if(this.panel&&!this.isMobile())if("right"===this.mode){const e=this.startX-t.clientX,s=Math.max(300,Math.min(window.innerWidth-50,this.startWidth+e));this.panel.style.width=`${s}px`,this.dispatchEvent(new CustomEvent("resize",{detail:{width:s},bubbles:!0,composed:!0}))}else{const e=Math.max(200,Math.min(window.innerHeight-50,this.startHeight+(this.startY-t.clientY)));this.panel.style.height=`${e}px`,this.dispatchEvent(new CustomEvent("resize",{detail:{height:e},bubbles:!0,composed:!0}))}},s=()=>{document.removeEventListener("mousemove",e),document.removeEventListener("mouseup",s),document.body.style.userSelect="",document.body.style.cursor=""};t.addEventListener("mousedown",t=>{this.isMobile()||(this.startX=t.clientX,this.startY=t.clientY,this.panel&&(this.panel.classList.contains("dock-bottom")?(this.mode="bottom",this.startHeight=this.panel.offsetHeight):(this.mode="right",this.startWidth=this.panel.offsetWidth)),document.addEventListener("mousemove",e),document.addEventListener("mouseup",s),document.body.style.userSelect="none",document.body.style.cursor="right"===this.mode?"col-resize":"row-resize")})}render(){this._shadow.innerHTML=`\n <style>\n ${l}\n :host {\n display: block;\n z-index: 100;\n }\n .handle {\n background: transparent;\n transition: background 0.2s;\n }\n .handle:hover {\n background: var(--hs-primary);\n }\n \n /* Right Dock Mode (Vertical Handle on Left) */\n :host-context(.dock-right) .handle {\n width: 4px;\n height: 100%;\n cursor: col-resize;\n position: absolute;\n left: 0;\n top: 0;\n }\n\n /* Bottom Dock Mode (Horizontal Handle on Top) */\n :host-context(.dock-bottom) .handle {\n width: 100%;\n height: 4px;\n cursor: row-resize;\n position: absolute;\n top: 0;\n left: 0;\n }\n \n /* 移动端隐藏拖拽手柄 */\n @media (max-width: 480px) {\n .handle {\n display: none;\n }\n }\n </style>\n <div class="handle"></div>\n `}}customElements.define("hs-resizer",y);class x extends HTMLElement{constructor(){super(),e(this,"_shadow"),e(this,"store"),e(this,"scheduler"),e(this,"rAFId"),e(this,"lastTime",0),e(this,"$panel"),e(this,"$header"),e(this,"$taskList"),e(this,"$taskDetail"),e(this,"$timeline"),e(this,"$trigger"),e(this,"$overlay"),this._shadow=this.attachShadow({mode:"open"}),this.store=new h}connectedCallback(){const t=this.getAttribute("language");"en"!==t&&"zh"!==t||this.store.setLanguageSync(t),this.render(),this.cacheDom(),this.bindStore();const e=this.getAttribute("dock");console.log("[DevTools] dock attribute:",e),"bottom"===e&&this.store.setDockPosition("bottom");const s=this.getAttribute("theme");"light"!==s&&"dark"!==s&&"auto"!==s||this.store.setTheme(s),"en"!==t&&"zh"!==t||this.store.setLanguage(t);const n=this.getAttribute("trigger-bg"),i=this.getAttribute("trigger-color"),o=this.getAttribute("trigger-position");console.log("[DevTools] trigger attrs:",{triggerBg:n,triggerColor:i,triggerPosition:o}),n&&this.$trigger.setAttribute("bg-color",n),i&&this.$trigger.setAttribute("text-color",i),o&&this.$trigger.setAttribute("position",o);const a=this.getAttribute("default-zoom");if(a){const t=parseFloat(a);!isNaN(t)&&t>=.5&&t<=5&&(this.$timeline.defaultZoom=t)}this.addEventListeners(),this.startLoop()}disconnectedCallback(){this.rAFId&&cancelAnimationFrame(this.rAFId)}setScheduler(t){this.scheduler=t,this.store.setScheduler(t);this.scheduler.getTasks().forEach(t=>this.store.updateTask(t));const e=this.scheduler.isRunning();this.store.setSchedulerRunning(e);const n=()=>{var t;((null==(t=this.scheduler)?void 0:t.getTasks())||[]).forEach(t=>this.store.updateTask(t))};this.scheduler.on(s.SchedulerEvents.TASK_REGISTERED,n),this.scheduler.on(s.SchedulerEvents.TASK_UPDATED,t=>{console.log("[DevTools] task_updated event:",t),n()}),this.scheduler.on(s.SchedulerEvents.TASK_STARTED,n),this.scheduler.on(s.SchedulerEvents.TASK_REMOVED,n),this.scheduler.on(s.SchedulerEvents.TASK_STOPPED,t=>{console.log("[DevTools] task_stopped event:",t),n()}),this.scheduler.on(s.SchedulerEvents.SCHEDULER_STARTED,()=>{console.log("[DevTools] scheduler_started event"),this.store.setSchedulerRunning(!0),n()}),this.scheduler.on(s.SchedulerEvents.SCHEDULER_STOPPED,()=>{console.log("[DevTools] scheduler_stopped event"),this.store.setSchedulerRunning(!1),n()}),this.scheduler.on(s.SchedulerEvents.TASK_COMPLETED,t=>{var e;n(),t&&t.taskId&&this.store.addHistory(t.taskId,{timestamp:(null==(e=t.task)?void 0:e.lastRun)||Date.now(),duration:t.duration||0,success:!0,error:null})}),this.scheduler.on(s.SchedulerEvents.TASK_FAILED,t=>{var e;n(),t&&t.taskId&&this.store.addHistory(t.taskId,{timestamp:(null==(e=t.task)?void 0:e.lastRun)||Date.now(),duration:t.duration||0,success:!1,error:t.error||"Unknown error"})}),setInterval(()=>{this.store.getState().isOpen&&n()},500)}cacheDom(){this.$panel=this._shadow.querySelector(".panel"),this.$header=this._shadow.querySelector("hs-task-header"),this.$taskList=this._shadow.querySelector("hs-task-list"),this.$taskDetail=this._shadow.querySelector("hs-task-detail"),this.$timeline=this._shadow.querySelector("hs-timeline"),this.$trigger=this._shadow.querySelector("hs-floating-trigger"),this.$overlay=this._shadow.querySelector(".overlay")}bindStore(){try{const t=localStorage.getItem("hs-panel-size");t&&this.store.setPanelSize(JSON.parse(t))}catch(t){}this.store.subscribe("isOpen",t=>{const e=this.store.getState().dockPosition,s=this.store.getState().panelSize;t?(this.$panel.classList.add("open"),this.$trigger.style.display="none",this.$overlay.style.display="block","right"===e?this.$panel.style.right="0":this.$panel.style.bottom="0"):(this.$panel.classList.remove("open"),this.$trigger.style.display="block",this.$overlay.style.display="none","right"===e?this.$panel.style.right=`-${s.width}px`:this.$panel.style.bottom=`-${s.height}px`)}),this.store.subscribe("theme",t=>{const e="auto"===t?"light":t;this.setAttribute("theme",e),this.$header.setAttribute("theme",e),this.$taskList.setAttribute("theme",e),this.$taskDetail.setAttribute("theme",e),this.$timeline.setAttribute("theme",e),this.$header.theme=t}),this.store.subscribe("tasks",t=>{this.$taskList.tasks=t,this.$timeline.data={tasks:t,history:this.store.getState().history};let e=0;t.forEach(t=>{"idle"!==t.status&&"running"!==t.status||e++}),this.$header.stats={active:e,total:t.size};const s=this.store.getState().selectedTaskId;s&&t.has(s)&&(this.$taskDetail.task=t.get(s)||null)}),this.store.subscribe("history",t=>{this.$timeline.data={tasks:this.store.getState().tasks,history:t};const e=this.store.getState().selectedTaskId;e&&(this.$taskDetail.history=t.get(e)||[])}),this.store.subscribe("selectedTaskId",t=>{if("tasks"===this.store.getState().activeTab)if(t){this.$taskList.style.display="none",this.$taskDetail.style.display="block";const e=this.store.getState().tasks.get(t),s=this.store.getState().history.get(t);this.$taskDetail.task=e||null,this.$taskDetail.history=s||[]}else this.$taskList.style.display="block",this.$taskDetail.style.display="none"}),this.store.subscribe("activeTab",t=>{this.$header.activeTab=t,"tasks"===t?(this.$taskList.style.display="block",this.$timeline.style.display="none",this.store.getState().selectedTaskId?(this.$taskList.style.display="none",this.$taskDetail.style.display="block"):(this.$taskList.style.display="block",this.$taskDetail.style.display="none")):(this.$taskList.style.display="none",this.$taskDetail.style.display="none",this.$timeline.style.display="block")}),this.store.subscribe("dockPosition",t=>{this.$header.dockPosition=t;const e=this.store.getState().panelSize,s=this.store.getState().isOpen,n=window.innerWidth<=480;if("right"===t){this.$panel.classList.add("dock-right"),this.$panel.classList.remove("dock-bottom");const t=n?window.innerWidth:e.width;this.$panel.style.width=n?"100vw":`${t}px`,this.$panel.style.height="100vh",this.$panel.style.bottom="",this.$panel.style.right=s?"0":`-${t}px`}else{this.$panel.classList.add("dock-bottom"),this.$panel.classList.remove("dock-right"),this.$panel.style.width="100%";const t=n?"50vh":`${e.height}px`;this.$panel.style.height=t,this.$panel.style.right="",this.$panel.style.bottom=s?"0":n?"-50vh":`-${e.height}px`}}),this.store.subscribe("panelSize",t=>{const e=this.store.getState().dockPosition;"right"===e&&t.width?(this.$panel.style.width=`${t.width}px`,this.$panel.style.right=this.store.getState().isOpen?"0":`-${t.width}px`):"bottom"===e&&t.height&&(this.$panel.style.height=`${t.height}px`,this.$panel.style.bottom=this.store.getState().isOpen?"0":`-${t.height}px`)}),this.store.subscribe("language",t=>{var e,s,n,i;this.$header.language=t,this.$taskList.updateHeaders(),null==(s=(e=this.$taskDetail).updateTexts)||s.call(e),null==(i=(n=this.$timeline).updateTexts)||i.call(n)}),this.store.subscribe("filterText",t=>{const e=this.store.getState().tasks;this.$taskList.filter(t,e)}),this.store.subscribe("schedulerRunning",t=>{this.$header.schedulerRunning=t})}addEventListeners(){this.$trigger.addEventListener("toggle",()=>{this.store.toggle()}),this.$header.addEventListener("close",t=>{t.stopPropagation(),this.store.toggle()}),this.$header.addEventListener("dock-toggle",()=>{const t=this.store.getState().dockPosition;this.store.setDockPosition("right"===t?"bottom":"right")}),this.$header.addEventListener("theme-toggle",t=>{const e=t.detail;this.store.setTheme(e)}),this.$header.addEventListener("lang-toggle",t=>{const e=t.detail;this.store.setLanguage(e)}),this.$header.addEventListener("tab-change",t=>{const e=t.detail;this.store.setTab(e)}),this.$header.addEventListener("search",t=>{const e=t.detail;this.store.setFilterText(e)}),this.$overlay.addEventListener("click",()=>{this.store.toggle()}),this.$taskList.addEventListener("task-select",t=>{const e=t.detail;this.store.selectTask(e)}),this.$taskDetail.addEventListener("back",()=>{this.store.selectTask(null)}),this.$taskList.addEventListener("task-action",t=>{const{action:e,id:s}=t.detail;switch(console.log("[DevTools] task-action:",e,s),e){case"trigger":this.store.triggerTask(s);break;case"stop":this.store.stopTask(s);break;case"start":this.store.startTask(s);break;case"remove":confirm(`Remove task "${s}"?`)&&this.store.removeTask(s)}}),this._shadow.addEventListener("resize",t=>{const{width:e,height:s}=t.detail;void 0!==e&&this.store.setPanelSize({width:e}),void 0!==s&&this.store.setPanelSize({height:s})})}startLoop(){const t=e=>{const s=e-this.lastTime;this.lastTime=e;const n=1e3/s;this.$header&&(this.$header.fps=n),this.rAFId=requestAnimationFrame(t)};this.rAFId=requestAnimationFrame(t)}render(){this._shadow.innerHTML=`\n <style>\n ${l}\n :host {\n font-family: var(--hs-font-family);\n font-size: var(--hs-font-size);\n color: var(--hs-text);\n line-height: var(--hs-line-height);\n }\n .overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n background: rgba(0, 0, 0, 0.5);\n z-index: calc(var(--hs-z-index) - 1); /* 确保在面板之下,但在应用之上 */\n display: none; /* 默认隐藏 */\n }\n .panel {\n position: fixed;\n background: var(--hs-bg);\n box-shadow: var(--hs-shadow);\n z-index: var(--hs-z-index);\n transition: all 0.3s ease;\n display: flex;\n flex-direction: column;\n border-left: 1px solid var(--hs-border);\n }\n /* Default Right Dock */\n .panel.dock-right {\n top: 0;\n right: -500px;\n width: 500px;\n height: 100vh;\n border-left: 1px solid var(--hs-border);\n border-top: none;\n }\n \n /* Bottom Dock */\n .panel.dock-bottom {\n bottom: -50vh;\n left: 0;\n width: 100%;\n height: 50vh;\n max-height: 50vh;\n border-top: 1px solid var(--hs-border);\n border-left: none;\n }\n\n .content {\n flex: 1;\n overflow: hidden;\n position: relative;\n display: flex;\n flex-direction: column;\n }\n .content > * {\n flex: 1;\n min-height: 0;\n }\n \n /* Mobile - 固定尺寸,禁用拖拽 */\n @media (max-width: 480px) {\n .panel.dock-right {\n width: 100vw !important;\n right: -100vw;\n }\n .panel.dock-right.open {\n right: 0;\n }\n .panel.dock-bottom {\n height: 50vh !important;\n max-height: 50vh !important;\n bottom: -50vh;\n }\n .panel.dock-bottom.open {\n bottom: 0;\n }\n }\n </style>\n \n <div class="overlay"></div>\n <hs-floating-trigger></hs-floating-trigger>\n \n <div class="panel dock-right">\n <hs-resizer></hs-resizer>\n <hs-task-header></hs-task-header>\n <div class="content">\n <hs-task-list></hs-task-list>\n <hs-task-detail style="display:none"></hs-task-detail>\n <hs-timeline style="display:none"></hs-timeline>\n </div>\n </div>\n `}}customElements.define("hs-devtools",x),exports.DevTools=x;
|