8501fd074c
Go TUI sleep timer with modular architecture. - Config: YAML-based, profile inheritance, auto WM detection - Engine: timer, executor, stage pipeline (pre→timer→post) - Modules: workspace (niri/hyprland/kde), media, lock, kill, notify, sound, brightness, mute, custom, script - TUI: bubbletea app with profiles, module toggles, progress bar - CLI: run, list-profiles, export, import, history, stats - History: JSONL log with statistics - QR: config export as ASCII QR
75 lines
1.5 KiB
Go
75 lines
1.5 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
)
|
|
|
|
type StageRunner struct {
|
|
Timer *Timer
|
|
PreCmds []string
|
|
PostCmds []string
|
|
Events chan TimerEvent
|
|
ExecTimeout time.Duration
|
|
}
|
|
|
|
func NewStageRunner(timer *Timer, pre, post []string) *StageRunner {
|
|
return &StageRunner{
|
|
Timer: timer,
|
|
PreCmds: pre,
|
|
PostCmds: post,
|
|
Events: make(chan TimerEvent, 100),
|
|
ExecTimeout: 30 * time.Second,
|
|
}
|
|
}
|
|
|
|
func (sr *StageRunner) Run(ctx context.Context) {
|
|
defer close(sr.Events)
|
|
|
|
// PreCmds
|
|
for _, cmd := range sr.PreCmds {
|
|
result := Execute(ctx, cmd, sr.ExecTimeout)
|
|
if result.Err != nil {
|
|
sr.Events <- TimerEvent{Stage: StagePre, Status: StatusError, Done: true}
|
|
return
|
|
}
|
|
sr.Events <- TimerEvent{Stage: StagePre, Status: StatusRunning}
|
|
}
|
|
|
|
// Timer
|
|
timerEvents := make(chan TimerEvent, 100)
|
|
go sr.Timer.Start(ctx, timerEvents)
|
|
|
|
timerDone := false
|
|
timerLoop:
|
|
for {
|
|
select {
|
|
case ev := <-timerEvents:
|
|
sr.Events <- ev
|
|
if ev.Done {
|
|
timerDone = true
|
|
break timerLoop
|
|
}
|
|
case <-ctx.Done():
|
|
break timerLoop
|
|
}
|
|
}
|
|
|
|
// PostCmds (only on normal timer completion)
|
|
if timerDone && sr.Timer.Status == StatusDone {
|
|
for _, cmd := range sr.PostCmds {
|
|
result := Execute(ctx, cmd, sr.ExecTimeout)
|
|
if result.Err != nil {
|
|
sr.Events <- TimerEvent{Stage: StagePost, Status: StatusError, Done: true}
|
|
return
|
|
}
|
|
sr.Events <- TimerEvent{Stage: StagePost, Status: StatusRunning}
|
|
}
|
|
sr.Events <- TimerEvent{Status: StatusDone, Done: true}
|
|
}
|
|
}
|
|
|
|
func (sr *StageRunner) Stop() {
|
|
sr.Timer.Stop()
|
|
}
|