The text is laid out on a page of one fixed size and the sheet scaled to the screen, so a phone breaks it exactly where a desktop does — twenty-one lines to the page on both. The type size is computed from the page and the line count rather than chosen. A PDF's full screen is the browser window, not the screen, and its button sits beside the zoom. "Recent Notes" is stamped by every write, not only by a rename. And a PDF that will not open says which of the four reasons it is. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
509 lines
27 KiB
Markdown
509 lines
27 KiB
Markdown
# Changelog
|
|
|
|
What changed, newest first — and the digests of every image that went out, so it
|
|
stays possible to tell what is actually running on the server. Digests are read
|
|
back from the registry (`docker buildx imagetools inspect`), never taken from the
|
|
output of `docker push`.
|
|
|
|
Kept short on purpose. A day's work usually went out as several images, and a
|
|
round that repaired what an earlier round the same day had broken says nothing
|
|
worth keeping on its own — so each day is one entry, describing where it ended
|
|
up, with every digest of that day listed. Where the server image is named as
|
|
unchanged in substance, the digest still moved: `build.sh` builds both images and
|
|
a rebuild is not bit-identical.
|
|
|
|
---
|
|
|
|
## 2026-08-26 — The same page on every screen
|
|
|
|
**Images** — web `sha256:e475c080…`, server `sha256:49a98c66…`
|
|
|
|
The server image carries no change of its own; `build.sh` builds both.
|
|
|
|
**Changed**
|
|
- **Twenty-one lines to the page, and the same twenty-one everywhere.** The text
|
|
is now laid out on a page of one fixed size and the whole sheet is scaled to
|
|
the screen, instead of being set in screen pixels. Set that way, a phone and a
|
|
desktop disagreed about everything that matters — the same note broke into
|
|
different lines on different pages depending on what you read it on, and a
|
|
phone page held eighteen lines where a desktop held far more. The type size is
|
|
computed from the page and the line count rather than chosen, so the count
|
|
survives a change to either; a desktop therefore shows larger writing on a
|
|
smaller-looking sheet, the way a ruled notebook app does.
|
|
- **Full screen for a PDF means the browser window, not the screen.** Asking the
|
|
browser for its own full screen took the tabs and the address bar with it,
|
|
which was more than was wanted. The overlay over the app — the part that
|
|
removes the sidebar, the list and the title bar — is the whole feature now.
|
|
The button moved next to the zoom, where the other question about how much
|
|
room the document gets already lives.
|
|
|
|
**Fixed**
|
|
- **"Recent Notes" is now sorted by when you last wrote in a note**, not by when
|
|
you last renamed one. The store always stamped every write; the lists on screen
|
|
read the app's own copy of the notes, and writing *in* a note left that copy
|
|
untouched — so the dashboard and the "Last changed" sort showed the order from
|
|
the last reload.
|
|
- **A PDF that will not open says why.** One message covered every failure and
|
|
guessed at the cause ("offline?"), which was usually wrong and left a report of
|
|
"it will not open on my phone" with nothing in it. The status now travels with
|
|
the failure: a missing file, a server that refused, a server that could not be
|
|
reached and a file that will not parse are four different messages.
|
|
|
|
## 2026-08-25 — Colour, a page you can see the edge of, and a document on its own
|
|
|
|
**Images** — web `sha256:3e1b47e1…`, server `sha256:89e474cc…`
|
|
(earlier that day: web `783ccf92…`, `d7c66235…`; server `2141f145…`, `308ed1b7…`)
|
|
|
|
The server changed earlier in the day for the bar's runtime configuration; this
|
|
last pair carries everything below and no backend change of its own. The two
|
|
entries under it are the same day's earlier rounds, kept apart because they are
|
|
about different things.
|
|
|
|
**Added**
|
|
- **A marker pen and a colour for the letters**, both under one button in the
|
|
toolbar — two buttons would be two panels for one question. A highlight is a
|
|
`<mark>`, the element that *means* highlighted, and marked text is painted with
|
|
dark ink whatever the theme, the way a real marker leaves the writing alone.
|
|
Markdown has no colour, so both travel as HTML like underline does, and both
|
|
are read back — including a bare `<mark>`, which is what most other writers
|
|
emit.
|
|
- **The PDF fills the screen.** The button asks the browser for its own full
|
|
screen *and* lays the document out over the app; either alone is half the job,
|
|
and a browser that refuses the first still gets the second. Escape leaves, and
|
|
the button stays visible while full — a way out that is only known is not a way
|
|
out.
|
|
- **Dragging in from the left edge shows the sidebar** on a phone or tablet, and
|
|
dragging it back hides it. It must start at the edge, travel far enough to be
|
|
meant, and be more sideways than downwards; without the first rule every
|
|
sideways drag inside a note opens it, without the third every scroll does.
|
|
|
|
**Changed**
|
|
- **The dashboard leads with what you were last writing**, the shelf of notebooks
|
|
second — and "Recent" is sorted by time whatever order the notebooks are in.
|
|
It is a question about time, and a hand-made arrangement is the wrong answer.
|
|
- **Tapping the blank part of a page starts writing there.** The text is only as
|
|
tall as what has been written, so most of a fresh page is beside the editable
|
|
area rather than in it, and a tap down there landed on nothing at all: no
|
|
caret, and on a phone no keyboard. The caret goes to the position nearest the
|
|
tap rather than to the end.
|
|
- **The page has an edge on a phone.** The paper runs to the edges of the screen
|
|
there, but it was drawn with no border and no shadow, so nothing said where one
|
|
page ended and the next began.
|
|
|
|
---
|
|
|
|
## 2026-08-25 — The bar that ties the apps together, and Ctrl over a PDF
|
|
|
|
**Images** — web `sha256:783ccf92…`, server `sha256:2141f145…`
|
|
(earlier that day: web `d7c66235…`, server `308ed1b7…`)
|
|
|
|
The server image changed for the bar's runtime configuration; the second pair of
|
|
digests carries the PDF work as well, and no backend change of its own.
|
|
|
|
### The bar
|
|
|
|
**Added**
|
|
- **A strip along the top that leads to the other apps.** It is served by
|
|
HoneyOS, so what it looks like and which apps it lists are settled there —
|
|
InkNote only says where to find it. `HONEYBAR_ORIGIN` in the `.env`
|
|
switches it on, empty leaves everything exactly as before.
|
|
- **`GET /api/public/config`** hands out that origin, and the key that goes with
|
|
it when HoneyOS lives on a different site. Runtime, not build time: an image
|
|
is pulled ready-made, so a value baked in at build time could never be changed
|
|
from the `.env` — and a service worker keeps whole responses, headers
|
|
included, so anything frozen into `index.html` outlives the setting in the
|
|
browser. Aether learned that one the hard way first.
|
|
|
|
**Changed**
|
|
- **The app now measures its height against the bar**, in `#root` and in the
|
|
four places that asked for the full window. With `overflow: hidden` on the
|
|
body the surplus would not have scrolled — it would have been cut off at the
|
|
bottom, silently. And `#root` padded itself around the phone's notch, which
|
|
the bar now covers, so that gap would have been counted twice. Without a bar
|
|
`--honeybar-h` is `0px` and every one of these is what it was.
|
|
|
|
### Reading a PDF
|
|
|
|
**Added**
|
|
- **Ctrl (or ⌘) and the wheel zoom the document**, as in every other viewer, and
|
|
a trackpad pinch with it — it arrives as the same event. The listener is
|
|
registered by hand rather than through `onWheel`: React attaches wheel
|
|
listeners passively, a passive listener may not call `preventDefault`, and
|
|
without that the *browser* zooms the whole application instead, toolbar and
|
|
all. **The point under the pointer stays under the pointer**: you are pointing
|
|
at a word and asking for that to grow, not to be sent to the top of the page.
|
|
|
|
**Fixed**
|
|
- **Zooming with the buttons drifted.** A zoom changes how tall the document is,
|
|
so the scroll position means something else afterwards, and nothing was holding
|
|
anything still — three presses of + and you were elsewhere in the file. They
|
|
hold the page being read now, which is what "show me this page larger" asks
|
|
for.
|
|
|
|
---
|
|
|
|
## 2026-08-22 — A bigger face, and the theme where the settings are
|
|
|
|
**Images** — web `sha256:fe35b0e7…`, server `sha256:a557fb4d…`
|
|
|
|
The server image changed: `theme` is now one of the settings an account keeps.
|
|
|
|
**Changed**
|
|
- **The profile picture fills the account button**, 34px instead of 20 — a face
|
|
at twenty pixels is a smudge — and the same size in the account panel.
|
|
- **Light or dark moved into Account → Profile**, beside the typeface and how a
|
|
chapter opens, which is the kind of thing it is. It used to be a sun in the
|
|
corner of the sidebar: reached for by accident, and gone again on the next
|
|
device. That corner now holds one button, the way into the account.
|
|
- **And it stays.** The theme was pure screen state before, so every reload
|
|
landed on dark whatever had been chosen. It belongs to the account now and
|
|
follows you to the phone, with a copy in the browser so a reload does not flash
|
|
the other one while the account is being fetched. The server takes `dark` or
|
|
`light` and drops anything else, like every other setting written by a browser.
|
|
|
|
---
|
|
|
|
## 2026-08-21 — Asking for the picture properly
|
|
|
|
**Images** — web `sha256:4310304d…`, server `sha256:7a486929…`
|
|
|
|
The server image changed; the web image only because `build.sh` builds both.
|
|
|
|
**Fixed**
|
|
- **The profile picture never arrived**, and two assumptions of mine were why.
|
|
The picture was only looked for in the ID token — a provider may keep its token
|
|
lean and hold the rest at its `userinfo` endpoint, which is what PocketID does,
|
|
and that endpoint was never asked. And the image was then fetched with no
|
|
credentials at all, while providers commonly keep avatars behind their own
|
|
login. The endpoint is now asked when something is missing (and its answer
|
|
trusted only if the `sub` matches the verified token), and the access token
|
|
goes with the picture request — but only when the picture lives at the
|
|
provider, because a token sent to a third party is a credential given away.
|
|
- **And it failed in silence**, which is the part that actually cost the time:
|
|
there was no way to see which of those two it was. Every outcome is now one
|
|
line in the server log — stored, no claim, refused, not an image, too large,
|
|
unreachable.
|
|
|
|
The test provider now behaves like a lean one: it leaves the picture out of the
|
|
token and hands it over only against the access token, so the check passing is
|
|
evidence for both new paths.
|
|
|
|
---
|
|
|
|
## 2026-08-21 — The face from the provider, and whose business a password is
|
|
|
|
**Images** — web `sha256:e525b11d…`, server `sha256:95d56c46…`
|
|
|
|
The server image changed: a table for profile pictures and the route that serves
|
|
them.
|
|
|
|
- **The profile picture comes along.** When the provider sends one — PocketID
|
|
does — the server fetches it at each sign-in and keeps it, encrypted, in a
|
|
table of its own rather than in `users`, where a blob would ride along with
|
|
every `SELECT *` for the sake of one corner of the screen. It is served back
|
|
through the API: nothing on the page talks to the provider, no third-party
|
|
cookie has to survive for a face to appear, and the picture keeps working when
|
|
the provider does not. It shows in the sidebar, in the account panel and beside
|
|
the name in the administration list, and arrives with the **next** sign-in —
|
|
the one moment this app speaks to the provider at all.
|
|
- **An account that signs in through the provider has no password tab**, and that
|
|
now includes one that had a password before it was linked — the usual case for
|
|
whoever set this up, and the reason the tab was still there. The password it
|
|
kept is the emergency door rather than a second thing for its owner to
|
|
maintain: it is set by an administrator, and the account panel says so.
|
|
|
|
---
|
|
|
|
## 2026-08-18 — Which accounts belong to the provider
|
|
|
|
**Images** — web `sha256:5fb51ed4…`, server `sha256:ce1f4649…`
|
|
|
|
- **The administration list marks every account the provider can sign in**, not
|
|
only the ones it created. Those are two different things and the badge had been
|
|
hung on the wrong one: an account that already existed and was *linked* — which
|
|
is what happens to the account you were using when you first sign in this way —
|
|
showed nothing at all. The row says which it is: an account with no password
|
|
here says so, and the button offers to **give** it one rather than to reset
|
|
something that does not exist, with a line explaining that this adds a second
|
|
way in and takes nothing away.
|
|
- **The account panel says how you get in.** The password tab is gone for an
|
|
account that has none — it was already, and the server refuses that route too —
|
|
but nothing said why. It now names the provider, and tells an account that has
|
|
both that the password still works.
|
|
|
|
---
|
|
|
|
## 2026-08-18 — A second way in
|
|
|
|
**Images** — web `sha256:232982cb…`, server `sha256:5ed577b1…`
|
|
|
|
The server image changed: OpenID Connect, and an `oidc_sub` column on users.
|
|
|
|
- **Signing in through an identity provider**, when one is configured — issuer,
|
|
client id and secret, and the address this app is reached at; without them
|
|
nothing about it appears anywhere. It is a second way to the *same* token the
|
|
password login mints, which is why it fits in one file and needs no new
|
|
dependency: Node imports a JWK itself, so checking an ID token is a signature
|
|
and five claims.
|
|
- **The browser is handed a one-time ticket, never the token.** The code is
|
|
exchanged on the server, so the client secret stays there, and a token in a
|
|
redirect URL would otherwise sit in the history, in the proxy's log and in the
|
|
referrer of the next request. The ticket is good for a minute and for one use.
|
|
- **An account is made for somebody the app has not seen**, and an account that
|
|
already exists under that name is linked to the identity rather than
|
|
duplicated — by subject from then on, so either side can be renamed. Never an
|
|
administrator: those rights are granted in the app, and a group at the provider
|
|
must not be a way around it.
|
|
- **Both doors stay open.** An account made this way has no password here at all
|
|
— the interface stops offering to change one and the login route refuses it as
|
|
it refuses a wrong one, at the same speed — but an administrator can still set
|
|
one, which is the way back in the day the provider is unreachable.
|
|
- **A suite that signs in at a provider of its own** (`e2e/oidc.mjs`), which
|
|
signs real RS256 tokens with a key InkNote fetches and checks. It holds down
|
|
the four ways the journey must fail as well as the way it must work: a refusal,
|
|
a replayed ticket, a token signed with an unpublished key, and one addressed to
|
|
another client.
|
|
|
|
---
|
|
|
|
## 2026-08-17 — Quotes that hold more than a sentence, and one list of marks
|
|
|
|
**Images** — web `sha256:30f67832…`, server `sha256:6ffe4b58…`
|
|
|
|
No backend source changed this round.
|
|
|
|
- **A quote may hold whole blocks — and now has room for them.** A heading, a
|
|
list and a closing line inside a quote are ordinary blocks, but the rule that
|
|
gives the writing its rhythm reaches only the editor's own children, so inside
|
|
a quote everything stood jammed against its neighbour, with the items of the
|
|
list further apart than the blocks around them.
|
|
- **The same divide when it leaves as Markdown.** Blocks inside a quote were
|
|
written on consecutive lines, which every other reader folds into one: the
|
|
sentence after a quoted list became more of the last bullet. They are separated
|
|
by a **quoted** blank line now — `>` on its own, since an empty line would end
|
|
the quote — which is the same rule the top level has had since 11 August, and
|
|
reading one back gives the blocks it was written with, quotes inside quotes
|
|
included.
|
|
- **Marks are one list again.** There were two copies, in the note dialog and the
|
|
chapter dialog, and only one of them had ever been extended — so which marks
|
|
existed depended on which dialog you happened to open. One grid, and with it
|
|
twice as many marks, **divided under headings** now that there are too many for
|
|
one wall of pictures.
|
|
- **A mark nobody thought of can simply be typed.** The search box is a text
|
|
field, which on a phone is exactly where the emoji keyboard lives, so anything
|
|
pasted or typed into it is offered as a tile — one glyph with its modifiers, the
|
|
same eight code points the server keeps.
|
|
- **The changelog is a third of its length.** A day usually went out as several
|
|
images, and a round that repaired what an earlier round the same day had broken
|
|
reads as noise afterwards. One entry per day now, saying where it ended up, with
|
|
every digest of that day listed.
|
|
|
|
---
|
|
|
|
## 2026-08-17 — A notebook arranged by hand
|
|
|
|
**Images** — web `sha256:c5e460cf…`, server `sha256:ac3b0527…`
|
|
(earlier that day: web `b7c9f8b7…`, `c1bcdcc4…`, `b8bc284a…`, `18a68a2d…`;
|
|
server `a0fd140a…`, `6c9af404…`, `a66cc8eb…`, `febc4b93…`)
|
|
|
|
The server gained one column: `sort_mode` on chapters.
|
|
|
|
**Arrangement**
|
|
- **A drop lands the row *under* the one it was released on, and only there.**
|
|
Dropping above a row and below its predecessor are the same move described
|
|
twice, so every gap could be aimed at from two sides. A strip **above the first
|
|
row** covers the one place a drop *under* something cannot reach.
|
|
- **A new note appears at the very top and nothing else moves.** A row arriving
|
|
without a position left every separator counting rows that were no longer above
|
|
it, and a little more of the order came undone with each note. Every way in and
|
|
every way out — new, restored, moved, binned, or a chapter removed — now
|
|
renumbers the list.
|
|
- **A chapter is a place *and* a neighbour**: its header takes a row dropped on
|
|
it, its bottom edge is the gap under it. It is always in the user's own order,
|
|
and its notes are dragged under the same switch as the list beside them.
|
|
- **A chapter can be given an order of its own** — in the same dialog as its name,
|
|
mark and colour. In any order but the hand-made one it loses its lines and
|
|
handles, as the notebook does.
|
|
- **Two lines could not be put in one gap.** They carry the same position, and the
|
|
comparator claimed both directions at once for that case; the write that fixed
|
|
it was then discarded by the "did anything change?" test, which sorted before
|
|
comparing and so could not see a sequence.
|
|
|
|
**Elsewhere**
|
|
- ***Move to…* is divided under headings** — the notebook itself, *Chapters here*,
|
|
*Other notebooks* — each row wearing its chapter's mark or its notebook's cover.
|
|
The headings are deliberately not shaped like entries.
|
|
- **A separator and a chapter each have one *Edit*** instead of an entry per
|
|
property, and the separator dialog draws the line as it will look.
|
|
- **A link wears the mark of the note it points at**, in the text and in the `@`
|
|
menu, instead of one sheet-of-paper glyph for everything.
|
|
- **The mark, the title and the task-list box sit on the line they belong to.**
|
|
Boxes had been centred, which is not the same as letters being aligned; the
|
|
mark and title share a baseline, the checkbox is centred on the capitals and
|
|
sized in em, and the mark stands in the same column as the tools below it.
|
|
- **Grey letters ran down the bar while dragging a column edge.** An absolutely
|
|
positioned child in a cell of a `border-collapse` table sends Chromium down
|
|
another painting path, and the cell's own text came out a second time, squeezed
|
|
under the bar. The marker is never drawn now; the line is an inset shadow on the
|
|
cells the column is made of.
|
|
- **Opening a note flashed a long narrow page**, because the drawing stage was
|
|
measured a frame after it was drawn. Measured before the first paint now.
|
|
|
|
---
|
|
|
|
## 2026-08-16 — Marks, lines, and a table that breaks
|
|
|
|
**Images** — web `sha256:1f9f5141…`, server `sha256:3b2f8b44…`
|
|
|
|
The server gained `icon` on notes and chapters, `icon_color` on notes, and colour
|
|
and style on separators.
|
|
|
|
- **A note and a chapter can wear a mark**, chosen from a short list searched by
|
|
what a thing is *for* — "invoice", "house", "travel" — rather than from the
|
|
system emoji keyboard, which is a different thing on every machine.
|
|
- **A separator takes its own colour and one of five shapes.** Three faint grey
|
|
lines through one notebook read as the same line repeated.
|
|
- **A long table lay across the gap between two sheets** with a row cut in half.
|
|
Tables are broken between their rows now, which is where a table breaks on
|
|
paper; the spacer is a `tr`, since a `div` inside a `tbody` is not something the
|
|
browser keeps.
|
|
- **A dialog closed in the middle of an edit** when a selection was dragged past
|
|
its edge: the click lands on the nearest common ancestor of press and release.
|
|
All ten of them remember where the press began.
|
|
- **A chapter's colour could not be cleared** — "None" sent null, and the update
|
|
rode on `COALESCE`, which reads null as "leave it alone".
|
|
- A table's header row is legible on the dark theme, and opening a notebook opens
|
|
its first note on a wide screen.
|
|
|
|
---
|
|
|
|
## 2026-08-12 — Chapters, and a phone that works
|
|
|
|
**Images** — web `sha256:c077f0e0…`, server `sha256:c13af5d5…`
|
|
(earlier that day: web `65799ecb…`, `2041f103…`, `95657aaf…`, `3700247c…`,
|
|
`790ff73a…`, `1217b81b…`, `5ea846f3…`, `c1222665…`; server `a51b96ad…`,
|
|
`ed0b9970…`, `b4db0725…`, `181d77be…`, `3303d3dc…`, `06f94f43…`)
|
|
|
|
The server gained a `chapters` table, `chapter_id` on notes, a `color` column and
|
|
a reorder route for chapters, named positions on the note reorder, a
|
|
`cover_pattern` column, and the per-account setting for how a chapter opens.
|
|
|
|
- **Chapters — folders inside a notebook.** A note is in one or in none. Removing
|
|
a chapter never removes what it held, and the dialog says so with the number
|
|
affected. They sit at the same level as notes and separators: one counter, one
|
|
grip, one kind of drag.
|
|
- **Two ways for a chapter to open**, chosen per account: a further column beside
|
|
the list, or unfolded under its own row. A phone fills the list instead.
|
|
- **A chapter is made complete in one dialog** — name and colour together — and a
|
|
chapter with no notes in it is visible at last; "nothing here" had been asked of
|
|
the notes alone.
|
|
- **A notebook starts in the hand-made order**, which is where its arrangement
|
|
lives. The views across all notebooks keep "last changed": they have nowhere to
|
|
keep an arrangement. **Rearranging is a mode**, switched on beside the sort menu.
|
|
- **A filed note came back out of its chapter by itself.** The server had it right
|
|
the whole time; the sync that follows rebuilds every note from the server's
|
|
answer, and the function doing that never carried `chapter_id`.
|
|
- **Removing a chapter left you stuck in the confirmation** — that one path set
|
|
the dialog directly instead of going through the helper that takes it down. The
|
|
suites had read the list straight through the open overlay and passed; they now
|
|
assert the question is gone.
|
|
- **The order of notes could not be changed on a phone**: the list used the HTML5
|
|
drag API, which mobile browsers never fire from touch. Pointer events and a grip
|
|
per row. **"Move to…" opened off the side of the screen** and reacted only to
|
|
hover; it opens on a tap and flips side when there is no room.
|
|
- **A note made offline lost everything written into it** — its create reached the
|
|
server, the server named it something else, and every queued edit went on
|
|
addressing the name only that device knew. The queue is repointed with the
|
|
database now.
|
|
- **The install button never appeared, even over HTTPS.** `beforeinstallprompt`
|
|
fires before React mounts, so it is caught in `index.html`; and the manifest is
|
|
fetched without credentials unless the link says `use-credentials`, so behind an
|
|
authenticating proxy the browser read the login page as the manifest.
|
|
- **The service worker could cache a proxy's login page as the application** and
|
|
then "work offline" by showing a login screen it could never get past.
|
|
Redirected and unsuccessful responses are no longer cached.
|
|
- **A cover is a colour and a pattern, chosen in turn** — every combination, drawn
|
|
in CSS, with existing notebooks keeping exactly the cover they had.
|
|
- The search box in a notebook is simply there, the margin-note column is half
|
|
again as wide, and a submenu is no longer clipped away on a wide screen.
|
|
|
|
---
|
|
|
|
## 2026-08-11 — Writing on paper
|
|
|
|
**Images** — web `sha256:3a181ff3…`, server `sha256:0832ee2b…`
|
|
(earlier that day: web `b764a9d8…`, `dc804f7b…`, `ed11f034…`, `3b040d99…`;
|
|
server `1648498f…`)
|
|
|
|
The server gained a column and two routes for extracted PDF text.
|
|
|
|
- **You write on paper.** Each page is a sheet of its own, centred, with a gap
|
|
after it, and the text is broken against those sheets so no paragraph is cut in
|
|
half. The breaks are decorations: nothing enters the document, reaches the
|
|
server, or appears in an export. The toolbar says *Page 2 of 4*.
|
|
- **A drawing runs over as many pages as the note has**, numbered the way the PDF
|
|
reader numbers them, with one blank page past the end to start on. A drawing
|
|
that lives on page one alone is still stored in the old shape.
|
|
- **A PDF is searchable on every device**, not only the one that imported it: the
|
|
importing device extracts the text and stores it beside the file, encrypted. The
|
|
server still has no PDF library.
|
|
- **The typeface is a per-account setting** — serif, sans or mono — so it follows
|
|
you to the phone. **Text alignment** likewise, with its own extension so the
|
|
schema stays in files this project owns; non-left blocks travel as
|
|
`<div align="…">` in Markdown.
|
|
- **A note with a drawing could not be typed in at all**: the box wrapping each
|
|
page's ink layer lay over the text and swallowed every click.
|
|
- **A drawing exported in a ZIP was lost on re-import** — the exporter had written
|
|
`drawings/…` since the interface went English, the importer still looked for
|
|
`zeichnungen/…`.
|
|
- **"Start writing..." was printed at the foot of every page** with a break after
|
|
it: the placeholder styles `[contenteditable]:empty::before`, and the spacer
|
|
holding a gap open is exactly that.
|
|
- The Markdown exporter began putting a blank line between top-level blocks, and
|
|
the margins narrowed from 73 to about 80 characters.
|
|
|
|
---
|
|
|
|
## 2026-08-10 — Notes on passages
|
|
|
|
- **Pinned notes in a PDF.** A remark is attached to a passage and shown in the
|
|
margin beside the page, never written on top of it; on a phone it opens as a
|
|
bottom sheet. Such a remark can be linked from the notes panel and written about
|
|
at length.
|
|
- The server stored an overlay holding only pinned notes as `NULL`, throwing every
|
|
one of them away, because it counted strokes and nothing else.
|
|
|
|
---
|
|
|
|
## 2026-08-09 — Arranging a notebook
|
|
|
|
- **Sorting per notebook**, remembered on the notebook, including an arrangement
|
|
made by hand — which comes back intact after filtering.
|
|
- **Separators**, **Markdown import** (single files or a ZIP this app exported),
|
|
and **moving notes between notebooks**.
|
|
- Switching to Draw shrank the page from 733px to 460px: the editor was a flex
|
|
item at `flex: 0 1 auto`, so it was only as wide as its widest row — the
|
|
toolbar, which loses a dozen buttons in Draw.
|
|
|
|
---
|
|
|
|
## 2026-08-08 — PDFs
|
|
|
|
- Import a PDF, read it, select its text, draw on each page, and export it with
|
|
the drawings written in. Notes written beside a PDF.
|
|
- Uploaded files are encrypted at rest, like note bodies already were, and stored
|
|
as deduplicated, content-addressed attachments; unused ones are freed, and
|
|
versions are kept by count rather than by age.
|
|
- Export as Markdown or PDF; the browser suites moved into the repository.
|
|
- Vector clocks were never read — replaced with a conflict check that works.
|
|
- Public sign-up removed: an administrator creates the accounts.
|
|
|
|
---
|
|
|
|
## Earlier
|
|
|
|
The interface became English throughout, and search inside a notebook replaced
|
|
the sidebar's global search (2026-08-11). Before that: the TipTap editor, version
|
|
history, full-text search and attachments (2026-08-07); drawing sync with
|
|
WebP/JPEG support (2026-06-21); the first syncing alpha and PWA (2026-02-18).
|