Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
24ffc1b
fix: close data-loss paths in the note store
jamubc Jul 10, 2026
b36c28c
feat: flush pending edits on quit and recover corrupt databases
jamubc Jul 10, 2026
b1d9aa2
feat: capture panel dismisses on blur and confirms saves
jamubc Jul 10, 2026
33bd5bc
feat: add undo toasts and an in-app confirm dialog
jamubc Jul 10, 2026
8a19086
feat: honest save states and safe destructive flows in the library
jamubc Jul 10, 2026
b11a3f1
feat: highlight search matches in the note list
jamubc Jul 10, 2026
007712d
feat: open notes from the command palette
jamubc Jul 10, 2026
1648d07
feat: rename and delete tags from the sidebar
jamubc Jul 10, 2026
81b083b
test: cover the save queue, race tokens, and quit flush
jamubc Jul 10, 2026
237f4fc
fix: bump package-lock.json together with the app version
jamubc Jul 11, 2026
2829cb5
ci: pin the toolchain, gate releases on tests, and lint every build
jamubc Jul 11, 2026
960fa6d
chore: remove stale ignore entries and fix docs drift
jamubc Jul 11, 2026
ff5ef96
chore: bump version to 0.8.0
jamubc Jul 11, 2026
cee1f18
docs: mark Linux support in 0.7.0 as an early preview
jamubc Jul 11, 2026
905de58
spec: add spaces design for the workspace management rework
jamubc Jul 11, 2026
949f06b
feat: turn workspaces into spaces with undo delete and scoped tag chips
jamubc Jul 11, 2026
6947992
docs: write the open-loop product thesis into the project context
jamubc Jul 11, 2026
59dd70c
feat: resurface never-opened captures in a Revisit view
jamubc Jul 11, 2026
033eeb9
feat: group the note list into native time sections
jamubc Jul 11, 2026
25e9aa7
Update README.md
jamubc Jul 11, 2026
fc00538
feat: measure capture reveal-to-ready latency as a product number
jamubc Jul 11, 2026
659d173
fix: exit revisit mode when a new note is created
jamubc Jul 11, 2026
5ce2740
feat(editor): rebuild markdown editing on a construct kernel
jamubc Jul 11, 2026
0b05fd7
feat(view): collapsible, resizable sidebar
jamubc Jul 11, 2026
cd90ae2
chore: remove scaffold residue and archive shipped specs
jamubc Jul 11, 2026
764bcdc
docs: restore the API and data-model contracts
jamubc Jul 11, 2026
b2a55f7
refactor(core): drop the unused sync schema
jamubc Jul 11, 2026
bfface5
refactor(core): split the store into per-domain modules
jamubc Jul 11, 2026
4284b29
refactor(shell): extract data commands into command modules
jamubc Jul 11, 2026
a144447
perf(core): bulk multi-select actions in one transaction
jamubc Jul 11, 2026
fff46d2
refactor(ui): merge SpaceRow and TagRow into one row component
jamubc Jul 11, 2026
2b5b660
refactor(library): dedupe filter navigation and save-queue collection…
jamubc Jul 11, 2026
fe3c266
test(ui): add component testing, starting with the sidebar row
jamubc Jul 11, 2026
28dd883
refactor(library): extract the body-save queue into its own unit
jamubc Jul 11, 2026
ff8f3bd
refactor(library): extract the list selection into its own model
jamubc Jul 11, 2026
3174a3d
refactor(shell): extract the desktop shell out of lib.rs
jamubc Jul 11, 2026
006001e
refactor(ui): split SettingsView into per-page components
jamubc Jul 11, 2026
db5e4d0
test(ui): cover SettingsView navigation
jamubc Jul 11, 2026
22b91c1
docs: pin the Space/Workspace naming boundary
jamubc Jul 11, 2026
be4582e
docs: drop a where-it-came-from comment on formatDate
jamubc Jul 11, 2026
3e32f8c
style: rustfmt the extracted store and command modules
jamubc Jul 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Weekly dependency updates, one grouped PR per ecosystem so the review load is a
# single PR a week rather than one per package. Grouping every update into the
# same PR keeps the three lockfiles (package-lock.json, src-tauri/Cargo.lock,
# workflow SHAs) moving in lockstep with CI.
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly
groups:
npm:
patterns: ["*"]

- package-ecosystem: cargo
directory: "/src-tauri"
schedule:
interval: weekly
groups:
cargo:
patterns: ["*"]

- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
groups:
github-actions:
patterns: ["*"]
14 changes: 13 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ jobs:
node-version: 22
cache: npm

- uses: dtolnay/rust-toolchain@stable
# Must match rust-toolchain.toml; @stable would export RUSTUP_TOOLCHAIN and
# override the pin, building CI on a different compiler than local.
# components must be listed explicitly: the action installs a minimal
# profile, and the clippy/fmt steps below die without them.
- uses: dtolnay/rust-toolchain@1.91.1
with:
components: clippy, rustfmt

- uses: swatinem/rust-cache@v2
with:
Expand All @@ -44,4 +50,10 @@ jobs:

- run: cargo test --workspace --manifest-path src-tauri/Cargo.toml

# Lint gate: deny clippy warnings and enforce rustfmt so style and common
# bug patterns can't merge. Same src-tauri workspace as the cargo test above.
- run: cargo clippy --workspace --all-targets --manifest-path src-tauri/Cargo.toml -- -D warnings

- run: cargo fmt --all --check --manifest-path src-tauri/Cargo.toml

- run: npm run tauri -- build --no-bundle
12 changes: 11 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ jobs:
node-version: 22
cache: npm

- uses: dtolnay/rust-toolchain@stable
# Must match rust-toolchain.toml; @stable would export RUSTUP_TOOLCHAIN and
# override the pin, building CI on a different compiler than local.
- uses: dtolnay/rust-toolchain@1.91.1

- uses: swatinem/rust-cache@v2
with:
Expand All @@ -46,6 +48,14 @@ jobs:

- run: npm ci

# A tag push ships to real users, so run the same quality gate as build.yml
# before spending time on the signed bundle: a red release must never draft.
- run: npm run check

- run: npm test

- run: cargo test --workspace --manifest-path src-tauri/Cargo.toml

# Release notes = the CHANGELOG section for this tag. tauri-action writes
# this into both the GitHub release body and latest.json's "notes", so the
# in-app updater shows real "What's new" text, not install boilerplate.
Expand Down
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ src-tauri/gen/
# SvelteKit generated files
.svelte-kit/

# Lock files (generated, noisy in diffs)
package-lock.json
src-tauri/Cargo.lock

# generated update manifest (scripts/make-update-manifest.sh)
/latest.json

Expand Down
44 changes: 39 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,48 @@ uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Search results highlight what matched, in both the note title and the
excerpt.
- The command palette opens notes: press the palette shortcut and see your
five most recent notes, or type to search everything without leaving the
keyboard.
- Rename or delete a tag right in the sidebar: right-click it (or press
Shift+F10) for options, or double-click the tag to rename it in place.

### Changed
- Deleting is calmer: moving notes to the Trash shows an Undo toast instead
of interrupting you, and permanent deletions ask in a proper in-app dialog
instead of a system popup.
- The quick capture panel closes when you click elsewhere (your draft is
kept), shows a brief "Saved" confirmation, and Cmd+Enter (Ctrl+Enter on
Windows) saves and opens the library.

### Fixed
- Quitting can no longer lose your last moments of typing: every quit path
saves pending edits first, and a note whose save failed says "Not saved"
instead of pretending otherwise.
- Removing a #tag from a note's text now actually removes that tag from the
note.
- If the notes database is ever corrupted, the app sets it aside and starts
fresh instead of failing to launch, and tells you what happened. The
database is also backed up automatically before any update that changes
its format.
- Typing quickly in search can no longer show results for an older query.
- If another app owns the capture shortcut, the welcome screen now says so
instead of the shortcut silently doing nothing.

## [0.7.0] - 2026-07-06

### Added
- Windows and Linux support: InstantNotes now ships an installer for Windows
(x64) and an AppImage for Linux (x64) alongside the macOS build, all with
in-app updates. On Windows and Linux the capture panel is summoned with
`Ctrl+Shift+Space`, Settings and Quit live in the File menu, and shortcut
labels show `Ctrl+` combinations instead of mac glyphs.
- Windows support: InstantNotes now ships an installer for Windows (x64)
alongside the macOS build, both with in-app updates. On Windows the capture
panel is summoned with `Ctrl+Shift+Space`, Settings and Quit live in the
File menu, and shortcut labels show `Ctrl+` combinations instead of mac
glyphs.
- Linux (early preview): an AppImage (x64) is included, but Linux support is
still under development and may not work properly yet. The current focus is
getting InstantNotes fully functional on macOS and Windows first.
- Settings is now a small wiki: a landing grid of category cards opening
focused sub-pages with a breadcrumb back, including the new Contexting page.
- Contexting: a template that shapes what "Copy note as context" hands to
Expand Down
82 changes: 22 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
# InstantNotes

Instant notes for macOS, Windows, and Linux. Capture, organize, and search your thoughts.

InstantNotes is a desktop notes app built around fast capture and a focused library. Save a thought from anywhere with a global shortcut, then organize and retrieve it without being forced into a folder system.
Instantly externalize your writing. Capture and locate your thoughts.

## Features

- **Instant capture**: a lightweight capture panel summoned from the system tray or via a global hotkey (`Option+Space` on macOS, `Ctrl+Shift+Space` on Windows and Linux), with drafts preserved if dismissed
- **Focused library**: a two-section sidebar (All Notes and Workspaces) over a note list and editor, with pinned notes floated to the top and a status filter for archived and trashed notes
- **Workspaces**: named collections that group related notes; a note can live in many workspaces, and deleting a workspace never deletes its notes
- **Full-text search**: SQLite FTS5 search over titles and bodies with ranked results, using plain-language queries with no search syntax to learn
- **Command palette**: a `Cmd+K` (`Ctrl+K`) palette for running actions and switching themes, with arrow-key navigation and recents; search reaches into sub-menus (typing a theme name jumps straight to it), and the Themes sub-menu applies each theme live so you can preview as you arrow through
- **Tags, not folders**: lightweight labels, including tags extracted from `#inline` text
- **Local and private**: all data stored locally in SQLite; note content never appears in logs or diagnostics
- **Quick Capture:** Global hotkey (`Opt` / `Ctrl+Shift+Space`) with drafts.
- **Command Palette:** `Cmd/Ctrl+P` for actions, search, and themes.
- **Flexible Organization:** Group notes with Workspaces and `#inline` tags instead of strict folders.
- **100% Local & Private:** Everything lives in a local SQLite database. Zero telemetry.

## Installation

Download the latest build for your platform from the [releases page](../../releases). The builds are unsigned, so each OS asks for a one-time confirmation on first launch; the in-app updater applies later versions without any of it.
> The builds are unsigned, You will likely be prompted on first launch.
Download the latest build for your platform from the [releases page](../../releases).

### macOS (Apple Silicon)

Download the `.dmg`, open it, and drag InstantNotes to Applications. The app is not notarized, so macOS blocks the first launch with an "Apple could not verify" message. Clear the quarantine flag and it opens normally from then on:
Download the `.dmg`, open it, and drag InstantNotes to Applications.
The app is not notarized, macOS blocks the first launch with an "Apple could not verify" message.
Clear the quarantine flag and it opens normally from then on:

```sh
xattr -d com.apple.quarantine /Applications/InstantNotes.app
Expand All @@ -30,40 +27,27 @@ Alternatively, after the blocked first launch, open System Settings, go to Priva

### Windows (x64)

Download and run the `-setup.exe` installer. SmartScreen flags the unsigned build: click "More info", then "Run anyway".

### Linux (x64)

Download the `.AppImage`, make it executable, and run it:

```sh
chmod +x InstantNotes_*.AppImage
./InstantNotes_*.AppImage
```

The app lives in the system tray; on desktops without tray support (such as stock GNOME, which needs the AppIndicator extension), use the in-window File menu to quit and the library window to work.

To build from source instead, see [Development](#development).
Download release then run installer.
You will get a SmartScreen flags due to the unsigned build:
1. To proceed --> click "More info", then "Run anyway".

## Development
To build from source instead

### Prerequisites

- macOS, Windows, or Linux
- macOS, Windows
- [Rust](https://rustup.rs/) via rustup (the version is pinned by `rust-toolchain.toml`)
- Node.js 22+
- Linux only: the [Tauri system dependencies](https://v2.tauri.app/start/prerequisites/#linux) (webkit2gtk 4.1 and friends)
- Windows only: the Visual Studio Build Tools with the C++ workload
- Windows: the Visual Studio Build Tools with the C++ workload

### Run the app

```sh
npm install
npm run tauri dev
npm run tauri:dev
```

This builds the Rust core, starts the Vite dev server, and launches the app. Frontend changes hot-reload instantly; Rust changes trigger an incremental rebuild and app restart.

### Test

```sh
Expand All @@ -82,7 +66,10 @@ Produces the platform's bundles under `src-tauri/target/release/bundle/`: an `.a

### Release (with self-update)

The app checks GitHub Releases for updates on launch and every 6 hours, via `latest.json` attached to the latest release. Each release's "What's new" text - shown in the in-app update panel and on the GitHub release - comes from the matching `CHANGELOG.md` section. Releases are built, signed, and published by CI:
The app checks GitHub Releases for updates on launch and every 6 hours, via `latest.json` attached to the latest release.

Release's are shown on update panel
Releases are built, signed, and published by CI:

```sh
# 1. Add a "## [X.Y.Z]" section to CHANGELOG.md describing the release.
Expand All @@ -97,33 +84,8 @@ git tag vX.Y.Z && git push origin vX.Y.Z

`npm run bump` updates package.json, src-tauri/tauri.conf.json, src-tauri/Cargo.toml, and src-tauri/Cargo.lock together (the `instantnotes-core` crate versions independently). Publishing the smoke-tested draft is the deliberate ship gate; the draft stays invisible to the in-app updater until then.

CI signs the updater artifact with the minisign key stored in the repo secrets `TAURI_SIGNING_PRIVATE_KEY` and `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`, and the app verifies downloads against the matching public key in `tauri.conf.json`. If the secret is ever lost, generate a new keypair with `npm run tauri signer generate`, update both the secret and the pubkey, and ship one manual release so installs can cross over.

For a fully local release without CI (macOS-only fallback: `make-update-manifest.sh` writes just the `darwin-aarch64` entry), build with `TAURI_SIGNING_PRIVATE_KEY` set, run `./scripts/make-update-manifest.sh`, and upload the dmg, `InstantNotes.app.tar.gz`, and `latest.json` with `gh release create`. Release downloads must be publicly reachable for the in-app check to work.

### Project structure

```
src/ Svelte 5 frontend (library window, capture panel)
src-tauri/ Rust core and Tauri 2 shell
openspec/ Product specification and project conventions
static/ Static assets
```

### Tech stack

| Layer | Technology |
|---|---|
| Desktop shell | Tauri 2 |
| Core (persistence, search, commands) | Rust |
| Storage and search | SQLite + FTS5 |
| UI | Svelte 5 + TypeScript |
| Editor | CodeMirror 6 |
| Testing | cargo test, Vitest, svelte-check |

### Architecture
CI signs the updater artifact with the minisign key stored in the repo secrets `TAURI_SIGNING_PRIVATE_KEY` and `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`, and the app verifies downloads against the matching public key in `tauri.conf.json`.

A local Rust core sits behind a thin desktop shell. Rust owns business rules and persistence; TypeScript owns view state and typed IPC calls. The library window, capture panel, and future settings window communicate through typed commands and change events. UI code calls the API client rather than invoking Tauri commands directly.

Product requirements and conventions live in [`openspec/`](openspec/):

Expand Down
Loading
Loading