[Samenwerken][P1] Elke eigen toetsaanslag remount de editor: focus en cursor weg, bij de gast springt tekst terug #2680

Closed
opened 2026-10-31 09:47:39 +00:00 by brenno · 4 comments
Owner

Symptoom

In een samenwerksessie verliest het veld waarin je typt de focus en de cursorpositie na vrijwel elke toetsaanslag:

  • bij de eigenaar (host) direct na elke aanslag;
  • bij een gast zodra de echo van de eigenaar terugkomt. Tekst die je tijdens die rondgang typte, springt dan eerst terug naar de oudere versie en komt pas met de volgende echo terug.

Daarnaast wordt het deck bij elke echo als gewijzigd gemarkeerd, en lopen per aanslag drie volledige deck-diffs.

Oorzaak

  1. CollabSessionController luistert op session.deckChanges (lib/collab/collab_session_controller.dart:43). Die stream vuurt ook voor de eigen, net vastgelegde op. Bij de host gebeurt dat in CollabSession._commit (lib/collab/collab_session.dart:133-138), bij de gast via de echo in _onRemoteOp.
  2. _applySessionDeck (collab_session_controller.dart:74-85) roept writeDeck(merged) ook aan als deckDiffToOps leeg is. merged is dan gewoon het lokale deck.
  3. writeDeck is DeckNotifier.applyCollabDeck (lib/state/deck_provider.dart:750-757). Die hoogt altijd revision op en zet isDirty.
  4. EditorPanel sleutelt zijn hele editorkolom op die revisie: KeyedSubtree(key: ValueKey('editor-rev-$revision')) (lib/widgets/panels/editor_panel.dart:93-94). Elk veld wordt dus opnieuw opgebouwd, met een nieuwe FocusNode en controller.
  5. Bij de gast komt de echo van op N binnen terwijl er lokaal al verder getypt is. deckDiffToOps(local, sessionDeck) levert dan een SetSlideField die het hele veld terugzet op de oudere waarde (lib/collab/collab_deck_diff.dart:42-58).
  6. De echobewaking _applyingRemote werkt niet voor de deckstream. StateNotifier.stream levert asynchroon af, dus onLocalEdit na applyCollabDeck draait pas als de vlag alweer uit staat. Elke diff loopt over dia's × 109 SlideFields, zonder kortsluiting voor ongewijzigde dia's.

Voorgestelde oplossing

  1. _applySessionDeck: if (ops.isEmpty) return;. Een echo die niets verandert, schrijft dan niets, verhoogt de revisie niet en maakt het deck niet vuil. Dit alleen lost het hostgeval al op.
  2. Gast: schrijf een echo van je eigen intentie niet terug zolang er een nieuwere lokale intentie voor hetzelfde (dia, veld) onderweg is. Houd daarvoor per veld de laatst ingediende waarde bij en wis die zodra de bijbehorende echo binnen is.
  3. applyCollabDeck: hoog revision alleen op als de samengevoegde wijziging de geselecteerde dia raakt. Velden van andere dia's lezen bij een selectiewissel toch opnieuw. Op termijn kan het beter zonder remount: werk de controller van een veld zonder focus bij, en meld een wijziging in het veld mét focus in plaats van het te overschrijven.
  4. deckDiffToOps: zet if (identical(old, slide)) continue; vóór de veldlus.

Wat overeind moet blijven

  • Een echte wijziging van een co-auteur op de dia die je open hebt, verschijnt nog steeds in de velden. Dat is de reden van de revisiebump (deck_provider.dart:747).
  • Ctrl+Z draait alleen je eigen bewerkingen terug.
  • De bestaande gevallen in test/collab_session_controller_test.dart (echo guard, merge van niet-syncbare velden) blijven groen.

Toets

  • Unit: CollabSessionController met een writeDeck-spion. Eén lokale bewerking bij de host geeft nul aanroepen van writeDeck.
  • Widget, host met loopback-transport: typ drie tekens in de titel. FocusManager.instance.primaryFocus blijft hetzelfde node en de tekst is abc.
  • Gast met vertraagde loopback: typ abc vóór de echo van a binnen is. De tekst blijft abc.

Gevonden bij codelezing tijdens de vervolginspectie van 31-10-2026. Nog niet in een live sessie nagespeeld.

## Symptoom In een samenwerksessie verliest het veld waarin je typt de focus en de cursorpositie na vrijwel elke toetsaanslag: - **bij de eigenaar (host)** direct na elke aanslag; - **bij een gast** zodra de echo van de eigenaar terugkomt. Tekst die je tijdens die rondgang typte, springt dan eerst terug naar de oudere versie en komt pas met de volgende echo terug. Daarnaast wordt het deck bij elke echo als gewijzigd gemarkeerd, en lopen per aanslag drie volledige deck-diffs. ## Oorzaak 1. `CollabSessionController` luistert op `session.deckChanges` (`lib/collab/collab_session_controller.dart:43`). Die stream vuurt ook voor de eigen, net vastgelegde op. Bij de host gebeurt dat in `CollabSession._commit` (`lib/collab/collab_session.dart:133-138`), bij de gast via de echo in `_onRemoteOp`. 2. `_applySessionDeck` (`collab_session_controller.dart:74-85`) roept `writeDeck(merged)` ook aan als `deckDiffToOps` leeg is. `merged` is dan gewoon het lokale deck. 3. `writeDeck` is `DeckNotifier.applyCollabDeck` (`lib/state/deck_provider.dart:750-757`). Die hoogt **altijd** `revision` op en zet `isDirty`. 4. `EditorPanel` sleutelt zijn hele editorkolom op die revisie: `KeyedSubtree(key: ValueKey('editor-rev-$revision'))` (`lib/widgets/panels/editor_panel.dart:93-94`). Elk veld wordt dus opnieuw opgebouwd, met een nieuwe FocusNode en controller. 5. Bij de gast komt de echo van op N binnen terwijl er lokaal al verder getypt is. `deckDiffToOps(local, sessionDeck)` levert dan een `SetSlideField` die het hele veld terugzet op de oudere waarde (`lib/collab/collab_deck_diff.dart:42-58`). 6. De echobewaking `_applyingRemote` werkt niet voor de deckstream. `StateNotifier.stream` levert asynchroon af, dus `onLocalEdit` na `applyCollabDeck` draait pas als de vlag alweer uit staat. Elke diff loopt over dia's × 109 `SlideField`s, zonder kortsluiting voor ongewijzigde dia's. ## Voorgestelde oplossing 1. `_applySessionDeck`: `if (ops.isEmpty) return;`. Een echo die niets verandert, schrijft dan niets, verhoogt de revisie niet en maakt het deck niet vuil. Dit alleen lost het hostgeval al op. 2. Gast: schrijf een echo van je eigen intentie niet terug zolang er een nieuwere lokale intentie voor hetzelfde (dia, veld) onderweg is. Houd daarvoor per veld de laatst ingediende waarde bij en wis die zodra de bijbehorende echo binnen is. 3. `applyCollabDeck`: hoog `revision` alleen op als de samengevoegde wijziging de **geselecteerde dia** raakt. Velden van andere dia's lezen bij een selectiewissel toch opnieuw. Op termijn kan het beter zonder remount: werk de controller van een veld zonder focus bij, en meld een wijziging in het veld mét focus in plaats van het te overschrijven. 4. `deckDiffToOps`: zet `if (identical(old, slide)) continue;` vóór de veldlus. ## Wat overeind moet blijven - Een echte wijziging van een co-auteur op de dia die je open hebt, verschijnt nog steeds in de velden. Dat is de reden van de revisiebump (`deck_provider.dart:747`). - Ctrl+Z draait alleen je eigen bewerkingen terug. - De bestaande gevallen in `test/collab_session_controller_test.dart` (echo guard, merge van niet-syncbare velden) blijven groen. ## Toets - Unit: `CollabSessionController` met een `writeDeck`-spion. Eén lokale bewerking bij de host geeft nul aanroepen van `writeDeck`. - Widget, host met loopback-transport: typ drie tekens in de titel. `FocusManager.instance.primaryFocus` blijft hetzelfde node en de tekst is `abc`. - Gast met vertraagde loopback: typ `abc` vóór de echo van `a` binnen is. De tekst blijft `abc`. Gevonden bij codelezing tijdens de vervolginspectie van 31-10-2026. Nog niet in een live sessie nagespeeld.
Author
Owner

Opgepakt. Tak: fix/vervolginspectie-3110. Verwachte reikwijdte: de bestanden die in de oorzaak van dit issue staan, plus een regressietest.

Opgepakt. Tak: fix/vervolginspectie-3110. Verwachte reikwijdte: de bestanden die in de oorzaak van dit issue staan, plus een regressietest.
Author
Owner

Opgepakt. Tak: fix/vervolginspectie-3110. Verwachte reikwijdte: lib/collab/, lib/state/deck_provider.dart, lib/widgets/panels/editor_panel.dart.

Opgepakt. Tak: fix/vervolginspectie-3110. Verwachte reikwijdte: lib/collab/, lib/state/deck_provider.dart, lib/widgets/panels/editor_panel.dart.
Author
Owner

Staat op main in a6b4dc066 (5ca53eed8, 6779d989f). Een eigen aanslag schrijft het deck niet terug en hermount de editor niet. Een co-auteur blijft zichtbaar na de eigen echo. Een widgettoets die drie tekens typt en hetzelfde focusknooppunt vasthoudt is niet geschreven.

Staat op main in a6b4dc066 (5ca53eed8, 6779d989f). Een eigen aanslag schrijft het deck niet terug en hermount de editor niet. Een co-auteur blijft zichtbaar na de eigen echo. Een widgettoets die drie tekens typt en hetzelfde focusknooppunt vasthoudt is niet geschreven.
Author
Owner

Opgelost op main in a6b4dc066. De zin „het veld verliest de focus en de cursorpositie na vrijwel elke toetsaanslag“ is gedekt doordat een lege echo niets schrijft (lib/collab/collab_session_controller.dart) en applyCollabDeck de revisie alleen ophoogt als de open dia verandert (lib/state/deck_provider.dart). Niet meegenomen: de widgettoets die drie tekens typt en hetzelfde focusknooppunt vasthoudt. De eenheidstoetsen dekken wel dat de host writeDeck niet aanroept en dat de gast abc houdt.

Opgelost op main in a6b4dc066. De zin „het veld verliest de focus en de cursorpositie na vrijwel elke toetsaanslag“ is gedekt doordat een lege echo niets schrijft (lib/collab/collab_session_controller.dart) en applyCollabDeck de revisie alleen ophoogt als de open dia verandert (lib/state/deck_provider.dart). Niet meegenomen: de widgettoets die drie tekens typt en hetzelfde focusknooppunt vasthoudt. De eenheidstoetsen dekken wel dat de host writeDeck niet aanroept en dat de gast abc houdt.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
LibreKAT/Ocideck#2680
No description provided.