- Swift 99.3%
- Shell 0.7%
| Sources/MemoBar | ||
| .gitignore | ||
| build.sh | ||
| Info.plist | ||
| LICENSE | ||
| Package.swift | ||
| README.md | ||
Memobar
A macOS menu bar client for Memos. Write a note from anywhere with a keystroke, search and edit what you already wrote, and never open the web UI for a one-line thought again.
Native Swift and SwiftUI. No Xcode project, no package dependencies, one
build.sh.
Requirements
macOS 14 or later, the Xcode command line tools (xcode-select --install), and
a Memos server you can reach. Tested against the v1 REST API; see
API compatibility for how older servers are handled.
Build and install
git clone <this-repo> memobar
cd memobar
./build.sh
cp -R MemoBar.app /Applications/ && open /Applications/MemoBar.app
Roughly two minutes with the command line tools already installed. build.sh
compiles a release binary, assembles the .app bundle around it, and ad-hoc
signs it.
Setup
The first launch opens Settings. Two fields:
Server URL is the base URL with no trailing path, for example
https://memos.example.com. Plain http:// works, which matters because a
self-hosted instance is often at something like http://192.168.1.50:5230.
Prefer https:// where you can, since the access token is a bearer credential
sent on every request.
Access token comes from Memos under Settings, My Account, Access Tokens. It is stored in your login keychain, not in a plist or a dotfile.
Test Connection confirms both before you save. Launch at login is a checkbox
there too, wired through SMAppService.
Using it
Click the menu bar icon, or press ⌃⌥M from any application, and the compose
panel appears. Type, then ⌘↩ to save and close. Markdown and #tags work as
they do in Memos, and typing # offers the tags you already use.
Attach files by dropping them anywhere on the panel, through the paperclip
button, or by pasting an image from the clipboard via the ··· menu. Files go
up to 32 MB each.
Below the compose box are a search field and your memos. The list pages in fifty at a time as you scroll, with a Load more button as a backstop. Drag the handle at the very bottom edge to resize the panel; the height is remembered and capped so the panel always fits under the menu bar on whatever screen you are on.
Click a memo to load it into the compose box and edit it in place. The button
becomes Update, the border turns orange, and esc cancels and restores whatever
you were writing before. Right click a memo for pin, archive, copy, and open in
browser.
··· then Open in Window moves the panel into an ordinary resizable window that
stays where you put it. Dragging the popover away from the menu bar does the
same thing. While that window is open the menu bar icon raises and hides it
instead of opening the popover.
Keyboard
| Shortcut | Action |
|---|---|
⌃⌥M |
Open the compose panel from any app |
⌘↩ |
Save the new memo, or update the one being edited |
⌘F |
Focus the search field |
↑ ↓ |
Move the list selection (when the compose box is not focused) |
↩ |
Edit the selected memo |
⌘⌫ |
Archive the selected memo |
esc |
Cancel an edit |
The list shortcuts are deliberately inert while the compose box has focus, so
⌘⌫ still means delete-to-start-of-line where you are typing.
What survives a restart
The draft is written on every keystroke, so closing the popover or quitting
mid-note loses nothing. Staged attachments survive a closed popover. The token
lives in the keychain; the server URL, default visibility, panel height, and the
API dialects the app has learned live in UserDefaults.
API compatibility
Memos has renamed things across releases, and rather than pinning a version MemoBar works out what it is talking to and remembers the answer. Everything targets the v1 REST surface.
Visibility enum. Both PRIVATE and VISIBILITY_PRIVATE have shipped. The
spelling is read off the list response, and if a save still returns 400 the app
flips it and retries once.
Attachments. /api/v1/resources became /api/v1/attachments in v0.25.
Uploads try one and fall back to the other on a 404, and the memo payload then
uses the matching field name. If a server refuses attachments inline on create,
the memo is created first and the files are bound with SetMemoAttachments.
Search. The list endpoint takes a CEL filter whose grammar was rewritten
twice between v0.22 and v0.30. Only content.contains("…") is used, stable
since v0.23. A 400 turns server-side search off and the box filters loaded
memos locally instead, labelling itself "local" so the difference is visible.
Saving Settings resets that, since a different server may accept it.
Archive. state replaced row_status in v0.23, so archiving tries the
first and falls back to the second.
Tags. Three shapes across versions. The current user is resolved through
/auth/me, then /auth/sessions/current, then /auth/status; tags come from
users/{u}:getStats, then users/{u}/stats, then the old memos/-/tags. If
all of them fail, autocomplete stays quiet.
Notes for hacking on it
Sources/MemoBar is nine files. AppState holds everything and is the only
ObservableObject; MemosAPI and Attachments are the network layer;
ComposeView and SettingsView are the whole UI.
The global hotkey uses Carbon's RegisterEventHotKey, which deliberately avoids
needing Accessibility permission. To rebind it, change the defaults in
HotKey.register: keyCode is a kVK_* constant and modifiers an OR of
controlKey, optionKey, cmdKey, shiftKey.
Two things that are easy to break. The popover's height is set explicitly and
the memo list is the only element allowed to flex, which is what stops the panel
from growing past the screen; NSHostingController.sizingOptions must stay
empty or its Auto Layout constraints override that. And the Edit menu in
MainMenu.swift looks pointless because an accessory app never displays a menu
bar, but without it ⌘V has nothing to match against and pasting only works
from the right-click menu.
Known limitations
Attachments can only be added to a new memo, not to an edit, because the app does not track a memo's existing files and a partial update could replace them. Archiving has no undo. The bundle is ad-hoc signed, so macOS may ask once for keychain access, and again after each rebuild since the signature changes.
License
MIT. See LICENSE.