File size: 3,037 Bytes
774fe36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
/* ===== Task Timer ===== */
window.NAITimer = {
    startTime: null,
    timerInterval: null,
    history: [],       // recent task durations for ETA
    MAX_HISTORY: 10,

    start() {
        this.startTime = Date.now();
        this._updateDisplay();
        this.timerInterval = setInterval(() => this._updateDisplay(), 100);
        this._showTimer(true);
    },

    stop(completed = true) {
        if (this.startTime && completed) {
            const duration = Date.now() - this.startTime;
            this.history.push(duration);
            if (this.history.length > this.MAX_HISTORY) this.history.shift();
        }
        clearInterval(this.timerInterval);
        this.timerInterval = null;
        this.startTime = null;

        // Show final time briefly, then hide
        setTimeout(() => this._showTimer(false), 3000);
    },

    _getAvgDuration() {
        if (this.history.length === 0) return 0;
        return this.history.reduce((a, b) => a + b, 0) / this.history.length;
    },

    _formatTime(ms) {
        const s = Math.floor(ms / 1000);
        const m = Math.floor(s / 60);
        const sec = s % 60;
        const tenths = Math.floor((ms % 1000) / 100);
        if (m > 0) return `${m}:${String(sec).padStart(2, '0')}.${tenths}`;
        return `${sec}.${tenths}s`;
    },

    _updateDisplay() {
        const el = document.getElementById('task-timer');
        if (!el || !this.startTime) return;

        const elapsed = Date.now() - this.startTime;
        const avg = this._getAvgDuration();

        let text = this._formatTime(elapsed);
        if (avg > 0) {
            const remaining = Math.max(0, avg - elapsed);
            if (remaining > 0) {
                text += ` / 预计 ${this._formatTime(avg)}`;
            } else {
                text += ` / 预计 ${this._formatTime(avg)}`;
            }
        }

        el.textContent = text;

        // Progress bar
        const bar = document.getElementById('task-timer-bar');
        if (bar && avg > 0) {
            const pct = Math.min(100, (elapsed / avg) * 100);
            bar.style.width = pct + '%';
            bar.className = 'task-timer-bar-fill' + (pct >= 100 ? ' overtime' : '');
        }
    },

    _showTimer(show) {
        const wrap = document.getElementById('task-timer-wrap');
        if (wrap) wrap.style.display = show ? 'flex' : 'none';
    },

    init() {
        // Create timer UI in header
        const headerRight = document.querySelector('.header-right');
        if (!headerRight) return;

        const wrap = document.createElement('div');
        wrap.id = 'task-timer-wrap';
        wrap.className = 'task-timer-wrap';
        wrap.style.display = 'none';
        wrap.innerHTML = `
            <div class="task-timer-bar-bg">
                <div id="task-timer-bar" class="task-timer-bar-fill" style="width:0%"></div>
            </div>
            <span id="task-timer" class="task-timer-text"></span>
        `;

        headerRight.insertBefore(wrap, headerRight.firstChild);
    },
};