You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The touch pointer is a plugin that add a pointer to the slide view.
From the presenter view, you can move the pointer by touching the slide view and it moves in the same way on stage view.
To activate this plugin, click on Presenter view "plugins" button menu and select "TouchPointerInput".
Then, to deactivate the plugin, click on the "Fermer" button at the top right.
When the plugin is activated, the pointer div is added on the fly to the view.
When the plugin is deactivated, it should be removed from the shadow DOM.
Checkout the develop branch
Open the file src\plugins\input\touch-pointer\index.js
Look for _removePointer function definition
The line pointer.remove(); doesn't work
You can see and confirm the problem by deactivating the plugin following the previous process and look for the pointer div in the dev tools.
If you activate / deactivate the plugin several times you'll see that you'll have several pointer div in the source code.
Re-tested on develop @ v0.6.0 — the reported symptom is gone, but the teardown is still incomplete
pointer.remove() works now
Measured on the real app, three activate/deactivate cycles from the presenter view, counting #pointer nodes in currentSlide.shadowRoot:
cycle 1 → after activation: 1 | after "Fermer": 0
cycle 2 → after activation: 1 | after "Fermer": 0
cycle 3 → after activation: 1 | after "Fermer": 0
No accumulation. The 2020 code looked the pointer up in the light DOM; the current _removePointer queries currentSlide.shadowRoot.getElementById('pointer') (src/plugins/input/touch-pointer/index.ts), which resolves correctly.
But unload() still leaks four things
Re-scoping this issue rather than closing it, because "the remove process" is genuinely still broken — just not at the line the issue points to.
1. callbacks grows without bound. Instrumented across the same three cycles:
activateOnComponent pushes a new callback via plugin.instance.onEvent(...) on every activation (src/common/services/plugin.ts), and nothing ever purges the array. After N cycles every pointerMove is broadcast N times.
2. The setInterval is never cleared._setPointer schedules an interval to hide the pointer after 2s and stores it in this.interval. unload() does not clearInterval it, so it keeps firing for the lifetime of the page.
3. Frame styles are not restored._addPointer sets width, height, transitionDuration and cursor: zoom-in on #slideViewFrame. _removePointer removes the pointer div but leaves all four in place, so the slide frame keeps a zoom cursor with no zoom behind it.
4. Deactivating while zoomed strands the view. The transform applied by _toggleZoom survives unload(), and this.zooming stays true. The slide is left off-screen with no way to bring it back short of a reload.
Suggested fix — make the lifecycle symmetric in the base class
Points 1–4 are one defect wearing four hats: Plugin declares init() / unload() but only init() is ever really implemented. The same gap exists in keyboard and touch, which register global listeners in init() and inherit the no-op unload().
Plugin.unload() (base) clears this.callbacks and resets initialized.
TouchPointerInput.unload() clears the interval, resets transform / cursor / transitionDuration on the frame, and sets zooming = false.
Keep a reference to the message handler so removeEventListener is actually possible — it is currently registered with an inline arrow function, so it can never be detached.
changed the title [-]Fix the touch pointer remove process[/-][+]Complete the touch pointer plugin teardown (unload leaks callbacks, interval, frame styles, zoom state)[/+]on Aug 11, 2026
Le teardown est désormais symétrique : Plugin.unload() purge les callbacks et initialized, touch-pointer nettoie son timer, restaure les styles du frame et remet le zoom à zéro, et keyboard/touch détachent leurs listeners. Vérifié sur l'app réelle : callbacks plafonne à 1 au lieu de grimper 1 → 2 → 3 sur trois cycles.
Fermeture manuelle (le Closes #42 ne se déclenche que sur merge dans la branche par défaut, main).
Activity
jefBinomed commentedon Aug 11, 2026
Re-tested on
develop@ v0.6.0 — the reported symptom is gone, but the teardown is still incompletepointer.remove()works nowMeasured on the real app, three activate/deactivate cycles from the presenter view, counting
#pointernodes incurrentSlide.shadowRoot:No accumulation. The 2020 code looked the pointer up in the light DOM; the current
_removePointerqueriescurrentSlide.shadowRoot.getElementById('pointer')(src/plugins/input/touch-pointer/index.ts), which resolves correctly.But
unload()still leaks four thingsRe-scoping this issue rather than closing it, because "the remove process" is genuinely still broken — just not at the line the issue points to.
1.
callbacksgrows without bound. Instrumented across the same three cycles:activateOnComponentpushes a new callback viaplugin.instance.onEvent(...)on every activation (src/common/services/plugin.ts), and nothing ever purges the array. After N cycles everypointerMoveis broadcast N times.2. The
setIntervalis never cleared._setPointerschedules an interval to hide the pointer after 2s and stores it inthis.interval.unload()does notclearIntervalit, so it keeps firing for the lifetime of the page.3. Frame styles are not restored.
_addPointersetswidth,height,transitionDurationandcursor: zoom-inon#slideViewFrame._removePointerremoves the pointer div but leaves all four in place, so the slide frame keeps a zoom cursor with no zoom behind it.4. Deactivating while zoomed strands the view. The
transformapplied by_toggleZoomsurvivesunload(), andthis.zoomingstaystrue. The slide is left off-screen with no way to bring it back short of a reload.Suggested fix — make the lifecycle symmetric in the base class
Points 1–4 are one defect wearing four hats:
Plugindeclaresinit()/unload()but onlyinit()is ever really implemented. The same gap exists inkeyboardandtouch, which register global listeners ininit()and inherit the no-opunload().Plugin.unload()(base) clearsthis.callbacksand resetsinitialized.TouchPointerInput.unload()clears the interval, resetstransform/cursor/transitionDurationon the frame, and setszooming = false.messagehandler soremoveEventListeneris actually possible — it is currently registered with an inline arrow function, so it can never be detached.Retitling to reflect what is actually left to do.
[-]Fix the touch pointer remove process[/-][+]Complete the touch pointer plugin teardown (unload leaks callbacks, interval, frame styles, zoom state)[/+]🐛 fix(plugins): rendre le cycle init/unload symétrique (#42)
Merge pull request #71 from TalkControl/fix/zoom-referentiel
jefBinomed commentedon Aug 13, 2026
Corrigé par #71, mergé dans
develop(5a9afd6).Le teardown est désormais symétrique :
Plugin.unload()purge les callbacks etinitialized, touch-pointer nettoie son timer, restaure les styles du frame et remet le zoom à zéro, et keyboard/touch détachent leurs listeners. Vérifié sur l'app réelle :callbacksplafonne à 1 au lieu de grimper 1 → 2 → 3 sur trois cycles.Fermeture manuelle (le
Closes #42ne se déclenche que sur merge dans la branche par défaut,main).Merge pull request #75 from TalkControl/develop