Skip to content

feat: compare, mirror navigation, and file browser improvements#17

Open
dw-Aleksandar wants to merge 55 commits intomainfrom
feat/file-browser-enhancements
Open

feat: compare, mirror navigation, and file browser improvements#17
dw-Aleksandar wants to merge 55 commits intomainfrom
feat/file-browser-enhancements

Conversation

@dw-Aleksandar
Copy link
Copy Markdown
Collaborator

@dw-Aleksandar dw-Aleksandar commented Apr 29, 2026

Summary

  • Per-pane back/forward navigation history with mirror-coupled stacks; stacks clear when mirror mode activates
  • Compare toolbar: outlined inactive state when enabled but paths don't match; expand animation on toggle; eye filter button for filtering both panes to selected diff statuses; pane-aware filtering (local pane ignores remote-only pill, remote pane ignores local-only pill); diff pills with count badges
  • Mirror toolbar: outlined inactive state; path-match arrow buttons (→ navigate remote to local path, ← navigate local to remote path) with fallback to localStartPath and project root on missing subfolders; backwards-compatible with installations where localStartPath does not include a /Files segment
  • Compare and Mirror toolbar buttons freeze visual state during navigation to eliminate flicker; expand animation only fires on explicit button click
  • Breadcrumb path editor: pencil button replaces breadcrumb with editable text input; Enter navigates, Escape/blur cancels; mirror-aware typed navigation; always uses forward-slash separators; empty or slash navigates local to drives view
  • Breadcrumb no longer mirrors remote when clicking above the /Files folder
  • Resizable dual-pane divider with double-click to reset; sort controls; split ratio persisted
  • Hover and click feedback on Compare/Mirror/Theme segmented buttons, diff pills, and top navigation tabs via CSS brightness filter
  • Browse button in Add/Edit env modals guarded against opening multiple pickers simultaneously
  • Env sidebar resizable with sort controls and double-click reset on dividers

Test plan

  • Back/forward mouse buttons navigate per-pane; mirror mode couples both panes when paths match
  • Compare button toggles with expand animation; eye filter hides/shows files by status; pane-aware filtering works for local-only and remote-only pills
  • Mirror arrow buttons navigate to matching path; fallback to localStartPath when subfolder missing
  • Breadcrumb pencil edit: Enter navigates, Escape cancels, empty input goes to drives on local
  • Typing a /Files/… path in local breadcrumb mirrors remote; typing in remote breadcrumb mirrors local
  • Clicking breadcrumb above /Files does not move remote pane
  • Toolbar buttons show hover brightness and click press in all themes
  • Browse button in env modals opens only one picker regardless of click speed

🤖 Generated with Claude Code

dw-Aleksandar and others added 30 commits April 28, 2026 16:40
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>
dw-Aleksandar and others added 5 commits April 29, 2026 11:58
…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>
@dw-Aleksandar dw-Aleksandar requested a review from nicped April 29, 2026 09:17
dw-Aleksandar and others added 3 commits April 30, 2026 10:30
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>
@dw-Aleksandar dw-Aleksandar marked this pull request as draft April 30, 2026 07:56
dw-Aleksandar and others added 5 commits April 30, 2026 11:27
- 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>
@dw-Aleksandar dw-Aleksandar marked this pull request as ready for review April 30, 2026 09:45
@dw-Aleksandar
Copy link
Copy Markdown
Collaborator Author

Compare and mirror for when local and remote paths match starting from Files
image

dw-Aleksandar and others added 7 commits April 30, 2026 15:30
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>
@dw-Aleksandar dw-Aleksandar force-pushed the feat/file-browser-enhancements branch from 4dc645e to b06e509 Compare April 30, 2026 13:08
dw-Aleksandar and others added 5 commits April 30, 2026 16:09
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>
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