Skip to content

Comments

Redesign UI with modern dark theme and improved UX#1

Open
DevStrategist wants to merge 1 commit intomainfrom
improve-ui-experience
Open

Redesign UI with modern dark theme and improved UX#1
DevStrategist wants to merge 1 commit intomainfrom
improve-ui-experience

Conversation

@DevStrategist
Copy link
Owner

@DevStrategist DevStrategist commented Dec 10, 2025

Summary

  • Complete UI overhaul with a modern dark theme inspired by Tokyo Night color palette
  • Implemented custom styled widgets (ModernButton with hover effects, labeled time inputs, status bar)
  • Redesigned layout to be compact and require no scrolling for controls
  • Made preset buttons additive - they now adjust existing values instead of replacing them
  • Added syntax highlighting for timestamps and line numbers in the preview panel

Changes

Visual Design

  • Dark color scheme with carefully chosen accent colors for different states
  • Custom rounded buttons with hover/click feedback
  • Card-style sections with clear visual hierarchy
  • Status bar showing current state, file info, and keyboard shortcuts

UX Improvements

  • Labeled time inputs (Hours, Min, Sec, Ms) for clarity
  • Additive preset buttons (+1s, +.5, -.5, -1s) that modify current values
  • Reset button to restore original content before saving
  • Sync dialog button renamed to "Calculate" for better clarity
  • Tooltips on all interactive elements

Technical

  • Custom widget classes: ModernButton, CardFrame, StatusBar, Tooltip
  • Syntax highlighting for SRT timestamps using text tags
  • Keyboard shortcuts: ⌘O (open), ⌘S (save), ⌘⇧A (apply shift)
  • Responsive layout with proper grid weights

Test plan

  • Open an SRT file and verify syntax highlighting works
  • Test time shift with various values and verify preview updates correctly
  • Test preset buttons - verify they add/subtract from current values
  • Test Sync Line feature - select a timestamp and calculate shift
  • Verify Save creates properly shifted SRT file
  • Test Reset button restores original content
  • Verify keyboard shortcuts work (⌘O, ⌘S, ⌘⇧A)
  • Check all buttons have working tooltips

Summary by CodeRabbit

  • New Features

    • Modern dark interface redesign with improved visual aesthetics
    • Real-time preview of timestamp shifts before applying changes
    • Keyboard shortcuts for quick access (Open, Save, Apply)
    • Contextual help tooltips throughout the interface
    • Preset management for common shift scenarios
    • Syntax highlighting and line numbers in content editor
  • User Experience

    • Enhanced status bar providing real-time feedback on all actions
    • Improved error messages and success confirmations
    • Refined dialog workflow for timestamp synchronization
    • Better file handling and subtitle management

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

- Implement modern dark color scheme (Tokyo Night inspired)
- Add custom styled widgets: ModernButton, ModernSpinbox, CardFrame
- Create compact, no-scroll layout for all controls
- Add labeled time inputs (Hours, Min, Sec, Ms) for clarity
- Make preset buttons additive (adjust values instead of replacing)
- Add syntax highlighting for timestamps in preview
- Add line numbers to preview panel
- Include status bar with keyboard shortcut hints
- Add tooltips to all buttons
- Improve sync dialog with proper sizing and "Calculate" button
- Add keyboard shortcuts (Cmd+O, Cmd+S, Cmd+Shift+A)
- Show subtitle count in status bar after file load
- Add Reset button to restore original content before saving
@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Walkthrough

The subtitle_sync.py file receives a complete UI/UX overhaul, transitioning from a ttk-based interface to a modern dark-themed design. Six new custom widget classes (Colors, Tooltip, ModernButton, ModernSpinbox, CardFrame, StatusBar) are introduced, and the SRTShifter class is heavily refactored to incorporate a new layout with panel-based UI construction, file loading with syntax highlighting, timestamp preview shifting, keyboard shortcuts, and enhanced status feedback.

Changes

Cohort / File(s) Summary
UI Component Classes
subtitle_sync.py (Colors, Tooltip, ModernButton, ModernSpinbox, CardFrame, StatusBar classes)
Six new custom widget classes: Colors for dark palette definition, Tooltip for contextual help, ModernButton for canvas-based buttons with hover states and rounded visuals, ModernSpinbox for styled numeric input with plus/minus controls, CardFrame as a titled container, and StatusBar for status and keyboard hint messages.
SRTShifter Class Refactoring
subtitle_sync.py (SRTShifter class)
Complete refactor of SRTShifter with new layout construction (create_ui, create_header, create_left_panel, create_right_panel), file loading workflow (browse_file), content display with line numbers (update_line_numbers), syntax highlighting (highlight_syntax), timestamp preview shifting (preview_shift, shift_timestamp), preset application (apply_preset), direction toggling (set_direction), modern dialog workflow (show_sync_dialog), and comprehensive error/success handling.
Entry Point
subtitle_sync.py (main function and name == "main" guard)
Adds main() startup function and name == "main" guard for script execution.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as SRTShifter UI
    participant SRTShifter as SRTShifter Class
    participant FileSystem as File System
    participant Preview as Preview Engine

    User->>UI: Click Browse/Load File
    UI->>SRTShifter: browse_file()
    SRTShifter->>FileSystem: Read SRT File
    FileSystem-->>SRTShifter: File Content
    SRTShifter->>SRTShifter: highlight_syntax()
    SRTShifter->>SRTShifter: update_line_numbers()
    SRTShifter->>UI: Update Display + Count Subtitles
    UI-->>User: File Loaded, UI Enabled

    User->>UI: Enter Time Shift Value
    UI->>SRTShifter: preview_shift(shift_value)
    SRTShifter->>Preview: shift_timestamp() for all timestamps
    Preview-->>SRTShifter: Shifted Content
    SRTShifter->>UI: Update Preview Panel
    UI-->>User: Preview Updated

    User->>UI: Click Apply/Save
    UI->>SRTShifter: show_sync_dialog()
    SRTShifter->>FileSystem: Write Shifted SRT
    FileSystem-->>SRTShifter: File Saved
    SRTShifter->>UI: StatusBar: Success Message
    UI-->>User: Save Complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Six new custom widget classes with interdependent styling and functionality requiring verification of visual correctness and event handling
  • Complete SRTShifter refactoring with numerous new methods (20+ additions/modifications) spanning UI construction, file I/O, preview logic, and dialog workflows
  • Timestamp parsing and shifting logic (shift_timestamp helper) requires careful validation for edge cases and correctness of time format handling
  • Syntax highlighting and line number syncing mechanisms need verification for proper content synchronization
  • Keyboard shortcuts and event binding flow (bind_shortcuts) should be reviewed for completeness and conflicts
  • Single-file scope reduces surface area, but method interdependencies and state management add complexity

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.55% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: a complete UI redesign with modern dark theme and improved user experience, which aligns with the primary focus of the PR.
✨ Finishing touches
  • 📝 Docstrings were successfully generated.
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch improve-ui-experience

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

Copy link

@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: 2

Caution

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

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

1063-1119: Preview shift implementation is correct; minor robustness improvements possible

The core shifting logic is sound:

  • Shift magnitude comes from the time fields; sign from direction_var.
  • You re-read the original file each time, so shifts are not compounded.
  • shift_timestamp clamps negative results to zero.
  • Highlighting and line numbers are refreshed correctly, and Save/Reset are enabled only after a successful preview.

Two minor improvement ideas:

  • The broad except Exception could be narrowed to expected errors (e.g., OSError, ValueError) so true programming errors propagate during development.
  • For very large SRTs, consider streaming or line-wise processing to avoid holding the entire file in memory twice, though this is likely fine for most real-world subtitle sizes.
🧹 Nitpick comments (7)
subtitle_sync.py (7)

43-86: Tooltip scheduling is generally fine; optional cleanup of pending after callbacks

The tooltip logic works, but schedule_show doesn’t cancel any existing pending after job. It’s harmless (extra callbacks just early-return), but you could optionally clear any previous scheduled_id before scheduling a new one to avoid redundant callbacks and keep the state tighter.


91-220: Custom button looks good; clarify config/configure usage for future callers

The ModernButton behavior and styling look solid. The only caveat is that config/configure are overridden in a way that treats state specially and may ignore additional options when state is present (e.g., config(state=tk.DISABLED, text="X") wouldn’t pass text through). That’s fine for current usage (you only pass state), but it’s worth documenting or refactoring to support mixed keyword updates if this widget is reused more broadly.


225-311: Unused width argument in ModernSpinbox.__init__

width is accepted but never used. Either wire it into the entry/overall layout or drop the parameter to avoid confusion and satisfy linting.


241-247: Unicode minus sign may confuse tooling and future edits

You’re using the Unicode minus sign in several labels/strings. Visually it’s nice, but it triggers linters and can be hard to distinguish from - in editors/diffs.

Consider standardizing on ASCII - in source and, if you really want the typographic minus, generating it at runtime or centralizing it in a constant so it’s easier to grep/change later.

Also applies to: 503-507, 528-535, 818-820, 1031-1033


753-760: Shortcuts are macOS-centric; consider Control-based bindings too

Key bindings use <Command-*>, which is perfect on macOS but will be inert on most Windows/Linux setups.

If you expect cross-platform use, consider also binding the Control variants (and updating the status bar hint accordingly), for example:

self.root.bind("<Control-o>", lambda e: self.browse_file())
self.root.bind("<Control-s>", lambda e: self.save_file())
self.root.bind("<Control-Shift-a>", lambda e: self.preview_shift())

924-1035: Sync dialog and shift calculation look correct; ms handling for negative deltas is a bit opaque

The sync dialog UX and the resulting shift computation are solid:

  • parse_timestamp to timedelta, then shift_delta = new_td - current_td is correct.
  • Direction (+/-) is derived from shift_delta.total_seconds().
  • The decomposition into hours/minutes/seconds plus milliseconds correctly handles negative timedeltas by using abs(total_seconds) for the h/m/s and the microsecond trick for ms.

The milliseconds expression for negative deltas:

milliseconds = int(
    (shift_delta.microseconds // 1000)
    if shift_delta.total_seconds() >= 0
    else (1000 - (shift_delta.microseconds // 1000)) % 1000
)

is mathematically correct but non-obvious. A short comment explaining why the negative branch is needed (Python’s internal representation of negative timedeltas) would help future maintainers.


1178-1187: Bare except around iconbitmap

iconbitmap('') is wrapped in a bare except that ignores all errors. Similar to the scaling block, consider at least catching tk.TclError explicitly or skipping the call entirely if there’s no valid icon path yet.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e327b5f and 45fd15c.

📒 Files selected for processing (1)
  • subtitle_sync.py (5 hunks)
🧰 Additional context used
🪛 Ruff (0.14.8)
subtitle_sync.py

56-56: Unused method argument: event

(ARG002)


59-59: Unused method argument: event

(ARG002)


79-79: Unused method argument: event

(ARG002)


188-188: Unused method argument: event

(ARG002)


193-193: Unused method argument: event

(ARG002)


197-197: Unused method argument: event

(ARG002)


202-202: Unused method argument: event

(ARG002)


227-227: Unused method argument: width

(ARG002)


241-241: String contains ambiguous (MINUS SIGN). Did you mean - (HYPHEN-MINUS)?

(RUF001)


246-246: Unused lambda argument: e

(ARG005)


247-247: Unused lambda argument: e

(ARG005)


263-263: Unused lambda argument: e

(ARG005)


264-264: Unused lambda argument: e

(ARG005)


274-274: Unused method argument: event

(ARG002)


277-277: Unused method argument: event

(ARG002)


289-289: Unused method argument: event

(ARG002)


297-297: Unused method argument: event

(ARG002)


402-402: Do not use bare except

(E722)


402-403: try-except-pass detected, consider logging the exception

(S110)


501-501: Unused lambda argument: e

(ARG005)


503-503: String contains ambiguous (MINUS SIGN). Did you mean - (HYPHEN-MINUS)?

(RUF001)


508-508: Unused lambda argument: e

(ARG005)


528-528: String contains ambiguous (MINUS SIGN). Did you mean - (HYPHEN-MINUS)?

(RUF001)


528-528: String contains ambiguous (MINUS SIGN). Did you mean - (HYPHEN-MINUS)?

(RUF001)


533-533: Unused lambda argument: e

(ARG005)


534-534: Unused lambda argument: e

(ARG005)


535-535: Unused lambda argument: e

(ARG005)


588-588: Unused function argument: event

(ARG001)


715-715: Unused method argument: event

(ARG002)


755-755: Unused lambda argument: e

(ARG005)


756-756: Unused lambda argument: e

(ARG005)


757-757: Unused lambda argument: e

(ARG005)


758-758: Unused lambda argument: e

(ARG005)


759-759: Unused lambda argument: e

(ARG005)


818-818: String contains ambiguous (MINUS SIGN). Did you mean - (HYPHEN-MINUS)?

(RUF001)


878-878: Do not catch blind exception: Exception

(BLE001)


879-879: Use explicit conversion flag

Replace with conversion flag

(RUF010)


919-919: Do not catch blind exception: Exception

(BLE001)


920-920: Use explicit conversion flag

Replace with conversion flag

(RUF010)


999-999: Unused lambda argument: e

(ARG005)


1000-1000: Unused lambda argument: e

(ARG005)


1031-1031: String contains ambiguous (MINUS SIGN). Did you mean - (HYPHEN-MINUS)?

(RUF001)


1120-1120: Do not catch blind exception: Exception

(BLE001)


1121-1121: Use explicit conversion flag

Replace with conversion flag

(RUF010)


1161-1161: Do not catch blind exception: Exception

(BLE001)


1162-1162: Use explicit conversion flag

Replace with conversion flag

(RUF010)


1184-1184: Do not use bare except

(E722)


1184-1185: try-except-pass detected, consider logging the exception

(S110)

🔇 Additional comments (7)
subtitle_sync.py (7)

342-383: Status bar implementation matches PR goals

The StatusBar encapsulation and its set_status/set_info API are clean and make it straightforward to drive state from the rest of the app. This aligns nicely with the “status bar with state + shortcuts” objective.


399-403: Avoid bare except around scaling call

The HiDPI scaling block uses a bare except: pass. It will silently swallow any exception, including unexpected programming errors.

Consider narrowing this to tk.TclError (or at least logging the exception) so genuine issues aren’t hidden:

-        try:
-            self.root.tk.call('tk', 'scaling', 2.0)  # HiDPI support
-        except:
-            pass
+        try:
+            self.root.tk.call("tk", "scaling", 2.0)  # HiDPI support
+        except tk.TclError:
+            # Scaling not supported on this platform/installation
+            pass

[ suggest_recommended_refactor ]


456-568: Left panel/layout and button enablement look solid

The left panel composition, initial disabled state for Apply/Save/Reset, and enabling logic in browse_file are coherent and prevent invalid actions before a file is loaded. This matches the compact, no-scroll controls requirement well.


569-598: Time input validation is robust

The create_labeled_time_input helper with FocusOut clamping to [min_val, max_val] provides good protection against invalid values. Combined with the stringvars, this keeps the shift controls resilient.


715-752: Line-number and syntax-highlighting implementation is correct and efficient enough for typical SRT sizes

The update_line_numbers and highlight_syntax functions correctly map regex match offsets to Tk text indices using "1.0 + N chars", and they handle timestamps, arrows, and numeric cue indices cleanly. This should give a nice preview experience without correctness issues for normal-sized subtitle files.


775-821: Preset logic correctly applies additive deltas regardless of direction

The apply_preset implementation is nicely thought through: computing new_total_ms = current_total_ms + (delta_ms * direction) and flipping direction when crossing zero ensures that pressing +1s or −1s always adjusts the actual shift by that amount, independent of the current sign. Hours/minutes/seconds/ms recomposition is also correct and keeps values in sensible ranges.


885-923: sync_selected_line flow is safe and user-friendly

The selection handling, validation of a timestamp line, and graceful warnings for “no selection” / “invalid selection” are well done. Temporarily toggling the text widget to NORMAL and ensuring it’s set back to DISABLED on all early-returns avoids leaving the preview editable.

Comment on lines +598 to +666
def create_right_panel(self, parent):
"""Create the right preview panel"""
right_panel = tk.Frame(parent, bg=Colors.BG_DARK)
right_panel.grid(row=0, column=1, sticky="nsew")
right_panel.rowconfigure(1, weight=1)
right_panel.columnconfigure(0, weight=1)

# Title and sync button for content panel
content_header = ttk.Frame(right_panel)
content_header.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
content_header.columnconfigure(0, weight=1)

content_title = ttk.Label(content_header, text="SRT Content Preview",
font=("Arial", 12, "bold"))
content_title.grid(row=0, column=0, sticky=tk.W)

self.sync_btn = ttk.Button(content_header, text="Sync Selected Line",
command=self.sync_selected_line, state=tk.DISABLED)
self.sync_btn.grid(row=0, column=1, padx=(10, 0))

# Text widget with scrollbar
text_frame = ttk.Frame(right_panel)
text_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
text_frame.columnconfigure(0, weight=1)
text_frame.rowconfigure(0, weight=1)

# Scrollbar
scrollbar = ttk.Scrollbar(text_frame)
scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

# Text widget
self.content_text = tk.Text(text_frame, wrap=tk.WORD,
yscrollcommand=scrollbar.set,
font=("Courier", 10),
bg="#f5f5f5", fg="#1a1a1a", relief=tk.SUNKEN,
padx=10, pady=10)
self.content_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
scrollbar.config(command=self.content_text.yview)

# Make text selectable
# Header
header = tk.Frame(right_panel, bg=Colors.BG_DARK)
header.grid(row=0, column=0, sticky="ew", pady=(0, 10))

tk.Label(header, text="Preview", font=("SF Pro Display", 12, "bold"),
bg=Colors.BG_DARK, fg=Colors.TEXT_PRIMARY).pack(side=tk.LEFT)

self.sync_btn = ModernButton(header, text="Sync Line",
command=self.sync_selected_line, width=100,
style="secondary", tooltip="Sync selected timestamp")
self.sync_btn.pack(side=tk.RIGHT)
self.sync_btn.config(state=tk.DISABLED)

# Text area container
text_container = tk.Frame(right_panel, bg=Colors.BG_MEDIUM,
highlightbackground=Colors.BORDER,
highlightthickness=1)
text_container.grid(row=1, column=0, sticky="nsew")
text_container.rowconfigure(0, weight=1)
text_container.columnconfigure(0, weight=1)

# Line numbers
self.line_numbers = tk.Text(text_container, width=4, font=("SF Mono", 11),
bg=Colors.BG_LIGHT, fg=Colors.TEXT_SECONDARY,
relief=tk.FLAT, padx=8, pady=12, state=tk.DISABLED,
cursor="arrow", highlightthickness=0)
self.line_numbers.grid(row=0, column=0, sticky="ns")

# Main text widget
self.content_text = tk.Text(text_container, wrap=tk.WORD,
font=("SF Mono", 11), bg=Colors.BG_MEDIUM,
fg=Colors.TEXT_PRIMARY, relief=tk.FLAT,
padx=12, pady=12, insertbackground=Colors.TEXT_PRIMARY,
selectbackground=Colors.ACCENT_PRIMARY,
selectforeground=Colors.BG_DARK,
highlightthickness=0)
self.content_text.grid(row=0, column=1, sticky="nsew")

# Vertical scrollbar only
v_scrollbar = tk.Scrollbar(text_container, orient=tk.VERTICAL,
command=self.on_scroll_y)
v_scrollbar.grid(row=0, column=2, sticky="ns")

self.content_text.config(yscrollcommand=v_scrollbar.set)

# Configure tags for syntax highlighting
self.content_text.tag_configure("timestamp",
foreground=Colors.TIMESTAMP_FG,
font=("SF Mono", 11, "bold"))
self.content_text.tag_configure("number",
foreground=Colors.ACCENT_PURPLE)
self.content_text.tag_configure("arrow",
foreground=Colors.TEXT_SECONDARY)

# Bind events
self.content_text.bind("<<Selection>>", self.on_text_selection)
self.content_text.bind("<KeyRelease>", self.update_line_numbers)
self.content_text.bind("<MouseWheel>", self.on_mousewheel)

# Initial message
self.content_text.insert("1.0", "No file loaded.\n\nSelect an SRT file to view its content here.")
self.show_welcome_message()

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Welcome message claims drag-and-drop support that isn’t implemented

The welcome copy advertises “Click \"Browse Files\" or drop an SRT file” and “Click \"Apply Shift\" to preview”, but:

  • The actual button labels are “Browse” and “Apply”.
  • There’s no drag-and-drop handling implemented anywhere.

This can confuse users and QA following the instructions. I’d recommend aligning the text with the actual controls and either removing or implementing drag-and-drop.

-   1. Click "Browse Files" or drop an SRT file
+   1. Click "Browse" to open an SRT file
-   3. Click "Apply Shift" to preview
+   3. Click "Apply" to preview the shift

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In subtitle_sync.py around lines 598 to 666, the welcome text in
show_welcome_message advertises “Browse Files”, “Apply Shift”, and drag-and-drop
support which don’t match the UI: the buttons are labeled “Browse” and “Apply”
and there's no drag-and-drop handling. Fix by updating the welcome copy to
exactly reference the existing controls (use "Browse" and "Apply") and remove
any mention of drag-and-drop, or alternatively implement drag-and-drop support
(e.g., add platform-appropriate DnD bindings or tkdnd integration, handle
dropped file paths, validate SRT, and call the same load logic as the Browse
button). Ensure the final text matches the chosen approach and tests open a file
via the Browse button and, if added, via DnD.

Comment on lines +1124 to +1163
def reset_preview(self):
"""Reset to original content"""
if self.original_content:
self.content_text.config(state=tk.NORMAL)
self.content_text.delete("1.0", tk.END)
self.content_text.insert("1.0", self.original_content)
self.highlight_syntax()
self.content_text.config(state=tk.DISABLED)
self.update_line_numbers()

self.shifted_content = None
self.save_btn.config(state=tk.DISABLED)
self.reset_btn.config(state=tk.DISABLED)

self.status_bar.set_status("Preview reset to original", "info")

def save_file(self):
"""Save the shifted content to a file"""
if not self.shifted_content:
messagebox.showerror("Error", "No shifted content to save. Click 'Apply Shift' first.")
self.show_error("Error", "No shifted content to save. Click 'Apply Shift' first.")
return

try:
# Ask where to save
save_path = filedialog.asksaveasfilename(
title="Save Shifted SRT File",
defaultextension=".srt",
initialfile=os.path.basename(self.file_path).replace('.srt', '_shifted.srt'),
initialfile=os.path.basename(self.file_path).replace('.srt', '_synced.srt'),
filetypes=[("SRT files", "*.srt"), ("All files", "*.*")]
)

if save_path:
with open(save_path, 'w', encoding='utf-8') as f:
f.write(self.shifted_content)

messagebox.showinfo("Success",
f"File saved successfully!\n\nSaved to: {os.path.basename(save_path)}")
self.status_bar.set_status(f"Saved: {os.path.basename(save_path)}", "success")
self.show_success("Success", f"File saved successfully!\n\n{os.path.basename(save_path)}")

except Exception as e:
messagebox.showerror("Error", f"An error occurred:\n{str(e)}")
self.show_error("Error", f"An error occurred:\n{str(e)}")

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Reset and save flows behave correctly; tighten error handling and message text

Behavior-wise this section looks good:

  • reset_preview restores the original content, reapplies highlighting, disables Save/Reset, and clears shifted_content.
  • save_file guards against saving before a preview and uses a sensible default filename with _synced.

Two nits:

  • The “No shifted content to save. Click 'Apply Shift' first.” message is slightly out of sync with the button text (“Apply”). Consider updating the copy.
  • As above, the except Exception as e blocks could be narrowed and/or logged so unexpected programming errors aren’t silently wrapped as generic runtime errors.
-            self.show_error("Error", "No shifted content to save. Click 'Apply Shift' first.")
+            self.show_error("Error", "No shifted content to save. Click 'Apply' first.")
📝 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
def reset_preview(self):
"""Reset to original content"""
if self.original_content:
self.content_text.config(state=tk.NORMAL)
self.content_text.delete("1.0", tk.END)
self.content_text.insert("1.0", self.original_content)
self.highlight_syntax()
self.content_text.config(state=tk.DISABLED)
self.update_line_numbers()
self.shifted_content = None
self.save_btn.config(state=tk.DISABLED)
self.reset_btn.config(state=tk.DISABLED)
self.status_bar.set_status("Preview reset to original", "info")
def save_file(self):
"""Save the shifted content to a file"""
if not self.shifted_content:
messagebox.showerror("Error", "No shifted content to save. Click 'Apply Shift' first.")
self.show_error("Error", "No shifted content to save. Click 'Apply Shift' first.")
return
try:
# Ask where to save
save_path = filedialog.asksaveasfilename(
title="Save Shifted SRT File",
defaultextension=".srt",
initialfile=os.path.basename(self.file_path).replace('.srt', '_shifted.srt'),
initialfile=os.path.basename(self.file_path).replace('.srt', '_synced.srt'),
filetypes=[("SRT files", "*.srt"), ("All files", "*.*")]
)
if save_path:
with open(save_path, 'w', encoding='utf-8') as f:
f.write(self.shifted_content)
messagebox.showinfo("Success",
f"File saved successfully!\n\nSaved to: {os.path.basename(save_path)}")
self.status_bar.set_status(f"Saved: {os.path.basename(save_path)}", "success")
self.show_success("Success", f"File saved successfully!\n\n{os.path.basename(save_path)}")
except Exception as e:
messagebox.showerror("Error", f"An error occurred:\n{str(e)}")
self.show_error("Error", f"An error occurred:\n{str(e)}")
def reset_preview(self):
"""Reset to original content"""
if self.original_content:
self.content_text.config(state=tk.NORMAL)
self.content_text.delete("1.0", tk.END)
self.content_text.insert("1.0", self.original_content)
self.highlight_syntax()
self.content_text.config(state=tk.DISABLED)
self.update_line_numbers()
self.shifted_content = None
self.save_btn.config(state=tk.DISABLED)
self.reset_btn.config(state=tk.DISABLED)
self.status_bar.set_status("Preview reset to original", "info")
def save_file(self):
"""Save the shifted content to a file"""
if not self.shifted_content:
self.show_error("Error", "No shifted content to save. Click 'Apply' first.")
return
try:
save_path = filedialog.asksaveasfilename(
title="Save Shifted SRT File",
defaultextension=".srt",
initialfile=os.path.basename(self.file_path).replace('.srt', '_synced.srt'),
filetypes=[("SRT files", "*.srt"), ("All files", "*.*")]
)
if save_path:
with open(save_path, 'w', encoding='utf-8') as f:
f.write(self.shifted_content)
self.status_bar.set_status(f"Saved: {os.path.basename(save_path)}", "success")
self.show_success("Success", f"File saved successfully!\n\n{os.path.basename(save_path)}")
except Exception as e:
self.show_error("Error", f"An error occurred:\n{str(e)}")
🧰 Tools
🪛 Ruff (0.14.8)

1161-1161: Do not catch blind exception: Exception

(BLE001)


1162-1162: Use explicit conversion flag

Replace with conversion flag

(RUF010)

🤖 Prompt for AI Agents
In subtitle_sync.py around lines 1124 to 1163, update the save failure message
to match the UI button text by changing "No shifted content to save. Click
'Apply Shift' first." to "No shifted content to save. Click 'Apply' first.";
also tighten the exception handling in save_file by catching file/IO related
errors (e.g., OSError, IOError) to show user-facing error messages and log them,
and handle unexpected exceptions separately (log the full exception and re-raise
or re-show a generic error) so programming errors are not silently swallowed.

@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #2

coderabbitai bot added a commit that referenced this pull request Dec 10, 2025
Docstrings generation was requested by @DevStrategist.

* #1 (comment)

The following files were modified:

* `subtitle_sync.py`
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