Skip to content

feat: the vault, whiteboards, graph, stickies, agents, and the license (0.9.0) - #54

Merged
jamubc merged 86 commits into
mainfrom
0.9.0-pre
Oct 1, 2026
Merged

jamubc merged 86 commits into
mainfrom
0.9.0-pre

Conversation

@jamubc

@jamubc jamubc commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Why

InstantNotes keeps notes in a SQLite library that only InstantNotes can read. That is fast and safe, but it asks the user to trust a single opaque file with everything they write, and it makes the library a dead end: nothing else can open it, and nothing carries it to another machine. 0.9.0 answers that directly with a Vault, a plain Markdown folder the app keeps in step with the library, readable in any editor with the app closed.

The release then widens what a note can be. A note has been one thing since 0.1.0, a Markdown document, so anything spatial had to live outside the app. Whiteboards make a note a canvas without making it a second class of object: a board is still a note, still searchable, still tagged, still exported. Graph answers the companion question, which is what the library looks like as a whole rather than as a list.

It also lets a note leave the library: a sticky note sits on the desktop in its own window, Apple Stickies come in as notes, and an agent (Claude Code, Codex, Cursor) can read and, if allowed, write notes over the Model Context Protocol, visibly and without overwriting what the user is typing. And it is the first release under the PolyForm Strict license and the Jam-Sw EULA (#58), which every user now agrees to before using the app.

The rest is debt this growth exposed. Images accumulated with no way to reclaim the space. The two halves of the app named error codes and events in two places that could silently drift apart. Updates arrived as a modal dialog that interrupted the user to ask a question they could not act on yet.

What Changes

  • Vault: a one-way export, then a live mirror. Every note becomes a Markdown file carrying its tags and Spaces, attachments come along, trashed notes move to a trash folder, and "Check vault" confirms each file matches its note. Schema v5 tracks the mirror. The app writes the folder and does not read it back yet, so the app stays the place you edit.
  • Whiteboards: a note can be a freeform Excalidraw canvas. Schema v4 adds the surface mode, v6 stores a board in the vault as a standard .excalidraw file beside its note. Text on a board stays searchable and its #tags still tag the note. Creating one is now visible in the note list rather than only in the File menu and the command palette.
  • Graph: a sidebar view drawing notes, tags, and Spaces and the links between them, entered around the note you have open, with click-through to a note, a tag filter, or a Space.
  • Image lifecycle: deleting a note for good deletes the images only it used, and Settings > Images reports and removes images no note references. Images held by notes in Trash or Archive are always kept.
  • Update as a place: the update dialog becomes an Update Space at the top of the sidebar holding the target version, the current one, the size delta, the release notes as a readable note, and the install button. Nothing blocks the window and nothing has to be dismissed.
  • License and EULA: on first launch, and when either gets a new version, the library opens into a License Space holding both as notes, each agreed on its own; until both are, the rest of the app, every shortcut, capture, and stickies stand down. It renders the shared agreement gate from installers (installers#3), which also supplies the final installer art.
  • Sticky notes: any note or whiteboard pops out into its own window that floats, sits like a window, or lies on the desktop, and comes back with its edits on disk. The sticky is the note's only editor in the app while it is out.
  • Agent access: instantnotes mcp serves the library over MCP (2026-07-28 stateless and 2025-11-25), off until Settings > Agents turns it on. Every save names the version it was based on, so an agent's write is never silently overwritten: an idle note takes it in place, and typing wins with a "Restore theirs" toast, in the library and in stickies alike.
  • Apple Stickies import (macOS): Settings > Import previews every sticky in its color and brings the chosen ones into a Space with formatting, images, and dates; importing again brings in only new ones.
  • Settings: a dashboard front page with live library counts, plus Images, Editor, Links, Vault, and Feedback pages.
  • One registry per language: error codes and event names each live in exactly one place per half of the app, with a test holding the two halves equal.
  • Fixes: a newer-schema library shows a message instead of aborting at launch, pre-release whiteboard libraries open again, per-note editor state no longer leaks between notes, Linux window corruption, the release script's Windows path guard, and titles no longer carry bold markers or a leading image's path. The dashboard's "What's new" also reads the changelog on a Windows checkout, where the file arrives as CRLF.

ADDED Requirements

Requirement: the vault mirrors the library without owning it

The app SHALL keep every non-deleted note as a Markdown file in the chosen folder, carrying its tags and Spaces, and MUST treat the SQLite library as the source of truth. A vault write failure MUST NOT fail the note write that triggered it.

Scenario: a note edit reaches the folder

  • GIVEN a live vault configured at a folder the user chose
  • WHEN the user edits a note and stops typing
  • THEN the matching Markdown file is rewritten a moment later with the new body, its tags, and its Spaces, and the note is saved in the library whether or not that file write succeeded

Requirement: a whiteboard is a note, not a separate object

A note SHALL be either a document or a whiteboard, and a whiteboard MUST remain subject to every behavior a note has: search, tagging, Spaces, trash, and export. Converting a document to a board MUST carry its text onto the canvas.

Scenario: searching for text written on a board

  • GIVEN a whiteboard note with the word "harbour" written in a text element and the tag #ports on the canvas
  • WHEN the user searches for "harbour"
  • THEN the whiteboard note appears in the results and is reachable under the #ports tag

Requirement: a whiteboard can be created from the note list

The note list SHALL offer creating a whiteboard from its create control, so a board can be started without knowing a keyboard shortcut or opening the command palette. The plain create action MUST continue to create a document in one click.

Scenario: starting a board from the list

  • GIVEN the note list toolbar
  • WHEN the user opens the create control's menu and chooses New whiteboard
  • THEN a new whiteboard note is created and opened on its canvas, and the menu shows the Cmd+Shift+N shortcut that does the same thing

Requirement: removing images never removes one still in use

Deleting a note permanently SHALL delete only the images no surviving note references. Images referenced by a note in the Trash or the Archive MUST be kept, because those notes can be restored.

Scenario: emptying the Trash with a shared image

  • GIVEN two notes embedding the same image, one deleted permanently and one in the Archive
  • WHEN the permanent deletion runs
  • THEN the image file remains on disk, because the archived note still references it

Requirement: the license and the EULA are agreed before use

The app SHALL show the source license and the EULA in full and MUST require a separate agreement to each, at its current version, before any window, shortcut, or menu command does its work. Declining SHALL quit.

Scenario: first launch

  • GIVEN a device that has agreed to nothing
  • WHEN InstantNotes starts and the user presses the capture shortcut
  • THEN the library shows the License Space with both documents, and the capture panel only offers to open the library, until both are agreed

Requirement: an agent never silently overwrites the user

A save SHALL name the note version it was based on. When another process wrote in between, the user's typing MUST win and the user MUST be offered the other version back, in the library and in a sticky window.

Scenario: an agent edits a sticky while the user types

  • GIVEN a note open as a sticky with unsaved typing
  • WHEN an agent appends to that note over MCP and the sticky then saves
  • THEN the typing is saved, and a toast names the agent with Restore theirs

MODIFIED Requirements

Requirement: a newer library is refused without crashing

Opening a library whose user_version is past the last migration this build knows SHALL surface a message telling the user to update. The app MUST NOT abort at launch.

Scenario: opening a library from a newer build

  • GIVEN a library written by a later version of InstantNotes
  • WHEN the app launches against it
  • THEN the app stays open and explains that the library needs a newer version, instead of terminating

Requirement: an update informs rather than interrupts

An available update SHALL be presented as a Space in the sidebar and MUST NOT open a modal dialog or require the user to dismiss a reminder. Installing MUST remain the user's explicit action.

Scenario: an update arrives while the user is writing

  • GIVEN the user is typing in a note when an update is found
  • WHEN the updater reports the new version
  • THEN an Update Space appears at the top of the sidebar with a green marker and the editor keeps focus, with nothing to dismiss

Requirement: the dashboard reads the changelog on every platform

The bundled changelog SHALL parse identically whichever line ending the checkout produces. A CRLF copy MUST yield the same release, sections, and items as an LF copy.

Scenario: a Windows checkout renders What's new

  • GIVEN a working copy where CHANGELOG.md was checked out with CRLF endings
  • WHEN the Settings dashboard looks up the running version's release notes
  • THEN it finds the section and lists its bullets, exactly as it does on macOS and Linux

Verification

  1. Build gates, mirroring .github/workflows/build.yml:
    1.1 npm run check passes: 636 files, 0 errors, 0 warnings.
    1.2 npm test passes: 63 files, 553 tests, including the ten shared agreement-gate cases.
    1.3 cargo test --workspace --manifest-path src-tauri/Cargo.toml passes: 282 tests.
    1.4 cargo clippy --workspace --all-targets --manifest-path src-tauri/Cargo.toml -- -D warnings is clean.
    1.5 cargo fmt --all --check --manifest-path src-tauri/Cargo.toml is clean.
    1.6 All three platform jobs pass, including windows-latest.
  2. Migrations, which run against real user libraries:
    2.1 A 0.8.0 library (schema v3) opens and applies v4, v5, and v6 with every note intact.
    2.2 A library touched by a pre-release whiteboard build opens instead of being refused, and its boards render as whiteboards.
    2.3 A library at a schema past v6 shows the update message and does not abort the app.
  3. Vault behavior:
    3.1 Turning the vault on writes every note, and "Check vault" reports no mismatch.
    3.2 Editing a note updates its file; trashing one moves the file to the vault's trash folder.
    3.3 A whiteboard writes a .excalidraw file beside its note.
  4. Whiteboards:
    4.1 The note list create control makes a document on click and offers New whiteboard from its menu, as does a right-click on the control.
    4.2 Cmd+Shift+N and the File menu still create a board, and the palette still converts a note.
    4.3 Text written on a board is returned by search, and a #tag on the canvas tags the note.
  5. Agents:
    5.1 The real binary's MCP replies validate against the official JSON Schema of 2026-07-28 and 2025-11-25.
    5.2 A write while access is Read is refused; with Read and write, a created note lands in its Space and is queued for the vault.
  6. Licensing:
    6.1 installers/scripts/apply.mjs reports InstantNotes already up to date, so LICENSE.md, the EULA, the gate, and the art match installers main.
    6.2 dev-standards/org-check.sh passes every licensing and gate rule for InstantNotes.
  7. Release metadata:
    7.1 package.json, src-tauri/tauri.conf.json, and src-tauri/Cargo.toml all read 0.9.0. The core crate versions independently by design and stays at 0.8.0, per scripts/bump-version.mjs.
    7.2 CHANGELOG.md dates 0.9.0 as 2026-09-30 and the dashboard's "What's new" finds that section for the running version.

jamubc added 30 commits July 11, 2026 23:31
Post-0.8 batch addressing accumulated user feedback.

Added:
- Settings front page is now a dashboard: live library stats (notes, tags,
  Spaces, attachments, capture readiness, trash) and the installed version's
  release notes parsed from the bundled changelog.
- Images settings page with copy-in vs link-original storage, a preview height
  control, and attachment location, count, and size. An Image button in the
  editor toolbar inserts from a file honoring the storage mode; linked files
  render through a narrow per-file asset scope rather than widening it.
- Editor settings page with a "show exact save time" toggle; the exact save
  time, to the minute, is always available on hover over any note date.
- Contexting image handling with an absolute-path default, so copying a note as
  context can hand a tool the real image path, keep the reference, or drop it.
- In-app feedback: a Feedback page that saves a local record and opens a
  prefilled GitHub issue, backed by a submit_feedback command.

Fixed:
- Stray caret when switching notes: each note now loads a clean editor state, so
  no caret lingers in a note being viewed, and undo no longer reaches into the
  previously open note.
- Links set to open with Cmd/Ctrl+Click now show the pointer cursor while the
  modifier is held, so they read as clickable.

Verified: svelte-check clean, vitest (298) green, cargo test green, build ok.
Only the bump-version half of 0decff9; its NoteEditor hunk cleaned up a
duplication introduced by the whiteboard merge and is not needed here.
Default WEBKIT_DISABLE_DMABUF_RENDERER=1 on Linux in both the app entry
point and the dev script, without overriding an explicit setting; the
WebKit compositor otherwise renders a blank or corrupted window on many
Linux systems. README gains Linux prerequisites and Arch/CachyOS build
notes.
SEQUENCE.md defines the order work lands in and the definition of done that
gates each unit. It records that everything between v0.8.0 and this branch is
unfinished: ten streams, none of them decided, including a whiteboard backend
that migration v4 already has a column for.

feat-portable-vault-sync carries the vault and sync design, blocked on that
decision.
The branch held ten unfinished streams, seven of them entangled in the already
pushed 44bea94, so those are finished in place as four grouped changes. The
whiteboard was local-only and comes off cleanly onto feat/note-whiteboard,
taking migration v4, three runtime deps, and four defects out of the release.

Sequencing it after the vault makes its sidecar an additive file type instead
of a user-data migration.
Settle the props on SegmentedRow and ToggleRow, give them the keyboard and
focus behavior their roles promise, and move the row shell each of them was
carrying a copy of into one PrefRow component. SegmentedRow is a real radio
group now: the selected segment is the only tab stop, the arrow keys move and
wrap, and Home and End reach the ends.

The Links page hand-rolled both controls and now uses them, which is what
proves the API holds before three more settings pages depend on it.
Describe library_stats and every field it returns, and say where each number
comes from: seven counts out of the store, and the two attachment numbers off
the filesystem, where a missing directory reports zero rather than failing the
whole call.

Cover the two changelog states the dashboard has to render, an undated heading
and a version with no section at all, and correct the release note to the tiles
that actually ship.
Tick the tasks that are done and write down what was settled: the frozen row
API, why it carries no error state, and where the row shell now lives. The one
task left needs a running app, so it says so.
cargo test, npm test, and npm run check all pass clean. The live
dashboard-number check and the archive step stay open for jam to verify
in the running app.
The "Insert image..." toolbar action hand-wrote the same
attachments/<name> markdown that editor/images.ts already builds for
paste and drop. Reuse that helper so the two insertion paths produce
their reference from one place instead of two copies that happen to
match.
…exting

SettingsImages.svelte (210 lines) and the imagePrefs store it reads from
were both untested. imagePrefs.init() gates a single settings read
behind a private flag, which breaks Svelte's effect context under
vi.resetModules() + dynamic import, so it's covered separately from the
mounted component: images.svelte.test.ts drives init/persistence/clamping
directly, SettingsImages.test.ts covers what the page renders and
persists.

Also extends contexting-format.test.ts: a linked image (an absolute
path, not an attachments/ reference) now has explicit coverage across
keep, absolute, and strip, alone and alongside a copied image.
import_image_file, allow_image_file, and open_attachments_folder were
missing from the attachments section. Also notes how link mode's
absolute-path reference relates to the vault portability requirement,
which feat-portable-vault-sync's serializer will need.
…mage-handling

Resolves the open decision feat-portable-vault-sync deferred to this
unit: link mode survives as a live-editing convenience, and the vault
serializer materializes a copy into attachments/ at flush time rather
than carrying the vault's portability guarantee on a path that can
break on another device. Recorded in tasks.md with the reasoning, since
the vault serializer reads it.

Every task in the unit is done and its suites are green, so the change
moves to openspec/changes/archive/.
An earlier truncated terminal read missed the 22-test migration_tests
module. The real count was always 92 Rust tests, not 70.
feedback.jsonl accumulates every submission with nothing surfacing it
to the user, unlike attachments, which already had "Open attachments
folder". Add open_feedback_log, mirroring that command: it reveals the
file in the OS file manager rather than opening it, creating an empty
file first if none exists yet. Wire a "Reveal saved feedback" button
and a line on the Feedback page stating nothing is pruned automatically.

Also fixes the send() error handling: a GitHub hand-off failure after
a successful local save used to report "Couldn't send feedback", which
is wrong since the feedback was saved. The two failures are now
distinguished, and the draft only survives a local-save failure.
Only the title was capped; a long feedback body had no limit, and
open_url hands the result to the OS shell, whose URL length tolerance
is tighter than a browser address bar in some environments. The
message is now truncated past 1500 characters with a note that the
full text is saved locally; the diagnostics block is never truncated.
SettingsFeedback.svelte (185 lines) was untested. Covers category
switching, empty-body rejection, submit success, a local-save failure
that keeps the draft, a GitHub-hand-off-only failure that still clears
it, and the reveal-log button. feedback.test.ts gains coverage for the
new body-length cap, including that diagnostics survive truncation.
The Feedback section was missing entirely. Documents FeedbackInput's
shape and states plainly that no note content ever reaches this path.
…app-feedback

Recorded: no pruning (a submission is bytes, not the megabytes
attachments needed a different answer for), but revealed, since
invisibility was the real gap the proposal called out. Reasoning is in
tasks.md.

Every task in the unit is done and its suites are green, so the change
moves to openspec/changes/archive/.
Neither existing suite exercised loadDoc's fix for the stray caret and
undo-reaches-into-the-previous-note bug: library.svelte.test.ts's
"undo" coverage is workspace-delete undo, and kernel.test.ts's
"caret" coverage is fold behavior over a headless state. Mounts the
real component with a real EditorView in jsdom, recovers it via
EditorView.findFromDOM, and switches notes on the same instance via
rerender rather than a fresh render, which would mount a new view and
pass trivially either way.

Verified by temporarily reverting loadDoc to reuse state across notes
and confirming the test fails against it before restoring the fix.
editorPrefs.init() reads three settings, then assigns all three
unconditionally once the read resolves. A setter called after init()
started but before it resolved (setShowExactTime, setZoom,
toggleToolbar) got silently overwritten back to the old persisted
value moments later. Guard each field with a #touched set so a live
change always wins over a slower stale read.

Found via a test asserting exactly this sequence in
editor.svelte.test.ts (6 tests), which also covers init()'s
read/default/error-fallback behavior directly, since a component
mount can't drive a #loaded-guarded singleton's read path without
breaking Svelte's effect context under vi.resetModules().
SettingsEditor.svelte (57 lines, one toggle) had no test.
The cursor feedback for the two link-open modes that need Cmd/Ctrl
(preview + modclick, and edit mode) depends on ModKeyCursor toggling
cm-mod-held correctly; linkMarkClass's existing tests only proved
which modes lack cm-link-clickable; the fallback mechanism itself was
untested. Covers meta/ctrl detection, release, window blur, and
cleanup on destroy so a stale modifier state can't leak into the next
note.
Every task is done: the missing regression test exists and was
verified to actually catch the regression, SettingsEditor and
editorPrefs are tested (surfacing and fixing a real race in the
process), and the modifier-held cursor mechanism is covered across
all three link-open modes. Suites are green, so the change moves to
openspec/changes/archive/.
The prior commit's git add ran against both the new and old
(already-moved, nonexistent) paths in one call; git appears to reject
the whole add when one pathspec doesn't match, so it silently staged
only the earlier git-mv snapshot rather than the fully-edited content
sitting in the working tree. Same content this unit's task list
always described; only the commit that carried it was wrong.
Opening a database with a user_version past the last known migration
returned an error, but the setup hook let it flow into build().expect(),
which panics inside a call that cannot unwind and aborts the whole
process. Give AppError a distinct SchemaTooNew variant so setup can
catch it before that expect, show a message telling the user to
update, and exit cleanly instead.
The pinned 1.91.1 channel had no rust-analyzer component available,
so VS Code's extension fell back to a bundled version too new for the
toolchain to satisfy.
…gs-dashboard

Verified against dev-0.9.0.db, a real v3-schema library, after the app's own
pre-migration backup made that possible without touching the drifted-to-v4
primary database. All counts matched.
Every task is done: the frozen row primitives, the dashboard counts
confirmed live against real data, the attachments and capture-readiness
gaps closed, and the changelog view's edge cases covered. Suites are
green, so the change moves to openspec/changes/archive/.
Dates the changelog and adds the Linux and release-script fixes that
were already done but had no changelog line of their own.
Archive feat-graph-view with its decisions (tags and Spaces as edges, a sidebar place, nothing stored), add the Graph View requirement, and mark unit 13 done.
Several Tauri projects share the default 1420 dev port, so running
InstantNotes alongside another one collides. Give this project its
own port (1422) across vite.config.js, tauri.conf.json,
tauri.dev.conf.json, and the tauri-dev script so multiple projects
can run side by side.
Move @tauri-apps/plugin-updater from 2.10.1 to 2.12.0, window-vibrancy
from 0.6 to 0.8, and rusqlite from 0.32 to 0.40, along with the
resulting lockfile churn in package-lock.json and Cargo.lock.
Replace the update-available modal with a synthetic Update Space in
the sidebar: a green asterisk that holds two notes, the version you
are going to and the one you are coming from, the install button, and
the release notes as a readable note. It removes the old 'remind me
later' snooze in favor of a Space you can simply ignore, and the
welcome pill and tray check now open it directly.
Introduce src-tauri/src/error.rs, src-tauri/src/events.rs,
src/lib/api/error-codes.ts, and src/lib/api/events.ts as the single
source of each language's error codes and event names, plus
src/lib/api/contract.test.ts to hold the two halves equal. Replace
roughly 40 hand-rolled code: literals and 12 bare event-name literals
with references into the registries. This is behaviour-preserving: it
comes from docs/CODE_SMELL_AUDIT.md phases 0-4 and 6, and the
VALIDATION code it removes was latent and never user-visible, since
the paths that emitted it surface e.message directly rather than
going through friendlyMessage.
The unit landed on 0.9.0-pre on 2026-09-24 with every task checked, but no
entry was ever made in SEQUENCE.md, so the file that says what a release
contains left it out. It takes 13a by landing order and the boundary
registries move to 13b.

Add a test that fails when an archived change records landing on a release
branch and the ledger does not name it, since nothing was checking.
…resolved

Dependabot's three grouped PRs were closed rather than merged: the cargo group
was superseded by 6197b97, and the npm and github-actions groups are stale and
belong to the dependency baseline unit. Unit 6 now says so, and corrects its
own claim that the github-actions group is CI only, since tauri-action v0 to v1
is the action that builds and publishes the release artifacts.

The two whiteboard safety refs are deleted, with the evidence that 0.9.0 holds
everything they carried. Also clears the em dashes this file had collected.
A whiteboard could only be created from the File menu or the command
palette, so the feature was invisible to anyone reading the UI: the
toolbar's one create button always made a document.

The button becomes a split control. The + still makes a document in one
click, and a chevron beside it opens the existing context menu with both
kinds, each labelled with its keyboard shortcut so the menu teaches the
keys rather than replacing them. Right-clicking the control opens the
same menu.

ContextMenu grows an optional shortcut hint per item and an optional
anchor element. Presses on the anchor no longer count as outside the
menu, so the control that opened it can own the toggle instead of the
menu closing on press and reopening on the click.
Set the release date to 2026-09-25, the day it is cut. The Whiteboards
entry now names the note list's new create button as the first way to
start a board, since that is the one a reader will see without knowing
the shortcut.
@jamubc
jamubc requested a review from a team as a code owner September 26, 2026 02:10
@jamubc
jamubc requested a review from Shubin123 September 26, 2026 02:10
A Windows checkout converts CHANGELOG.md to CRLF, and the parser split
it on "\n" alone, so every line kept a trailing carriage return. The
bullet pattern ends in `(.+)$` with no `\s*`, and `.` does not match a
carriage return, so no bullet matched and the Settings dashboard showed
an empty "What's new" on Windows. Split on either ending instead, which
fixes it for every pattern rather than one.
The new CRLF tests replaced "\n" to make their fixture, which on a
Windows checkout turned the already-CRLF file into "\r\r\n" and tested
an input no checkout produces. Convert from either ending instead, so
the fixture is the same on every platform.
The export path test hardcoded "/tmp/a.md". On Windows a leading
separator is not an absolute path without a drive prefix, so every
fixture was rejected for being relative before its extension was
considered, and the test failed there. Build the paths from temp_dir(),
which is absolute on every platform, and keep one explicit relative
case for the check it is meant to cover.
The old faceted crystal read as the Obsidian logo. The new icon follows the Jam-Sw Faceted Stipple style on the standard 824 plate, every bundle size is regenerated from icon.svg, and the menu bar template is now the bolt.
Any note or whiteboard can leave the library for a small frameless window
that floats, sits like an ordinary window, or lies on the desktop, and comes
back in with its edits on disk. The sticky is the note's only editor while it
is out, quit waits for every window that holds edits, and stickies reopen
where they were left.
The app binary answers `instantnotes mcp` over stdio with the 2026-07-28
stateless protocol and the 2025-11-25 handshake, exposing search, read, and
gated write tools that go through the same Store rules as the UI. Agent work
shows on the notes it touches, every save names the version it was based on
so an agent never overwrites unseen text, and Settings > Agents controls
access. Also drops the unused get_or_create_tag command.
Record each known debt with why it exists and when it should be paid, starting with the findings from the agent access audit.
A sticky loaded its note once and never followed an agent's write, so the
next keystroke there saved over the agent's text without a word. It now
adopts the write in place when nothing is unsaved and names the agent with a
restore otherwise, through the same SaveQueue read the library uses.
A note opening with a bold line was titled with its asterisks, and one
opening with a screenshot was titled with the image's file path. Titles now
come from the first line with words on it, with emphasis markers stripped
from the ends of each word.
Settings > Import on macOS reads the Stickies folder through the system
picker, previews each sticky in its own color, and imports the chosen ones
with formatting, lists, links, images, and original dates. Re-importing
brings in only new stickies. The RTF reader and folder parser are plain Rust
and tested on every platform; imported and typed notes share one insert path,
and image storing and the folder picker are shared with their first users.
On first launch, and when either document gets a new version, the library
opens into a License Space holding the source license and the EULA as notes,
each agreed on its own; until both are, the rest of the library, every
shortcut and menu command, capture, and stickies stand down. It renders the
shared agreement gate generated by installers in place of the retired
EulaGate modal.
Shorter entries throughout, with the license gate, agent access, Stickies import, and the title fixes each kept as a line.
Replaces the placeholder DMG background and NSIS images before 0.9.0 ships.
Dates 0.9.0 for today's release and brings the core crate's version to 0.9.0 with the app, as in 0.8.0.
scripts/bump-version.mjs versions the core crate independently, so the 0.9.0 cut keeps it at 0.8.0.
@jamubc jamubc changed the title feat: the vault, whiteboards, and the graph (0.9.0) feat: the vault, whiteboards, graph, stickies, agents, and the license (0.9.0) Oct 1, 2026
@jamubc
jamubc merged commit 5300582 into main Oct 1, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant