mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-17 20:26:35 +02:00
v0.4.2 : refresh instantane du chat (fin du layout thrashing O(n2) au replay)
Au refresh, le client rejouait tout le journal depuis seq 0 et forcait un reflow synchrone par evenement (scrollMaybe lisait scrollHeight ; collapseInstant lisait offsetHeight) sur un DOM grandissant -> cout quadratique, 20-30s pour que les derniers messages s'affichent. Le scroll est desormais fait une seule fois a la fin du replay (caught_up), plus aucun reflow force pendant le rejeu -> O(n).
This commit is contained in:
1 parent
e16bebda8a
commit
7d104e3213
5 files changed
+18
-7
No files matched your search
@@ -16,7 +16,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const Version = "0.4.1"
|
||||
const Version = "0.4.2"
|
||||
|
||||
func main() {
|
||||
// Migration one-shot des anciens skills (SKILLS/<nom>/SKILL.md) vers la
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+14
-3
@@ -1188,6 +1188,11 @@ function isNearBottom(){
|
||||
return c.scrollHeight - c.scrollTop - c.clientHeight < 60;
|
||||
}
|
||||
function scrollMaybe(){
|
||||
// Pendant le replay initial on NE force AUCUN reflow : lire scrollHeight à chaque
|
||||
// événement rejoué = un layout synchrone forcé sur un DOM qui grossit → coût
|
||||
// quadratique (20-30 s de rendu au refresh sur un long fil). Le scroll est fait
|
||||
// une seule fois à la fin du replay, via jumpBottom() au signal {caught_up}.
|
||||
if(REPLAYING) return;
|
||||
if(stickyBottom){
|
||||
const c = chatEl();
|
||||
c.scrollTop = c.scrollHeight;
|
||||
@@ -1261,9 +1266,12 @@ function collapseAll(list){ for(const el of list){ if(el) collapseBody(el); } li
|
||||
function collapseInstant(el){
|
||||
const bw=el.querySelector('.bodywrap'); if(!bw) return;
|
||||
el.classList.add('collapsed');
|
||||
const t=bw.style.transition; bw.style.transition='none';
|
||||
// Pas de `void bw.offsetHeight` ici : la bulle vient d'être créée et n'a jamais
|
||||
// été peinte dépliée, donc poser height:0 n'anime pas — inutile de forcer un
|
||||
// reflow par bulle (ce qui, multiplié par le replay, coûtait très cher).
|
||||
bw.style.transition='none';
|
||||
bw.style.height='0px'; bw.style.width='0px';
|
||||
void bw.offsetHeight; bw.style.transition=t;
|
||||
requestAnimationFrame(()=>{ bw.style.transition=''; });
|
||||
}
|
||||
function setLabel(el, text){ el.querySelector('.label').textContent = text; }
|
||||
function bodyOf(el){ return el.querySelector('.body'); }
|
||||
@@ -1391,7 +1399,10 @@ function restoreChat(){
|
||||
// sauter en bas (effet de clignotement). Il est révélé, positionné en bas, au
|
||||
// signal {caught_up}. Filet de sécurité : révélé quoi qu'il arrive après 2s.
|
||||
const c=chatEl(); c.style.opacity='0';
|
||||
setTimeout(()=>{ c.style.transition='opacity .15s'; c.style.opacity='1'; }, 2000);
|
||||
// Si {caught_up} tarde au-delà de 2s (replay anormalement long), on révèle quand
|
||||
// même — et on saute en bas DIRECTEMENT (scrollMaybe est neutralisé tant que
|
||||
// REPLAYING, donc on force ici le positionnement).
|
||||
setTimeout(()=>{ c.style.transition='opacity .15s'; c.style.opacity='1'; c.scrollTop=c.scrollHeight; }, 2000);
|
||||
connectStream();
|
||||
}
|
||||
function onKey(e){ if(e.key==='Enter' && !e.shiftKey){ e.preventDefault(); send(); } }
|
||||
|
||||
+3
-3
@@ -3,13 +3,13 @@
|
||||
"FileVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 4,
|
||||
"Patch": 1,
|
||||
"Patch": 2,
|
||||
"Build": 0
|
||||
},
|
||||
"ProductVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 4,
|
||||
"Patch": 1,
|
||||
"Patch": 2,
|
||||
"Build": 0
|
||||
},
|
||||
"FileFlagsMask": "3f",
|
||||
@@ -25,7 +25,7 @@
|
||||
"LegalCopyright": "Copyright (c) 2026 Jean contributors. MIT License.",
|
||||
"OriginalFilename": "jean.exe",
|
||||
"ProductName": "Jean",
|
||||
"ProductVersion": "0.4.1",
|
||||
"ProductVersion": "0.4.2",
|
||||
"Comments": "https://github.com/nathaninline/jean — projet open source (MIT)"
|
||||
},
|
||||
"VarFileInfo": {
|
||||
|
||||
Reference in new issue
Block a user