Skip to content
Merged
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: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ These features are **implemented on the main development branch**. They are not

| **Scripts menu** | A Scripts menu in the header runs PowerShell scripts from `%LOCALAPPDATA%\Meridian\Scripts`: subfolders become menu groups, rebuilt every time the menu opens. A script runs in a visible console with the active pane's folder as its working folder and receives the panes' folders and the selected items' paths as environment variables (`CURRENT_DIR`, `OPPOSITE_DIR`, `SELECTED_FILES`, plus `OC_SCRIPTS_DIR`, `OC_SETTINGS_DIR`, `OC_USERDATA_DIR`) — the variables OneCommander sets, so OneCommander scripts run unchanged. PowerShell 7 is used when installed, Windows PowerShell otherwise. The folder is seeded on first run with the KK-OneCommanderScripts pack by KPC Integrations (MIT) and is never overwritten afterwards. |
| **Event hooks** | Scripts placed in `Scripts\Hooks\<Event>` run automatically and hidden after file operations — `AfterCopy`, `AfterMove`, `AfterDelete`, `AfterRename`, `AfterTransferComplete` — receiving the script variables plus `TRIGGER`, `FILE`, `DESTINATION`, and `SCOPE`. A failing hook is only written to the failure log; it can never block or fail the operation. The event folders and a README explaining them are created automatically when missing. |
| **Conservative file actions** | Copy, move, rename, create folders, view basic properties, and request Recycle Bin deletion. |
| **Conservative file actions** | Copy, move, rename — including multi-item batch rename with a before/after preview (F2 or the Rename command, each name checked before anything changes). Renames run through the Transfers queue, so they show progress, can be cancelled, and — like every finished transfer — can be undone from History. Also: create folders, view basic properties, request Recycle Bin deletion, and apply color tags to files and folders. |

| **Batch rename** | With several items selected, Rename (or `F2`) opens a batch rename dialog: edit the names as a text list, one full name per line, or fill the list from a find-and-replace pass and edit further. Generators add regex replacement, case conversion, and padded numbering; a template mode fills tokens from each file ({name}, {date}, {size}, image dimensions {w}/{h}, a counter, EXIF {exifdate}/{camera} for photos, and ID3 {title}/{artist}/{album}/{track} for MP3s); and the name mapping can be exported to and imported from CSV. The plan is checked as you type — invalid names, duplicates, and collisions are listed per line and renaming stays unavailable until the plan is clean. Case-only renames and swaps go through temporary names, nothing is ever overwritten, and one failure does not stop the rest. |
| **Batch rename** | With several items selected, Rename (or `F2`) opens a batch rename dialog: edit the names as a text list, one full name per line, or fill the list from a find-and-replace pass and edit further. Generators add regex replacement, case conversion, and padded numbering; a template mode fills tokens from each file ({name}, {date}, {size}, image dimensions {w}/{h}, a counter, EXIF {exifdate}/{camera} for photos, and ID3 {title}/{artist}/{album}/{track} for MP3s); and the name mapping can be exported to and imported from CSV. The plan is checked as you type — invalid names, duplicates, and collisions are listed per line and renaming stays unavailable until the plan is clean. Renames run through the Transfers queue with progress, cancellation, recovery checks, and undo from History. Existing destinations wait for a keep-both or skip choice; files are never overwritten. |
| **Universal Viewer** | Preview text (UTF-8/UTF-16), hex, and images, and play audio/video with Windows media controls and no autoplay. Reads are bounded and read-only; playback behavior is not yet verified on real devices. |
| **Archives** | Inspect ZIP/TAR/TGZ contents read-only, create an archive from selected files and folders, or extract into a new no-overwrite folder under entry-policy and size limits, with cancellation. |
| **TimeVault (read-only)** | Discover existing Windows shadow copies and list a snapshot's version of the current folder. Discovery only — no recovery operations, and real VSS access is unverified. |
Expand Down
5 changes: 5 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased

- Batch rename with preview and undo: select multiple files or folders and rename them all at once from the Rename command or F2. Every new name is checked before anything is renamed, collisions are flagged in the preview, and the whole batch runs as a transfer job in the Transfers queue — progress shows in the strip, ordinary item errors do not stop the others; a persistence or recovery failure stops the batch, and every finished rename can be undone from Transfer History.
- Color tags: mark files and folders with a colored indicator (red, orange, yellow, green, blue, purple, gray or none) from the right-click menu in either pane. Tags persist across sessions and travel with the item row wherever it is shown.

- Rename intent, identity and tag migration checkpoints must be durable before execution continues. Interrupted settlement recovers the History receipt before removing the queue entry; persistence failure stops the batch.

## 0.9.6

- A command panel now docks under the panes (View menu or F4): type a command and it runs in the active pane's folder through PowerShell (or the Command Prompt where PowerShell is absent), with its output shown in the panel as it runs, a stop button for the running command, and Up/Down recall of earlier commands. The panel follows the active pane, so its folder is always the folder you are looking at. It runs commands and shows their output; programs that wait for typed answers still belong in a full terminal — "Open terminal here" remains for those.
Expand Down
130 changes: 130 additions & 0 deletions docs/PROJECT_MOCKUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Meridian — Complete Project Mockup

> This document is the synthesized mockup/specification for Meridian based on the current roadmap (`ROADMAP.md`, `PRODUCT_VISION.md`, `docs/ARCHITECTURE.md`) and the differentiation decision: **"A project-focused file manager that makes complex file operations fast, visible, and recoverable."** It is aspirational, not a claim that any unchecked item is implemented. Implemented scope is tracked in `STATUS.md`.

---

## Positioning (decision)

**One-sentence promise:** "A project-focused file manager that makes complex file operations fast, visible, and recoverable."

**Why not "OneCommander with more options":** OneCommander is a broad "file manager for everyone" with configurable views, color tags, previews, and scripting. Meridian's deliberate differentiation is three pillars:

1. **Safe/recoverable operations** (signature): every destructive or bulk action is previewed before execution, journaled, cancellable, and — where technically possible — undoable. *Just shipped: queued rename with before/after preview and History undo.*
2. **Project workspaces**: a project is a first-class concept — saved source/destination folder pairs, pinned files, saved searches, notes, and repeatable actions — not just a sidebar bookmark list.
3. **Dependable large-scale work**: per-item progress, partial-failure reporting, pause/resume/retry, and honest recovery limits at any scale.

---

## Complete capability map (roadmap synthesized)

| Area | Status | What "complete" looks like | Differentiation angle |
|------|--------|---------------------------|----------------------|
| **Dual-pane / columns navigation** | Implemented | One-, two-, four-pane layouts; Miller columns; adjustable splits | Keyboard-first, predictable |
| **Workspace tabs + sessions** | Partial (single session) | Multi-tab, pinning, tab groups, saved workspace profiles | Task-oriented, not just locations |
| **Bookmarks & favorites** | Implemented | Project groups, folder trees, mini trees | Grouped by project, not just recency |
| **Drop Shelf** | Implemented (in-memory) | Persistent shelf, capsule save/load, cross-session | Gather before acting |
| **Copy/Move queue** | Implemented | Reorderable queue, pause/resume, speed limits, selective retry | Visible progress per file + per job |
| **Transfer History + Undo** | Implemented | Operation journal, recovery receipts, undo where reliable | *Signature: nothing is silently destructive* |
| **Rename** | Implemented (queued + undo) | Batch rename with regex, counters, metadata tokens, live preview | Preview before touching anything |
| **Delete** | Implemented (Recycle Bin) | Secure-deletion research with honest SSD/snapshot limitations | Never claim guarantees we can't substantiate |
| **Color tags** | Implemented | Ratings, file-age visualization, NTFS ADS storage option | Visual organization without moving files |
| **Folder DNA** | Implemented (summary) | Read-only hashes, signatures, provenance analysis | Understand before acting |
| **Archives ZIP/TAR/TGZ** | Implemented | Archive-as-folder navigation, 7z/RAR/ISO where licensed | Safe extraction by default |
| **TimeVault (VSS)** | Implemented (read-only) | File History adapters, unified version timeline | Recovery paths are explicit, not assumed |
| **Universal Viewer** | Implemented (build) | Spacebar quick preview, PDF/Office/SVG, dockable viewer | Preview is part of every workflow |
| **Smart Collections** | **Not started** | Metadata-backed cross-folder groups, rules, dynamic refresh | *Key differentiator: organize without relocating* |
| **Search (OmniSearch)** | Not started | Filename + content + metadata, saved searches, Everything integration | First-class search workspace |
| **Duplicate finder** | Not started | Name/size/hash + perceptual media matching, review-before-delete | Safe by default |
| **Directory compare/sync** | Not started | Two-way sync with conflict preview, conservative defaults | Explicit before implicit |
| **Project workspaces** | **Not started** | Saved folder pairs, pinned files, saved searches, notes, repeatable actions | *Key differentiator: project as first-class concept* |
| **Embedded terminal** | Not started | PowerShell/CMD/Git Bash/WSL panel, working-directory sync | Developer workflow |
| **Git integration** | Not started | Branch/status columns, diff, ahead/behind | Developer workflow |
| **Themes/customization** | Partial (Dark/Light/System tokens) | Mica/Acrylic, export/import setup, configurable shortcuts | Calm default, reveal complexity on demand |
| **Shell integration** | Not started | Context-menu actions, compact menu option | Respect Windows policy |

---

## UI mockup — default workspace (text layout)

```
┌─────────────────────────────────────────────────────────────────────────────┐
│ █ MERIDIAN · PROJECT: "Q4 Launch" [● active] ⭒ Settings ✕ │
├─────────────────────────────────────────────────────────────────────────────┤
│ Copy ▾ Move ▾ New folder Rename Preview Properties Archive ▾ │
│ Tools ▾ Delete│
├──────────┬──────────────────────────────────────────────────────────────────┤
│ PROJECTS │ LEFT PANE — "source" │ │
│ ▼ Q4 Launch RIGHT PANE — "destination" │ │
│ • assets/ 📁 report (2).docx [blue tag] │ │
│ • drafts/ budget_Q4.xlsx [green tag] │ │
│ • ▼ images/ notes.md │ │
│ • hero.png contract.pdf │ │
│ • ▼ notes/ [3 selected · 2.4 MB] │ │
│ • meeting.txt ⏳ Renaming 2 of 3 items… │ │
│ Settings [Cancel] │ │
├──────────┴──────────────────────────────────────────────────────────────────┤
│ Status: Renaming 2 items · 45% · report (2).docx → report_Q4.docx ⚠ Undo │
└─────────────────────────────────────────────────────────────────────────────┘
```

**Key UI distinctions from OneCommander:**
- The project name is persistent in the header, not buried in a favorites sidebar.
- Tag colors appear on rows as small badges (already implemented).
- The status bar shows per-operation progress with an undo affordance, not just "Ready."
- The transfer strip is always visible below the panes when anything is queued.
- No ribbon, no icon-only mystery buttons, no copied artwork.

---

## Project workspace spec (next flagship feature)

A **project** is a saved workspace that remembers:

| Element | Purpose |
|---------|---------|
| Name + color + optional notes | Identity, shown in header |
| Folder pairs (left/right) | The locations the user switches between |
| Pinned files | Quick-open within the project |
| Saved searches | Queries that refresh as files change |
| Layout + view preferences | Split ratio, density, columns per pane |
| Repeatable actions | Named copy/move/rename plans with preview |

**Workflow:** Open project → both panes restore to their folders → tags/colors show status → run a queued operation → undo from History if needed → close project, state persists.

**Storage:** `%LOCALAPPDATA%\Meridian\projects\<name>.json`, atomic write, migration-tolerant schema.

---

## Implemented → Roadmap traceability

| Roadmap item | Implemented | Evidence |
|--------------|-------------|----------|
| Phase 1: Paired workspace tabs + session persistence | Partial | `WorkspaceStore`, single session |
| Phase 1: Central transfer queue | Implemented | `TransferCoordinator`, `TransferExecutor` |
| Phase 1: Rename with validation + no-overwrite | Implemented (queued + undo) | `TransferJobKind.Rename`, undo planner |
| Phase 1: Delete to Recycle Bin | Implemented | `FileOperations`, confirmed dialog |
| Phase 1: Properties dialog | Implemented | Basic metadata |
| Phase 1: Installer + portable artifacts | Implemented | `packaging/Meridian.iss`, CI |
| Phase 2: Color labels/tags | Implemented | `FileTagStore`, `FileTagPalette`, UI badges |
| Phase 2: Folder DNA | Implemented (summary) | `FolderDna`, immediate listing only |
| Phase 2: Color labels → ratings/ADS storage | Planned | Roadmap Phase 2 |
| Phase 3: Batch rename with preview | Implemented (basic) | Preview dialog + queue |
| Phase 3: Directory compare/sync | Planned | Roadmap Phase 3 |
| Phase 4: Search/metadata | Planned | Roadmap Phase 4 |
| Phase 5: Preview/viewer | Partial | `QuickPreviewOverlay`, Universal Viewer |
| Phase 6: Archives | Implemented (read-only + create/extract) | `Archives/*` |
| Phase 7: Developer tools | Planned | Roadmap Phase 7 |
| Phase 8: Customization | Partial | Theme/density/accent settings |

---

## Next implementation priorities (my decision)

1. **Smart Collections** (Phase 2) — metadata-backed cross-folder groups. Organize without relocating files; dynamic refresh; tags + rules. *This is the clearest OneCommander differentiator.*
2. **Project workspaces** (Phase 2/7) — named workspace snapshots with folder pairs, pinned files, saved searches.
3. **Duplicate finder** (Phase 4) — name/size/hash with review-before-delete.
4. **Directory compare/sync** (Phase 3) — conflict preview before two-way sync.
5. **Spacebar quick preview** (Phase 5) — inline preview without opening a dialog.

I will implement **Smart Collections** next.
5 changes: 4 additions & 1 deletion src/Meridian.App/App.xaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Application x:Class="Meridian.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" RequestedTheme="Dark">
<Application x:Class="Meridian.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converters="using:Meridian.Converters" RequestedTheme="Dark">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
Expand Down Expand Up @@ -144,6 +144,9 @@
<SolidColorBrush x:Key="TreeViewItemBackgroundSelectedDisabled" Color="#F2E4E6" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<converters:FileTagToBrushConverter x:Key="FileTagToBrushConverter" />
<converters:FileTagToVisibilityConverter x:Key="FileTagToVisibilityConverter" />
<converters:FileTagToLabelConverter x:Key="FileTagToLabelConverter" />
<CornerRadius x:Key="MeridianCardCornerRadius">2</CornerRadius>
<CornerRadius x:Key="MeridianControlCornerRadius">1</CornerRadius>
<FontFamily x:Key="MeridianReadoutFontFamily">Cascadia Mono, Consolas</FontFamily>
Expand Down
50 changes: 50 additions & 0 deletions src/Meridian.App/Converters/FileTagToBrushConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Globalization;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Media;
using Meridian.Models;
using Meridian.Services;
using Windows.UI;

namespace Meridian.Converters;

/// <summary>Converts a <see cref="FileTagColor"/> to the <see cref="SolidColorBrush"/>
/// used to paint a tag indicator, or null when the item is untagged. Colors are
/// stored as hex strings in <see cref="FileTagPalette"/> so Core stays free of
/// WinUI dependencies; this converter parses them into brushes.</summary>
public sealed class FileTagToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var tag = value as FileTagColor?;
var hex = FileTagPalette.HexFor(tag);
if (string.IsNullOrEmpty(hex) || string.Equals(hex, "transparent", StringComparison.OrdinalIgnoreCase))
return null;

Check warning on line 22 in src/Meridian.App/Converters/FileTagToBrushConverter.cs

View workflow job for this annotation

GitHub Actions / Build Windows 11 (ARM64)

Possible null reference return.

Check warning on line 22 in src/Meridian.App/Converters/FileTagToBrushConverter.cs

View workflow job for this annotation

GitHub Actions / Build Windows 11 (ARM64)

Possible null reference return.

Check warning on line 22 in src/Meridian.App/Converters/FileTagToBrushConverter.cs

View workflow job for this annotation

GitHub Actions / Build Windows 11 (x64)

Possible null reference return.

Check warning on line 22 in src/Meridian.App/Converters/FileTagToBrushConverter.cs

View workflow job for this annotation

GitHub Actions / Build Windows 11 (x64)

Possible null reference return.
try
{
var color = ColorFromHex(hex);
return new SolidColorBrush(color);
}
catch
{
// A malformed hex string must not break rendering; fall back to no tag.
return null;

Check warning on line 31 in src/Meridian.App/Converters/FileTagToBrushConverter.cs

View workflow job for this annotation

GitHub Actions / Build Windows 11 (ARM64)

Possible null reference return.

Check warning on line 31 in src/Meridian.App/Converters/FileTagToBrushConverter.cs

View workflow job for this annotation

GitHub Actions / Build Windows 11 (ARM64)

Possible null reference return.

Check warning on line 31 in src/Meridian.App/Converters/FileTagToBrushConverter.cs

View workflow job for this annotation

GitHub Actions / Build Windows 11 (x64)

Possible null reference return.

Check warning on line 31 in src/Meridian.App/Converters/FileTagToBrushConverter.cs

View workflow job for this annotation

GitHub Actions / Build Windows 11 (x64)

Possible null reference return.
}
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
=> throw new NotSupportedException();

private static Color ColorFromHex(string hex)
{
var h = hex.TrimStart('#');
byte a = 0xFF, r = 0, g = 0, b = 0;
if (h.Length == 6)
{
r = byte.Parse(h.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
g = byte.Parse(h.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
b = byte.Parse(h.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
}
return Color.FromArgb(a, r, g, b);
}
}
19 changes: 19 additions & 0 deletions src/Meridian.App/Converters/FileTagToLabelConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using Microsoft.UI.Xaml.Data;
using Meridian.Models;
using Meridian.Services;

namespace Meridian.Converters;

/// <summary>Converts a <see cref="FileTagColor"/> to its plain-text accessibility label.</summary>
public sealed class FileTagToLabelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var tag = value as FileTagColor?;
return FileTagPalette.LabelFor(tag);
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
=> throw new NotSupportedException();
}
Loading
Loading