hyper-scheduler 1.0.0 → 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.
- package/README.md +17 -0
- package/dist/devtools-Bxtz0rO_.cjs +1 -0
- package/dist/devtools-ByJU-Gv1.js +2505 -0
- package/dist/index.cjs +1 -0
- package/dist/index.js +1048 -0
- package/dist/index.umd.cjs +1 -0
- package/docs/.vitepress/cache/deps/_metadata.json +31 -0
- package/docs/.vitepress/cache/deps/chunk-EKBJ2FPM.js +12798 -0
- package/docs/.vitepress/cache/deps/chunk-EKBJ2FPM.js.map +7 -0
- package/docs/.vitepress/cache/deps/package.json +3 -0
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4505 -0
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +9731 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
- package/docs/.vitepress/cache/deps/vue.js +347 -0
- package/docs/.vitepress/cache/deps/vue.js.map +7 -0
- package/docs/.vitepress/config.ts +4 -0
- package/docs/.vitepress/theme/components/DemoFrame.vue +111 -0
- package/docs/.vitepress/theme/custom.css +6 -0
- package/docs/.vitepress/theme/index.ts +10 -0
- package/docs/api/devtools.md +13 -0
- package/docs/api/scheduler.md +28 -8
- package/docs/examples/index.md +55 -8
- package/docs/guide/getting-started.md +38 -0
- package/package.json +13 -4
- package/.editorconfig +0 -21
- package/.eslintrc.cjs +0 -26
- package/GEMINI.md +0 -1
- package/examples/browser/index.html +0 -354
- package/examples/node/simple.js +0 -36
- package/examples/react-demo/index.html +0 -12
- package/examples/react-demo/package.json +0 -23
- package/examples/react-demo/src/App.css +0 -212
- package/examples/react-demo/src/App.jsx +0 -160
- package/examples/react-demo/src/main.jsx +0 -9
- package/examples/react-demo/vite.config.ts +0 -12
- package/examples/react-demo/yarn.lock +0 -752
- package/examples/vue-demo/index.html +0 -12
- package/examples/vue-demo/package.json +0 -21
- package/examples/vue-demo/src/App.vue +0 -373
- package/examples/vue-demo/src/main.ts +0 -4
- package/examples/vue-demo/vite.config.ts +0 -13
- package/examples/vue-demo/yarn.lock +0 -375
- package/src/constants.ts +0 -18
- package/src/core/retry-strategy.ts +0 -28
- package/src/core/scheduler.ts +0 -601
- package/src/core/task-registry.ts +0 -58
- package/src/index.ts +0 -74
- package/src/platform/browser/browser-timer.ts +0 -66
- package/src/platform/browser/main-thread-timer.ts +0 -16
- package/src/platform/browser/worker.ts +0 -31
- package/src/platform/node/debug-cli.ts +0 -19
- package/src/platform/node/node-timer.ts +0 -15
- package/src/platform/timer-strategy.ts +0 -19
- package/src/plugins/dev-tools.ts +0 -101
- package/src/types.ts +0 -115
- package/src/ui/components/devtools.ts +0 -525
- package/src/ui/components/floating-trigger.ts +0 -102
- package/src/ui/components/icons.ts +0 -16
- package/src/ui/components/resizer.ts +0 -129
- package/src/ui/components/task-detail.ts +0 -228
- package/src/ui/components/task-header.ts +0 -319
- package/src/ui/components/task-list.ts +0 -416
- package/src/ui/components/timeline.ts +0 -364
- package/src/ui/debug-panel.ts +0 -56
- package/src/ui/i18n/en.ts +0 -76
- package/src/ui/i18n/index.ts +0 -42
- package/src/ui/i18n/zh.ts +0 -76
- package/src/ui/store/dev-tools-store.ts +0 -191
- package/src/ui/styles/theme.css.ts +0 -56
- package/src/ui/styles.ts +0 -43
- package/src/utils/cron-lite.ts +0 -221
- package/src/utils/cron.ts +0 -20
- package/src/utils/id.ts +0 -10
- package/src/utils/schedule.ts +0 -93
- package/src/vite-env.d.ts +0 -1
- package/stats.html +0 -4949
- package/tests/integration/Debug.test.ts +0 -58
- package/tests/unit/Plugin.test.ts +0 -16
- package/tests/unit/RetryStrategy.test.ts +0 -21
- package/tests/unit/Scheduler.test.ts +0 -38
- package/tests/unit/schedule.test.ts +0 -70
- package/tests/unit/ui/DevToolsStore.test.ts +0 -67
- package/tsconfig.json +0 -28
- package/vite.config.ts +0 -51
- package/vitest.config.ts +0 -24
package/README.md
CHANGED
|
@@ -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.
|
|
@@ -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),this._shadow=this.attachShadow({mode:"open"})}static get observedAttributes(){return["position","bg-color","text-color"]}connectedCallback(){this.loadState(),this.render(),this.addEventListeners(),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.applyPosition()):"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.style.setProperty("--hs-trigger-left",`${e}px`),this.style.setProperty("--hs-trigger-top",`${s}px`),this.style.setProperty("--hs-trigger-position-set","true")}"true"===localStorage.getItem(p)&&(this._isCollapsed=!0)}catch(t){console.warn("[FloatingTrigger] Failed to load state:",t)}}saveState(){const t=this._shadow.querySelector("button");if(!t)return;const e=t.getBoundingClientRect();localStorage.setItem(c,JSON.stringify({x:e.left,y:e.top})),localStorage.setItem(p,String(this._isCollapsed))}applyPosition(){const t=this._shadow.querySelector("button");if(!t)return;const e=window.innerWidth-t.offsetWidth,s=window.innerHeight-t.offsetHeight;if(this.style.getPropertyValue("--hs-trigger-position-set")){let n=parseFloat(this.style.getPropertyValue("--hs-trigger-left")||"0"),i=parseFloat(this.style.getPropertyValue("--hs-trigger-top")||"0");n=Math.max(0,Math.min(n,e)),i=Math.max(0,Math.min(i,s)),this.style.setProperty("--hs-trigger-left",`${n}px`),this.style.setProperty("--hs-trigger-top",`${i}px`),t.style.left=`${n}px`,t.style.top=`${i}px`,t.style.right="auto",t.style.bottom="auto"}else{const e=this._position;t.style.top=e.includes("top")?"20px":"auto",t.style.bottom=e.includes("bottom")?"20px":"auto",t.style.left=e.includes("left")?"20px":"auto",t.style.right=e.includes("right")?"20px":"auto"}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.updateCollapsedState(),this.saveState()}),t.addEventListener("dblclick",t=>{this._isCollapsed&&(t.stopPropagation(),this._isCollapsed=!1,this.updateCollapsedState(),this.saveState())}),t.addEventListener("mousedown",e=>{if(0!==e.button)return;if(e.target.closest(".collapse-btn"))return;this._isDragging=!0,this._wasDragging=!1,this._offsetX=e.clientX-t.getBoundingClientRect().left,this._offsetY=e.clientY-t.getBoundingClientRect().top;const s=e.clientX,n=e.clientY;let i=!1;const o=e=>{if(this._isDragging&&(!i&&(Math.abs(e.clientX-s)>2||Math.abs(e.clientY-n)>2)&&(i=!0,this._wasDragging=!0,t.style.cursor="grabbing",t.style.transition="none"),i)){let s=e.clientX-this._offsetX,n=e.clientY-this._offsetY;const i=window.innerWidth-t.offsetWidth,o=window.innerHeight-t.offsetHeight;s=Math.max(0,Math.min(s,i)),n=Math.max(0,Math.min(n,o)),t.style.left=`${s}px`,t.style.top=`${n}px`,t.style.right="auto",t.style.bottom="auto",this.style.setProperty("--hs-trigger-position-set","true"),this.style.setProperty("--hs-trigger-left",`${s}px`),this.style.setProperty("--hs-trigger-top",`${n}px`)}},a=()=>{if(this._isDragging){if(this._isDragging=!1,t.style.cursor="pointer",t.style.transition="",i){this.saveState();const t=t=>{t.stopPropagation(),t.preventDefault()};window.addEventListener("click",t,{capture:!0,once:!0})}window.removeEventListener("mousemove",o),window.removeEventListener("mouseup",a)}};window.addEventListener("mousemove",o),window.addEventListener("mouseup",a)})}render(){const t=`\n top: var(--hs-trigger-top, ${this._position.includes("top")?"20px":"auto"});\n bottom: var(--hs-trigger-bottom, ${this._position.includes("bottom")?"20px":"auto"});\n left: var(--hs-trigger-left, ${this._position.includes("left")?"20px":"auto"});\n right: var(--hs-trigger-right, ${this._position.includes("right")?"20px":"auto"});\n transform: ${this._isCollapsed?"translateX(calc(100% - 12px))":"translateX(0)"};\n `,e=this._bgColor?`background: ${this._bgColor};`:"",s=this._textColor?`color: ${this._textColor};`:"";this._shadow.innerHTML=`\n <style>\n ${l}\n button {\n position: fixed;\n ${t}\n width: 48px;\n height: 48px;\n border-radius: 12px; /* 更现代的圆角 */\n background: var(--hs-primary);\n color: white;\n ${e}\n ${s}\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 transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), background 0.2s, box-shadow 0.2s;\n overflow: visible; /* 允许子元素(收起按钮)溢出或显示 */\n }\n button:hover {\n ${this._bgColor?`background: ${this._bgColor}; filter: brightness(1.1);`:"background: var(--hs-primary-hover);"}\n transform: scale(1.05);\n box-shadow: 0 6px 16px rgba(0,0,0,0.2);\n }\n button:active {\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-bottom-left-radius: 8px;\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 }\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: 36px;\n height: 36px;\n border-radius: 18px 0 0 18px;\n transform: translateX(calc(100% - 12px));\n opacity: 0.8;\n }\n button.collapsed:hover {\n transform: translateX(calc(100% - 24px)); /* hover 时稍微伸出一点 */\n opacity: 1;\n }\n button.collapsed .icon {\n display: none;\n }\n button.collapsed .collapse-btn {\n right: auto;\n left: 0;\n border-radius: 0 12px 12px 0;\n }\n button.collapsed .collapse-btn::before {\n content: '›'; /* 展开图标 */\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 `,this.applyPosition(),this.updateCollapsedState(),this.updateStyles()}}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,"_tasks",[]),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._tasks.find(e=>e.id===t.id);e&&t.executionCount>e.executionCount&&this._lastExecutionTimes.set(t.id,Date.now())}),this._tasks=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){const s=Array.from(e.values());if(t){const e=t.toLowerCase();this._tasks=s.filter(t=>t.id.toLowerCase().includes(e)||t.tags.some(t=>t.toLowerCase().includes(e))||t.namespace&&t.namespace.toLowerCase().includes(e))}else this._tasks=s;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 ${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._tasks);let s="",n=0;const i=e.get("default");i&&i.length>0&&(s+=i.map(t=>this.renderTaskRow(t,++n,!1)).join("")),e.delete("default");Array.from(e.keys()).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(""))}),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)}})}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;
|