This is not a list of vague “improvements.” Each entry names the measured problem, the fix we shipped, and what the same measurement says afterward.
New drops land here weekly to fortnightly during the beta.
On a Mac, Legend looks and behaves like a Mac app
Legend drew its own window buttons in the Windows shape, on the Windows side, on every platform. On macOS it now uses the real ones: the traffic lights, where they belong, doing what they do everywhere else. Full screen works the way full screen works.
What was wrong
Legend drew its own minimize, maximize and close buttons because it draws its own title bar. On Windows that is fine. On a Mac it was wrong in every particular: the wrong shapes, on the wrong side of the window, and the middle one did nothing at all, because macOS has no "maximize" for it to do. Full screen was worse than cosmetic. Legend implemented its own, and it could trap you: it covered the Dock unlike every other app, the gesture to leave it worked once and then stopped, and the state survived a restart, so a first session could end stuck full screen and the next one started there.
What changed
On macOS the operating system now draws the window controls, which means they are the real ones, in the real place, with the behaviour every other Mac app has. Full screen, zoom and the green button are macOS's own, so the trap is gone by removal rather than by repair. Legend's own controls simply do not appear there. Because the OS owns the window frame, the corners are rounded as a Mac window's corners are. Legend's logo, settings and extensions move to the top right, since the traffic lights now occupy the top left. Tabs stay where they are, in the order they were, which is what Chrome and every other Mac app with tabs does.
What the numbers say now
Nothing changes on Windows. On a Mac the window stops announcing that it was designed somewhere else. The full screen trap is the part worth stating plainly: it was the kind of defect where a new user force-quits, remembers it, and does not come back, and it is gone because we stopped implementing something the operating system already does.
Control-click a note and the menu stays open
On macOS the file browser's context menu opened and closed itself in the same instant, so it could not be used at all, and Delete appeared to be missing. The menu was always there. Something else was closing it.
What was wrong
Control-click is how you open a context menu on a Mac. macOS also reports it as an ordinary click with the Control key held, and Legend read that second half as "open this note in a new tab". So Control-clicking a note in the file browser opened the menu and opened a tab at the same moment. The new tab took focus, the menu vanished, and the result was a menu that flickered, could not be scrolled, disappeared if you reached for it, and seemed to have no Delete. Deleting a note from the file browser was effectively impossible on a Mac.
What changed
On macOS, Control-click now opens the context menu and does nothing else. Command-click still opens a note in a new tab, which is the Mac convention for that gesture anyway, so nothing is lost. Windows is untouched: Ctrl-click there still opens a new tab, as it always has.
What the numbers say now
The menu opens and stays open, with every entry it always had, including Delete. Nothing was added to the menu to fix this. The entries were there the whole time and could not be reached.
Files Legend writes show up in Finder
Import a folder on a Mac, and the new notes appeared in Legend but not in the Finder window already open on that folder. The files were on disk the whole time. Finder had not been told to look again.
What was wrong
macOS does not refresh an already-open Finder window when another program creates files in it, unless that program says so. Legend never did. So after a mass import the notes were visible in Legend, absent in Finder, and revealing one made it appear — which is a deeply alarming thing to watch when you have just imported someone's documents. Nothing was lost, delayed, or pending: the files were written and flushed to disk before Legend reported success. The folder simply looked empty.
What changed
Legend now tells macOS when it writes a file, so an open Finder window updates on its own.
What the numbers say now
What you see in Legend and what you see in Finder agree. This mattered most in exactly the situation it is least survivable: importing a pile of someone else's documents and then showing them the folder.
Four smaller repairs
A mass import you could not start, two drag handles that did nothing, an install that never said it had finished, and a way to ask Legend whether it is up to date.
What was wrong
Mass import's review screen reserved room for sixty rows whether the plan had sixty or nine, so with a small import the button that starts it sat well below the bottom of the window with no way to scroll to it: the plan was ready and there was no visible way to run it. In the graph navigator, the drag bars on the Menu and Analytics cards were both inert, one refusing to move at all and the other growing the card while leaving the content behind, so dragging it produced blank space. Installing an extension reported every failure and said nothing at all on success, so a finished install looked exactly like a stuck one. And there was no way to ask Legend to check for an update; it checked on its own schedule and said nothing unless it found one.
What changed
The review list is now as tall as the plan it holds, so the button that starts the import is where you would expect it. Both navigator drag bars resize their cards, give the space back to the panels below, and stop at the point where the card's own header and handle would disappear. Installing an extension confirms that it finished. Settings, under About, has a Check for updates button that tells you what it found, including that you are already up to date or that the check itself could not reach anything.
What the numbers say now
Four things that each looked like the app ignoring you.
Rename a note and its links follow
Renaming or moving a note now updates the [[wikilinks]] that point at it, across the whole vault. You see exactly what will change before anything changes, and nothing moves until you say so. Renames that affect no links skip the confirmation entirely.
What was wrong
Legend did not update inbound links when you renamed a note. Every [[link]] pointing at the old name silently went nowhere, and the only fix was to find and hand-edit each one first. The first person outside the project to use Legend seriously hit this and did not report it as a bug. He worked around it and taught the workaround to his assistant: "You will want to start by changing everything that links to [[old name]] in legend to [[new name]] because it does not change the links automatically upon renaming a file." He offered that unprompted, as advice. A gap a user stops reporting and starts planning around has become part of how the tool works.
What changed
Renaming or moving a note scans every note in the vault and rewrites the links that pointed at it. Before anything is written you get a preview saying how many notes were scanned, how many links will change, and anything it could not safely rewrite. Cancel and nothing happens. If the rename affects no links at all, there is no dialog and the rename just happens. A link that still resolves to the same note afterward is left byte-for-byte alone, because rewriting a link that already works is a change you did not ask for. A bare [[old name]] becomes [[new name|old name]] rather than having its text replaced, so a link written into the middle of a sentence still reads as the sentence you wrote. Links inside code blocks, inline code, and HTML comments are left alone.
What the numbers say now
The workaround is unnecessary. You rename a note and the links follow, which is what the reporter expected from the beginning and what he built a habit to compensate for. The preview exists because a rename that quietly edits thirty notes should say so first, and because a confirmation you can predict is one you stop reading. Underneath, a move that is interrupted partway leaves a journal on disk recording what it was doing, so a crash mid-rename is recoverable rather than a half-renamed vault.
The first five minutes explain themselves
Legend opens with a guided walkthrough now. Eight steps, skippable at any point, that show you the parts of the app you would otherwise have to find on your own. You can start it again any time from Settings. It exists because an outside reader told us the features were there and nothing explained them.
What was wrong
Legend has a lot of surface. A graph of your whole vault, full-text search, backlinks, frontmatter rendered as a header instead of raw YAML, templates, a file browser that treats no folder as special. None of it announces itself. The first person outside the project to sit down with Legend found the editor immediately and then spent his session not finding the rest, including the graph, which is one of the reasons to use it at all. His report was not that anything was broken. It was that the app never told him what it could do.
What changed
A walkthrough runs the first time Legend opens, and can be started again whenever you like from Settings under Behavior. It opens a vault, opens a note, follows a link, shows the graph, selects a group of notes and assigns a property to all of them at once, explains what properties are for, and searches the vault. Each step points at the real control in the real interface, with the target highlighted and an arrow from the card to the thing itself, so what you learn is where the thing actually is. If you already have a vault open it runs there, in your own notes, rather than insisting on an example one. Skip it and nothing nags you again.
What the numbers say now
The parts of Legend that took a conversation to discover now take five minutes and no conversation. Everything the walkthrough shows was already there, which is the point: this release adds no features, it stops the existing ones from being invisible. The graph is the clearest case. It is one of the reasons to use Legend and the first person outside the project to try it never found it at all.
Legend can be added to
Until now, everything Legend could do had to ship inside Legend. This release opens a second path: extensions that install from inside the app, downloaded and verified by Legend itself. The first one imports a whole folder of documents into your vault.
What was wrong
An editor either does the thing you need or it does not, and the only way to add something was for us to build it into the app for everyone. That is a bad trade in both directions. Work that matters to a few people makes the app heavier for everyone else, and work that would only ever be used occasionally never gets built at all. Bringing a folder of Word documents into a vault is exactly that shape: enormously useful the week you switch to Legend, and dead weight every week after.
What changed
Legend now installs extensions itself. Settings has an Extensions page listing what is available. An extension downloads from one endpoint, is checked against its published fingerprint before any of it runs, and is installed into its own versioned folder that can be rolled back if a version misbehaves. Nothing is found by dropping files into a folder, so an extension you did not install cannot appear. The first one is Mass import: point it at a folder, see exactly what would land where before anything is written, then let it run and read the report it leaves behind.
What the numbers say now
Mass import is a paid extension, unlocked with a code tied to the installation that redeemed it. The editor does not change: everything that was free is still free, and an extension you have not bought is a row in a list rather than a nag. What this release actually adds is a seam. Legend can grow in directions we do not have to guess at in advance, and which of those directions turn out to matter is the part we know least about.
You can write a bracket again
Someone writing in Legend reported that they could not type square brackets as ordinary punctuation: the text turned blue and the brackets vanished off the screen. They were right, it had three separate causes, and all three are fixed.
3separate causes behind one complaint. Fixing the first two still left the text looking like a link
0documents were ever damaged. The brackets were always in the file; only the screen was wrong
1new setting, Auto-pair brackets and quotes, on by default and switchable in Settings under Behavior
1outside reader, which is how many it takes. This is the first defect reported by someone other than us
What was wrong
Writing see [1] below put the number in link colour and hid the brackets entirely, so the line read see 1 below. Writing [draft] did the same. Nothing was clickable and nothing was broken in the file, but the screen said otherwise, and a writer using brackets as punctuation had no way to make Legend stop. Markdown treats [text] as the opening of a link, and Legend was believing that even when no link followed. Typing an opening bracket also inserted a closing one, which is helpful when you are writing a link and an intrusion when you are not, and there was no way to switch it off.
What changed
A bracket is now only treated as a link when a destination actually follows it. [the docs](https://example.com) is a link. [draft] is two brackets and a word, shown in ordinary text colour with the brackets visible, exactly as typed. The bracket pairing that happens as you type is now a setting: Settings, then Behavior, then Auto-pair brackets and quotes. It stays on by default, because typing the closing bracket yourself steps over the one already there rather than doubling it, so most writers never notice it. Turn it off and every character you type is the only one that appears.
What the numbers say now
Brackets are punctuation again. Links still look like links, still hide their syntax, and still work the way they did. The setting is there for anyone who would rather Legend never added a character they did not type.
Open a Word file, a PDF, or a spreadsheet export
Legend reads six file types now, not two. Word documents, LibreOffice documents, web pages, PDFs and CSV exports convert to Markdown when you open them, and the conversion was rebuilt against real business documents rather than test fixtures.
6formats: .docx, .odt, .html, .pdf, .csv and plain text, all through the same Import button
3bugs that only real documents exposed: a dropped image, silently deleted table cells, and two paragraphs run together
2conversion engines, with the faster one measured at about 12 times the speed and matched for fidelity
What was wrong
Legend could open text and Markdown. Anything else meant converting it somewhere else first, which is a poor answer when the document you want to read is a Word file someone just sent you. Table cells lost their links and their bold when converted. And a set of real LibreOffice documents found three faults no test fixture had: an image inside a paragraph disappeared with no warning, a table using repeated-column shorthand silently lost cells, and two paragraphs inside one list item ran together into a single word.
What changed
Import handles the six formats above from the Import button in the toolbar. Table cells now convert through the same path as the rest of the document, so a link stays a link and bold stays bold. The three faults the real documents exposed are fixed, and those documents are now part of what gets tested. PDF import reads text in reading order, works out headings from the sizes actually used in that document rather than an assumed scale, and detects two-column layouts by looking for a recurring column edge instead of guessing from gap widths.
What the numbers say now
You can open what you have. A document that converts imperfectly says what it could not carry rather than dropping it silently, which matters more than the conversion being flawless.
Pick a colour scheme in one click
Legend had a full theming pipeline and no way to reach it without hand-editing a theme file. There are now four built-in palettes, one click each, and the hue slider finally points at the colour it produces.
4built-in palettes: Deep Water, Harbor Paper, Beacon, and Submarine, which is the original Legend look, now named
41degrees the hue slider's thumb sat away from the colour it was actually selecting, before this release
2themes in each palette, light and dark, both authored rather than one derived from the other
0new theming machinery. The presets ride the pipeline that already shipped
What was wrong
Theming worked and was unreachable. Changing Legend's colours meant writing a theme file, which is a fine thing to offer an enthusiast and a poor thing to require of everyone else. The hue slider that did exist drew its thumb 41 degrees away from the colour it selected, so the control disagreed with its own output, and 0 degrees did not give you red. A slider whose handle misreports its value is worse than no slider, because you calibrate against it and it teaches you the wrong thing.
What changed
Four palettes ship as authored values rather than as a starting point the app then adjusts. Each carries a real light theme and a real dark theme. The hue slider starts at the current palette's own hue instead of a fixed default, its thumb renders the colour it selects, and 0 degrees means red. Picking a palette no longer writes a hue override, which had been a self-inflicted problem: the app was overwriting the choice you had just made.
What the numbers say now
Changing how Legend looks takes one click and no files. If you want to keep going from there, the slider now agrees with the result, so the manual route got better at the same time.
The FAQ said Legend has no telemetry. That was not true.
An audit of the two documents a new user actually reads found several claims that were false rather than merely dated. The worst one denied a feature Legend has, which is the kind of error that reads as a lie once you notice it yourself.
6integers in the entire telemetry payload, which is all it has ever been
30days after which telemetry consent expires on its own. It does not renew silently
2documents that both told you Ctrl+B opens the file browser. Ctrl+B is Bold
What was wrong
The FAQ answered "Is there any telemetry?" with "No. Legend makes no analytics or telemetry calls." Legend does make one, if and only if you opt in through a dialog that explains itself. The feature was built correctly: explicit opt-in, expires after 30 days on its own, and the payload is six counters and nothing else. The documentation was the problem, and it was the worst shape that problem takes. Someone opts in through a consent dialog, later reads that Legend makes no telemetry calls, and reasonably concludes they were lied to. Two other claims were also wrong: both documents said Ctrl+B toggles the file browser when Ctrl+B is Bold and a new user following that instruction bolds their text in their first minute, and the FAQ promised Legend "never rewrites, renames, or deletes your existing files" when it does all three at your direction. A promise like that should be exact or it is worth nothing. Bug reports were also still pointed at the wrong repository.
What changed
The telemetry answer now states what is collected, by name, and reads as a feature rather than a denial. Ctrl+B is documented as Bold, with the file browser on its real shortcut. The never-touches-your-files promise was rewritten to say precisely what Legend does and does not do. Shortcuts are no longer written as though Ctrl were universal, since they bind to Cmd on macOS and the docs cover a macOS install. Bug reports point at the repository the in-app reporter actually uses. The README's claim that the vault root is hardcoded to a personal folder was also removed; the first-run picker has existed for a long time.
What the numbers say now
The documents a stranger reads now agree with the software. Separately, and worth saying: Legend renders on Linux. The blank window some people hit was a WebKitGTK renderer issue rather than anything in Legend.
A menu that ran off the screen, and rows greyed out for no reason
Four things you could see and one you could not: a context menu with no height limit, file rows dimmed as though unusable, a button outline below the legibility floor, and a sort control wearing the filter icon because no sort icon existed.
0.68opacity being stacked onto file rows that are fully usable, saying unavailable about something available
1.46contrast ratio of the indexing banner's retry button outline, against the 3.0 the standard asks of a control boundary
What was wrong
The context menu had no maximum height, so a long menu fit on neither side of the cursor and ran past the edge of the screen. Files Legend does not manage were drawn at reduced opacity, which is the visual language for unavailable, while those rows are focusable and have real actions on them. The retry button on the indexing banner had an outline you could barely locate against its background. And the sort control in the file tree had been borrowing the filter icon, a drawing of sliders, because the icon set had no sort mark in it.
What changed
The menu is capped to the space available on the side it opened. The dimming came off the foreign-file rows entirely, because it was the whole defect rather than a value needing adjustment. The retry button moved to a stronger border colour. The icon set gained a real sort mark, chosen from a rendered comparison rather than picked from a description.
What the numbers say now
Long menus stay on screen, files you can act on stop looking disabled, and the sort button looks like sorting.
Pointing a link at a note you already have
Clicking an unresolved wikilink offered one thing: create a new note. Very often the note you meant was already in the vault, and the only way to reach it was to type its exact name into a box whose title and only button both said Create.
3ways review found this dialog could damage the text of a note, all closed before release
2of those three that were already present in 0.1.10, so this release fixes them for existing users
1ranking engine, shared with vault search rather than written a second time for this list
0words of your own sentence lost when a link changes what it points at
What was wrong
The capability was never missing. If you typed a name that resolved exactly, the old dialog would open that note. But nothing said so: it was titled Create and open note, its only button said Create and open, and it offered no suggestions at all. A capability you can only reach by guessing its exact input is not really a capability. Review then found three ways the dialog could damage a note. A chosen filename containing a bracket or a pipe wrote a link that cannot be parsed, so a reader saw raw brackets where a sentence used to be. Creating a note under a changed name left the original link pointing at nothing. And narrowing the list could slide the keyboard selection onto Create, so Enter made a file instead of a link. Two of those three were already present in 0.1.10.
What changed
Clicking an unresolved wikilink now opens a ranked list of notes that actually exist, seeded with the link text and scored by the same engine as vault search, with Create demoted to one row among the results. Choosing a note repoints the link and keeps the words you wrote, so a link written as Opening becomes a link to Opening Large Folders that still reads Opening on the page. A filename that cannot be expressed as a wikilink is refused by name instead of written badly. Creating under a changed name repoints the link before it navigates. Filtering resets the selection to the first result.
What the numbers say now
The most common thing you want from a broken link, connect this to the note I actually meant, takes one keystroke instead of an exact spelling. The three ways it could have quietly mangled a paragraph are closed, including the two that are not this feature's fault.
Blockquotes are body text again
Quoted prose rendered in the muted colour reserved for labels and hints, in the editor, in the reader, and in every HTML and PDF export. A quotation is something you read, so it now renders at full strength.
5.49 to 17.37contrast ratio of quoted text against the page, light theme, before and after
6.92 to 13.38the same measurement in dark theme
4places the dimming was deleted outright rather than pointed at a different colour
3earlier attempts that changed a rule which could never have applied
What was wrong
Two previous fixes recorded this as closed and neither changed a single pixel, which is the interesting part. The colour those fixes moved was itself an alias for the muted colour, so renaming what quote text pointed at moved a name and not a colour. The rule they edited also sat one layer above the thing that actually paints quoted prose, and a colour set on an inner element beats an inherited one every time. Exports were worse again: they carried a hard-coded grey with no connection to the theme at all.
What changed
The colour declaration was deleted at four sites rather than repointed, so quoted prose simply inherits body text. Callout bodies follow the same rule. HTML and PDF exports inherit the document's own text colour instead of their private grey.
What the numbers say now
A quotation reads like something worth reading, on screen and in a file you send someone. The general lesson, which cost three attempts to learn: a colour whose value is an alias for another colour cannot be reasoned about from the place it is referenced.
Making the dots bigger
Graph nodes stay the same size on screen however far you zoom, which is deliberate, and which means zooming has never made a node easier to see. Ctrl and the scroll wheel now scale the marks themselves, separately from the camera.
0.5x to 4xrange of the mark scale
1xwhat an install that never touches it renders at, identical to before
1.1xchange per notch of the wheel
2508 to 3365pixels of node ink measured on screen going from neutral to 1.77x
What was wrong
Node radius is constant in screen pixels by design, so the graph reads the same at any zoom level. The consequence nobody had addressed is that there was then no way at all to make a node bigger. Zoom moves the camera and the dots stay exactly where they were.
What changed
Ctrl and the wheel, or Cmd and the wheel, scale the marks between half size and four times, in steps of about ten percent. The setting persists per vault, and its absence means neutral, so an install that never uses it renders exactly as it did before. The plain wheel is untouched and still drives the camera. In an arrangement the dots scale and the labels stay at their authored size.
What the numbers say now
You can size the graph to your own eyes without moving the camera. One thing worth knowing on a Mac: macOS reports a trackpad pinch as a ctrl-wheel, the same event rather than a similar one, so no software can tell the two apart and a pinch drives the mark scale here rather than the camera. Camera zoom keeps the plain wheel and the zoom buttons.
Legend looks like itself now
The neutral greys were retuned cool, the interface chrome moved to Space Grotesk with JetBrains Mono beside it, and the default accent became sea glass. This changes how a stock install looks the moment you update it.
23neutral colour declarations retuned in the cool pass
2typefaces added to the interface, bundled with the app rather than fetched from anywhere
62 KBwhat both of those typefaces weigh in total
0custom accent colours overwritten: a hue you already chose is left exactly as it was
What was wrong
The interface had grown its palette one decision at a time and had never been designed as a set. The neutrals were warm where the rest of the app was not, the chrome used the same face as the prose it framed, and the accent was a blue chosen early and never revisited.
What changed
The neutrals were retuned as one cool family rather than adjusted individually. Interface chrome uses Space Grotesk and its monospace pairs with JetBrains Mono; the typeface your notes are set in is untouched. The stock accent is sea glass. All three are the defaults only: if you have already chosen your own accent hue, it stays, and links stay blue unless you turn on the option that colours them.
What the numbers say now
A stock install looks deliberate rather than accumulated. If you had customised anything, your choices survive the update.
A guardrail you cannot turn off is a wall
The accent controls were quietly walking your colour choice back whenever it failed a contrast rule. The slider stopped moving and said nothing, which reads as a broken control rather than as a policy. You can now switch that constraint off for colours you deliberately chose.
10 to 90percent range of the brightness control
50percent, the neutral point, where the authored palette is untouched
6.59 to 80percent of the available travel the brightness control actually moves, before and after
1checkbox, off by default, that releases the contrast floor on colours you moved yourself
What was wrong
Legend held a contrast floor on every piece of text, which is a real standard and a defensible default. What was not defensible was enforcing it silently against a deliberate choice: you would drag the slider, the colour would refuse to go where you put it, and nothing on screen said why. A control that will not do what you tell it, and does not explain itself, is indistinguishable from one that is broken. Separately, the brightness axis was moving the accent through only a fraction of its available range.
What changed
Brightness now mixes toward black and white around a neutral midpoint, so the control moves the colour through most of its range instead of a sliver. The contrast constraint remains on by default, so an install nobody touches still clears the floors it always did. A setting called Allow low-contrast accent colours releases it for colours you have deliberately moved. The limits of the display itself are a different matter and still apply, because those are a fact about the screen rather than a judgement about taste.
What the numbers say now
If a light orange on white works for you, you can have it. The shipped default is unchanged and still clears its floors. Two known pairings sit below the ideal ratio in dark mode and are recorded rather than papered over: a hovered selected row's label, and a link drawn on the accent colour.
Three things that did the opposite of what you meant
Dragging a tab to reorder it tore the tab out of the window instead. A quote mark anywhere in a search turned the rest of the box into one phrase. And clearing a pinned category in the graph immediately re-pinned the biggest one.
3gestures that produced the opposite of the obvious intent
0tabs torn out of the window by a reorder that stays inside the tab strip
1stray quote mark that used to be enough to glue an entire search into a single phrase
What was wrong
Tab reordering had never worked on Windows: the browser drag events were taken by the shell, and the code that handles the end of a drag read the unconsumed gesture as a request to tear the tab into its own window. In search, a quote anywhere toggled phrase matching, so typing a height like 5"6 searched for 56, and one unmatched quote swallowed everything after it. In the graph, unpinning a category fell straight back to the largest one, so nothing pinned was a state you could not actually reach.
What changed
Tab dragging is pointer-driven, shows where the tab will land, and tears out only when the pointer leaves the window edge. Quotes open a phrase only at the boundaries where a phrase can legitimately start, and are otherwise ordinary characters. Unpinning a category turns the orb off rather than choosing a new pin for you, and pinning one turns it on.
What the numbers say now
Reordering tabs reorders tabs. Searching for text containing a quote finds that text. Nothing pinned is a state you can be in.
Every sentence the app says, read back and ruled on
A pass over the user-facing strings, one row at a time, with each proposed rewrite either accepted, rejected in favour of the original, or replaced. Nothing was reworded because it sounded nicer; the test was whether the sentence promised only what the code actually does.
39strings reviewed individually
27 / 8 / 4shipped as drafted, returned to the original, and rewritten again by hand
46strings across 56 places in 15 files with their punctuation swept
0sentences left claiming more certainty than the mechanism behind them supports
What was wrong
Copy accumulates. Some of it was written to describe a mechanism that later changed, some was more formal than anything else in the app, and some stated an outcome more confidently than the code could guarantee. A close guard that could not tell a failed save from a deliberate close is the clearest example: the same sentence covered two very different situations.
What changed
Each string was reviewed on its own and ruled on rather than swept. A third of the proposed rewrites were rejected and the original kept, which is the point of reviewing them one at a time. Failing to save is now worded differently from choosing not to save. Import offers to import a copy, because that is what it does.
What the numbers say now
The app says accurate things in a consistent voice, and the eight rewrites that would have made it worse are not in the build.
Exporting a PDF on a Mac stopped taking the app down with it
The print job ran on the same thread that draws the window, so for as long as an export took, Legend was not slow, it was not running. The job now runs on its own thread and waits on a bounded clock rather than indefinitely.
1,959 of 1,959stack samples landing on the main thread in the version before this one, which is why the window froze
70sceiling on how long an export waits before it reports, instead of continuing to wait
4supporting changes the one-line fix could not ship without
What was wrong
Exporting a PDF ran the whole print job on the thread that draws the interface. A stack sample taken on the previous version put every one of its 1,959 samples on the main thread, so the app was not merely busy, it was not repainting. On a fast local folder that reads as a pause; into a cloud-synced folder it reads as a hang, because the export was also waiting on a filesystem that had gone away to think about it. Two earlier attempts reasoned from what the API's name implied about threading rather than from a measurement, and both were wrong about which thread was doing the work.
What changed
The print job is allowed to spawn its own thread, together with the four pieces that change could not ship without: a completion that can arrive later and from somewhere else, a channel to carry it, a watcher for the late case, and a teardown when the job ends. The export waits on a bounded clock, and a wait that runs out says so rather than continuing.
What the numbers say now
Exporting a PDF on a Mac leaves the app usable while it works, and the worst case is a message rather than a window that has stopped repainting. Confirmed by hand on an M1 Air, which is the only instrument there is for this: no test runner launches the app on macOS, so nothing automated exercises this path.
Less interface, and a graph you can read at scale
Recents and Bookmarks can be removed from the sidebar rather than merely collapsed, the graph Menu stops shrinking below a usable height, analytics sections stay where you left them, and node titles get a halo so they survive a dense graph.
2sidebar sections that can now be removed outright, not just collapsed
1.81 to 4category rows the graph Menu is guaranteed to show when the panel is crowded
6analytics sections that now stay as you left them through a refresh
19,967links in the vault where node titles had become unreadable
What was wrong
Collapsing Recents and Bookmarks left their headers and the space around them, so the sidebar never actually got quieter. The graph Menu could be squeezed down to roughly 1.8 category rows before anything would scroll. Analytics sections reopened or closed themselves after a refresh, so a section you had opened would not stay open. And node titles were painted straight onto the edges behind them, which is fine on a small graph and not on a real one.
What changed
A toolbar toggle and a command palette entry remove Recents and Bookmarks entirely, and the preference persists. The Menu now carries its own minimum height derived from its actual row heights rather than sharing one floor with its neighbours, and the rail scrolls when the open sections outgrow it. Analytics sections remember their state for the session and start collapsed rather than all open at once. Node labels draw a halo before their text.
What the numbers say now
The sidebar can be as quiet as you want it, the Menu stays usable on a short window, and titles stay readable on a graph with twenty thousand links in it.
Faint text is no longer faint by accident
Hints, counts and badges across the editor, the graph panels and Settings were dimmed by lowering their opacity, which quietly pushed them under the readable contrast floor — worst in light mode. They now use a colour calibrated to stay quiet and stay legible.
250pieces of on-screen text measured against the WCAG floors, in both themes
34that were failing the floor before this release
0still failing, now checked automatically on every run
What was wrong
De-emphasised text was made quiet by turning down its opacity on top of an already-calibrated colour. Opacity blends toward whatever sits behind it, so the result depended on the surface rather than on the palette — and blending toward a near-white page loses contrast faster than blending toward a dark one. Every failure was this same mechanism; none was a badly chosen colour. Several were readable in dark mode and not in light.
What changed
The dimming moved from opacity to a dedicated colour, calibrated just above the readable floor so the text stays as quiet as it can while remaining legible. A new automated checker measures the actual rendered pixels — including panels that blur the graph behind them, where the contrast cannot be calculated from the stylesheet at all — and the release now fails if any measured text drops below the floor.
What the numbers say now
Small print reads as small print rather than as a smudge, in both themes, and a future change cannot quietly undo it.
Back now undoes moving a node
Dragging a node into place inside an arrangement was invisible to the graph's Back button, so there was no way to step out of a move you did not want. Each drag is now one history step, and Back returns the node to where it was.
1history step per drag — verified by measuring the nodes actually moving on screen
What was wrong
The history entry was only recorded when the graph was in a layout mode that saves positions to disk. An arrangement deliberately does not save, so a drag inside one recorded nothing — even though the graph's Back and Forward are in-memory and never needed the saved copy. The rule that blocked it belonged to a different feature.
What changed
A move is recorded whenever a drag actually happened, and restoring it puts the positions straight back onto the graph rather than through the saved layout it never used.
What the numbers say now
Move a node, change your mind, press Back, and the node goes back. Forward does not yet reliably replay the move: the restored position can be overwritten by the arrangement recomputing its grid. That half is fixed and lands in the next release, and this entry will say so when it does.
Finding a category in the graph Menu
The Menu listed every category, but with no way to search it you could not reach one you could already name. It now has a filter, a count of what matched, and a way back when nothing does.
36automated checks on the new filter, run against the real app rather than a simulation
What was wrong
The list was always complete — that was never the gap. With hundreds of categories and no filter, search or sort, the only way to a specific one was scrolling. The first row of the list also wrapped its controls onto a second line while its neighbours stayed on one, because the row's width varied with its count and the list is sorted by count.
What changed
Type to narrow the list, with a readout of how many of how many matched and a one-key clear. Matching is forgiving about accents and case when your query is plain, and exact when it is not — type marks and you are taken at your word. Every row now reserves the same width for its count, so the rows wrap together or not at all.
What the numbers say now
You can find a category by typing its name, and the list stops rearranging itself around whichever row has the biggest number.
The graph panel can no longer crush its own menu
Opening enough panel sections used to squeeze the Menu card into an unusable sliver. Every section now keeps a working minimum height and the rail scrolls the overflow instead of mashing what is already open.
2menu rows guaranteed visible at the default sidebar width, however many sections are open
21live checks on the new floor-and-scroll behavior, including the worst section pairing
What was wrong
Panel sections shared one column and split it evenly under pressure, so a busy panel drove the Menu below the height of its own rows. A later style layer silently overrode the minimum that was supposed to prevent exactly this, which is why earlier floor fixes never held.
What changed
The minimum height is now applied in the style layer that wins, per card, and the Menu derives its floor from its actual chrome and rows. When the open sections outgrow the rail, the rail scrolls.
What the numbers say now
You can open every section at once and still read and click the Menu. The guarantee is pinned by automated checks so the crush cannot quietly return.
The program is finally named Legend
The installed executable was still called app.exe, a leftover from the project template. It is now Legend.exe, and the installer migrates existing installs in place: old binary removed, shortcuts rewritten, nothing to reinstall.
Legendwhat Task Manager and Activity Monitor now show, instead of "app"
What was wrong
Every install ran a process named app.exe, which is what showed in Task Manager, security prompts, and anywhere else a process name appears. It looked generic because it was the template's default, never changed.
What changed
The build names its output Legend (Alegend for the paid flavor), the installer migration deletes the old binary and rewrites shortcut targets during an ordinary update, and file associations repair themselves only when they provably pointed at our old binary and it is provably gone.
What the numbers say now
The app you installed is named after itself everywhere the system names it.
Every destructive action now asks first, because none of them did
Six paths that should have confirmed before discarding your work never asked: closing with unsaved changes, closing a dirty tab, switching vaults with edits pending, and worst, overwriting a file another program had changed. All of them now ask, and any non-answer means no.
6destructive paths that silently proceeded, now confirmed
20notifications that were invisible in popped-out windows, now delivered
0remaining uses of the broken dialog path, pinned by an automated check
nowhat any unanswered dialog resolves to: closing, Escape, clicking away
What was wrong
The system dialog layer these confirmations relied on returned an always-affirmative answer, so every one of them proceeded as if you had clicked yes. Closing a window with unsaved changes discarded them without asking, and a save conflict overwrote the other writer's version silently. Popped-out windows had a related gap: their notifications went nowhere.
What changed
Legend now ships its own confirmation dialog. Every non-answer, closing the dialog, pressing Escape, clicking outside it, resolves to no, and the save-conflict path is wired so that a missing or failing dialog can never overwrite: the file keeps the other writer's version unless you explicitly choose otherwise. Notifications in popped-out windows render in the window itself.
What the numbers say now
Nothing is discarded or overwritten without a question you actually saw. An automated check pins the count of broken-dialog uses at zero so the class cannot quietly return.
A command that declines now says why, instead of doing nothing
With editing locked, every formatting command failed in silence, indistinguishable from being broken. Formatting, table, and link commands now announce why they declined, and disabled graph controls state what would activate them.
23formatting commands that inherit the decline notice from one shared path
7locked-editor gestures that now announce instead of ignoring you
1real lock escape closed: pasting an image wrote to the vault while locked
3investigations in one hour that silence cost during live testing, the reason this exists
What was wrong
A command that correctly declines to act, because editing is locked, or the caret is not in a table, looks identical to a command that is broken. Live testing hit that ambiguity three times in one hour, and each time the app was right and simply refused to say so.
What changed
The decline notice lives at the one path all formatting commands run through, so every command inherits it: locked editing says so, a table command with no table under the caret says so, and a command that ran but changed nothing says so. Disabled history and view controls got tooltips naming their activating condition. The sweep also found pasting an image bypassed the editing lock entirely, and closed it.
What the numbers say now
Locked is a state the app tells you about, not one you diagnose. The next silently-declining command fails an automated source check before it ships.
Deleting a file no longer freezes the app on a big vault
Deleting from the navigator rebuilt the entire vault index synchronously before returning, with no feedback: seconds of apparent hang on a real vault. Delete, duplicate, and new-folder now update only the affected entries.
201msformer rebuild cost per 1,400 files, and it grew with vault size
2path tricks the new delete guard refuses: links that resolve elsewhere, and names Windows silently rewrites
0deleted rows left on screen afterward
What was wrong
Every delete paid a full index rebuild whose cost scaled with vault size, so on a multi-thousand-file vault the app sat frozen for many seconds with nothing on screen to say why. Duplicate and new-folder paid the same tax.
What changed
All three now go through the same incremental index update the rename path already trusted. The delete path also gained a guard that resolves the real target before acting: a link that points outside the vault, or a name Windows would silently rewrite to the vault root, is refused instead of trashed.
What the numbers say now
File operations return immediately at real vault sizes, and a delete can only ever remove the thing you clicked.
The Mac round: shortcuts, icons, and PDF export that cannot ruin your file
Every Option-based shortcut was dead on macOS, the icon set shrank relative to everything at larger UI scales, and PDF export into a cloud-synced folder could hang the app and leave a blank file at your chosen path. All three fixed.
37icons moved to relative sizing so they scale with the interface
60shard ceiling on any PDF export before a clear timeout message
0ways a timed-out export can write a blank PDF over your chosen path
2toggles that now announce on screen: focus mode and typewriter scroll
What was wrong
On macOS the Option key composes special characters, so every keyboard shortcut built on it never reached the app. The icon set was pinned to pixel sizes and fell behind at larger UI scales. PDF export into iCloud or OneDrive folders could wedge for minutes with no cancel, and a late-completing export could overwrite your target file with an empty document.
What changed
Shortcut matching now recognizes both what the key says and what it composes, on macOS only, with recorded custom shortcuts preserved. Icons size in the same units as the interface. PDF export writes to a temporary file and renames onto your path only on success, carries watchdogs on every waiting stage, and refuses re-entry while an export is running.
What the numbers say now
Mac shortcuts fire, the interface scales as one piece, and the worst outcome a stuck cloud folder can produce is a clear timeout message, never a ruined file.
The first five minutes: ranked link completion, frontmatter, an example vault
Typing [[ now ranks suggestions by recency and match quality instead of dumping the vault alphabetically, a palette command adds frontmatter to any note, and the vault screen offers a ready-made example vault to explore.
3ranking signals for [[ completion: recency, match quality, then depth
~2msranking cost per completion open at a multi-thousand-note vault
36notes in the example vault, built to light up search, links, and the graph
1command to add properties to a note that has none
What was wrong
On a real vault, [[ opened an unranked alphabetical dump with deep folder paths visually drowning the note names. Adding a property to a note without frontmatter meant hand-typing YAML on line one, which nothing explained. A new user opened an empty app with nothing to explore.
What changed
Link completion ranks by what you touched recently, how well the name matches, then path depth, and the note name now leads the row with the path trimmed to fit. Add frontmatter property in the command palette inserts a correct block at the top of the note with the cursor on the key. Example Vault on the vault screen clones a 36-note starter into a folder you pick, and never overwrites anything that exists.
What the numbers say now
The most knowledge-base gesture in the app produces a useful list on the first keystroke, and there is a working vault to poke at before you commit your own notes.
Every message in the app is now a written sentence
A full pass over the strings a user actually meets: 29 placeholder messages replaced with authored copy, two overlapping error libraries collapsed into one message per error, and the vault-open dialog now says what it will actually do.
29 → 0placeholder strings compiled into the app
2 → 1error libraries; one authored message per error
95shipping strings restyled in the same pass
13error sentences rewritten from scratch
What was wrong
Parts of the interface still carried developer placeholder text, and two separate error libraries meant the same failure could surface two different ways, neither written for a person.
What changed
Every placeholder was replaced with copy written for the screen it appears on. The error libraries were merged so each failure has exactly one message, and the vault-open choices name what each button does, including the honest fine print: opening just the notes still indexes all files, so image embeds keep working.
What the numbers say now
The app explains itself in plain sentences. No string a beta user meets was generated or left over from development.
The graph can now answer questions, not just draw itself
An analytics panel for the graph: which notes hold your vault together, how connected your groups really are, and which notes are related to the one you are focused on, computed from your actual links.
0extra influence a duplicate link buys
129automated checks driving the panel end to end
2relatedness signals per focus note: co-citation and shared references
100%of on-screen rankings reproducible from the reported counts
What was wrong
A graph view shows shape but answers nothing. Which notes are structurally load-bearing, whether a topic cluster actually links across to the rest, and what is related to the note in focus were all judgment calls made by eye.
What changed
Influence ranking runs over the directed link graph with duplicate links counted once. Group connectivity reports internal, cross-group, and unclassified link counts over any tag or property you pick. A focus note gets related notes by co-citation and shared references. When a dimension cannot be answered honestly, such as notes carrying multiple values, the panel refuses with a counted reason instead of shading the truth.
What the numbers say now
Rankings are computed on exact integer scores, so the order on screen is exactly reproducible from the numbers shown. A refusal is a real answer; an empty result and a cannot-answer are never conflated.
Reading and writing posture: eight additions, zero cost if you leave them off
Line spacing, reading width, focus mode, typewriter scroll, a per-vault startup note, a random-note command, an image lightbox, and footnote completion. An untouched install renders exactly as before.
430px → ~1pxcursor drift while typing, with typewriter scroll on
8additions in this batch
0rendering changes for an install that touches none of them
29new automated checks on this layer
What was wrong
Long writing sessions pushed the cursor to the bottom of the window, reading width and line spacing were fixed, and there was no way to jump to a random note or view an image at full size.
What changed
Typewriter scroll holds your typing line steady. Line spacing and reading width are settings under Appearance, gated so defaults render byte-identical to before. Focus mode dims everything but the current block. The startup note is stored per vault, so configuring one vault never changes what another opens. Footnote completion knows where it is: it will not insert a definition inside a code fence or frontmatter.
What the numbers say now
Measured with the feature on, the typing line drifts about one pixel where it previously drifted the height of the window. Everything here is opt-in.
Rename stopped rebuilding the whole index, and two standing annoyances fell
Renaming a file was re-indexing the entire vault while the dialog was still open, and it got slower the more you wrote. It is now a targeted update, 26 times faster at 1,400 files.
202.8ms → 7.7msrename cost over a 1,400-file vault
26×speedup, and it no longer grows with vault size
1window raise that finally works from the taskbar
0permanently cached dictionary failures
What was wrong
Rename and move triggered a full synchronous index rebuild that scaled linearly with vault size. Opening a markdown file while Legend was minimized did nothing visible. A single failed spellcheck dictionary fetch was cached forever, leaving spellcheck silently dead until restart.
What changed
Rename and move now update only the affected index entries, through the same code path the file watcher already trusted. The single-instance file-open path unminimizes the window before focusing it. Dictionary fetches retry with a bounded backoff instead of caching the first failure permanently.
What the numbers say now
File operations are effectively instant at real vault sizes, double-clicking a file wakes a minimized Legend, and spellcheck recovers from a bad network moment on its own.
Opening a vault should not get quadratically slower
At 50,000 files, set_vault took 19.1 seconds. A scale harness isolated 28.5 seconds in tree derivation alone. The rewritten derivation completes that phase in 66 milliseconds.
19.1sset_vault at 50,000 files
28.5s → 66mstree derivation before and after
745MBRSS at 120,000 notes before the guard
205,357files in the folder that killed the app
What was wrong
The file tree was derived with repeated work that grew quadratically. Separately, Legend would attempt to index any folder without warning; a 205,357-file development tree hid only 3,504 markdown files but still pushed the process into a hard failure.
What changed
Tree derivation now builds its hierarchy in one pass instead of repeatedly rescanning the path set. Before a very large folder opens, Legend counts what it will walk and reports the real total. Notes-only mode skips known build and dependency directories before the index reads them, the part that actually bounds memory, and the user can still continue with the full folder.
What the numbers say now
The measured tree phase fell from 28.5 seconds to 66 milliseconds. The size guard turns the previous silent crash into an informed choice and reports what pruning actually saved instead of pretending the approximation is perfect.