{"id":50957469,"url":"https://github.com/felipstein/xhare","last_synced_at":"2026-06-18T09:03:06.584Z","repository":{"id":357867364,"uuid":"1238881046","full_name":"Felipstein/xhare","owner":"Felipstein","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-14T16:54:03.000Z","size":482,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-14T17:10:48.251Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Felipstein.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-14T14:42:47.000Z","updated_at":"2026-05-14T16:54:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Felipstein/xhare","commit_stats":null,"previous_names":["felipstein/xhare"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/Felipstein/xhare","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Felipstein%2Fxhare","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Felipstein%2Fxhare/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Felipstein%2Fxhare/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Felipstein%2Fxhare/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Felipstein","download_url":"https://codeload.github.com/Felipstein/xhare/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Felipstein%2Fxhare/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34483284,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-18T02:00:06.871Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-18T09:03:03.650Z","updated_at":"2026-06-18T09:03:06.564Z","avatar_url":"https://github.com/Felipstein.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Xhare\n\n**Real-time file sharing between Macs and PCs on the same local network.**\nDrag, drop, done.\n\nXhare is a small desktop app that finds every other Xhare instance on your Wi‑Fi\nor wired LAN and lets you push files to all of them at once — no accounts, no\ncloud round-trips, no chat-app compression. Files travel directly between\nmachines over TCP at LAN speeds (think: hundreds of MB/s on gigabit Ethernet).\n\nBuilt with [Tauri 2](https://tauri.app/) (Rust + WebView), React 19,\nTypeScript, and Tailwind CSS v4. Lives in your menu bar / system tray.\n\n---\n\n## ✨ Features\n\n- **Zero‑config LAN discovery** — mDNS-based, peers show up automatically the\n  moment they launch Xhare. Falls back to manual IP entry when needed.\n- **Direct TCP transfer** — 256 KB chunks with CRC32 per chunk, broadcast to\n  every online peer in parallel. No server in the middle.\n- **Folder support** — drop a folder and Xhare auto-zips it in the background\n  with progress feedback (\"Zipping… 47%\") before transmitting.\n- **Clipboard‑aware** — `⌘V` / `Ctrl+V` pastes a file straight from Finder or\n  Explorer (including screenshots and image data from the system clipboard);\n  `⌘C` / `Ctrl+C` copies selected received files as real file references so\n  pasting in Finder / Explorer / WhatsApp / iMessage drops the actual file.\n- **Smart received files**\n  - Browser-style name dedupe: `notes.txt`, `notes (1).txt`, …\n  - Saved-to-disk indicator, hover actions for save / open / reveal\n  - Batched OS notifications (one toast per burst, suppressed while window\n    is focused)\n- **Multi-select with bulk actions** — `⌘A` / `Ctrl+A` toggles select-all,\n  click anywhere on a row to toggle, floating action bar to save or delete\n  selected files in one go.\n- **Native tray icon** — closing the window keeps the app alive in the menu\n  bar (macOS) or system tray (Windows), Docker-style. The macOS dock icon\n  hides itself in that state and comes back when the window is reopened.\n- **Cross-platform UX parity** — keyboard, drag-drop, clipboard, and reveal\n  behaviors all map to native OS conventions on both platforms.\n\n---\n\n## 📥 Download\n\nPre-built installers are published on the\n[Releases page](https://github.com/Felipstein/xhare/releases):\n\n- **macOS** — universal `.dmg` (Intel + Apple Silicon)\n- **Windows** — `.exe` NSIS installer (no admin required, auto-detects pt-BR)\n\n### First launch\n\nBuilds are **unsigned**, so the OS will complain the first time you open them.\n\n**macOS:** double-click is blocked. Go to **System Settings → Privacy \u0026\nSecurity**, scroll to the bottom and click **\"Open Anyway\"** next to the\nXhare entry. First time only — subsequent launches are normal.\n\n**Windows:** SmartScreen may warn. Click **\"More info\"** → **\"Run anyway\"**.\n\n---\n\n## 🚀 Quick start (after installing)\n\n1. Launch Xhare on every machine you want to share between.\n2. They show up in the **Devices** sidebar within ~2 seconds.\n3. Drag any file or folder onto the window — or `⌘V` / `Ctrl+V` to paste —\n   and it streams to every online peer.\n4. Received files land in the feed. Click **Save** to copy them to your\n   download folder, or open them straight from cache.\n\nThe cache is wiped on app exit, so anything you don't explicitly save is\ndiscarded — your real copies live wherever you chose to save them.\n\n---\n\n## 🛠 Development\n\n### Prerequisites\n\n| | macOS | Windows |\n|---|---|---|\n| Node.js 20+ | `volta install node` or [nodejs.org](https://nodejs.org/) | same |\n| pnpm 9+ | `npm i -g pnpm` | same |\n| Rust (stable) | `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \\| sh` | `winget install Rustlang.Rustup` |\n| C/C++ toolchain | — | [MSVC Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) with \"Desktop development with C++\" workload |\n\nAfter installing Rust, restart your terminal so `cargo` is on PATH.\n\n### Setup\n\n```bash\ncd app\npnpm install\n\n# pnpm 10+ requires explicit approval for packages with build scripts\npnpm approve-builds   # approve esbuild and unrs-resolver when prompted\n```\n\n### Run\n\n```bash\ncd app\npnpm tauri dev\n```\n\nThe first launch builds the Rust crate (a couple of minutes); subsequent\nlaunches are near-instant thanks to incremental compilation.\n\n### Commands\n\n| Command | What it does |\n|---|---|\n| `pnpm tauri dev` | Full app in dev mode with hot reload |\n| `pnpm dev` | Vite dev server only (no Tauri shell) |\n| `pnpm build` | Production frontend build |\n| `pnpm tauri build` | Build native installers locally (`.dmg` / `.exe`) |\n| `pnpm typecheck` | TypeScript check, no emit |\n| `pnpm lint` / `pnpm lint:fix` | ESLint |\n| `pnpm test` / `pnpm test:watch` | Vitest |\n\n---\n\n## 🏛 Architecture\n\n```\napp/\n├── src/\n│   ├── components/        UI primitives (Button, Dialog, Toast, …)\n│   ├── features/\n│   │   ├── devices/       Peer list + manual-add dialog\n│   │   ├── file-feed/     File rows, drag overlay, bulk action bar\n│   │   ├── logs/          In-app log viewer\n│   │   └── settings/      Download folder + cache TTL\n│   ├── hooks/             Cross-cutting hooks (paste, drag-drop, shortcuts)\n│   ├── services/          Thin wrappers around Tauri invokes\n│   ├── stores/            Zustand stores (devices, files, settings, …)\n│   ├── types/             Shared TS types\n│   └── utils/             Small helpers (formatSize, uniqueName, cn)\n└── src-tauri/\n    └── src/\n        ├── discovery.rs   mDNS browse + ARP scan + heartbeat reconciliation\n        ├── transfer.rs    TCP file protocol, folder zip, clipboard ops\n        ├── lan_scan.rs    Cross-platform `arp -a` parser\n        ├── settings.rs    Persistent JSON settings (atomic writes)\n        ├── tray.rs        Native tray icon + unread badge\n        └── logger.rs      File + terminal logging with daily rotation\n```\n\n### Wire protocol (TCP, port 9876)\n\n```\n┌──────────────────────┬──────────────────────────────┐\n│ u32  header length   │ header bytes (JSON, UTF-8)   │\n└──────────────────────┴──────────────────────────────┘\n┌──────────────────────┬──────────┬───────────────────┐\n│ u32  chunk length    │ u32 crc32│ chunk bytes       │   ← repeats\n└──────────────────────┴──────────┴───────────────────┘\n┌─────────────────┐\n│ u32 length = 0  │ ← EOF sentinel\n└─────────────────┘\n```\n\nHeader schema: `{ fileId, name, size, from }`. Chunks are 256 KB; CRC mismatch\nor short read aborts the transfer with a structured error event.\n\n### State of the art on each platform\n\n| Concern | macOS | Windows |\n|---|---|---|\n| Cache dir | `~/Library/Caches/Xhare/` | `%LOCALAPPDATA%\\Xhare\\` |\n| Logs | `~/Library/Application Support/com.felipe.xhare/logs/` | `%APPDATA%\\com.felipe.xhare\\logs\\` |\n| Settings | `~/Library/Application Support/com.felipe.xhare/settings.json` | `%APPDATA%\\com.felipe.xhare\\settings.json` |\n| Tray | Menu bar (top) | System tray (bottom right) |\n| Reveal in folder | `open -R` | `explorer /select,` |\n| Clipboard files | `NSPasteboard` via AppleScriptObjC | `Get-Clipboard -Format FileDropList` |\n\n---\n\n## 📦 Releasing\n\nUse the helper script — it bumps every manifest, commits, tags, and pushes in\none shot:\n\n```bash\ncd app\npnpm release:patch    # 0.1.0 → 0.1.1   bug fixes\npnpm release:minor    # 0.1.5 → 0.2.0   new feature, backward compatible\npnpm release:major    # 0.4.2 → 1.0.0   breaking change\n```\n\nThe script ([`app/scripts/release.mjs`](app/scripts/release.mjs)):\n\n1. Refuses to run if you're not on `main`, the working tree is dirty, or\n   `main` is out of sync with `origin`.\n2. Bumps the version in `package.json`, `tauri.conf.json`, and `Cargo.toml`\n   (plus refreshes `Cargo.lock`).\n3. Commits with `release: vX.Y.Z` and creates an annotated tag.\n4. Pushes both the commit and the tag.\n\nThe tag push triggers\n[`.github/workflows/release.yml`](.github/workflows/release.yml), which builds\nthe macOS universal `.dmg` and Windows `.exe` (NSIS) in parallel and publishes\na GitHub Release with both binaries attached (~15-25 min end-to-end).\n\n\u003e **First release:** since `package.json` already starts at `0.1.0`, cut the\n\u003e very first tag manually instead of bumping:\n\u003e ```bash\n\u003e git tag v0.1.0 \u0026\u0026 git push --tags\n\u003e ```\n\u003e Every release after that goes through `pnpm release:*`.\n\nYou can also dispatch the workflow manually from the Actions tab (no new tag\nneeded) to rebuild after fixing a CI issue.\n\n---\n\n## 🧪 Testing\n\nUnit tests run with Vitest (`pnpm test`). Components use\n`@testing-library/react`; Tauri APIs are mocked in `src/test/setup.ts`.\n\nThe Rust side has `cargo test` (mostly small unit tests for path helpers and\nparsers).\n\n---\n\n## 🛣 Roadmap\n\n- [ ] Image / video thumbnails in the feed\n- [ ] Pause / resume / cancel mid-transfer\n- [ ] Global hotkey (`⌘Shift+X` / `Ctrl+Shift+X`) to summon the window\n- [ ] Drag onto the tray icon to send (native, hard cross-platform)\n- [ ] Code signing + notarization for friction-free first launch\n- [ ] Auto-update via Tauri updater\n\n---\n\n## 📜 License\n\nMIT — see [LICENSE](LICENSE) if present, otherwise treat as MIT.\n\n---\n\n## 🙋 FAQ\n\n**Does it work across networks?**\nNo — both devices must be on the same LAN (same Wi-Fi, same router). Xhare\ndoesn't punch through NAT or proxy through a server.\n\n**Why are notifications coming from \"Terminal\" / \"PowerShell\"?**\nThat's expected in **dev mode** (`pnpm tauri dev`): the OS attributes the\nnotification to the parent process. Run a build (`pnpm tauri build`) and\ninstall the produced `.dmg` / `.exe` — the bundled app registers properly and\nnotifications come from \"Xhare\" with the right icon.\n\n**Where do received files go?**\nInto the OS cache dir (see [State of the art](#state-of-the-art-on-each-platform)\nabove), until you either save them explicitly or quit the app — quitting wipes\nthe cache so nothing accumulates.\n\n**Can I add a peer manually if mDNS isn't working?**\nYes — there's a \"+\" button at the top of the device list that takes an IP.\nThe peer needs to be running Xhare for the transfer port (`9876/tcp`) to be\nopen.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffelipstein%2Fxhare","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffelipstein%2Fxhare","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffelipstein%2Fxhare/lists"}