hyper-scheduler 1.0.1 → 1.1.0

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.
Files changed (34) hide show
  1. package/README.md +17 -0
  2. package/dist/devtools-Bxtz0rO_.cjs +1 -0
  3. package/dist/{devtools-4xVHTaUz.js → devtools-ByJU-Gv1.js} +378 -34
  4. package/dist/index.cjs +1 -1
  5. package/dist/index.js +91 -38
  6. package/dist/index.umd.cjs +1 -1
  7. package/docs/.vitepress/cache/deps/_metadata.json +31 -0
  8. package/docs/.vitepress/cache/deps/chunk-EKBJ2FPM.js +12798 -0
  9. package/docs/.vitepress/cache/deps/chunk-EKBJ2FPM.js.map +7 -0
  10. package/docs/.vitepress/cache/deps/package.json +3 -0
  11. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4505 -0
  12. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
  13. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +9731 -0
  14. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
  15. package/docs/.vitepress/cache/deps/vue.js +347 -0
  16. package/docs/.vitepress/cache/deps/vue.js.map +7 -0
  17. package/docs/.vitepress/config.ts +56 -0
  18. package/docs/.vitepress/theme/components/DemoFrame.vue +111 -0
  19. package/docs/.vitepress/theme/custom.css +6 -0
  20. package/docs/.vitepress/theme/index.ts +10 -0
  21. package/docs/README.md +120 -0
  22. package/docs/api/devtools.md +245 -0
  23. package/docs/api/index.md +178 -0
  24. package/docs/api/scheduler.md +342 -0
  25. package/docs/api/task.md +439 -0
  26. package/docs/api/types.md +365 -0
  27. package/docs/examples/index.md +342 -0
  28. package/docs/guide/best-practices.md +436 -0
  29. package/docs/guide/core-concepts.md +363 -0
  30. package/docs/guide/getting-started.md +176 -0
  31. package/docs/index.md +33 -0
  32. package/docs/public/logo.svg +54 -0
  33. package/package.json +9 -6
  34. package/dist/devtools-DdQ1I25H.cjs +0 -1
@@ -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,203 @@ 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();
439
+ }
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
+ const rect = button.getBoundingClientRect();
462
+ localStorage.setItem(STORAGE_KEY_POS, JSON.stringify({ x: rect.left, y: rect.top }));
463
+ localStorage.setItem(STORAGE_KEY_COLLAPSED, String(this._isCollapsed));
464
+ }
465
+ applyPosition() {
466
+ const button = this._shadow.querySelector("button");
467
+ if (!button) return;
468
+ const maxX = window.innerWidth - button.offsetWidth;
469
+ const maxY = window.innerHeight - button.offsetHeight;
470
+ if (!this.style.getPropertyValue("--hs-trigger-position-set")) {
471
+ const pos = this._position;
472
+ button.style.top = pos.includes("top") ? "20px" : "auto";
473
+ button.style.bottom = pos.includes("bottom") ? "20px" : "auto";
474
+ button.style.left = pos.includes("left") ? "20px" : "auto";
475
+ button.style.right = pos.includes("right") ? "20px" : "auto";
476
+ } else {
477
+ let currentX = parseFloat(this.style.getPropertyValue("--hs-trigger-left") || "0");
478
+ let currentY = parseFloat(this.style.getPropertyValue("--hs-trigger-top") || "0");
479
+ currentX = Math.max(0, Math.min(currentX, maxX));
480
+ currentY = Math.max(0, Math.min(currentY, maxY));
481
+ this.style.setProperty("--hs-trigger-left", `${currentX}px`);
482
+ this.style.setProperty("--hs-trigger-top", `${currentY}px`);
483
+ button.style.left = `${currentX}px`;
484
+ button.style.top = `${currentY}px`;
485
+ button.style.right = "auto";
486
+ button.style.bottom = "auto";
415
487
  }
416
- if (this._shadow.querySelector("button")) {
417
- console.log("[FloatingTrigger] re-rendering with:", this._position, this._bgColor, this._textColor);
418
- this.render();
419
- this.addEventListeners();
488
+ this.updateCollapsedState();
489
+ }
490
+ updateStyles() {
491
+ const button = this._shadow.querySelector("button");
492
+ if (button) {
493
+ button.style.background = this._bgColor || "var(--hs-primary)";
494
+ button.style.color = this._textColor || "white";
495
+ if (this._bgColor) {
496
+ button.style.setProperty("--hs-trigger-bg-hover", `${this._bgColor}; filter: brightness(1.1);`);
497
+ } else {
498
+ button.style.removeProperty("--hs-trigger-bg-hover");
499
+ }
500
+ }
501
+ }
502
+ updateCollapsedState() {
503
+ const button = this._shadow.querySelector("button");
504
+ if (button) {
505
+ if (this._isCollapsed) {
506
+ button.classList.add("collapsed");
507
+ } else {
508
+ button.classList.remove("collapsed");
509
+ }
420
510
  }
421
511
  }
422
512
  addEventListeners() {
423
513
  const btn = this._shadow.querySelector("button");
424
- btn == null ? void 0 : btn.addEventListener("click", () => {
514
+ if (!btn) return;
515
+ btn.addEventListener("click", (e) => {
516
+ if (this._wasDragging) {
517
+ e.preventDefault();
518
+ e.stopPropagation();
519
+ this._wasDragging = false;
520
+ return;
521
+ }
425
522
  this.dispatchEvent(new CustomEvent("toggle", { bubbles: true, composed: true }));
426
523
  });
524
+ const collapseBtn = this._shadow.querySelector(".collapse-btn");
525
+ collapseBtn == null ? void 0 : collapseBtn.addEventListener("click", (e) => {
526
+ e.stopPropagation();
527
+ e.preventDefault();
528
+ this._isCollapsed = !this._isCollapsed;
529
+ this.updateCollapsedState();
530
+ this.saveState();
531
+ });
532
+ btn.addEventListener("dblclick", (e) => {
533
+ if (this._isCollapsed) {
534
+ e.stopPropagation();
535
+ this._isCollapsed = false;
536
+ this.updateCollapsedState();
537
+ this.saveState();
538
+ }
539
+ });
540
+ btn.addEventListener("mousedown", (e) => {
541
+ if (e.button !== 0) return;
542
+ if (e.target.closest(".collapse-btn")) return;
543
+ this._isDragging = true;
544
+ this._wasDragging = false;
545
+ this._offsetX = e.clientX - btn.getBoundingClientRect().left;
546
+ this._offsetY = e.clientY - btn.getBoundingClientRect().top;
547
+ const startX = e.clientX;
548
+ const startY = e.clientY;
549
+ let hasMoved = false;
550
+ const onMouseMove = (moveEvent) => {
551
+ if (!this._isDragging) return;
552
+ if (!hasMoved && (Math.abs(moveEvent.clientX - startX) > 2 || Math.abs(moveEvent.clientY - startY) > 2)) {
553
+ hasMoved = true;
554
+ this._wasDragging = true;
555
+ btn.style.cursor = "grabbing";
556
+ btn.style.transition = "none";
557
+ }
558
+ if (hasMoved) {
559
+ let newX = moveEvent.clientX - this._offsetX;
560
+ let newY = moveEvent.clientY - this._offsetY;
561
+ const maxX = window.innerWidth - btn.offsetWidth;
562
+ const maxY = window.innerHeight - btn.offsetHeight;
563
+ newX = Math.max(0, Math.min(newX, maxX));
564
+ newY = Math.max(0, Math.min(newY, maxY));
565
+ btn.style.left = `${newX}px`;
566
+ btn.style.top = `${newY}px`;
567
+ btn.style.right = "auto";
568
+ btn.style.bottom = "auto";
569
+ this.style.setProperty("--hs-trigger-position-set", "true");
570
+ this.style.setProperty("--hs-trigger-left", `${newX}px`);
571
+ this.style.setProperty("--hs-trigger-top", `${newY}px`);
572
+ }
573
+ };
574
+ const onMouseUp = () => {
575
+ if (!this._isDragging) return;
576
+ this._isDragging = false;
577
+ btn.style.cursor = "pointer";
578
+ btn.style.transition = "";
579
+ if (hasMoved) {
580
+ this.saveState();
581
+ const preventClick = (e2) => {
582
+ e2.stopPropagation();
583
+ e2.preventDefault();
584
+ };
585
+ window.addEventListener("click", preventClick, { capture: true, once: true });
586
+ }
587
+ window.removeEventListener("mousemove", onMouseMove);
588
+ window.removeEventListener("mouseup", onMouseUp);
589
+ };
590
+ window.addEventListener("mousemove", onMouseMove);
591
+ window.addEventListener("mouseup", onMouseUp);
592
+ });
427
593
  }
428
594
  render() {
429
- const pos = this._position;
430
595
  const posStyles = `
431
- top: ${pos.includes("top") ? "20px" : "auto"};
432
- bottom: ${pos.includes("bottom") ? "20px" : "auto"};
433
- left: ${pos.includes("left") ? "20px" : "auto"};
434
- right: ${pos.includes("right") ? "20px" : "auto"};
596
+ top: var(--hs-trigger-top, ${this._position.includes("top") ? "20px" : "auto"});
597
+ bottom: var(--hs-trigger-bottom, ${this._position.includes("bottom") ? "20px" : "auto"});
598
+ left: var(--hs-trigger-left, ${this._position.includes("left") ? "20px" : "auto"});
599
+ right: var(--hs-trigger-right, ${this._position.includes("right") ? "20px" : "auto"});
600
+ transform: ${this._isCollapsed ? "translateX(calc(100% - 12px))" : "translateX(0)"};
435
601
  `;
436
602
  const bgStyle = this._bgColor ? `background: ${this._bgColor};` : "";
437
603
  const colorStyle = this._textColor ? `color: ${this._textColor};` : "";
@@ -443,33 +609,92 @@ class FloatingTrigger extends HTMLElement {
443
609
  ${posStyles}
444
610
  width: 48px;
445
611
  height: 48px;
446
- border-radius: 50%;
612
+ border-radius: 12px; /* 更现代的圆角 */
447
613
  background: var(--hs-primary);
448
614
  color: white;
449
615
  ${bgStyle}
450
616
  ${colorStyle}
451
617
  border: none;
452
- box-shadow: var(--hs-shadow);
618
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15); /* 更柔和的阴影 */
453
619
  cursor: pointer;
454
620
  display: flex;
455
621
  align-items: center;
456
622
  justify-content: center;
457
623
  font-family: var(--hs-font-family);
458
624
  z-index: var(--hs-z-index);
459
- transition: transform 0.2s;
625
+ transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), background 0.2s, box-shadow 0.2s;
626
+ overflow: visible; /* 允许子元素(收起按钮)溢出或显示 */
460
627
  }
461
628
  button:hover {
462
629
  ${this._bgColor ? `background: ${this._bgColor}; filter: brightness(1.1);` : "background: var(--hs-primary-hover);"}
463
- transform: scale(1.1);
630
+ transform: scale(1.05);
631
+ box-shadow: 0 6px 16px rgba(0,0,0,0.2);
464
632
  }
465
633
  button:active {
466
634
  transform: scale(0.95);
467
635
  }
636
+
637
+ /* 显式收起按钮 */
638
+ .collapse-btn {
639
+ position: absolute;
640
+ top: 0;
641
+ right: 0;
642
+ width: 20px;
643
+ height: 20px;
644
+ background: rgba(0,0,0,0.1);
645
+ border-bottom-left-radius: 8px;
646
+ display: flex;
647
+ align-items: center;
648
+ justify-content: center;
649
+ opacity: 1; /* 默认可见 */
650
+ transition: opacity 0.2s, background 0.2s;
651
+ color: white;
652
+ font-weight: bold;
653
+ }
654
+ .collapse-btn::before {
655
+ content: '—'; /* 最小化图标 */
656
+ font-size: 12px;
657
+ color: currentColor;
658
+ }
659
+ button:hover .collapse-btn {
660
+ opacity: 1;
661
+ }
662
+ .collapse-btn:hover {
663
+ background: rgba(0,0,0,0.3);
664
+ }
665
+
666
+ /* 收起状态样式 */
667
+ button.collapsed {
668
+ width: 36px;
669
+ height: 36px;
670
+ border-radius: 18px 0 0 18px;
671
+ transform: translateX(calc(100% - 12px));
672
+ opacity: 0.8;
673
+ }
674
+ button.collapsed:hover {
675
+ transform: translateX(calc(100% - 24px)); /* hover 时稍微伸出一点 */
676
+ opacity: 1;
677
+ }
678
+ button.collapsed .icon {
679
+ display: none;
680
+ }
681
+ button.collapsed .collapse-btn {
682
+ right: auto;
683
+ left: 0;
684
+ border-radius: 0 12px 12px 0;
685
+ }
686
+ button.collapsed .collapse-btn::before {
687
+ content: '›'; /* 展开图标 */
688
+ }
468
689
  </style>
469
690
  <button title="Toggle Hyper Scheduler DevTools">
470
- ${ICONS.chart}
691
+ <div class="collapse-btn" title="Minimize"></div>
692
+ <span class="icon">${ICONS.chart}</span>
471
693
  </button>
472
694
  `;
695
+ this.applyPosition();
696
+ this.updateCollapsedState();
697
+ this.updateStyles();
473
698
  }
474
699
  }
475
700
  customElements.define("hs-floating-trigger", FloatingTrigger);
@@ -504,7 +729,7 @@ class TaskHeader extends HTMLElement {
504
729
  this._fps = Math.round(val);
505
730
  if (this.$fps) {
506
731
  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")})`;
732
+ this.$fps.innerHTML = `⚡ ${t("stats.fps")}: <span style="color:${color}; font-family:var(--hs-font-monospaced-num);">${this._fps}</span> (${t("stats.mainThread")})`;
508
733
  }
509
734
  }
510
735
  set stats(val) {
@@ -766,6 +991,7 @@ class TaskList extends HTMLElement {
766
991
  __publicField(this, "_shadow");
767
992
  __publicField(this, "_tasks", []);
768
993
  __publicField(this, "_lastExecutionTimes", /* @__PURE__ */ new Map());
994
+ __publicField(this, "_expandedNamespaces", /* @__PURE__ */ new Set(["default"]));
769
995
  this._shadow = this.attachShadow({ mode: "open" });
770
996
  }
771
997
  connectedCallback() {
@@ -782,6 +1008,17 @@ class TaskList extends HTMLElement {
782
1008
  this._tasks = newTasks;
783
1009
  this.renderRows();
784
1010
  }
1011
+ groupTasksByNamespace(tasks) {
1012
+ const groups = /* @__PURE__ */ new Map();
1013
+ tasks.forEach((task) => {
1014
+ const ns = task.namespace || "default";
1015
+ if (!groups.has(ns)) {
1016
+ groups.set(ns, []);
1017
+ }
1018
+ groups.get(ns).push(task);
1019
+ });
1020
+ return groups;
1021
+ }
785
1022
  filter(text, map) {
786
1023
  const all = Array.from(map.values());
787
1024
  if (!text) {
@@ -789,7 +1026,7 @@ class TaskList extends HTMLElement {
789
1026
  } else {
790
1027
  const lower = text.toLowerCase();
791
1028
  this._tasks = all.filter(
792
- (t2) => t2.id.toLowerCase().includes(lower) || t2.tags.some((tag) => tag.toLowerCase().includes(lower))
1029
+ (t2) => t2.id.toLowerCase().includes(lower) || t2.tags.some((tag) => tag.toLowerCase().includes(lower)) || t2.namespace && t2.namespace.toLowerCase().includes(lower)
793
1030
  );
794
1031
  }
795
1032
  this.renderRows();
@@ -862,18 +1099,21 @@ class TaskList extends HTMLElement {
862
1099
  }
863
1100
  return `<span class="driver-badge main" title="${t("list.driverMain")}">M</span>`;
864
1101
  }
865
- renderRows() {
866
- const tbody = this._shadow.querySelector("tbody");
867
- if (!tbody) return;
868
- tbody.innerHTML = this._tasks.map((task, index) => {
869
- const lastExec = this._lastExecutionTimes.get(task.id);
870
- const isRecentlyExecuted = lastExec && Date.now() - lastExec < 1e3;
871
- const rowClass = isRecentlyExecuted ? "recently-executed" : "";
872
- return `
873
- <tr data-id="${task.id}" class="${rowClass}">
874
- <td class="col-num">${index + 1}</td>
1102
+ renderTaskRow(task, index, isNested = false) {
1103
+ const lastExec = this._lastExecutionTimes.get(task.id);
1104
+ const isRecentlyExecuted = lastExec && Date.now() - lastExec < 1e3;
1105
+ const rowClass = isRecentlyExecuted ? "recently-executed" : "";
1106
+ const nestedClass = isNested ? "nested-task" : "";
1107
+ return `
1108
+ <tr data-id="${task.id}" class="${rowClass} ${nestedClass}">
1109
+ <td class="col-num">
1110
+ ${isNested ? "" : index + 1}
1111
+ </td>
875
1112
  <td class="col-id">
876
- <div class="task-id">${task.id}</div>
1113
+ <div class="task-id">
1114
+ ${task.id}
1115
+ ${task.namespace && task.namespace !== "default" && !isNested ? `<span class="namespace-badge" title="Namespace">${task.namespace}</span>` : ""}
1116
+ </div>
877
1117
  <div class="tags">
878
1118
  ${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
1119
  </div>
@@ -892,7 +1132,38 @@ class TaskList extends HTMLElement {
892
1132
  </td>
893
1133
  </tr>
894
1134
  `;
895
- }).join("");
1135
+ }
1136
+ renderRows() {
1137
+ const tbody = this._shadow.querySelector("tbody");
1138
+ if (!tbody) return;
1139
+ const groups = this.groupTasksByNamespace(this._tasks);
1140
+ let html = "";
1141
+ let globalIndex = 0;
1142
+ const defaultTasks = groups.get("default");
1143
+ if (defaultTasks && defaultTasks.length > 0) {
1144
+ html += defaultTasks.map((task) => this.renderTaskRow(task, ++globalIndex, false)).join("");
1145
+ }
1146
+ groups.delete("default");
1147
+ const sortedNamespaces = Array.from(groups.keys()).sort((a, b) => a.localeCompare(b));
1148
+ sortedNamespaces.forEach((ns) => {
1149
+ const tasks = groups.get(ns);
1150
+ const isExpanded = this._expandedNamespaces.has(ns);
1151
+ const icon = isExpanded ? "▼" : "▶";
1152
+ html += `
1153
+ <tr class="namespace-row ${isExpanded ? "ns-expanded" : ""}" data-ns="${ns}">
1154
+ <td colspan="8">
1155
+ <span class="ns-toggle">${icon}</span>
1156
+ <span class="ns-icon">📂</span>
1157
+ <span class="ns-name">${ns}</span>
1158
+ <span class="ns-count">(${tasks.length})</span>
1159
+ </td>
1160
+ </tr>
1161
+ `;
1162
+ if (isExpanded) {
1163
+ html += tasks.map((task) => this.renderTaskRow(task, ++globalIndex, true)).join("");
1164
+ }
1165
+ });
1166
+ tbody.innerHTML = html;
896
1167
  }
897
1168
  render() {
898
1169
  this._shadow.innerHTML = `
@@ -974,6 +1245,18 @@ class TaskList extends HTMLElement {
974
1245
  .task-id {
975
1246
  font-weight: 600;
976
1247
  }
1248
+ .namespace-badge {
1249
+ display: inline-block;
1250
+ background: var(--hs-bg-secondary);
1251
+ color: var(--hs-text-secondary);
1252
+ border: 1px solid var(--hs-border);
1253
+ border-radius: 4px;
1254
+ padding: 0 4px;
1255
+ font-size: 9px;
1256
+ margin-left: 6px;
1257
+ font-family: monospace;
1258
+ vertical-align: middle;
1259
+ }
977
1260
  .tags {
978
1261
  display: flex;
979
1262
  gap: 4px;
@@ -1079,6 +1362,40 @@ class TaskList extends HTMLElement {
1079
1362
  color: var(--hs-text-secondary);
1080
1363
  border-color: var(--hs-border);
1081
1364
  }
1365
+ /* Namespace Styles */
1366
+ .namespace-row {
1367
+ background: var(--hs-bg-secondary);
1368
+ cursor: pointer;
1369
+ font-weight: 600;
1370
+ user-select: none;
1371
+ }
1372
+ .namespace-row:hover {
1373
+ background: var(--hs-border);
1374
+ }
1375
+ .namespace-row td {
1376
+ padding: 8px 12px;
1377
+ border-bottom: 1px solid var(--hs-border);
1378
+ }
1379
+ .ns-toggle {
1380
+ display: inline-block;
1381
+ width: 20px;
1382
+ text-align: center;
1383
+ font-size: 10px;
1384
+ color: var(--hs-text-secondary);
1385
+ transition: transform 0.2s;
1386
+ }
1387
+ .ns-icon {
1388
+ margin-right: 4px;
1389
+ }
1390
+ .ns-count {
1391
+ font-weight: normal;
1392
+ color: var(--hs-text-secondary);
1393
+ font-size: 11px;
1394
+ margin-left: 4px;
1395
+ }
1396
+ .nested-task .col-id {
1397
+ padding-left: 32px !important;
1398
+ }
1082
1399
  </style>
1083
1400
  <div class="table-container">
1084
1401
  <table>
@@ -1105,6 +1422,19 @@ class TaskList extends HTMLElement {
1105
1422
  `;
1106
1423
  this._shadow.addEventListener("click", (e) => {
1107
1424
  const target = e.target;
1425
+ const nsRow = target.closest(".namespace-row");
1426
+ if (nsRow) {
1427
+ const ns = nsRow.dataset.ns;
1428
+ if (ns) {
1429
+ if (this._expandedNamespaces.has(ns)) {
1430
+ this._expandedNamespaces.delete(ns);
1431
+ } else {
1432
+ this._expandedNamespaces.add(ns);
1433
+ }
1434
+ this.renderRows();
1435
+ }
1436
+ return;
1437
+ }
1108
1438
  const btn = target.closest("button");
1109
1439
  if (btn) {
1110
1440
  const action = btn.dataset.action;
@@ -1119,7 +1449,7 @@ class TaskList extends HTMLElement {
1119
1449
  return;
1120
1450
  }
1121
1451
  const tr = target.closest("tr");
1122
- if (tr && !target.closest(".col-actions")) {
1452
+ if (tr && !target.closest(".col-actions") && !tr.classList.contains("namespace-row")) {
1123
1453
  const id = tr.dataset.id;
1124
1454
  this.dispatchEvent(new CustomEvent("task-select", {
1125
1455
  detail: id,
@@ -1744,6 +2074,7 @@ class DevTools extends HTMLElement {
1744
2074
  __publicField(this, "$taskDetail");
1745
2075
  __publicField(this, "$timeline");
1746
2076
  __publicField(this, "$trigger");
2077
+ __publicField(this, "$overlay");
1747
2078
  this._shadow = this.attachShadow({ mode: "open" });
1748
2079
  this.store = new DevToolsStore();
1749
2080
  }
@@ -1857,6 +2188,7 @@ class DevTools extends HTMLElement {
1857
2188
  this.$taskDetail = this._shadow.querySelector("hs-task-detail");
1858
2189
  this.$timeline = this._shadow.querySelector("hs-timeline");
1859
2190
  this.$trigger = this._shadow.querySelector("hs-floating-trigger");
2191
+ this.$overlay = this._shadow.querySelector(".overlay");
1860
2192
  }
1861
2193
  bindStore() {
1862
2194
  try {
@@ -1872,6 +2204,7 @@ class DevTools extends HTMLElement {
1872
2204
  if (isOpen) {
1873
2205
  this.$panel.classList.add("open");
1874
2206
  this.$trigger.style.display = "none";
2207
+ this.$overlay.style.display = "block";
1875
2208
  if (pos === "right") {
1876
2209
  this.$panel.style.right = "0";
1877
2210
  } else {
@@ -1880,6 +2213,7 @@ class DevTools extends HTMLElement {
1880
2213
  } else {
1881
2214
  this.$panel.classList.remove("open");
1882
2215
  this.$trigger.style.display = "block";
2216
+ this.$overlay.style.display = "none";
1883
2217
  if (pos === "right") {
1884
2218
  this.$panel.style.right = `-${size.width}px`;
1885
2219
  } else {
@@ -2024,9 +2358,8 @@ class DevTools extends HTMLElement {
2024
2358
  const text = e.detail;
2025
2359
  this.store.setFilterText(text);
2026
2360
  });
2027
- this.addEventListener("resize", (e) => {
2028
- const size = e.detail;
2029
- this.store.setPanelSize(size);
2361
+ this.$overlay.addEventListener("click", () => {
2362
+ this.store.toggle();
2030
2363
  });
2031
2364
  this.$taskList.addEventListener("task-select", (e) => {
2032
2365
  const id = e.detail;
@@ -2078,6 +2411,16 @@ class DevTools extends HTMLElement {
2078
2411
  color: var(--hs-text);
2079
2412
  line-height: var(--hs-line-height);
2080
2413
  }
2414
+ .overlay {
2415
+ position: fixed;
2416
+ top: 0;
2417
+ left: 0;
2418
+ width: 100vw;
2419
+ height: 100vh;
2420
+ background: rgba(0, 0, 0, 0.5);
2421
+ z-index: calc(var(--hs-z-index) - 1); /* 确保在面板之下,但在应用之上 */
2422
+ display: none; /* 默认隐藏 */
2423
+ }
2081
2424
  .panel {
2082
2425
  position: fixed;
2083
2426
  background: var(--hs-bg);
@@ -2141,6 +2484,7 @@ class DevTools extends HTMLElement {
2141
2484
  }
2142
2485
  </style>
2143
2486
 
2487
+ <div class="overlay"></div>
2144
2488
  <hs-floating-trigger></hs-floating-trigger>
2145
2489
 
2146
2490
  <div class="panel dock-right">