github akitaonrails/FrankMD v1.15.0

41 minutes ago

v1.15.0

Released: 2026-11-07

External-edit protection for saves, a new grammar-check provider, and a broad dependency refresh including the editor stack.

Added

  • External-edit protection (closes #272): every save now carries the revision the note was loaded with. If the file changed on disk in the meantime — another editor, another machine, a sync client — FrankMD shows a side-by-side diff instead of silently overwriting it. Keep the server version (reloaded in the editor) or keep yours (saved on top of the new revision). Text typed while the request was in flight is never replaced by the older snapshot, and the conflict is scoped to its own note even if you switch documents with the dialog open. The check is best-effort against writers that ignore FrankMD's lock, such as sync tools writing mid-save.
  • Cheaper Inference provider for grammar checking (#273, by @aiapienthusiast): an OpenAI-compatible gateway selected through ai_provider = cheaperinference, configured with cheaperinference_api_key and cheaperinference_model (default gpt-5.4-mini). It sits after OpenRouter in auto priority. The API key is masked like the other providers and the endpoint is a fixed constant.

Fixed

  • Save responses were never parsed: @rails/request.js exposes the body as a json getter, so the autosave success path threw while reading it and the returned revision was silently dropped. It is read correctly now, which is what makes revision tracking and conflict detection real.
  • AI provider isolation under concurrency: RubyLLM keeps its configuration in a process-global object that a chat reads when it runs, so overlapping requests could pair one provider's key with another provider's endpoint. Configuration and chat now run under a mutex.
  • Two stale system tests from v1.14.0: the Settings theme test assumed tokyo-night was the last option (the Omarchy theme is appended when detected), and the workspace layout test clicked opacity-0 pins in rows that have no layout at some breakpoints.

Dependencies

  • Ruby: json 3.1.0, parallel 2.3.0, rubocop 1.92.2 (+ast/performance/rails), parser 3.3.13.0, minitest 6.1.0, net-ssh 7.3.9, net-scp 4.2.1, net-sftp 4.1.1, faraday 2.15.1, irb 1.19.0, sshkit 1.25.1, aws-sdk-kms 1.140.0, aws-sdk-s3 1.239.0, tailwindcss-rails 4.7.0. Rails is already on the newest published release (8.1.4.1).
  • JavaScript: CodeMirror view 6.43.15, state 6.7.6, language 6.13.1, lang-markdown 6.5.2, commands 6.11.1, search 6.7.2, autocomplete 6.20.4, @lezer updates, vitest 5.1.0, plus a new @codemirror/streamparser module that @codemirror/language 6.13 split out. The importmap runtime pins were re-vendored to the same versions the tests use.
  • Not taken, deliberately: marcel 2, net-ssh 8, prism 2 (blocked by Rails, sshkit, rubocop constraints), jsdom 31 (test-only major that changes dialog focus semantics), @replit/codemirror-vim 6.4 (split into a second package needing its own vendoring pass).

Test Coverage

  • 1,182 Ruby tests (4,761 assertions), 2,281 JavaScript tests (99 files), 92 system tests (742 assertions).
  • Extra checks: zeitwerk:check, production boot with eager loading, assets:precompile, bin/importmap audit and npm audit clean, full system suite run against the vendored (not npm) CodeMirror build.

Don't miss a new FrankMD release

NewReleases is sending notifications on new releases.