Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

- Files and folders can now carry Meridian tags, a star rating, and a comment: the right-click menu's "Tags & rating…" edits them for the selection (tags and rating apply to every selected item, the comment to a single item), they are kept in Meridian's own local store — never written into the files, so they work for any file or folder — and they show in the pane's preview block.

- The Recycle Bin is now browsable like a folder: a Places entry in the sidebar opens it in a pane, showing every item in the signed-in user's bin across all drives with its original location and deletion time. Items can be restored to where they were deleted from (with the name numbered if something already sits there), deleted permanently after a confirmation, or the whole bin emptied; file operations that cannot apply to recycled items step aside while a pane shows the bin, and a tab left on the bin reopens on it.

- The dark theme is now charcoal with a crimson accent: layered charcoal surfaces (#232323 window, #2B2B2B panels, #333333 raised areas, #404040 borders) replace the near-black ones, the default accent is crimson (#DC143C, listed as "Crimson" in Settings; a saved "Red" choice lands on it), the panel hairlines carry the same crimson, and text on the accent is now white. The red-gray selection, green readouts, and the light theme's surfaces are unchanged.

- Each pane now keeps its own folder tabs, living in the path-bar area: inactive tabs sit as compact chips above the bar, the active tab is the breadcrumb bar itself, and new-tab and close-tab buttons sit at the bar's right end. Every tab remembers its own back/forward history, the session restores both panes' tabs, and the shortcuts are Ctrl+T (new tab), Ctrl+W (close tab) and Ctrl+Tab / Ctrl+Shift+Tab (cycle); middle-clicking a chip closes that tab. Closed tabs are remembered (the last 50, with their history) and Ctrl+Shift+T reopens the most recently closed one.
Expand Down
7 changes: 4 additions & 3 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ See [STATUS.md](STATUS.md) for the dated implemented/CI/runtime inventory. Check

Meridian's feature target is KDE's Dolphin, adapted to Windows. Already at parity: dual-pane (split) browsing, per-pane tabs with closed-tab history, clickable breadcrumbs, view modes remembered per folder, a Places-style sidebar, previews, batch rename, sorting and grouping, and content search. The remaining parity work, in planned order:

1. **Virtual locations** — one location layer under the panes, so the Recycle Bin, recent files, search results, and archives browse like folders (builds on Phase 6's archive-as-folder and virtual-view items).
1. **Virtual locations** — one location layer under the panes, so the Recycle Bin, recent files, search results, and archives browse like folders (builds on Phase 6's archive-as-folder and virtual-view items). _Started: the Recycle Bin browses as a virtual location with restore, permanent delete, and empty._
2. **Indexed search** — a USN-journal-backed index for instant name search, growing toward content (Phase 4).
3. **Embedded terminal panel** that follows the current folder (Phase 7).
4. **Tags, ratings, and comments** stored locally and searchable, with optional NTFS alternate-data-stream storage investigated for portability (Phase 2).
4. **Tags, ratings, and comments** stored locally and searchable, with optional NTFS alternate-data-stream storage investigated for portability (Phase 2). _Started: tags, ratings, and comments are stored locally and shown in the preview block; searching by them and the ADS investigation remain._
5. **Network protocols** — SMB browsing first, then optional SFTP, FTP/FTPS, WebDAV, and SCP clients and optional cloud providers, all behind provider interfaces; local file management never depends on them (Phase 6).

Operation undo/redo and Dolphin-style context-menu actions (service menus — the Scripts menu is the seed) round out the program.
Expand Down Expand Up @@ -165,7 +165,7 @@ Avoid starting large Phase 2/3 experiments until these release blockers are reso
- [x] In-memory Drop Shelf with advisory preview, confirmed sequential copy, cancellation and partial-result reporting; persistence remains planned
- [x] Flat/branch view across nested directories
- [ ] Per-folder view preferences stored in a local database (columns, sorting, icon size, and supported visual options), rather than depending on fragile registry Bags
- [ ] Color labels, tags, and ratings stored locally; investigate optional NTFS alternate data stream (ADS) storage with explicit portability and privacy warnings
- [ ] Color labels, tags, and ratings stored locally; investigate optional NTFS alternate data stream (ADS) storage with explicit portability and privacy warnings (tags, ratings, and comments stored locally with editing and preview display done; color labels, search/filter, and the ADS investigation remain)
- [ ] File-age visualization using restrained tinting or age bars
- [ ] Disk-usage treemap/sunburst visualization
- [x] Saved/virtual search folders with live criteria
Expand Down Expand Up @@ -222,6 +222,7 @@ Avoid starting large Phase 2/3 experiments until these release blockers are reso
- [ ] Improve Android MTP/PTP and supported Apple-device browsing/transfers where Windows APIs permit
- [ ] WSL distribution navigation and \wsl$ support; investigate safe read/browse support for EXT4 VHDX images (distribution navigation and \wsl$ browsing delivered; EXT4 VHDX investigation remains)
- [ ] Investigate virtual views for Windows Registry hives and running processes, with strict read-only defaults and prominent warnings
- [x] Recycle Bin as a browsable virtual location, with restore, permanent delete, and empty (the first virtual location; Registry/process views remain under the investigation item above)

## Phase 7 — Developer and power-user tools

Expand Down
5 changes: 5 additions & 0 deletions src/Meridian.App/MainWindow.BatchRename.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public sealed partial class MainWindow
/// single-item rename exactly as it was, warning included.</summary>
private async Task RenameEntryAsync(PaneState pane)
{
if (pane.IsRecycleBin)
{
SetStatus("Recycled items keep the name they were deleted with — restore an item to rename it.", StatusSeverity.Warning);
return;
}
if (pane.SelectionList.SelectedItems.Count >= 2)
await BatchRenameAsync(pane);
else
Expand Down
4 changes: 2 additions & 2 deletions src/Meridian.App/MainWindow.Clipboard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private void ApplyCutDimming()
/// </summary>
private async Task PasteClipboardAsFileAsync(PaneState pane)
{
if (pane.IsHome || string.IsNullOrWhiteSpace(pane.CurrentPath))
if (pane.IsHome || pane.IsRecycleBin || string.IsNullOrWhiteSpace(pane.CurrentPath))
{
SetStatus("Open a folder to paste into.", StatusSeverity.Warning);
return;
Expand Down Expand Up @@ -195,7 +195,7 @@ private async Task SetClipboardFromPaneAsync(PaneState pane, bool cut)

private async Task PasteIntoPaneAsync(PaneState pane)
{
if (pane.IsHome || string.IsNullOrWhiteSpace(pane.CurrentPath))
if (pane.IsHome || pane.IsRecycleBin || string.IsNullOrWhiteSpace(pane.CurrentPath))
{
SetStatus("Open a folder to paste into.", StatusSeverity.Warning);
return;
Expand Down
14 changes: 14 additions & 0 deletions src/Meridian.App/MainWindow.Preview.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ private async Task PopulatePreviewPanelAsync(FileEntry selected, StackPanel pane
private async Task OpenSelectedAsync(PaneState pane)
{
if (pane.SelectionList.SelectedItem is not FileEntry selected) return;
if (pane.IsRecycleBin)
{
// Opening a recycled item restores it — its bin data path is
// never launched or navigated into directly.
await RestoreSelectedAsync(pane);
return;
}
if (selected.IsDirectory)
{
// In the columns layout a selected folder is already cascaded into
Expand Down Expand Up @@ -186,6 +193,11 @@ private async Task MoveSelectedAsync(PaneState source, PaneState destination)
SetStatus("Open a folder in the other pane first — Home is not a move destination.", StatusSeverity.Warning);
return;
}
if (source.IsRecycleBin || destination.IsRecycleBin)
{
SetStatus("Recycled items cannot be moved directly — restore an item to bring it back, or delete it permanently.", StatusSeverity.Warning);
return;
}
if (string.IsNullOrWhiteSpace(destination.CurrentPath)) return;

var confirmedPaths = selection.Select(item => item.FullPath).ToArray();
Expand Down Expand Up @@ -243,6 +255,7 @@ private async Task CopySelectedAsync(PaneState source, PaneState destination)
var selection = GetSelectedEntries(source);
if (selection.Count == 0) { SetStatus($"Select an item in the {source.Name.ToLowerInvariant()} pane first.", StatusSeverity.Warning); return; }
if (destination.IsHome) { SetStatus("Open a folder in the other pane first \u2014 Home is not a copy destination.", StatusSeverity.Warning); return; }
if (source.IsRecycleBin || destination.IsRecycleBin) { SetStatus("Recycled items cannot be copied directly — restore an item to bring it back, or delete it permanently.", StatusSeverity.Warning); return; }
if (string.IsNullOrWhiteSpace(destination.CurrentPath)) return;

var sourcePaths = selection.Select(item => item.FullPath).ToArray();
Expand Down Expand Up @@ -405,6 +418,7 @@ private async Task RefreshPreviewBlockAsync(PaneState pane)
$"Created: {info.CreationTime.ToString("g")}",
$"Modified: {info.LastWriteTime.ToString("g")}"
};
lines.AddRange(UserMetadataLines(entry));
if (entry.IsDirectory)
{
// The folder treatment the Space quick preview gives a
Expand Down
Loading