Skip to content

Test#23

Merged
AlanRockefeller merged 2 commits intomainfrom
test
Dec 31, 2025
Merged

Test#23
AlanRockefeller merged 2 commits intomainfrom
test

Conversation

@AlanRockefeller
Copy link
Copy Markdown
Owner

@AlanRockefeller AlanRockefeller commented Dec 10, 2025

Summary by CodeRabbit

  • New Features

    • Added auto-level strength automation mode to prevent clipping when adjusting levels.
    • Added AWB tint bias control for enhanced white balance adjustment.
    • Added right-click crop functionality in the image editor.
    • Added keyboard shortcuts: H for RGB histogram, I for EXIF metadata display.
    • Improved zoom/pan interaction with cursor position awareness.
    • Enhanced crop dragging with aspect-ratio constraints.
  • Bug Fixes

    • Improved crop box validation and bounds checking.
    • Enhanced EXIF metadata preservation during image export.
  • Documentation

    • Added tooltips for AWB controls and expanded control ranges.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 10, 2025

Walkthrough

This pull request adds configurable auto-level strength adaptation, AWB tint bias control, and crop state reset on image navigation. It enhances image editing with EXIF metadata handling during save, improves rotation/cropping in the image editor with aspect-ratio constraints, and expands the UI with new settings, keyboard shortcuts, and right-click crop interactions.

Changes

Cohort / File(s) Change Summary
Documentation & Metadata
faststack/ChangeLog.md, faststack/faststack.egg-info/PKG-INFO
Added changelog entries for Auto-Levels, Image Metadata, Image Processing, and Crop Operations; fixed typographical error ("increassing" → "increasing"); added keyboard shortcuts for RGB Histogram (H) and EXIF Metadata (I) to package info.
Configuration
faststack/faststack/config.py
Added new configuration keys: core.auto_level_strength_auto (bool, default False) and awb.tint_bias (int, default 0) to DEFAULT_CONFIG with initialization on load.
Application Controller & Logic
faststack/faststack/app.py
Added _reset_crop_settings() helper to normalize crop/rotation state on image navigation; introduced adaptive auto-level strength logic via auto_level_strength_auto flag; added public slots for get/set_auto_level_strength_auto and get/set_awb_tint_bias; enhanced save/crop flows with status updates and undo history tracking; updated histogram refresh after undo operations.
Image Editor & Processing
faststack/faststack/imaging/editor.py
Added is_export flag to _apply_edits(); swapped rotation orientations (90↔270) for CW interpretation; implemented free rotation with negative angle for PIL compatibility; added crop box normalization from 0-1000 to pixel coordinates with bounds clamping; introduced EXIF orientation sanitization (tag 274) and serialization during save with graceful fallbacks.
UI State Management
faststack/faststack/ui/provider.py
Added awbTintBias and autoLevelStrengthAuto properties with corresponding signals (awbTintBiasChanged, autoLevelStrengthAutoChanged); extended currentCropBox setter to synchronize changes to image editor.
QML Settings Dialog
faststack/faststack/qml/SettingsDialog.qml
Introduced autoLevelStrengthAuto (bool) and awbTintBias (int) properties; refactored Auto Levels Strength UI from single slider to RowLayout with auto checkbox; added per-field tooltips and expanded AWB control ranges (e.g., awbWarmBias from -10..20 to -50..50); added tint bias SpinBox control.
QML Image Interaction
faststack/faststack/qml/Components.qml
Enabled right-click (Qt.RightButton) for crop interactions; introduced right-click crop start/cancel logic; refactored crop box update to use normalized 0-1000 space with aspect-ratio constrained dragging across multiple modes (new, move, edges, corners); improved zoom/pan to maintain cursor position during zoom; added updateCropBoxFromAspectRatio() and enhanced boundary/minimum-size checks.
QML Histogram Window
faststack/faststack/qml/HistogramWindow.qml
Adjusted H key press handler to set event.accepted = true after controller call with clarifying comments on key propagation behavior.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant QML as QML UI<br/>(SettingsDialog)
    participant UIState as UIState<br/>(provider.py)
    participant AppCtrl as AppController<br/>(app.py)
    participant Config as Config<br/>(config.py)
    participant ImgEditor as ImageEditor<br/>(editor.py)

    User->>QML: Toggle "Auto Level Strength Auto"
    QML->>UIState: set autoLevelStrengthAuto(true)
    UIState->>AppCtrl: autoLevelStrengthAutoChanged signal
    AppCtrl->>Config: set_auto_level_strength_auto(true)
    Config-->>AppCtrl: persisted to INI

    User->>QML: Trigger auto-levels
    QML->>AppCtrl: auto_levels()
    alt auto_level_strength_auto enabled
        AppCtrl->>ImgEditor: compute adaptive strength<br/>(avoid clipping)
        ImgEditor-->>AppCtrl: strength value
        AppCtrl->>ImgEditor: apply with adaptive strength
    else fixed mode
        AppCtrl->>ImgEditor: apply with fixed strength
    end
    ImgEditor-->>AppCtrl: result (bool/success)
    AppCtrl->>UIState: emit status update
    UIState->>QML: refresh display
    QML-->>User: show result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • faststack/faststack/qml/Components.qml: Extensive refactoring of crop interaction logic, aspect-ratio constrained dragging, and zoom/pan behavior with multiple coordinate transformations and edge cases—requires careful verification of drag modes and boundary handling.
  • faststack/faststack/app.py: Multiple feature additions (auto-level strength auto mode, crop state reset, AWB tint bias slots) with state synchronization and undo history integration—verify control flow consistency across auto_levels, crop, and navigation paths.
  • faststack/faststack/imaging/editor.py: EXIF metadata handling with rotation fixes and cropping normalization—verify EXIF serialization fallbacks, rotation direction correctness, and crop coordinate conversions.
  • faststack/faststack/qml/SettingsDialog.qml: Significant UI restructuring with new tooltips and expanded control ranges—verify data binding and state propagation.

Possibly related PRs

  • PR #21: Overlaps on auto-levels enhancement, crop behavior, image editor modifications, and settings UI expansion.
  • PR #19: Shares AWB configuration additions (tint bias) and AppController slot exposure for UI binding.
  • PR #22: Related through auto-levels undo handling and crop/rotation logic modifications in imaging/editor.py and ui/provider.py.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.54% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The pull request title 'Test' is vague and generic, providing no meaningful information about the substantial changes made across multiple files including auto-level strength, AWB tint bias, crop operations, histogram, and image processing features. Replace the title with a descriptive summary of the main changes, such as 'Add auto-level strength auto mode, AWB tint bias, and crop UI improvements' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (7)
faststack/ChangeLog.md (1)

21-22: Wrap bare URL in angle brackets or use Markdown link syntax.

Static analysis flagged a bare URL on line 21. Converting to a proper Markdown link improves readability and compatibility.

-- Some fixes to the image cache - it doesn't expire when it shouldn't, does expire when it should, and warns you when the cache is full so you can consider increasing the cache size in settings.
+- Some fixes to the image cache - it doesn't expire when it shouldn't, does expire when it should, and warns you when the cache is full so you can consider increasing the cache size in settings.

Note: The static analysis tool flagged the URL on line 21 (https://github.com/AlanRockefeller/faststack), which should be wrapped in angle brackets <url> or converted to a Markdown link [text](url).

faststack/faststack/imaging/editor.py (1)

132-132: Unused is_export parameter - implement or remove.

The is_export parameter is added but never used within _apply_edits. If this is a placeholder for future export-specific behavior (e.g., different quality settings), consider adding a TODO comment. Otherwise, remove it to avoid confusion.

Static analysis (Ruff ARG002) correctly flagged this as unused.

-    def _apply_edits(self, img: Image.Image, is_export: bool = False) -> Image.Image:
+    def _apply_edits(self, img: Image.Image) -> Image.Image:

Or if intentional for future use:

-    def _apply_edits(self, img: Image.Image, is_export: bool = False) -> Image.Image:
+    def _apply_edits(self, img: Image.Image, is_export: bool = False) -> Image.Image:  # noqa: ARG002 - reserved for export-specific processing
faststack/faststack/config.py (2)

19-21: Document the new INI key.

Per coding guidelines, new INI keys in config.py should be documented. Add a comment explaining what auto_level_strength_auto controls.

Based on learnings, document any new INI key in config.py.

         "auto_level_strength": "1.0",
-        "auto_level_strength_auto": "False",
+        "auto_level_strength_auto": "False",  # When True, automatically adjust strength based on image histogram

38-40: Document the new tint_bias INI key.

Per coding guidelines, add documentation for the new AWB tint bias setting.

         "warm_bias": "6",
-        "tint_bias": "0",
+        "tint_bias": "0",  # Magenta/Green bias for AWB (-50 to 50)
faststack/faststack/qml/Components.qml (1)

619-801: Complex but comprehensive aspect ratio constraint handling.

The logic handles all drag modes (edges, corners, new) with proper boundary clamping. A few observations:

  1. Multiple var newW/var newH declarations in different branches are valid JavaScript but could benefit from block scoping (let) for clarity.
  2. The corner-specific logic (lines 728-798) is thorough but verbose.

This is acceptable given the inherent complexity of aspect-ratio-constrained cropping.

Consider extracting corner constraint logic into a helper function to reduce duplication:

function constrainCorner(anchorX, anchorY, width, height, targetAspect, maxX, maxY) {
    // Common logic for corner constraints
}
faststack/faststack/app.py (2)

974-993: Prefer set_edit_param over direct dictionary manipulation.

Lines 992-993 directly modify self.image_editor.current_edits['straighten_angle'], which bypasses the set_edit_param method used elsewhere in this function (lines 983-984). This inconsistency could lead to missed side effects or future maintenance issues.

Apply this diff to use the consistent method:

-        # Also reset the straighten angle in current_edits since it affects rotation logic
-        if 'straighten_angle' in self.image_editor.current_edits:
-             self.image_editor.current_edits['straighten_angle'] = 0.0
+        # Ensure straighten_angle is reset via the proper method
+        self.image_editor.set_edit_param('straighten_angle', 0.0)

2708-2743: Strengthen error handling in auto-strength calculation.

Line 2735 catches a broad Exception, which can mask unexpected errors. Consider catching more specific exceptions related to image processing and mathematical operations.

Apply this diff to improve exception handling:

-            except Exception as e:
-                log.warning(f"Failed to calculate auto strength: {e}")
+            except (AttributeError, ValueError, ZeroDivisionError) as e:
+                log.warning("Failed to calculate auto strength: %s", e)
                 strength = self.auto_level_strength

Additionally, verify the division edge case:

At line 2725, the check abs(denom) > 0.001 might not be robust enough. Consider whether stricter validation or a different threshold is needed based on your image processing requirements.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3ccbece and 15b3570.

📒 Files selected for processing (9)
  • faststack/ChangeLog.md (1 hunks)
  • faststack/faststack.egg-info/PKG-INFO (1 hunks)
  • faststack/faststack/app.py (15 hunks)
  • faststack/faststack/config.py (2 hunks)
  • faststack/faststack/imaging/editor.py (4 hunks)
  • faststack/faststack/qml/Components.qml (9 hunks)
  • faststack/faststack/qml/HistogramWindow.qml (1 hunks)
  • faststack/faststack/qml/SettingsDialog.qml (9 hunks)
  • faststack/faststack/ui/provider.py (5 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
faststack/faststack/**/*.py

📄 CodeRabbit inference engine (faststack/faststack/AGENTS.md)

faststack/faststack/**/*.py: Use four-space indentation in Python files
Use trailing commas for multi-line structures in Python
Use descriptive snake_case identifiers for variables and functions in Python
Use CamelCase exclusively for class names and Qt signal names in Python
Include type hints in all Python code as seen in models.py and AppController
Every new Python module should expose a top-level log = logging.getLogger(__name__) logger
Format Python code with Black before opening a pull request
Lint Python code with Ruff before opening a pull request
Prefer relative paths in configuration and file handling

Files:

  • faststack/faststack/config.py
  • faststack/faststack/ui/provider.py
  • faststack/faststack/imaging/editor.py
  • faststack/faststack/app.py
faststack/faststack/**/config.py

📄 CodeRabbit inference engine (faststack/faststack/AGENTS.md)

Document any new INI key in config.py

Files:

  • faststack/faststack/config.py
faststack/faststack/ui/**/*.py

📄 CodeRabbit inference engine (faststack/faststack/AGENTS.md)

Treat drag-and-drop helpers like make_hdrop as security-sensitive and validate all user-supplied paths before invoking external tools

Files:

  • faststack/faststack/ui/provider.py
faststack/faststack/imaging/**/*.py

📄 CodeRabbit inference engine (faststack/faststack/AGENTS.md)

Cover every new branch in imaging/ or io/ modules with focused pytest test cases

Files:

  • faststack/faststack/imaging/editor.py
faststack/faststack/**/app.py

📄 CodeRabbit inference engine (faststack/faststack/AGENTS.md)

app.py is the PySide6 entrypoint that coordinates image indexing, caching, and QML screens

Files:

  • faststack/faststack/app.py
🧠 Learnings (2)
📚 Learning: 2025-12-08T04:46:41.632Z
Learnt from: CR
Repo: AlanRockefeller/faststack PR: 0
File: faststack/faststack/AGENTS.md:0-0
Timestamp: 2025-12-08T04:46:41.632Z
Learning: Applies to faststack/faststack/**/config.py : Document any new INI key in `config.py`

Applied to files:

  • faststack/faststack/config.py
📚 Learning: 2025-12-08T04:46:41.632Z
Learnt from: CR
Repo: AlanRockefeller/faststack PR: 0
File: faststack/faststack/AGENTS.md:0-0
Timestamp: 2025-12-08T04:46:41.632Z
Learning: Applies to faststack/faststack/**/app.py : `app.py` is the PySide6 entrypoint that coordinates image indexing, caching, and QML screens

Applied to files:

  • faststack/faststack/app.py
🧬 Code graph analysis (2)
faststack/faststack/ui/provider.py (2)
faststack/faststack/app.py (5)
  • get_awb_tint_bias (1271-1272)
  • set_awb_tint_bias (1275-1277)
  • get_auto_level_strength_auto (2842-2843)
  • set_auto_level_strength_auto (2846-2850)
  • set_crop_box (2242-2247)
faststack/faststack/imaging/editor.py (1)
  • set_crop_box (382-384)
faststack/faststack/imaging/editor.py (2)
faststack/faststack/ui/provider.py (2)
  • rotation (761-762)
  • rotation (765-768)
faststack/faststack/config.py (1)
  • get (86-87)
🪛 markdownlint-cli2 (0.18.1)
faststack/ChangeLog.md

21-21: Bare URL used

(MD034, no-bare-urls)

🪛 Ruff (0.14.8)
faststack/faststack/imaging/editor.py

132-132: Unused method argument: is_export

(ARG002)


436-436: Do not catch blind exception: Exception

(BLE001)

faststack/faststack/app.py

2612-2612: Do not use bare except

(E722)


2612-2613: try-except-pass detected, consider logging the exception

(S110)


2735-2735: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (17)
faststack/faststack/imaging/editor.py (1)

420-439: LGTM - Defensive EXIF handling.

The EXIF orientation sanitization and fallback logic is well-structured. The broad Exception catch on line 436 is acceptable here since EXIF parsing can fail in unpredictable ways, and the fallback (skipping EXIF to prevent double-rotation) is the safest option.

faststack/faststack/qml/HistogramWindow.qml (1)

20-26: LGTM - Correct keyboard event handling.

Moving event.accepted = true to only execute for the H key ensures other keypresses propagate correctly. The clarifying comment improves maintainability.

faststack/faststack/ui/provider.py (2)

342-349: LGTM - AWB tint bias property.

The property follows the established pattern with proper signal emission and delegation to the controller.


511-518: LGTM - Auto level strength auto property.

The property correctly follows the existing pattern with boolean type and proper signal notification.

faststack/faststack/qml/SettingsDialog.qml (2)

249-268: LGTM - Auto Levels Strength UI.

The RowLayout with slider and Auto checkbox is well-structured. The enabled/opacity binding provides clear visual feedback when auto mode is active.


340-359: LGTM - AWB Tint Bias control.

The new SpinBox for tint bias follows the same pattern as warm bias, with appropriate range (-50 to 50), editability, and tooltip.

faststack/faststack/qml/Components.qml (4)

133-133: LGTM - Right-click support for crop mode.

Expanding acceptedButtons to include Qt.RightButton enables the right-click crop initiation feature, which is a sensible UX addition.


171-198: LGTM - Right-click crop toggle behavior.

The implementation correctly:

  • Cancels crop mode if already active
  • Enters crop mode and initializes a new crop if inactive
  • Returns early to prevent left-click handling

498-538: LGTM - Unified zoom out behavior.

Changing zoom out to use cursor position (same as zoom in) provides a consistent and intuitive zoom experience. The comments explain the rationale well.


340-340: Verify rotation direction is correct.

The rotation calculation sign was changed. Combined with the handle placement fix (lines 216-218), ensure the rotation behaves as expected (CW-positive per the comment on line 216).

faststack/faststack/app.py (7)

169-169: LGTM!

The initialization follows the established pattern for config-backed properties and properly uses getboolean with a sensible default.


1270-1277: LGTM!

The AWB tint bias getters and setters follow the established pattern for config-backed properties and are correctly decorated as Qt Slots for QML integration.


481-489: Verify navigation crop reset doesn't disrupt workflows.

The addition of _reset_crop_settings() on image navigation ensures a clean state for each image. However, this means any in-progress crop adjustments will be lost when navigating away.

Please confirm this behavior is intentional and won't frustrate users who might navigate while adjusting crops. Consider whether a warning or "unsaved changes" prompt would improve UX.


1786-1787: LGTM!

The histogram update ensures UI consistency after undoing auto white balance, matching the pattern used for auto levels undo.


2768-2780: LGTM!

The logic correctly respects the auto-strength mode by checking the return value from auto_levels() and skipping the save operation when no adjustment is needed. This prevents unnecessary file writes and backup creation.


2841-2850: LGTM!

The auto-level strength auto property implementation follows the established pattern for boolean config properties, with proper type conversions and Qt Slot decorators.


3005-3006: LGTM!

The tint bias integration correctly adjusts the target A* channel in Lab color space, following the same pattern as the existing warm bias for the B* channel. The implementation aligns with standard color science practices.

Comment on lines +72 to +73
+- `H`: Show RGB Histogram
+- `I`: Show EXIF Metadata
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove extraneous + prefix from keyboard shortcut entries.

The lines appear to have +- as a prefix, which looks like diff notation accidentally included in the file content. This will display incorrectly in the rendered documentation.

-+- `H`: Show RGB Histogram
-+- `I`: Show EXIF Metadata
+- `H`: Show RGB Histogram
+- `I`: Show EXIF Metadata
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
+- `H`: Show RGB Histogram
+- `I`: Show EXIF Metadata
- `H`: Show RGB Histogram
- `I`: Show EXIF Metadata
🤖 Prompt for AI Agents
In faststack/faststack.egg-info/PKG-INFO around lines 72 to 73, the keyboard
shortcut entries incorrectly include diff-style '+-' prefixes; remove the
leading '+ ' or '+-' characters so the lines read "H: Show RGB Histogram" and
"I: Show EXIF Metadata" (no leading plus or minus), ensuring the PKG-INFO
content contains only the intended text.

Comment on lines +2608 to +2618
if not isinstance(crop_box_raw, tuple) or len(crop_box_raw) != 4:
# Try to convert if it came as list
try:
crop_box_raw = tuple(crop_box_raw) if isinstance(crop_box_raw, list) else tuple(crop_box_raw.toVariant())
except:
pass

if not isinstance(crop_box_raw, tuple) or len(crop_box_raw) != 4:
self.update_status_message("Invalid crop box")
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Fix bare except clause and add logging.

The bare except at line 2612 suppresses all exceptions without logging, making debugging difficult. This violates error handling best practices.

Apply this diff to improve error handling:

-             try:
-                 crop_box_raw = tuple(crop_box_raw) if isinstance(crop_box_raw, list) else tuple(crop_box_raw.toVariant())
-             except:
-                 pass
+             try:
+                 crop_box_raw = tuple(crop_box_raw) if isinstance(crop_box_raw, list) else tuple(crop_box_raw.toVariant())
+             except (TypeError, AttributeError) as e:
+                 log.warning("Failed to convert crop_box to tuple: %s", e)

Based on coding guidelines, ensure proper error handling with specific exception types.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if not isinstance(crop_box_raw, tuple) or len(crop_box_raw) != 4:
# Try to convert if it came as list
try:
crop_box_raw = tuple(crop_box_raw) if isinstance(crop_box_raw, list) else tuple(crop_box_raw.toVariant())
except:
pass
if not isinstance(crop_box_raw, tuple) or len(crop_box_raw) != 4:
self.update_status_message("Invalid crop box")
return
if not isinstance(crop_box_raw, tuple) or len(crop_box_raw) != 4:
# Try to convert if it came as list
try:
crop_box_raw = tuple(crop_box_raw) if isinstance(crop_box_raw, list) else tuple(crop_box_raw.toVariant())
except (TypeError, AttributeError) as e:
log.warning("Failed to convert crop_box to tuple: %s", e)
if not isinstance(crop_box_raw, tuple) or len(crop_box_raw) != 4:
self.update_status_message("Invalid crop box")
return
🧰 Tools
🪛 Ruff (0.14.8)

2612-2612: Do not use bare except

(E722)


2612-2613: try-except-pass detected, consider logging the exception

(S110)

🤖 Prompt for AI Agents
In faststack/faststack/app.py around lines 2608 to 2618, the bare except at line
~2612 swallows all exceptions and provides no logging; change it to catch the
likely exceptions (e.g., TypeError, AttributeError, ValueError) when attempting
to convert crop_box_raw and log the error before continuing so the subsequent
validity check still runs. Specifically, replace the bare except with an except
(TypeError, AttributeError, ValueError) as e: and call the instance logger
(e.g., self.logger.exception or self.logger.error with the exception) to record
the failure to convert crop_box_raw, then allow the code to fall through so the
"Invalid crop box" status is set when appropriate.

Comment on lines 150 to 166
# 3. Cropping
crop_box = self.current_edits.get('crop_box')
if crop_box:
if crop_box and len(crop_box) == 4:
width, height = img.size
# Normalized 0-1000 to pixels
left = int(crop_box[0] * width / 1000)
top = int(crop_box[1] * height / 1000)
right = int(crop_box[2] * width / 1000)
bottom = int(crop_box[3] * height / 1000)

# Bounds check
left = max(0, min(width - 1, left))
top = max(0, min(height - 1, top))
right = max(left + 1, min(width, right))
bottom = max(top + 1, min(height, bottom))

img = img.crop((left, top, right, bottom))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*test*.py" | grep -i "editor\|imaging" | head -20

Repository: AlanRockefeller/faststack

Length of output: 111


🏁 Script executed:

rg -l "crop" --type=py | grep -i test

Repository: AlanRockefeller/faststack

Length of output: 51


🏁 Script executed:

rg -n "def test.*crop" --type=py

Repository: AlanRockefeller/faststack

Length of output: 51


🏁 Script executed:

# Also check the structure of imaging tests
fd -t f "test" faststack/faststack/imaging/ 2>/dev/null || echo "No imaging tests found in that exact location"

Repository: AlanRockefeller/faststack

Length of output: 51


🏁 Script executed:

# Look for pytest test directory
fd -t d "test" faststack/ 2>/dev/null | head -10

Repository: AlanRockefeller/faststack

Length of output: 95


🏁 Script executed:

cat -n faststack/faststack/tests/test_editor.py

Repository: AlanRockefeller/faststack

Length of output: 1003


🏁 Script executed:

# Also check the editor.py file to understand the full cropping implementation
wc -l faststack/faststack/imaging/editor.py

Repository: AlanRockefeller/faststack

Length of output: 110


🏁 Script executed:

# Get the cropping method context
sed -n '140,180p' faststack/faststack/imaging/editor.py

Repository: AlanRockefeller/faststack

Length of output: 1850


🏁 Script executed:

# Double-check if there are any other test files that might test crop functionality
rg -l "ImageEditor\|crop" faststack/faststack/tests/ --type=py

Repository: AlanRockefeller/faststack

Length of output: 51


Add pytest test cases for the cropping logic in ImageEditor.

Per coding guidelines, every new branch in imaging/ modules must have focused pytest test cases. The crop bounds checking and normalization logic (lines 150-166) introduces multiple branches—including crop_box validation, normalization from 0-1000 range to pixels, and bounds clamping with max/min operations—that are not currently tested. Add tests covering edge cases such as invalid crop_box values, extreme normalized values, and boundary conditions where left/top/right/bottom need adjustment.

🤖 Prompt for AI Agents
In faststack/faststack/imaging/editor.py around lines 150 to 166, the cropping
logic (validation of crop_box length, normalization from 0-1000 to pixel coords,
and bounds clamping) lacks pytest coverage; add focused tests that call the
ImageEditor crop path and assert expected output images or box coordinates for:
invalid crop_box values (None, wrong length, non-numeric) to ensure no crop is
applied or proper error handling, extreme normalized values (0, 1000, values
outside range like -100, 1100) to verify normalization maps to image edges, and
boundary conditions where left/top/right/bottom would be adjusted (e.g.,
normalized coords that would produce zero-width/height) to confirm clamping
enforces minimum size and stays within image dimensions.

Comment on lines +736 to +737
# Sync with ImageEditor
self.app_controller.image_editor.set_crop_box(new_value)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add null check before accessing image_editor.

Direct access to self.app_controller.image_editor may raise AttributeError if image_editor hasn't been initialized when the crop box is set early in the lifecycle.

             self.current_crop_box_changed.emit(new_value)
             # Sync with ImageEditor
-            self.app_controller.image_editor.set_crop_box(new_value)
+            if hasattr(self.app_controller, 'image_editor') and self.app_controller.image_editor:
+                self.app_controller.image_editor.set_crop_box(new_value)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Sync with ImageEditor
self.app_controller.image_editor.set_crop_box(new_value)
# Sync with ImageEditor
if hasattr(self.app_controller, 'image_editor') and self.app_controller.image_editor:
self.app_controller.image_editor.set_crop_box(new_value)
🤖 Prompt for AI Agents
In faststack/faststack/ui/provider.py around lines 736-737, direct access to
self.app_controller.image_editor can raise AttributeError if image_editor is not
yet initialized; add a null/attribute check before calling set_crop_box: verify
image_editor exists and is not None (e.g., hasattr/getattr) and only call
set_crop_box when available, otherwise skip the call (or enqueue the crop update
for later) to avoid runtime exceptions.

@AlanRockefeller AlanRockefeller merged commit af24611 into main Dec 31, 2025
1 check passed
This was referenced Jan 2, 2026
Merged
@coderabbitai coderabbitai Bot mentioned this pull request Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant