Skip to content

Commit 5be0ac8

Browse files
v0.11.0: File Browser — browse all workspace files, not just agent-modified
- New GET /api/workspace/tree endpoint: lists directory contents (no hidden files) - FileInspector completely rewritten as full file browser: - Recursive directory tree with expand/collapse folders - Shows ALL workspace files by default (not just agent-modified) - Toggle to show only agent-modified files - Per-line inline comments on any file, any time - Syntax highlighting, comment bubbles, send-as-task - Refresh button to reload directory tree
1 parent f065487 commit 5be0ac8

4 files changed

Lines changed: 349 additions & 947 deletions

File tree

‎cmd/slmcode/ui/index.html‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
99
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
1010
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='6' fill='%237c3aed'/><text x='16' y='22' font-family='system-ui' font-weight='700' font-size='18' fill='white' text-anchor='middle'>SL</text></svg>" />
11-
<script type="module" crossorigin src="/assets/index-DyAZ-DpQ.js"></script>
12-
<link rel="stylesheet" crossorigin href="/assets/index-BwTTC6_4.css">
11+
<script type="module" crossorigin src="/assets/index-uYHsv3xX.js"></script>
12+
<link rel="stylesheet" crossorigin href="/assets/index-CrTmqy1n.css">
1313
</head>
1414
<body class="antialiased">
1515
<div id="root"></div>

‎pkg/server/server.go‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ func (s *Server) routes() {
163163
s.mux.HandleFunc("GET /api/queries", s.handleListQueries)
164164
s.mux.HandleFunc("GET /api/queries/{id}", s.handleGetQuery)
165165
s.mux.HandleFunc("GET /api/workspace/file", s.handleWorkspaceFile)
166+
s.mux.HandleFunc("GET /api/workspace/tree", s.handleWorkspaceTree)
166167

167168
if s.ui != nil {
168169
fileServer := http.FileServer(http.FS(s.ui))
@@ -1398,3 +1399,50 @@ func (s *Server) handleWorkspaceFile(w http.ResponseWriter, r *http.Request) {
13981399
}
13991400
writeJSON(w, map[string]any{"path": path, "content": string(data), "size": len(data)})
14001401
}
1402+
1403+
// handleWorkspaceTree lists files and directories in a workspace subdirectory.
1404+
func (s *Server) handleWorkspaceTree(w http.ResponseWriter, r *http.Request) {
1405+
path := r.URL.Query().Get("path")
1406+
fullPath := filepath.Join(s.h.Config.Root, filepath.Clean(path))
1407+
if !strings.HasPrefix(fullPath, filepath.Clean(s.h.Config.Root)) {
1408+
http.Error(w, "path traversal", 403)
1409+
return
1410+
}
1411+
entries, err := os.ReadDir(fullPath)
1412+
if err != nil {
1413+
http.Error(w, err.Error(), 404)
1414+
return
1415+
}
1416+
type treeEntry struct {
1417+
Name string `json:"name"`
1418+
Path string `json:"path"`
1419+
IsDir bool `json:"is_dir"`
1420+
Size int64 `json:"size,omitempty"`
1421+
}
1422+
var result []treeEntry
1423+
for _, e := range entries {
1424+
// Skip hidden files/directories
1425+
if strings.HasPrefix(e.Name(), ".") {
1426+
continue
1427+
}
1428+
entry := treeEntry{
1429+
Name: e.Name(),
1430+
Path: filepath.Join(path, e.Name()),
1431+
IsDir: e.IsDir(),
1432+
}
1433+
if !e.IsDir() {
1434+
if info, err := e.Info(); err == nil {
1435+
entry.Size = info.Size()
1436+
}
1437+
}
1438+
result = append(result, entry)
1439+
}
1440+
// Sort: directories first, then alphabetical
1441+
sort.Slice(result, func(i, j int) bool {
1442+
if result[i].IsDir != result[j].IsDir {
1443+
return result[i].IsDir
1444+
}
1445+
return strings.ToLower(result[i].Name) < strings.ToLower(result[j].Name)
1446+
})
1447+
writeJSON(w, map[string]any{"path": path, "entries": result})
1448+
}

‎web/src/api/client.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,3 +455,8 @@ export async function applyPipelinePreset(id: string): Promise<{ ok: boolean; re
455455
export async function getWorkspaceFile(path: string): Promise<{ path: string; content: string; size: number }> {
456456
return request(`/workspace/file?path=${encodeURIComponent(path)}`);
457457
}
458+
459+
export async function getWorkspaceTree(path?: string): Promise<{ path: string; entries: Array<{ name: string; path: string; is_dir: boolean; size?: number }> }> {
460+
const params = path ? `?path=${encodeURIComponent(path)}` : '';
461+
return request(`/workspace/tree${params}`);
462+
}

0 commit comments

Comments
 (0)