返回 DeepSeek-Reasonix
heartbeat.css
根目录 / desktop / frontend / src / custom / features / heartbeat / heartbeat.css
1 /* heartbeat 模块 — 定时任务配置面板;选择器以 .heartbeat- 为前缀 */
2
3 /* 面板分栏背景:浅色主题保持历史观感 #fafafa(接近主背景的近白),
4 暗色主题(data-theme=dark 或 OS 深色)跟随主背景 var(--bg),
5 避免 #fafafa 硬编码在暗色下刺眼。 */
6 :root[data-theme="light"],
7 :root:not([data-theme]) {
8 --heartbeat-split-bg: #fafafa;
9 }
10 :root[data-theme="dark"] {
11 --heartbeat-split-bg: var(--bg);
12 }
13 @media (prefers-color-scheme: dark) {
14 :root:not([data-theme]) {
15 --heartbeat-split-bg: var(--bg);
16 }
17 }
18
19 /* ── page(页面式容器,替换原 backdrop/modal 弹窗)────────────── */
20
21 .heartbeat-page {
22 display: flex;
23 flex-direction: column;
24 height: 100%;
25 min-width: 0;
26 overflow: hidden;
27 background: var(--bg);
28 position: relative;
29 container: heartbeat-page / inline-size;
30 }
31
32 /* ── split layout ─────────────────────────────────────────────── */
33
34 .heartbeat-split {
35 display: flex;
36 flex: 1;
37 min-height: 0;
38 overflow: hidden;
39 position: relative;
40 /* 面板分栏背景:浅色 #fafafa、暗色 var(--bg),见顶部变量定义 */
41 background: var(--heartbeat-split-bg);
42 }
43
44 /* 无详情时:页面顶部一条全宽透明拖拽条(可拖窗口),无独立背景色块 */
45 .heartbeat-drag-strip {
46 position: absolute;
47 top: 0;
48 left: 0;
49 right: 0;
50 height: 32px;
51 z-index: 3;
52 --reasonix-draggable: drag;
53 user-select: none;
54 }
55
56 /* 列表/详情内容区保持可交互,不参与窗口拖拽 */
57 .heartbeat-split__left,
58 .heartbeat-split__right {
59 --reasonix-draggable: no-drag;
60 }
61
62 /* 自定义滚动条可拖拽滑块不参与窗口拖拽 */
63 .heartbeat-scrollbar,
64 .heartbeat-scrollbar__thumb {
65 --reasonix-draggable: no-drag;
66 }
67
68 /* 分割线本身必须 no-drag——否则继承 .heartbeat-split 的 drag,
69 鼠标按下会被桌面壳当成拖窗口,React 收不到 mousedown 无法拖动分栏 */
70 .heartbeat-split__divider {
71 --reasonix-draggable: no-drag;
72 }
73
74 .heartbeat-split__left {
75 display: flex;
76 flex-direction: column;
77 flex: 0 0 auto;
78 min-width: 220px;
79 padding: 12px 2px 0 4px;
80 overflow: hidden;
81 /* 滚动条绝对定位参照:贴在左栏右缘(而非居中的 list-wrap 内容区) */
82 position: relative;
83 }
84
85 /* 未打开详情时:列表内容区最多 700px 且在页面中居中(背景仍全宽) */
86 .heartbeat-split__left--full {
87 max-width: 700px;
88 margin: 0 auto;
89 }
90
91 .heartbeat-split__left .heartbeat-toolbar {
92 flex-shrink: 0;
93 padding: 0 4px 8px 16px;
94 }
95
96 /* 无详情时(列表全宽):标签组左对齐与标题一致,容器本身无背景色 */
97 .heartbeat-split__left--full .heartbeat-status-tabs {
98 padding: 0;
99 }
100
101 /* 无详情时页面头部:大间距 + 大标题 + 副标题(ChatGPT 自动化页风格) */
102 .heartbeat-hero {
103 flex-shrink: 0;
104 padding: 36px 14px 18px 16px;
105 }
106
107 .heartbeat-hero__title {
108 margin: 0 0 6px;
109 font-size: 24px;
110 font-weight: 700;
111 color: var(--fg);
112 }
113
114 .heartbeat-hero__subtitle {
115 margin: 0;
116 font-size: 13px;
117 color: var(--fg-faint);
118 }
119
120 .heartbeat-list-search {
121 display: flex;
122 align-items: center;
123 gap: 6px;
124 flex-shrink: 0;
125 margin: 6px 12px 2px;
126 padding: 5px 12px;
127 border: 1px solid var(--border);
128 border-radius: 999px;
129 background: transparent;
130 color: var(--fg-faint);
131 }
132
133 /* 聚焦态不改变边框/背景,保持简洁 */
134 .heartbeat-list-search:focus-within {
135 color: var(--fg);
136 }
137
138 .heartbeat-list-search__icon,
139 .heartbeat-list-search__clear {
140 display: flex;
141 align-items: center;
142 flex-shrink: 0;
143 }
144
145 .heartbeat-list-search__input {
146 flex: 1;
147 min-width: 0;
148 height: 20px;
149 border: none;
150 background: transparent;
151 outline: none;
152 font-size: 12px;
153 color: var(--fg);
154 }
155
156 .heartbeat-list-search__input::placeholder {
157 color: var(--fg-faint);
158 }
159
160 .heartbeat-list-search__clear {
161 border: none;
162 background: transparent;
163 color: var(--fg-faint);
164 cursor: pointer;
165 padding: 0;
166 }
167
168 .heartbeat-list-search__clear:hover {
169 color: var(--fg);
170 }
171
172 .heartbeat-split__list-wrap {
173 position: relative;
174 flex: 1;
175 min-height: 0;
176 display: flex;
177 flex-direction: column;
178 /* 详情打开时左栏较宽:列表内容限宽居中(无详情时 left--full 已限宽,此值不叠加) */
179 width: 100%;
180 max-width: 700px;
181 margin: 0 auto;
182 }
183
184 .heartbeat-split__list {
185 flex: 1;
186 overflow-y: auto;
187 min-height: 0;
188 /* 让出右侧滚动条区域(选中背景/操作按钮不被滚动条遮挡) */
189 padding-right: 8px;
190 /* 隐藏系统滚动条(轨道槽、边线全部不显示) */
191 scrollbar-width: none;
192 }
193
194 .heartbeat-split__list::-webkit-scrollbar {
195 display: none;
196 }
197
198 /* 自定义滚动条:定位在滚动容器外层(不随内容滚动),只显示可拖拽滑块 */
199 .heartbeat-scrollbar {
200 position: absolute;
201 right: 1px;
202 /* 顶部与搜索输入框齐平:left padding-top 12px + toolbar(tab 28px + padding 8px) ≈ 48px,
203 避免滚动条覆盖顶部的状态 tab;无详情(edge)模式由 --edge 类独立处理 */
204 top: 48px;
205 bottom: 0;
206 width: 6px;
207 pointer-events: none;
208 z-index: 2;
209 /* 滚动时显示,停止滚动 1.2s 后自动淡出。
210 淡出时同时关闭交互:opacity:0 的元素仍会捕获鼠标(thumb 是 pointer-events:auto),
211 若不清除命中会挡住左栏右缘的点击;用 visibility 彻底移除命中。 */
212 opacity: 0;
213 visibility: hidden;
214 transition: opacity 0.25s ease, visibility 0.25s;
215 }
216
217 .heartbeat-scrollbar--visible {
218 opacity: 1;
219 visibility: visible;
220 }
221
222 /* 无详情时(列表 700px 居中):滚动条移到窗口最右缘 */
223 .heartbeat-scrollbar--edge {
224 right: calc(1px - max(0px, (100vw - 700px) / 2));
225 }
226
227 .heartbeat-scrollbar__thumb {
228 position: absolute;
229 left: 0;
230 width: 100%;
231 border-radius: 3px;
232 /* 淡色滚动条:fg-faint 的 35% 不透明度(用户要求 30-40%) */
233 background: color-mix(in srgb, var(--fg-faint) 35%, transparent);
234 pointer-events: auto;
235 cursor: pointer;
236 transition: background 0.12s;
237 display: none;
238 }
239
240 .heartbeat-scrollbar__thumb:hover {
241 /* hover 略加深,保持交互反馈但不突兀 */
242 background: color-mix(in srgb, var(--fg-faint) 55%, transparent);
243 }
244
245 .heartbeat-split__divider {
246 width: 0px;
247 flex-shrink: 0;
248 cursor: col-resize;
249 position: relative;
250 background: transparent;
251 z-index: 5;
252 }
253
254 /* 视觉 1px 细竖线(与页面整体分割风格一致),紧贴边缘 */
255 .heartbeat-split__divider::before {
256 content: "";
257 position: absolute;
258 top: 0;
259 bottom: 0;
260 left: 0;
261 width: 1px;
262 background: var(--border);
263 }
264
265 /* 5px 可拖拽热区:透明,以视觉线为中心向两侧各扩 2.5px,
266 width:0 的元素本身无法命中鼠标,热区由伪元素承载 */
267 .heartbeat-split__divider::after {
268 content: "";
269 position: absolute;
270 top: 0;
271 bottom: 0;
272 left: -2px;
273 width: 5px;
274 cursor: col-resize;
275 }
276
277 .heartbeat-split__divider:hover::before {
278 background: var(--accent);
279 opacity: 0.5;
280 }
281
282 .heartbeat-split__divider:active::before {
283 background: var(--accent);
284 opacity: 0.8;
285 }
286
287 .heartbeat-split__right {
288 flex: 1;
289 min-width: 280px;
290 display: flex;
291 flex-direction: column;
292 overflow: hidden;
293 background: var(--heartbeat-split-bg);
294 }
295
296 /* Use the actual task-page width, not the app viewport. At narrow widths the
297 existing editor fills the page without remounting or changing split prefs. */
298 @container heartbeat-page (max-width: 559px) {
299 .heartbeat-split--detail-open .heartbeat-split__left,
300 .heartbeat-split--detail-open .heartbeat-split__divider {
301 display: none;
302 }
303
304 .heartbeat-split--detail-open .heartbeat-split__right {
305 flex: 1 1 100%;
306 width: 100%;
307 min-width: 0;
308 }
309 }
310
311 .heartbeat-split__right .heartbeat-editor {
312 flex: 1;
313 min-height: 0;
314 display: flex;
315 flex-direction: column;
316 overflow: hidden;
317 }
318
319 .heartbeat-split__empty-hint {
320 font-size: 12px;
321 color: var(--fg-faint);
322 text-align: center;
323 max-width: 220px;
324 line-height: 1.5;
325 }
326
327 .heartbeat-split__empty {
328 display: flex;
329 flex-direction: column;
330 align-items: center;
331 justify-content: center;
332 flex: 1;
333 color: var(--fg-faint);
334 font-size: 13px;
335 }
336
337 .heartbeat-split__empty-inner {
338 margin-top: -70px;
339 display: flex;
340 flex-direction: column;
341 align-items: center;
342 gap: 8px;
343 }
344
345 /* ── tree node styles (reused from worktree) ──────────────────── */
346
347 .worktree-tree {
348 display: flex;
349 flex-direction: column;
350 gap: 0;
351 }
352
353 .worktree-node {
354 display: flex;
355 align-items: center;
356 gap: 2px;
357 height: 28px;
358 padding-right: 8px;
359 cursor: pointer;
360 font-size: 12.5px;
361 color: var(--fg);
362 user-select: none;
363 transition: background 0.1s;
364 white-space: nowrap;
365 }
366
367 .worktree-node:hover {
368 background: var(--list-row-hover-bg);
369 }
370
371 .worktree-node--selected {
372 background: color-mix(in srgb, var(--accent) 10%, transparent);
373 border-left: 2px solid var(--accent);
374 color: var(--accent);
375 }
376
377 .worktree-node--scope {
378 font-weight: 400;
379 font-size: 13px;
380 color: var(--fg-dim);
381 height: 32px;
382 margin: 6px 4px 0 4px;
383 border-radius: 8px;
384 }
385
386 .worktree-node--scope:hover {
387 background: color-mix(in srgb, var(--accent) 6%, transparent);
388 }
389
390 .worktree-node__scope-add {
391 display: flex;
392 align-items: center;
393 justify-content: center;
394 width: 20px;
395 height: 20px;
396 border-radius: 4px;
397 opacity: 0;
398 transition: opacity 0.1s;
399 color: var(--fg-faint);
400 cursor: pointer;
401 flex-shrink: 0;
402 margin-right: 4px;
403 }
404
405 .worktree-node--scope:hover .worktree-node__scope-add {
406 opacity: 1;
407 }
408
409 .worktree-node__scope-add:hover {
410 background: var(--list-row-hover-bg);
411 color: var(--fg);
412 }
413
414 .worktree-node--task {
415 position: relative;
416 display: flex;
417 flex-direction: column;
418 align-items: stretch;
419 gap: 0;
420 font-size: 13px;
421 height: auto;
422 padding-top: 8px;
423 padding-bottom: 8px;
424 margin: 3px 4px;
425 padding-right: 4px;
426 cursor: pointer;
427 transition: background 0.1s;
428 border-radius: 8px;
429 }
430
431 .worktree-node__main {
432 display: flex;
433 align-items: center;
434 gap: 2px;
435 width: 100%;
436 min-width: 0;
437 line-height: 1.25;
438 }
439
440 .worktree-node__meta {
441 display: flex;
442 align-items: center;
443 gap: 6px;
444 width: 100%;
445 min-width: 0;
446 padding-left: 22px;
447 font-size: 11px;
448 line-height: 1.2;
449 color: var(--fg-faint);
450 }
451
452 .worktree-node__meta .worktree-node__scope-tag {
453 margin-left: 0;
454 background: transparent;
455 padding: 0;
456 font-size: inherit;
457 color: inherit;
458 max-width: none;
459 }
460
461 .worktree-node--task:hover {
462 background: color-mix(in srgb, var(--accent) 6%, transparent);
463 }
464
465 .worktree-node--task.worktree-node--selected {
466 background: color-mix(in srgb, var(--accent) 10%, transparent);
467 border-left: 0;
468 }
469
470 .worktree-node--task.worktree-node--selected .worktree-node__label {
471 color: var(--fg);
472 }
473
474 /* 悬停/选中:两行文字都增强(标题保持最深,meta 从 faint 提到 dim) */
475 .worktree-node--task:hover .worktree-node__label {
476 color: var(--fg);
477 }
478 .worktree-node--task:hover .worktree-node__meta,
479 .worktree-node--task.worktree-node--selected .worktree-node__meta {
480 color: var(--fg-dim);
481 }
482
483 .worktree-node--task.worktree-node--selected .worktree-node__action-btn {
484 color: var(--fg-faint);
485 }
486
487 .worktree-node--task.worktree-node--selected .worktree-node__action-btn:hover {
488 color: var(--fg);
489 }
490
491 .worktree-node__tail {
492 flex-shrink: 0;
493 margin-left: auto;
494 position: relative;
495 min-width: 40px;
496 text-align: right;
497 }
498
499 .worktree-node__actions {
500 display: flex;
501 align-items: center;
502 gap: 4px;
503 position: absolute;
504 right: 4px;
505 top: 50%;
506 transform: translateY(-50%);
507 flex-shrink: 0;
508 opacity: 0;
509 transition: opacity 0.1s;
510 }
511
512 .worktree-node--task:hover .worktree-node__actions {
513 opacity: 1;
514 }
515
516 .worktree-node__interval {
517 font-size: 11px;
518 color: var(--fg-faint);
519 white-space: nowrap;
520 overflow: hidden;
521 text-overflow: ellipsis;
522 max-width: 100%;
523 }
524
525 .worktree-node__action-btn {
526 display: flex;
527 align-items: center;
528 justify-content: center;
529 width: 26px;
530 height: 26px;
531 border: none;
532 border-radius: 6px;
533 background: transparent;
534 color: var(--fg-faint);
535 cursor: pointer;
536 flex-shrink: 0;
537 padding: 0;
538 transition: background 0.12s, color 0.12s;
539 }
540
541 .worktree-node__action-btn:disabled {
542 opacity: 0.25;
543 cursor: default;
544 }
545
546 .worktree-node__action-btn:hover:not(:disabled) {
547 background: var(--list-row-hover-bg);
548 color: var(--fg);
549 }
550
551 .worktree-node__icon {
552 width: 12px;
553 flex-shrink: 0;
554 display: flex;
555 align-items: center;
556 justify-content: center;
557 color: var(--fg-faint);
558 }
559
560 .worktree-node__marker {
561 display: flex;
562 align-items: center;
563 justify-content: center;
564 width: 16px;
565 flex-shrink: 0;
566 color: var(--fg-faint);
567 margin-right: 4px;
568 }
569
570 /* 启停状态(ChatGPT 三态):运行中=空圆圈,hover 显示暂停条提示可暂停;暂停中=播放三角提示可启动 */
571 .worktree-node__toggle {
572 display: flex;
573 align-items: center;
574 justify-content: center;
575 width: 20px;
576 height: 20px;
577 border: none;
578 border-radius: 50%;
579 background: transparent;
580 color: var(--fg-faint);
581 cursor: pointer;
582 transition: background 0.12s, color 0.12s;
583 flex-shrink: 0;
584 padding: 0;
585 }
586
587 .worktree-node__toggle:hover {
588 background: var(--list-row-hover-bg);
589 color: var(--fg);
590 }
591
592 /* 停用态进一步弱化:降低透明度,呈现“被禁用”的感觉 */
593 .worktree-node__toggle:not(.worktree-node__toggle--on) {
594 opacity: 0.45;
595 }
596
597 .worktree-node__toggle:not(.worktree-node__toggle--on):hover {
598 opacity: 1;
599 }
600
601 /* 运行中默认:空圆圈图标(lucide Circle,与 hover/停用图标同尺寸同粗细,切换零抖动) */
602 .worktree-node__toggle--on .worktree-node__toggle-circle {
603 display: block;
604 }
605
606 /* 运行中 hover:空圆圈换成暂停条图标,提示点击会暂停;hover 图标高亮 */
607 .worktree-node__toggle--on .worktree-node__toggle-hover {
608 display: none;
609 }
610
611 .worktree-node__toggle--on:hover .worktree-node__toggle-circle {
612 display: none;
613 }
614
615 .worktree-node__toggle--on:hover .worktree-node__toggle-hover {
616 display: block;
617 }
618
619 .worktree-node--scope .worktree-node__marker {
620 color: var(--fg-faint);
621 }
622
623 .worktree-node__label {
624 flex: 1;
625 overflow: hidden; text-overflow: ellipsis;
626 white-space: nowrap;
627 }
628
629 .worktree-node__count {
630 font-size: 10px;
631 color: var(--fg-faint);
632 flex-shrink: 0;
633 margin-right: 4px;
634 }
635
636 /* 暂停任务(未选中):两行文字都弱化;选中后与激活任务一样高亮(:not 排除选中态)
637 meta 在 faint 基础上再降透明度,与激活任务第二行拉开差异 */
638 .worktree-node--task.worktree-node--paused:not(.worktree-node--selected) .worktree-node__label {
639 color: var(--fg-faint);
640 }
641 .worktree-node--task.worktree-node--paused:not(.worktree-node--selected) .worktree-node__meta {
642 color: var(--fg-faint);
643 opacity: 0.65;
644 }
645
646 .worktree-node__dot {
647 width: 8px;
648 height: 8px;
649 border-radius: 50%;
650 flex-shrink: 0;
651 background: var(--border);
652 }
653
654 .worktree-node--task.worktree-node--selected .worktree-node__dot {
655 background: var(--border);
656 }
657
658 .worktree-node__dot--on {
659 background: #4caf50;
660 }
661
662 .worktree-node--task.worktree-node--selected .worktree-node__dot--on {
663 background: #4caf50;
664 }
665
666 /* ── toolbar ──────────────────────────────────────────────────── */
667
668 .heartbeat-toolbar {
669 display: flex;
670 align-items: center;
671 gap: 6px;
672 flex-shrink: 0;
673 flex-wrap: wrap;
674 }
675
676 /* All toolbar children share height: 28px for visual consistency */
677 .heartbeat-toolbar__btn,
678 .heartbeat-toolbar__search {
679 height: 28px;
680 box-sizing: border-box;
681 }
682
683 .heartbeat-toolbar__btn {
684 display: inline-flex;
685 align-items: center;
686 gap: 4px;
687 padding: 0 10px;
688 font-size: 12px;
689 font-weight: 500;
690 border: 1px solid var(--border);
691 border-radius: 6px;
692 background: var(--bg);
693 color: var(--fg);
694 cursor: pointer;
695 white-space: nowrap;
696 flex-shrink: 0;
697 transition: background 0.12s, border-color 0.12s, color 0.12s;
698 font-family: inherit;
699 }
700 /* Icon-only buttons in toolbar: minimal width, borderless ghost icon
701 (默认纯图标无边框;hover 由基础 .heartbeat-toolbar__btn:hover 提供轻背景) */
702 .heartbeat-toolbar__btn--icon {
703 width: 28px;
704 padding: 0;
705 justify-content: center;
706 border: none;
707 background: transparent;
708 color: var(--fg-faint);
709 }
710 .heartbeat-toolbar__btn:hover {
711 background: var(--list-row-hover-bg);
712 color: var(--fg);
713 }
714 .heartbeat-toolbar__btn:disabled {
715 opacity: 0.45;
716 cursor: default;
717 }
718
719 .heartbeat-toolbar__btn--primary,
720 .heartbeat-toolbar__btn--primary:hover {
721 color: #fff;
722 }
723 .heartbeat-toolbar__btn--primary {
724 background: var(--accent);
725 border-color: var(--accent);
726 }
727 .heartbeat-toolbar__btn--primary:hover {
728 background: color-mix(in srgb, var(--accent) 88%, black);
729 }
730 .heartbeat-toolbar__btn--add {
731 border-radius: 999px;
732 padding: 0 13px;
733 gap: 5px;
734 font-weight: 600;
735 }
736
737 .heartbeat-toolbar__btn--select {
738 min-width: 76px;
739 max-width: 130px;
740 padding: 0 4px 0 8px;
741 justify-content: space-between;
742 background: transparent;
743 border-color: transparent;
744 color: var(--fg-dim);
745 }
746 .heartbeat-toolbar__btn--select > span {
747 flex: 1;
748 text-align: left;
749 overflow: hidden;
750 text-overflow: ellipsis;
751 white-space: nowrap;
752 }
753
754 .heartbeat-toolbar__status {
755 display: flex;
756 gap: 2px;
757 flex-shrink: 0;
758 }
759
760 .heartbeat-scope-filter {
761 position: relative;
762 }
763
764 /* ── generic button (used in editor) ─────────────────────────── */
765
766 .heartbeat-btn {
767 display: inline-flex;
768 align-items: center;
769 gap: 6px;
770 height: 32px;
771 padding: 0 14px;
772 font-size: 12.5px;
773 font-weight: 500;
774 border: 1px solid var(--border);
775 border-radius: 7px;
776 background: var(--bg-elev);
777 color: var(--fg);
778 cursor: pointer;
779 white-space: nowrap;
780 transition: background 0.12s, border-color 0.12s;
781 font-family: inherit;
782 box-sizing: border-box;
783 }
784 .heartbeat-btn:hover {
785 background: var(--list-row-hover-bg);
786 border-color: var(--button-border-hover);
787 }
788 .heartbeat-btn:disabled {
789 opacity: 0.45;
790 cursor: default;
791 }
792 .heartbeat-btn--primary {
793 background: var(--accent);
794 color: #fff;
795 border-color: var(--accent);
796 }
797 .heartbeat-btn--primary:hover {
798 opacity: 0.88;
799 background: var(--accent);
800 }
801 .heartbeat-btn--danger {
802 background: transparent;
803 border-color: var(--border);
804 color: var(--err);
805 }
806 .heartbeat-btn--danger:hover {
807 background: color-mix(in srgb, var(--err) 10%, transparent);
808 border-color: var(--err);
809 }
810
811 .heartbeat-editor__confirm-del {
812 display: inline-flex;
813 align-items: center;
814 gap: 6px;
815 font-size: 12px;
816 color: var(--fg-faint);
817 }
818
819 /* ── empty / loading ──────────────────────────────────────────── */
820
821 .heartbeat-empty {
822 display: flex;
823 flex-direction: column;
824 align-items: center;
825 gap: 12px;
826 padding: 40px 0;
827 color: var(--fg-faint);
828 font-size: 13px;
829 }
830
831 /* ── 双视图:视图切换 + flat 行项目标签 + 引导式空态 ───────────── */
832
833 .heartbeat-toolbar__view {
834 display: flex;
835 align-items: center;
836 gap: 10px;
837 }
838
839 /* ── 状态筛选 tab(全部/已开启/已暂停) ────────────────────────── */
840
841 /* 列表区与详情区头部支持拖拽窗口(--reasonix-draggable 标记),
842 交互元素(按钮/输入框)保持 no-drag 可点击 */
843 .heartbeat-toolbar,
844 .heartbeat-editor__header {
845 --reasonix-draggable: drag;
846 user-select: none;
847 }
848
849 .heartbeat-toolbar button,
850 .heartbeat-toolbar input,
851 .heartbeat-editor__header button {
852 --reasonix-draggable: no-drag;
853 }
854
855 .heartbeat-status-tabs {
856 display: flex;
857 align-items: center;
858 gap: 2px;
859 flex-shrink: 0;
860 margin-right: 4px;
861 }
862
863 .heartbeat-status-tabs__tab {
864 height: 26px;
865 padding: 0 10px;
866 border: none;
867 border-radius: 6px;
868 background: transparent;
869 color: var(--fg-faint);
870 font-size: 12px;
871 cursor: pointer;
872 white-space: nowrap;
873 }
874
875 .heartbeat-status-tabs__tab:hover {
876 background: var(--list-row-hover-bg);
877 color: var(--fg);
878 }
879
880 .heartbeat-status-tabs__tab--on {
881 background: color-mix(in srgb, var(--accent) 12%, transparent);
882 color: var(--fg);
883 font-weight: 600;
884 }
885
886 .worktree-node__scope-tag {
887 flex-shrink: 0;
888 max-width: 110px;
889 overflow: hidden;
890 text-overflow: ellipsis;
891 white-space: nowrap;
892 font-size: 10.5px;
893 color: var(--fg-faint);
894 background: var(--list-row-hover-bg);
895 border-radius: 4px;
896 padding: 1px 5px;
897 margin-left: 4px;
898 }
899
900 .heartbeat-empty--guided {
901 gap: 14px;
902 padding: 48px 0;
903 }
904
905 .heartbeat-empty--guided .heartbeat-btn--primary {
906 display: inline-flex;
907 align-items: center;
908 gap: 5px;
909 height: 28px;
910 padding: 0 14px;
911 border: none;
912 border-radius: 6px;
913 background: var(--accent);
914 color: #fff;
915 font-size: 12px;
916 font-weight: 600;
917 cursor: pointer;
918 }
919
920 .heartbeat-empty--guided .heartbeat-btn--primary:hover {
921 opacity: 0.9;
922 }
923
924 @keyframes heartbeat-keyframe-pulse {
925 0%, 100% { opacity: 0.3; transform: scale(1); }
926 50% { opacity: 0.7; transform: scale(1.08); }
927 }
928 .heartbeat-pulse {
929 animation: heartbeat-keyframe-pulse 1.2s ease-in-out infinite;
930 }
931
932 /* ── filter menu (popover) ──────────────────────────────────── */
933
934 .heartbeat-filter-menu {
935 display: flex;
936 flex-direction: column;
937 gap: 2px;
938 min-width: 120px;
939 max-height: 200px;
940 overflow-y: auto;
941 border: 1px solid var(--border);
942 border-radius: 8px;
943 padding: 4px;
944 box-shadow: 0 4px 12px rgba(0,0,0,0.15);
945 }
946 .heartbeat-filter-menu__option {
947 display: flex;
948 align-items: center;
949 gap: 6px;
950 padding: 6px 8px;
951 font-size: 12px;
952 border-radius: 4px;
953 border: none;
954 background: transparent;
955 color: var(--fg);
956 cursor: pointer;
957 text-align: left;
958 width: 100%;
959 }
960 .heartbeat-filter-menu__option:hover {
961 background: var(--accent-soft, rgba(128,128,128,0.1));
962 }
963 .heartbeat-filter-menu__option--selected {
964 color: var(--accent);
965 font-weight: 600;
966 }
967 .heartbeat-filter-menu__check {
968 margin-left: auto;
969 flex-shrink: 0;
970 }
971
972 /* ── editor ─────────────────────────────────────────────────────── */
973
974 .heartbeat-editor {
975 padding: 0;
976 display: flex;
977 flex-direction: column;
978 gap: 0;
979 overflow: hidden;
980 }
981
982 /* 详情头部:状态文字(点击切换,CodeX 式)+ 操作菜单 + 关闭 */
983 .heartbeat-editor__header {
984 display: flex;
985 align-items: center;
986 gap: 8px;
987 padding: 10px 16px 4px;
988 font-size: 13px;
989 font-weight: 600;
990 color: var(--fg);
991 flex-shrink: 0;
992 }
993 .heartbeat-editor__header-spacer {
994 flex: 1;
995 }
996
997 .heartbeat-editor__status {
998 display: inline-flex;
999 align-items: center;
1000 gap: 6px;
1001 border: none;
1002 background: transparent;
1003 padding: 0;
1004 font-size: 13px;
1005 font-weight: 600;
1006 color: var(--fg-faint);
1007 cursor: pointer;
1008 }
1009
1010 .heartbeat-editor__status:hover {
1011 color: var(--fg);
1012 }
1013
1014 .heartbeat-editor__status--on {
1015 color: var(--accent);
1016 }
1017
1018 .heartbeat-editor__close {
1019 display: flex;
1020 align-items: center;
1021 justify-content: center;
1022 width: 26px;
1023 height: 26px;
1024 border: none;
1025 border-radius: 6px;
1026 background: transparent;
1027 color: var(--fg-faint);
1028 cursor: pointer;
1029 flex-shrink: 0;
1030 }
1031
1032 .heartbeat-editor__close:hover {
1033 background: var(--list-row-hover-bg);
1034 color: var(--fg);
1035 }
1036
1037 /* 详情头部操作按钮(启停/删除):图标+文字,统一 26px 高,与关闭按钮同一水平线 */
1038 .heartbeat-editor__header-action {
1039 display: inline-flex;
1040 align-items: center;
1041 gap: 5px;
1042 height: 26px;
1043 padding: 0 10px;
1044 border: none;
1045 border-radius: 6px;
1046 background: transparent;
1047 color: var(--fg-faint);
1048 font-size: 12px;
1049 font-family: inherit;
1050 cursor: pointer;
1051 flex-shrink: 0;
1052 }
1053
1054 .heartbeat-editor__header-action:hover {
1055 background: var(--list-row-hover-bg);
1056 color: var(--fg);
1057 }
1058
1059 .heartbeat-editor__header-action--on {
1060 color: var(--fg);
1061 }
1062
1063 .heartbeat-editor__header-action--danger:hover {
1064 color: var(--danger);
1065 }
1066
1067 .heartbeat-editor__header-action--confirm {
1068 color: var(--danger);
1069 background: color-mix(in srgb, var(--danger) 14%, transparent);
1070 }
1071
1072 .heartbeat-editor__header-action:disabled {
1073 opacity: 0.4;
1074 }
1075
1076 /* 表单滚动区 */.heartbeat-editor__fields {
1077 flex: 1;
1078 min-height: 0;
1079 overflow-y: auto;
1080 padding: 8px 16px 16px;
1081 display: flex;
1082 flex-direction: column;
1083 gap: 24px;
1084 }
1085
1086 /* 标题:隐形输入框(无边框大标题样式,点击直接编辑) */
1087 .heartbeat-editor__title {
1088 border: none;
1089 background: transparent;
1090 width: 100%;
1091 padding: 2px 0;
1092 font-size: 17px;
1093 font-weight: 650;
1094 line-height: 1.35;
1095 letter-spacing: -0.01em;
1096 color: var(--fg);
1097 outline: none;
1098 border-radius: 0;
1099 }
1100
1101 .heartbeat-editor__title::placeholder {
1102 color: var(--fg-faint);
1103 font-weight: 500;
1104 }
1105
1106 .heartbeat-editor__field {
1107 display: flex;
1108 flex-direction: column;
1109 gap: 5px;
1110 }
1111
1112 .heartbeat-editor__field > label {
1113 font-size: 11.5px;
1114 font-weight: 600;
1115 color: var(--fg-faint);
1116 text-transform: uppercase;
1117 letter-spacing: 0.05em;
1118 }
1119
1120 .heartbeat-editor__title-row {
1121 display: flex;
1122 align-items: center;
1123 gap: 8px;
1124 }
1125
1126 .heartbeat-editor__title-row .heartbeat-editor__input {
1127 flex: 1;
1128 }
1129
1130 /* ── editor text inputs ───────────────────────────────────────── */
1131
1132 .heartbeat-editor__input {
1133 height: 32px;
1134 padding: 0 11px;
1135 font-size: 13px;
1136 border: 1px solid var(--border);
1137 border-radius: 7px;
1138 background: transparent;
1139 color: var(--fg);
1140 font-family: inherit;
1141 transition: border-color 0.15s;
1142 box-sizing: border-box;
1143 }
1144
1145 .heartbeat-editor__textarea {
1146 padding: 8px 11px;
1147 font-size: 13px;
1148 border: 1px solid var(--border);
1149 border-radius: 7px;
1150 background: transparent;
1151 color: var(--fg);
1152 font-family: inherit;
1153 transition: border-color 0.15s;
1154 box-sizing: border-box;
1155 }
1156
1157 .heartbeat-editor__input:focus,
1158 .heartbeat-editor__textarea:focus {
1159 outline: none;
1160 border-color: var(--border);
1161 }
1162
1163 .heartbeat-editor__textarea {
1164 resize: vertical;
1165 height: 300px;
1166 }
1167
1168 /* 滚动条:无轨道背景,只显示可拖拽滑块 */
1169 .heartbeat-editor__textarea::-webkit-scrollbar {
1170 width: 8px;
1171 }
1172
1173 .heartbeat-editor__textarea::-webkit-scrollbar-track {
1174 background: transparent;
1175 }
1176
1177 .heartbeat-editor__textarea::-webkit-scrollbar-thumb {
1178 /* 与列表滚动条一致:fg-faint 的 35% 不透明度 */
1179 background: color-mix(in srgb, var(--fg-faint) 35%, transparent);
1180 border-radius: 4px;
1181 border: 2px solid transparent;
1182 background-clip: content-box;
1183 }
1184
1185 .heartbeat-editor__textarea::-webkit-scrollbar-thumb:hover {
1186 background: color-mix(in srgb, var(--fg-faint) 55%, transparent);
1187 background-clip: content-box;
1188 }
1189
1190 /* ── editor rows ──────────────────────────────────────────────── */
1191
1192 .heartbeat-editor__row {
1193 display: flex;
1194 gap: 12px;
1195 }
1196
1197 .heartbeat-editor__row > .heartbeat-editor__field {
1198 flex: 0;
1199 }
1200
1201 .heartbeat-editor__field--grow {
1202 flex: 1 !important;
1203 min-width: 0;
1204 }
1205
1206 .heartbeat-editor__field--auto {
1207 flex: 0 1 auto !important;
1208 }
1209
1210 .heartbeat-editor__field--toggle {
1211 min-width: 180px;
1212 }
1213
1214 .heartbeat-editor__optional {
1215 font-weight: 400;
1216 text-transform: none;
1217 letter-spacing: normal;
1218 color: var(--fg-faint);
1219 font-size: 10.5px;
1220 }
1221
1222 /* ── frequency: unified components ──────────────────────────────── */
1223 /* All freq-* components share height: 32px for visual consistency */
1224
1225 .heartbeat-editor__freq-interval,
1226 .heartbeat-editor__cycle-row {
1227 display: flex;
1228 align-items: center;
1229 gap: 6px;
1230 }
1231
1232 .heartbeat-editor__freq-label {
1233 font-size: 13px;
1234 color: var(--fg-dim);
1235 white-space: nowrap;
1236 }
1237
1238 .heartbeat-editor__freq-input {
1239 height: 32px;
1240 padding: 0 8px;
1241 font-size: 13px;
1242 border: 1px solid var(--border);
1243 border-radius: 7px;
1244 background: var(--bg);
1245 color: var(--fg);
1246 font-family: var(--mono);
1247 transition: border-color 0.15s;
1248 box-sizing: border-box;
1249 width: 48px;
1250 }
1251 .heartbeat-editor__freq-input:focus {
1252 outline: none;
1253 border-color: var(--border);
1254 }
1255 .heartbeat-editor__freq-input--time {
1256 width: 100px;
1257 font-family: var(--font-ui);
1258 }
1259
1260 .heartbeat-editor__freq-input--cron {
1261 width: 220px;
1262 font-family: var(--mono);
1263 font-size: 12px;
1264 }
1265
1266 .heartbeat-editor__cron-hint {
1267 font-size: 11px;
1268 color: var(--fg-faint);
1269 margin-top: 2px;
1270 }
1271
1272 .heartbeat-editor__freq-select {
1273 height: 32px;
1274 padding: 0 10px;
1275 font-size: 13px;
1276 border: 1px solid var(--border);
1277 border-radius: 7px;
1278 background: var(--bg);
1279 color: var(--fg);
1280 cursor: pointer;
1281 box-sizing: border-box;
1282 }
1283 .heartbeat-editor__freq-select:focus {
1284 outline: none;
1285 border-color: var(--border);
1286 }
1287
1288 /* ── weekday buttons ──────────────────────────────────────────── */
1289
1290 .heartbeat-editor__weekdays {
1291 display: flex;
1292 gap: 4px;
1293 flex-wrap: wrap;
1294 }
1295
1296 .heartbeat-editor__weekday-btn {
1297 height: 32px;
1298 min-width: 38px;
1299 padding: 0 8px;
1300 font-size: 12px;
1301 font-weight: 500;
1302 border: 1px solid var(--border);
1303 border-radius: 6px;
1304 background: transparent;
1305 color: var(--fg-faint);
1306 cursor: pointer;
1307 transition: background 0.12s, border-color 0.12s, color 0.12s;
1308 }
1309 .heartbeat-editor__weekday-btn:hover {
1310 background: var(--list-row-hover-bg);
1311 }
1312 .heartbeat-editor__weekday-btn--on {
1313 background: var(--accent);
1314 border-color: var(--accent);
1315 color: #fff;
1316 }
1317 .heartbeat-editor__weekday-btn--on:hover {
1318 opacity: 0.88;
1319 }
1320
1321 /* ── cycle wrap ────────────────────────────────────────────────── */
1322
1323 .heartbeat-editor__cycle-wrap {
1324 display: flex;
1325 flex-direction: column;
1326 gap: 8px;
1327 }
1328
1329 /* ── editor toggle switch ─────────────────────────────────────── */
1330
1331 .heartbeat-editor__toggle-row {
1332 display: flex;
1333 align-items: center;
1334 gap: 8px;
1335 padding-top: 4px;
1336 }
1337
1338 .heartbeat-editor__toggle {
1339 position: relative;
1340 width: 36px;
1341 height: 20px;
1342 flex-shrink: 0;
1343 border: none;
1344 border-radius: 10px;
1345 background: var(--border);
1346 cursor: pointer;
1347 transition: background 0.2s;
1348 padding: 0;
1349 }
1350
1351 .heartbeat-editor__toggle:hover {
1352 background: var(--fg-faint);
1353 }
1354
1355 .heartbeat-editor__toggle--on {
1356 background: #4caf50;
1357 }
1358
1359 .heartbeat-editor__toggle--on:hover {
1360 background: #43a047;
1361 }
1362
1363 .heartbeat-editor__toggle-knob {
1364 position: absolute;
1365 top: 2px;
1366 left: 2px;
1367 width: 16px;
1368 height: 16px;
1369 border-radius: 50%;
1370 background: #fff;
1371 transition: transform 0.2s ease;
1372 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
1373 }
1374
1375 .heartbeat-editor__toggle--on .heartbeat-editor__toggle-knob {
1376 transform: translateX(16px);
1377 }
1378
1379 .heartbeat-editor__toggle-label {
1380 font-size: 12px;
1381 color: var(--fg-faint);
1382 white-space: nowrap;
1383 }
1384
1385 /* ── editor actions ───────────────────────────────────────────── */
1386
1387 /* ── 运行历史记录(频率字段之后) ─────────────────────────── */
1388 .heartbeat-run-history {
1389 margin: 0;
1390 }
1391 .heartbeat-run-history__header {
1392 display: flex;
1393 align-items: center;
1394 gap: 6px;
1395 font-size: 11.5px;
1396 font-weight: 600;
1397 color: var(--fg-faint);
1398 text-transform: uppercase;
1399 letter-spacing: 0.05em;
1400 margin: 18px 0 8px;
1401 }
1402 .heartbeat-run-history__empty {
1403 font-size: 12px;
1404 color: var(--fg-faint);
1405 padding: 6px 0;
1406 }
1407 .heartbeat-run-history__list {
1408 display: flex;
1409 flex-direction: column;
1410 gap: 2px;
1411 }
1412 .heartbeat-run-history__item {
1413 display: flex;
1414 align-items: center;
1415 gap: 8px;
1416 width: 100%;
1417 padding: 9px 8px;
1418 border: none;
1419 border-radius: 6px;
1420 background: transparent;
1421 font-family: inherit;
1422 cursor: pointer;
1423 color: var(--fg);
1424 font-size: 12px;
1425 text-align: left;
1426 }
1427 .heartbeat-run-history__item:hover:not(:disabled) {
1428 background: var(--list-row-hover-bg);
1429 }
1430 .heartbeat-run-history__item:disabled {
1431 cursor: default;
1432 color: var(--fg-faint);
1433 }
1434 .heartbeat-run-history__title {
1435 flex-shrink: 1;
1436 min-width: 0;
1437 overflow: hidden;
1438 text-overflow: ellipsis;
1439 white-space: nowrap;
1440 font-weight: 600;
1441 }
1442 .heartbeat-run-history__scope {
1443 flex-shrink: 0;
1444 max-width: 110px;
1445 overflow: hidden;
1446 text-overflow: ellipsis;
1447 white-space: nowrap;
1448 font-size: 10.5px;
1449 color: var(--fg-faint);
1450 margin-left: 6px;
1451 }
1452 .heartbeat-run-history__rel {
1453 margin-left: auto;
1454 flex-shrink: 0;
1455 font-size: 11px;
1456 color: var(--fg-faint);
1457 font-variant-numeric: tabular-nums;
1458 }
1459 .heartbeat-run-history__notopic {
1460 margin-left: 6px;
1461 flex-shrink: 0;
1462 font-size: 11px;
1463 color: var(--fg-faint);
1464 }
1465
1466 .heartbeat-editor__actions {
1467 display: flex;
1468 gap: 8px;
1469 padding-top: 6px;
1470 }
1471
1472 .heartbeat-editor__inline-error,
1473 .heartbeat-editor__save-error {
1474 color: var(--danger);
1475 font-size: 12px;
1476 line-height: 1.4;
1477 }
1478
1479 .heartbeat-editor__save-notice {
1480 padding: 9px 16px;
1481 border-top: 1px solid var(--border);
1482 background: color-mix(in srgb, var(--danger) 7%, transparent);
1483 flex-shrink: 0;
1484 }
1485
1486 /* 保存/取消操作条(仅脏状态显示) */
1487 .heartbeat-editor__actions {
1488 display: flex;
1489 justify-content: flex-end;
1490 gap: 8px;
1491 padding: 10px 16px 14px;
1492 border-top: 1px solid var(--border);
1493 flex-shrink: 0;
1494 }
1495
1496 .heartbeat-editor__action-btn {
1497 height: 30px;
1498 padding: 0 14px;
1499 border: 1px solid var(--border);
1500 border-radius: 7px;
1501 background: transparent;
1502 color: var(--fg);
1503 font-size: 13px;
1504 cursor: pointer;
1505 }
1506
1507 .heartbeat-editor__action-btn:hover {
1508 background: var(--list-row-hover-bg);
1509 }
1510
1511 .heartbeat-editor__action-btn--primary {
1512 background: var(--accent);
1513 border-color: var(--accent);
1514 color: #fff;
1515 }
1516
1517 .heartbeat-editor__action-btn--primary:hover {
1518 opacity: 0.9;
1519 background: var(--accent);
1520 }
1521
1522 .heartbeat-editor__action-btn--primary:disabled {
1523 opacity: 0.5;
1524 cursor: default;
1525 }
1526
1527 .heartbeat-editor__mode-hint {
1528 font-size: 11px;
1529 color: var(--fg-faint);
1530 line-height: 1.4;
1531 }
1532
1533 .heartbeat-editor__tw-row {
1534 display: flex;
1535 align-items: center;
1536 gap: 6px;
1537 flex-wrap: wrap;
1538 margin-top: 2px;
1539 }
1540
1541 .heartbeat-editor__tw-inputs {
1542 display: flex;
1543 align-items: center;
1544 gap: 4px;
1545 }
1546
1547 .heartbeat-editor__tw-sep {
1548 font-size: 12px;
1549 color: var(--fg-faint);
1550 }
1551
1552 .heartbeat-editor__tw-add {
1553 cursor: pointer;
1554 font-size: 11.5px;
1555 color: var(--accent);
1556 font-weight: 500;
1557 user-select: none;
1558 }
1559
1560 .heartbeat-editor__tw-add:hover {
1561 opacity: 0.8;
1562 }
1563
1564 .heartbeat-editor__tw-remove {
1565 display: flex;
1566 align-items: center;
1567 justify-content: center;
1568 width: 22px;
1569 height: 22px;
1570 border: none;
1571 border-radius: 4px;
1572 background: transparent;
1573 color: var(--fg-faint);
1574 cursor: pointer;
1575 padding: 0;
1576 flex-shrink: 0;
1577 }
1578
1579 .heartbeat-editor__tw-remove:hover {
1580 background: var(--list-row-hover-bg);
1581 color: var(--err);
1582 }
1583
1584 /* ── scope selector ───────────────────────────────────────────── */
1585
1586 .heartbeat-scope-wrap {
1587 position: relative;
1588 }
1589
1590 .heartbeat-scope-select {
1591 display: inline-flex;
1592 align-items: center;
1593 gap: 6px;
1594 padding: 6px 10px;
1595 font-size: var(--font-control);
1596 font-weight: 400;
1597 border: 1px solid var(--border);
1598 border-radius: 7px;
1599 background: var(--bg);
1600 color: var(--fg);
1601 cursor: pointer;
1602 font-family: inherit;
1603 transition: border-color 0.15s;
1604 }
1605
1606 .heartbeat-scope-select:hover {
1607 border-color: var(--button-border-hover);
1608 }
1609
1610 .heartbeat-project-menu {
1611 position: absolute;
1612 top: calc(100% + 4px);
1613 left: 0;
1614 z-index: 10;
1615 min-width: 220px;
1616 max-height: 240px;
1617 overflow-y: auto;
1618 border: 1px solid var(--border);
1619 border-radius: 8px;
1620 box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
1621 padding: 4px;
1622 }
1623
1624 .heartbeat-project-menu__item {
1625 display: flex;
1626 align-items: center;
1627 justify-content: space-between;
1628 width: 100%;
1629 padding: 7px 10px;
1630 font-size: 12.5px;
1631 border: none;
1632 border-radius: 5px;
1633 background: transparent;
1634 color: var(--fg);
1635 cursor: pointer;
1636 text-align: left;
1637 }
1638
1639 .heartbeat-project-menu--up {
1640 top: auto;
1641 bottom: calc(100% + 4px);
1642 }
1643
1644 .heartbeat-project-menu__item:hover {
1645 background: color-mix(in srgb, var(--accent) 8%, transparent);
1646 }
1647
1648 .heartbeat-project-menu__item--active {
1649 background: rgba(128, 128, 128, 0.08);
1650 color: var(--fg-dim);
1651 }
1652
1653 .heartbeat-project-menu__current {
1654 font-size: 10px;
1655 color: var(--fg-faint);
1656 margin-left: 8px;
1657 }
1658
1659 .heartbeat-project-menu__empty {
1660 padding: 12px;
1661 text-align: center;
1662 font-size: 12px;
1663 color: var(--fg-faint);
1664 }
1665
1666 /* ── footer hint ──────────────────────────────────────────────── */
1667
1668
1669 /* ── Sidebar header automation button ── */
1670
1671 .sidebar__hb-header {
1672 display: flex;
1673 align-items: center;
1674 padding: 0 12px;
1675 gap: 2px;
1676 }
1677
1678 .sidebar__hb-header--new {
1679 padding-top: 2px;
1680 padding-bottom: 0;
1681 }
1682
1683 .sidebar__hb-add {
1684 display: flex;
1685 align-items: center;
1686 justify-content: center;
1687 width: 24px;
1688 height: 24px;
1689 border: none;
1690 border-radius: 5px;
1691 background: transparent;
1692 color: var(--fg-faint);
1693 cursor: pointer;
1694 flex-shrink: 0;
1695 transition: background 0.12s, color 0.12s;
1696 }
1697
1698 .sidebar__hb-add:hover {
1699 background: var(--list-row-hover-bg);
1700 color: var(--fg);
1701 }
1702
1703 /* ── Project collapsible section ── */
1704
1705 .sidebar__project-section {
1706 margin-top: 8px;
1707 padding: 0 12px;
1708 position: relative;
1709 display: flex;
1710 flex-direction: column;
1711 flex: 1;
1712 min-height: 0;
1713 }
1714
1715 .sidebar__project-section .project-tree__header-title {
1716 display: none;
1717 }
1718
1719 .sidebar__project-section .project-tree__header {
1720 position: absolute;
1721 top: 2px;
1722 right: 12px;
1723 border: none;
1724 padding: 0;
1725 background: transparent;
1726 display: flex;
1727 align-items: center;
1728 gap: 2px;
1729 opacity: 0;
1730 transition: opacity 0.12s;
1731 }
1732
1733 .sidebar__project-section:hover .project-tree__header {
1734 opacity: 1;
1735 }
1736
1737 .sidebar__project-section .project-tree {
1738 --project-tree-edge-gap: 12px;
1739 }
1740
1741 .sidebar__project-section .sidebar__section--projects {
1742 flex: 1;
1743 min-height: 0;
1744 overflow-y: auto;
1745 }
1746
1747 .sidebar__project-section .project-tree > .project-tree__list {
1748 margin: 0;
1749 padding: 0;
1750 }
1751
1752 /* 项目树行与顶部菜单宽度对齐 */
1753 :root[data-theme-style] .sidebar__project-section .project-tree__folder,
1754 :root[data-theme-style] .sidebar__project-section .project-tree__topic {
1755 margin-right: 8px;
1756 }
1757
1758 :root[data-theme-style] .project-tree__search {
1759 margin: 0 12px;
1760 }
1761
1762 :root[data-theme-style] .project-tree__topic {
1763 padding-right: 0;
1764 }
1765
1766 /* 项目文件夹右侧的新建话题按钮悬停显示(上游 styles.css 已内置) */
1767
1768 :root[data-theme-style] .project-tree__topic-label {
1769 font-size: 13px;
1770 font-weight: 400;
1771 }
1772
1773 :root[data-theme-style] .project-tree__folder-label {
1774 font-size: 13px;
1775 font-weight: 400;
1776 }
1777
1778 /* 项目文件夹右侧 + 图标默认隐藏,悬停显示 */
1779 .sidebar__project-section .project-tree__new-topic {
1780 opacity: 0;
1781 pointer-events: none;
1782 transition: opacity 0.12s;
1783 }
1784
1785 .sidebar__project-section .project-tree__folder:hover .project-tree__new-topic,
1786 .sidebar__project-section .project-tree__folder:focus-within .project-tree__new-topic {
1787 opacity: 1;
1788 pointer-events: auto;
1789 }
1790
1791 :root[data-theme-style] .project-tree__topic--active .project-tree__topic-label {
1792 color: var(--fg);
1793 font-weight: 400;
1794 }
1795
1796 .sidebar__project-head {
1797 display: flex;
1798 align-items: center;
1799 gap: 4px;
1800 flex-shrink: 0;
1801 height: 30px;
1802 }
1803
1804 /* ── 建议区(全部视图列表底部,一键创建预设任务) ── */
1805 .heartbeat-suggestions {
1806 padding: 20px 14px 10px;
1807 border-top: 1px solid var(--border-soft);
1808 margin-top: 16px;
1809 }
1810 .heartbeat-suggestions__header {
1811 display: flex;
1812 align-items: center;
1813 gap: 6px;
1814 font-size: 12px;
1815 font-weight: 600;
1816 color: var(--fg-faint);
1817 margin-bottom: 8px;
1818 }
1819 .heartbeat-suggestions__hint {
1820 font-size: 11px;
1821 font-weight: 400;
1822 color: var(--fg-faint);
1823 }
1824 .heartbeat-suggestion {
1825 display: flex;
1826 flex-direction: column;
1827 gap: 3px;
1828 width: 100%;
1829 padding: 9px 10px;
1830 border: none;
1831 border-radius: 8px;
1832 background: transparent;
1833 cursor: pointer;
1834 text-align: left;
1835 font-family: inherit;
1836 margin-bottom: 6px;
1837 transition: background 0.12s;
1838 }
1839 .heartbeat-suggestion:hover {
1840 background: var(--list-row-hover-bg);
1841 }
1842 .heartbeat-suggestion__main {
1843 display: flex;
1844 align-items: center;
1845 justify-content: space-between;
1846 gap: 8px;
1847 }
1848 .heartbeat-suggestion__title {
1849 font-size: 13px;
1850 font-weight: 600;
1851 color: var(--fg);
1852 }
1853 .heartbeat-suggestion__freq {
1854 font-size: 11px;
1855 color: var(--accent);
1856 flex-shrink: 0;
1857 }
1858 .heartbeat-suggestion__desc {
1859 font-size: 12px;
1860 color: var(--fg-faint);
1861 line-height: 1.4;
1862 }
1863
1864 /* ── 任务行 ⋯ 菜单(仅删除) ── */
1865 .heartbeat-task-menu {
1866 display: flex;
1867 flex-direction: column;
1868 gap: 2px;
1869 min-width: 120px;
1870 border: 1px solid var(--border);
1871 border-radius: 8px;
1872 padding: 4px;
1873 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
1874 }
1875 .heartbeat-task-menu__item {
1876 display: flex;
1877 align-items: center;
1878 gap: 7px;
1879 width: 100%;
1880 padding: 6px 9px;
1881 border: none;
1882 border-radius: 6px;
1883 background: transparent;
1884 color: var(--fg);
1885 font-size: 12px;
1886 font-family: inherit;
1887 cursor: pointer;
1888 text-align: left;
1889 }
1890 .heartbeat-task-menu__item:hover {
1891 background: var(--list-row-hover-bg);
1892 }
1893 .heartbeat-task-menu__item--danger {
1894 color: var(--danger);
1895 }
1896
1897 /* Management surfaces retain their own list/detail layout at every theme. */
1898 .management-screen .heartbeat-page { flex: 1; min-height: 0; }
1899 .management-screen .heartbeat-split { background: var(--bg); }
1900 .management-screen .heartbeat-split__left { min-width: 280px; }
1901 .management-screen .heartbeat-page[data-detail="true"] .heartbeat-split__left { max-width: calc(100% - 425px); }
1902 .management-screen .heartbeat-split__right { min-width: 420px; display: flex; flex-direction: column; min-height: 0; }
1903 .management-screen .heartbeat-toolbar { flex-wrap: wrap; gap: 8px; }
1904 .management-screen .heartbeat-editor { flex: 1; min-height: 0; }
1905 .automation-fields { border: 0; padding: 0; margin: 0; min-width: 0; display: flex; flex-direction: column; gap: 20px; }
1906 .automation-fields[hidden], .heartbeat-run-history[hidden] { display: none; }
1907 .heartbeat-detail-tabs { display: flex; gap: 8px; padding: 8px 20px; border-bottom: 1px solid var(--border); }
1908 .heartbeat-detail-tabs button { border: 0; padding: 8px 12px; background: transparent; color: var(--fg-dim); border-radius: 8px; cursor: pointer; }
1909 .heartbeat-detail-tabs button[aria-selected="true"] { background: var(--bg-soft); color: var(--fg); }
1910 .automation-draft-list { display: flex; flex-direction: column; gap: 4px; padding: 8px 16px; }
1911 .automation-draft-list h3 { font-size: 12px; color: var(--fg-dim); }
1912 .automation-draft-list button { justify-content: space-between; white-space: normal; text-align: left; }
1913 .automation-dirty { color: var(--fg-dim); font-weight: normal; }
1914 @media (max-width: 959px) {
1915 .management-screen .heartbeat-page[data-detail="true"] .heartbeat-split__left, .management-screen .heartbeat-split__divider { display: none; }
1916 .management-screen .heartbeat-split__right { min-width: 0; width: 100%; flex: 1; }
1917 .management-screen .heartbeat-split__left { min-width: 0; width: 100%; }
1918 }
1919 .automation-task-select { background: transparent; border: 0; color: inherit; padding: 0; text-align: left; font: inherit; cursor: pointer; }
1920 .automation-task-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
1921
1922 .automation-save-status { margin-right: auto; color: var(--fg-dim); font-size: 12px; }
1923
1923 lines CSS