Skip to content

NOTO v1.2.0

Choose a tag to compare

@github-actions github-actions released this 28 Aug 04:27
· 106 commits to main since this release

Noto has a face, documents are no longer plain text, and the editor finally
keeps everything you type.

Branding

Noto ships with its own icon on every platform. Until now the desktop
application carried Electron's default atom and Android carried the green robot
placeholder — somebody else's artwork standing in for a product that had none
of its own. There was no launch screen at all; Android improvised one from
whatever the launcher icon happened to be.

One source image now drives all of them. scripts/generate-brand-assets.py
derives the Windows .ico at seven sizes, the macOS .icns up to 1024,
the Linux PNG, the Expo icon and adaptive icon, the launch-screen logo, the
favicons and the Open Graph card. When the artwork changes, that script is the
only thing that runs.

The sidebar carries the wordmark, so the application says what it is without
the window title having to.

Editor

Autosave cleared its pending timer on unmount without ever writing it, so
switching documents inside the debounce window silently dropped the last few
keystrokes. The queue is now flushed on unmount, when the window is hidden,
and on an explicit save. The indicator distinguishes unsaved from saving
rather than calling a document clean while a write is still queued.

Deleting a document had a core implementation and a command definition but no
way to reach either. It is now part of the data contract, implemented by both
data sources on top of the existing soft delete, and offered per row in the
sidebar. The tombstone is what sync will need later.

Keyboard shortcuts were declared in the command registry and bound to nothing.
Keys now resolve through the registry, so a command disabled in the current
context cannot fire, and an unbound accelerator still falls through to the
editor. Matching lives in core as pure functions over a structural event, so
the desktop main process can reuse it for the application menu.

Formatting

Documents are no longer plain text. Headings, bold, italic, underline,
strikethrough, bullet and numbered lists, blockquotes, inline and block code,
links, images, tables, horizontal rules and text alignment are all there, from
a toolbar above the document. Everything but images, tables and the divider —
which need a URL or a size first — also has a keyboard shortcut.

Every toolbar control names a command in the registry rather than describing
one of its own, so the label and the shortcut on a button are the ones the
command palette shows. The registry is the editor's keymap now rather than a
description of one: an accelerator is rewritten into the form ProseMirror
expects and those bindings load last, so changing CmdOrCtrl+Shift+7 in
@noto/core changes the key that makes a numbered list. Formatting is bound
there and nowhere else — binding it in the window listener as well would toggle
a mark on and straight back off.

Link is CmdOrCtrl+Shift+K, not the more usual CmdOrCtrl+K, which the
command palette already owns: the palette is reachable from every screen and
formatting is not, so the palette keeps the shorter key.

Link and image addresses go through a scheme allowlist. A note is user data
that renders as HTML and will travel to other devices once sync lands, so a
javascript: href would be stored cross-site scripting rather than a bad link.
Typing a bare host is the common case, so a value with no scheme gets
https://; an explicit scheme is accepted or rejected, never rewritten.

Images are referenced by address or kept as a pasted data: URI. Noto has no
asset store until image capture, and copying binaries into the document — and
into every sync payload — is not something to do behind your back.

This is web and desktop. Tiptap needs a DOM, so the mobile editor stays plain
text, writing the same document model the other platforms read — which means it
can read a formatted document but not preserve one. See the limitation below
before editing on the phone.

Tabs and files

Several documents can be open at once. Which one was showing used to be a
private detail of each platform's data source, derived by falling back to the
newest row whenever nothing was selected — which made Close All impossible,
because closing the last tab reopened something immediately. A tab store in
@noto/core owns that choice now and the data sources follow it, so
activeDocument: null means nothing is open rather than "show me the newest
one".

Tabs hold ids, never documents, so a tab whose document was deleted resolves to
nothing and is pruned once the list is known. They persist per platform the way
settings already do — hydrated at startup, written back on change — so
reopening Noto restores the desk rather than an arbitrary document. The bar
draws nothing for a single document: one tab is a label, not a choice.

Around that, the rest of file handling. Rename happens in the sidebar row
itself and commits on blur as well as on Enter, because the field appears under
the pointer and losing a rename to a stray click would be its own small
betrayal. Recent is what "open" means in an application with no file dialog.
Save All asks every mounted editor to flush; only the front one is mounted
today, but it is the editors that hold unwritten work, not the shell.

Recovery is new. Autosave already survives every ordinary way an edit escapes,
because it flushes on all of them; what it cannot survive is the process
disappearing between a keystroke and the debounced write. A snapshot written
synchronously to local storage on every edit closes that window, and is offered
back — as a choice, not applied — when it outlives what reached storage.

Find, undo and the view

Undo and redo run on ProseMirror's own history and are bound inside the editor
rather than on the window, so undo means the document when the caret is in it.

Find and replace is ProseMirror's search plugin, which is the part worth not
reimplementing: highlights stay correct as the document changes underneath
them. The layer above counts matches for the tally, and the tally says "3
matches" until something is stepped to, rather than claiming the caret is on
the first one.

Word wrap reaches the only blocks that can outgrow the measure. Zoom scales the
type rather than transforming the element, because a transform blurs text and
leaves the caret measuring an unscaled box; it steps along a fixed ladder, so
zooming in and back out lands exactly where it started.

Commands now carry a scope. Formatting and undo are bound inside ProseMirror,
find and the view controls on the window. That was a category heuristic before
and is stated outright now, because getting it wrong binds one key twice and a
toggle turns itself off again.

Four faults were found and fixed on the way, each with a test that fails
without it: the web data source reported an empty list before the workspace
opened, so every restored tab was pruned on reload — "no workspace yet" is not
"no documents"; pruning returned early on the tab count alone, so a document
closed and then deleted stayed in recents for good; renaming from the sidebar
never reached the open editor, whose title lives in local state so that typing
need not wait for a write; and replace did nothing on its first press, because
ProseMirror only replaces a match that is already selected.

Mobile

Every screen shares one store, and now actually does. Each screen used to call
the store hook directly, which gave each of them its own copy of the document
list — a title typed in the editor was written to SQLite while the list carried
on showing the row it had loaded at mount. One provider at the root now holds
the copy both screens read.

Autosave has the same flush-on-unmount fix as the desktop editor: leaving a
document within 600ms of the last keystroke used to clear the pending timer
without ever writing it.

Documents can be deleted, from a button on each row of the list, from the
editor header, or by long-pressing a row. Deleting drops the queued autosave
first — flushing it afterwards would write a fresh updatedAt over the
tombstone and bring the document back.

There is a launch screen, where before there was none: the Noto mark on a flat
backdrop that follows the system appearance.

Upgrading

The desktop installer replaces 1.1.6 in place; Windows may keep the old
shortcut icon in its cache for a few minutes after the upgrade. On Android,
install the new APK over the existing one — the signing key is unchanged, and
documents on the device are untouched.

Known limitations

Editing a formatted document on Android flattens it. The mobile editor
reads a document as plain text and writes it back the same way, so opening one
that has headings, tables, images or links and typing a single character
replaces all of it with unformatted paragraphs. That has been true since the
phone got an editor, but it costs a great deal more now that documents can hold
tables and pictures. Until the mobile editor understands the document model,
edit formatted documents on web or desktop, where nothing is lost.

Unchanged: the desktop installers are unsigned, so Windows SmartScreen and
macOS Gatekeeper warn on first launch — verify your download against
SHA256SUMS.txt. macOS auto-update needs a signed build, and Windows ARM64 is
installer-only. iOS is not published. Cloud sync is not enabled on any
platform.


Downloads

Windows

Package File
Installer (x64) Noto-1.2.0-win-x64.exe
Installer (ARM64) Noto-1.2.0-win-arm64.exe

macOS

Package File
Disk image (Apple Silicon) Noto-1.2.0-mac-arm64.dmg
Disk image (Intel) Noto-1.2.0-mac-x64.dmg
Archive (Apple Silicon) Noto-1.2.0-mac-arm64.zip
Archive (Intel) Noto-1.2.0-mac-x64.zip

Linux

Package File
AppImage (x64) Noto-1.2.0-linux-x64.AppImage
Debian package (x64) Noto-1.2.0-linux-x64.deb
RPM package (x64) Noto-1.2.0-linux-x64.rpm

Android

Package File
APK (64-bit) Noto-1.2.0-android.apk
APK (32-bit) Noto-1.2.0-android-armeabi-v7a.apk
App bundle (Play Store) Noto-1.2.0-android.aab

The web application needs no download and is always at the latest version.


Verifying a download

Checksums for every asset are published as SHA256SUMS.txt.

sha256sum --check --ignore-missing SHA256SUMS.txt

Updating

Windows and macOS installations update themselves: Noto checks for a new version on startup, downloads it in the background, and applies it on the next restart.

On Linux, replace the AppImage or install the new .deb/.rpm through your package manager.


System requirements

Platform Requirements
Windows Windows 10 or later, x64 or ARM64, 4 GB RAM, 500 MB storage
macOS A supported modern macOS version, Apple Silicon or Intel, 4 GB RAM, 500 MB storage
Linux x64, 4 GB RAM, 500 MB storage
Web A current version of Chrome, Edge, Firefox or Safari

Full details: System requirements

What's Changed

Other Changes

  • feat(editor): add 5.2 rich formatting — marks, blocks, links, images,… by @utkarsh032 in #4
  • Merge pull request #4 from utkarsh032/dev by @utkarsh032 in #5
  • Merge pull request #5 from utkarsh032/main by @utkarsh032 in #6

Full Changelog: v1.1.6...v1.2.0