mirror of
https://github.com/crabwise-ai/crabwalk.git
synced 2026-08-14 09:02:07 +00:00
feat(workspace): add tilde expansion for workspace paths
Add support for expanding tilde (~) to the user's home directory in workspace path operations. This allows users to use shorthand paths like '~/Documents' instead of full absolute paths when configuring their workspace. - Added expandTilde() utility function with cross-platform support - Updated validatePath to return expanded path for UI consumption - Applied expansion to listDirectory and readFile operations - Modified UI to store and display expanded absolute paths
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
readFile,
|
||||
pathExists,
|
||||
getDefaultWorkspacePath,
|
||||
expandTilde,
|
||||
type DirectoryEntry,
|
||||
type FileContent,
|
||||
} from '~/lib/workspace-fs'
|
||||
@@ -229,13 +230,14 @@ const workspaceRouter = router({
|
||||
// Validate workspace path exists
|
||||
validatePath: publicProcedure
|
||||
.input(z.object({ path: z.string() }))
|
||||
.query(async ({ input }): Promise<{ valid: boolean; error?: string }> => {
|
||||
.query(async ({ input }): Promise<{ valid: boolean; error?: string; expandedPath?: string }> => {
|
||||
try {
|
||||
const exists = await pathExists(input.path)
|
||||
const expandedPath = expandTilde(input.path)
|
||||
const exists = await pathExists(expandedPath)
|
||||
if (!exists) {
|
||||
return { valid: false, error: 'Path does not exist' }
|
||||
}
|
||||
return { valid: true }
|
||||
return { valid: true, expandedPath }
|
||||
} catch (error) {
|
||||
return {
|
||||
valid: false,
|
||||
@@ -254,7 +256,9 @@ const workspaceRouter = router({
|
||||
.input(z.object({ workspaceRoot: z.string(), path: z.string() }))
|
||||
.query(async ({ input }): Promise<{ entries: DirectoryEntry[]; error?: string }> => {
|
||||
try {
|
||||
const entries = await listDirectory(input.workspaceRoot, input.path)
|
||||
const expandedRoot = expandTilde(input.workspaceRoot)
|
||||
const expandedPath = expandTilde(input.path)
|
||||
const entries = await listDirectory(expandedRoot, expandedPath)
|
||||
return { entries }
|
||||
} catch (error) {
|
||||
return {
|
||||
@@ -269,7 +273,9 @@ const workspaceRouter = router({
|
||||
.input(z.object({ workspaceRoot: z.string(), path: z.string() }))
|
||||
.query(async ({ input }): Promise<FileContent & { error?: string }> => {
|
||||
try {
|
||||
const result = await readFile(input.workspaceRoot, input.path)
|
||||
const expandedRoot = expandTilde(input.workspaceRoot)
|
||||
const expandedPath = expandTilde(input.path)
|
||||
const result = await readFile(expandedRoot, expandedPath)
|
||||
return result
|
||||
} catch (error) {
|
||||
return {
|
||||
|
||||
+27
-1
@@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
|
||||
/**
|
||||
@@ -158,7 +159,7 @@ export async function pathExists(targetPath: string): Promise<boolean> {
|
||||
* Returns the user's home directory + .openclaw/workspace
|
||||
*/
|
||||
export function getDefaultWorkspacePath(): string {
|
||||
const homeDir = process.env.HOME || process.env.USERPROFILE || '/tmp'
|
||||
const homeDir = os.homedir()
|
||||
return path.join(homeDir, '.openclaw', 'workspace')
|
||||
}
|
||||
|
||||
@@ -170,6 +171,31 @@ export function isMarkdownFile(filename: string): boolean {
|
||||
return ext === '.md' || ext === '.markdown'
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands tilde (~) to the user's home directory on Unix-based systems
|
||||
* Handles both "~/" prefix and standalone "~" path
|
||||
*/
|
||||
export function expandTilde(inputPath: string): string {
|
||||
// Only expand if path starts with ~
|
||||
if (!inputPath.startsWith('~')) {
|
||||
return inputPath
|
||||
}
|
||||
|
||||
// Get home directory using Node.js built-in (handles cross-platform)
|
||||
// Returns /root in containerized environments if HOME is not set
|
||||
const homeDir = os.homedir()
|
||||
|
||||
// Handle "~/" prefix or standalone "~"
|
||||
if (inputPath === '~' || inputPath.startsWith('~/')) {
|
||||
return path.join(homeDir, inputPath.slice(1))
|
||||
}
|
||||
|
||||
// Path starts with ~ but not followed by / (e.g., ~username)
|
||||
// This is a valid Unix path referring to another user's home
|
||||
// Return as-is and let the system handle it
|
||||
return inputPath
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a file is viewable as text
|
||||
*/
|
||||
|
||||
@@ -128,11 +128,13 @@ function WorkspacePage() {
|
||||
path: pathToValidate,
|
||||
})
|
||||
|
||||
if (result.valid) {
|
||||
setWorkspacePath(pathToValidate)
|
||||
if (result.valid && result.expandedPath) {
|
||||
// Use the expanded path (e.g., ~/Documents -> /home/user/Documents)
|
||||
setWorkspacePath(result.expandedPath)
|
||||
setWorkspacePathInput(result.expandedPath)
|
||||
setPathValid(true)
|
||||
// Persist to localStorage
|
||||
localStorage.setItem('crabcrawl:workspacePath', pathToValidate)
|
||||
localStorage.setItem('crabcrawl:workspacePath', result.expandedPath)
|
||||
// Clear cache when path changes
|
||||
setPathCache(new Map())
|
||||
setSelectedPath(null)
|
||||
|
||||
Reference in New Issue
Block a user