Skip to content

feat(gui): add GTK4 Python GUI for file-manager integration - #7

Merged
fathriAbanoub merged 3 commits into
mainfrom
feature/gtk4-gui
Jul 1, 2026
Merged

feat(gui): add GTK4 Python GUI for file-manager integration#7
fathriAbanoub merged 3 commits into
mainfrom
feature/gtk4-gui

Conversation

@fathriAbanoub

@fathriAbanoub fathriAbanoub commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Add a native GTK4 Python GUI for right-click file/folder gluing that provides:

  • Default filename with collision-safe auto-increment (Glued_Code.md, Glued_Code_1.md, ... with pid fallback after 99 collisions)
  • Format-aware file extension (.md/.txt) with live switching when user hasn't customized the name
  • Theme support (auto/light/dark/roselle) with CSS styling and persistence in ~/.config/codegluer/theme
  • Smart flag visibility: dir-only flags (tree/toc/respect-gitignore) hidden when selection is files-only
  • Exclude pattern validation with auto-fix dialog for space-separated input
  • Apply Theme button that grays out when no change is pending
  • Nautilus (env var) and Nemo (positional args) integration via single codegluer-gui binary in ~/.local/bin

Architecture separates pure logic (build_command, default_name, should_update_default, theme management) from GTK UI for testability. 22-test suite covers all logic paths without requiring a display.

Installer updated to check for GTK4 Python bindings (python3-gi, gir1.2-gtk-4.0) and fail fast with install instructions if missing.

Summary by CodeRabbit

  • New Features
    • Added a GTK4 graphical launcher for CodeGluer with configurable output format, filename, exclusions, theme, and advanced options (including dry-run).
    • Added file-manager right-click integration to start the GUI using the current selection.
    • Added persisted theme support with Light/Dark/Auto modes.
  • Bug Fixes
    • Improved collision-safe default naming and safer behavior when updating defaults.
    • Added validation for exclude inputs and more robust handling of invalid theme settings.
  • Tests
    • Added automated checks for command construction, naming logic, and theme read/write behavior.

Add a native GTK4 Python GUI for right-click file/folder gluing that
provides:
- Default filename with collision-safe auto-increment (Glued_Code.md,
  Glued_Code_1.md, ... with pid fallback after 99 collisions)
- Format-aware file extension (.md/.txt) with live switching when user
  hasn't customized the name
- Theme support (auto/light/dark/roselle) with CSS styling and
  persistence in ~/.config/codegluer/theme
- Smart flag visibility: dir-only flags (tree/toc/respect-gitignore)
  hidden when selection is files-only
- Exclude pattern validation with auto-fix dialog for space-separated input
- Apply Theme button that grays out when no change is pending
- Nautilus (env var) and Nemo (positional args) integration via single
  codegluer-gui binary in ~/.local/bin

Architecture separates pure logic (build_command, default_name,
should_update_default, theme management) from GTK UI for testability.
22-test suite covers all logic paths without requiring a display.

Installer updated to check for GTK4 Python bindings (python3-gi,
gir1.2-gtk-4.0) and fail fast with install instructions if missing.
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@fathriAbanoub, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 32ceffbb-f5bc-4eb5-bbeb-dd40a18b9590

📥 Commits

Reviewing files that changed from the base of the PR and between 7b57094 and 51a86f5.

📒 Files selected for processing (2)
  • codegluer_gui.py
  • test_codegluer_gui.py
📝 Walkthrough

Walkthrough

Adds a GTK4 GUI for CodeGluer command generation, theme persistence, and file-manager launch integration. The installer now installs and wires the GUI, and a standalone test script exercises the pure helper functions.

Changes

CodeGluer GUI Feature

Layer / File(s) Summary
CLI command-building helpers
codegluer_gui.py
Pure functions derive collision-safe default output names, detect directories/output paths, and translate GUI options into codegluer CLI arguments including -r and --exclude.
Theme persistence and resolution
codegluer_gui.py
Reads/writes a persisted theme choice, resolves "auto" via gsettings with fallback, and returns CSS for a given theme.
GTK4 window and application flow
codegluer_gui.py
Builds the GTK4 window UI, applies theme CSS, validates exclude patterns, and runs codegluer and notify-send with dry-run support.
CLI entry point
codegluer_gui.py
main() parses --dry-run and file arguments from CLI or file-manager environment variables, validates input, and launches run_gui.
Installer updates for GUI packaging and integration
install.sh
Updates banner/step text, adds PATH and GTK4 bindings checks, installs codegluer_gui.py as codegluer-gui, and rewrites Nautilus/Nemo scripts and usage docs to launch the GUI.
Standalone GUI logic tests
test_codegluer_gui.py
A self-contained script runs tests validating command building, naming/directory detection, theme round-trip/fallback, and default-name update logic.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CodeGluerWindow
  participant build_command
  participant subprocess
  participant notify_send

  User->>CodeGluerWindow: click Generate
  CodeGluerWindow->>CodeGluerWindow: collect options and validate excludes
  CodeGluerWindow->>build_command: build_command(files, opts)
  build_command-->>CodeGluerWindow: CLI argument list
  CodeGluerWindow->>subprocess: subprocess.run(codegluer command)
  subprocess-->>CodeGluerWindow: return code
  CodeGluerWindow->>notify_send: subprocess.run(notify-send success/failure)
  CodeGluerWindow->>CodeGluerWindow: close window
Loading

Possibly related PRs

  • fathriAbanoub/codegluer#2: The GUI command builder propagates format selection and markdown-only flags into the CLI.
  • fathriAbanoub/codegluer#3: The GUI build_command wires -r, --respect-gitignore, and repeated --exclude options onto the CLI interface.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: a new GTK4 Python GUI for file-manager integration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/gtk4-gui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (1)
codegluer_gui.py (1)

91-91: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor: prefer unpacking over concatenation.

♻️ Proposed fix
-    cmd = ["codegluer"] + files
+    cmd = ["codegluer", *files]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@codegluer_gui.py` at line 91, The command list construction in
codegluer_gui.py uses concatenation for cmd, which should be simplified to
unpack the files directly; update the command assembly near the cmd assignment
to use list unpacking instead of ["codegluer"] + files, keeping the existing
behavior in the same place.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@codegluer_gui.py`:
- Around line 71-114: Update build_command so the --toc option is only added
when both opts.get("toc") is true and fmt is "markdown", alongside the existing
any_dir check. Also adjust the UI flow in _on_format_changed to disable or clear
the TOC checkbox when the format is switched to plain, keeping the TOC control
consistent with its markdown-only behavior.
- Around line 353-363: The theme reapply path in _apply_theme currently creates
and registers a new Gtk.CssProvider every time, which leaks providers and can
leave stale styles behind. Initialize and reuse a single provider on the
instance (for example, set self._css_provider in __init__), then update
_apply_theme to remove the previously added provider before adding the new one
or keep one persistent provider and reload its data instead of creating a fresh
provider on each call; make sure _on_apply_theme and the initial theme
application both go through the same reusable provider path.
- Around line 387-395: Gtk.AlertDialog is used in the exclude-warning flow
without guarding for GTK 4.10+, so older GTK 4.0–4.9 installs can fail when this
dialog is opened. Update the exclude dialog path in codegluer_gui.py (the
Gtk.AlertDialog block in the exclude validation flow) to either check the GTK
version before instantiating Gtk.AlertDialog and fall back to a supported
dialog, or ensure the installer/docs enforce GTK 4.10+ consistently with this
API usage.

In `@install.sh`:
- Around line 44-53: The GTK4 Python bindings preflight in the “Check for GTK4
Python bindings” block currently runs too late, after the package install has
already started. Move this entire GTK4 check block to before the install step in
install.sh so the script exits early on missing dependencies and avoids leaving
a partial install behind.
- Around line 36-41: The installer and launcher flow still depend on the user’s
shell PATH, so right-click/file-manager launches can fail even after install.
Update the launcher scripts that invoke codegluer-gui and the GUI path where it
shells out to codegluer so they use an absolute installed path or compute it
relative to the script, and keep the PATH warning in install.sh only as a
fallback hint. Use the codegluer-gui launch points and the internal codegluer
invocation as the places to fix.
- Around line 60-69: Create the target install directory before copying the GUI
so fresh systems do not fail when ~/.local/bin is missing. In install.sh, around
the GUI_SRC/GUI_DEST setup, add a step before the cp in the GUI install flow to
ensure the parent directory for GUI_DEST exists, then keep the existing chmod on
the installed codegluer-gui binary. Refer to the install script’s GUI copy block
to place the directory creation in the right spot.

In `@test_codegluer_gui.py`:
- Around line 1-308: Wrap the module-level self-check logic in
`test_codegluer_gui.py` with an `if __name__ == "__main__":` guard so importing
the file during pytest collection does not execute the checks. Keep the
direct-run behavior intact by moving the existing top-level test setup and 22
checks under that guard, and use the existing helpers like `assert_present`,
`assert_absent`, and `assert_eq` there. Also avoid import-time `sys.exit(1)`
during collection by ensuring it only runs in the guarded main path.
- Around line 186-188: Avoid explicit equality checks against True/False in the
test assertions using cg.is_any_dir; replace them with direct truthy/falsey
assertions for consistency with Ruff E712. Update the three assertions in
test_codegluer_gui.py to use the result of is_any_dir(...) directly, keeping the
same behavior while matching the linted style used elsewhere.

---

Nitpick comments:
In `@codegluer_gui.py`:
- Line 91: The command list construction in codegluer_gui.py uses concatenation
for cmd, which should be simplified to unpack the files directly; update the
command assembly near the cmd assignment to use list unpacking instead of
["codegluer"] + files, keeping the existing behavior in the same place.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 34799af8-3923-4ce7-b706-6dd1c5ee8a87

📥 Commits

Reviewing files that changed from the base of the PR and between e37c2ab and 68a4285.

📒 Files selected for processing (3)
  • codegluer_gui.py
  • install.sh
  • test_codegluer_gui.py

Comment thread codegluer_gui.py
Comment thread codegluer_gui.py
Comment thread codegluer_gui.py Outdated
Comment thread install.sh
Comment thread install.sh Outdated
Comment thread install.sh
Comment thread test_codegluer_gui.py Outdated
Comment thread test_codegluer_gui.py Outdated
Apply nine fixes identified during code review of the GTK4 file-manager
integration, improving reliability, correctness, and compatibility.

Correctness fixes:
- build_command: --toc now gated on fmt == 'markdown' (codegluer's TOC
  is markdown-only; previously passed unconditionally)
- _on_format_changed: clear TOC checkbox when switching to plain,
  keeping UI state consistent with the command builder
- _apply_theme: reuse a single Gtk.CssProvider per window instead of
  allocating a new one on every apply (was leaking providers)
- _on_glue: guard Gtk.AlertDialog with a GTK 4.10+ version check,
  falling back to silent auto-fix on older GTK 4.0-4.9 installs

Reliability fixes:
- install.sh: move GTK4 Python bindings check before the pipx/pip
  install step so missing deps fail fast instead of leaving a partial
  install behind
- install.sh: mkdir -p ~/.local/bin before copying codegluer-gui so
  fresh systems without the directory don't fail
- install.sh + launchers: use absolute path $HOME/.local/bin/codegluer-gui
  in Nautilus/Nemo integration; GUI resolves codegluer via shutil.which
  with ~/.local/bin fallback so right-click launches work even when
  the user's shell PATH is incomplete (common in file-manager contexts)

Code quality:
- test_codegluer_gui.py: wrap all test logic in if __name__ == '__main__'
  so pytest collection doesn't execute the self-checks or trigger
  sys.exit(1) at import time
- test_codegluer_gui.py: replace == True / == False assertions with
  truthy/falsey checks (Ruff E712)
- build_command: use list unpacking ['codegluer', *files] instead of
  concatenation
- Remove unused 'import json' from codegluer_gui.py

Add Test 23 as a regression guard for the TOC+plain fix, verifying
--toc is absent from the command when format is plain even if the
user had the TOC checkbox enabled.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
codegluer_gui.py (1)

426-447: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Blocking subprocess.run with no timeout on the UI thread.

_execute runs codegluer synchronously inside a button-click handler with no timeout= argument. If the underlying command hangs (large tree, slow filesystem, symlink loop), the GTK window freezes indefinitely with no way for the user to cancel or see progress.

Consider adding a timeout (catching subprocess.TimeoutExpired) as a minimum safeguard, or better, run the command asynchronously (e.g., via GLib.spawn_async or a worker thread with GLib.idle_add to marshal results back) so the UI stays responsive.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@codegluer_gui.py` around lines 426 - 447, The `_execute` method is running
`subprocess.run` synchronously on the UI thread with no safeguard, so update
this flow to avoid freezing the GTK window when `codegluer` hangs. Add a timeout
to the `subprocess.run` call and handle `subprocess.TimeoutExpired` in the
existing try/except path, or preferably move the command execution out of
`_execute` into an asynchronous worker and use `GLib.idle_add` to deliver
notifications back to the UI. Keep the existing `build_command`, `save_theme`,
and `_notify` behavior intact while ensuring the close/notify logic still runs
after completion or timeout.
🧹 Nitpick comments (2)
test_codegluer_gui.py (1)

196-198: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Awkward inline import via __import__.

cg.CONFIG_DIR = Path = __import__("pathlib").Path assigns cg.CONFIG_DIR to the Path class itself, immediately overwritten on the next line. A plain from pathlib import Path at the top of the file (or inline) would be clearer.

♻️ Suggested fix
-    cg.CONFIG_DIR = Path = __import__("pathlib").Path
-    cg.CONFIG_DIR = Path(TMP) / "config" / "codegluer"
+    from pathlib import Path
+    cg.CONFIG_DIR = Path(TMP) / "config" / "codegluer"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test_codegluer_gui.py` around lines 196 - 198, The test setup currently uses
an awkward inline __import__("pathlib").Path assignment that briefly sets
cg.CONFIG_DIR to the Path class before overwriting it. Replace this with a
direct Path import in the test module and use that Path symbol when configuring
cg.CONFIG_DIR and cg.CONFIG_FILE so the setup is clear and consistent.
codegluer_gui.py (1)

430-430: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor style nit: use unpacking instead of concatenation.

Same pattern already fixed at line 91 for build_command; static analysis flags this occurrence too.

♻️ Suggested fix
-            cmd = [codegluer_path] + build_command(self.files, opts)[1:]
+            cmd = [codegluer_path, *build_command(self.files, opts)[1:]]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@codegluer_gui.py` at line 430, The command construction in the codegluer_gui
flow still uses list concatenation for build_command, which static analysis
flags as a style issue. Update the cmd assembly to use unpacking instead of
concatenating codegluer_path with the sliced build_command(self.files, opts)
result, following the same pattern already used in build_command so the command
list is built consistently.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@codegluer_gui.py`:
- Around line 426-447: The `_execute` method is running `subprocess.run`
synchronously on the UI thread with no safeguard, so update this flow to avoid
freezing the GTK window when `codegluer` hangs. Add a timeout to the
`subprocess.run` call and handle `subprocess.TimeoutExpired` in the existing
try/except path, or preferably move the command execution out of `_execute` into
an asynchronous worker and use `GLib.idle_add` to deliver notifications back to
the UI. Keep the existing `build_command`, `save_theme`, and `_notify` behavior
intact while ensuring the close/notify logic still runs after completion or
timeout.

---

Nitpick comments:
In `@codegluer_gui.py`:
- Line 430: The command construction in the codegluer_gui flow still uses list
concatenation for build_command, which static analysis flags as a style issue.
Update the cmd assembly to use unpacking instead of concatenating codegluer_path
with the sliced build_command(self.files, opts) result, following the same
pattern already used in build_command so the command list is built consistently.

In `@test_codegluer_gui.py`:
- Around line 196-198: The test setup currently uses an awkward inline
__import__("pathlib").Path assignment that briefly sets cg.CONFIG_DIR to the
Path class before overwriting it. Replace this with a direct Path import in the
test module and use that Path symbol when configuring cg.CONFIG_DIR and
cg.CONFIG_FILE so the setup is clear and consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5a28f2e6-9067-4cae-a59f-d0898171465d

📥 Commits

Reviewing files that changed from the base of the PR and between 68a4285 and 7b57094.

📒 Files selected for processing (3)
  • codegluer_gui.py
  • install.sh
  • test_codegluer_gui.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • install.sh

The _execute method was running subprocess.run synchronously on the
GTK main thread with no timeout. If codegluer hung (e.g. waiting on
a slow filesystem, a large directory tree, or an unresponsive pipe),
the entire GUI would freeze indefinitely with no way to recover.

Add a 60-second timeout to the subprocess.run call and handle
subprocess.TimeoutExpired with a user-visible notification so the
user knows what happened and the window closes cleanly. The timeout
is generous enough that legitimate large glues complete normally,
but short enough that a genuinely stuck process doesn't strand the
user with a frozen window.

Also clean up two style issues flagged by static analysis:
- _execute: replace list concatenation with unpacking
  [codegluer_path, *build_command(...)[1:]] to match the pattern
  already used in build_command itself
- test_codegluer_gui.py: import Path at module level instead of
  the awkward inline __import__('pathlib').Path assignment that
  briefly set cg.CONFIG_DIR to the Path class before overwriting it
@fathriAbanoub
fathriAbanoub merged commit a1b267c into main Jul 1, 2026
1 check passed
@fathriAbanoub
fathriAbanoub deleted the feature/gtk4-gui branch July 1, 2026 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant