feat: v0.11.0 VS Code-like explorer, markdown preview, search in files
- Explorer: context menus, multi-select, inline rename/new, drag to move or copy, desktop drag-and-drop uploads (folders too), cut/copy/paste, duplicate, download, delete to a 7-day trash with Undo. Server side in libexec/devdesk-fsops.sh with bats tests. Closes #54 - Markdown preview in a shadow root: headings with anchors and outline, highlighted code with copy, callouts, task lists, front matter, links that open files, live split view. Editor status bar, multi-cursor, closeBrackets, Ctrl+G, tab menus and reordering. Closes #55 - Search in files (Ctrl+Shift+F) via git grep / grep. Closes #56 - Fix: nested $(...) path checks now fail hard (set -e does not reach them; an escape created /x in testing); newest folder listing wins; sessionAction returns the reply JSON. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WHRLCCdXYm41Qx5kNhehaP
This commit is contained in:
@@ -1195,3 +1195,146 @@ JSONL
|
||||
run devdesk attach --unsplit alpha
|
||||
[ "$(jq -r .closed <<<"$output")" = 0 ]
|
||||
}
|
||||
|
||||
fs_put() { # SESSION PATH MODE CONTENT-FILE
|
||||
local size
|
||||
size=$(stat -c %s "$4")
|
||||
base64 -w0 "$4" | devdesk attach --fs-put "$1" "$2" "$size" "$3"
|
||||
}
|
||||
last_json() { tail -n1 <<<"$output"; }
|
||||
|
||||
@test "fs-mkdir, fs-move and fs-copy stay inside the folder and never overwrite (#54)" {
|
||||
p="$DEVDESK_PROJECTS_ROOT/alpha"
|
||||
mkdir -p "$p/src" && printf 'a\n' >"$p/src/a.txt" && printf 'b\n' >"$p/b.txt"
|
||||
ln -s "$TMP/outside" "$p/out"
|
||||
ln -s b.txt "$p/link"
|
||||
run devdesk attach --fs-mkdir alpha docs/guides
|
||||
[ "$status" -eq 0 ] && [ -d "$p/docs/guides" ]
|
||||
[ "$(last_json | jq -r .path)" = docs/guides ]
|
||||
run devdesk attach --fs-mkdir alpha docs
|
||||
[[ $output == *"already exists"* ]]
|
||||
for bad in ../x /abs out/x 'a/../b' . '' docs/; do
|
||||
run devdesk attach --fs-mkdir alpha "$bad"
|
||||
[ "$status" -ne 0 ]
|
||||
done
|
||||
[ -z "$(ls -A "$TMP/outside")" ]
|
||||
# out/x once escaped to /x: set -e doesn't reach into $(...) (#54)
|
||||
[ ! -e /x ] && [ ! -e /abs ]
|
||||
|
||||
# rename a file, move a folder into another, never onto something
|
||||
run devdesk attach --fs-move alpha src/a.txt src/renamed.txt
|
||||
[ "$status" -eq 0 ] && [ -f "$p/src/renamed.txt" ] && [ ! -e "$p/src/a.txt" ]
|
||||
run devdesk attach --fs-move alpha b.txt src/renamed.txt
|
||||
[ "$status" -ne 0 ] && [[ $output == *"already exists"* ]]
|
||||
[ "$(cat "$p/b.txt")" = b ]
|
||||
run devdesk attach --fs-move alpha src docs/src
|
||||
[ "$status" -eq 0 ] && [ -f "$p/docs/src/renamed.txt" ]
|
||||
run devdesk attach --fs-move alpha docs docs/guides/docs
|
||||
[ "$status" -ne 0 ] && [[ $output == *"inside itself"* ]]
|
||||
run devdesk attach --fs-move alpha b.txt out/b.txt
|
||||
[ "$status" -ne 0 ]
|
||||
run devdesk attach --fs-move alpha ../outside x
|
||||
[ "$status" -ne 0 ]
|
||||
# a symlink moves as a link
|
||||
run devdesk attach --fs-move alpha link link2
|
||||
[ "$status" -eq 0 ] && [ -L "$p/link2" ] && [ -f "$p/b.txt" ]
|
||||
# copy: folders recursively, missing parents created
|
||||
run devdesk attach --fs-copy alpha docs/src new/place/src
|
||||
[ "$status" -eq 0 ] && [ -f "$p/new/place/src/renamed.txt" ] && [ -f "$p/docs/src/renamed.txt" ]
|
||||
run devdesk attach --fs-copy alpha b.txt docs/src/renamed.txt
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "fs-delete moves to the trash and fs-restore puts it back (#54)" {
|
||||
p="$DEVDESK_PROJECTS_ROOT/alpha"
|
||||
mkdir -p "$p/dir/sub" && printf 'x\n' >"$p/dir/sub/x" && printf 'y\n' >"$p/y.txt"
|
||||
ln -s "$TMP/outside" "$p/out"
|
||||
touch "$TMP/outside/keep"
|
||||
run devdesk attach --fs-delete alpha dir y.txt out
|
||||
[ "$status" -eq 0 ]
|
||||
id=$(last_json | jq -r .trash)
|
||||
[ "$(last_json | jq -c .paths)" = '["dir","y.txt","out"]' ]
|
||||
[ ! -e "$p/dir" ] && [ ! -e "$p/y.txt" ] && [ ! -L "$p/out" ] && [ -f "$TMP/outside/keep" ]
|
||||
# nothing deleted when one path is bad
|
||||
printf 'z\n' >"$p/z.txt"
|
||||
run devdesk attach --fs-delete alpha z.txt ../outside
|
||||
[ "$status" -ne 0 ] && [ -f "$p/z.txt" ]
|
||||
run devdesk attach --fs-delete alpha .
|
||||
[ "$status" -ne 0 ]
|
||||
# restore; a path taken meanwhile is refused
|
||||
printf 'new\n' >"$p/y.txt"
|
||||
run devdesk attach --fs-restore alpha "$id"
|
||||
[ "$status" -ne 0 ] && [[ $output == *"already exists: y.txt"* ]]
|
||||
rm "$p/y.txt"
|
||||
run devdesk attach --fs-restore alpha "$id"
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(cat "$p/dir/sub/x" "$p/y.txt")" = "$(printf 'x\ny')" ] && [ -L "$p/out" ]
|
||||
[ ! -e "$DEVDESK_STATE_DIR/trash/alpha/$id" ]
|
||||
run devdesk attach --fs-restore alpha "../../etc"
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "fs-put uploads any file into a folder; fs-download returns it (#54)" {
|
||||
p="$DEVDESK_PROJECTS_ROOT/alpha"
|
||||
head -c 3000 /dev/urandom >"$TMP/bin"
|
||||
run fs_put alpha assets/img/logo.bin new "$TMP/bin"
|
||||
[ "$status" -eq 0 ] && cmp -s "$TMP/bin" "$p/assets/img/logo.bin"
|
||||
[ "$(stat -c %a "$p/assets/img/logo.bin")" = 644 ]
|
||||
run fs_put alpha assets/img/logo.bin new "$TMP/bin"
|
||||
[ "$status" -ne 0 ] && [[ $output == *"already exists"* ]]
|
||||
printf 'v2' >"$TMP/v2"
|
||||
chmod 600 "$p/assets/img/logo.bin"
|
||||
run fs_put alpha assets/img/logo.bin replace "$TMP/v2"
|
||||
[ "$status" -eq 0 ] && [ "$(cat "$p/assets/img/logo.bin")" = v2 ] && [ "$(stat -c %a "$p/assets/img/logo.bin")" = 600 ]
|
||||
: >"$TMP/empty"
|
||||
run fs_put alpha empty.txt new "$TMP/empty"
|
||||
[ "$status" -eq 0 ] && [ -f "$p/empty.txt" ] && [ ! -s "$p/empty.txt" ]
|
||||
ln -s "$TMP/outside" "$p/out"
|
||||
for bad in out/x ../x /x assets; do
|
||||
run fs_put alpha "$bad" new "$TMP/v2"
|
||||
[ "$status" -ne 0 ]
|
||||
done
|
||||
[ -z "$(ls -A "$TMP/outside")" ]
|
||||
run fs_put alpha assets replace "$TMP/v2"
|
||||
[ "$status" -ne 0 ] && [ -d "$p/assets" ]
|
||||
# short stream: nothing left behind
|
||||
run bash -c "printf aGVs | '$REPO/bin/devdesk' attach --fs-put alpha short.txt 6 new"
|
||||
[ "$status" -ne 0 ] && [ ! -e "$p/short.txt" ] && [ -z "$(find "$p" -name '.*devdesk-*')" ]
|
||||
# download: binary body comes back whole
|
||||
run devdesk attach --fs-download alpha assets/img/logo.bin
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(sed -n '/DEVDESK-BLOB-BEGIN/{n;p}' <<<"$output" | base64 -d)" = v2 ]
|
||||
head -c 3000 /dev/urandom >"$p/rand"
|
||||
run devdesk attach --fs-download alpha rand
|
||||
sed -n '/DEVDESK-BLOB-BEGIN/{n;p}' <<<"$output" | base64 -d | cmp -s - "$p/rand"
|
||||
run devdesk attach --fs-download alpha out
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "fs-grep searches files, literally or by regex, git or not (#56)" {
|
||||
p="$DEVDESK_PROJECTS_ROOT/alpha"
|
||||
mkdir -p "$p/src" "$p/node_modules/x"
|
||||
printf 'const Answer = 42;\nlet b = "a:b";\n' >"$p/src/a.js"
|
||||
printf 'answer here\n' >"$p/node_modules/x/i.js"
|
||||
printf '\x00\x01answer' >"$p/bin.dat"
|
||||
run devdesk attach --fs-grep alpha - 'Answer'
|
||||
[ "$(jq -c '[.matches[] | [.p, .l, .t]]' <<<"$output")" = '[["src/a.js",1,"const Answer = 42;"]]' ]
|
||||
run devdesk attach --fs-grep alpha i 'answer'
|
||||
[ "$(jq -r '[.matches[].p] | join(",")' <<<"$output")" = src/a.js ]
|
||||
run devdesk attach --fs-grep alpha - '"a:b"'
|
||||
[ "$(jq -r '.matches[0].t' <<<"$output")" = 'let b = "a:b";' ]
|
||||
run devdesk attach --fs-grep alpha r 'A[n]swer = [0-9]+'
|
||||
[ "$(jq -r '.matches | length' <<<"$output")" = 1 ]
|
||||
run devdesk attach --fs-grep alpha - '[n]'
|
||||
[ "$(jq -r '.matches | length' <<<"$output")" = 0 ]
|
||||
# in a repository: git grep, .gitignore honoured, untracked files included
|
||||
git -C "$p" init -q
|
||||
printf 'node_modules/\n' >"$p/.gitignore"
|
||||
printf 'Answer again\n' >"$p/untracked.txt"
|
||||
run devdesk attach --fs-grep alpha w 'Answer'
|
||||
[ "$(jq -c '[.matches[] | [.p, .l]] | sort' <<<"$output")" = '[["src/a.js",1],["untracked.txt",1]]' ]
|
||||
run devdesk attach --fs-grep alpha x 'Answer'
|
||||
[ "$status" -ne 0 ]
|
||||
run devdesk attach --fs-grep alpha - ''
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user