| 1 | // A generation fence also rejects callbacks already queued when cancelled. |
| 2 | export class StartupDelay { |
| 3 | private revision = 0; |
| 4 | private pending: (() => void) | undefined; |
| 5 | constructor(private readonly schedule: (callback: () => void) => () => void = (callback) => { |
| 6 | const timer = setTimeout(callback, 1000); |
| 7 | timer.unref(); |
| 8 | return () => clearTimeout(timer); |
| 9 | }) {} |
| 10 | |
| 11 | start(show: () => void): void { |
| 12 | this.cancel(); |
| 13 | const revision = this.revision; |
| 14 | this.pending = this.schedule(() => { |
| 15 | if (revision !== this.revision) return; |
| 16 | this.cancel(); |
| 17 | show(); |
| 18 | }); |
| 19 | } |
| 20 | |
| 21 | cancel(): void { |
| 22 | this.revision++; |
| 23 | this.pending?.(); |
| 24 | this.pending = undefined; |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | export function renderStartupPage(): string { |
| 29 | return `<!doctype html><html lang="en"><head><meta charset="utf-8"> |
| 30 | <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'"> |
| 31 | <title>Reasonix</title><style> |
| 32 | html,body{height:100%;margin:0;background:#1a1a2e;color:#f4f4f3;font:14px system-ui} |
| 33 | main{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center} |
| 34 | b{font-size:32px;color:#e58a3a}p{color:#b8b8c8} |
| 35 | </style></head><body><main><b>Reasonix</b><p>Starting… / 正在启动…</p></main></body></html>`; |
| 36 | } |
| 37 |