Skip to content

fix(ui): restore titlebar dragging on the right, and unbreak AeroSync template export - #516

Merged
axpnet merged 2 commits into
mainfrom
fix/titlebar-drag-and-sync-templates
Jul 30, 2026
Merged

fix(ui): restore titlebar dragging on the right, and unbreak AeroSync template export#516
axpnet merged 2 commits into
mainfrom
fix/titlebar-drag-and-sync-templates

Conversation

@axpnet

@axpnet axpnet commented Jul 29, 2026

Copy link
Copy Markdown
Member

Three reports from Ehud Kirsh, one commit: two window-level defects in the titlebar and the AeroSync template export, plus the path-bar chevron from the wishlist.

The titlebar could not be dragged from its right half (#511)

The outer titlebar container deliberately carries no data-tauri-drag-region: on Windows the runtime intercepts the mousedown geometrically over a drag region before the DOM event is dispatched, which used to swallow clicks on modal close buttons falling inside the bar's 36px. The affordance was left to two explicit children, and everything to the right of the AeroShare button was neither.

Two dead zones, both now filled with drag regions that contain no interactive children, so the Windows behaviour above cannot come back:

  • the reserved 96px navigation slot. It exists so the utility cluster does not shift when Connect and Disconnect swap, and on My Servers and Add Service it renders no button at all: 96 inert pixels immediately left of AeroVault, which is exactly the spot in the report.
  • the two 12px inter-cluster gaps, previously a gap-3 on the flex container. A gap is dead space the window manager cannot see, so they are now explicit spacers of the same width.

This also restores double-click to maximise and restore on that side, which follows the drag region rather than a handler of ours.

AeroSync template export failed in all four formats (#514)

Export returned Profile '<id>' not found for .aeroftp-script, .aerosync, bash and PowerShell alike. Two objects in this codebase are called a profile, and the dialog was handed the wrong one: the connected saved server, while all three export commands resolve the id against the sync preset store (Mirror, Two-way, Backup, Pull, Remote Backup). The lookup could never match, so no format could ever have worked.

The dialog now has a preset picker, which is the object the export actually describes (direction, comparison keys, retry policy), and the saved server travels separately as what the generated script connects with.

That second half was a defect on its own, latent behind the first. The export wrote CONNECT --profile with the template name the user typed into the dialog, and the bash and PowerShell wrappers put the same value in aeroftp-cli sync --profile. Since the CLI resolves that name against the vault, even a script that had exported successfully would not have connected. The string in the error message is the saved-server id, not a credential.

The path-bar chevron now points down while its menu is open (discussion #347)

Pure CSS, rotate-90 on the open state, matching what File Explorer does. One component covers every surface Ehud named: BreadcrumbBar is shared by AeroFile, the local panel and the remote panel. The separator chevrons also gained the aria-haspopup and aria-expanded the trailing one already had.

Verification

tsc (TS 7.0.2) clean, npm run build ok, vitest 531/531, cargo clippy --all-features --tests exit 0, i18n:validate 46/46 clean with 0 placeholders. No new i18n key: the preset picker reuses syncPresets.title, which is already translated in all 47 locales, so this PR does not touch the locale files and cannot conflict with the i18n chain.

Frontend only. Branched off main at 64efc8f5, after TypeScript 7.

Merge order

This is outside the agreed #502 -> #506 -> #509 chain and blocks nobody, but it shares one file with #506 (src/App.tsx, one added line in a different block), so it should land after it.

Reported in #511 and #514, and in discussion #347.

Not marked as closing either issue: they stay open until Ehud confirms on Windows 10.

Summary by CodeRabbit

  • New Features

    • Added sync preset selection when exporting synchronization templates and scripts.
    • Exports now use the selected preset and active server profile name for connection details.
    • Export actions remain unavailable until a preset is selected.
  • Bug Fixes

    • Improved breadcrumb dropdown indicators with clearer open states, rotation, and accessibility attributes.
    • Improved custom titlebar dragging between control areas for more reliable window movement.

… template export

The titlebar's right half could not drag or double-click the window. The reserved 96px navigation slot and the inter-cluster gaps carried no drag region, and on My Servers and Add Service that slot renders no button at all, leaving an inert strip immediately left of AeroVault. Fill the reserved width and the two cluster gaps with explicit drag regions; the outer container stays free of one, since a drag region spanning the interactive controls is what used to swallow clicks on Windows.

Every AeroSync template export failed with "Profile '<id>' not found", in all four formats. The dialog was handed the connected saved-server id, while the three export commands resolve that id against the sync-preset store (Mirror, Two-way, Backup, Pull, Remote Backup), so the lookup could never match. Add a preset picker to the dialog and pass the saved server separately, as the name the generated script connects with. That second half was wrong on its own: the exported script wrote CONNECT --profile with the template name the user typed, which the CLI cannot resolve against the vault, so even a script that had exported would not have run.

The path-bar chevron now points down while its dropdown is open, matching File Explorer, and the separator chevron gained the aria-haspopup and aria-expanded its sibling already had.

Reported in #511 and #514, and in discussion #347.

Co-Authored-By: Ehud Kirsh <100953560+EhudKirsh@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@snyk-io

snyk-io Bot commented Jul 29, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 43 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: b50823bc-d7fb-46c1-9f2e-9fbb022dce48

📥 Commits

Reviewing files that changed from the base of the PR and between 6d497ba and 644e277.

📒 Files selected for processing (2)
  • src/components/BreadcrumbBar.tsx
  • src/components/Sync/SyncTemplateDialog.tsx
📝 Walkthrough

Walkthrough

The sync template dialog now selects backend-loaded sync presets for exports while using the active saved server name for connection metadata. Breadcrumb chevrons expose and visualize open state, and the custom titlebar adds explicit draggable spacer regions.

Changes

Sync preset export

Layer / File(s) Summary
Saved server context propagation
src/App.tsx, src/components/AeroSync/types.ts, src/components/AeroSync/AeroSyncDialog.tsx, src/components/Sync/SyncTemplateDialog.tsx
Adds activeProfileName to the AeroSync context and passes it as serverProfileName to the template dialog.
Preset loading and export payloads
src/components/Sync/SyncTemplateDialog.tsx
Loads sync profiles when opened, provides a preset selector, uses the selected preset id for exports, and disables export without a preset.

Breadcrumb dropdown state

Layer / File(s) Summary
Chevron state and accessibility
src/components/BreadcrumbBar.tsx
Uses derived open state for dropdown rendering, ARIA attributes, and rotating chevron icons.

Titlebar drag regions

Layer / File(s) Summary
Cluster drag-region layout
src/components/CustomTitlebar.tsx
Adds explicit draggable spacers between titlebar clusters and around window controls.

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

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant AeroSyncDialog
  participant SyncTemplateDialog
  participant Backend
  participant ExportScript
  App->>AeroSyncDialog: provide activeProfileName
  AeroSyncDialog->>SyncTemplateDialog: pass serverProfileName
  SyncTemplateDialog->>Backend: load_sync_profiles_cmd
  Backend-->>SyncTemplateDialog: return SyncProfile entries
  SyncTemplateDialog->>Backend: export with presetId
  Backend-->>ExportScript: generate script using serverProfileName
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main UI fixes: right-side titlebar dragging and AeroSync template export.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/titlebar-drag-and-sync-templates

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

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@src/components/BreadcrumbBar.tsx`:
- Around line 411-425: Update the separator toggle in BreadcrumbBar so its
aria-haspopup value matches the rendered role-less dropdown; remove the
menu-specific declaration unless the dropdown is also converted to a complete
menu with menuitem roles and keyboard behavior. Preserve the existing
aria-expanded state and popup interaction.

In `@src/components/Sync/SyncTemplateDialog.tsx`:
- Around line 498-510: Give the preset select in the sync preset section an
accessible programmatic name by converting the visible syncPresets.title element
into a label associated with the control via matching htmlFor and id attributes,
or by adding an equivalent aria-label directly to the select.
- Around line 99-116: Clear syncProfiles and presetId at the start of the isOpen
branch in the useEffect before invoking load_sync_profiles_cmd, so every dialog
open begins with empty state and load failures cannot retain stale selections.
Keep the successful profile loading and preset selection behavior unchanged.
🪄 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: b97577f4-8123-44ef-911e-fb5007022ee4

📥 Commits

Reviewing files that changed from the base of the PR and between fb18add and 6d497ba.

📒 Files selected for processing (6)
  • src/App.tsx
  • src/components/AeroSync/AeroSyncDialog.tsx
  • src/components/AeroSync/types.ts
  • src/components/BreadcrumbBar.tsx
  • src/components/CustomTitlebar.tsx
  • src/components/Sync/SyncTemplateDialog.tsx

Comment thread src/components/BreadcrumbBar.tsx Outdated
Comment thread src/components/Sync/SyncTemplateDialog.tsx
Comment thread src/components/Sync/SyncTemplateDialog.tsx
…a menu the dropdown is not

Addresses the CodeRabbit review on #516.

The preset id and the loaded list survived a close and reopen, so a failed reload left the previous selection in place with Export still enabled. The id then reached the backend and produced the same "Profile not found" this change exists to remove. Both are now cleared before the load rather than only on success, which also drops the id-preserving branch: the picker settles on the first preset, and there is no path where a removed custom preset stays selected.

The chevron buttons no longer claim aria-haspopup="menu". Their dropdown is a plain list of buttons with no role="menu" and no arrow-key navigation, so the declaration promised keyboard behaviour that does not exist. Removed from the trailing chevron as well, where it predates this branch, rather than leaving the two siblings inconsistent. aria-expanded stays: on a disclosure button it is accurate on its own.

The preset select gained a real label element tied to it by id, so assistive technology announces a named control instead of reading a nearby div.

Co-Authored-By: Ehud Kirsh <100953560+EhudKirsh@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@axpnet

axpnet commented Jul 29, 2026

Copy link
Copy Markdown
Member Author

All three findings applied in 644e2779. The Major one was worth catching: a stale preset id surviving a failed reload would have reached the backend and produced the same Profile not found this PR exists to remove, so the failure mode was a reappearance of the bug rather than a new one. Cleared before the load instead of only on success, which also let the id-preserving branch go.

On the ARIA popup semantics: correct, and the inconsistency was not introduced here. The trailing chevron already declared aria-haspopup="menu" over the same role-less dropdown, and I had propagated it to the separator. Rather than leave two siblings disagreeing, I removed the declaration from both. Implementing real menu/menuitem semantics would mean arrow-key navigation and focus management this dropdown does not have, and declaring the role without them promises keyboard behaviour that is not there. aria-expanded stays, which is accurate on a disclosure button by itself.

The select now has a <label htmlFor> tied to it by id.

Re-verified after the change: tsc clean, vitest 531/531, i18n:validate 46/46 with 0 placeholders, vite build ok.

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