Changelog
All notable changes to janalis/collaborate. Format: Keep a Changelog; versions follow Semantic Versioning.
Unreleased
0.2.0 - 2026-11-03
Added
- Shared drafts and field marks work with every stock Symfony form theme (
form_div_layout,form_table_layout, Bootstrap 3/4/5, Foundation 5/6, Tailwind 2, daisyUI 5), not only with.form-grouprows and EasyAdmin's collection markup. A shared form marks its rows (data-collaborate-row), its collections' widgets (data-collaborate-entries,data-collaborate-placeholder) and their entries (data-collaborate-entry); EasyAdmin's markup is still read.
0.1.2 - 2026-11-03
Fixed
- On MySQL, which returns JSON objects with their keys sorted, a saved value holding an object no longer reads as changed: Save no longer refuses it as changed outside the draft, Join reports no conflict for it, a Save records no needless batch and Replay recognises the page's digest. MySQL also runs in CI.
- The journal's size counts the batches as stored, with their revision and author, on MariaDB and MySQL alike, so a draft can no longer pass
max_draft_bytesand a checkpoint finds the size the updates added up to. - Field states are written with
REPLACE, not MySQL's deprecatedVALUES(). - Join builds the page's view once it has released the draft: decoding a long journal no longer holds off the draft's appends past their wait (503).
DraftJournal::saved()andcreated()take the draft's lock unless their caller holds it.- Saved-view refresh follows saves again once a refused page enters another place (
collaborate:enter), and keeps the view of the address the page has when it refreshes, after ahistory.pushState. - Cursors stopped on a layer start again on it, and leave it as they found it.
- A draft session whose form a host removed stops: its replays, listeners and hub membership no longer outlive the form, which starts a new session if it comes back.
- A peer's event redraws that peer alone: its pointer is measured in the next frame rather than as the event comes, the markers and the popup line are drawn again only once a peer changes record, popup, name or colour, and a caret that did not move is not measured. A frame measures every pointer and record before it draws any, looks each region, path and record up once, and the layer's announcements and popup lines no longer draw the page again.
Upgrading
collaborate_drafthas an index onupdated_at, which the purge reads: generate a migration (doctrine:migrations:diff) or addCREATE INDEX collaborate_draft_updated_at ON collaborate_draft (updated_at).
0.1.1 - 2026-11-02
Added
cursors.places: aPlaceProviderInterfacenames a page's room and is asked again at every hub request, so a place the user lost is refused before the grant expires.- Host options describing pages other than EasyAdmin's:
regions,key_attributeandkey_scope(a record placed in whatever element shows it),anchor_attributes,anchor_classes,caret_regions,ignored_parameters. cursors.popups: the popup of a record a peer has open, and a status under its title saying who else has it open.markers,edges,idle,top_layer,presence: menu(find a peer, hide pointers),distinct_colors,silent_aloneandshare_stream.enter(grant),cursorsFor()and thecollaborate:enterevent: a page that replaces its content enters another place without loading._layer.scss, the cursor styles as a module that needs no framework;_cursors.scssloads it.- A tab sends less often past three peers.
Fixed
- A page that sent every edit reloads on its own when a compaction replaces its generation before it heard of the last Save, instead of stopping in Reset: compactions record the generations they replaced, and views answer them as
rebasedFrom. - Packages leave out the tests, documentation sources and tooling.
Documentation
- Discard resets fields to the values the draft last saved and never reads the record; the review of a record saved elsewhere stays open until Use saved record.
- Pages without EasyAdmin render each form row inside a
form-groupelement, which field marks outline.
0.1.0 - 2026-11-02
Added
- Shared drafts of Symfony forms: editors of the same record, or of the same new-record page through its invitation link, share one durable draft of its text, rich-text, choice and collection fields. A journal on MariaDB or MySQL orders every change under a per-draft named lock; pages replay it over CSRF-protected requests that recheck the user's signed field grant.
- A state bar above each shared form (saved, unsaved, synchronizing, interrupted, changed, in conflict, reset, created, refused, full, expired, incompatible, unavailable), Discard with confirmation, an invitation link for new records, and a review of the differences with a record saved elsewhere, where editors keep the draft or use the saved record.
- Save through the form's own validation and persistence, holding the draft's lock from its revision check to the response; a save overtaken by a peer's edit or by another save returns to the form with a warning.
- A CKEditor 5 plugin,
Collaborate, that shares the editor's model operations and lets CKEditor transform concurrent ones, undo included, and keeps a Markdown source mode in step;sessionFor(),editorConfiguration(),editorReady(),editorFailed()andaddRowInitializer()for the host's editors and collection widgets. - Shared cursors and presence on any page with the collaboration layer: peers' pointers with their platform's shapes, carets and selections in text fields and rich text, the field each peer is in, and header avatars with a
+Ncounter. - Saved-list and saved-detail refresh after writes elsewhere, limited to the pages whose entity the write can alter.
- One Mercure subscription per page for all of the above, with a subscriber JWT minted for the topics the page's signed grants admit, renewed in the background.
- An EasyAdmin adapter: edit and new CRUD pages share drafts, saves go through EasyAdmin's persistence, and index and detail pages refresh.
- Per-locale draft fields for Gedmo translatable properties.
collaborate:purge, which deletes idle drafts.- Extension points:
CollaboratorProviderInterface(who a user is),DraftAccessCheckerInterface(what they may edit) andFormContextInterface(which record a page edits). - English and French translations.