comic/odin/src/gui/diagnostics.odin
2026-05-21 06:10:32 +02:00

160 lines
6.0 KiB
Odin

package gui
import "core:fmt"
import "core:os"
import "core:path/filepath"
import rl "vendor:raylib"
import "../ui"
build_diagnostics_snapshot :: proc(controller: ui.App_Controller, is_dirty, autosave_enabled, project_ok, export_ok: bool, autosave_secs: int, project_path, export_path: string, log_show_lines: i32, log_oldest_first: bool) -> string {
log_order := "newest"
if log_oldest_first {
log_order = "oldest"
}
return fmt.aprintf("screen=%s workflow=%v next=%s dirty=%s autosave=%s(%ds) content=pages:%d,panels:%d,layouts:%d,chars:%d paths=P:%s,E:%s project=%s export=%s log=%d,%s uptime=%.1fs", ui.screen_name(controller.active_screen), controller.state.workflow.current_step, gui_next_hint(controller), yn(is_dirty), yn(autosave_enabled), autosave_secs, len(controller.state.script.pages), len(controller.state.panel_images), len(controller.state.page_layouts), len(controller.state.characters), yn(project_ok), yn(export_ok), project_path, export_path, log_show_lines, log_order, rl.GetTime())
}
build_action_log_snapshot :: proc(log: Action_Log) -> string {
if log.count == 0 {
return fmt.aprintf("(action log empty)")
}
now := rl.GetTime()
max_lines := len(log.entries)
if log.count < max_lines {
max_lines = log.count
}
out := ""
for line in 0..<max_lines {
idx := (log.count - 1 - line) % len(log.entries)
if idx < 0 {
idx += len(log.entries)
}
entry := fmt.aprintf("[%2.0fs] %s", now-log.entry_times[idx], log.entries[idx])
if line == 0 {
out = entry
} else {
prev := out
out = fmt.aprintf("%s\n%s", prev, entry)
delete(prev)
delete(entry)
}
}
return out
}
build_session_report :: proc(controller: ui.App_Controller, log: Action_Log, is_dirty, autosave_enabled, project_ok, export_ok: bool, autosave_secs: int, project_path, export_path: string, log_show_lines: i32, log_oldest_first: bool) -> string {
diag := build_diagnostics_snapshot(controller, is_dirty, autosave_enabled, project_ok, export_ok, autosave_secs, project_path, export_path, log_show_lines, log_oldest_first)
log_text := build_action_log_snapshot(log)
report := fmt.aprintf("# comic-odin gui session report\n\n[meta]\ngenerated_uptime=%.1fs\n\n[diagnostics]\n%s\n\n[action_log]\n%s\n", rl.GetTime(), diag, log_text)
delete(diag)
delete(log_text)
return report
}
Diagnostics_Action_Context :: struct {
controller: ^ui.App_Controller,
action_log: ^Action_Log,
is_dirty: bool,
autosave_enabled: bool,
project_ok: bool,
export_ok: bool,
autosave_secs: int,
project_path: string,
export_path: string,
log_show_lines: i32,
log_oldest_first: bool,
}
make_diagnostics_action_context :: proc(controller: ^ui.App_Controller, action_log: ^Action_Log, is_dirty, autosave_enabled, project_ok, export_ok: bool, autosave_secs: int, project_path, export_path: string, log_show_lines: i32, log_oldest_first: bool) -> Diagnostics_Action_Context {
return Diagnostics_Action_Context{
controller = controller,
action_log = action_log,
is_dirty = is_dirty,
autosave_enabled = autosave_enabled,
project_ok = project_ok,
export_ok = export_ok,
autosave_secs = autosave_secs,
project_path = project_path,
export_path = export_path,
log_show_lines = log_show_lines,
log_oldest_first = log_oldest_first,
}
}
diagnostics_path_for_project :: proc(project_path: string) -> string {
dir, _ := filepath.split(project_path)
if len(dir) == 0 {
dir = "./"
}
parts := []string{dir, "gui_diagnostics.txt"}
joined, err := filepath.join(parts)
if err != nil {
return "./gui_diagnostics.txt"
}
return joined
}
session_report_path_for_project :: proc(project_path: string) -> string {
dir, _ := filepath.split(project_path)
if len(dir) == 0 {
dir = "./"
}
parts := []string{dir, "gui_session_report.txt"}
joined, err := filepath.join(parts)
if err != nil {
return "./gui_session_report.txt"
}
return joined
}
write_diagnostics_file :: proc(project_path, diag: string) -> string {
diag_path := diagnostics_path_for_project(project_path)
defer delete(diag_path)
if err := os.write_entire_file(diag_path, diag); err == nil {
return fmt.aprintf("Wrote diagnostics file: %s", diag_path)
}
return fmt.aprintf("Failed writing diagnostics file")
}
write_session_report_file :: proc(project_path, report: string) -> string {
report_path := session_report_path_for_project(project_path)
defer delete(report_path)
if err := os.write_entire_file(report_path, report); err == nil {
return fmt.aprintf("Wrote session report: %s", report_path)
}
return fmt.aprintf("Failed writing session report")
}
write_session_report_with_message :: proc(ctx: Diagnostics_Action_Context) -> string {
report := build_session_report(ctx.controller^, ctx.action_log^, ctx.is_dirty, ctx.autosave_enabled, ctx.project_ok, ctx.export_ok, ctx.autosave_secs, ctx.project_path, ctx.export_path, ctx.log_show_lines, ctx.log_oldest_first)
msg := write_session_report_file(ctx.project_path, report)
delete(report)
return msg
}
write_diagnostics_with_message :: proc(ctx: Diagnostics_Action_Context) -> string {
diag := build_diagnostics_snapshot(ctx.controller^, ctx.is_dirty, ctx.autosave_enabled, ctx.project_ok, ctx.export_ok, ctx.autosave_secs, ctx.project_path, ctx.export_path, ctx.log_show_lines, ctx.log_oldest_first)
msg := write_diagnostics_file(ctx.project_path, diag)
delete(diag)
return msg
}
copy_diagnostics_with_message :: proc(ctx: Diagnostics_Action_Context) -> string {
diag := build_diagnostics_snapshot(ctx.controller^, ctx.is_dirty, ctx.autosave_enabled, ctx.project_ok, ctx.export_ok, ctx.autosave_secs, ctx.project_path, ctx.export_path, ctx.log_show_lines, ctx.log_oldest_first)
msg := copy_text_with_status(diag, "Copied diagnostics snapshot")
delete(diag)
return msg
}
copy_action_log_snapshot_with_message :: proc(ctx: Diagnostics_Action_Context) -> string {
log_text := build_action_log_snapshot(ctx.action_log^)
msg := copy_text_with_status(log_text, "Copied action log snapshot")
delete(log_text)
return msg
}
copy_text_with_status :: proc(text, status: string) -> string {
rl.SetClipboardText(fmt.ctprintf("%s", text))
return status
}