feat: add tar.gz Linux target and simplify AUR PKGBUILD#133
Conversation
adibhanna
left a comment
There was a problem hiding this comment.
Nice direction — moving the AUR -bin package off AppImage extraction onto a plain tarball is the right call, and dropping the whole --appimage-extract + chmod dance (#70/#74/#92) is justified since tar preserves permissions. I verified the two load-bearing assumptions and they hold:
- Top-level tar dir — electron-builder's
tar()setsprefix: path.basename(outFile, ".tar.gz"), so the archive's top folder is exactlyZenNotes-<ver>-linux-x64, matching the PKGBUILD's_extracted. ✅ - Symlink target —
executableName: "ZenNotes"means the binary is namedZenNotes, so/usr/bin/zennotes → /opt/zennotes-bin/ZenNotesandExec=zennotesare correct. ✅
A few blocking items before merge:
1. Scope the new extraResources/extraFiles to linux
extraFiles (LICENSE) and the new arch-extras extraResources entries are added at the global build level, so they ship in the mac and win builds too. On macOS extraFiles copies into ZenNotes.app/Contents/, leaving a stray Contents/LICENSE at the bundle root — and this app notarizes (afterSign: scripts/notarize.cjs), where unexpected top-level files in Contents/ are a common signing/notarization failure. The Linux .desktop + 8 PNG icons inside the mac/win installers are also pure bloat. Please move both new blocks under "linux" (electron-builder supports per-platform extraFiles/extraResources).
2. pkgver 2.3.0 points at a tarball that doesn't exist
The v2.3.0 release only has .AppImage, .deb, and .pacman — the tar.gz is produced by this PR, so …/releases/download/v2.3.0/ZenNotes-2.3.0-linux-x64.tar.gz 404s and makepkg can't fetch the source. The PKGBUILD/.SRCINFO need to target the first release that actually ships the tarball (the next release), not 2.3.0.
3. sha256sums still reference the old AppImage hash
As you noted, f7f6a67b… is the v2.2.0 AppImage hash. Needs regenerating with updpkgsums against the real published tarball — gated on #2, since the asset has to exist first.
Minor
- You verified
chrome_crashpad_handleris-rwxr-xr-x; the mainZenNotesbinary's exec bit is equally load-bearing for launch — worth a quicktar -tvzf … | grep -E '/ZenNotes$'to confirm. - The removed PKGBUILD comments captured real context (#70/#74/#92). A one-liner noting "tar.gz preserves perms, so the chmod workarounds are gone" would keep that history.
|
Addressed review feedback:
tar -tvzf ZenNotes-2.3.0-linux-x64.tar.gz | grep -E '/ZenNotes$' sha256sums still needs |
4e1b961 to
b3a0342
Compare
- Add tar.gz to electron-builder Linux targets - Include desktop file icons via arch-extras in extraResources + LICENSE - Rewrite PKGBUILD to use tar.gz instead of AppImage extraction
- Moved package spope to linux
- Comment in PKGBUILD
b3a0342 to
b49d227
Compare
* docs: update in-app help for v2.4.0 Document CSV databases (how-to + core concept + database-grid shortcuts), the comments keyboard flow (Mod+Alt+M, Mod+Shift+C, panel j/k/e/r/d), the always-on Alt+H/J/K/L pane-focus motions, a new Palettes and pickers shortcut section, and the expanded theme family list in Appearance. * Fix tab scroll position restore (#127) * Fix calendar date note detection (#131) * Add configurable daily and weekly note patterns (#77) Daily and weekly notes can use date-pattern tokens (yyyy, MM, MMM, dd, EEE, ww, ...) for both the directory and the title/filename, with a locale for localized month and weekday names. Literal text is single-quoted, e.g. 'Daily Notes'/yyyy/MM-MMM. Recognition re-formats each note's recovered date and accepts it only when it round-trips, so any token combination resolves -- including titles that encode the day via ISO week + weekday (yyyy-'W'ww-EEE) rather than day-of-month. Changing a pattern snapshots the previous one so existing notes stay recognized, and a Reset to defaults control restores the directory, naming, and locale. Closes #77 * Fix duplicated hover text on sidebar icons (#141) IconBtn rendered its label through both the native title attribute and a custom hover bubble, so macOS showed the tooltip text twice. Drop the redundant native title; the styled bubble and aria-label remain, matching the editor toolbar's tooltip pattern. * docs: add #77 feature and #141 fix to v2.4.0 release notes * Keep release notes local-only (stop tracking docs/releases) Release notes and launch copy are personal/local working docs, not repo content. Untrack the v2.4.0 notes (the file stays on disk) and gitignore docs/releases/ so they can't be committed again. * Fix blank Window menu items on macOS (#154) Electron 41 no longer supplies default text for the macOS tab roles (toggleTabBar, selectNextTab, selectPreviousTab, mergeAllWindows), so the Window menu rendered them as blank rows that thinned out on repeat opens. Give each role an explicit label so the rows render their text. * Fix sidebar header not reflecting the remote vault (#153) In remote mode the sidebar header showed vault.name -- the server-side vault folder (e.g. "workspace") -- while the vault switcher names the same vault after the connection profile the user created (e.g. "Home"). The header label and its color/initial badge therefore disagreed with the switcher. Bind the header to the current switcher entry's name so the label and badge stay in sync with the dropdown; fall back to vault.name when there is no current entry. * Fix switching from a remote workspace back to a local vault openLocalVault bailed out when the target path equaled the current vault.root, without checking workspaceMode. In remote mode vault.root is the server-reported path, and a localhost server reports the served folder's real on-disk path -- which equals the local vault's own path. Switching back to that local vault was therefore mistaken for reopening the current vault and silently did nothing. Only short-circuit when already in local mode on the same root. * Fix hint mode in the tasks and tags views (#151) 2.3.0 moved hint mode to <leader>h, but VimNav bailed out of the tasks/tags tabs for every key except the old standalone `f`, so the leader -- and therefore hint mode and every other leader command -- was dead there. Let leader input through in those panels; VimNav now arms and consumes the leader before TasksView can see it, so <leader>h works and Space-to-toggle falls back to x (Space still toggles for anyone who remapped their leader off Space). While the hint overlay is open, the tasks/tags handlers (list, calendar, kanban) now yield every key to it, so hint labels that collide with panel keys (j/k/x/o, Esc) are no longer stolen. Also stop Esc from closing the tasks and tags tabs. They are tabs like any note tab, so Esc now only clears an active filter and leaves the tab open; close with :q, the header button, or the OS shortcut. * Fix remapped pane-focus shortcuts colliding with the editor (#124) Remapping the pane-focus shortcuts (global.focusPane*) onto a key CodeMirror also binds -- e.g. Ctrl-h (delete char) or Ctrl-k (kill line) -- ran both: the bubble-phase window handler fired *after* CodeMirror had already executed its command, so the key deleted text and moved focus. Handle these shortcuts in the capture phase and consume the event so the editor never sees a key bound to focus. The handler bails while Settings, a palette, or a modal is open so it can't steal keys from the keybinding recorder. Also fix sidebar->pane navigation: focusPaneOrEdgePanel always moved geometrically from activePaneId, so pressing focus-right from the sidebar jumped from the last active pane and skipped the pane next to the sidebar. When focus is on a horizontal edge panel, move relative to that panel and land on the edge-most pane when crossing into the editor. * Add Vim yank-to-clipboard and yank highlight; fix Linux clipboard hints (#144) On Linux the editor's right-click menu hardcoded Mac hints ("⌘X" etc.), and the Ctrl clipboard keys collided with codemirror-vim's own Ctrl bindings (Ctrl+A increment, Ctrl+V visual block, Ctrl+X decrement), so they appeared broken. - Menu hints now derive from formatKeyToken('Mod+...'), showing Ctrl on Linux/Windows and ⌘ on macOS. - New 'Yank to system clipboard' Vim setting (clipboard=unnamed): yank/delete/change to the unnamed register also copy to the OS clipboard, while vim's Ctrl bindings keep working. Implemented by wrapping codemirror-vim's register controller (created once, so the patch is stable). Default off. - Highlight on yank (like nvim's vim.highlight.on_yank): the yanked range briefly flashes. The range is read from the editor selection, which codemirror-vim sets to the operator range while the yank runs. * Let Space reach Vim while a command awaits an argument (#147) VimNav's capture-phase leader handling armed on Space even while codemirror-vim was mid-command in the focused editor. After f/t/F/T/r (or an operator/count) the next key is the command's argument, so r<Space> (replace with space) and f<Space> (find next space) instead opened the which-key leader UI. Add isVimAwaitingArgument() -- true when codemirror-vim has a partial command buffered (inputState.keyBuffer) or expects a literal next char (expectLiteralNext) -- and skip arming the leader when the editor is focused and awaiting an argument, so Space falls through to codemirror-vim. The leader still works at rest. * Ship a complete macOS app icon (.icns) (#165) electron-builder auto-generated the macOS icon from build/icon.png, producing an .icns that omitted the standard 32x32 (@1x) and 1024x1024 representations. Add a canonical build/icon.icns (all 10 Apple iconset sizes, built from the 1024 source) and point mac.icon at it, so future builds carry a complete icon for every consumer. The reported Raycast case is most likely a stale Raycast icon cache -- the bundle icon is correct and renders in Spotlight/Finder -- but a complete .icns rules out a strict icon reader. * docs: refresh in-app help for the Tasks/Tags keyboard changes Reflect the keyboard updates shipped this release: hint mode is <leader>h (was shown as f), the Tasks view toggles with x (Space is the leader), Esc clears the filter rather than closing the tab, and the leader works in the Tasks/Tags views. Adds the <Space> h entry to the Vim command list. * Show editor ex-command errors inline instead of a blocking alert (#173) Running an ex command with a bad argument (e.g. :mv trash) reported the error with window.alert. In an Electron renderer that native dialog blurs CodeMirror, and after dismissing it the editor never regained focus -- in Vim mode the user could no longer type. Route the :mv/:move, :e/:edit, and :new error paths through a non-blocking, in-editor notification (cm.openNotification) -- the same red bottom-of-editor message codemirror-vim uses for its own errors -- so the editor keeps focus. Falls back to alert + refocus when the editor notification is unavailable. * Sort sidebar folders/notes/assets by name numerically (#168) Sidebar folders rendered in on-disk/insert order and were never name-sorted, so folders named with numeric/char prefixes only fell into place after a restart (a fresh load), and a folder created mid-session appeared last. Note and asset name sorting also used a plain locale compare, so '10 Foo' sorted above '2 Foo'. Add a shared naturalCompare() (numeric-aware, case-insensitive) and use it for folder children (sorted at render time, so new folders land in place immediately) and for the note/asset Name sort modes in the sidebar tree and note list. * Skip unreadable files/dirs in the server vault scan (#159) The server's ListNotes and ListFolders walked the vault with filepath.WalkDir and returned the error for anything except os.ErrNotExist, so the first permission-denied entry aborted the whole scan -- a vault copied in with root-owned files while the container runs as a non-root user showed up empty, and fixing some files only revealed those. ListAssets already tolerated permission errors; notes and folders did not. Add a shared isSkippableWalkErr helper (skips ErrNotExist and ErrPermission) and use it in all three list functions, so unreadable entries are skipped and the rest of the vault still loads. * Fix zen CLI opening two windows on cold start (#178) * Add WYSIWYG live-preview editor (tables, blocks, vim tables) Render markdown in Edit mode (live preview), ported from the WYSIWYG work in PR #185 (author: songgnqing) — excluding that PR's breaking database restructure: - Tables as an editable grid (cm-table) + action menu (cm-table-menu) - Blockquote bars, list bullets, horizontal rules (cm-wysiwyg-blocks) - Hashtag chips (cm-hashtags), wikilink rendering (cm-wikilink-render) - Fenced-code cards + language flair (cm-code-block-flair, cm-code-block-font) - Strikethrough across all editor highlight styles (#187) - Task lines: marker hidden when rendered, raw source on the active line (Obsidian-style) Vim-navigable tables: a block cursor over the current character with h/l/w/b/e/0/$ motions, i/a/I/A editing, j/k row moves, and an `m` action menu. Wired via wysiwygExtensions() in the live-preview compartment, gated by the livePreview setting. 400 app-core tests green. * Render Obsidian-style callouts in Edit mode Detect `> [!type] title` blockquotes and render them as colored cards matching the Preview palette (note/info=blue, tip/hint/success=green, warning/warn=yellow, danger/error=red, quote=grey). The typed title shows bold (custom title, or the capitalized type name when none); the `[!type]` token is hidden off the line and revealed when the cursor is on the header for editing. Also fixes nested lists inside callouts/blockquotes breaking out of the box (re-add the box padding on top of the list hanging-indent) and de-italicizes callout body text to match plain blockquotes. * Add vim editing to WYSIWYG table cells In a table cell (Vim mode), beyond h/l/w/b/e/0/$ navigation: - Editing operators: x, D, C, and operator-pending dd/cc, dw/cw, d$/c$, d0, db. - Char-wise visual mode (v) with motions + text objects, then d/c/y; rendered as a themed overlay (not a native selection, which CodeMirror would mirror across cells and show in the OS color). - Text objects: viw/vaw, vi"/va", vi(/va) (and operators diw/ciw/ca", …), via a pure, unit-tested textObjectRange (word/quote/bracket). - Undo/redo: u and Ctrl-r route to the editor history (committing the pending cell edit first). - Esc in normal mode is a no-op (was jumping out below the table); leave via j/k at the edges. - Cells disable native selection in view mode (re-enabled while editing), so the only highlight is the themed visual overlay. * Close the window on Cmd+W when no tab is open (#192) `global.closeActiveTab` (Cmd+W / Ctrl+W) called closeActiveNote(), which returns early when there's no active note — so on an empty window (all tabs closed) nothing happened and the window never closed. In vim mode Ctrl+W was also always reserved for the `<C-w>` pane prefix, swallowing the empty-window case on Linux. Now: when no tab is open, close the window via window.zen.windowClose(); and only reserve Ctrl+W for vim when a tab is actually open (an empty window has no pane to navigate), so Cmd+W / Ctrl+W closes the window on macOS and Linux, vim on or off. * Keep the reference-link definition colon visible in Edit mode (#188) The `:` in a reference-style definition (`[label]: url`) parses as a LinkMark, which the live-preview plugin hides along with inline-link brackets — so the colon disappeared in Edit mode, leaving a broken-looking `[label] url`. Skip hiding LinkMarks whose parent is LinkReference; inline-link brackets (parent Link/Image) still hide as before. Preview was already correct (the definition is consumed and the reference link resolves). * Show images/PDFs in a capital-cased Inbox folder (#186) On a case-insensitive filesystem (macOS/Windows), an inbox folder stored as `Inbox/` (or any system folder with non-canonical casing) made the file browser hide its images and PDFs while still showing its notes. The vault walk's `realpath` (symlink-loop detection) resolves to the on-disk case, so both note and asset paths come back as `Inbox/...`. Notes still appeared because the walk assigns `folder: 'inbox'` authoritatively, but assets carry no folder field and the renderer's `folderForVaultRelativePath` matched system folders case-sensitively, dropping the capital path to `null` -> hidden. Make the renderer classify and strip system-folder prefixes case-insensitively (`folderForVaultRelativePath`, `assetPathWithinFolder`, `notePathWithinFolder`), so a capital `Inbox/` behaves exactly like `inbox/` and notes + their sibling assets group into the same subpath. The real on-disk path is preserved for file ops, keeping this correct on case-sensitive Linux. Adds renderer unit coverage plus an end-to-end test wiring the real main-process listAssets/listNotes to the real renderer classifier. * Stop logging an error when an open database is deleted Deleting a database (or any client removing the .csv) left the renderer trying to re-read the gone file: the watcher's delete event called syncDatabaseFromDisk, and nothing ever removed the database from state or closed its tab, so DatabaseView kept re-requesting it. Each attempt threw "Database not found" from the open-database IPC handler, which Electron logs as "Error occurred in handler for 'vault:open-database'". - The open-database handler now returns null for a missing database (a stale tab outliving its file is not exceptional); real parse/ permission errors still throw. - loadDatabase / syncDatabaseFromDisk treat null as "gone" and forget the database, closing its tab (both the database-tab and .csv asset-tab forms) instead of looping on a missing file. - The watcher's delete event proactively forgets the database, and openDatabase won't open a tab for one that no longer exists. Covers live deletes, a stale tab restored on startup, and external deletes. Adds store tests for all three. * Don't trigger actions on the Enter that confirms an IME conversion (#183) When typing with an IME (e.g. Japanese), pressing Enter to confirm a conversion also fired our own keydown handlers — committing a rename, selecting a search result, blurring a field, etc. — because the handlers never checked whether a composition was in progress. Add a shared `isImeComposing(e)` helper (standard `isComposing` flag plus the legacy `keyCode === 229` fallback, for both React synthetic and raw DOM events) and guard every text-entry "Enter confirms" handler: the rename prompt and breadcrumb rename, all search/command palettes, database inline renames (view/field/cell/group/tag), settings fields and the combobox picker, the remote-profile and directory-picker modals, quick-capture result pickers, and the tasks filter and Kanban column rename. While composing, these now let the IME own Enter/Arrows/Tab; a fresh Enter still confirms once composition ends. Pure-navigation handlers and modifier combos (e.g. Cmd+Enter) are left as-is, and the onboarding handler already bailed on editable targets. * Don't let the file watcher hang the server where inotify is restricted (#179) In an unprivileged LXC container, inotify on a bind-mounted vault can wedge the process (unkillable, host volume locked) rather than returning an error, and errors from fsw.Add() were silently discarded so a restricted environment failed invisibly. - Add ZENNOTES_DISABLE_WATCHER=1 → a no-op watcher (no fsnotify handle, no tree walk, no inotify syscalls). The vault is still fully served; only live auto-refresh stops. This is the reliable escape hatch on restricted hosts because it never makes the syscalls that hang. - New watcher.StartOrDisabled is used at startup and on vault switch: if the watcher can't be created it logs a warning and falls back to the no-op watcher instead of log.Fatal / failing the switch. - Surface fsw.Add failures: the initial walk counts and logs them once (with a pointer to ZENNOTES_DISABLE_WATCHER), and watching a newly created directory logs on error. Document the variable and a "container hangs and won't stop" troubleshooting entry in the Docker how-to. Adds tests for the no-op watcher. * Make a database a self-contained .base folder A database is now one folder, `<Name>.base/`, holding `data.csv`, `schema.json` (the sidecar), and its record-page notes as `.md` files. The folder is the database: rename/move/trash are plain folder operations and record-page paths in the schema are stored relative to it, so nothing needs path rewriting. The sidebar renders a `.base` folder as a database — database icon, title without the suffix, click opens the grid, expand reveals its record pages — and the folder menu renames/deletes it as a database. The database header title is editable (double-click) and renames the folder, rehoming the open grid tab. Walks treat a `.base` folder as one unit: the desktop lists it as a database and its record pages as notes, but hides data.csv/schema.json from notes, folders, and assets; the MCP server hides the folder; the self-hosted Go server hides it entirely (databases are desktop-only). A legacy loose `<Name>.csv` plus co-located `.csv.base.json` (with record notes under `<Name>/`) is migrated to the new layout on vault open — idempotent, and only for managed databases (a plain CSV is left alone). Also fixes a couple of grid rough edges: a new/blank row no longer collapses shorter than filled rows, and the rename input is sized to the title. Adds shared-path, main-process (folder ops + migration + walk), and Go-server tests. * Add back/forward nav and restyle the tab bar (from #185) Back/forward history navigation: ◀ ▶ buttons in the tab-bar header (wired to the existing jump history, dimmed when there's nothing to go to) plus `global.historyBack`/`historyForward` shortcuts bound to Alt+←/Alt+→ on every platform (VS Code-style); vim's Ctrl+O/Ctrl+I history nav is unchanged. Restyle the workspace tabs from rounded "browser pills" to flat, full-height VS Code-style segments: right-border separators, no rounded tops, the active tab filled with an accent ring, inactive tabs muted with hover. The close button stays on the active tab and reveals on hover for the rest, and the active tab scrolls into view when switching among many tabs. * Unify assets under assets/ + add an Assets view (from #185) Assets are no longer mixed with notes: they live in a top-level `assets/` folder. A `migrateLooseAssets` step runs on vault open and moves loose root-level attachments plus the legacy `attachements/`/`_assets/` dirs into `assets/`, skipping any basename collision so existing `![[…]]`/`` embeds keep resolving (the resolver falls back to a unique basename). Notes and `.base` databases are never touched. New imports — including pasted images — now land in `assets/`. A dedicated Assets view (the `zen://assets` tab + a sidebar entry) browses every asset in one place, with filter, open, and delete. The desktop main, MCP server, and self-hosted Go server all recognize `assets/` (reserved root); the Go server lists from it. Asset migration runs on the desktop. Adds migration tests and updates the paste-image tests for the new target. * Assets view: usage + actions + table layout, and nav polish Assets view: - Show "used in N" / "unused" per asset, computed from a new per-note assetEmbeds field (captures ![[asset]] and  embeds, which note metadata previously dropped) resolved via the same relative-path + unique-basename logic embeds render with. The note-meta cache version is bumped so it rebuilds with the new field on first open. - Right-click context menu: Open, Copy embed, Reveal in file manager, Rename…, Move to Trash. - Reorganized the list into an aligned table (header + shared grid columns, right-aligned numeric fields, whole-row click, hover-reveal delete). - Fix a restore crash: the zen://assets tab is now treated as a virtual tab, so it's never loaded from disk as a note (ENOENT on relaunch). Nav polish: - Move the sidebar toggle up next to back/forward (shown when the sidebar is hidden); drop the redundant toggles from the breadcrumb, database, and assets headers. - Anchor the leftmost toolbar tooltips to the button's left edge so "Show sidebar"/"Go back" no longer clip at the window edge. * Fix Vim/normal-mode key interaction in list views The Trash/Archive/Tasks/Tags/Quick Notes views register global capture-phase keydown handlers that fired single-key shortcuts straight through open confirm dialogs and context menus — Enter bypassed the delete-confirm and repeated Enter kept deleting — and ignored the Vim-mode setting entirely. - Add isAppOverlayOpen() so the views stand down while a prompt/confirm modal or context menu owns the keyboard. - Gate the single-key Vim shortcuts (j/k/x/d/gg/G/o/r/Space/1-3) behind vimMode; only arrows/Enter/Escape navigate when Vim is off. Reported by songgenqing. * Add markdown snippet auto-close in the editor Type an inline delimiter (`**` `__` `~~` ``` `==` `[[` `%%`) then Space to wrap the cursor, or a fence (``````, `~~~`, `$$`) then Enter to expand a block with the cursor inside. Skips already-closed pairs, closing fences, and escaped delimiters. Gated by a new 'Markdown snippets' setting (Settings → Writing, default on) and only active while typing — Vim off or Vim insert mode, never normal/visual mode. Wired into every editor surface via one shared helper. Ported from community PR #185 (songgenqing). * Add Flatpak packaging (#145) * Add GitHub Sponsors username to FUNDING.yml * docs: add GitHub Sponsors link to README * Add Flatpak packaging Adds packaging/flatpak/ mirroring the existing AUR packaging. The manifest downloads the official AppImage from the GitHub release and extracts it at build time (--appimage-extract, no FUSE required), then installs the unpacked Electron app against the Flatpak Electron base app and launches it through zypak. Flatpak ships a self-contained runtime, so it avoids the host FUSE / system-library issues that keep the AppImage from starting on Fedora Atomic, minimal installs and some Arch variants (same class of problem as #65). Includes the flatpak-builder manifest, zypak launcher, desktop entry, AppStream metainfo and a README documenting build and release-bump steps. --------- Co-authored-by: Adib Hanna <adibhanna@gmail.com> * feat: add tar.gz Linux target and simplify AUR PKGBUILD (#133) Add a tar.gz electron-builder Linux target that bundles the desktop file, icons, and LICENSE (via extraResources/extraFiles), and rewrite the AUR PKGBUILD to repackage that tarball instead of extracting the AppImage — dropping the chmod workarounds behind #70/#74/#92 (tar.gz preserves permissions). Fixes #103. Tarball layout verified end-to-end against an electron-builder build. * packaging(aur): SKIP sha256 until the v2.4.0 tarball is published The rewritten PKGBUILD/.SRCINFO still carried the old v2.x AppImage sha256, which matches nothing now that the source is the tar.gz. The v2.4.0 tarball doesn't exist until release time, so there's no artifact to hash — use SKIP as a placeholder. `updpkgsums` pins the real checksum against the uploaded release asset before publishing to AUR. * ci(aur): skip makepkg build until the release asset is published The AUR check runs makepkg -s, which downloads the source tarball. The PKGBUILD points at the upcoming release's tarball (v2.4.0), which 404s until that release is cut — so the check failed for a non-bug reason. Probe the source URL first: when the asset isn't published yet, skip the build/lint with a notice (still validating PKGBUILD metadata + .SRCINFO sync); the full build runs once the release exists. * feat: [[Note#heading]] wikilinks — resolve, jump to heading, autocomplete (#196) `[[Note#heading]]` was matched as a note literally named "Note#heading", so it never resolved — the connections tab even offered to create it. The whole heading-link experience now works: - resolveWikilinkTarget (and suggestCreateNotePath) strip the #heading / ^block anchor, so [[Note#heading]] resolves to Note everywhere (editor click, connections, backlinks) and 'create from link' makes a valid Note.md. - Clicking, and `gd`, on [[Note#heading]] now scroll to the heading — shared openWikilinkHeading() → openNoteAtOffset — in both the editor and the preview. - Typing [[Note# autocompletes that note's headings ([[# uses the current note). - Ctrl+Y accepts the highlighted completion (Vim-style); Tab accepts a note link but keeps the caret inside [[…]] so you can chain into a #heading. Tests: wikilinks.test.ts (resolution / anchor / create-path, incl. the /Untitled#test edge case) and cm-wikilinks.test.ts (heading autocomplete). * feat(ui): add middle-click to close tabs * packaging(nix): add Nix/NixOS flake and packages (#149) Adds a root flake plus packaging/nix/ with separate desktop and server packages that build ZenNotes from source, a dev shell, and install docs. Linux packaging only — no application-code changes. Co-authored-by: Krysteq <justkrysteq@proton.me> * fix: set quick capture window title * fix: Ctrl+D and other emacs chords work in Vim mode on macOS CodeMirror's defaultKeymap binds the emacs-style control chords (Ctrl-d/a/e/f/b/v/h/k/o/t/p/n) as macOS-only bindings, and its key handler outranks the Vim plugin's — so on macOS those chords ran the emacs action (Ctrl-d deleted a character) instead of the Vim motion (<C-d> half-page down). Ctrl-u, which has no emacs binding, already worked, hence the up/down asymmetry. Linux/Windows were unaffected. Add vimAwareDefaultKeymap(): in Vim mode the emacs chords are stripped so Vim handles them natively; with Vim off they're kept (standard macOS editing keys). Applied to every Vim-capable editor surface — the main editor (dynamic via a compartment, so toggling Vim mode updates it live), quick capture, floating/external/template editors, and the pinned reference pane. * feat: render comment bodies as Markdown Comment bodies in the comments panel rendered as raw text, so Markdown a user typed (## headings, lists, **bold**, `code`) showed as literal source. They now render as sanitized Markdown via the same renderMarkdown pipeline the preview uses, with a compact `comment-prose` variant that scales headings and block spacing down to fit the narrow comment card. The input stays a plain textarea (you type Markdown source). * feat: focus the title field when creating a note from the sidebar Creating a note from the sidebar (the note-list "+" button, the folder context menu, and the empty-area menu) dropped the cursor straight into the body with the name left as "Untitled", so it had to be renamed afterward. These paths now pass focusTitle: true — matching the command palette's "New Note" — so focus lands on the title input with "Untitled" selected; Enter then moves to the body. Daily/weekly notes, wikilink-create, and trash-restore are intentionally unchanged. * fix: warn when inbox-mode vault hides root-level notes (#195) An Obsidian-style vault whose notes live at the root (and in custom folders) showed up empty when ZenNotes opened it in "inbox" mode — the scan only walks inbox/quick/archive/trash, so top-level content was silently hidden. This commonly happened when a flaky first directory read (iCloud-backed, symlinked folders) fell back to the inbox default. Add a sidebar banner that detects the mismatch — vault in inbox mode but its root holds markdown/non-system folders that only root mode surfaces (rootContentHiddenByInboxMode in the main process, exposed over a new IPC) — and offers a one-click "Switch to Vault root". Replaces the silent empty vault with a clear, actionable message. The banner lives in the sidebar (the unified layout's note tree), not the retired NoteList column. Refreshed on vault open and after settings changes. Covered by vault.test.ts cases. * feat: per-folder accent colors Folders can now be given a preset accent color (10-color palette) from the sidebar context menu — "Change color…" / "Reset color" — alongside the existing folder icons. The color tints the folder's icon and name and stays readable in every state: a colored row that's selected uses a faint accent tint + ring instead of the saturated fill, so same-hue text doesn't vanish. Stored as `folderColors: Record<"folder:subpath", FolderColorId>` on VaultSettings (persisted to .zennotes/vault.json via the existing setVaultSettings — no new IPC), keyed exactly like folderIcons and rewritten in lock-step on folder rename/delete/duplicate (main process + store). * feat: copy the whole note as Markdown Add a "Copy Note as Markdown" command (command palette) plus a Vim leader binding (Space l y, under the note-actions group) that copies the active note's full Markdown source — including unsaved edits — to the clipboard. Wired through a copyActiveNoteAsMarkdown store action, registered as the remappable vim.leaderCopyMarkdown keymap, surfaced in the leader-l which-key hint, and documented in the in-app help. * feat: Excalidraw drawings as a first-class file type Adds `.excalidraw` drawings alongside notes and databases: - Listed in the sidebar (not as assets), with the real Excalidraw icon; they open in an embedded, lazy-loaded @excalidraw/excalidraw editor that loads/saves the scene JSON and follows the app's light/dark theme. - Create from the sidebar ("New drawing" in the folder and empty-area menus) and from the breadcrumb. Rename and move preserve the `.excalidraw` extension instead of turning a drawing into a .md note. - The drawing header hides Markdown-only controls (edit/split/preview, connections, comments, outline, PDF export), keeping archive + trash. - New vault:create-excalidraw IPC (desktop); the web build degrades until the Go server learns to list/create drawings. Also adds a breadcrumb create menu: right-click — or press `m` while a crumb is focused (e.g. via hint mode) — any folder crumb to create a new note / drawing / folder in that folder. * feat(server): Excalidraw drawings on the self-hosted/web build Brings the desktop's `.excalidraw` support to the Go server so drawings work on the self-hosted and web client: - ListNotes includes `.excalidraw` files (shown in the sidebar); ListAssets excludes them so they aren't treated as attachments. - readMeta/ReadNote skip Markdown parsing for drawings — a scene's hex color (e.g. #1971c2) no longer registers as a #tag, and the raw JSON never leaks into the excerpt. - Rename, move, duplicate, and move-between-folders preserve the `.excalidraw` extension instead of turning a drawing into a .md note. - New CreateExcalidraw method + POST /excalidraw/create endpoint, backing the web bridge's existing createExcalidraw call (which previously 404'd). - Root-mode detection recognizes a vault of only drawings; full-text search stays .md-only (drawings remain findable by title). Tests: vault listing/create/rename-move parity + an end-to-end HTTP test (login → POST /api/excalidraw/create → appears in /api/notes). * feat: Favorites + Notion-style sidebar reorganization Favorites — pin notes and folders to the top of the sidebar: - Right-click a note or subfolder → Add/Remove from Favorites, or press the Vim leader binding `<leader> l s` on the active note (which-key + remappable). - A FAVORITES section lists them with their real icons; click or Vim-Enter to open. Stored per-vault in VaultSettings.favorites (note path or folder key). - Favorites survive note rename/move and folder rename, and are pruned on folder delete (rewrite logic lives in the store; the Go server carries favorites through folder ops so the web client persists them too). Sidebar reorganized into clear labeled sections (Notion-style): - FAVORITES (top), QUICK ACCESS (Tasks / Quick Notes / Daily+Weekly), NOTES (the tree), TAGS (flows after the notes), and SYSTEM (Archive / Trash / Assets) pinned to the bottom. - The header `+` now opens a create menu for every supported type (note, drawing, template, database, folder) instead of only a note. - Fix: Vim j/k now stops on Daily/Weekly note rows (they were missing navigation indices); Assets is Vim-activatable; removed stray top margin that made the gap above Daily Notes uneven; dropped the Tags chevron. * feat(sync): foundation for bidirectional synced vaults Lays the groundwork for a third workspace mode, `synced` — a local vault that bidirectionally syncs with a server hub — while leaving `local` and the thin-client `remote` modes untouched. No user-facing behavior yet. Server (M2): - GET /api/sync/manifest returns {path, hash, size, mtime} per note/.excalidraw for content-addressed diffing. sha256 hashes are cached in the existing note-meta-cache (keyed by mtime+size, version bumped 1->2), so an unchanged vault answers from stat() calls alone. supportsSyncManifest capability added. - Refactored the notes walk into a shared collectNoteFiles(). Tests for hash stability/change, database+internal exclusion, and the HTTP endpoint (auth). Desktop plumbing (M1): - WorkspaceMode / WindowWorkspaceMode gain 'synced'; PersistedConfig gains syncedVaults[] (root + server profile + conflict policy + asset mode) with a normalizer. WindowVaultRegistry.setSyncedVault treats synced as disk-backed (shares the chokidar watcher) with an onLocalChange fan-out hook for the engine. store.workspaceModeFrom handles synced. Reconcile core (M3, pure): - Three-way diff (local/remote/base content hashes): push, pull, bidirectional delete-propagation, keep-both conflicts, first-sync union merge, hash-only (clock-skew-safe) decisions. 16 unit tests cover every decision-table row. * feat(sync): the bidirectional sync engine (M3 I/O) Builds the background replicator on top of the pure reconcile core: - sync-state.ts — per-device base snapshot at .zennotes/sync-state.json (atomic writes, intent log for crash recovery). - scan-local.ts — walks/hashes local note/.excalidraw files, mirroring the server's collectNoteFiles (inbox/root + quick/archive/trash, excluding databases, hidden, .zennotes). - conflict.ts — keep-both conflict-copy naming ("Note (conflict <dev> <ts>).md"). - engine.ts — debounced, single-flight reconcile loop triggered by the local watcher fan-out, the server /api/watch WS, and a periodic backstop; executes push/pull/delete/converge/conflict via the existing RemoteServerClient with atomic, path-safe local writes. Offline/error/conflict status surface. - RemoteServerClient.getSyncManifest(); SyncManifestEntry + SyncStatus types. Verified: 22 unit tests (reconcile decision table + an end-to-end engine test against an in-memory server over real local files: first-sync union merge, pull, push, delete-propagation, keep-both conflict with no data loss, idempotency). Not yet wired into app startup (next: activation + status UI). * feat(sync): app integration — activation, IPC, and status UI (M3+M4) Wires the sync engine into the running app: - index.ts activation: a synced vault opens disk-backed (all file ops stay local — isRemoteWorkspaceActive stays false) plus a background SyncEngine, started from loadCurrentVaultFromConfig and torn down when switching to local/remote. setSyncedVaultForWindow mirrors setVaultForWindow; the local watcher fan-out (onLocalChange) drives the engine. - New IPC: sync:get-status / now / on-status / attach-server / detach-server / open-vault, exposed through preload + the ZenBridge interface; the web bridge gets inert stubs (the browser is itself the thin client). - Store: syncStatus slice, onSyncStatus subscription + initial fetch, and syncNow / attachSyncServer / detachSyncServer actions. - UI: a cloud sync-status button in the sidebar header (synced / syncing / offline / error + a conflict badge; click to sync now) and vault-menu items to "Back up & sync to <server>" (attach a local vault) or "Stop syncing". Typechecks clean across all packages; 36 desktop/sync tests + app-core tests green; web builds. Runtime not yet exercised end-to-end (next: assets, then databases/comments/settings, then the conflict-resolution UI). * feat(sync): full-vault coverage — databases, comments, settings, assets (M5+M6) Unifies all syncable content under one manifest instead of notes-only: - Server: Manifest() now walks collectSyncFiles() — the whole vault tree (notes, drawings, databases, assets, comments, vault.json) excluding only the per-device sync-state, the meta cache, dotfiles, unrelated dot-dirs, and temp files. New POST /api/sync/write (multipart) writes raw bytes to an exact path for binary files (Vault.WriteSyncFile, atomic + size-capped). - Client: scanLocalVault() walks the whole tree to match (also removing the earlier inbox/root-mode parity concern). The engine classifies each path as text (.md/.excalidraw/.json/.csv/.txt) — transferred via writeNote/readNote — or binary, transferred byte-for-byte via writeSyncFile/readSyncFile. Conflict copies handle both. Databases (CSV/JSON), comments, and settings are text, so they sync through the existing endpoints with no special-casing. Tests: 25 sync unit tests (added binary push/pull byte-equality, databases/ comments/settings sync, and a never-sync guard for sync-state/meta-cache); the server manifest test now asserts syncables-in / internal-out. All green; typechecks + web build clean. Note: assets sync as a full mirror for now; the lazy on-demand fetch is a follow-up optimization. * feat(sync): conflict-resolution UI (M7) When the sidebar sync indicator shows kept-both conflicts, clicking it opens a resolver listing each conflict copy with its original. Per conflict the user can open either version, adopt the copy's content into the original ("Use the copy's version"), or discard the copy — all via normal vault ops that then sync. The engine gains dismissConflict() + a sync:dismiss-conflict IPC to clear a resolved entry from the status list; the store adds resolveSyncConflict(keepMine|keepTheirs) and an originalFromConflictPath() helper. Completes the synced-vault feature end to end: notes, drawings, databases, comments, settings, and assets sync bidirectionally with keep-both conflicts, a live status indicator, attach/detach, and conflict resolution. Typechecks + tests + web build all green. Ready for live testing. * Revert the synced-vaults work (M1–M7) — will revisit Reverts the five feat(sync) commits (731abb7, 757098a, aa22df5, 4fd805b, a38ce62): the server sync manifest + binary write endpoint, the desktop sync engine, app activation/IPC/status UI, full-vault coverage, and the conflict UI. The bidirectional-sync direction needs more design/validation before it ships, so it's parked. The work remains in history and can be restored later by reverting this commit (or cherry-picking those SHAs). * feat(web): databases on the web client (desktop/web parity) Databases now work in the web client, reusing the exact same shared TS logic the desktop uses so the on-disk format is identical across platforms. - Server (apps/server/internal/vault/vault.go): ListFolders now lists `.base` database folders (then stops descending), mirroring the desktop — that one divergence was why databases never reached the web sidebar. ListNotes and ListAssets still hide the internals. Test updated. - Web bridge (apps/web/src/bridge/http-bridge.ts): implemented the 8 database methods (open/writeRows/writeSchema/create/createRecordPage/rename/list) over HTTP, reusing @shared/database-csv + @shared/databases (parseCsv, parseRows, serializeRows, inferFields, buildDefaultViews, path helpers). Reads/writes use the existing generic /notes/read|write endpoints; `.base` folders are created/ renamed via the folder endpoints — no new server endpoints, no Go re-implementation, so CSV/schema parity is guaranteed. Drawings already worked end-to-end on web (no change). Verified: web typecheck + build, app-core tests (shared CSV unchanged), Go vault/httpserver tests. * feat: daily-note task calendar, vim calendar control, and Notion-style sidebar Tasks calendar - Daily-note tasks get an implicit due date (the note's date) so they surface on the right day without writing due: tokens; gated by the new "Tasks are due on the note's date" setting. - Add, move, reschedule, and delete tasks directly from the calendar, creating the daily note on demand when needed. - Drag a task to another day (pointer-based) with a move-vs-set-due choice. - Optional "Roll over unfinished tasks to today" (Obsidian-style). - Week agenda under the month grid in the sidebar calendar panel. Vim keyboard - Full keyboard control of the big Tasks calendar and the sidebar calendar panel: h/j/k/l day nav, Tab to pick a task, x/e/dd/m/</>/T/a actions, gg/G, [/] month, gt today. Sidebar - Notion-style restyle (soft active highlight, roomier rows, muted icons). - Per-folder/note/database icon + color customization with inline reset. - Folder hover-chevron and aligned rows. Editor - "Task" slash command; calendar hidden on quick notes. Settings + docs - New daily-notes task settings (server round-trips them as *bool). - Help docs for the tasks calendar and daily-note settings. * fix: don't reorganize loose files in an existing Obsidian vault (#202) Pointing ZenNotes at an existing Obsidian vault silently relocated every loose root-level non-md file — images, PDFs, and Obsidian `.canvas` files — into `assets/` on first open (the loose-asset migration), which a user reported as "a number of different files were changed that I made no edits to." A vault with an `.obsidian/` directory is managed by another app, so its loose files are the user's own, not ZenNotes' legacy attachments. Skip the root-level relocation step in that case; the legacy ZenNotes attachment dirs (which never exist in such a vault) stay handled, so the former-ZenNotes upgrade path is unchanged. Adds end-to-end integrity tests that wire the real main-process vault I/O and the real renderer store for the reporter's exact shape (root mode, nested folders, spaces in filenames, symlinked `~/sync` root): notes never cross-wire content, pure navigation writes nothing to disk, edits autosave to only the edited file, and external watcher changes land under the right path. * fix: make standard Markdown links clickable (#201) Standard `[text](target)` links didn't navigate the way `[[wikilinks]]` do: internal note links opened as broken asset tabs (the asset enhancer tagged every relative link with a zen-asset URL), and external links — especially a scheme-less domain like `[site](google.com)` — did nothing. Now, across the preview pane, the editor live preview, and `gd`: - `[text](Note.md)` and relative paths (`../x.md`, `#heading`) navigate to the note, resolved relative to the current note. - `[site](https://…)` and bare domains (`google.com`, `www.x.com/path`) open in the browser; a scheme-less domain is promoted to https. - In the editor a plain click follows a rendered link (cursor outside it), mirroring wikilinks; clicking a link the cursor is inside edits it. Cmd/Ctrl- click always follows. Markdown links now show the pointer cursor on hover. - `.md` and external links are no longer mis-tagged as vault assets. New pure module `internal-links.ts` (resolveInternalNoteHref / externalLinkUrl / markdownLinkAt / extractLinkAtCursor) shared by all surfaces, with unit tests and a regression test that note/external links aren't tagged as assets. * feat: live-sync database fields to record-page frontmatter + tidy metadata card The database table is the source of truth for a record's properties, and a record page's frontmatter is a derived "metadata" mirror. Previously the frontmatter was only re-mirrored when the page was opened, so editing a cell or adding a field in the table left an already-open record page stale. Now table changes (a cell value, an added/renamed/removed field) re-mirror the frontmatter of any open record page live, preserving its body; pages that aren't open are still synced lazily on next open. The existing database write-guards keep this from looping through the file watcher. Also redesign the in-editor frontmatter rendering: instead of small grey text it reads as a compact properties card — a subtle rounded container, the `---` fences hidden into quiet rounded edges, and each key shown as a muted label beside its value. * feat: Notion-style selection toolbar with formatting + shortcuts Selecting text in the editor now pops up a floating bubble toolbar: a "Turn into" block menu (Text, Heading 1–3, bulleted/numbered/to-do lists, quote, code), inline formatting (bold, italic, strikethrough, highlight, code, math), a link, and a comment. The footer shows the focused/hovered action's keyboard shortcut on the popup itself. Inline formats also have keyboard shortcuts that work on every platform, in or out of Vim mode — handled in VimNav's window-capture pass so they beat Vim's own Ctrl chords on Linux/Windows, with `Mod` resolving to ⌘ on macOS and Ctrl elsewhere: ⌘B bold, ⌘I italic, ⌘E code, ⌘K link, ⇧⌘S strikethrough, ⇧⌘H highlight, ⇧⌘M math. The toolbar is fully keyboard-navigable: `Mod+/` focuses it, then the arrows — or h/j/k/l in Vim mode — move row-aware (h/l across the format row, j/k between it and "Turn into"); Enter applies, Esc returns to text. `l`/→ on "Turn into" opens its submenu. New pure `cm-format` commands (toggleWrap / wrapLink / setBlockType) with unit tests; new toolbar icons; in-app Help updated. * fix: recognize tags in any script — Cyrillic, CJK, etc. (#205) Hashtags required an ASCII letter to start (`#[a-zA-Z]…`, with `\w` for the rest), so `#тест`, `#ошибка`, `#标签` and other non-Latin tags were never recognized and rendered as plain text. Make the tag character classes Unicode-aware everywhere — first char `\p{L}` (a letter in any script), rest letters/digits/`_`/`-`/`/` — keeping behavior otherwise identical (a heading `# x` still isn't a tag; a hex colour like `#1971c2` still isn't, since the first char must be a letter). Updated in lockstep across the preview, editor pills, inline render, live sidebar extraction, tag rename, the desktop scanner + MCP, shared task tags, and the Go server so desktop and the self-hosted/web client stay byte-for-byte. Adds regression tests: the editor renders Cyrillic/CJK tags as pills, and the Go scanner extracts them. * chore(release): prep v2.4.0 — version bump, green typecheck, packaging - Bump all package versions 2.3.0 → 2.4.0. - Fix the typecheck gate (was red, would fail CI build:prod): - Add RemoteServerClient.createExcalidraw — a real fix: creating a drawing while connected to a self-hosted server called a method that didn't exist. - Add the now-required `assetEmbeds` field to older NoteMeta test fixtures. - release.yml: upload `*.tar.gz` so the AUR source asset is published (was built then dropped by the upload glob). - Flatpak: add the 2.4.0 metainfo release entry and point the AppImage URL at v2.4.0 (sha256 pinned at release once the asset exists). --------- Co-authored-by: Weslley Almeida <weslleyalmeida6@gmail.com> Co-authored-by: Lund <59971256+Lund1337@users.noreply.github.com> Co-authored-by: Victor Ribeiro <victruu@outlook.com> Co-authored-by: Raihan Adam <hallwack.id@gmail.com> Co-authored-by: Krysteq <justkrysteq@proton.me> Co-authored-by: dannrs <danniramdhani@proton.me>
Verified
Tarball produced by
npm run dist:linux(ZenNotes-2.3.0-linux-x64.tar.gz):Confirmed resources/arch-extras exists with desktop file + icons
tar -tzf ZenNotes-2.3.0-linux-x64.tar.gz | grep -i arch-extrasLICENSE present at tarball root
tar -tzf ZenNotes-2.3.0-linux-x64.tar.gz | grep -i licensechrome_crashpad_handler ships with correct execute permissions (-rwxr-xr-x)
tar -tvzf ZenNotes-2.3.0-linux-x64.tar.gz | grep crashpadNote: sha256sums in
.SRCINFO/PKGBUILDstill reference the old AppImage hash.