diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html
index 74de8e7..d7a1adc 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html
@@ -716,6 +716,12 @@
Time Span Entries (UTC)
+
+
+ When multiple entries overlap, spend this many minutes at each before cycling. Leave blank to disable.
+
| Start | End | Bookmark | Label | |
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/scheduler.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/scheduler.js
index e0a660d..84659d2 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/scheduler.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/scheduler.js
@@ -195,6 +195,13 @@
renderBookmarkSelect("scheduler-gl-night", null);
}
+ // Interleave input
+ const ilEl = document.getElementById("scheduler-ts-interleave");
+ if (ilEl) {
+ const il = currentConfig && currentConfig.interleave_min;
+ ilEl.value = il ? il : "";
+ }
+
// TimeSpan entries
renderTimespanEntries();
@@ -374,6 +381,8 @@
} else if (mode === "time_span") {
config.entries =
currentConfig && currentConfig.entries ? currentConfig.entries : [];
+ const ilVal = parseInt(document.getElementById("scheduler-ts-interleave").value, 10);
+ config.interleave_min = isNaN(ilVal) || ilVal <= 0 ? null : ilVal;
}
const btn = document.getElementById("scheduler-save-btn");
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/scheduler.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/scheduler.rs
index 060cef7..fbba02f 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/src/scheduler.rs
+++ b/src/trx-client/trx-frontend/trx-frontend-http/src/scheduler.rs
@@ -78,6 +78,11 @@ pub struct SchedulerConfig {
pub grayline: Option,
#[serde(default)]
pub entries: Vec,
+ /// When multiple entries are active simultaneously, cycle through them,
+ /// spending this many minutes at each before advancing to the next.
+ /// `None` (or 0) disables interleaving — the first matching entry wins.
+ #[serde(default)]
+ pub interleave_min: Option,
}
// ============================================================================
@@ -265,21 +270,41 @@ fn grayline_bookmark_id(gl: &GraylineConfig, now_min: f64) -> Option {
}
}
-fn timespan_bookmark_id(entries: &[ScheduleEntry], now_min: f64) -> Option {
- for entry in entries {
- let start = entry.start_min as f64;
- let end = entry.end_min as f64;
- let in_window = if start <= end {
- now_min >= start && now_min < end
- } else {
- // Spans midnight.
- now_min >= start || now_min < end
- };
- if in_window {
- return Some(entry.bookmark_id.clone());
+fn entry_is_active(entry: &ScheduleEntry, now_min: f64) -> bool {
+ let start = entry.start_min as f64;
+ let end = entry.end_min as f64;
+ if start <= end {
+ now_min >= start && now_min < end
+ } else {
+ // Spans midnight.
+ now_min >= start || now_min < end
+ }
+}
+
+fn timespan_bookmark_id(
+ entries: &[ScheduleEntry],
+ now_min: f64,
+ interleave_min: Option,
+) -> Option {
+ let active: Vec<&ScheduleEntry> = entries
+ .iter()
+ .filter(|e| entry_is_active(e, now_min))
+ .collect();
+
+ if active.is_empty() {
+ return None;
+ }
+
+ // With interleaving and more than one active entry, pick by time slot.
+ if active.len() > 1 {
+ if let Some(step) = interleave_min.filter(|&s| s > 0) {
+ let slot = (now_min as u64 / step as u64) as usize % active.len();
+ return Some(active[slot].bookmark_id.clone());
}
}
- None
+
+ // Default: first matching entry wins.
+ Some(active[0].bookmark_id.clone())
}
/// Current UTC time as minutes since midnight.
@@ -346,7 +371,7 @@ pub fn spawn_scheduler_task(
.as_ref()
.and_then(|gl| grayline_bookmark_id(gl, now_min)),
SchedulerMode::TimeSpan => {
- timespan_bookmark_id(&config.entries, now_min)
+ timespan_bookmark_id(&config.entries, now_min, config.interleave_min)
}
};
@@ -459,6 +484,7 @@ pub async fn get_scheduler(
mode: SchedulerMode::Disabled,
grayline: None,
entries: vec![],
+ interleave_min: None,
});
HttpResponse::Ok().json(config)
}