Skip to content

ZenNotes for Android 1.1.25: search creates the note you typed, and the shared 2.54.1 core

Choose a tag to compare

@adibhanna adibhanna released this 22 Sep 23:00
c01fad3

ZenNotes for Android 1.1.25: search creates the note you typed, and the shared 2.54.1 core

One update in two parts: the shared ZenNotes core moves from 2.53.0 to the published 2.54.1 (the desktop's 2.54.0 and 2.54.1 releases, both from September 22), and the Android shell adds the one CSS rule the new core's form needed. No native code changes; android/app/build.gradle only moves the version to 1.1.25 (28). The headline is the New note form (#826), requested by @vinivinoo: note search used to end in "No matches." for a name no note had, and the trip went through the menu, New, and typing the name again. Now the search itself creates the note. One item in this core came from this app: #820, the footer that kept showing a tapped wikilink's target, was reported from the Android app on core 2.53.0 and listed as a known follow-up in the 1.1.24 notes.

What changes on the phone

  • Note search creates the note you typed (#826). Tap the menu button, then Search, and type a name. Whatever the results, the list now ends with a Create "Meeting notes"… row with its destination on the right (Inbox, or Inbox › projects for a typed path). Tap it and a New note form opens in the same full-screen search, with the query kept behind it so Cancel returns you to the results. Name is prefilled from what you typed. Folder is empty for the Inbox root, or a path such as projects/ideas to nest under it, archive/x or quick/x for those areas; once the field has focus, every folder of the vault is listed under the fields (roots first, never the Trash, never a database folder), typing narrows the list, and a typed path that does not exist yet is created with the note. Tags takes chips: any #tag in the query arrives as one, the vault's tags are listed most used first with a new tag row last for a word that is not one yet, and a tag that cannot be one blocks Create and says why. Before writing, the form checks the name against the live notes: a same-titled note in the chosen folder blocks Create in red ("Weekly review" already exists in Inbox. Change the name, or open it.) and Open it opens that note instead; a same-titled note in another folder only warns, since the folder was chosen on purpose. The check is by title, ignoring case, never counting a trashed note or a partial match. The status line under the fields names the outcome in words (Creates "Q4 plan" in Inbox › projects with #planning #q4). Create writes the note with a heading and one #a #b line under it (the shape zn create --tag writes, not frontmatter) and opens it. The default destination is the Inbox root, matching what a dead wikilink does; the "New notes go to" setting is not consulted, on purpose, the same choice the desktop made.
  • The first tap on Create counts while a tag is still typed (core 2.54.1). In 2.54.0's form, typing a tag and then tapping Create lost the tap: the button took focus on touch-down, that blurred the Tags field, which committed the typed word as a chip and removed the Add #urgent row under the fields, so the footer with Back and Create moved up before the finger came back up. On the phone the footer moved a full row and the tap landed on nothing; a second tap worked. Now the footer buttons (and Open it) do not take focus on touch-down, the same rule the folder and tag rows already followed, so nothing moves until the tap completes, and Create counts the text still typed in Tags the way Enter does. One tap creates the note with the tag under its heading. Found on the iPhone simulator while that shell was tested on 2.54.0, and fixed in the core before either phone shipped, so this app never had the bug.
  • The form's header keeps its label clear of Cancel (this shell). On the phone the search is a full-screen takeover, and the shell places a Cancel button at its top right, the spot the form's header uses for its destination label ("in Inbox"). In the first cut the two overlapped: Cancel was drawn across the faded label. The header now ends before Cancel begins, and the three fields give back the right-hand padding that had been reserved for a Cancel over the search input, so their text no longer stops short of the field's edge.
  • Following a wikilink clears its target from the footer (#820). Reported from this app on core 2.53.0: in Reading mode, tap [[Alpha plan#Milestones]] and the target opened at the heading, but the footer's left slot on the new note kept reading Alpha plan#Milestones until the next tap on something that was not a link. A tap reaches the page as a mouse sequence with no mouseleave, and the only clears were the article's mouseleave and the preview's unmount, neither of which a tap fires. Following a link now clears the slot, and so does a change of the open note. With a mouse (a tablet with a trackpad, or a desktop), a pointer resting on a link still shows its target.
  • Leaving Reading mode opens the editor where you were reading (#822). Read on through a long note, scroll to a later section, switch to Edit from the menu, and the editor came back at the line the cursor had when you left (the top of the note, for one just opened). Every route out of Reading mode now reads what the reading view has on screen and opens the editor with that block at the top and the cursor on its first line; if the cursor's line is already on screen nothing moves, so a peek at the render and back leaves the cursor alone. The desktop's double-click on a paragraph to edit it is in the same core change; whether the Android WebView turns a double tap into that event was not checked in this cycle, so it is not claimed here.
  • The YAML header no longer renders in heading type with Live Preview off (#827). A note that opens with ---, three key: value lines and --- showed the property lines big and bold in Edit mode with Live Preview off, because CommonMark reads a paragraph followed by a --- line as a setext heading. The editor now carves the frontmatter block out before markdown sees it, so no heading, list or rule token can come out of it in any mode. With Live Preview on the properties card looks as before.
  • A note whose text returns to what is on disk is not written again (#828). Type a character and delete it, or undo back to the saved text, and the note used to be saved anyway: identical bytes, a new modified time, {{modified_time}} tokens moving, and a sync change to carry. The store now remembers what is on disk for a note with unsaved changes and treats an edit that brings the text back to those bytes as no change: the note reads clean, the pending save is cancelled, nothing is written. Real typing still saves once. On the desktop this showed as a custom Vim escape (jk) rewriting the note; the phone shares the store, so it gets the same rule, and on a folder vault it is one fewer write through the Storage Access Framework.
  • The built-in Help note is updated for 2.54.

Desktop only, on purpose: Harper's dictionary fix (#829, the phones never run Harper), the Vim half-page motions in visual mode (#825, a hardware-keyboard Vim change with no phone surface) and zn mcp's flags (#831, the desktop CLI). On the phone the Create row is the way into the form; the desktop's Shift+Enter shortcut needs a hardware keyboard and was not tried on a tablet in this cycle. Known follow-up: the form's footer shows the desktop's key hints (↑↓ pick, ↵ create, esc back) on a phone with no hardware keyboard, tracked as ZenNotes/zennotes#842.

Under the hood

The shell change is thirteen lines of src/ui-mobile/mobile.css: two rules scoped to .zn-mobile.zn-phone .z-palette [data-search-create-form], the marker the core's form carries. The first gives the header row padding-right: 4.25rem so the destination label ends before the shell's injected Cancel begins; the second sets the three fields' padding-right back to 0.625rem, undoing the padding the phone stylesheet reserves for a Cancel drawn over the search input, which the form does not have. The two rules are the same text as the iPhone shell's, where an XCUITest measures the header label's frame against Cancel's on the simulator. Nothing else in the shell had to move: the menu button, the search's full-screen takeover, the Cancel injection, MobileShell.tsx, MobileDrawer.tsx and the folder-vault plugin are as they were in 1.1.24. src/bridge/mobile-bridge.ts changes only its APP_VERSION string; core 2.54.1 adds no bridge-contract methods, so nothing in the bridge had to be implemented.

The adoption was done with the script that landed on main after 1.1.24 (npm run core:adopt -- core-2.54.1-core.h75d82a521571dc20 --source 0285443b51889a6034dc24b5b603c0dbab099d4f, tooling/adopt-core.mjs): it downloaded the core release from ZenNotes/zennotes, verified every archive's SHA-256 and SHA-512 against its .tgz.json provenance, the packed package versions, and that all three archives record the same clean source commit, then swapped them into vendor/zennotes/, rewrote manifest.json, the three file: dependencies in package.json and the README's version sentence, ran npm install and the boundary check. The same script was brought over to the iPhone shell in this cycle, so both phones now adopt a core the same way.

Core packages: core-2.54.1-core.h75d82a521571dc20, built from desktop commit 0285443b (tag v2.54.1, clean tree), replacing 2.53.0: @zennotes/app-core 2.54.1-core.h75d82a521571dc20, and @zennotes/bridge-contract and @zennotes/shared-domain at 2.54.1-boundaries.h1138e41f8e2ad650. Each archive's SHA-256 and SHA-512 matched its provenance file and every recorded source commit matched before the archives went into vendor/zennotes/; npm run boundaries:check passes without an override. No permissions, no new account data flows, no native SDK changes; minSdkVersion and targetSdkVersion are unchanged. On-device and folder vaults still work without an account.

Installing the APK

zennotes-1.1.25-universal.apk is the Google Play-signed universal APK for version 1.1.25 (28), Android 7.0 and later, same certificate as before (signer SHA-256 9f1af3a4b6b11156a6b1f49e1a54bcaecef480f18eab1692a3bd8f416af6ae50, identical to the 1.1.23 and 1.1.24 assets), attached exactly as downloaded from Play Console's App bundle explorer (31,772,464 bytes, SHA-256 d1b65922e6250eccdadd3a015f5930ce1421cba9cce78614e64175e1734d7efc). It was checked before publishing: aapt dump badging reads md.zennotes versionCode 28, versionName 1.1.25, minSdk 24, targetSdk 36, with only the INTERNET and VIBRATE permissions plus the app's own DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION; no CHECK_LICENSE permission in the manifest, no pairip or licensing entries in the zip, no com/pairip in the DEX strings, and Play Console lists the bundle's automatic protection as None, so it opens on devices without Google Play. The sideload-over-the-previous-version check on an emulator was not repeated this cycle. Sideloaded installs do not self-update; use Obtainium. The same version was sent to Google Play review on September 22 and rolls out there once approved.

Validation: 161 automated tests, boundary check (clean archives, no override), typecheck, production build, Capacitor sync and the signed release bundle passed on the release tree; all 474 files of the production web build are byte-identical inside the bundle. CI on PR #80 ran the build and boundary job and the emulator job (an API 35 emulator on GitHub's runner, 4 of 4 instrumentation tests), both green. Android 15 emulator (Pixel 7, API 35), driven with real system taps after the merge: the search ended with the Create "Emu check 1125"… row with Inbox on its right; the form opened with New note, "in Inbox" and the shell's Cancel in one header line with a 10 px gap between the label and Cancel; Name was prefilled and selected; typing urgent in Tags without Enter showed the Add #urgent row; one tap on Create closed the form and the search and opened the note, whose file reads # Emu check 1125, a blank line, #urgent; entering the form again with the same name showed the red already-exists message with Create disabled and Open it offered. Not run this cycle: testDebugUnitTest, lintDebug and a local connectedDebugAndroidTest (CI's emulator job ran it), physical devices, Cloud flows and the tablet layout.