From be0ccc1539b85ad11148bd1d81e2ab05c340e3f2 Mon Sep 17 00:00:00 2001 From: echo Date: Fri, 22 May 2026 17:36:24 +0200 Subject: [PATCH] Phase B: Eliminate duplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - workspace_nav helper replaces 4x duplicated nav bar patterns - Hoisted make_diagnostics_action_context call in process_clicks (computed once, reused 4 times) - Removed duplicate section header comments in workspaces.odin workspaces.odin: 328 → 280 lines (down 15%) All 156 tests pass. --- odin/src/gui/runtime.odin | 26 +++++----------- odin/src/gui/workspaces.odin | 57 ++++++++++++------------------------ 2 files changed, 26 insertions(+), 57 deletions(-) diff --git a/odin/src/gui/runtime.odin b/odin/src/gui/runtime.odin index ebbd988..4c14604 100644 --- a/odin/src/gui/runtime.odin +++ b/odin/src/gui/runtime.odin @@ -413,27 +413,15 @@ process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_expo } if clicked(clay.ID("btn_autosave_toggle")) { push_status(&app.status_msg, &app.action_log, toggle_autosave_with_message(&app.autosave_enabled)) } if clicked(clay.ID("btn_help")) { toggle_help_overlay(&app.show_help_overlay) } + + diag_ctx := make_diagnostics_action_context(&app.controller, &app.action_log, app.is_dirty, app.autosave_enabled, proj_ok, export_ok, autosave_secs, app.project_path, app.export_path, app.log_show_lines, app.log_oldest_first) if clicked(clay.ID("btn_log_reset")) { push_status(&app.status_msg, &app.action_log, reset_log_view_with_message(&app.log_show_lines, &app.log_oldest_first)) } if clicked(clay.ID("btn_log_clear")) { set_status(&app.status_msg, clear_action_log_with_message(&app.action_log)) } - if clicked(clay.ID("btn_log_report")) { - diag_ctx := make_diagnostics_action_context(&app.controller, &app.action_log, app.is_dirty, app.autosave_enabled, proj_ok, export_ok, autosave_secs, app.project_path, app.export_path, app.log_show_lines, app.log_oldest_first) - push_status(&app.status_msg, &app.action_log, write_session_report_with_message(diag_ctx)) - } - if clicked(clay.ID("btn_log_copy")) { - diag_ctx := make_diagnostics_action_context(&app.controller, &app.action_log, app.is_dirty, app.autosave_enabled, proj_ok, export_ok, autosave_secs, app.project_path, app.export_path, app.log_show_lines, app.log_oldest_first) - push_status(&app.status_msg, &app.action_log, copy_action_log_snapshot_with_message(diag_ctx)) - } - if clicked(clay.ID("btn_log_diag")) { - diag_ctx := make_diagnostics_action_context(&app.controller, &app.action_log, app.is_dirty, app.autosave_enabled, proj_ok, export_ok, autosave_secs, app.project_path, app.export_path, app.log_show_lines, app.log_oldest_first) - push_status(&app.status_msg, &app.action_log, write_diagnostics_with_message(diag_ctx)) - } - if clicked(clay.ID("btn_log_status_copy")) { - push_status(&app.status_msg, &app.action_log, copy_text_with_status(app.status_msg, "Copied status to clipboard")) - } - if clicked(clay.ID("btn_log_diag_copy")) { - diag_ctx := make_diagnostics_action_context(&app.controller, &app.action_log, app.is_dirty, app.autosave_enabled, proj_ok, export_ok, autosave_secs, app.project_path, app.export_path, app.log_show_lines, app.log_oldest_first) - push_status(&app.status_msg, &app.action_log, copy_diagnostics_with_message(diag_ctx)) - } + if clicked(clay.ID("btn_log_report")) { push_status(&app.status_msg, &app.action_log, write_session_report_with_message(diag_ctx)) } + if clicked(clay.ID("btn_log_copy")) { push_status(&app.status_msg, &app.action_log, copy_action_log_snapshot_with_message(diag_ctx)) } + if clicked(clay.ID("btn_log_diag")) { push_status(&app.status_msg, &app.action_log, write_diagnostics_with_message(diag_ctx)) } + if clicked(clay.ID("btn_log_status_copy")) { push_status(&app.status_msg, &app.action_log, copy_text_with_status(app.status_msg, "Copied status to clipboard")) } + if clicked(clay.ID("btn_log_diag_copy")) { push_status(&app.status_msg, &app.action_log, copy_diagnostics_with_message(diag_ctx)) } screen := app.controller.active_screen diff --git a/odin/src/gui/workspaces.odin b/odin/src/gui/workspaces.odin index 6d18f37..4583849 100644 --- a/odin/src/gui/workspaces.odin +++ b/odin/src/gui/workspaces.odin @@ -4,6 +4,18 @@ import clay "clay:." import "core:fmt" import "../core" +// ─── Shared Helpers ───────────────────────────────────────────── + +workspace_nav :: proc(id_prefix, pos_label: string) { + if clay.UI(clay.ID(fmt.tprintf("%sNav", id_prefix)))({ + layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(36)}, layoutDirection = .LeftToRight, childGap = 8, childAlignment = {y = .Center}}, + }) { + clay_body_text(pos_label, color = CLAY_ACCENT) + declare_button_small(fmt.tprintf("btn_%s_prev", id_prefix), "< Prev") + declare_button_small(fmt.tprintf("btn_%s_next", id_prefix), "Next >") + } +} + // ─── Script Workspace ──────────────────────────────────────────── declare_script_workspace :: proc(app: ^GUI_App_State) { page_count := len(app.controller.state.script.pages) @@ -18,27 +30,15 @@ declare_script_workspace :: proc(app: ^GUI_App_State) { } idx := clamp_script_cursor(page_count, app.summary_opts.script_page_cursor) - page := app.controller.state.script.pages[idx] + workspace_nav("script", fmt.tprintf("Page %d/%d", idx+1, page_count)) - // Nav bar - if clay.UI(clay.ID("ScriptNav"))({ - layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(36)}, layoutDirection = .LeftToRight, childGap = 8, childAlignment = {y = .Center}}, - }) { - clay_body_text(fmt.tprintf("Page %d/%d", idx+1, page_count), color = CLAY_ACCENT) - declare_button_small("btn_script_prev", "< Prev") - declare_button_small("btn_script_next", "Next >") - // Title info - title := app.controller.state.script.title - if len(title) > 50 { title = fmt.tprintf("%s...", title[:47]) } - clay_muted_text(title) - } + title := app.controller.state.script.title + if len(title) > 50 { title = fmt.tprintf("%s...", title[:47]) } + clay_muted_text(title) - // Detail card declare_script_detail(app) } -// ─── Panels Workspace ──────────────────────────────────────────── - // ─── Panels Workspace ──────────────────────────────────────────── declare_panels_workspace :: proc(app: ^GUI_App_State) { panel_count := count_script_panels(app.controller.state.script) @@ -52,20 +52,10 @@ declare_panels_workspace :: proc(app: ^GUI_App_State) { } idx := clamp_panel_cursor(panel_count, app.summary_opts.panel_cursor) - - if clay.UI(clay.ID("PanelsNav"))({ - layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(36)}, layoutDirection = .LeftToRight, childGap = 8, childAlignment = {y = .Center}}, - }) { - clay_body_text(fmt.tprintf("Panel %d/%d", idx+1, panel_count), color = CLAY_ACCENT) - declare_button_small("btn_panels_prev", "< Prev") - declare_button_small("btn_panels_next", "Next >") - } - + workspace_nav("panels", fmt.tprintf("Panel %d/%d", idx+1, panel_count)) declare_panels_detail(app) } -// ─── Layout Workspace ──────────────────────────────────────────── - // ─── Layout Workspace ──────────────────────────────────────────── declare_layout_workspace :: proc(app: ^GUI_App_State) { layout_count := len(app.controller.state.page_layouts) @@ -79,20 +69,10 @@ declare_layout_workspace :: proc(app: ^GUI_App_State) { } idx := clamp_layout_cursor(layout_count, app.summary_opts.layout_page_cursor) - - if clay.UI(clay.ID("LayoutNav"))({ - layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(36)}, layoutDirection = .LeftToRight, childGap = 8, childAlignment = {y = .Center}}, - }) { - clay_body_text(fmt.tprintf("Page %d/%d", idx+1, layout_count), color = CLAY_ACCENT) - declare_button_small("btn_layout_prev", "< Prev") - declare_button_small("btn_layout_next", "Next >") - } - + workspace_nav("layout", fmt.tprintf("Page %d/%d", idx+1, layout_count)) declare_layout_detail(app) } -// ─── Bubbles Workspace ─────────────────────────────────────────── - // ─── Bubbles Workspace ─────────────────────────────────────────── declare_bubbles_workspace :: proc(app: ^GUI_App_State) { layout_count := len(app.controller.state.page_layouts) @@ -109,6 +89,7 @@ declare_bubbles_workspace :: proc(app: ^GUI_App_State) { layout_val := app.controller.state.page_layouts[page_idx] panel_count := len(layout_val.panels) + // Bubbles needs dual nav (page + panel) if clay.UI(clay.ID("BubblesNav"))({ layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(36)}, layoutDirection = .LeftToRight, childGap = 8, childAlignment = {y = .Center}}, }) {