diff --git a/gui/log_viewer.py b/gui/log_viewer.py
index d76d5b3..62f31a7 100644
--- a/gui/log_viewer.py
+++ b/gui/log_viewer.py
@@ -3,7 +3,7 @@ Log Viewer - Real-time log display and monitoring
"""
from PyQt5.QtWidgets import (
- QWidget, QVBoxLayout, QTextEdit, QLabel, QSizePolicy
+ QWidget, QVBoxLayout, QTextEdit, QLabel, QSizePolicy, QTabWidget
)
from PyQt5.QtCore import Qt, pyqtSignal, QTimer
from PyQt5.QtGui import QTextCursor, QFont, QTextCharFormat
@@ -24,6 +24,7 @@ class LogViewer(QWidget):
# Lightweight append-only log viewer. Logs are provided by the
# translation worker via signals so we don't poll files or provide
# controls here. This keeps the UI responsive.
+ self._error_count = 0
self.init_ui()
def init_ui(self):
@@ -49,20 +50,56 @@ class LogViewer(QWidget):
# content lines up visually.
layout.setSpacing(8)
- # Log display area (append-only)
- self.log_display = QTextEdit()
- self.log_display.setReadOnly(True)
- self.log_display.setFont(QFont("Consolas", 10))
- self.log_display.setStyleSheet("""
+ # Tab widget for All / Errors views
+ self._tab_widget = QTabWidget()
+ self._tab_widget.setStyleSheet("""
+ QTabWidget::pane {
+ border: 1px solid #555555;
+ background-color: #1e1e1e;
+ }
+ QTabBar::tab {
+ background-color: #2d2d2d;
+ color: #cccccc;
+ padding: 5px 12px;
+ border: 1px solid #555555;
+ border-bottom: none;
+ }
+ QTabBar::tab:selected {
+ background-color: #1e1e1e;
+ color: #ffffff;
+ }
+ QTabBar::tab:hover {
+ background-color: #3a3a3a;
+ }
+ """)
+ self._tab_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+
+ _log_font = QFont("Consolas", 10)
+ _text_edit_style = """
QTextEdit {
background-color: #1e1e1e;
color: #ffffff;
- border: 1px solid #555555;
+ border: none;
}
- """)
- # Make the log display expand to take available vertical space
+ """
+
+ # --- All tab ---
+ self.log_display = QTextEdit()
+ self.log_display.setReadOnly(True)
+ self.log_display.setFont(_log_font)
+ self.log_display.setStyleSheet(_text_edit_style)
self.log_display.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- layout.addWidget(self.log_display, 1)
+ self._tab_widget.addTab(self.log_display, "All")
+
+ # --- Errors tab (MISMATCH + API_ERROR only) ---
+ self.error_display = QTextEdit()
+ self.error_display.setReadOnly(True)
+ self.error_display.setFont(_log_font)
+ self.error_display.setStyleSheet(_text_edit_style)
+ self.error_display.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ self._tab_widget.addTab(self.error_display, "Errors")
+
+ layout.addWidget(self._tab_widget, 1)
# Lightweight status label
self.status_label = QLabel("Ready")
@@ -123,6 +160,10 @@ class LogViewer(QWidget):
# Reset the char format so the red color from previous [MISMATCH]
# HTML spans doesn't bleed into new plain-text appends.
self.log_display.setCurrentCharFormat(QTextCharFormat())
+ self.error_display.clear()
+ self.error_display.setCurrentCharFormat(QTextCharFormat())
+ self._error_count = 0
+ self._tab_widget.setTabText(1, "Errors")
self.status_label.setText("Log cleared")
# Reset tail file pointer and buffer if active
self._tail_buffer = ""
@@ -247,27 +288,35 @@ class LogViewer(QWidget):
def append_log_message(self, message):
"""Append a message to the log display.
- Lines containing '[MISMATCH]' are rendered in red to highlight
- translation batch failures.
+ Lines containing '[MISMATCH]' or '[API_ERROR]' are also routed to the
+ Errors filter tab so they are never lost in a fast-scrolling log.
"""
+ escaped = html.escape(message)
if "[MISMATCH]" in message:
- escaped = html.escape(message)
- self.log_display.append(f'{escaped}')
+ html_msg = f'{escaped}'
+ self.log_display.append(html_msg)
+ self.error_display.append(html_msg)
+ self._error_count += 1
+ self._tab_widget.setTabText(1, f"Errors ({self._error_count})")
# Counting is handled via stdout MISMATCH_EVENT markers in
# TranslationTab.append_log — the log viewer only handles display.
elif "[API_ERROR]" in message:
- escaped = html.escape(message)
- self.log_display.append(f'{escaped}')
+ html_msg = f'{escaped}'
+ self.log_display.append(html_msg)
+ self.error_display.append(html_msg)
+ self._error_count += 1
+ self._tab_widget.setTabText(1, f"Errors ({self._error_count})")
else:
# Explicitly wrap in a white span so Qt doesn't inherit red from
# a preceding [MISMATCH] HTML append.
- escaped = html.escape(message)
self.log_display.append(f'{escaped}')
- # Scroll to bottom
- cursor = self.log_display.textCursor()
- cursor.movePosition(QTextCursor.End)
- self.log_display.setTextCursor(cursor)
+ # Scroll to bottom of whichever tab is active; always scroll the
+ # background error display too so it stays at the latest entry.
+ for display in (self.log_display, self.error_display):
+ cursor = display.textCursor()
+ cursor.movePosition(QTextCursor.End)
+ display.setTextCursor(cursor)
def get_current_log_content(self):
"""Get the current log content."""
diff --git a/modules/rpgmakermvmz.py b/modules/rpgmakermvmz.py
index a1624a6..e8e910f 100644
--- a/modules/rpgmakermvmz.py
+++ b/modules/rpgmakermvmz.py
@@ -44,7 +44,7 @@ _speakerCacheLock = threading.Lock()
SPEAKER_COLLECTED = [] # Original speaker names collected during parse mode (untranslated)
# Regex - Need to change this if you want to translate from/to other languages. Default is Japanese Regex
-LANGREGEX = r"[\u3000-\u303F\u3040-\u309F\u30A0-\u30FB\u30FD-\u30FF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFF02-\uFF5D\uFF5F-\uFFEF]+"
+LANGREGEX = r"[\u3000-\u303F\u3040-\u309F\u30A0-\u30FF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFF61-\uFF9F]+"
# Get pricing configuration based on the model
PRICING_CONFIG = getPricingConfig(MODEL)
diff --git a/modules/srpg.py b/modules/srpg.py
index af8b127..e9be217 100644
--- a/modules/srpg.py
+++ b/modules/srpg.py
@@ -36,7 +36,7 @@ FILENAME = None
TIMETOTAL = 0 # Total Time Taken for all translations
# Regex - Need to change this if you want to translate from/to other languages. Default is Japanese Regex
-LANGREGEX = r"[\u3000-\u303F\u3040-\u309F\u30A0-\u30FF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFF00-\uFF5D\uFF5F-\uFFEF]+"
+LANGREGEX = r"[\u3000-\u303F\u3040-\u309F\u30A0-\u30FF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uFF61-\uFF9F]+"
# Get pricing configuration based on the model
PRICING_CONFIG = getPricingConfig(MODEL)