本文へ移動
cccskills
無料GitHub で公開

refresh-scheduler

Manage timed data refresh intervals for all dashboard panels. Handles pause-when-hidden, stale-data flush on visibility restore, and staggered refresh to avoid API bursts.

インストール方法を見る

含まれるファイル(2)

  • SKILL.md4.7 KB
  • .skill_id32 B

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Refresh Scheduler Pattern

The refresh scheduler manages periodic data fetching for all panels, pausing when the tab is hidden and flushing stale data when the user returns.

Implementation

Create src/services/refresh-scheduler.ts:

export interface RefreshRegistration {
  name: string;
  fn: () => Promise<void>;
  intervalMs: number;
  condition?: () => boolean;
}

interface RunnerEntry {
  timer: ReturnType<typeof setInterval> | null;
  intervalMs: number;
  fn: () => Promise<void>;
  condition?: () => boolean;
  lastRun: number;
}

export class RefreshScheduler {
  private runners = new Map<string, RunnerEntry>();
  private inFlight = new Set<string>();
  private hiddenSince = 0;
  private visibilityHandler: (() => void) | null = null;

  constructor() {
    this.visibilityHandler = () => this.onVisibilityChange();
    document.addEventListener('visibilitychange', this.visibilityHandler);
  }

  private onVisibilityChange(): void {
    if (document.hidden) {
      this.hiddenSince = Date.now();
      // Pause all timers
      for (const entry of this.runners.values()) {
        if (entry.timer) {
          clearInterval(entry.timer);
          entry.timer = null;
        }
      }
    } else {
      // Resume and flush stale
      this.flushStaleRefreshes();
      for (const [name, entry] of this.runners) {
        if (!entry.timer) {
          entry.timer = setInterval(() => this.runRefresh(name), entry.intervalMs);
        }
      }
    }
  }

  private async runRefresh(name: string): Promise<void> {
    const entry = this.runners.get(name);
    if (!entry) return;
    if (this.inFlight.has(name)) return;
    if (entry.condition && !entry.condition()) return;

    this.inFlight.add(name);
    try {
      await entry.fn();
      entry.lastRun = Date.now();
    } catch (err) {
      console.error(`[Refresh] ${name} failed:`, err);
    } finally {
      this.inFlight.delete(name);
    }
  }

  scheduleRefresh(
    name: string,
    fn: () => Promise<void>,
    intervalMs: number,
    condition?: () => boolean,
  ): void {
    // Clean up existing
    const existing = this.runners.get(name);
    if (existing?.timer) clearInterval(existing.timer);

    const entry: RunnerEntry = {
      timer: null,
      intervalMs,
      fn,
      condition,
      lastRun: 0,
    };

    if (!document.hidden) {
      entry.timer = setInterval(() => this.runRefresh(name), intervalMs);
    }

    this.runners.set(name, entry);
  }

  /**
   * After returning from hidden, flush all runners whose data
   * is older than their interval. Stagger to avoid API burst.
   */
  flushStaleRefreshes(): void {
    if (!this.hiddenSince) return;
    const hiddenMs = Date.now() - this.hiddenSince;
    this.hiddenSince = 0;

    let stagger = 0;
    for (const [name, entry] of this.runners) {
      if (hiddenMs < entry.intervalMs) continue;
      const delay = stagger;
      stagger += 150; // 150ms between each flush
      setTimeout(() => this.runRefresh(name), delay);
    }
  }

  registerAll(registrations: RefreshRegistration[]): void {
    for (const reg of registrations) {
      this.scheduleRefresh(reg.name, reg.fn, reg.intervalMs, reg.condition);
    }
  }

  /** Manually trigger a specific refresh immediately */
  trigger(name: string): void {
    this.runRefresh(name);
  }

  destroy(): void {
    if (this.visibilityHandler) {
      document.removeEventListener('visibilitychange', this.visibilityHandler);
    }
    for (const entry of this.runners.values()) {
      if (entry.timer) clearInterval(entry.timer);
    }
    this.runners.clear();
    this.inFlight.clear();
  }
}

Usage

// src/main.ts
import { RefreshScheduler } from './services/refresh-scheduler';

const scheduler = new RefreshScheduler();

// Register panel refresh intervals
scheduler.registerAll([
  { name: 'stocks', fn: () => stockPanel.refresh(), intervalMs: 60_000 },
  { name: 'news',   fn: () => newsPanel.refresh(),  intervalMs: 5 * 60_000 },
  { name: 'email',  fn: () => emailPanel.refresh(), intervalMs: 2 * 60_000 },
  { name: 'calendar', fn: () => calendarPanel.refresh(), intervalMs: 5 * 60_000 },
]);

Key Behaviors

  1. Pause when hidden — stops all intervals when tab is not visible
  2. Flush on return — refreshes stale panels with 150ms stagger to avoid API burst
  3. In-flight guard — prevents duplicate requests for the same panel
  4. Configurable intervals — each panel has its own refresh rate
  5. Condition guard — optional condition function to skip refresh (e.g. panel hidden)

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Incremental audio production with duration mismatch handling, adaptive stem extension, and pre-mix alignment verification

日本語の概要は準備中です。原文の説明を表示しています。

HKUDS/OpenSpace7,7552026年8月13日 更新

Incremental audio production with duration alignment handling, per-stem verification, and adaptive extension strategies

日本語の概要は準備中です。原文の説明を表示しています。

HKUDS/OpenSpace7,7552026年8月13日 更新

Create serverless API proxy endpoints that hide API keys and provide a unified backend for the dashboard frontend. Designed for Vercel deployment.

日本語の概要は準備中です。原文の説明を表示しています。

HKUDS/OpenSpace7,7552026年8月13日 更新

End-to-end audio production workflow with stems, effects, archiving, and verification

日本語の概要は準備中です。原文の説明を表示しています。

HKUDS/OpenSpace7,7552026年8月13日 更新

Handle cascading data retrieval tool failures by falling back to embedded knowledge generation

日本語の概要は準備中です。原文の説明を表示しています。

HKUDS/OpenSpace7,7552026年8月13日 更新

Fallback pattern for executing Python code when execute_code_sandbox fails

日本語の概要は準備中です。原文の説明を表示しています。

HKUDS/OpenSpace7,7552026年8月13日 更新

HKUDS のスキルをすべて見る

このスキルの問題を報告する