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 withcheaperinference_api_keyandcheaperinference_model(defaultgpt-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.jsexposes the body as ajsongetter, 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-nightwas 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/streamparsermodule that@codemirror/language6.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-vim6.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 auditandnpm auditclean, full system suite run against the vendored (not npm) CodeMirror build.