1
1
/*
2
2
Create stable unsafe HTML DOM node. This is a way to render HTML that stays stable
3
3
irregardless of it being unmounted/remounted.
4
- This supports virtualization, window splitting, etc., without loss of state...
4
+
5
+ This supports virtualization, window splitting, etc., without loss of state,
5
6
unless there are too many of them, then we delete the oldest.
6
7
8
+ By default, the HTML is just directly put into the DOM exactly as is, except that
9
+ we *do* process links so internal references work and math using katex.
10
+
7
11
Unsafe is in the name since there is NO SANITIZATION. Only use this on trusted
8
12
documents.
13
+
14
+ Elements only get re-rendered when for IDLE_TIMEOUT_S, both:
15
+
16
+ - the underlying react element does not exist, AND
17
+ - the parent is not scrolled at all.
18
+
19
+ OR
20
+
21
+ - if there are more than MAX_ELEMENTS, then the oldest are removed (to avoid catastrophic memory usage).
22
+
23
+ If for any reason the react element exists or the parent is scrolled, then
24
+ the idle timeout is reset.
9
25
*/
10
26
11
27
import { useCallback , useEffect , useRef } from "react" ;
12
28
import $ from "jquery" ;
13
29
import { useFrameContext } from "@cocalc/frontend/frame-editors/frame-tree/frame-context" ;
14
30
import { useIFrameContext } from "@cocalc/frontend/jupyter/cell-list" ;
15
31
import { sha1 } from "@cocalc/util/misc" ;
32
+ import TTL from "@isaacs/ttlcache" ;
16
33
17
- interface Props {
18
- docId : string ;
19
- html : string ;
20
- zIndex ?: number ;
21
- }
22
-
23
- import LRU from "lru-cache" ;
34
+ const IDLE_TIMEOUT_S = 10 * 60 ; // 10 minutes
35
+ const MAX_ELEMENTS = 500 ; // max items
24
36
25
- const CACHE_SIZE = 100 ;
26
-
27
- const immortals = new LRU < string , any > ( {
28
- max : CACHE_SIZE ,
37
+ const cache = new TTL < string , any > ( {
38
+ ttl : IDLE_TIMEOUT_S * 1000 ,
39
+ max : MAX_ELEMENTS ,
29
40
updateAgeOnGet : true ,
30
- updateAgeOnHas : true ,
31
41
dispose : ( elt ) => {
32
42
elt . empty ( ) ;
33
43
elt . remove ( ) ;
34
44
} ,
35
45
} ) ;
36
- // const immortals : { [globalKey: string]: any } = {};
46
+ // const cache : { [globalKey: string]: any } = {};
37
47
38
48
const Z_INDEX = 1 ;
39
49
@@ -52,6 +62,12 @@ const SCROLL_COUNT = 25;
52
62
const PADDING = 0 ;
53
63
const STYLE = { } as const ;
54
64
65
+ interface Props {
66
+ docId : string ;
67
+ html : string ;
68
+ zIndex ?: number ;
69
+ }
70
+
55
71
export default function StableUnsafeHtml ( {
56
72
docId,
57
73
html,
@@ -134,18 +150,19 @@ export default function StableUnsafeHtml({
134
150
} , [ ] ) ;
135
151
136
152
const getElt = ( ) => {
137
- console . log ( "size" , immortals . size ) ;
138
- if ( ! immortals . has ( globalKey ) ) {
139
- console . log ( "cache miss" , globalKey ) ;
153
+ if ( ! cache . has ( globalKey ) ) {
140
154
const elt = $ (
141
155
`<div id="${ globalKey } " style="border:0;position:absolute;overflow-y:hidden;z-index:${ zIndex } "/>${ html } </div>` ,
142
156
) ;
143
- immortals . set ( globalKey , elt ) ;
157
+ // @ts -ignore
158
+ elt . process_smc_links ( ) ;
159
+ // @ts -ignore
160
+ elt . katex ( { preProcess : true } ) ;
161
+ cache . set ( globalKey , elt ) ;
144
162
$ ( "body" ) . append ( elt ) ;
145
163
return elt ;
146
164
} else {
147
- console . log ( "cache hit" , globalKey ) ;
148
- return immortals . get ( globalKey ) ;
165
+ return cache . get ( globalKey ) ;
149
166
}
150
167
} ;
151
168
0 commit comments