5
5
title =" Missing Models"
6
6
message =" When loading the graph, the following models were not found"
7
7
/>
8
- <ListBox
9
- :options =" missingModels"
10
- optionLabel =" label"
11
- scrollHeight =" 100%"
12
- class =" comfy-missing-models"
13
- >
14
- <template #option =" slotProps " >
15
- <div
16
- class =" missing-model-item flex flex-row items-center"
17
- :style =" { '--progress': `${slotProps.option.progress}%` }"
18
- >
19
- <div class =" model-info" >
20
- <div class =" model-details" >
21
- <span class =" model-type" :title =" slotProps.option.hint" >{{
22
- slotProps.option.label
23
- }}</span >
24
- </div >
25
- <div v-if =" slotProps.option.error" class =" model-error" >
26
- {{ slotProps.option.error }}
27
- </div >
28
- </div >
29
- <div class =" model-action" >
30
- <Select
31
- class =" model-path-select mr-2"
32
- v-if ="
33
- slotProps.option.action &&
34
- !slotProps.option.downloading &&
35
- !slotProps.option.completed &&
36
- !slotProps.option.error
37
- "
38
- v-show =" slotProps.option.paths.length > 1"
39
- v-model =" slotProps.option.folderPath"
40
- :options =" slotProps.option.paths"
41
- @change =" updateFolderPath(slotProps.option, $event)"
42
- />
43
- <Button
44
- v-if ="
45
- slotProps.option.action &&
46
- !slotProps.option.downloading &&
47
- !slotProps.option.completed &&
48
- !slotProps.option.error
49
- "
50
- @click =" slotProps.option.action.callback"
51
- :label =" slotProps.option.action.text"
52
- size =" small"
53
- outlined
54
- class =" model-action-button"
55
- />
56
- <div v-if =" slotProps.option.downloading" class =" download-progress" >
57
- <span class =" progress-text"
58
- >{{ slotProps.option.progress.toFixed(2) }}%</span
59
- >
60
- </div >
61
- <div v-if =" slotProps.option.completed" class =" download-complete" >
62
- <i class =" pi pi-check" style =" color : var (--p-green-500 )" ></i >
63
- </div >
64
- <div v-if =" slotProps.option.error" class =" download-error" >
65
- <i class =" pi pi-times" style =" color : var (--p-red-600 )" ></i >
66
- </div >
67
- </div >
68
- </div >
8
+ <ListBox :options =" missingModels" class =" comfy-missing-models" >
9
+ <template #option =" { option } " >
10
+ <FileDownload
11
+ :url =" option.url"
12
+ :label =" option.label"
13
+ :error =" option.error"
14
+ />
69
15
</template >
70
16
</ListBox >
71
17
</template >
72
18
73
19
<script setup lang="ts">
74
20
import { ref , computed } from ' vue'
75
21
import ListBox from ' primevue/listbox'
76
- import Select from ' primevue/select'
77
22
import NoResultsPlaceholder from ' @/components/common/NoResultsPlaceholder.vue'
78
- import { SelectChangeEvent } from ' primevue/select'
79
- import Button from ' primevue/button'
80
- import { api } from ' @/scripts/api'
81
- import { DownloadModelStatus } from ' @/types/apiTypes'
23
+ import FileDownload from ' @/components/common/FileDownload.vue'
82
24
83
25
// TODO: Read this from server internal API rather than hardcoding here
84
26
// as some installations may wish to use custom sources
@@ -107,86 +49,13 @@ const props = defineProps<{
107
49
}>()
108
50
109
51
const modelDownloads = ref <Record <string , ModelInfo >>({})
110
- let lastModel: string | null = null
111
-
112
- const updateFolderPath = (model : any , event : SelectChangeEvent ) => {
113
- const downloadInfo = modelDownloads .value [model .name ]
114
- downloadInfo .folder_path = event .value
115
- return false
116
- }
117
- const handleDownloadProgress = (detail : DownloadModelStatus ) => {
118
- if (detail .download_path ) {
119
- lastModel = detail .download_path
120
- }
121
- if (! lastModel ) return
122
- if (detail .status === ' in_progress' ) {
123
- modelDownloads .value [lastModel ] = {
124
- ... modelDownloads .value [lastModel ],
125
- downloading: true ,
126
- progress: detail .progress_percentage ,
127
- completed: false
128
- }
129
- } else if (detail .status === ' pending' ) {
130
- modelDownloads .value [lastModel ] = {
131
- ... modelDownloads .value [lastModel ],
132
- downloading: true ,
133
- progress: 0 ,
134
- completed: false
135
- }
136
- } else if (detail .status === ' completed' ) {
137
- modelDownloads .value [lastModel ] = {
138
- ... modelDownloads .value [lastModel ],
139
- downloading: false ,
140
- progress: 100 ,
141
- completed: true
142
- }
143
- } else if (detail .status === ' error' ) {
144
- modelDownloads .value [lastModel ] = {
145
- ... modelDownloads .value [lastModel ],
146
- downloading: false ,
147
- progress: 0 ,
148
- error: detail .message ,
149
- completed: false
150
- }
151
- }
152
- // TODO: other statuses?
153
- }
154
-
155
- const triggerDownload = async (
156
- url : string ,
157
- directory : string ,
158
- filename : string ,
159
- folder_path : string
160
- ) => {
161
- modelDownloads .value [filename ] = {
162
- name: filename ,
163
- directory ,
164
- url ,
165
- downloading: true ,
166
- progress: 0
167
- }
168
- const download = await api .internalDownloadModel (
169
- url ,
170
- directory ,
171
- filename ,
172
- 1 ,
173
- folder_path
174
- )
175
- lastModel = filename
176
- handleDownloadProgress (download )
177
- }
178
-
179
- api .addEventListener (' download_progress' , (event : CustomEvent ) => {
180
- handleDownloadProgress (event .detail )
181
- })
182
-
183
52
const missingModels = computed (() => {
184
53
return props .missingModels .map ((model ) => {
185
54
const paths = props .paths [model .directory ]
186
55
if (model .directory_invalid || ! paths ) {
187
56
return {
188
57
label: ` ${model .directory } / ${model .name } ` ,
189
- hint : model .url ,
58
+ url : model .url ,
190
59
error: ' Invalid directory specified (does this require custom nodes?)'
191
60
}
192
61
}
@@ -204,37 +73,27 @@ const missingModels = computed(() => {
204
73
if (! allowedSources .some ((source ) => model .url .startsWith (source ))) {
205
74
return {
206
75
label: ` ${model .directory } / ${model .name } ` ,
207
- hint : model .url ,
76
+ url : model .url ,
208
77
error: ` Download not allowed from source '${model .url }', only allowed from '${allowedSources .join (" ', '" )}' `
209
78
}
210
79
}
211
80
if (! allowedSuffixes .some ((suffix ) => model .name .endsWith (suffix ))) {
212
81
return {
213
82
label: ` ${model .directory } / ${model .name } ` ,
214
- hint : model .url ,
83
+ url : model .url ,
215
84
error: ` Only allowed suffixes are: '${allowedSuffixes .join (" ', '" )}' `
216
85
}
217
86
}
218
87
return {
88
+ url: model .url ,
219
89
label: ` ${model .directory } / ${model .name } ` ,
220
- hint: model .url ,
221
90
downloading: downloadInfo .downloading ,
222
91
completed: downloadInfo .completed ,
223
92
progress: downloadInfo .progress ,
224
93
error: downloadInfo .error ,
225
94
name: model .name ,
226
95
paths: paths ,
227
- folderPath: downloadInfo .folder_path ,
228
- action: {
229
- text: ' Download' ,
230
- callback : () =>
231
- triggerDownload (
232
- model .url ,
233
- model .directory ,
234
- model .name ,
235
- downloadInfo .folder_path
236
- )
237
- }
96
+ folderPath: downloadInfo .folder_path
238
97
}
239
98
})
240
99
})
@@ -245,85 +104,4 @@ const missingModels = computed(() => {
245
104
max-height : 300px ;
246
105
overflow-y : auto ;
247
106
}
248
-
249
- .missing-model-item ::before {
250
- content : ' ' ;
251
- position : absolute ;
252
- top : 0 ;
253
- left : 0 ;
254
- height : 100% ;
255
- width : var (--progress );
256
- background-color : var (--p-green-500 );
257
- opacity : 0.2 ;
258
- transition : width 0.3s ease ;
259
- }
260
-
261
- .model-info {
262
- flex : 1 ;
263
- min-width : 0 ;
264
- z-index : 1 ;
265
- display : flex ;
266
- flex-direction : column ;
267
- margin-right : 1rem ;
268
- overflow : hidden ;
269
- }
270
-
271
- .model-details {
272
- display : flex ;
273
- align-items : center ;
274
- flex-wrap : wrap ;
275
- }
276
-
277
- .model-type {
278
- font-weight : 600 ;
279
- color : var (--text-color );
280
- margin-right : 0.5rem ;
281
- white-space : nowrap ;
282
- overflow : hidden ;
283
- text-overflow : ellipsis ;
284
- }
285
-
286
- .model-hint {
287
- font-style : italic ;
288
- color : var (--text-color-secondary );
289
- white-space : nowrap ;
290
- overflow : hidden ;
291
- text-overflow : ellipsis ;
292
- }
293
-
294
- .model-error {
295
- color : var (--p-red-600 );
296
- font-size : 0.8rem ;
297
- margin-top : 0.25rem ;
298
- }
299
-
300
- .model-action {
301
- display : flex ;
302
- align-items : center ;
303
- justify-content : flex-end ;
304
- z-index : 1 ;
305
- }
306
-
307
- .model-action-button {
308
- min-width : 80px ;
309
- }
310
-
311
- .download-progress ,
312
- .download-complete ,
313
- .download-error {
314
- display : flex ;
315
- align-items : center ;
316
- justify-content : center ;
317
- min-width : 80px ;
318
- }
319
-
320
- .progress-text {
321
- font-size : 0.8rem ;
322
- color : var (--text-color );
323
- }
324
-
325
- .download-complete i ,
326
- .download-error i {
327
- font-size : 1.2rem ;
328
- }
329
107
</style >
0 commit comments