Changelog
All notable changes to this project are documented here. The format is based on Keep a Changelog.
1.8.1 (2026-08-19)
Added
docs/user_guide.rst: document--auto-update, and add a new “Configuration Files” section listing every persisted config file and its default location per platform
Fixed
docs/user_guide.rst:--config-dir’s documented macOS default was wrong (~/Library/Application Support/pbrenamer); it’s actually~/Library/Preferences/pbrenamer(matchesplatform/dirs.py:_MacDirs.config_home)--auto-updatecrashed the PyInstaller executable withModuleNotFoundError: No module named 'http'— the Linux/Windows build excluded the stdlibhttppackage, whichurllib.requestneeds. Removedhttpfrompbrenamer.spec’s excludes--auto-updatethen failed every HTTPS request withunknown url type: httpson the Linux build — PyInstaller’s binary scan resolvedlibssl.so.3/libcrypto.so.3to the system copy (missing symbols the bundled_ssl/_hashlibextensions need) instead of the conda env’s own matching copy.pbrenamer.specnow forces the env’s own OpenSSL libraries back into the bundle, and addsssltohiddenimportssinceurllib.requestonly imports it inside a try/except that PyInstaller’s static analysis missesREADME: “Internationalised” feature bullet claimed only English and French were included; it now lists all 8 bundled languages (English, German, Spanish, French, Italian, Russian, Vietnamese, Chinese)
1.8.0 (2026-08-19)
Added
--auto-updatedownloads the latest GitHub release matching the running platform/architecture and replaces the current executable with it, then exits. Only available in PyInstaller-built executables (sys.frozen) — rejected with an error for source checkouts and pip/PyPI installs, which have no single running binary to replace. Newplatform/auto_update.pyHeadless mode:
--dry-run/--no-dry-runflag prints the equivalentmvcommands to stdout instead of renaming files. Output is a valid POSIX shell script (paths quoted withshlex.quote, conflicts reported as#comments) that can be piped toshto actually perform the rename. Takes precedence over--confirm— no interactive prompt is shownHeadless mode:
--select GLOBrestricts the rename to entries matchingGLOB, mirroring GUI row selection (clicking a file). Repeatable to accumulate matches (equivalent of Ctrl+click). With--sidecar-mode, selecting a base file also selects its sidecars and vice versa, using the same grouping and ambiguity handling as the GUI (core/sidecar.build_sidecar_groups()). New__main__._resolve_selection()
Fixed
make distalways resolved the release version todev, even on a clean, exactly-tagged commit —PY_SOURCESin the Makefile was built from unsortedfindoutput, sopybabel extract(viamake translate, adistprerequisite) reordered.pocatalogue entries on every run despite no actual string changes, dirtying the working tree right beforetools/git_version.shchecked it.findoutput is now sorted, makingmake translateidempotentSidecar mode: a sidecar file ambiguous between multiple base files (e.g. two candidate base files sharing a suffix) no longer disables the Rename button for the whole file list — the error now only blocks rename when one of the files involved in the ambiguity is actually selected, matching how every other field-resolution error is already scoped to the current selection
Sidecar mode: re-clicking (plain click, no modifier) any file belonging to an already fully-selected sidecar group no longer deselects the whole group. Only a genuine Ctrl+click now toggles a single item against the existing selection; a plain click or Shift range-select always replaces the selection with the clicked file’s whole group, consistent with clicking an ungrouped file staying selected
1.7.0 (2026-08-18)
Added
Drag’n’drop of files and directories between PBRenamer and external applications, and between the directory tree and the file list: copy or move (negotiated via the OS/Qt drag modifier keys), with a batched Overwrite/Skip/Cancel confirmation when a destination already exists. New
core/filetools.copy_path()/move_path()functions back the operation; only moves are recorded to the undo history (copies are not undoable). Implemented by the newui/dnd_widgets.py(DirectoryTreeView,FileListWidget)Custom, HiDPI-aware drag cursors (copy / move / alias / not-allowed), sourced from PBIcons’ new
cursors/icon set and synced viatools/update_icons.py(now also covers the fixed_CURSOR_FILESlist and searches thecursorsPBIcons subdirectory); implemented by the newui/dnd_cursors.pyREADME badges: latest GitHub release, CI status, test coverage (Codecov), and PyPI version
CI now uploads
coverage.xmlto Codecov (codecov/codecov-action) after the test suite runs; pytest gained a--cov-report=xmloutput alongside the existing terminal reportSidecar file support: a file associated with a base file by a name suffix (e.g.
img.jpg+img.xmp) can now be grouped, colored, selected, and renamed together with its base file. New Edit → Sidecar Files… dialog (ui/sidecar_dialog.py), with one tab per base-file category (Images / Video / Audio / Other) plus a Common tab, configures which extensions count as that category’s base files and which suffixes identify its sidecars, each list independently restorable to its default; persisted tosidecar_config.json. A new toolbar “Sidecar mode” checkbox (enabled only for the Keep/Lowercase/Uppercase extension modes) drives the behaviour: sidecar rows are colored distinctly in the file list, selecting a base or sidecar auto-selects the whole group, a sidecar with more than one candidate base file is flagged as an explicit, unrenamable ambiguity error, and renaming a group applies the pattern to the base file’s stem only, reusing it verbatim (own suffix unchanged) for every sidecar. New pure grouping algorithm incore/sidecar.py(SidecarConfig,build_sidecar_groups()). Mirrored in headless mode via--sidecar-mode/--no-sidecar-mode(__main__.py)
Changed
About dialog restyled to match the PBIcons-family apps (e.g. PBRegisterActivity): icon in its own column on the left, application name/version/author/description/details framed in a raised panel on the right, centered Close button
Fixed
_on_rename()could use a row’s raw error text (e.g. an ambiguous-sidecar message) as a literal rename target if invoked while the Rename button’s disabled state was bypassed; it now skips any row flagged with a field/grouping errorAdding a base-file extension already assigned to a different category (e.g.
jpgto both Images and Video) silently created a duplicate that made category lookup ambiguous; the Sidecar Files dialog now rejects it with an explicit warning naming the conflicting categoryThe Sidecar Files dialog let a sidecar suffix (per-category or Common) be added even when it was already a base extension in one of the categories (e.g. adding
jpgas a Video sidecar suffix while it is an Images base extension), creating the same category-lookup ambiguity as the base-extension case; it is now rejected with the same warningIn sidecar mode, Ctrl+click-toggling off one member (base or sidecar) of an already-fully-selected group left the whole group selected instead of deselecting it:
_link_group_selection()only ever looked at the current selection to decide which groups to expand, so the group’s other still-selected members immediately re-pulled the toggled-off file back in. It now also tracks the previous selection and, when part of a group is toggled off, deselects the whole group — mirroring the existing behaviour where selecting any member selects the whole group. Toggling off an unrelated file (outside any group, or the sole selected member of a solo file) is unaffected
1.6.0 (2026-08-13)
Added
Five editorial
{vi:…}video metadata fields, read from the MediaInfoGeneraltrack:performer,copyright,comment,description,genreExtension normalization table, managed from its own tab in the Settings dialog (Edit → Settings… → Extension Normalization): user-editable
from → toextension mappings (e.g.jpeg → jpg,yml → yaml), persisted toextension_normalization.json, used by the new “Normalize extension” mode; changes apply immediately regardless of whether the dialog is later confirmed or cancelled
Changed
Menu actions (Open folder, Information, History, Settings, Edit shortcuts, About, Quit) and the search/replace toolbar buttons (add-to-history “+”, pattern help “?”) now use bundled SVG icons (
resources/*.svg) instead of plain text / programmatically-painted icons;pattern_help.make_add_icon()/make_help_icon()removedThe “Keep extension” checkbox is now a 5-way combobox: Keep extension (unchanged behaviour), Extension in lowercase, Extension in uppercase, Normalize extension (via the new normalization table), Modify extension (unchanged behaviour of the former unchecked state). Existing saved preferences (
keep_extension: true/false, in toolbar state and named saves) are migrated transparently tokeep/modify.CLI:
--keep-ext/--no-keep-extreplaced by--ext-mode {keep,lower,upper,normalize,modify}(defaultkeep), consistent with the existing string-choicesstyle of--case/--sep. This is a breaking change for scripts invoking--keep-extdirectly.
Fixed
make pypi-buildnow removesdist/pypi/before rebuilding, sopublish-pypi/publish-testpypino longer pick up wheel/sdist artifacts left over from previous versions alongside the current oneNavigating via a shortcut/bookmark (
_on_shortcut) triggered_reload_files()twice —_navigate_to()’ssetCurrentIndex()synchronously fired_on_directory_selected()before_current_dirhad been updated, so its same-path guard did not catch the redundant call — causing the file list (and, with auto-preview on, the preview column) to be cleared and repopulated a second time right after the first; fixed by setting_current_dirbefore calling_navigate_to(), matching the pattern already used in_on_open()Preview/file list still flickered once per directory change even after the above fix:
QFileSystemWatchercan emit a spuriousdirectoryChangedsignal shortly after a directory starts being watched (e.g. a desktop indexer or thumbnailer touching directory metadata with no actual file added/removed/renamed), which_on_fs_changetreated as a real change and rebuilt the whole file list for._on_fs_changenow compares the on-disk listing against what’s currently displayed (_listing_unchanged()) and skips the reload when nothing actually changed.
1.5.1 (2026-08-09)
Added
make verify-testpypi/make verify-pypi: install the latestpbrenamerfrom TestPyPI / the real PyPI into a throwaway venv, runpbrenamer $(ARGS)(default--version, override e.g.make verify-testpypi ARGS="--help-search") as a smoke test, then remove the venv — cleanup always runs even if the install or the smoke test fails, and the target still exits non-zero on failure
Fixed
Mixed-type replacement templates (e.g.
{im:…}{vi:…}) on a file that matches none of the namespaces used in the template still flag the file as an error (unchanged), but the message is now explicit about which namespaces were tried instead of misleadingly naming only the first field — e.g.⚠ no matching type (im/vi)instead of⚠ im:DateTimeDigitized unavailable, which wrongly suggested only that one field was the problem.
1.5.0 (2026-08-09)
Added
make update-icons(new Makefile target, backed by the newtools/update_icons.py) syncspbrenamer.svgand the newpbrenamer.ico/pbrenamer.icnsbyte-for-byte from the PBIcons reference project (local checkout or GitHub)PyInstaller build now embeds a native icon (
pbrenamer.icoon Windows,pbrenamer.icnson macOS viaBUNDLE), synced from PBIcons--install-desktop-entry/--uninstall-desktop-entryCLI flags (platform/desktop.py): install/remove a user-level.desktopmenu entry and icon on Linux, working around pip’s lack of a post-install hook forpip install pbrenamerThe generated
.desktopentry’sComment=is now localized (Comment[fr]=,Comment[de]=, …) for every language with a compiled catalogue, reusing the existing translated “A graphical batch file renaming utility.” string from the About dialog;Name=PBRenameris intentionally left unlocalizedmake pypi-build/make publish-testpypi/make publish-pypi(new Makefile targets): build the wheel + sdist intodist/pypi/and upload to TestPyPI or the real PyPI viatwine;publish-pypiasks for an explicityesconfirmation before the irreversible upload. Addsbuild/twineas dev dependencies
Fixed
Picking an entry from the search pattern history no longer overwrites the replace field with the first entry of the replace history (and vice versa); each history combo box now only refreshes its own list
PyPI wheel now ships compiled
.motranslation catalogues: a new hatchling build hook (hatch_build.py) compiles the committed.posources at wheel-build time and force-includes the result, since.mofiles are gitignored generated artifacts and were previously silently dropped from the wheel — the installed app had no languages available (i18n.available_languages()globs for.mo)licensemetadata switched from the legacy{ file = "LICENSE" }form (which duplicated the full GPLv3 text into theLicense:metadata field) to the SPDX expressionGPL-3.0-onlywithlicense-files = ["LICENSE"], and dropped the now-redundantLicense ::classifier
1.4.4 (2026-08-05)
Added
Documentation: the app icon (
src/pbrenamer/resources/pbrenamer.svg) is now used as the Sphinx logo and favicon, copied todocs/_static/pbrenamer.svgat build time so the artwork has a single source of truth.
Changed
Development environment management migrated from conda to [pixi](https://pixi.sh):
environment.ymlis replaced by[tool.pixi.*]sections inpyproject.tomland a committedpixi.lock.make venvnow bootstraps pixi itself if missing; the Makefile’sCONDA_RUNwrapper now resolves topixi run --instead ofconda run -n pbrenamer. CI provisions pixi viaprefix-dev/setup-pixiinstead ofconda-incubator/setup-minicondain the test, hooks, docs, and build jobs. No application code changed.
Fixed
docs/conf.py: the## [Unreleased]section ofCHANGELOG.mdwas dropped by the RST changelog generator except for its orphaned### Added/### Changed/… subheadings, which were emitted without content and at an incorrect nesting level.[Unreleased]is now recognised like a dated release and included only when it has actual content.Makefile: the
hookstarget was missing from.PHONYand silently shadowed by thehooks/directory (holding the PyInstaller runtime font hook), somake hookswas a no-op instead of running pre-commit.CI: the Windows build job’s PyInstaller step used a bash-style multi-line command with
\line continuations, which is not valid under PowerShell (the default shell onwindows-latest); it now runs as a single line.
1.4.3 (2026-06-24)
Fixed
Linux bundle — font selection:
_load_bundled_fonts()now explicitly sets Ubuntu as the application font after registering the bundled.ttffiles. Thelibfontconfig.sobundled by PyInstaller has its default config path hardcoded to the build machine’s conda prefix; on any other machine that path is absent and fontconfig fails silently, leaving Qt with no valid system font and an unpredictable default. Forcing Ubuntu bypasses fontconfig for font selection while the runtime hook (pyi_rth_fonts.py) still provides a portablefonts.confso rendering settings (anti-aliasing, hinting…) are inherited from the system/etc/fonts/fonts.conf.
1.4.2 (2026-06-24)
Fixed
Linux bundle: fonts and font size are now identical between local and CI builds. The
fonts.confembedded by PyInstaller contained absolute paths to the build machine’s conda environment; on any other machine those paths are missing and Qt fell back to system fonts only, at a different size. Fix: conda fonts (fonts-conda-ecosystem: Ubuntu, DejaVu, Inconsolata, SourceCodePro) are included in the bundle viapbrenamer.specfor glyph coverage; a runtime hook (hooks/pyi_rth_fonts.py) generates a portablefonts.confat startup that includes the system/etc/fonts/fonts.confand callsFcFini()/FcInit()to force fontconfig re-initialisation with the correct config — the system font is then resolved normally by Qt, with the same size as in the local run. (Closes [#2](https://github.com/ppoilbarbe/PBRenamer/issues/2))ReadTheDocs build: removed the
pre_buildstep that attempted to runpyside6-uicon*.uifiles — no such files exist (*_ui.pyare committed hand-written sources); the stale step caused the RTD build to fail on every tag since1.4.1
1.4.1 (2026-06-18)
Fixed
audio_meta.read_fieldnow returnsNonefor video container files (MP4, MKV, MOV, …); previouslyread_fieldcalled mutagen directly without going throughcan_read, so mutagen — which can read MP4 tags — returned audio field values (title, duration, …) for video files;{au:…}tokens in a replacement template could therefore resolve on video files as if they were pure-audio files
## [1.4.0] - 2026-06-18 [YANKED]
> Regression: audio_meta.read_field returns audio metadata for video
> containers (MP4, MKV, …) because it bypasses can_read. Replaced by 1.4.1.
Added
settings.configure(config_dir)— new public function that overrides the configuration directory used by all settings and i18n functions; a single call redirectssettings,i18n, andPatternPresetsconfig I/O simultaneously; passNoneto restore the platform default; intended for testing--config-dir DIRCLI option (both GUI and headless modes): overrides the configuration directory at startup before any settings access; intended for testing (noted in--help)Window geometry persistence for all windows and dialogs (except About):
MainWindow,FileInfoWindow,HistoryDialog,SettingsDialog,ShortcutsDialog, andPatternHelpDialogsave/restore size and position across sessions viaWindowState.save_geometry/load_geometryLRU management for search, replace, and named-saves dropdowns: the most recently used entry is promoted to position 0 on each use; history is capped at 20 entries
Mixed file-type replacement templates: when
{im:…},{vi:…}, or{au:…}tokens from more than one namespace appear in the same template,substitute()probes each namespace withcan_read()at runtime — the applicable namespace follows strict behaviour (absent field without default →FieldResolutionError), non-applicable namespaces silently contribute""without requiring a default{vi:…}field resolution now rejects files that have no video track even when MediaInfo returns a General track (e.g. JPEG files parsed by pymediainfo)UI layouts converted from Qt Designer
.uifiles to hand-written Python source (*_ui.py):main_window_ui.py,history_dialog_ui.py,settings_dialog_ui.py,about_dialog_ui.py— eliminates the Qt Designer file-parsing step at build time
Changed
Test configuration isolation:
conftest.pyprovides aconfig_dirautouse fixture that redirects all settings I/O to a per-test temporary directory viasettings.configure(); the real~/.config/pbrenameris never touched during test runs; tests that need direct file access can request theconfig_dirfixture to obtain the pathNamed saves storage format migrated from a flat JSON dict (no ordering guarantee) to an ordered JSON list
[{"name": …, …config…}, …]; legacy dict files are read transparently and upgraded on next writepatternHelp.PatternHelpDialog: geometry now restored inshowEvent(first paint) rather than in the constructor, matching the behaviour of all other secondary windows
Fixed
audio_meta.can_readnow returnsFalsefor video container files (MP4, MKV, MOV, …) that carry no audio-only track; previously aGeneraltrack from pymediainfo could satisfy the read check, causing{au:…}fields to attempt — and silently fail — audio tag extraction on video filesMain window geometry was not restored at startup:
restoreGeometry()was called in__init__beforeshow(), so the window manager ignored it; moved toshowEvent(fires once via_geometry_restoredflag)Named saves combo list order reverted to sorted order on a subsequent save: Qt’s
QCompleter(auto-attached to the editableQComboBox) receivedtextChangedevents from the embeddedQLineEditeven whilecombo.blockSignals(True)was active, and could queue a deferredactivatedcallback that calleduse_savewith a wrong index; fixed by blocking theQLineEditsignals separately and usingsetCurrentIndexinstead ofsetCurrentTextduring_populate_named_savesPreview pane appeared and immediately disappeared on the first click in the directory tree:
_on_directory_selectedwas called twice for the initial path, the second call with the same path clearing the just-populated preview; fixed with an early-return guard when the path has not changed
Removed
Qt Designer
.uifiles (about_dialog.ui,history_dialog.ui,main_window.ui,settings_dialog.ui) — replaced by*_ui.pyPython sourcestools/extract_ui_strings.py— no longer needed; translatable strings are extracted directly from Python source via pybabel
1.3.0 (2026-06-15)
Added
Undo button displays the number of available undo levels — text changes to “Undo (N)” and the tooltip explains how many rename batches can be reverted
Case-transform modifier in replacement field tokens:
=(unchanged),-(lower),+(upper),!(capitalise first char),*(Title Case)Centre-align (
^) in replacement field tokensToolbar-state and preview-delay persistence in Settings
tools/po_check.py: PO file inspection tool using the babel library (statistics, untranslated entries, regex search, language diff) — replaces ad-hocgrep/msgfmtusage that breaks on multi-line entriessrc/pbrenamer/argparse_qt.py:add_qt_arguments()declares Qt CLI flags (--style,--platform, …) as a proper argparse argument group; collected inargs.qt_argsas single-dash tokens ready forQApplication; Qt options are hidden from theusage:line to reduce noise while remaining fully visible in the--helpoutputtools/fix_po_files.py: utility to normalise PO file formatting in bulkComprehensive test suite covering all source modules at 100%; includes the full main window (
_on_preview,_refresh_conflicts, context menus, undo, conflict detection, drag-and-drop, keyboard shortcuts, settings dialog, file-info window, pattern-help dialog, presets, bookmarks, i18n bootstrap, and all core modules)Headless Qt test execution via
QT_QPA_PLATFORM=offscreeninconftest.py;QMenu.execpatched at module level so no blocking dialog appears during CI or localmake testruns
Changed
main()now usesparse_args()instead ofparse_known_args(); unknown flags are rejected natively by argparse —_validate_qt_argvremoved
Fixed
{L}search-pattern token now matches Unicode letters; accented Latin (é,ê,ü, …), Cyrillic, Greek and all other Unicode letter categories are captured correctly — was limited to ASCII[a-zA-Z], now uses[^\W\d_]which covers all\wcharacters that are neither digits nor underscoresQt built-in translations are now loaded alongside the application catalogue so dialog buttons (
OK,Cancel,Yes,No, …) are correctly translated when a non-English language is selectedPattern help dialog: HTML-escaped
{1:<12}so the browser no longer interprets<12>as an HTML tagFifteen
# pragma: no coverguards on reachable logic replaced by targeted tests: case-insensitive no-match inrename_using_plain_text, zero-width regex guard,{newnum}CLI branch (start value, invalid start, no-match, field error, collision k+=1), rename failure exit path,main()headless dispatch,_parse_exif_datetimeon malformed input,_read_iptcunknown key,_apply_caseunknown modifier
1.2.0 (2026-06-09)
Added
Shortcuts menu (
Go → Shortcuts): navigate instantly to system directories (Home, Desktop, Documents, Downloads, Pictures, Music, Videos on all platforms; GTK bookmarks on Linux) and user-defined shortcutsRight-click on any directory in the tree to Add as shortcut; shortcuts are persisted to
shortcuts.jsonin the user config directoryEdit Shortcuts dialog (
Go → Edit Shortcuts): reorder (Move up / Move down) and remove user-defined shortcutsFile information window (
View → File information): non-modal window that shows the actual values of all replacement fields ({date},{mdatetime},{dir},{im:…},{au:…},{vi:…}, …) for the currently selected file; updates live as the selection changesRestore last opened directory setting in the new Behaviour group of the Settings dialog; when enabled, the app reopens the last accessed directory at startup (CLI-provided directory always takes priority; default: disabled)
make runnow accepts anARGSvariable to forward arguments and options to the program (e.g.make run ARGS="--debug /some/dir")
Fixed
email.utilswas incorrectly listed in PyInstallerexcludes, causingimportlib.metadatacalls in the About dialog to fail in packaged buildsCI
testjob now compiles.mocatalogues before running the test suite, fixing fourtest_i18n.pyfailures (available_languages()globs.mofiles which are gitignored and were absent on the runner)
Changed
Executable names are now lowercase in the PyInstaller spec and CI artifact globs (e.g.
pbrenamer-1.2.0-linux-x86_64instead ofPBRenamer-…)pybabel extractnow uses--no-locationto omit source-file/line-number comments from.pofiles;make translateis idempotent on an unchanged source tree, fixingmake distalways producing adevbuild instead of using the git tag version
1.1.0 (2026-06-01)
Added
srcdistMakefile target: produces a source archivedist/pbrenamer-<ver>-src.tar.gzviagit archivetools/git_version.sh: derives the version string from the exact Git tag when HEAD is tagged and the working tree is clean, falls back todev; used by bothdistandsrcdisttargets
Changed
i18n toolchain replaced:
xgettext/msgmerge/msgfmt/msginitremoved in favour ofpybabel extract/update/compile/init;babel.cfgmapping file added;gettextdependency replaced bybabelinenvironment.yml
Removed
.mocompiled catalogues removed from the repository (now gitignored); the CI regenerates them viapybabel compilebefore each PyInstaller build
1.0.0 (2026-05-03)
Added
Translations for six new languages: German (
de), Spanish (es), Italian (it), Russian (ru), Vietnamese (vi), and Simplified Chinese (zh_CN)All 272 translatable strings covered in each new locale;
.mofiles compiled and committed alongside the.posources
0.3.2 (2026-04-25)
Fixed
docs/conf.py: readreleaseandversiondynamically frompbrenamer.__version__instead of a hard-coded string, so the Sphinx build always reflects the current version without a manual update
0.3.1 (2026-04-25)
Fixed
README: remove insert/delete at position and manual rename from feature list (functions exist in
filetools.pybut are not wired to the GUI)
0.3.0 (2026-04-25)
Added
Complete Sphinx documentation: user guide (GUI usage, all search modes, replacement fields reference, headless CLI reference) and API reference (autodoc for all modules)
docs/conf.pygenerateschangelog.rstfromCHANGELOG.mdat build time; the generated file is gitignored
0.2.0 (2026-04-24)
Added
Non-modal help dialogs for search and replace patterns, with geometry persistence (position and size saved across sessions)
Help dialog content fully translated (English and French); field-name tokens left in code syntax, all descriptions and headers localised
Log-level preference in the Settings dialog (DEBUG / INFO / WARNING / ERROR / CRITICAL, default INFO); applied at startup and immediately on change
CLI flags
--debug/-d,--verbose/-v,--quiet/-qto override the saved log-level preference at launchDebug traces throughout the codebase via Python
logging(file listing, per-file preview, rename operations, metadata reads, template parsing).uifiles for the About, Settings and History dialogs; all dialogs now follow the Designer →pyside6-uicworkflowWindowState.save_geometry/load_geometryfor named secondary-window geometry slots
Fixed
CI “Compile Qt UI files” step now compiles all
*.uifiles instead of onlymain_window.ui, fixingModuleNotFoundErrorfor the new dialog modules
0.1.0 (2026-04-23)
Added
Live-preview rename table with per-row conflict highlighting; Rename button disabled until all conflicts are resolved
Flexible renaming rules: pattern search/replace, text substitution, case transforms, insert/delete at position, counter, date stamps, parent-folder name, and manual rename
Keep-extension option so transforms apply to the stem only
Multi-selection — apply rules to selected files only
Recursive directory traversal
Directory entries shown in a distinct colour
One-click undo of the last rename batch
Pattern preset save/restore (persisted per user)
Case-aware conflict detection honouring the filesystem’s actual case-sensitivity (probed at runtime)
Language settings dialog; language preference persisted across sessions
Internationalisation via gettext — English and French bundled
Platform abstraction layer (
platform/) covering Linux (XDG), macOS, and Windows for config/data directories, filesystem probing, and locale detectionStandalone executable builds via PyInstaller for Linux, macOS, and Windows
CI/CD pipeline: test → pre-commit hooks → 3-platform build → GitHub release on semver tag
make bump-patch / bump-minor / bump-major / bump-settargets backed bytools/bump_version.pyfor atomic version incrementsNOCONDA=1Makefile flag to bypass conda wrapping when tools are onPATH