From 4c466d78d1ba1394965caba40cc7af396b15f3ef Mon Sep 17 00:00:00 2001 From: DanS Date: Sat, 4 Jul 2026 17:58:47 -0500 Subject: [PATCH] feat(debug): per-tab screenshot subfolders + overwrite + Open location button - Organize sweep output into per-tab subfolders: /screenshots//.png (was one timestamped folder with idx_skin_tab.png names). - Fixed folder (no timestamp): subsequent sweeps overwrite the existing PNGs in place instead of creating a new directory each run. - Add an "Open location" TactileButton next to "Run screenshot sweep" that opens the screenshots folder via Platform::openFolder(app->screenshotDir()). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/app.h | 1 + src/app_network.cpp | 26 +++++++++++++------------- src/ui/pages/settings_page.cpp | 3 +++ src/util/i18n.cpp | 3 ++- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/app.h b/src/app.h index 9e52897..c2a994f 100644 --- a/src/app.h +++ b/src/app.h @@ -317,6 +317,7 @@ public: bool wantsScreenshotThisFrame() const { return sweep_capture_this_frame_; } const std::string& screenshotSweepPath() const { return sweep_current_path_; } void onScreenshotCaptured(); + std::string screenshotDir() const; // /screenshots (fixed; sweeps overwrite in place) // Dialog triggers (used by settings page to open modal dialogs) void showImportKeyDialog() { show_import_key_ = true; } diff --git a/src/app_network.cpp b/src/app_network.cpp index 5bac3f1..95d1844 100644 --- a/src/app_network.cpp +++ b/src/app_network.cpp @@ -766,6 +766,11 @@ static const char* sweepPageName(ui::NavPage page) } } +std::string App::screenshotDir() const +{ + return (std::filesystem::path(util::Platform::getObsidianDragonDir()) / "screenshots").string(); +} + void App::startScreenshotSweep() { if (screenshot_sweep_active_) return; @@ -780,14 +785,10 @@ void App::startScreenshotSweep() } if (sweep_skins_.empty() || sweep_pages_.empty()) return; - // Timestamped output folder under the config dir. - std::time_t now = std::time(nullptr); - char stamp[32] = "sweep"; - if (std::tm* tmv = std::localtime(&now)) std::strftime(stamp, sizeof(stamp), "%Y%m%d_%H%M%S", tmv); - namespace fs = std::filesystem; - fs::path dir = fs::path(util::Platform::getObsidianDragonDir()) / "screenshots" / (std::string("sweep_") + stamp); - std::error_code ec; fs::create_directories(dir, ec); - sweep_dir_ = dir.string(); + // Fixed output folder (organized into per-tab subfolders below); subsequent sweeps overwrite the + // existing PNGs in place rather than creating a new folder each time. + sweep_dir_ = screenshotDir(); + std::error_code ec; std::filesystem::create_directories(sweep_dir_, ec); sweep_saved_skin_ = ui::schema::SkinManager::instance().activeSkinId(); sweep_saved_page_ = current_page_; @@ -806,11 +807,10 @@ void App::applySweepTarget() { current_page_ = sweep_pages_[sweep_page_idx_]; page_alpha_ = 1.0f; // skip the page-switch fade so the shot isn't captured mid-animation - int idx = sweep_skin_idx_ * static_cast(sweep_pages_.size()) + sweep_page_idx_; - char name[160]; - snprintf(name, sizeof(name), "%03d_%s_%s.png", idx, - sweep_skins_[sweep_skin_idx_].c_str(), sweepPageName(current_page_)); - sweep_current_path_ = (std::filesystem::path(sweep_dir_) / name).string(); + // Organized as //.png — one subfolder per tab, one PNG per theme, + // overwritten in place on the next sweep. (writePng in main.cpp creates the parent subfolder.) + sweep_current_path_ = (std::filesystem::path(sweep_dir_) / sweepPageName(current_page_) + / (sweep_skins_[sweep_skin_idx_] + ".png")).string(); sweep_settle_frames_ = 4; // let the new skin (TOML reload / acrylic recapture) + page settle sweep_capture_this_frame_ = false; } diff --git a/src/ui/pages/settings_page.cpp b/src/ui/pages/settings_page.cpp index 85da49b..da3f949 100644 --- a/src/ui/pages/settings_page.cpp +++ b/src/ui/pages/settings_page.cpp @@ -2463,6 +2463,9 @@ void RenderSettingsPage(App* app) { ImGui::Dummy(ImVec2(0, Layout::spacingXs())); if (TactileButton(TR("screenshot_sweep"), ImVec2(0, 0), S.resolveFont("button"))) app->startScreenshotSweep(); + ImGui::SameLine(); + if (TactileButton(TR("screenshot_open_dir"), ImVec2(0, 0), S.resolveFont("button"))) + util::Platform::openFolder(app->screenshotDir()); ImGui::Dummy(ImVec2(0, Layout::spacingSm())); ImGui::Separator(); ImGui::Dummy(ImVec2(0, Layout::spacingSm())); diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index b86ab41..59579a3 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -254,7 +254,8 @@ void I18n::loadBuiltinEnglish() strings_["stop_external"] = "Stop external daemon"; strings_["verbose_logging"] = "Verbose logging"; strings_["screenshot_sweep"] = "Run screenshot sweep"; - strings_["screenshot_sweep_desc"] = "Cycles every theme across every tab and saves a screenshot of each to a timestamped folder in the config directory. Runs for a few seconds; the path is shown when it finishes."; + strings_["screenshot_open_dir"] = "Open location"; + strings_["screenshot_sweep_desc"] = "Cycles every theme across every tab and saves a screenshot of each into per-tab subfolders under the config directory's screenshots folder (overwriting the previous sweep). Runs for a few seconds."; strings_["mine_when_idle"] = "Mine when idle"; strings_["setup_wizard"] = "Run Setup Wizard...";