-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathshared.js
More file actions
121 lines (101 loc) · 3.45 KB
/
shared.js
File metadata and controls
121 lines (101 loc) · 3.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/**
* SessionPulse — Shared Utilities (ES Module)
* v6.0.0
*
* Canonical formatting, badge mapping, and state helpers for SessionPulse UIs.
*
* NOTE: The built-in HTML files (timer_dock, timer_overlay, etc.) inline their
* own copies of these functions because OBS Browser Sources load via file://
* protocol, which doesn't support ES module imports from relative paths in CEF.
*
* This module is provided for:
* - External tool builders creating custom dashboards or bots
* - HTTP-served UIs (e.g., served via `python -m http.server`)
* - Reference implementation of formatting logic
*
* Usage: import { formatTime, getBadgeInfo } from './shared.js';
*/
// ── Time Formatting ──
export function formatTime(seconds) {
if (seconds == null || seconds < 0) return '--:--';
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
if (h > 0) return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
}
export function formatDuration(seconds) {
if (!seconds || seconds <= 0) return '0m';
const mins = Math.floor(seconds / 60);
if (mins < 60) return `${mins}m`;
const h = Math.floor(mins / 60);
const rm = mins % 60;
return rm === 0 ? `${h}h` : `${h}h ${rm}m`;
}
export function formatOvertime(seconds) {
return '+' + formatTime(seconds || 0);
}
// ── Badge / Color Mapping ──
export const BADGE_CLASSES = {
'Focus': 'focus',
'Short Break': 'short-break',
'Long Break': 'long-break',
};
export const SESSION_ICONS = {
'Focus': '📚',
'Short Break': '☕',
'Long Break': '🧘',
'Stopwatch': '⏱️',
'Countdown': '⏳',
};
// ── State Helpers ──
export function getBadgeInfo(state) {
if (!state || !state.is_running) return { text: 'Ready', cls: 'idle' };
const { session_type, timer_mode, is_paused, is_overtime,
custom_segment_name } = state;
const mode = timer_mode || 'pomodoro';
let text = session_type || 'Ready';
let cls = BADGE_CLASSES[session_type] || 'idle';
if (is_overtime) {
text = (session_type || 'Session') + ' · Overtime';
cls = 'overtime';
} else if (mode === 'stopwatch') {
text = 'Stopwatch'; cls = 'stopwatch';
} else if (mode === 'countdown') {
text = 'Countdown'; cls = 'countdown';
} else if (mode === 'custom' && custom_segment_name) {
text = custom_segment_name; cls = 'custom';
}
if (is_paused && !is_overtime) text += ' · Paused';
return { text, cls };
}
export function getProgress(state) {
if (!state) return 0;
const { current_time, total_time, timer_mode, is_overtime, progress_percent } = state;
// Use pre-computed value if available (v5.1+)
if (progress_percent != null) return progress_percent;
if (is_overtime) return 100;
const mode = timer_mode || 'pomodoro';
if (mode === 'stopwatch') return ((current_time % 3600) / 3600) * 100;
if (total_time > 0) return ((total_time - current_time) / total_time) * 100;
return 0;
}
// ── Polling Helper ──
export function createPoller(url, callback, interval = 500) {
let lastState = null;
async function poll() {
try {
const resp = await fetch(url + '?t=' + Date.now());
if (resp.ok) {
lastState = await resp.json();
callback(lastState);
} else {
callback(null);
}
} catch (e) {
callback(lastState);
}
}
poll();
return setInterval(poll, interval);
}