hyper-scheduler 1.0.1 → 1.1.1
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 +18 -1
- package/dist/{devtools-4xVHTaUz.js → devtools-_ah1nNTk.js} +416 -33
- package/dist/devtools-xPwLHcqi.cjs +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +91 -38
- package/dist/index.umd.cjs +1 -1
- package/dist/logo.svg +54 -0
- package/package.json +9 -6
- package/public/logo.svg +54 -0
- package/dist/devtools-DdQ1I25H.cjs +0 -1
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="
|
|
2
|
+
<img src="public/logo.svg" width="120" height="120" alt="Hyper Scheduler Logo">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">Hyper Scheduler</h1>
|
|
@@ -14,6 +14,8 @@ A lightweight, dependency-free (core) JavaScript task scheduler supporting Cron
|
|
|
14
14
|
## Features
|
|
15
15
|
- 🚀 **Cross-platform**: Works in Node.js and Browser.
|
|
16
16
|
- ⏰ **Precise Timing**: Uses Web Workers in browser to avoid background throttling.
|
|
17
|
+
- 🏷️ **Namespaces**: Isolate tasks into logical groups for batch control.
|
|
18
|
+
- ⚡ **Immediate Trigger**: Option to execute tasks immediately upon start.
|
|
17
19
|
- 🛠 **Debuggable**: Built-in debug panel and CLI output.
|
|
18
20
|
- 📦 **Tiny**: < 20KB gzipped.
|
|
19
21
|
|
|
@@ -27,12 +29,27 @@ npm install hyper-scheduler
|
|
|
27
29
|
import { Scheduler } from 'hyper-scheduler';
|
|
28
30
|
|
|
29
31
|
const scheduler = new Scheduler({ debug: true });
|
|
32
|
+
|
|
33
|
+
// Standard task
|
|
30
34
|
scheduler.createTask({
|
|
31
35
|
id: 'hello',
|
|
32
36
|
schedule: '*/5 * * * * *',
|
|
33
37
|
handler: () => console.log('Hello World')
|
|
34
38
|
});
|
|
39
|
+
|
|
40
|
+
// Task in a namespace with immediate execution
|
|
41
|
+
scheduler.createTask({
|
|
42
|
+
id: 'system-check',
|
|
43
|
+
schedule: '1h',
|
|
44
|
+
handler: () => console.log('System Check'),
|
|
45
|
+
options: {
|
|
46
|
+
namespace: 'system',
|
|
47
|
+
runImmediately: true
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
35
51
|
scheduler.start();
|
|
52
|
+
// Or start only 'system' namespace: scheduler.start('system');
|
|
36
53
|
```
|
|
37
54
|
|
|
38
55
|
See [Documentation](docs/guide/getting-started.md) for more details.
|
|
@@ -327,6 +327,9 @@ const themeStyles = `
|
|
|
327
327
|
--hs-panel-width: 400px;
|
|
328
328
|
--hs-panel-height: 300px;
|
|
329
329
|
|
|
330
|
+
/* 等宽数字字体 */
|
|
331
|
+
--hs-font-monospaced-num: var(--hs-font-mono);
|
|
332
|
+
|
|
330
333
|
/* Light Theme (Default) */
|
|
331
334
|
--hs-bg: #ffffff;
|
|
332
335
|
--hs-bg-secondary: #f3f4f6;
|
|
@@ -344,6 +347,7 @@ const themeStyles = `
|
|
|
344
347
|
--hs-radius: 6px;
|
|
345
348
|
--hs-header-height: 40px;
|
|
346
349
|
--hs-z-index: 9999;
|
|
350
|
+
--hs-z-index-overlay: 9998;
|
|
347
351
|
|
|
348
352
|
/* Default display styles for the host itself */
|
|
349
353
|
background: var(--hs-bg);
|
|
@@ -387,6 +391,8 @@ const ICONS = {
|
|
|
387
391
|
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>`,
|
|
388
392
|
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>`
|
|
389
393
|
};
|
|
394
|
+
const STORAGE_KEY_POS = "hs-trigger-position";
|
|
395
|
+
const STORAGE_KEY_COLLAPSED = "hs-trigger-collapsed";
|
|
390
396
|
class FloatingTrigger extends HTMLElement {
|
|
391
397
|
constructor() {
|
|
392
398
|
super();
|
|
@@ -395,43 +401,230 @@ class FloatingTrigger extends HTMLElement {
|
|
|
395
401
|
__publicField(this, "_position", "bottom-right");
|
|
396
402
|
__publicField(this, "_bgColor", "");
|
|
397
403
|
__publicField(this, "_textColor", "");
|
|
404
|
+
__publicField(this, "_isDragging", false);
|
|
405
|
+
__publicField(this, "_wasDragging", false);
|
|
406
|
+
// 标记是否发生过拖拽
|
|
407
|
+
__publicField(this, "_offsetX", 0);
|
|
408
|
+
__publicField(this, "_offsetY", 0);
|
|
409
|
+
__publicField(this, "_isCollapsed", false);
|
|
398
410
|
this._shadow = this.attachShadow({ mode: "open" });
|
|
399
411
|
}
|
|
412
|
+
// 新增:收起状态
|
|
400
413
|
static get observedAttributes() {
|
|
401
414
|
return ["position", "bg-color", "text-color"];
|
|
402
415
|
}
|
|
403
416
|
connectedCallback() {
|
|
417
|
+
this.loadState();
|
|
404
418
|
this.render();
|
|
405
419
|
this.addEventListeners();
|
|
420
|
+
this.applyPosition();
|
|
421
|
+
window.addEventListener("resize", this.onResize.bind(this));
|
|
422
|
+
}
|
|
423
|
+
disconnectedCallback() {
|
|
424
|
+
window.removeEventListener("resize", this.onResize.bind(this));
|
|
425
|
+
}
|
|
426
|
+
onResize() {
|
|
427
|
+
this.applyPosition();
|
|
406
428
|
}
|
|
407
429
|
attributeChangedCallback(name, _oldVal, newVal) {
|
|
408
|
-
console.log("[FloatingTrigger] attributeChangedCallback:", name, newVal);
|
|
409
430
|
if (name === "position") {
|
|
410
431
|
this._position = newVal || "bottom-right";
|
|
432
|
+
this.applyPosition();
|
|
411
433
|
} else if (name === "bg-color") {
|
|
412
434
|
this._bgColor = newVal || "";
|
|
435
|
+
this.updateStyles();
|
|
413
436
|
} else if (name === "text-color") {
|
|
414
437
|
this._textColor = newVal || "";
|
|
438
|
+
this.updateStyles();
|
|
415
439
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
440
|
+
}
|
|
441
|
+
loadState() {
|
|
442
|
+
try {
|
|
443
|
+
const savedPos = localStorage.getItem(STORAGE_KEY_POS);
|
|
444
|
+
if (savedPos) {
|
|
445
|
+
const { x, y } = JSON.parse(savedPos);
|
|
446
|
+
this.style.setProperty("--hs-trigger-left", `${x}px`);
|
|
447
|
+
this.style.setProperty("--hs-trigger-top", `${y}px`);
|
|
448
|
+
this.style.setProperty("--hs-trigger-position-set", "true");
|
|
449
|
+
}
|
|
450
|
+
const savedCollapsed = localStorage.getItem(STORAGE_KEY_COLLAPSED);
|
|
451
|
+
if (savedCollapsed === "true") {
|
|
452
|
+
this._isCollapsed = true;
|
|
453
|
+
}
|
|
454
|
+
} catch (e) {
|
|
455
|
+
console.warn("[FloatingTrigger] Failed to load state:", e);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
saveState() {
|
|
459
|
+
const button = this._shadow.querySelector("button");
|
|
460
|
+
if (!button) return;
|
|
461
|
+
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
|
+
}
|
|
477
|
+
applyPosition() {
|
|
478
|
+
const button = this._shadow.querySelector("button");
|
|
479
|
+
if (!button) return;
|
|
480
|
+
const maxX = window.innerWidth - button.offsetWidth;
|
|
481
|
+
const maxY = window.innerHeight - button.offsetHeight;
|
|
482
|
+
if (this._isCollapsed) {
|
|
483
|
+
let currentY = parseFloat(this.style.getPropertyValue("--hs-trigger-top") || "20");
|
|
484
|
+
currentY = Math.max(0, Math.min(currentY, maxY));
|
|
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
|
+
} else {
|
|
491
|
+
if (!this.style.getPropertyValue("--hs-trigger-position-set")) {
|
|
492
|
+
const pos = this._position;
|
|
493
|
+
button.style.top = pos.includes("top") ? "20px" : "auto";
|
|
494
|
+
button.style.bottom = pos.includes("bottom") ? "20px" : "auto";
|
|
495
|
+
button.style.left = pos.includes("left") ? "20px" : "auto";
|
|
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";
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
this.updateCollapsedState();
|
|
511
|
+
}
|
|
512
|
+
updateStyles() {
|
|
513
|
+
const button = this._shadow.querySelector("button");
|
|
514
|
+
if (button) {
|
|
515
|
+
button.style.background = this._bgColor || "var(--hs-primary)";
|
|
516
|
+
button.style.color = this._textColor || "white";
|
|
517
|
+
if (this._bgColor) {
|
|
518
|
+
button.style.setProperty("--hs-trigger-bg-hover", `${this._bgColor}; filter: brightness(1.1);`);
|
|
519
|
+
} else {
|
|
520
|
+
button.style.removeProperty("--hs-trigger-bg-hover");
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
updateCollapsedState() {
|
|
525
|
+
const button = this._shadow.querySelector("button");
|
|
526
|
+
if (button) {
|
|
527
|
+
if (this._isCollapsed) {
|
|
528
|
+
button.classList.add("collapsed");
|
|
529
|
+
} else {
|
|
530
|
+
button.classList.remove("collapsed");
|
|
531
|
+
}
|
|
420
532
|
}
|
|
421
533
|
}
|
|
422
534
|
addEventListeners() {
|
|
423
535
|
const btn = this._shadow.querySelector("button");
|
|
424
|
-
|
|
536
|
+
if (!btn) return;
|
|
537
|
+
btn.addEventListener("click", (e) => {
|
|
538
|
+
if (this._wasDragging) {
|
|
539
|
+
e.preventDefault();
|
|
540
|
+
e.stopPropagation();
|
|
541
|
+
this._wasDragging = false;
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
425
544
|
this.dispatchEvent(new CustomEvent("toggle", { bubbles: true, composed: true }));
|
|
426
545
|
});
|
|
546
|
+
const collapseBtn = this._shadow.querySelector(".collapse-btn");
|
|
547
|
+
collapseBtn == null ? void 0 : collapseBtn.addEventListener("click", (e) => {
|
|
548
|
+
e.stopPropagation();
|
|
549
|
+
e.preventDefault();
|
|
550
|
+
this._isCollapsed = !this._isCollapsed;
|
|
551
|
+
this.applyPosition();
|
|
552
|
+
this.saveState();
|
|
553
|
+
});
|
|
554
|
+
btn.addEventListener("dblclick", (e) => {
|
|
555
|
+
if (this._isCollapsed) {
|
|
556
|
+
e.stopPropagation();
|
|
557
|
+
this._isCollapsed = false;
|
|
558
|
+
this.applyPosition();
|
|
559
|
+
this.saveState();
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
btn.addEventListener("mousedown", (e) => {
|
|
563
|
+
if (e.button !== 0) return;
|
|
564
|
+
if (e.target.closest(".collapse-btn")) return;
|
|
565
|
+
this._isDragging = true;
|
|
566
|
+
this._wasDragging = false;
|
|
567
|
+
this._offsetX = e.clientX - btn.getBoundingClientRect().left;
|
|
568
|
+
this._offsetY = e.clientY - btn.getBoundingClientRect().top;
|
|
569
|
+
const startX = e.clientX;
|
|
570
|
+
const startY = e.clientY;
|
|
571
|
+
let hasMoved = false;
|
|
572
|
+
const onMouseMove = (moveEvent) => {
|
|
573
|
+
if (!this._isDragging) return;
|
|
574
|
+
if (!hasMoved && (Math.abs(moveEvent.clientX - startX) > 2 || Math.abs(moveEvent.clientY - startY) > 2)) {
|
|
575
|
+
hasMoved = true;
|
|
576
|
+
this._wasDragging = true;
|
|
577
|
+
btn.style.cursor = "grabbing";
|
|
578
|
+
btn.style.transition = "none";
|
|
579
|
+
}
|
|
580
|
+
if (hasMoved) {
|
|
581
|
+
let newX = moveEvent.clientX - this._offsetX;
|
|
582
|
+
let newY = moveEvent.clientY - this._offsetY;
|
|
583
|
+
const maxX = window.innerWidth - btn.offsetWidth;
|
|
584
|
+
const maxY = window.innerHeight - btn.offsetHeight;
|
|
585
|
+
newX = Math.max(0, Math.min(newX, maxX));
|
|
586
|
+
newY = Math.max(0, Math.min(newY, maxY));
|
|
587
|
+
if (this._isCollapsed) {
|
|
588
|
+
btn.style.left = "auto";
|
|
589
|
+
btn.style.right = "0px";
|
|
590
|
+
} else {
|
|
591
|
+
btn.style.left = `${newX}px`;
|
|
592
|
+
btn.style.right = "auto";
|
|
593
|
+
this.style.setProperty("--hs-trigger-left", `${newX}px`);
|
|
594
|
+
}
|
|
595
|
+
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
|
+
}
|
|
600
|
+
};
|
|
601
|
+
const onMouseUp = () => {
|
|
602
|
+
if (!this._isDragging) return;
|
|
603
|
+
this._isDragging = false;
|
|
604
|
+
btn.style.cursor = "pointer";
|
|
605
|
+
btn.style.transition = "";
|
|
606
|
+
if (hasMoved) {
|
|
607
|
+
this.saveState();
|
|
608
|
+
const preventClick = (e2) => {
|
|
609
|
+
e2.stopPropagation();
|
|
610
|
+
e2.preventDefault();
|
|
611
|
+
};
|
|
612
|
+
window.addEventListener("click", preventClick, { capture: true, once: true });
|
|
613
|
+
}
|
|
614
|
+
window.removeEventListener("mousemove", onMouseMove);
|
|
615
|
+
window.removeEventListener("mouseup", onMouseUp);
|
|
616
|
+
};
|
|
617
|
+
window.addEventListener("mousemove", onMouseMove);
|
|
618
|
+
window.addEventListener("mouseup", onMouseUp);
|
|
619
|
+
});
|
|
427
620
|
}
|
|
428
621
|
render() {
|
|
429
|
-
const pos = this._position;
|
|
430
622
|
const posStyles = `
|
|
431
|
-
top: ${
|
|
432
|
-
bottom: ${
|
|
433
|
-
left: ${
|
|
434
|
-
right: ${
|
|
623
|
+
top: var(--hs-trigger-top, ${this._position.includes("top") ? "20px" : "auto"});
|
|
624
|
+
bottom: var(--hs-trigger-bottom, ${this._position.includes("bottom") ? "20px" : "auto"});
|
|
625
|
+
left: var(--hs-trigger-left, ${this._position.includes("left") ? "20px" : "auto"});
|
|
626
|
+
right: var(--hs-trigger-right, ${this._position.includes("right") ? "20px" : "auto"});
|
|
627
|
+
transform: translateX(0);
|
|
435
628
|
`;
|
|
436
629
|
const bgStyle = this._bgColor ? `background: ${this._bgColor};` : "";
|
|
437
630
|
const colorStyle = this._textColor ? `color: ${this._textColor};` : "";
|
|
@@ -443,33 +636,104 @@ class FloatingTrigger extends HTMLElement {
|
|
|
443
636
|
${posStyles}
|
|
444
637
|
width: 48px;
|
|
445
638
|
height: 48px;
|
|
446
|
-
border-radius:
|
|
639
|
+
border-radius: 12px; /* 更现代的圆角 */
|
|
447
640
|
background: var(--hs-primary);
|
|
448
641
|
color: white;
|
|
449
642
|
${bgStyle}
|
|
450
643
|
${colorStyle}
|
|
451
644
|
border: none;
|
|
452
|
-
box-shadow:
|
|
645
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15); /* 更柔和的阴影 */
|
|
453
646
|
cursor: pointer;
|
|
454
647
|
display: flex;
|
|
455
648
|
align-items: center;
|
|
456
649
|
justify-content: center;
|
|
457
650
|
font-family: var(--hs-font-family);
|
|
458
651
|
z-index: var(--hs-z-index);
|
|
652
|
+
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), background 0.2s, box-shadow 0.2s;
|
|
653
|
+
overflow: visible; /* 允许子元素(收起按钮)溢出或显示 */
|
|
654
|
+
}
|
|
655
|
+
.icon {
|
|
459
656
|
transition: transform 0.2s;
|
|
657
|
+
display: flex;
|
|
460
658
|
}
|
|
461
659
|
button:hover {
|
|
462
660
|
${this._bgColor ? `background: ${this._bgColor}; filter: brightness(1.1);` : "background: var(--hs-primary-hover);"}
|
|
661
|
+
box-shadow: 0 6px 16px rgba(0,0,0,0.2);
|
|
662
|
+
}
|
|
663
|
+
button:hover .icon {
|
|
463
664
|
transform: scale(1.1);
|
|
464
665
|
}
|
|
465
|
-
button:active {
|
|
666
|
+
button:active .icon {
|
|
466
667
|
transform: scale(0.95);
|
|
467
668
|
}
|
|
669
|
+
|
|
670
|
+
/* 显式收起按钮 */
|
|
671
|
+
.collapse-btn {
|
|
672
|
+
position: absolute;
|
|
673
|
+
top: 0;
|
|
674
|
+
right: 0;
|
|
675
|
+
width: 20px;
|
|
676
|
+
height: 20px;
|
|
677
|
+
background: rgba(0,0,0,0.1);
|
|
678
|
+
border-top-right-radius: 12px;
|
|
679
|
+
display: flex;
|
|
680
|
+
align-items: center;
|
|
681
|
+
justify-content: center;
|
|
682
|
+
opacity: 1; /* 默认可见 */
|
|
683
|
+
transition: opacity 0.2s, background 0.2s;
|
|
684
|
+
color: white;
|
|
685
|
+
font-weight: bold;
|
|
686
|
+
}
|
|
687
|
+
.collapse-btn::before {
|
|
688
|
+
content: '—'; /* 最小化图标 */
|
|
689
|
+
font-size: 12px;
|
|
690
|
+
color: currentColor;
|
|
691
|
+
}
|
|
692
|
+
button:hover .collapse-btn {
|
|
693
|
+
opacity: 1;
|
|
694
|
+
}
|
|
695
|
+
.collapse-btn:hover {
|
|
696
|
+
background: rgba(0,0,0,0.3);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/* 收起状态样式 */
|
|
700
|
+
button.collapsed {
|
|
701
|
+
width: 24px;
|
|
702
|
+
height: 48px;
|
|
703
|
+
border-radius: 12px 0 0 12px;
|
|
704
|
+
transform: none; /* 完全展示,不再隐藏 */
|
|
705
|
+
opacity: 1;
|
|
706
|
+
right: 0 !important; /* 强制右对齐 */
|
|
707
|
+
left: auto !important;
|
|
708
|
+
}
|
|
709
|
+
button.collapsed:hover {
|
|
710
|
+
width: 28px; /* hover 时稍微变宽 */
|
|
711
|
+
opacity: 1;
|
|
712
|
+
}
|
|
713
|
+
button.collapsed .icon {
|
|
714
|
+
display: none;
|
|
715
|
+
}
|
|
716
|
+
button.collapsed .collapse-btn {
|
|
717
|
+
position: static; /* 充满父容器 */
|
|
718
|
+
width: 100%;
|
|
719
|
+
height: 100%;
|
|
720
|
+
border-radius: 0;
|
|
721
|
+
background: transparent;
|
|
722
|
+
}
|
|
723
|
+
button.collapsed .collapse-btn::before {
|
|
724
|
+
content: '‹'; /* 展开图标 (左箭头) */
|
|
725
|
+
font-size: 20px;
|
|
726
|
+
line-height: 48px;
|
|
727
|
+
}
|
|
468
728
|
</style>
|
|
469
729
|
<button title="Toggle Hyper Scheduler DevTools">
|
|
470
|
-
|
|
730
|
+
<div class="collapse-btn" title="Minimize"></div>
|
|
731
|
+
<span class="icon">${ICONS.chart}</span>
|
|
471
732
|
</button>
|
|
472
733
|
`;
|
|
734
|
+
this.applyPosition();
|
|
735
|
+
this.updateCollapsedState();
|
|
736
|
+
this.updateStyles();
|
|
473
737
|
}
|
|
474
738
|
}
|
|
475
739
|
customElements.define("hs-floating-trigger", FloatingTrigger);
|
|
@@ -504,7 +768,7 @@ class TaskHeader extends HTMLElement {
|
|
|
504
768
|
this._fps = Math.round(val);
|
|
505
769
|
if (this.$fps) {
|
|
506
770
|
const color = this._fps < 30 ? "var(--hs-danger)" : this._fps < 50 ? "var(--hs-warning)" : "var(--hs-success)";
|
|
507
|
-
this.$fps.innerHTML = `⚡ ${t("stats.fps")}: <span style="color:${color}">${this._fps}</span> (${t("stats.mainThread")})`;
|
|
771
|
+
this.$fps.innerHTML = `⚡ ${t("stats.fps")}: <span style="color:${color}; font-family:var(--hs-font-monospaced-num);">${this._fps}</span> (${t("stats.mainThread")})`;
|
|
508
772
|
}
|
|
509
773
|
}
|
|
510
774
|
set stats(val) {
|
|
@@ -766,6 +1030,7 @@ class TaskList extends HTMLElement {
|
|
|
766
1030
|
__publicField(this, "_shadow");
|
|
767
1031
|
__publicField(this, "_tasks", []);
|
|
768
1032
|
__publicField(this, "_lastExecutionTimes", /* @__PURE__ */ new Map());
|
|
1033
|
+
__publicField(this, "_expandedNamespaces", /* @__PURE__ */ new Set(["default"]));
|
|
769
1034
|
this._shadow = this.attachShadow({ mode: "open" });
|
|
770
1035
|
}
|
|
771
1036
|
connectedCallback() {
|
|
@@ -782,6 +1047,17 @@ class TaskList extends HTMLElement {
|
|
|
782
1047
|
this._tasks = newTasks;
|
|
783
1048
|
this.renderRows();
|
|
784
1049
|
}
|
|
1050
|
+
groupTasksByNamespace(tasks) {
|
|
1051
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1052
|
+
tasks.forEach((task) => {
|
|
1053
|
+
const ns = task.namespace || "default";
|
|
1054
|
+
if (!groups.has(ns)) {
|
|
1055
|
+
groups.set(ns, []);
|
|
1056
|
+
}
|
|
1057
|
+
groups.get(ns).push(task);
|
|
1058
|
+
});
|
|
1059
|
+
return groups;
|
|
1060
|
+
}
|
|
785
1061
|
filter(text, map) {
|
|
786
1062
|
const all = Array.from(map.values());
|
|
787
1063
|
if (!text) {
|
|
@@ -789,7 +1065,7 @@ class TaskList extends HTMLElement {
|
|
|
789
1065
|
} else {
|
|
790
1066
|
const lower = text.toLowerCase();
|
|
791
1067
|
this._tasks = all.filter(
|
|
792
|
-
(t2) => t2.id.toLowerCase().includes(lower) || t2.tags.some((tag) => tag.toLowerCase().includes(lower))
|
|
1068
|
+
(t2) => t2.id.toLowerCase().includes(lower) || t2.tags.some((tag) => tag.toLowerCase().includes(lower)) || t2.namespace && t2.namespace.toLowerCase().includes(lower)
|
|
793
1069
|
);
|
|
794
1070
|
}
|
|
795
1071
|
this.renderRows();
|
|
@@ -862,18 +1138,21 @@ class TaskList extends HTMLElement {
|
|
|
862
1138
|
}
|
|
863
1139
|
return `<span class="driver-badge main" title="${t("list.driverMain")}">M</span>`;
|
|
864
1140
|
}
|
|
865
|
-
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1141
|
+
renderTaskRow(task, index, isNested = false) {
|
|
1142
|
+
const lastExec = this._lastExecutionTimes.get(task.id);
|
|
1143
|
+
const isRecentlyExecuted = lastExec && Date.now() - lastExec < 1e3;
|
|
1144
|
+
const rowClass = isRecentlyExecuted ? "recently-executed" : "";
|
|
1145
|
+
const nestedClass = isNested ? "nested-task" : "";
|
|
1146
|
+
return `
|
|
1147
|
+
<tr data-id="${task.id}" class="${rowClass} ${nestedClass}">
|
|
1148
|
+
<td class="col-num">
|
|
1149
|
+
${isNested ? "" : index + 1}
|
|
1150
|
+
</td>
|
|
875
1151
|
<td class="col-id">
|
|
876
|
-
<div class="task-id"
|
|
1152
|
+
<div class="task-id">
|
|
1153
|
+
${task.id}
|
|
1154
|
+
${task.namespace && task.namespace !== "default" && !isNested ? `<span class="namespace-badge" title="Namespace">${task.namespace}</span>` : ""}
|
|
1155
|
+
</div>
|
|
877
1156
|
<div class="tags">
|
|
878
1157
|
${task.tags && task.tags.length > 0 ? task.tags.map((t2) => `<span class="tag">${t2}</span>`).join("") : `<span class="no-tags">${t("list.noTags")}</span>`}
|
|
879
1158
|
</div>
|
|
@@ -892,7 +1171,38 @@ class TaskList extends HTMLElement {
|
|
|
892
1171
|
</td>
|
|
893
1172
|
</tr>
|
|
894
1173
|
`;
|
|
895
|
-
|
|
1174
|
+
}
|
|
1175
|
+
renderRows() {
|
|
1176
|
+
const tbody = this._shadow.querySelector("tbody");
|
|
1177
|
+
if (!tbody) return;
|
|
1178
|
+
const groups = this.groupTasksByNamespace(this._tasks);
|
|
1179
|
+
let html = "";
|
|
1180
|
+
let globalIndex = 0;
|
|
1181
|
+
const defaultTasks = groups.get("default");
|
|
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));
|
|
1187
|
+
sortedNamespaces.forEach((ns) => {
|
|
1188
|
+
const tasks = groups.get(ns);
|
|
1189
|
+
const isExpanded = this._expandedNamespaces.has(ns);
|
|
1190
|
+
const icon = isExpanded ? "▼" : "▶";
|
|
1191
|
+
html += `
|
|
1192
|
+
<tr class="namespace-row ${isExpanded ? "ns-expanded" : ""}" data-ns="${ns}">
|
|
1193
|
+
<td colspan="8">
|
|
1194
|
+
<span class="ns-toggle">${icon}</span>
|
|
1195
|
+
<span class="ns-icon">📂</span>
|
|
1196
|
+
<span class="ns-name">${ns}</span>
|
|
1197
|
+
<span class="ns-count">(${tasks.length})</span>
|
|
1198
|
+
</td>
|
|
1199
|
+
</tr>
|
|
1200
|
+
`;
|
|
1201
|
+
if (isExpanded) {
|
|
1202
|
+
html += tasks.map((task) => this.renderTaskRow(task, ++globalIndex, true)).join("");
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
tbody.innerHTML = html;
|
|
896
1206
|
}
|
|
897
1207
|
render() {
|
|
898
1208
|
this._shadow.innerHTML = `
|
|
@@ -974,6 +1284,18 @@ class TaskList extends HTMLElement {
|
|
|
974
1284
|
.task-id {
|
|
975
1285
|
font-weight: 600;
|
|
976
1286
|
}
|
|
1287
|
+
.namespace-badge {
|
|
1288
|
+
display: inline-block;
|
|
1289
|
+
background: var(--hs-bg-secondary);
|
|
1290
|
+
color: var(--hs-text-secondary);
|
|
1291
|
+
border: 1px solid var(--hs-border);
|
|
1292
|
+
border-radius: 4px;
|
|
1293
|
+
padding: 0 4px;
|
|
1294
|
+
font-size: 9px;
|
|
1295
|
+
margin-left: 6px;
|
|
1296
|
+
font-family: monospace;
|
|
1297
|
+
vertical-align: middle;
|
|
1298
|
+
}
|
|
977
1299
|
.tags {
|
|
978
1300
|
display: flex;
|
|
979
1301
|
gap: 4px;
|
|
@@ -1079,6 +1401,40 @@ class TaskList extends HTMLElement {
|
|
|
1079
1401
|
color: var(--hs-text-secondary);
|
|
1080
1402
|
border-color: var(--hs-border);
|
|
1081
1403
|
}
|
|
1404
|
+
/* Namespace Styles */
|
|
1405
|
+
.namespace-row {
|
|
1406
|
+
background: var(--hs-bg-secondary);
|
|
1407
|
+
cursor: pointer;
|
|
1408
|
+
font-weight: 600;
|
|
1409
|
+
user-select: none;
|
|
1410
|
+
}
|
|
1411
|
+
.namespace-row:hover {
|
|
1412
|
+
background: var(--hs-border);
|
|
1413
|
+
}
|
|
1414
|
+
.namespace-row td {
|
|
1415
|
+
padding: 8px 12px;
|
|
1416
|
+
border-bottom: 1px solid var(--hs-border);
|
|
1417
|
+
}
|
|
1418
|
+
.ns-toggle {
|
|
1419
|
+
display: inline-block;
|
|
1420
|
+
width: 20px;
|
|
1421
|
+
text-align: center;
|
|
1422
|
+
font-size: 10px;
|
|
1423
|
+
color: var(--hs-text-secondary);
|
|
1424
|
+
transition: transform 0.2s;
|
|
1425
|
+
}
|
|
1426
|
+
.ns-icon {
|
|
1427
|
+
margin-right: 4px;
|
|
1428
|
+
}
|
|
1429
|
+
.ns-count {
|
|
1430
|
+
font-weight: normal;
|
|
1431
|
+
color: var(--hs-text-secondary);
|
|
1432
|
+
font-size: 11px;
|
|
1433
|
+
margin-left: 4px;
|
|
1434
|
+
}
|
|
1435
|
+
.nested-task .col-id {
|
|
1436
|
+
padding-left: 32px !important;
|
|
1437
|
+
}
|
|
1082
1438
|
</style>
|
|
1083
1439
|
<div class="table-container">
|
|
1084
1440
|
<table>
|
|
@@ -1105,6 +1461,19 @@ class TaskList extends HTMLElement {
|
|
|
1105
1461
|
`;
|
|
1106
1462
|
this._shadow.addEventListener("click", (e) => {
|
|
1107
1463
|
const target = e.target;
|
|
1464
|
+
const nsRow = target.closest(".namespace-row");
|
|
1465
|
+
if (nsRow) {
|
|
1466
|
+
const ns = nsRow.dataset.ns;
|
|
1467
|
+
if (ns) {
|
|
1468
|
+
if (this._expandedNamespaces.has(ns)) {
|
|
1469
|
+
this._expandedNamespaces.delete(ns);
|
|
1470
|
+
} else {
|
|
1471
|
+
this._expandedNamespaces.add(ns);
|
|
1472
|
+
}
|
|
1473
|
+
this.renderRows();
|
|
1474
|
+
}
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1108
1477
|
const btn = target.closest("button");
|
|
1109
1478
|
if (btn) {
|
|
1110
1479
|
const action = btn.dataset.action;
|
|
@@ -1119,7 +1488,7 @@ class TaskList extends HTMLElement {
|
|
|
1119
1488
|
return;
|
|
1120
1489
|
}
|
|
1121
1490
|
const tr = target.closest("tr");
|
|
1122
|
-
if (tr && !target.closest(".col-actions")) {
|
|
1491
|
+
if (tr && !target.closest(".col-actions") && !tr.classList.contains("namespace-row")) {
|
|
1123
1492
|
const id = tr.dataset.id;
|
|
1124
1493
|
this.dispatchEvent(new CustomEvent("task-select", {
|
|
1125
1494
|
detail: id,
|
|
@@ -1744,6 +2113,7 @@ class DevTools extends HTMLElement {
|
|
|
1744
2113
|
__publicField(this, "$taskDetail");
|
|
1745
2114
|
__publicField(this, "$timeline");
|
|
1746
2115
|
__publicField(this, "$trigger");
|
|
2116
|
+
__publicField(this, "$overlay");
|
|
1747
2117
|
this._shadow = this.attachShadow({ mode: "open" });
|
|
1748
2118
|
this.store = new DevToolsStore();
|
|
1749
2119
|
}
|
|
@@ -1857,6 +2227,7 @@ class DevTools extends HTMLElement {
|
|
|
1857
2227
|
this.$taskDetail = this._shadow.querySelector("hs-task-detail");
|
|
1858
2228
|
this.$timeline = this._shadow.querySelector("hs-timeline");
|
|
1859
2229
|
this.$trigger = this._shadow.querySelector("hs-floating-trigger");
|
|
2230
|
+
this.$overlay = this._shadow.querySelector(".overlay");
|
|
1860
2231
|
}
|
|
1861
2232
|
bindStore() {
|
|
1862
2233
|
try {
|
|
@@ -1872,6 +2243,7 @@ class DevTools extends HTMLElement {
|
|
|
1872
2243
|
if (isOpen) {
|
|
1873
2244
|
this.$panel.classList.add("open");
|
|
1874
2245
|
this.$trigger.style.display = "none";
|
|
2246
|
+
this.$overlay.style.display = "block";
|
|
1875
2247
|
if (pos === "right") {
|
|
1876
2248
|
this.$panel.style.right = "0";
|
|
1877
2249
|
} else {
|
|
@@ -1880,6 +2252,7 @@ class DevTools extends HTMLElement {
|
|
|
1880
2252
|
} else {
|
|
1881
2253
|
this.$panel.classList.remove("open");
|
|
1882
2254
|
this.$trigger.style.display = "block";
|
|
2255
|
+
this.$overlay.style.display = "none";
|
|
1883
2256
|
if (pos === "right") {
|
|
1884
2257
|
this.$panel.style.right = `-${size.width}px`;
|
|
1885
2258
|
} else {
|
|
@@ -2024,9 +2397,8 @@ class DevTools extends HTMLElement {
|
|
|
2024
2397
|
const text = e.detail;
|
|
2025
2398
|
this.store.setFilterText(text);
|
|
2026
2399
|
});
|
|
2027
|
-
this.addEventListener("
|
|
2028
|
-
|
|
2029
|
-
this.store.setPanelSize(size);
|
|
2400
|
+
this.$overlay.addEventListener("click", () => {
|
|
2401
|
+
this.store.toggle();
|
|
2030
2402
|
});
|
|
2031
2403
|
this.$taskList.addEventListener("task-select", (e) => {
|
|
2032
2404
|
const id = e.detail;
|
|
@@ -2078,6 +2450,16 @@ class DevTools extends HTMLElement {
|
|
|
2078
2450
|
color: var(--hs-text);
|
|
2079
2451
|
line-height: var(--hs-line-height);
|
|
2080
2452
|
}
|
|
2453
|
+
.overlay {
|
|
2454
|
+
position: fixed;
|
|
2455
|
+
top: 0;
|
|
2456
|
+
left: 0;
|
|
2457
|
+
width: 100vw;
|
|
2458
|
+
height: 100vh;
|
|
2459
|
+
background: rgba(0, 0, 0, 0.5);
|
|
2460
|
+
z-index: calc(var(--hs-z-index) - 1); /* 确保在面板之下,但在应用之上 */
|
|
2461
|
+
display: none; /* 默认隐藏 */
|
|
2462
|
+
}
|
|
2081
2463
|
.panel {
|
|
2082
2464
|
position: fixed;
|
|
2083
2465
|
background: var(--hs-bg);
|
|
@@ -2141,6 +2523,7 @@ class DevTools extends HTMLElement {
|
|
|
2141
2523
|
}
|
|
2142
2524
|
</style>
|
|
2143
2525
|
|
|
2526
|
+
<div class="overlay"></div>
|
|
2144
2527
|
<hs-floating-trigger></hs-floating-trigger>
|
|
2145
2528
|
|
|
2146
2529
|
<div class="panel dock-right">
|