Skip to content

Commit 24596c1

Browse files
committed
feat(desktop): UX overhaul — drag-drop, column pickers, smart paths, reveal
Real UX wins, not just visual polish. - Drag and drop a CSV onto the window to open it (Wails OnFileDrop + full-window drop overlay). - File info bar shows path, row count, size, column count, and a Reveal button right under the header. - Filter: column is now a Select populated from actual file headers. - Dedupe: key columns are clickable chips (multi-select), no comma typing. - Output paths auto-suggest sibling files (input.filtered.csv, input.dedup.csv, input.data.db) — set on file open, editable. - Success banners get a Reveal-in-Finder/Explorer button so users find their output in one click. Backend additions: FileInfoCSV (size + rows + headers), RevealFile (uses 'open -R' on macOS, 'explorer /select,' on Windows, xdg-open on Linux). Cancel button + recent-files menu still TODO.
1 parent e6476aa commit 24596c1

3 files changed

Lines changed: 351 additions & 84 deletions

File tree

desktop/app.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"context"
55
"fmt"
66
"os"
7+
"os/exec"
8+
goruntime "runtime"
79
"strings"
810

911
"github.com/maherelgamil/csvops/pkg/csvops"
@@ -82,6 +84,63 @@ func (a *App) OpenDirectory(title string) (string, error) {
8284
})
8385
}
8486

87+
// ----- File metadata --------------------------------------------------------
88+
89+
type FileInfo struct {
90+
Path string `json:"path"`
91+
Size int64 `json:"size"`
92+
Rows int64 `json:"rows"`
93+
Headers []string `json:"headers"`
94+
}
95+
96+
// FileInfoCSV returns size + row count + headers for a CSV. The frontend uses
97+
// it to show file context and to populate column dropdowns.
98+
func (a *App) FileInfoCSV(path string) (FileInfo, error) {
99+
st, err := os.Stat(path)
100+
if err != nil {
101+
return FileInfo{}, err
102+
}
103+
rows, _ := csvops.CountDataRows(path, ',')
104+
headers, _ := readHeaders(path)
105+
return FileInfo{
106+
Path: path,
107+
Size: st.Size(),
108+
Rows: rows,
109+
Headers: headers,
110+
}, nil
111+
}
112+
113+
func readHeaders(path string) ([]string, error) {
114+
res, err := csvops.Preview(context.Background(), csvops.PreviewOptions{
115+
Input: path,
116+
Rows: 0,
117+
})
118+
if err != nil {
119+
return nil, err
120+
}
121+
return res.Headers, nil
122+
}
123+
124+
// RevealFile shows a file (or dir) in the OS native file manager.
125+
func (a *App) RevealFile(path string) error {
126+
if path == "" {
127+
return fmt.Errorf("empty path")
128+
}
129+
switch goruntime.GOOS {
130+
case "darwin":
131+
return exec.Command("open", "-R", path).Start()
132+
case "windows":
133+
return exec.Command("explorer", "/select,", path).Start()
134+
default:
135+
// best effort on Linux: open the parent dir
136+
dir := path
137+
if st, err := os.Stat(path); err == nil && !st.IsDir() {
138+
dir = strings.TrimSuffix(path, "/"+st.Name())
139+
}
140+
return exec.Command("xdg-open", dir).Start()
141+
}
142+
}
143+
85144
// ----- Preview --------------------------------------------------------------
86145

87146
type PreviewPayload struct {

0 commit comments

Comments
 (0)