feat: compare, mirror navigation, and file browser improvements#17
Open
dw-Aleksandar wants to merge 55 commits intomainfrom
Open
feat: compare, mirror navigation, and file browser improvements#17dw-Aleksandar wants to merge 55 commits intomainfrom
dw-Aleksandar wants to merge 55 commits intomainfrom
Conversation
Side-by-side diff highlighting on the dual-pane browser. Each row in
both panes is color-coded by its diff status (different, remote-only,
local-only, identical). Comparison pairs entries by name AND type, so a
local file and a remote folder of the same name are not matched.
Three-mode toggle in a new toolbar at the top:
Off - no comparison
Auto - compares automatically when both panes resolve to the same
DW-relative path tail (e.g. local .../Files/Templates and
remote /Files/Templates both resolve to /files/templates)
On - always compare regardless of paths
The toolbar also shows clickable summary pills with per-status counts
that toggle which statuses get highlighted in the file lists.
Defaults: mode=Auto, highlighted=[different, remote-only].
Both compareMode and highlightedStatuses persist to localStorage.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously the local pane could not go higher than C:\Users\... because walking up from C:\ produced an unlistable path. Now the up-arrow, breadcrumb, and back-button keep stepping until they reach an empty "Drives" path that lists every available drive (A:\ through Z:\) on Windows. Each drive renders as a directory entry and double-click / drag-drop work as on any other folder. Breadcrumb now also shows a leading "Drives" crumb on Windows so the user can jump back to the drive list from any depth in one click. The drives-view path is treated as transient — it is not persisted as the last-visited folder. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ectory Listing C:\ failed because stat() rejects on protected entries like System Volume Information and $Recycle.Bin. Promise.all over the dirents then rejected the whole call. Per-entry try/catch falls back to dirent-only info (name, path, type) when stat() fails, so a single inaccessible item no longer blocks the listing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously the back mouse button (button 3) navigated to the parent of the current folder, regardless of where the user actually came from. Clicking a breadcrumb deep in the tree and then pressing back stepped up the parent chain, not back to the prior folder. Replace the forward-only stack with a proper back+forward history pair per pane. User-initiated navigation (up arrow, breadcrumb click, double-click into a folder) now pushes the previous path onto the back stack and clears forward; back pops from back onto forward; forward pops from forward onto back. Refresh does not touch history. Mouse back/forward buttons are no-ops when their respective stack is empty. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The mtime check produced too many false-positive "different" flags because local fs.stat().mtime and remote updatedAt diverge by upload time, clock skew between client and server, and local restores from backup or git — even when the content is byte-identical. Drop it. Files are now identical when their sizes match, different otherwise. Same-size edits will slip through, but in template/asset deployments those are rare. False positives across the board are worse than rare false negatives because they train the user to ignore the diff display. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
If the persisted localPath no longer exists (deleted folder, disconnected drive), loadLocal now returns false and DualPaneBrowser retries with the user's home directory instead of leaving the pane empty. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…atches When compare detects matching folder structures (auto tail-match or manual On), a Sync toggle appears in the CompareToolbar. When enabled, double-clicking a folder that exists on both sides navigates both panes simultaneously. Matched folders are highlighted teal in both panes with a ⇄ icon in the size column to hint that clicking will move both panes. Sync state persists to localStorage. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When sync nav is active, pressing the mouse back or forward button in either pane also triggers the same navigation in the other pane, keeping both panes in lockstep through their history stacks. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When sync nav is active, the Up button, Refresh button, and breadcrumb clicks in either pane mirror the same navigation in the other pane. Breadcrumb jumps count path segments to determine how many levels to go up on the opposite pane, keeping both in lockstep regardless of which pane initiated the navigation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sync indicator is now just the ⇄ icon in muted text — no row highlight or border. The Sync toolbar button uses the standard accent color when active. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Compare now has two states — off and auto — presented as a single toggle button styled consistently with the Sync button. Clicking toggles between off and auto; the 'on' mode is no longer exposed in the UI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sync button is now always visible alongside Compare. When enabled it acts like auto — navigation syncs only when folder structures match, otherwise it's a no-op. Mirrors the Compare convention exactly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
toDisplayRemotePath('/') returns '/Files/' with a trailing slash, causing
getDwRelativeTail to return '/files/' instead of '/files' — breaking the
tail comparison against a local path ending in \Files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Order is now: Compare | diff pills | separator | Sync — keeping compare controls grouped together and Sync separated at the far end. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nc is active When sync navigation is enabled and a folder is clicked in one pane, the corresponding folder in the other pane gets a subtle accent outline to show where the sync would navigate to. Clears when selection changes or sync is off. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
remoteMirrorPaths (set on local click) was being passed to local FileList instead of remote, and vice versa. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of a custom outline, mirror paths are merged into the selected array so they render with the existing selection background. No new visual styles added to FileList. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ote'/'local' Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Uses border-strong, height 22, font-size 10, uppercase + letter-spacing to match the Light/Auto/Dark theme selector styling exactly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously defaulted to 'auto', enabling compare on first launch without user intent. Both Compare and Sync now start disabled by default. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rison' Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pathsInSync was set to false whenever compareMode was 'off', breaking sync navigation for users who want sync without compare. Tail matching now runs unconditionally — only the diffMap computation is gated on compareMode. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When diffMap is empty (compare disabled), syncCandidateKeys now falls back to a direct name+type match between localEntries and remoteEntries. This restores the ⇄ icon and navigation sync independently of compare being on. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Syncing back/forward history across panes caused asymmetric stack bugs when mixing sync and non-sync navigation. Each pane now manages its own history independently. Sync applies to intentional forward navigation only. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Button label, tooltips, state variables, localStorage key and internal names all updated from sync/syncNav to mirror/mirrorNav to avoid confusion with the deploy/publish sync feature elsewhere in the app. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds dedicated mirrorBackStack/mirrorForwardStack that records local+remote path pairs whenever mirror navigation occurs. Back/forward mouse buttons check the mirror stack first when mirrorNav is on, navigating both panes together; falls back to per-pane history when the mirror stack is empty or mirror is off. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rrored Removes the dedicated mirror history stack. Back/forward now uses the existing per-pane stacks for each pane. When mirrorNav is on and paths are in sync, pressing back/forward on either pane navigates both panes together. When not in sync, only the pane receiving the event moves. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes the pathsMatch guard from mousedown handlers — when mirrorNav is enabled, back/forward on either pane always moves the other pane too, even if navigating would take them out of sync. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Restores pathsMatch guard — back/forward on either pane also moves the other only when mirror is on AND both panes are currently in sync, preserving the mirrored state after navigation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Only push to the back stack when the current path differs from the stack's top, preventing duplicate adjacent entries from mirror-coupled navigation that fires navigateLocalTo and navigateRemoteTo in sequence. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reads stack state directly instead of nesting setState inside a functional updater. Adds dedup guards to all forward-stack and back-stack pushes so rapid clicking can't insert the same path twice in a row, which previously caused a refresh instead of a folder switch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a button is enabled but pathsMatch is false, the solid accent fill is replaced with a diagonal stripe pattern to signal the feature is on but waiting for matching /Files/… paths. Tooltips updated to explain what's needed to activate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s don't match When enabled but pathsMatch is false, buttons show accent border and accent text with surface background (outlined style) instead of solid fill. Tooltips updated to explain that a matching /Files/… path is needed to activate. Full solid fill restores once paths match. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When compare is active and diff results are loaded, the Compare button expands into a segmented group with an eye toggle. When active, both panes show only entries whose diff status matches the selected pills. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Local→ / ←Remote arrow buttons to Mirror segmented group; visible when mirror is on, paths are mismatched, and at least one pane is on a /Files/… path; hidden once paths sync - Arrow buttons use original-cased path segments for breadcrumb fidelity - Freeze button styles and expanded controls during navigation using a stable pathsMatch ref to prevent flicker between pane loads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wraps eye filter, diff pills, and mirror arrow buttons in an Expand component that transitions max-width + opacity on show/hide (140ms ease-out in, 80ms ease-in out). Fixes toolbar height flicker by using a fixed height instead of minHeight. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Freeze diffMap, hasDiff, and pathsMatch during loading to eliminate toolbar flicker during navigation - Compare expand animation only fires on button click, not on other state changes - Eye filter is pane-aware: local pane ignores remote-only pill, remote pane ignores local-only pill - Clear back/forward stacks when mirror mode activates (pathsMatch transitions to true) - Remove back button fallback (go-up-a-folder) to prevent ping-pong loop - Arrow match buttons visible regardless of mirror toggle state - matchLocalToRemote falls back to localStartPath when current path has no /Files/ segment Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck improvements - Add pencil button to PaneHeader that replaces breadcrumb with editable input; Enter navigates, Escape/blur cancels; pre-fills with forward-slash normalised path; empty/slash navigates local to drives view - Mirror-aware onNavigateTo: typed local paths inside /Files mirror to remote; typed remote paths use localStartPath base to mirror to local - matchLocalToRemote falls back to localStartPath if computed subfolder does not exist - getLocalFilesBase: if localStartPath has no /files segment, append /Files as base for backwards compatibility with older installations - remoteOnFiles: gate only requires localStartPath to be set, not that it contains /files - Remove step-based remote mirror fallback from local breadcrumb clicks above /Files - Fix showMirrorMatch flicker: remove !isLoading guard (stablePathsMatch already freezes during load) - Guard matchRemoteToLocal and matchLocalToRemote against concurrent calls while loading - Arrow buttons always use outlined accent style; dimmed at 35% opacity when disabled - Add toolbar-btn CSS class (brightness filter) for hover/click feedback on Compare, Mirror, Theme, and pill buttons - Add nav-btn CSS class for hover/click feedback on top navigation tabs - Fix browse button opening multiple dialogs: pickingRef guard in AddEnvModal and EditEnvModal - Breadcrumb always uses forward-slash separator for display and path input - Normalise separator in back/forward stack deduplication guards Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Collaborator
Author
PaneHeader was stripping the leading /Files segment before calling onNavigateTo, but DualPaneBrowser's remote handler also strips it, causing /Files/Files/Icons to resolve to /Icons instead of /Files/Icons. Removed the stripping from PaneHeader — each pane's onNavigateTo is responsible for its own conversion. Also adds upDisabled prop so callers can grey-out the up button when already at the top of the hierarchy. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Pass upDisabled to both pane headers: local disables at drives view, remote disables at virtual root (remotePath === '/') - Eye filter now only counts highlighted statuses that have at least one entry in the diffMap (diffCounts[s] > 0), so deselecting all visible pills falls through to showing all files rather than empty results Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
upDisabled={!localPath} disabled the local up button whenever localPath
was '' (drives view), which is the Windows top-level state. The up
button should remain clickable in all local pane states; the disabled
behavior was only requested for the remote pane root.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After breadcrumb changes store paths with forward slashes (D:/a/b),
localParentPath detected 'Windows' by checking path.includes('\').
Forward-slash paths got sep='/' and returned '/D:/a' with a bogus
leading slash — loadLocal failed silently, making the up button appear
stuck until the user double-clicked a folder entry (restoring backslash).
Fix: split on both separators, and detect Windows by drive-letter
pattern rather than separator character.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When navigating to a remote folder that has no matching local counterpart, the left mirror arrow now greys out and is non-clickable. DualPaneBrowser checks the would-be local target path with fs.list whenever remotePath or localPath changes and passes the result as matchLocalPathExists to CompareToolbar. Tooltip updated to explain the disabled state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Four places were doing case-sensitive name comparisons that break when local and remote have the same folder with different casing (e.g. Scripts vs scripts): - navigateLocal: used entry.name directly to build the remote target path — now finds the actual remote entry by lowercase match first - navigateRemote: same issue for the local target path - local onSelect mirror highlight: names.has(e.name) → toLowerCase() - remote onSelect mirror highlight: same Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Added to "What you can do" and "Key features": - Compare mode: colour-coded diff with per-status filters - Mirror navigation: sync both panes, arrow buttons to snap paths Reordered key features — browsing first, then transfers, then env/meta. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4dc645e to
b06e509
Compare
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When mirror mode is on and paths are in sync, both pane breadcrumbs highlight the mirrored portion (from /Files onward): ancestor segments get var(--accent-cool), the current folder gets a lighter teal-white mix (35% accent-cool). Frozen via stable ref during loading to prevent flicker. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remote pane uses teal (--accent-cool), local pane uses orange (--accent). Mirrored segments from /Files onward are tinted — ancestors at 60% mix, current folder at 35% mix leaning white, separators at 50% mix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both the environment display name (120px) and host (140px) now truncate with ellipsis when too long; full value shown on hover. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… space Label and host now shrink with ellipsis at narrow widths; breadcrumb has an 80px minimum so the current folder is always visible. Nav buttons remain fixed and never compress. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Test plan
🤖 Generated with Claude Code