@@ -5,6 +5,8 @@ import { api } from "../../../scripts/api.js";
5
5
6
6
const CHECKPOINT_LOADER = "CheckpointLoader|pysssss" ;
7
7
const LORA_LOADER = "LoraLoader|pysssss" ;
8
+ const IMAGE_WIDTH = 384 ;
9
+ const IMAGE_HEIGHT = 384 ;
8
10
9
11
function getType ( node ) {
10
12
if ( node . comfyClass === CHECKPOINT_LOADER ) {
@@ -13,6 +15,50 @@ function getType(node) {
13
15
return "loras" ;
14
16
}
15
17
18
+ const getImage = ( imageId ) => document . querySelector ( `#${ CSS . escape ( imageId ) } ` ) ;
19
+
20
+ const calculateImagePosition = ( el , rootRect , bodyRect ) => {
21
+ const { x } = el . getBoundingClientRect ( ) ;
22
+ let { top, left } = rootRect ;
23
+ const { width : bodyWidth , height : bodyHeight } = bodyRect ;
24
+
25
+ const isSpaceRight = x + IMAGE_WIDTH <= bodyWidth ;
26
+ if ( isSpaceRight ) {
27
+ left += rootRect . width + IMAGE_WIDTH ;
28
+ }
29
+
30
+ const isSpaceBelow = rootRect . top + IMAGE_HEIGHT <= bodyHeight ;
31
+ if ( ! isSpaceBelow ) {
32
+ top = bodyHeight - IMAGE_HEIGHT ;
33
+ }
34
+
35
+ return { left : Math . round ( left ) , top : Math . round ( top ) } ;
36
+ } ;
37
+
38
+ function showImage ( el , imageId , getRootRect ) {
39
+ const img = getImage ( imageId ) ;
40
+ if ( img ) {
41
+ const rootRect = getRootRect ( ) ;
42
+ if ( ! rootRect ) return ;
43
+
44
+ const bodyRect = document . body . getBoundingClientRect ( ) ;
45
+ if ( ! bodyRect ) return ;
46
+
47
+ const { left, top } = calculateImagePosition ( el , rootRect , bodyRect ) ;
48
+
49
+ img . style . display = "block" ;
50
+ img . style . left = `${ left } px` ;
51
+ img . style . top = `${ top } px` ;
52
+ }
53
+ }
54
+
55
+ function closeImage ( imageId ) {
56
+ const img = getImage ( imageId ) ;
57
+ if ( img ) {
58
+ img . style . display = "none" ;
59
+ }
60
+ }
61
+
16
62
app . registerExtension ( {
17
63
name : "pysssss.Combo++" ,
18
64
init ( ) {
@@ -27,8 +73,8 @@ app.registerExtension({
27
73
left: 0;
28
74
top: 0;
29
75
transform: translate(-100%, 0);
30
- width: 384px ;
31
- height: 384px ;
76
+ width: ${ IMAGE_WIDTH } px ;
77
+ height: ${ IMAGE_HEIGHT } px ;
32
78
background-size: contain;
33
79
background-position: top right;
34
80
background-repeat: no-repeat;
@@ -81,13 +127,23 @@ app.registerExtension({
81
127
// After an element is created for an item, add an image if it has one
82
128
contextMenuHook [ "addItem" ] . push ( function ( el , menu , [ name , value , options ] ) {
83
129
if ( el && isCustomItem ( value ) && value ?. image && ! value . submenu ) {
130
+ const key = `pysssss-image-combo-${ name } ` ;
84
131
el . textContent += " *" ;
85
- $el ( "div.pysssss-combo-image" , {
86
- parent : el ,
132
+ $el ( `div.pysssss-combo-image` , {
133
+ id : key ,
134
+ parent : document . body ,
87
135
style : {
88
136
backgroundImage : `url(/pysssss/view/${ encodeRFC3986URIComponent ( value . image ) } )` ,
89
137
} ,
90
138
} ) ;
139
+
140
+ const getRootRect = ( ) => menu . root ?. getBoundingClientRect ( ) ;
141
+ const showHandler = ( ) => showImage ( el , key , getRootRect ) ;
142
+ const closeHandler = ( ) => closeImage ( key ) ;
143
+
144
+ el . addEventListener ( "mouseenter" , showHandler , { passive : true } ) ;
145
+ el . addEventListener ( "mouseleave" , closeHandler , { passive : true } ) ;
146
+ el . addEventListener ( "click" , closeHandler , { passive : true } ) ;
91
147
}
92
148
} ) ;
93
149
0 commit comments