Skip to content

Repository files navigation

Loft

Loft

A top slide-down shelf for macOS — stash your clipboard, notes, and files, a scroll away.

Release Latest release License: MIT Platform: macOS Built with Electron


Loft lives in your menu bar with no Dock icon. Flick your cursor to the top of the screen and scroll down — a shelf slides out of the menu bar with three panes: your clipboard history, a scratch note, and a drag-and-drop file tray. Scroll back up (or hit Esc) and it tucks away. It never steals focus, so it overlays whatever you're working in.

Loft reveal demo

✨ Features

  • 📋 Clipboard history — every copy (text and images) is captured automatically, newest first. Click an entry to view it in full and re-copy it to the system clipboard in one motion. Hover to remove, or Clear the lot.
  • 📝 Scratch notes — a persistent textarea that's always one scroll away. Debounced autosave; survives reveal, hide, and restart.
  • 🗂️ File tray — drag files onto the shelf to park them. Each shows its real macOS icon; double-click to open, hover to remove.
  • 🖱️ Scroll-to-reveal — a global gesture at the top edge of the screen. No hotkey to memorize, no window to manage.
  • 🎛️ Resizable panes — drag the dividers; your layout is remembered.
  • 🪶 Menu-bar native — no Dock icon, never steals focus, GPU-cheap slide.
  • 🔄 Auto-update — quiet background updates from GitHub Releases.

📦 Install

Grab the latest .dmg for your chip from the Releases page:

Chip File
Apple Silicon (M1–M4) Loft-<version>-arm64.dmg
Intel Loft-<version>-x64.dmg

Open the .dmg and drag Loft to Applications.

Important

Loft is currently distributed unsigned (no paid Apple Developer certificate yet), so macOS Gatekeeper blocks it on first launch. Clear the quarantine flag once after installing:

xattr -dr com.apple.quarantine /Applications/Loft.app

Grant Accessibility permission

The scroll-to-reveal gesture uses a global input hook, which needs Accessibility access. Loft prompts on first launch; if you miss it, enable it manually:

System Settings → Privacy & Security → Accessibility → enable Loft.

Loft detects the grant and starts working within a couple of seconds — no restart needed. (Because the build is unsigned, macOS resets this permission on each update; just toggle it back on.)

🇰🇷 한국어 설치 안내
  1. Releases 페이지에서 칩에 맞는 .dmg를 받으세요.

    파일
    Apple Silicon (M1–M4) Loft-<버전>-arm64.dmg
    Intel Loft-<버전>-x64.dmg
  2. .dmg를 열고 Loft를 Applications로 드래그합니다.

  3. 아직 코드 서명 전이라 macOS Gatekeeper가 첫 실행을 막습니다. 설치 후 한 번만 아래 명령으로 격리(quarantine) 플래그를 제거하세요.

    xattr -dr com.apple.quarantine /Applications/Loft.app

    macOS Sequoia(15)부터는 우클릭 → 열기 우회가 막혀서, 위 명령(또는 시스템 설정 → 개인정보 보호 및 보안 → "확인 없이 열기")이 확실한 방법입니다.

손쉬운 사용 권한 허용

스크롤로 펼치는 제스처는 전역 입력 훅을 쓰기 때문에 손쉬운 사용(Accessibility) 접근이 필요합니다. 첫 실행 시 Loft가 요청하며, 놓쳤다면 수동으로 켜세요.

시스템 설정 → 개인정보 보호 및 보안 → 손쉬운 사용 → Loft 활성화

권한을 켜면 몇 초 안에 Loft가 자동으로 인식해 동작합니다(재시작 불필요). 미서명 빌드라 업데이트할 때마다 이 권한이 초기화되니 다시 켜주면 됩니다. 또한 서명 전에는 자동 업데이트가 되지 않으므로 새 버전은 릴리즈 페이지에서 직접 받아야 합니다.

🛠️ Build from source

Prerequisites: Node.js 22+ and pnpm 10+.

git clone https://github.com/HuriHuchi/loft.git
cd loft
pnpm install        # also downloads Electron + rebuilds the native input hook

pnpm dev            # run in development (hot reload)
pnpm build:mac      # produce a distributable .dmg in dist/

Other scripts:

Command What it does
pnpm dev Launch the app with hot reload
pnpm typecheck Type-check main, preload, and renderer
pnpm build Bundle main/preload/renderer to out/
pnpm build:mac Bundle and package a .dmg

🧭 How it works

┌── main (Node) ───────────────┐     ┌── renderer (React) ──────────┐
│ uiohook-napi global wheel hook│ IPC │ slide-down panel, 3 panes    │
│ → detects scroll at top edge  │◀───▶│ clipboard / notes / files    │
│ tray, clipboard watcher, IPC  │     │ GPU transform for the slide  │
└───────────────────────────────┘     └──────────────────────────────┘
  • A frameless, transparent window is pinned to the top of the active display and shown without stealing focus (showInactive). Revealing/hiding is a pure CSS translateY transition on the content — the OS window bounds never animate, so the slide stays cheap.
  • The reveal/dismiss gesture is decided in the main process from a global wheel hook (uiohook-napi); scroll-up inside a scrollable list navigates it and only dismisses on overscroll.
  • Clipboard history and dropped files persist under the app's userData; notes live in the renderer's localStorage.

Stack: Electron 43 · electron-vite · React 19 · Tailwind CSS 4 · TypeScript.

🚀 Releasing

Releases are fully automated with release-please + Conventional Commits:

  1. Land feat: / fix: commits on main (a commit-msg hook lints the format).
  2. release-please opens a release PR that bumps the version and updates the changelog. Merge it.
  3. CI tags the release, builds per-arch .dmgs, and publishes them with an latest-mac.yml auto-update feed.

📸 Capturing media

To refresh the demo GIF, screen-record the reveal gesture (e.g. with Kap), then optimize it before committing so it stays git-friendly (the current docs/demo.gif is ~4.4 MB, downscaled to 1200px at 15 fps with ffmpeg):

ffmpeg -i recording.gif -vf "fps=15,scale=1200:-1:flags=lanczos,palettegen=stats_mode=diff" -y palette.png
ffmpeg -i recording.gif -i palette.png -lavfi "fps=15,scale=1200:-1:flags=lanczos,paletteuse=dither=bayer:bayer_scale=3" -y docs/demo.gif

🤝 Contributing

Issues and PRs welcome. Please keep commit messages in Conventional Commits form (feat:, fix:, docs:, chore:, …) so the release automation and changelog stay accurate.

📄 License

MIT © 강희욱 (@HuriHuchi)

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages