diff --git a/gui/translation_tab.py b/gui/translation_tab.py
index 01419f0..f656da8 100644
--- a/gui/translation_tab.py
+++ b/gui/translation_tab.py
@@ -275,7 +275,7 @@ class TranslationWorker(QThread):
clear_cache()
# Check for required environment variables
- required_envs = ["api", "key", "organization", "model", "language", "timeout", "fileThreads", "threads", "width", "listWidth"]
+ required_envs = ["api", "key", "model", "language", "timeout", "fileThreads", "threads", "width", "listWidth"]
env_missing = False
for env in required_envs:
diff --git a/gui/workflow_tab.py b/gui/workflow_tab.py
index 9b7ff66..13a38c7 100644
--- a/gui/workflow_tab.py
+++ b/gui/workflow_tab.py
@@ -261,6 +261,30 @@ class _SubprocessWorker(QThread):
self.done.emit(False, f"{self.label}: {exc}")
+class _JsonFormatWorker(QThread):
+ """Format all JSON files in a directory using the bundled dazedformat utility."""
+ done = pyqtSignal(bool, str)
+ log = pyqtSignal(str)
+
+ def __init__(self, data_path: str):
+ super().__init__()
+ self.data_path = data_path
+
+ def run(self):
+ try:
+ from util.dazedformat import format_json_files
+ self.log.emit(f"Formatting JSON files in {self.data_path} …")
+ count, errors = format_json_files(self.data_path, log=self.log.emit)
+ for e in errors:
+ self.log.emit(f" ⚠ {e}")
+ if errors:
+ self.done.emit(False, f"dazedformat: {count} formatted, {len(errors)} error(s).")
+ else:
+ self.done.emit(True, f"dazedformat: {count} file(s) formatted successfully.")
+ except Exception as exc:
+ self.done.emit(False, f"dazedformat error: {exc}")
+
+
class _FileCopyWorker(QThread):
"""Recursively copy a source folder into a destination folder."""
done = pyqtSignal(int, list) # count_copied, errors
@@ -533,11 +557,6 @@ class WorkflowTab(QWidget):
row1.addWidget(sel_core)
row1.addStretch()
-
- self.import_btn = _make_btn("⬇ Import Selected → files/", "#007acc")
- self.import_btn.setEnabled(False)
- self.import_btn.clicked.connect(self._import_files)
- row1.addWidget(self.import_btn)
layout.addLayout(row1)
# ── Step 1: Vocab / Glossary ────────────────────────────────────────────
@@ -933,13 +952,14 @@ class WorkflowTab(QWidget):
tb.setSpacing(10)
# ---- Task A: dazedformat -----------------------------------------
- ta = QGroupBox("A — Format JSON files with dazedformat")
+ ta = QGroupBox("A — Format JSON files (dazedformat)")
ta.setStyleSheet(self._task_box_style())
ta_inner = QVBoxLayout(ta)
ta_inner.setSpacing(4)
ta_desc = QLabel(
- "Runs dazedformat . in the game\'s data folder to "
- "normalise all JSON files before importing."
+ "Normalises all JSON files in the game's data folder by round-tripping them "
+ "through json.load / json.dump. "
+ "Bundled — no external install required."
)
ta_desc.setTextFormat(Qt.RichText)
ta_desc.setWordWrap(True)
@@ -1043,14 +1063,18 @@ class WorkflowTab(QWidget):
layout.addWidget(tasks_box)
- # ---- Run All button ------------------------------------------
- run_all_row = QHBoxLayout()
- run_all_btn = _make_btn("▶▶ Run All 3 Tasks", "#007acc")
+ # ---- Run All + Import button row --------------------------------
+ bottom_row = QHBoxLayout()
+ bottom_row.addStretch()
+ run_all_btn = _make_btn("▶▶ Run All 3 Tasks", "#3a4a6a")
run_all_btn.setToolTip("Run dazedformat, prettier, and gameupdate copy in sequence")
run_all_btn.clicked.connect(self._run_all_preprocess)
- run_all_row.addStretch()
- run_all_row.addWidget(run_all_btn)
- layout.addLayout(run_all_row)
+ bottom_row.addWidget(run_all_btn)
+ self.import_btn = _make_btn("⬇ Import Selected → files/", "#3a4a6a")
+ self.import_btn.setEnabled(False)
+ self.import_btn.clicked.connect(self._import_files)
+ bottom_row.addWidget(self.import_btn)
+ layout.addLayout(bottom_row)
@staticmethod
def _task_box_style() -> str:
@@ -2393,7 +2417,7 @@ class WorkflowTab(QWidget):
if not data_path:
self._log("⚠ No data folder detected. Complete Step 0 first.")
return
- w = _SubprocessWorker(["dazedformat", "."], cwd=data_path, label="dazedformat")
+ w = _JsonFormatWorker(data_path)
w.log.connect(self._log)
w.done.connect(lambda ok, msg: self._log(("✅ " if ok else "❌ ") + msg))
self._worker = w
@@ -2445,13 +2469,12 @@ class WorkflowTab(QWidget):
gameupdate_src = self.pp_gameupdate_edit.text().strip()
skipped: list[str] = []
- # A — dazedformat
- if data_path and _shutil.which("dazedformat"):
+ # A — dazedformat (bundled, no PATH check needed)
+ if data_path:
self._log("▶ [A] dazedformat …")
self._run_dazedformat()
else:
- reason = "data folder missing" if not data_path else "dazedformat not on PATH"
- skipped.append(f"A (dazedformat): {reason}")
+ skipped.append("A (dazedformat): data folder missing")
# B — jsbeautifier (pure Python, no Node required)
if plugins_js and Path(plugins_js).is_file():
diff --git a/modules/main.py b/modules/main.py
index 0c979e3..92a167d 100644
--- a/modules/main.py
+++ b/modules/main.py
@@ -15,7 +15,6 @@ load_dotenv()
for env in [
"api",
"key",
- "organization",
"model",
"language",
"timeout",
diff --git a/util/dazedformat.py b/util/dazedformat.py
new file mode 100644
index 0000000..4049b7d
--- /dev/null
+++ b/util/dazedformat.py
@@ -0,0 +1,48 @@
+"""Bundled JSON formatter (dazedformat).
+
+Normalises all .json files in a directory tree by round-tripping them
+through json.load / json.dump with indent=4, which ensures consistent
+formatting before importing into files/.
+"""
+from __future__ import annotations
+
+import json
+import os
+from pathlib import Path
+from typing import Callable
+
+
+def format_json_files(
+ directory: str | Path,
+ log: Callable[[str], None] | None = None,
+) -> tuple[int, list[str]]:
+ """Format every .json file found under *directory* in-place.
+
+ Returns (formatted_count, error_list).
+ """
+ directory = Path(directory)
+ formatted = 0
+ errors: list[str] = []
+
+ for root, _, files in os.walk(directory):
+ for name in files:
+ if not name.lower().endswith(".json"):
+ continue
+ fp = Path(root) / name
+ try:
+ text = fp.read_text(encoding="utf-8")
+ data = json.loads(text)
+ pretty = json.dumps(data, indent=4, ensure_ascii=False)
+ # Only write if the content actually changed
+ if pretty != text:
+ fp.write_text(pretty, encoding="utf-8")
+ formatted += 1
+ if log:
+ log(f" Formatted: {fp.relative_to(directory)}")
+ except Exception as exc:
+ msg = f"Error in {fp}: {exc}"
+ errors.append(msg)
+ if log:
+ log(f" ⚠ {msg}")
+
+ return formatted, errors