chore(frontend): add diagnostics before app split
- main.js: global error handlers (error + unhandledrejection), try/catch around App mount with inline fallback UI - bindings_logging.go: FrontendLog() and LogStartupStep() backend bindings for persistent application logging - App.js: FrontendLog() and LogStartupStep() Wails JS bindings - docs/frontend-architecture.md: component architecture, state ownership rules, communication patterns, logging guide, troubleshooting All existing code untouched. This is a pure additive step that enables blank-window debugging before any App.svelte extraction begins.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"verstak/internal/core/config"
|
||||
)
|
||||
|
||||
// appLogPath returns the path to the application log file.
|
||||
// Uses ~/.local/state/verstak/logs/verstak.log on Linux,
|
||||
// or falls back to ~/.config/verstak/logs/verstak.log.
|
||||
func appLogPath() string {
|
||||
// Prefer XDG_STATE_HOME, then fallback to config dir
|
||||
stateDir := os.Getenv("XDG_STATE_HOME")
|
||||
if stateDir == "" {
|
||||
home, err := os.UserHomeDir()
|
||||
if err == nil {
|
||||
stateDir = filepath.Join(home, ".local", "state")
|
||||
}
|
||||
}
|
||||
if stateDir != "" {
|
||||
dir := filepath.Join(stateDir, "verstak", "logs")
|
||||
if err := os.MkdirAll(dir, 0o755); err == nil {
|
||||
return filepath.Join(dir, "verstak.log")
|
||||
}
|
||||
}
|
||||
// Fallback to config dir
|
||||
cfgDir, err := config.EnsureConfigDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
dir := filepath.Join(cfgDir, "logs")
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(dir, "verstak.log")
|
||||
}
|
||||
|
||||
// appLog writes a timestamped line to the application log file.
|
||||
func appLog(level, msg string) {
|
||||
logPath := appLogPath()
|
||||
if logPath == "" {
|
||||
log.Printf("[%s] %s", level, msg)
|
||||
return
|
||||
}
|
||||
line := fmt.Sprintf("[%s] [%s] %s\n", time.Now().Format("2006-01-02T15:04:05"), level, msg)
|
||||
f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
log.Printf("[%s] %s", level, msg)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
f.WriteString(line)
|
||||
}
|
||||
|
||||
// FrontendLog receives log messages from the frontend runtime.
|
||||
// Called from JS via Wails binding. Works even if vault is not open.
|
||||
// level: "info", "warn", "error"
|
||||
// message: human-readable message
|
||||
// stack: JS stack trace (optional, only for errors)
|
||||
func (a *App) FrontendLog(level, message, stack string) {
|
||||
msg := "[frontend] " + message
|
||||
if stack != "" {
|
||||
msg += "\n stack: " + stack
|
||||
}
|
||||
// Always log to Go's standard logger (visible in dev mode console)
|
||||
log.Printf("[frontend][%s] %s", level, message)
|
||||
// Persist to log file
|
||||
appLog("frontend-"+level, msg)
|
||||
}
|
||||
|
||||
// LogStartupStep logs a startup diagnostic step.
|
||||
// Called from Go side to trace initialization progress.
|
||||
func (a *App) LogStartupStep(step string, success bool, detail string) {
|
||||
status := "ok"
|
||||
if !success {
|
||||
status = "fail"
|
||||
}
|
||||
msg := fmt.Sprintf("[startup] %s: %s", step, status)
|
||||
if detail != "" {
|
||||
msg += " — " + detail
|
||||
}
|
||||
log.Print(msg)
|
||||
appLog("startup", msg)
|
||||
}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -19,7 +19,7 @@
|
||||
background: #13131f;
|
||||
}
|
||||
</style>
|
||||
<script type="module" crossorigin src="/assets/main-CzfuqGWF.js"></script>
|
||||
<script type="module" crossorigin src="/assets/main-Da3BSkUM.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/main-bQpH1es2.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user