@goodandready/dsh-cron 0.2.37 → 0.2.38

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 CHANGED
@@ -452,6 +452,7 @@ Developer-facing, no behaviour change. `parseScheduleExpression` was split into
452
452
  - **Modular Client Architecture (#150)**: Decomposed monolithic `lib/client.js` (~3950 lines) into 14 focused, single-responsibility fragments under `lib/client-src/` (none exceeding 580 lines). Integrated zero-dependency build script `scripts/build-client.mjs` wired into `package.json` (`build:client`, `pretest`). Development fragments are excluded from npm distribution via `"files": ["lib/*.js", ...]`.
453
453
  - **DSH Theme Tokens & Visual Standardization (#149)**: Replaced inline styles on task type badges (`onSuccess`, `onFailure`, `heartbeat`, `targetSession`, `preflight`) with dedicated `.dsh-cron-tag-*` CSS classes powered by semantic `--dsh-cron-*` theme variables. Modal overlay now adapts dynamically using `var(--dsw-alias-bg-mask, rgba(0, 0, 0, 0.75))`, and keyframe pulse animations use theme variables without hardcoded RGBA.
454
454
  - **Label Governance & Triage Audit (#95)**: Standardized 100% of repository issues and triage on the canonical repo-level label set.
455
+ - **Style Self-Healing on Dynamic DOM Sweeps (#263 / GH-4)**: Watches `<head>` and `<html>` via `MutationObserver` alongside a 2s backstop interval and `visibilitychange` listener (`startStyleSelfHeal`), automatically re-attaching or repairing the `<style id="dsh-cron-styles">` tag if swept by host head re-renders, theme switches, or neighbor cleanups.
455
456
 
456
457
  ---
457
458
 
package/README.ru.md CHANGED
@@ -319,6 +319,7 @@ cron({
319
319
  - **Модульная архитектура клиентской части (#150)**: Монолитный файл `lib/client.js` (~3950 строк) декомпозирован на 14 независимых специализированных модулей в каталоге `lib/client-src/` (каждый строго <= 580 строк). Сборка `lib/client.js` выполняется легковесным скриптом `scripts/build-client.mjs` перед запуском тестов и сборки. Исходные фрагменты исключены из npm-дистрибутива через `"files": ["lib/*.js", ...]`.
320
320
  - **Семантические токены тем DSH (#149)**: Инлайн-стили тегов типа задач (`onSuccess`, `onFailure`, `heartbeat`, `targetSession`, `preflight`) переведены на выделенные CSS-классы `.dsh-cron-tag-*` на базе переменных темы `--dsh-cron-*`. Оверлей модальных окон переведен на адаптивную маску `var(--dsw-alias-bg-mask, rgba(0, 0, 0, 0.75))`, а анимация пульсации избавлена от захардкоженных значений RGBA.
321
321
  - **Аудит меток репозитория (#95)**: Подтверждена 100% консистентность использования канонического репозиторного набора меток и фильтрации задач.
322
+ - **Самовосстановление стилей при динамических перерисовках DOM (#263 / GH-4)**: Отслеживает `<head>` и `<html>` через `MutationObserver`, резервный интервал 2с и событие `visibilitychange` (`startStyleSelfHeal`), мгновенно восстанавливая узел `<style id="dsh-cron-styles">` при перерисовке заголовка хостом, смене темы или очистке соседними модулями.
322
323
 
323
324
  ---
324
325
 
package/README.zh.md CHANGED
@@ -444,6 +444,7 @@ bash deploy.sh verify [exact-version]
444
444
  - **客户端模块化架构 (#150)**: 将庞大的单文件 `lib/client.js` (约 3950 行) 拆分为 `lib/client-src/` 下的 14 个高内聚模块文件 (各模块严格 <= 580 行)。集成零依赖构建脚本 `scripts/build-client.mjs` 并接入 `package.json` (`build:client`, `pretest`),通过 `"files": ["lib/*.js", ...]` 避免开发源码冗余打包进 npm 发布包。
445
445
  - **DSH 语义化主题变量对齐 (#149)**: 将任务类型标签 (`onSuccess`, `onFailure`, `heartbeat`, `targetSession`, `preflight`) 的内联样式全部替换为基于主题变量的 `.dsh-cron-tag-*` 类;模态框遮罩层接入自适应主题遮罩变量 `var(--dsw-alias-bg-mask, rgba(0, 0, 0, 0.75))`,移除脉冲动画关键帧中的硬编码 RGBA。
446
446
  - **标签治理与工单审计 (#95)**: 审计并确认全仓库 100% 统一规范使用仓库级标签集。
447
+ - **动态 DOM 清理下的样式自动恢复(#263 / GH-4)**: 通过 `MutationObserver`、2 秒兜底定时器与 `visibilitychange` 事件(`startStyleSelfHeal`)监听 `<head>` 与 `<html>`,当宿主重绘 `<head>`、切换主题或邻近插件清理样式时,自动重新注入并修复 `<style id="dsh-cron-styles">` 标签。
447
448
 
448
449
  ---
449
450
 
package/lib/client.js CHANGED
@@ -847,17 +847,63 @@ window.__ModuleLoader__.load({
847
847
  }
848
848
  `;
849
849
 
850
+ const STYLE_ID = 'dsh-cron-styles';
851
+
850
852
  function ensureStyles() {
851
- if (typeof document === 'undefined') return;
852
- let el = document.getElementById('dsh-cron-styles');
853
- if (!el) {
853
+ if (typeof document === 'undefined') return null;
854
+ let el = document.getElementById(STYLE_ID);
855
+ if (el === null) {
856
+ const head = document.head || document.documentElement;
857
+ if (!head) return null;
854
858
  el = document.createElement('style');
855
- el.id = 'dsh-cron-styles';
859
+ el.id = STYLE_ID;
856
860
  // Mark ownership before insertion so neighbor cleanups leave it alone (#91)
857
861
  el.dataset.dshPlugin = 'dsh-cron';
858
- document.head.appendChild(el);
862
+ el.textContent = STYLES;
863
+ head.appendChild(el);
864
+ return el;
859
865
  }
860
- el.textContent = STYLES;
866
+ if (el.textContent !== STYLES) el.textContent = STYLES;
867
+ return el;
868
+ }
869
+
870
+ // Registry is shared across hot reloads so the newest instance owns exactly
871
+ // one watcher instead of stacking one per module evaluation.
872
+ const STYLE_HEAL_KEY = '__dshCronStyleHeal';
873
+ const STYLE_HEAL_INTERVAL_MS = 2000;
874
+
875
+ /**
876
+ * Keep the stylesheet attached: watch <head> (and <html>, which catches a
877
+ * replaced <head>) and re-attach on demand, with a slow interval as the
878
+ * backstop. heal() is idempotent and writes nothing while healthy.
879
+ */
880
+ function startStyleSelfHeal() {
881
+ if (typeof document === 'undefined') return () => {};
882
+ if (typeof window !== 'undefined' && typeof window[STYLE_HEAL_KEY] === 'function') {
883
+ try { window[STYLE_HEAL_KEY](); } catch (_err) { void _err; }
884
+ }
885
+ const heal = () => { try { ensureStyles(); } catch (_err) { void _err; } };
886
+ heal();
887
+ let observer = null;
888
+ try {
889
+ if (typeof MutationObserver === 'function') {
890
+ observer = new MutationObserver(heal);
891
+ if (document.head) observer.observe(document.head, { childList: true });
892
+ if (document.documentElement) observer.observe(document.documentElement, { childList: true });
893
+ }
894
+ } catch (_err) { observer = null; }
895
+ let timer = null;
896
+ try { timer = setInterval(heal, STYLE_HEAL_INTERVAL_MS); } catch (_err) { timer = null; }
897
+ const onVisibility = () => { if (document.visibilityState !== 'hidden') heal(); };
898
+ try { document.addEventListener('visibilitychange', onVisibility); } catch (_err) { void _err; }
899
+ const stop = () => {
900
+ try { if (observer) observer.disconnect(); } catch (_err) { void _err; }
901
+ try { if (timer !== null) clearInterval(timer); } catch (_err) { void _err; }
902
+ try { document.removeEventListener('visibilitychange', onVisibility); } catch (_err) { void _err; }
903
+ if (typeof window !== 'undefined' && window[STYLE_HEAL_KEY] === stop) window[STYLE_HEAL_KEY] = undefined;
904
+ };
905
+ if (typeof window !== 'undefined') window[STYLE_HEAL_KEY] = stop;
906
+ return stop;
861
907
  }
862
908
 
863
909
  function createErrorBoundary() {
@@ -4014,6 +4060,9 @@ window.__ModuleLoader__.load({
4014
4060
 
4015
4061
  const applyActive = () => {
4016
4062
  if (toggle.isOpen()) {
4063
+ // Repair a dropped stylesheet before the panel paints, so opening the
4064
+ // screen never shows an unstyled page (see startStyleSelfHeal).
4065
+ ensureStyles();
4017
4066
  document.documentElement.setAttribute(ACTIVE_ATTR, '');
4018
4067
  document.dispatchEvent(new CustomEvent(ACTIVATE_EVENT, { detail: PANEL }));
4019
4068
  } else {
@@ -4070,7 +4119,7 @@ window.__ModuleLoader__.load({
4070
4119
  }
4071
4120
 
4072
4121
  function apply(ctx) {
4073
- ensureStyles();
4122
+ const stopStyleSelfHeal = startStyleSelfHeal();
4074
4123
 
4075
4124
  // Register the English canonical strings; rebinding the translator lets
4076
4125
  // the translation plugin drive the active language at runtime (#87).
@@ -4158,7 +4207,7 @@ window.__ModuleLoader__.load({
4158
4207
  mountSidebarJobs(toggle, translate('sidebar.jobsTitle'), (taskId) => { pendingTaskHighlight = taskId; }),
4159
4208
  mountCronScreen(ctx, toggle)
4160
4209
  ];
4161
- return () => { for (const dispose of off) dispose(); };
4210
+ return () => { for (const dispose of off) dispose(); stopStyleSelfHeal(); };
4162
4211
  }, 'dsh-cron: client overlay');
4163
4212
 
4164
4213
  exports.slots = { card: cardSlot, chip: chipSlot };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-cron",
3
- "version": "0.2.37",
3
+ "version": "0.2.38",
4
4
  "description": "Background automation runner for DSH: isolated agent runs, script/HTTP/SSH/Docker runtimes, cost guard, notifications, heartbeats.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",