Implement Hero File Manager — full WebDAV + Dioxus UI #2

Open
mik-tf wants to merge 28 commits from development into main
Owner

Summary

Web-based file manager with WebDAV backend, Dioxus UI, and Rhai scripting.

Architecture

Crate Purpose
hero_filemanager_server OServer with FileService RPC + WebDAV bridge
hero_filemanager_ui Dioxus 0.7 web UI (browse, preview, edit)
hero_filemanager_sdk JSON-RPC client library
hero_filemanager_rhai Rhai scripting bindings
hero_filemanager Binary entry point

Backend (FileService RPC over WebDAV)

  • List, create, rename, delete files and folders
  • Move/copy with auto-rename conflict resolution
  • Recursive breadth-first search across directories
  • Trash with original-path tracking (restore to correct location)
  • File content read/write via WebDAV GET/PUT
  • MCP endpoint auto-provided by OServer (22 tools)

UI (Dioxus 0.7 WASM)

  • Browse with list/grid toggle, breadcrumb navigation
  • Recursive sidebar folder tree (lazy-loaded, 4 levels)
  • Right-click context menu on file rows (rename, delete, trash, move, copy)
  • File preview (text, images, markdown) and text editor
  • Dialogs for new folder, rename, move/copy, delete confirmation
  • Keyboard shortcuts (Ctrl+N, Delete, F2, Ctrl+A, Ctrl+F)
  • Search bar with live results

Rhai Bindings

10 functions: fm_list, fm_mkdir, fm_rename, fm_delete, fm_search, fm_move, fm_copy, fm_trash, fm_restore, fm_read_file

Test Coverage

Suite Count CI
Unit tests (cargo test) 11 Yes
Smoke tests (curl) 19 Yes
Playwright API (RPC + MCP) 17 Yes
Playwright browser (UI) 6 Local only
Total 53 47 in CI

CI

  • make test-all: fmt, lint, unit tests, build, smoke tests, Playwright API
  • make ci-docker: above + cross-compile x86_64-gnu + aarch64-gnu
  • build-linux.yaml: triggers on v* tags only

Closes #1

Test plan

  • cargo fmt --check passes
  • cargo clippy --all-targets -- -D warnings clean
  • cargo test — 11 unit tests pass
  • make smoke-test — 19 pass, 3 skip (no WebDAV)
  • make e2e-test — 17 Playwright API tests pass
  • make ci-docker — all phases pass (test-all + amd64 + arm64)
## Summary Web-based file manager with WebDAV backend, Dioxus UI, and Rhai scripting. ### Architecture | Crate | Purpose | |-------|---------| | `hero_filemanager_server` | OServer with FileService RPC + WebDAV bridge | | `hero_filemanager_ui` | Dioxus 0.7 web UI (browse, preview, edit) | | `hero_filemanager_sdk` | JSON-RPC client library | | `hero_filemanager_rhai` | Rhai scripting bindings | | `hero_filemanager` | Binary entry point | ### Backend (FileService RPC over WebDAV) - List, create, rename, delete files and folders - Move/copy with auto-rename conflict resolution - Recursive breadth-first search across directories - Trash with original-path tracking (restore to correct location) - File content read/write via WebDAV GET/PUT - MCP endpoint auto-provided by OServer (22 tools) ### UI (Dioxus 0.7 WASM) - Browse with list/grid toggle, breadcrumb navigation - Recursive sidebar folder tree (lazy-loaded, 4 levels) - Right-click context menu on file rows (rename, delete, trash, move, copy) - File preview (text, images, markdown) and text editor - Dialogs for new folder, rename, move/copy, delete confirmation - Keyboard shortcuts (Ctrl+N, Delete, F2, Ctrl+A, Ctrl+F) - Search bar with live results ### Rhai Bindings 10 functions: `fm_list`, `fm_mkdir`, `fm_rename`, `fm_delete`, `fm_search`, `fm_move`, `fm_copy`, `fm_trash`, `fm_restore`, `fm_read_file` ### Test Coverage | Suite | Count | CI | |-------|------:|:--:| | Unit tests (cargo test) | 11 | Yes | | Smoke tests (curl) | 19 | Yes | | Playwright API (RPC + MCP) | 17 | Yes | | Playwright browser (UI) | 6 | Local only | | **Total** | **53** | **47 in CI** | ### CI - `make test-all`: fmt, lint, unit tests, build, smoke tests, Playwright API - `make ci-docker`: above + cross-compile x86_64-gnu + aarch64-gnu - `build-linux.yaml`: triggers on v* tags only Closes #1 ## Test plan - [x] `cargo fmt --check` passes - [x] `cargo clippy --all-targets -- -D warnings` clean - [x] `cargo test` — 11 unit tests pass - [x] `make smoke-test` — 19 pass, 3 skip (no WebDAV) - [x] `make e2e-test` — 17 Playwright API tests pass - [x] `make ci-docker` — all phases pass (test-all + amd64 + arm64)
Implement full PROPFIND response parsing following the hero_fossil
pattern with namespace-aware XML splitting (D:, ns0:, bare tags).
Add upload_file (PUT), download_file (GET), and read_text_file methods.
Include 6 unit tests for XML parsing, URL decoding, and multi-namespace support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add get_download_url, get_upload_url, read_file_content, and
upload_file methods to FileManagerClient for direct file transfer
operations bypassing JSON-RPC.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add api.rs module with JSON-RPC client for UI-to-backend communication.
Browse route now loads directory contents using use_effect + spawn async
pattern from hero_fossil. Preview and Edit routes load file content.
Includes refresh_directory helper for post-mutation reloads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NewFolder, Rename, Delete, MoveTo dialogs now make actual API calls
and refresh the directory listing after operations complete. Upload
dialog reads file bytes via Dioxus FileData API and uploads via PUT.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Toolbar paste button now calls move_items/copy_items API based on
clipboard action, then refreshes directory. Search input calls the
search API when query >= 2 chars and stores results in state.
File list double-click navigates to Browse (directories) or Preview
(files). Search results are displayed when a search is active.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Save button calls ApiClient::save_file_content via HTTP PUT with
status feedback. Preview pane renders markdown to HTML with support
for headings, bold, italic, inline code, fenced code blocks, links,
unordered lists, horizontal rules, and paragraphs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace stub functions with working implementations that use
tokio::runtime::Runtime::block_on() to call async SDK methods:
fm_list, fm_mkdir, fm_rename, fm_delete, fm_search.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Browse route now handles keyboard shortcuts on the main container:
Ctrl+C (copy), Ctrl+X (cut), Ctrl+V (paste with API call),
Ctrl+A (select all), Delete (open delete dialog), F2 (rename).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Dioxus.toml for the UI crate with web platform default and
app title. Add dist/ to .gitignore for Dioxus build output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
chore: final cleanup — fmt, clippy fixes, remove TODO stubs
Some checks failed
Build and Test / test (push) Failing after 3s
Build Linux / build-linux (linux-amd64, false, x86_64-unknown-linux-musl) (push) Failing after 13s
Build Linux / build-linux (linux-arm64, true, aarch64-unknown-linux-gnu) (push) Failing after 14s
Build and Test / test (pull_request) Failing after 3s
77d59bf86f
Apply cargo fmt across all crates, fix all clippy warnings in
hand-written code (clone_on_copy, redundant_closure, single_match).
Remove completed TODO comment in folder_tree. All 11 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(tests): add comprehensive smoke tests, Playwright E2E, and CI integration
Some checks failed
Build Linux / build-linux (linux-arm64, true, aarch64-unknown-linux-gnu) (push) Failing after 16s
Build Linux / build-linux (linux-amd64, false, x86_64-unknown-linux-musl) (push) Failing after 19s
Build and Test / test (pull_request) Successful in 2m27s
Build and Test / test (push) Successful in 2m52s
80436c809b
Add full automated test coverage for backend endpoints (JSON-RPC, MCP,
health, schema) and browser UI verification. Smoke tests (22 curl-based)
and Playwright API tests (17 tests) run in CI via make test-all. Browser
tests (6) are local-only when WebDAV is available.

- scripts/smoke-test.sh: curl-based endpoint tests (health, RPC CRUD, MCP, errors)
- scripts/run-smoke-tests.sh: server lifecycle wrapper for smoke tests
- tests/e2e/rpc.spec.ts: Playwright JSON-RPC protocol tests
- tests/e2e/mcp.spec.ts: Playwright MCP endpoint tests
- tests/e2e/browser.spec.ts: Dioxus UI tests (local, needs WebDAV)
- Makefile: smoke-test, e2e-test, e2e-browser targets
- scripts/test-all.sh: integrated pipeline (fmt, lint, test, build, smoke, e2e)
- lib.rs: crate-level clippy allows for generated code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix(ci): use gnu target and package-specific build for build-linux
Some checks failed
Build and Test / test (push) Successful in 2m25s
Build Linux / build-linux (linux-amd64, false, x86_64-unknown-linux-gnu) (push) Failing after 3m56s
Build Linux / build-linux (linux-arm64, true, aarch64-unknown-linux-gnu) (push) Failing after 4m55s
Build and Test / test (pull_request) Successful in 5m1s
2ea9bfd8a7
The build-linux workflow was failing because:
1. x86_64-unknown-linux-musl target is incompatible with some deps
2. build_binaries() builds entire workspace including the WASM UI crate

Switch amd64 target from musl to gnu (matching hero_books pattern) and
build only the hero_filemanager package instead of the whole workspace.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mik-tf force-pushed development from 2ea9bfd8a7
Some checks failed
Build and Test / test (push) Successful in 2m25s
Build Linux / build-linux (linux-amd64, false, x86_64-unknown-linux-gnu) (push) Failing after 3m56s
Build Linux / build-linux (linux-arm64, true, aarch64-unknown-linux-gnu) (push) Failing after 4m55s
Build and Test / test (pull_request) Successful in 5m1s
to f0d3cbb9d7
Some checks failed
Build Linux / build-linux (linux-amd64, false, x86_64-unknown-linux-gnu) (push) Failing after 1m13s
Build Linux / build-linux (linux-arm64, true, aarch64-unknown-linux-gnu) (push) Failing after 4m47s
Build and Test / test (push) Successful in 6m13s
Build and Test / test (pull_request) Successful in 6m17s
2026-02-21 17:54:32 +00:00
Compare
feat: complete all feature gaps — recursive search, conflict handling, trash tracking, folder tree, context menu, Rhai bindings
All checks were successful
Build and Test / test (push) Successful in 2m22s
Build and Test / test (pull_request) Successful in 2m47s
98bde8525e
- Recursive breadth-first search through directories (skips dot-dirs)
- Conflict auto-rename strategy for move/copy (file (1).txt pattern)
- Trash path encoding/decoding to preserve original paths on restore
- Recursive folder tree with lazy-loading subdirectories (up to 4 levels)
- Wire context menu to file list/grid rows via right-click
- Add fm_move, fm_copy, fm_trash, fm_restore, fm_read_file Rhai bindings
- Fix build-linux.yaml to trigger only on v* tags (not branch pushes)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
chore: remove unnecessary comments
All checks were successful
Build and Test / test (push) Successful in 5m13s
Build and Test / test (pull_request) Successful in 5m13s
54c0762fd2
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: make UI compile for WASM and fix local dev workflow
Some checks failed
Build and Test / test (pull_request) Failing after 3s
Build and Test / test (push) Failing after 4s
fb3acf92e4
- Add WASM-compatible SDK types (types_wasm.rs) with conditional
  compilation so SDK builds for both native and wasm32 targets
- Split reqwest dependencies per target (rustls-tls for native only)
- Fix Dioxus context provider to wrap AppState in Signal
- Fix RPC method names to lowercase (fileservice.list_directory)
- Fix RPC URL (remove trailing slash)
- Fix tokio block_on nesting with block_in_place
- Add WebDAV client timeouts (3s connect, 10s request)
- Add Makefile targets: dev, run-ui, build-ui
- Add router catch-all route for unknown paths
- UI routing still renders blank — needs browser DevTools debugging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix(ui): resolve blank page caused by infinite use_effect loop
Some checks failed
Build and Test / test (push) Failing after 3s
Build and Test / test (pull_request) Failing after 4s
4bab50215d
Move state.read() calls outside use_effect in Browse component to break
infinite reactive loop (read+write same Signal inside effect). Also fix
Link destinations in FolderTree and Breadcrumb to use Route enum instead
of string paths for proper SPA client-side navigation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix(ui): show actual backend error instead of generic "Method not found"
Some checks failed
Build and Test / test (pull_request) Failing after 3s
Build and Test / test (push) Failing after 3s
1843e03096
The OServer framework wraps service errors with JSON-RPC -32601 code and
generic "Method not found" message, but puts the real error in the "data"
field. Prefer the data field when extracting RPC errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add comprehensive design and implementation plan
Some checks failed
Build and Test / test (push) Failing after 3s
Build and Test / test (pull_request) Failing after 4s
62554b7cbc
Captures full architecture, crate layout, RPC methods, development setup,
technical gotchas (Dioxus 0.7 reactive loops, Link navigation, etc.),
and remaining work organized by priority for spec completion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: complete all features, 102 tests, security hardening
Some checks failed
Build and Test / test (push) Failing after 2s
Build and Test / test (pull_request) Failing after 2s
e195f4648a
- Fix serde compatibility (created_at/updated_at u64 -> OTime)
- Embed hero_fossil WebDAV server in-process
- Expand test coverage to 102 tests across 4 layers
- Add path traversal protection on all RPC methods
- Add XSS protection in markdown renderer (sanitize URLs)
- Bind WebDAV to 127.0.0.1 (localhost only)
- Add 100MB upload size limit
- Harden .gitignore (keys, certs, databases, IDE)
- Update README and DESIGN.md to reflect completed state
style: apply cargo fmt to fix CI formatting check
Some checks failed
Build and Test / test (pull_request) Failing after 53s
Build and Test / test (push) Failing after 1m9s
3fb4692314
fix: resolve clippy warnings for Rust 1.92 CI
Some checks failed
Build and Test / test (push) Failing after 1m27s
Build and Test / test (pull_request) Failing after 1m48s
8b19d6e451
- Replace manual div_ceil with .div_ceil() (clippy::manual_div_ceil)
- Remove unnecessary .clone() on Copy types (clippy::clone_on_copy)
- Collapse nested if statements (clippy::collapsible_if)
fix: adapt SDK and tests for upstream OTime→u64 created_at change
Some checks failed
Build and Test / test (push) Failing after 6m16s
Build and Test / test (pull_request) Failing after 6m19s
c45135eb4a
The upstream code generator (hero_rpc_osis) now generates u64 for
auto-managed timestamp fields (created_at, updated_at) instead of OTime.
Update the SDK client types and compatibility tests to match.
fix: update smoke and e2e tests for created_at u64 type
All checks were successful
Build and Test / test (pull_request) Successful in 3m1s
Build and Test / test (push) Successful in 3m31s
4220966ddc
The smoke test and Playwright E2E test also validated that created_at
was a string. Updated to match the upstream OTime→u64 change.
fix: repair broken UI operations and add error feedback
Some checks failed
Build and Test / test (push) Failing after 4s
Build and Test / test (pull_request) Failing after 3s
f773a0fe7d
- Fix dialog spawn cancellation: move dialog close inside async spawn
  to prevent Dioxus component-scoped tasks from being cancelled on unmount
- Fix download: use fetch+blob approach for cross-origin file downloads
- Fix copy/paste: use duplicate_items with autorename instead of
  copy_items with fail strategy
- Fix rename_item backend: list parent directory instead of get_file_info
  which fails for directories
- Fix WebDAV URL encoding: percent-encode path segments for special chars
- Add Edit option in context menu for markdown/text files
- Add toast notifications for operation success/failure feedback
- Change onkeypress to onkeydown for better browser compatibility
- Enhance smoke tests: strict RPC checks, WebDAV file ops, CORS headers
fix: folder click navigates, markdown image insert works
Some checks failed
Build and Test / test (pull_request) Failing after 3s
Build and Test / test (push) Failing after 4s
e0c793e26a
- Single-click on folder navigates into it (Ctrl+click for multi-select)
- Fix Image button in markdown editor to actually insert ![alt](url) text
  into the textarea after uploading, instead of storing in unused JS var
- Rename button to "Insert Image" for clarity
style: apply cargo fmt for CI formatting check
Some checks failed
Build and Test / test (pull_request) Failing after 50s
Build and Test / test (push) Failing after 1m7s
dfcc613fd0
fix: clippy needless_borrow, fmt consistency, and CI race condition
Some checks failed
Build and Test / test (pull_request) Failing after 2s
Build and Test / test (push) Failing after 3s
f9d8fe7cff
- Remove needless `&` on dir_path in folder_tree.rs
- Apply rustfmt to generated files for consistent formatting
- Remove `make fmt` from test-all.sh to fix bind-mount race
  where host linter reformats files between fmt and fmt-check
chore: remove outdated design document
Some checks failed
Build and Test / test (push) Failing after 3s
Build and Test / test (pull_request) Failing after 3s
3e2f4dd42b
Some checks failed
Build and Test / test (push) Failing after 3s
Build and Test / test (pull_request) Failing after 3s
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin development:development
git switch development

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff development
git switch development
git rebase main
git switch main
git merge --ff-only development
git switch development
git rebase main
git switch main
git merge --no-ff development
git switch main
git merge --squash development
git switch main
git merge --ff-only development
git switch main
git merge development
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_drive!2
No description provided.