diff --git a/src/ui/windows/console_tab.cpp b/src/ui/windows/console_tab.cpp index 4df309f..228e8d0 100644 --- a/src/ui/windows/console_tab.cpp +++ b/src/ui/windows/console_tab.cpp @@ -59,15 +59,16 @@ ConsoleTab* s_rpc_trace_console = nullptr; std::string rpcTraceTimestamp() { + // Called on RPC worker threads. std::localtime shares a process-wide static tm, so a private + // mutex here can't stop another thread's localtime call from clobbering it between the call and + // the copy. Use the reentrant variant into a local tm instead (matches the codebase pattern). std::time_t now = std::time(nullptr); std::tm localTime{}; - static std::mutex timeMutex; - { - std::lock_guard lock(timeMutex); - if (const std::tm* current = std::localtime(&now)) { - localTime = *current; - } - } +#ifdef _WIN32 + localtime_s(&localTime, &now); +#else + localtime_r(&now, &localTime); +#endif char buffer[16]; std::strftime(buffer, sizeof(buffer), "%H:%M:%S", &localTime); @@ -297,6 +298,10 @@ void ConsoleTab::render(ConsoleCommandExecutor& exec) // model_) and the selection/scroll state are touched only on this (main) thread. drainModel(); + // Compute the filtered visible-line set once per frame, BEFORE the toolbar — so its " matches" + // label reflects the current frame (not last frame's stale count). renderOutput reuses the result. + computeVisibleLines(has_text_filter_, filter_lower_); + // Main console layout ImGui::BeginChild("ConsoleContainer", ImVec2(0, 0), false, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar); @@ -756,11 +761,8 @@ void ConsoleTab::renderOutput() output_scroll_y_ = ImGui::GetScrollY(); scanline_rows_.clear(); - // Build the filtered visible-line index list BEFORE mouse handling (screenToTextPos maps - // through visible_indices_). Also yields the text-filter state used for highlighting. - bool has_text_filter = false; - std::string filter_lower; - computeVisibleLines(has_text_filter, filter_lower); + // visible_indices_ / has_text_filter_ / filter_lower_ were already computed once at the top of + // render() (before the toolbar). screenToTextPos maps through visible_indices_, so it's ready. // Calculate wrapped heights AND build sub-row segments for each visible line. Each // segment records which bytes of the source text appear on that visual row, so @@ -785,7 +787,7 @@ void ConsoleTab::renderOutput() // Draw the visible lines: accent bars, JSON indent guides, selection + filter highlight, // scanline capture, and the text itself. - drawVisibleLines(padX, line_height, has_text_filter, filter_lower); + drawVisibleLines(padX, line_height, has_text_filter_, filter_lower_); ImGui::Unindent(padX); ImGui::PopStyleVar(); @@ -808,7 +810,7 @@ void ConsoleTab::renderOutput() } // Filter indicator (text filter only — daemon toggle is already visible in toolbar) - if (has_text_filter) { + if (has_text_filter_) { char filterBuf[128]; snprintf(filterBuf, sizeof(filterBuf), TR("console_showing_lines"), static_cast(visible_indices_.size()), model_.size()); @@ -1685,8 +1687,17 @@ void ConsoleTab::drainModel() // with them so the highlight stays on the text the user selected. selection_.shiftForEviction(static_cast(dr.popped)); - // Track new output that arrived while the user is scrolled up (for the indicator). - scroll_.onLinesAdded(static_cast(dr.added)); + // Track new output that arrived while the user is scrolled up (for the "N new lines" indicator). + // Count only the newly-added lines that pass the active filter, so the indicator isn't inflated + // by lines the current filter / errors-only view hides — the user wouldn't see those on jumping + // to the bottom. (The new lines are the last dr.added entries; eviction is from the front.) + ConsoleOutputFilter f{filter_text_, s_daemon_messages_enabled, s_errors_only_enabled, + s_rpc_trace_enabled, s_app_messages_enabled}; + const int n = static_cast(model_.size()); + int visibleAdded = 0; + for (int i = std::max(0, n - static_cast(dr.added)); i < n; i++) + if (consoleLinePassesFilter(model_[i].text, model_[i].channel, f)) ++visibleAdded; + scroll_.onLinesAdded(visibleAdded); } void ConsoleTab::addRpcTraceLine(const std::string& source, const std::string& method) diff --git a/src/ui/windows/console_tab.h b/src/ui/windows/console_tab.h index 021be47..921fc41 100644 --- a/src/ui/windows/console_tab.h +++ b/src/ui/windows/console_tab.h @@ -175,6 +175,8 @@ private: std::string context_token_; // hash/address under the cursor at right-click mutable std::vector visible_indices_; // Cached for selection mapping bool folding_active_ = true; // fold UI shown only in the unfiltered (foldable) view + bool has_text_filter_ = false; // computed once per frame (before the toolbar draws it) + std::string filter_lower_; // lowercased filter needle for match highlighting // Wrap layout for the visible lines (segments + per-line heights + cumulative Y), // recomputed each frame by the pure BuildConsoleLayout (console_text_layout.h) and