Skip to content

feat: per-host notes, synced with host data (#25)#27

Closed
ezhkov-ph wants to merge 4 commits into
VoltiusApp:mainfrom
ezhkov-ph:feat/host-notes
Closed

feat: per-host notes, synced with host data (#25)#27
ezhkov-ph wants to merge 4 commits into
VoltiusApp:mainfrom
ezhkov-ph:feat/host-notes

Conversation

@ezhkov-ph

Copy link
Copy Markdown
Contributor

Closes #25.

What this adds

Every host (Connection) gets a free-form notes field: multiline text, editable from the host form, persisted with the rest of the host's data and synced across devices the same way name/tags/etc. already are. No markdown rendering — plain text only, per the issue's "not now" on that.

How it's wired (for review)

Rust (src-tauri/)

  • notes: Option<String> added to Connection and ConnectionFormData in storage/config.rs, both #[serde(default, ...)] so existing connections.json files on disk deserialize unchanged — no migration needed.
  • commands/connections.rs: threaded notes through merge_form_into_connection, connection_save, and connection_update.
  • Added notes to the connection_clocks! macro's simple: list. This repo tracks a per-field CRDT clock per connection (clocks: HashMap<String, String>) so two devices editing different fields of the same host merge without clobbering each other — notes gets the same treatment as every other synced field, for free. This macro also seeds initial_clocks and is asserted against in two "golden field list" tests (bump_covers_the_expected_field_set, initial_clocks_match_bumpable_field_set) — both updated to expect notes.

TypeScript

  • notes?: string added to Connection / ConnectionFormData in src/types/index.ts.
  • src/stores/connectionStore.ts: added to all three places a Connection object gets assembled (local create/update, team-vault create/update), and to connectionToFormData — this last one matters because partial updates (pin, ping-toggle, tag rename, etc.) rebuild the form payload via { ...connectionToFormData(c), someField: x }, so omitting notes there would have silently wiped it on any unrelated edit.
  • src/plugins/runtime.ts: the plugin connections.update API (used by the SSH-config-import plugin, for one) now explicitly passes through the existing notes — plugins have no way to set notes themselves, so this just guarantees they can't accidentally erase a user's note on their next sync pass.
  • src/components/connections/ConnectionForm.tsx: a "Notes" FormSection with a plain <textarea> (4 rows, no resize handle — matches the existing private-key textarea's styling), wired into the existing autosave debounce. Used by both the desktop and mobile host-edit screens since they share this component.
  • i18n: en and fr keys added (connections.form.sectionNotes / notesPlaceholder). Not added to ru — the Russian locale doesn't exist on main yet (open in i18n: add Russian (ru) locale #26), so I kept this PR independent of it; happy to add the ru strings here once i18n: add Russian (ru) locale #26 lands, or you can layer them in.

Import/export + encryption

  • Export already builds ConnectionExport by passthrough-spreading most Connection fields (src/services/import-export/handlers/connections.ts), so notes rides along into JSON export bundles with no code change there.
  • Since notes (like passwords/keys) can carry sensitive info ("access details, maintenance windows"), src/components/import-export/ExportTab.tsx now checks whether the export bundle contains any plaintext secret (password / private key / passphrase / notes) and defaults the "Encrypt backup" toggle to on in that case, with a warning shown if the user switches it off anyway. This was already a pre-existing feature (XChaCha20-Poly1305 + PBKDF2, services/import-export/formats.ts) — this just changes the default and adds a nudge, doesn't touch the crypto.

What I deliberately left out

  • Markdown rendering — explicitly deferred per the issue.
  • Search inside notes / list preview — the "nice to have" list items. Wanted to keep this PR focused; can follow up if you want them.
  • No separate "sync notes" toggle in Settings → Sync: notes are a field on the host object, not a separate synced entity, so they ride under the existing "Hosts" sync toggle along with every other host field (tags, pre/post-command, etc.). Verified the merge path (src/services/crdt.ts's per-field LWW merge) and the Gist-sync plugin (src/plugins/runtime.ts's importStates, which uses the same generic mergeEntities) both carry notes with no extra code.

Testing

  • cargo test (full --lib) — 119 passed, including the two updated golden-field-list tests.
  • vitest run (full suite) — 318 passed, incl. i18n key-parity tests.
  • Manually exercised in a headless Tauri build (Xvfb + tauri-driver): added a note to a host, confirmed it persists across a form re-open, confirmed the field behaves like the rest of the form (autosave, no stray resize handle).

Adds a free-form notes field to connections: editable in the host form
(desktop + mobile), persisted in connections.json, CRDT-clock-tracked so
it merges per-field across devices, carried through import/export
bundles, and preserved across plugin-driven updates.

Also hardens export: when the bundle contains plaintext secrets
(passwords, keys, or host notes), encryption now defaults to on and a
warning is shown if the user opts out.
kipavy added 2 commits July 19, 2026 17:48
…hrough

- Add ru locale keys for the new Notes section (sectionNotes,
  notesPlaceholder) and the export plaintext-secrets warning, now that
  the ru locale has landed on main (VoltiusApp#26).
- SerialConnectionForm: pass through initial.notes on save. The serial
  form has no notes UI, so it was sending notes=undefined and would wipe
  a note that had synced onto a serial-typed host from another device.
kipavy added a commit that referenced this pull request Jul 19, 2026
Brings dev up to date with main (0.9.3, ru locale, #33, macOS signing, CI)
and adds the per-host notes feature (#25) with review fixes.
@kipavy

kipavy commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Thanks for this, Alex — really thorough work, and the write-up made review easy. Merged into dev (rather than main directly) since that's where features integrate here before a release.

I added two small follow-ups on top of your branch before merging:

  • ru locale keys for the new Notes section + the export plaintext-secrets warning — the Russian locale (i18n: add Russian (ru) locale #26) landed on main after you opened this, so the strings could finally go in.
  • SerialConnectionForm notes passthrough — the serial form has no notes UI, so it was sending notes: undefined and would wipe a note that had synced onto a serial-typed host from another device. Purely defensive.

Verified on the merged tree: tsc clean, full vitest suite (318) green, i18n key-parity passing. Also confirmed the field renders on the mobile host-edit screen (shared ConnectionForm).

Closing this PR since the commits are now on dev and will reach main at the next release. For future PRs, targeting dev and rebasing on latest dev avoids the main-history entanglement. Appreciate the contribution! 🙏

@kipavy kipavy closed this Jul 19, 2026
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.

Add comments/notes for hosts

2 participants