Thanks for the interest. This document covers how to set up a dev environment, run the test suite, and submit a pull request.
Be kind. This is a file manager, not a kernel — there's no need for intensity. Disagreements about design are resolved in code review, not in commit messages.
- Bun ≥ 1.3 (the project pins
bun@1.3.5inpackageManager). - Rust stable (the project uses Tauri 2.11, which requires
rustc ≥ 1.77). - Tauri system deps — see
https://tauri.app/start/prerequisites/. On Debian / Ubuntu:
sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev \ libayatana-appindicator3-dev librsvg2-dev \ build-essential curl wget file libssl-dev
- macOS: Xcode Command Line Tools (
xcode-select --install). - Windows (Sprint 7): Visual Studio Build Tools 2022 with the "Desktop development with C++" workload.
# Install JS deps.
bun install
# Run the frontend dev server on http://localhost:1420.
bun --filter @kenafold/app run dev
# In a second terminal, run the full Tauri app (frontend + Rust backend).
bun --filter @kenafold/app run tauri devThe first tauri dev invocation downloads and compiles a few hundred
Rust crates — go grab a coffee. Subsequent runs are incremental.
# Frontend (Vitest + Testing Library).
bun run test
# Rust (cargo test on every target the runner supports).
cd apps/app/src-tauri && cargo test --all
# Typecheck (TypeScript).
bun run typecheck
# Lint (ESLint flat config + react-hooks).
bun run lintBefore opening a PR, make sure all four pass. The CI workflow in
.github/workflows/ci.yml runs the same checks.
- TypeScript is the project's primary frontend language. The
codebase uses
strict: trueand"noUncheckedIndexedAccess": true. - Prettier is configured at the repo root — run
bun run formatbefore committing. - ESLint flat config (
apps/app/eslint.config.js) flagsreact-hooks/set-state-in-effect,react-hooks/refs, andreact-refresh/only-export-components. When you genuinely need one of these patterns, silence the rule with a comment explaining why. - Rust uses
cargo fmtandcargo clippywith-D warnings(set up in the CI workflow). New public items should carry doc comments (///).
The codebase is a Tauri 2 monorepo with two apps and a shared UI package:
apps/
app/ # The Kenafold desktop app (frontend + Rust backend).
web/ # Marketing landing page (TanStack Start).
packages/
ui/ # Shared shadcn/ui components + Tailwind 4 theme.
Inside apps/app, the frontend follows "Screaming Architecture" — each
feature owns its own layers under src/features/<feature>/:
src/features/<feature>/
api/ # React hooks = use-cases (call infra, expose state)
domain/ # Pure TS: types, logic, no framework deps
infra/ # Tauri invoke adapters (gateway pattern)
components/ # UI components
hooks/ # Feature-specific hooks (not use-cases)
Tauri commands live in apps/app/src-tauri/src/, one Rust file per
feature (fs.rs, commands.rs, git_status.rs, …). Commands are
exposed to the renderer via the fs.gateway.ts thin wrapper.
- Open an issue first for anything beyond a one-line fix. The issue should describe the user-visible problem and the proposed solution. Use the templates if they exist.
- Branch from
main. Use a descriptive name:feat/grid-thumbnails,fix/trash-leak-on-cancel,docs/translate-pt-br. - Write tests for the change. Bug fixes should add a regression test; features should add coverage for the new behavior.
- Run the test suite locally before pushing. CI is the source of truth, but a green local run saves a round-trip.
- One logical change per PR. If you find yourself mixing a refactor with a feature, split it into two PRs.
- Update docs if the change is user-visible: README,
docs/,CHANGELOG.md"Unreleased" section, i18n keys in bothen.tsandes.ts.
We use a manual tag-driven release:
- Move the "Unreleased" section in
CHANGELOG.mdinto a dated versioned entry. - Bump
versioninapps/app/src-tauri/tauri.conf.jsonandapps/app/package.json. git tag v0.x.y && git push --tags.- The
release.ymlworkflow builds the macOS, Linux.deb, and Linux arm64 artefacts and attaches them to the GitHub release.
Kenafold ships with English (en) and Spanish (es) locales. To add
a new language:
- Copy
apps/app/src/shared/i18n/locales/en.tstoapps/app/src/shared/i18n/locales/<code>.ts. - Translate the strings. Keep the keys (the part before the colon)
exactly as in
en.ts— the type system enforces that the locale file's keys match. - Add the new code to
SUPPORTED_LANGUAGESinapps/app/src/shared/i18n/i18n.ts. - The
LanguagePickerin the settings panel will surface the new locale automatically.
The locale files are type-checked (as const) — TypeScript will
catch any missing or extra key.
Use the issue templates (when present). For
security issues, follow SECURITY.md instead of opening a public
issue.