Release 1.11.0 (build 24): iOS 27 launch fix, Favorites, core 2.53.0 - #27
Merged
Merged
Conversation
Three WidgetKit widgets, zero configuration: New Note (small, plus the Lock Screen circular, rectangular and inline families on iOS 16+), Recent Notes and Today's Tasks (medium and large). They wear the app's theme, and every tap is a zennotes:// link: a fresh Inbox note with the title focused, the tapped note, the tapped task's line, or the Tasks view. A widget extension cannot see the vault, so the shell publishes a snapshot — pinned + recent notes, the Home dashboard's Today bucket (overdue first), the --z-* theme colors, no bodies — into the App Group through the new WidgetBridgePlugin. widgets.ts is the publisher, throttled to one reload per 8 s while editing and flushed on backgrounding; widget-snapshot.ts is the contract and WidgetSnapshot.swift its mirror. Task freshness on the phone is the publisher's job: app-core rescans a note's tasks only while a tasks surface is visible, so it diffs updatedAt and rescans changed notes itself. deep-links.ts runs the links after the workspace restores so the shell's cold-launch landing runs first and the link wins; while booting the newest link wins, read from ZenWidgets.consumeLaunchLink (the Android shell needs it because Capacitor's getLaunchUrl is stale for a recreated activity; kept identical here so the file stays shared). mobile-cloud-auth.ts now only takes zennotes://auth. The ZenWidgets target (md.zennotes.ZenWidgets) is wired by tooling/add-widget-extension.rb, mirroring the share extension script; the deployment target stays 15.0 with the 16+ and 17+ APIs gated. Verified on the iPhone 17 Pro simulator: gallery previews, all three placed, warm and cold taps landing on the right note and task line.
The 60-second auto-sync could stall the editor and rebuild every view even when nothing had changed. Three changes, all in the mobile bridge; the shared engine and the native filesystem are untouched: - Refresh only when the disk changed. cloud-sync-refresh.ts wraps the host filesystem and marks before every write, delete and rename (a native write can change the disk before it rejects), so a no-op sync no longer runs vault.rescan(), a batch of pulled files refreshes once, a failed pull still surfaces its partial writes, and a refresh that throws is carried into the next host operation. The scan marks too when it finds a changed local file without a pull. - Give input a turn. The directory walk and base64 decoding yield to the UI every 8 ms (scheduler.yield, setTimeout on iOS 15); decoding runs in 64 KiB chunks instead of Uint8Array.from's per-byte iterator, and binary uploads reuse the original base64 instead of re-encoding. - Tighten the scan cache. Files in a pending conflict (base/local/cloud paths and hashes, which also cover moved local versions, plus paused paths) are never skipped, and review and restore actions bypass the cache so they always get real bytes. Unknown or invalid mtimes are misses, and a file that changes while its bytes are read is not cached under the old fingerprint. Tests: tooling/load-mobile-module.ts bundles real mobile modules with the pinned source aliases (esbuild from .zennotes-source), so the repository, refresh, rescan and adapter -> host -> coordinator paths run under node --test against fake storage and server boundaries. 83 tests, 38 new. Claude-Session: https://claude.ai/code/session_01KWGNhGMPV2dC4APvksmJt7
Replace the cloned desktop source and private renderer/store imports with immutable core, bridge-contract, and shared-domain packages. iOS retains ownership of native files, Cloud/iCloud behavior, keyboard handling, and gestures while public core APIs coordinate editor and workspace actions. - Reserve native vault moves across save, move, reopen, and rollback; invalidate stale attachment/gesture contexts. - Initialize native typing and editor geometry through public host APIs. - Add package-boundary checks and a disposable native runtime fixture. - Align pod deployment targets with the app's existing iOS 15 minimum for the current Xcode toolchain. This PR is stacked on [#22](#22), `fix/cloud-live-regressions`. Its Cloud fixes are prerequisites, not duplicated changes. The unrelated local 1.9.10/build 21 Xcode version bump is excluded.
The production audit used to run inside the desktop source checkout, whose root manifest overrides the nested nanoid and mermaid parser versions that Excalidraw pulls in. Installing the core as a package moved the audit into this repository, where no overrides existed, so the same dependency tree failed on high advisories against nanoid 3.3.3, nanoid 4.0.2, and lodash-es 4.17.21. Add the same overrides. npm keeps already-nested lockfile entries even when overrides change, so the stale nested entries were removed and the tree re-resolved: 21 packages drop out, the resolution matches the desktop, and the audit reports nothing. The lockfile is stable on a second resolve. The same change on the local tree with the newer core candidate passes the boundary check, typecheck, unit tests, and the production build.
Refactor(ios): consume the shared editor through public packages
Replace the dirty candidate archives with the published, reviewed @ZenNotes core, bridge-contract, and shared-domain 2.51.0 bundle (source 8ff2cb86, workingTreeDirty false; checksums in the vendor manifest match the desktop boundary-release output). Implement the host side of the core's Cloud vault-retirement contract (retireState / hasRemoteChanges): when a sync or the background change probe finds a linked vault the server has deleted, confirm it against the signed-in account, then remove only that vault's link and sync state. Local notes and other links stay, and the full sync state, including unsent conflict drafts, is preserved in a retired copy.
Bring the two Dependabot dependency bumps (Capacitor 7.6.9, CodeMirror patches, xyflow, dompurify, jsxgraph, Vite 8.3.0, and the dev-dep group) onto the release line so main can fast-forward to 1.10.0. The refactor's removal of .zennotes-commit is kept over main's pin edit; the lockfile is regenerated to carry the bumps atop the vendored 2.51.0 core.
… of deleting In Browse, a long press on a database went straight to the delete confirmation, while folders opened a sheet. A Play Store review of the Android app pointed out that databases could not be managed like folders, and Android fixed it in 1.1.23 (ZenNotes/zennotesandroid 5abb3dc). This is the same change to the shared drawer, so the two shells match again. The long-press sheet now carries a kind. A database opens it with Rename (requestRenameBrowseDatabase, public since core 2.51.0) and Delete, and no Pin row; a folder's sheet is unchanged. The direct deleteDatabase helper is gone, since Delete in the sheet already handles a database with its own warning. Android's edge-swipe plugin is not ported: it works around Android's system Back gesture, which iOS does not have. Verified on the iPhone 17 Pro simulator: the database sheet shows Rename and Delete only, the backdrop closes it and the next tap opens a note, a rename moved inbox/Untitled Database.base to inbox/Reading List.base with its files intact, and a folder's sheet still offers Pin, Rename, Delete.
Installs the published 2.52.0 bundle from vendor/zennotes: @zennotes/app-core
2.52.0-core.hd89784f4ac848204, and @zennotes/bridge-contract and
@zennotes/shared-domain at 2.52.0-boundaries.h352657dc297ca727, all built
from ZenNotes/zennotes f90161fa with a clean tree. The archives are the ones
attached to the upstream core-2.52.0 release and match the manifest's
SHA-256 and the lockfile's integrity, and a local rebuild of the same commit
is byte-identical.
What the phone gets from 2.51.0 to 2.52.0: each note keeps its own undo
history across tab switches, and renaming or moving the open note keeps its
caret, scroll and history; typing | in the wikilink picker takes the
highlighted note and starts the display text; running a search from the
command palette puts typing in the search instead of the note
(zennotesandroid#65); Settings takes the keyboard. On iPad's desktop layout:
per-note panels ("Keep panels when switching notes"), which ride in the
workspace snapshot the shell already stores, the note keeps a readable width
when side panels do not fit, panel shortcuts leave non-note tabs alone, and
the Vim block-edit and sidebar-cursor fixes.
Nothing in the bridge changes. The contract's additions are optional: the
undo-file methods sit behind supportsUndoFile, which this shell does not
declare, so that setting stays hidden here, and requestMoveBrowseDirectory
waits for a "Move to…" row in the drawer.
Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a0b28d-09db-723f-9488-853441634a2d
…26) 1.10.0 (22) and 1.10.1 (23) were built with the iOS 27 SDK and die at launch on iOS 27 with EXC_BREAKPOINT in ___UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption. Apple enforces it from iOS 27: an app built with the latest SDK must adopt the scene-based life cycle or it fails to launch. The App target had no UIApplicationSceneManifest, and Capacitor 7.6.9 has no scene support of its own (that arrived in Capacitor 8.5), so the shell supplies it. Info.plist declares one window-scene configuration pointing at SceneDelegate and Main.storyboard, so UIKit builds the window and the ZNViewController itself. SceneDelegate only handles what UIKit now routes to the scene instead of the app delegate: URL opens and universal links. Each URL is stashed for WidgetBridgePlugin.consumeLaunchLink and fed to Capacitor's ApplicationDelegateProxy with the options mapped to the application(_:open:options:) shape, so @capacitor/app's appUrlOpen payload is unchanged. A URL that launches the app arrives in the connection options before the bridge exists; it is held until ZNViewController posts didAppearNotification, the same deferral Capacitor 8.5's own proxy uses. AppDelegate keeps the process-level hooks and answers configurationForConnecting by name, so the plist stays the one place the delegate class and storyboard are declared. Its window property, the empty foreground/background stubs and the open:/continue: forwarders are gone: under scenes UIKit no longer calls them. Extensions, deployment targets, entitlements and permissions are untouched. One side effect: under scenes willEnterForegroundNotification also fires at cold launch, so Capacitor 7's bridge logs a single benign "JS Eval error" line at startup while it evals a document resume event before the page exists. Nothing in this app listens to resume; appStateChange is unaffected. DeepLinkUITests (AppUITests target) opens zennotes://new warm and from a cold launch and requires a new "Untitled" note that was not on screen before, in either the phone or the iPad layout. Verified on the iPhone 17 Pro Max and iPad Pro 13-inch simulators; a zennotes://home link fails it. The crash itself was not reproduced: only the iOS 26.5 simulator runtime is installed here, and 26.5 does not enforce the rule. Fixes #26. Amp-Thread-ID: https://ampcode.com/threads/T-01a0b28d-09db-723f-9488-853441634a2d Co-authored-by: Amp <amp@ampcode.com>
Installs the 2.53.0 bundle from vendor/zennotes: @zennotes/app-core 2.53.0-core.h598c8d004c9228a3, and @zennotes/bridge-contract and @zennotes/shared-domain at 2.53.0-boundaries.h193dbe157c4e64d2, all built with a clean tree from ZenNotes/zennotes 3a622639, which is the v2.53.0 release tag and is on upstream main. The manifest's SHA-256 and the lockfile's integrity match the archives. Replaces the 2.52.0 bundle (hd89784f4ac848204 / h352657dc297ca727 from f90161fa). What the phone gets from 2.52.0 to 2.53.0: Favorites on the Home view and a way to favorite a note from anywhere (#810); a heading link followed in reading mode stays in reading mode (android#74); the template editor stacks its panes on narrow screens (android#78); the Cloud vault-settings conflict is asked where you are, per setting (#816); a sync that fails midway keeps what already landed instead of replaying it (#813); a task added to a past daily note after today's was opened still rolls over (#817); the app-info engine field behind `:version` and Settings > About (#814). The `zn open -n` and updater changes are desktop-only, as is the desktop settings writer's fix for dropped per-vault view keys (this shell's writer already passes every existing key through). Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-01a0b28d-09db-723f-9488-853441634a2d
…shows Favorites The ••• sheet gains an "Add to Favorites" / "Remove from Favorites" row that runs app-core's note.favorite palette command (2.53.0, #810), and the long-press menu on note rows gets the same row through requestToggleNoteFavorite; both hide for trashed notes, matching the command's own guard. Favorites are the vault's list (vault.json), the one Home and the desktop sidebar show, distinct from this phone's own Pin order. Home on phones now shows Recent and Favorites and still hides Today (the Tasks view has it): the CSS keys off the data-home-section stamp 2.53.0 adds, with the old "every section after Recent" rule kept for cores that do not stamp it. Folder favorites hide, since they open the desktop sidebar tree the phone replaces with the drawer, and the section goes with them once no note favorites remain. Row gestures read data-home-note-path when the core stamps it and fall back to position + title otherwise. Keyboard dismiss: the toolbar's hide button now blurs the active element before Keyboard.hide(), as the drawer and sheets already do. Hiding alone left DOM focus in CodeMirror, so the next touch (the ensō button) made the WebView first responder again and the keyboard came straight back under the finger. Cloud: the parked vault-settings conflict is reported with the parsed cloud_settings so the app can ask per setting (#816); an unparseable copy is still reported, without contents, and the app asks whole-file. App info carries the WKWebView user agent as `engine` for bug reports (#814). Adds FavoriteUITests (toggle from the ••• sheet, Home Favorites section). Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-01a0b28d-09db-723f-9488-853441634a2d
Amp-Thread-ID: https://ampcode.com/threads/T-01a0b28d-09db-723f-9488-853441634a2d Co-authored-by: Amp <amp@ampcode.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release branch for ZenNotes iOS 1.11.0 (build 24). Build 24 was archived from
56e17c8on 2026-09-18 and is submitted to App Review.What changed since 1.10.1 (23)
UIApplicationSceneManifest+SceneDelegate) so UIKit no longer traps at launch for apps built with the iOS 27 SDK. URL opens and universal links are forwarded to Capacitor with theapplication(_:open:options:)shape, soappUrlOpenand thezennotes://widget links are unchanged. Capacitor stays at 7.6.9.@zennotes/app-core2.51.0 → 2.53.0 (2.53.0-core.h598c8d004c9228a3, source ZenNotes/zennotes3a622639=v2.53.0, clean tree; SHA-256 invendor/zennotes/manifest.json).FavoriteUITestsandDeepLinkUITestsinAppUITests.Deployment target stays iOS 15.0; no permission, entitlement, or SDK changes.
Verified on this tree
npm run upstream,npm test(113/113),npm audit --omit=dev --audit-level=high(0),npm run sync, boot preloads clean. Debug build with Xcode 27.0 launches on iOS 27.0 simulators (iPhone 17 Pro Max, iPhone 18 Pro Max, iPad Pro 13"); the #26 crash reproduces on the pre-fix tree and not after3b97d70.DeepLinkUITests(2) andFavoriteUITests(1) pass on the iOS 27.0 iPhone 17 Pro Max.Merge as a fast-forward so
mainlands on the archived commit.