Add history calendar export view

This commit is contained in:
every_holiday
2026-06-30 11:03:17 +09:00
parent ae426244e0
commit 3ff33e40e3
4 changed files with 3506 additions and 255 deletions

View File

@@ -7,6 +7,7 @@ import (
"path/filepath"
"strconv"
"strings"
"time"
"vrc_osc_go/internal/common"
)
@@ -32,14 +33,20 @@ type VrcLogConfig struct {
}
type GUIConfig struct {
TopMost bool
FontSize int
TopMost bool
FontSize int
AutoUnmuteOnSelfLeave bool
RefreshIntervalValue int
RefreshIntervalUnit string
HistoryRegex string
HistoryFromDate string
HistoryToDate string
}
func Load(path string) (*Config, error) {
cfg := &Config{
OSC: OSCConfig{Host: "127.0.0.1", Port: 9001},
GUI: GUIConfig{FontSize: 18},
GUI: GUIConfig{FontSize: 18, RefreshIntervalValue: 2, RefreshIntervalUnit: "sec"},
}
if path == "" {
path = filepath.Join(common.RootDir(), "config", "config.toml")
@@ -114,9 +121,29 @@ func Load(path string) (*Config, error) {
if n, err := strconv.Atoi(val); err == nil && n > 0 {
cfg.GUI.FontSize = n
}
case "auto_unmute_on_self_leave":
cfg.GUI.AutoUnmuteOnSelfLeave = parseBool(val, cfg.GUI.AutoUnmuteOnSelfLeave)
case "refresh_interval_value":
if n, err := strconv.Atoi(val); err == nil && n > 0 {
cfg.GUI.RefreshIntervalValue = n
}
case "refresh_interval_unit":
cfg.GUI.RefreshIntervalUnit = normalizeRefreshIntervalUnit(val)
case "history_regex":
cfg.GUI.HistoryRegex = val
case "history_from":
cfg.GUI.HistoryFromDate = normalizeHistoryDate(val)
case "history_to":
cfg.GUI.HistoryToDate = normalizeHistoryDate(val)
}
}
}
if cfg.GUI.RefreshIntervalValue <= 0 {
cfg.GUI.RefreshIntervalValue = 2
}
cfg.GUI.RefreshIntervalUnit = normalizeRefreshIntervalUnit(cfg.GUI.RefreshIntervalUnit)
cfg.GUI.HistoryFromDate = normalizeHistoryDate(cfg.GUI.HistoryFromDate)
cfg.GUI.HistoryToDate = normalizeHistoryDate(cfg.GUI.HistoryToDate)
if cfg.VrcLog.GuestFile != "" {
if !filepath.IsAbs(cfg.VrcLog.GuestFile) {
cfg.VrcLog.GuestFile = filepath.Join(common.RootDir(), cfg.VrcLog.GuestFile)
@@ -222,13 +249,56 @@ func upsertGUISection(existing string, gui GUIConfig) string {
}
func appendGUISection(out *[]string, gui GUIConfig) {
gui.RefreshIntervalUnit = normalizeRefreshIntervalUnit(gui.RefreshIntervalUnit)
if gui.RefreshIntervalValue <= 0 {
gui.RefreshIntervalValue = 2
}
*out = append(*out,
"[gui]",
fmt.Sprintf("top_most = %t", gui.TopMost),
fmt.Sprintf("font_size = %d", gui.FontSize),
fmt.Sprintf("auto_unmute_on_self_leave = %t", gui.AutoUnmuteOnSelfLeave),
fmt.Sprintf("refresh_interval_value = %d", gui.RefreshIntervalValue),
fmt.Sprintf("refresh_interval_unit = %q", gui.RefreshIntervalUnit),
fmt.Sprintf("history_regex = %q", gui.HistoryRegex),
fmt.Sprintf("history_from = %q", gui.HistoryFromDate),
fmt.Sprintf("history_to = %q", gui.HistoryToDate),
)
}
func normalizeRefreshIntervalUnit(unit string) string {
switch strings.ToLower(strings.TrimSpace(unit)) {
case "min", "minute", "minutes":
return "min"
default:
return "sec"
}
}
func (g GUIConfig) RefreshInterval() time.Duration {
value := g.RefreshIntervalValue
if value <= 0 {
value = 2
}
switch normalizeRefreshIntervalUnit(g.RefreshIntervalUnit) {
case "min":
return time.Duration(value) * time.Minute
default:
return time.Duration(value) * time.Second
}
}
func normalizeHistoryDate(value string) string {
value = strings.TrimSpace(value)
if value == "" {
return ""
}
if _, err := time.Parse("2006-01-02", value); err != nil {
return ""
}
return value
}
func loadLines(path string) ([]string, error) {
data, err := os.ReadFile(path)
if err != nil {

View File

@@ -35,14 +35,14 @@ func TestLoadOSCValues(t *testing.T) {
func TestLoadGUIValues(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "config.toml")
if err := os.WriteFile(path, []byte("[gui]\ntop_most = true\nfont_size = 24\n"), 0o600); err != nil {
if err := os.WriteFile(path, []byte("[gui]\ntop_most = true\nfont_size = 24\nauto_unmute_on_self_leave = true\nrefresh_interval_value = 5\nrefresh_interval_unit = \"min\"\nhistory_regex = \"[wip]\"\nhistory_from = \"2026-01-01\"\nhistory_to = \"2026-01-31\"\n"), 0o600); err != nil {
t.Fatalf("WriteFile: %v", err)
}
cfg, err := Load(path)
if err != nil {
t.Fatalf("Load returned error: %v", err)
}
if cfg.GUI.TopMost != true || cfg.GUI.FontSize != 24 {
if cfg.GUI.TopMost != true || cfg.GUI.FontSize != 24 || cfg.GUI.AutoUnmuteOnSelfLeave != true || cfg.GUI.RefreshIntervalValue != 5 || cfg.GUI.RefreshIntervalUnit != "min" || cfg.GUI.HistoryRegex != "[wip]" || cfg.GUI.HistoryFromDate != "2026-01-01" || cfg.GUI.HistoryToDate != "2026-01-31" {
t.Fatalf("unexpected gui values: %+v", cfg.GUI)
}
}
@@ -54,14 +54,14 @@ func TestSaveGUIUpdatesSection(t *testing.T) {
if err := os.WriteFile(path, initial, 0o600); err != nil {
t.Fatalf("WriteFile: %v", err)
}
if err := SaveGUI(path, GUIConfig{TopMost: true, FontSize: 26}); err != nil {
if err := SaveGUI(path, GUIConfig{TopMost: true, FontSize: 26, AutoUnmuteOnSelfLeave: true, RefreshIntervalValue: 3, RefreshIntervalUnit: "sec", HistoryRegex: "[wip]", HistoryFromDate: "2026-01-01", HistoryToDate: "2026-01-31"}); err != nil {
t.Fatalf("SaveGUI returned error: %v", err)
}
cfg, err := Load(path)
if err != nil {
t.Fatalf("Load returned error: %v", err)
}
if cfg.GUI.TopMost != true || cfg.GUI.FontSize != 26 {
if cfg.GUI.TopMost != true || cfg.GUI.FontSize != 26 || cfg.GUI.AutoUnmuteOnSelfLeave != true || cfg.GUI.RefreshIntervalValue != 3 || cfg.GUI.RefreshIntervalUnit != "sec" || cfg.GUI.HistoryRegex != "[wip]" || cfg.GUI.HistoryFromDate != "2026-01-01" || cfg.GUI.HistoryToDate != "2026-01-31" {
t.Fatalf("unexpected saved gui values: %+v", cfg.GUI)
}
data, err := os.ReadFile(path)