ollamadiffuser 1.0.0__py3-none-any.whl → 1.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ollamadiffuser/api/server.py +147 -2
- ollamadiffuser/cli/main.py +325 -25
- ollamadiffuser/core/inference/engine.py +180 -9
- ollamadiffuser/core/models/manager.py +136 -2
- ollamadiffuser/core/utils/controlnet_preprocessors.py +317 -0
- ollamadiffuser/core/utils/download_utils.py +209 -60
- ollamadiffuser/ui/templates/index.html +384 -7
- ollamadiffuser/ui/web.py +181 -100
- ollamadiffuser-1.1.1.dist-info/METADATA +470 -0
- {ollamadiffuser-1.0.0.dist-info → ollamadiffuser-1.1.1.dist-info}/RECORD +14 -13
- ollamadiffuser-1.0.0.dist-info/METADATA +0 -493
- {ollamadiffuser-1.0.0.dist-info → ollamadiffuser-1.1.1.dist-info}/WHEEL +0 -0
- {ollamadiffuser-1.0.0.dist-info → ollamadiffuser-1.1.1.dist-info}/entry_points.txt +0 -0
- {ollamadiffuser-1.0.0.dist-info → ollamadiffuser-1.1.1.dist-info}/licenses/LICENSE +0 -0
- {ollamadiffuser-1.0.0.dist-info → ollamadiffuser-1.1.1.dist-info}/top_level.txt +0 -0
|
@@ -272,6 +272,130 @@
|
|
|
272
272
|
.btn-small.btn-danger:hover {
|
|
273
273
|
box-shadow: 0 5px 15px rgba(220, 38, 38, 0.3);
|
|
274
274
|
}
|
|
275
|
+
|
|
276
|
+
.controlnet-results {
|
|
277
|
+
display: grid;
|
|
278
|
+
grid-template-columns: 1fr 1fr;
|
|
279
|
+
gap: 20px;
|
|
280
|
+
margin-bottom: 20px;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
@media (max-width: 768px) {
|
|
284
|
+
.controlnet-results {
|
|
285
|
+
grid-template-columns: 1fr;
|
|
286
|
+
gap: 15px;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.controlnet-section {
|
|
291
|
+
background: #f0f9ff;
|
|
292
|
+
border: 1px solid #0ea5e9;
|
|
293
|
+
border-radius: 8px;
|
|
294
|
+
padding: 20px;
|
|
295
|
+
margin: 20px 0;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.controlnet-title {
|
|
299
|
+
color: #0369a1;
|
|
300
|
+
margin-bottom: 15px;
|
|
301
|
+
display: flex;
|
|
302
|
+
align-items: center;
|
|
303
|
+
gap: 8px;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.file-input {
|
|
307
|
+
width: 100%;
|
|
308
|
+
padding: 12px;
|
|
309
|
+
border: 1px solid #d1d5db;
|
|
310
|
+
border-radius: 8px;
|
|
311
|
+
font-size: 14px;
|
|
312
|
+
background: white;
|
|
313
|
+
cursor: pointer;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.file-input:hover {
|
|
317
|
+
border-color: #4f46e5;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.help-text {
|
|
321
|
+
color: #6b7280;
|
|
322
|
+
margin-top: 5px;
|
|
323
|
+
display: block;
|
|
324
|
+
font-size: 12px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.sample-item {
|
|
328
|
+
cursor: pointer;
|
|
329
|
+
border: 2px solid #e5e7eb;
|
|
330
|
+
border-radius: 6px;
|
|
331
|
+
padding: 8px;
|
|
332
|
+
background: white;
|
|
333
|
+
transition: all 0.2s;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.sample-item:hover {
|
|
337
|
+
border-color: #7c3aed;
|
|
338
|
+
box-shadow: 0 4px 8px -2px rgba(0, 0, 0, 0.1);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.sample-item img {
|
|
342
|
+
width: 100%;
|
|
343
|
+
height: 100px;
|
|
344
|
+
object-fit: cover;
|
|
345
|
+
border-radius: 4px;
|
|
346
|
+
margin-bottom: 6px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.sample-item div {
|
|
350
|
+
text-align: center;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.sample-item div div {
|
|
354
|
+
font-weight: 600;
|
|
355
|
+
color: #374151;
|
|
356
|
+
font-size: 14px;
|
|
357
|
+
margin-bottom: 4px;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.sample-item div p {
|
|
361
|
+
color: #6b7280;
|
|
362
|
+
font-size: 12px;
|
|
363
|
+
line-height: 1.3;
|
|
364
|
+
margin-bottom: 6px;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.sample-item div div span {
|
|
368
|
+
background: #e0e7ff;
|
|
369
|
+
color: #3730a3;
|
|
370
|
+
padding: 2px 6px;
|
|
371
|
+
border-radius: 12px;
|
|
372
|
+
font-size: 10px;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.control-tab {
|
|
376
|
+
padding: 8px 12px;
|
|
377
|
+
border: none;
|
|
378
|
+
background: #f8fafc;
|
|
379
|
+
border-bottom: 2px solid transparent;
|
|
380
|
+
cursor: pointer;
|
|
381
|
+
font-size: 13px;
|
|
382
|
+
color: #6b7280;
|
|
383
|
+
transition: all 0.2s;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.control-tab:hover {
|
|
387
|
+
background: #e2e8f0;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.active-tab {
|
|
391
|
+
border-bottom: 2px solid #7c3aed !important;
|
|
392
|
+
color: #374151 !important;
|
|
393
|
+
font-weight: 500;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.tab-content {
|
|
397
|
+
min-height: 80px;
|
|
398
|
+
}
|
|
275
399
|
</style>
|
|
276
400
|
</head>
|
|
277
401
|
<body>
|
|
@@ -289,6 +413,16 @@
|
|
|
289
413
|
{% if current_model %}
|
|
290
414
|
<div class="status-item">
|
|
291
415
|
<span>Current Model: <strong>{{ current_model }}</strong></span>
|
|
416
|
+
{% if is_controlnet_model %}
|
|
417
|
+
<span style="color: #7c3aed; font-weight: bold; margin-left: 8px;">🎛️ ControlNet ({{ controlnet_type }})</span>
|
|
418
|
+
{% endif %}
|
|
419
|
+
{% if model_parameters %}
|
|
420
|
+
<div style="margin-top: 5px; font-size: 12px; color: #6b7280;">
|
|
421
|
+
📋 Recommended:
|
|
422
|
+
{% if model_parameters.num_inference_steps %}Steps: {{ model_parameters.num_inference_steps }}{% endif %}
|
|
423
|
+
{% if model_parameters.guidance_scale %} | Guidance: {{ model_parameters.guidance_scale }}{% endif %}
|
|
424
|
+
</div>
|
|
425
|
+
{% endif %}
|
|
292
426
|
</div>
|
|
293
427
|
{% endif %}
|
|
294
428
|
<div class="status-item">
|
|
@@ -339,7 +473,7 @@
|
|
|
339
473
|
</div>
|
|
340
474
|
|
|
341
475
|
<div class="model-management">
|
|
342
|
-
<h3>🔄 LoRA Management</h3>
|
|
476
|
+
<h3>🔄 LoRA Management(style, content, or identity)</h3>
|
|
343
477
|
|
|
344
478
|
<!-- Download LoRA Section -->
|
|
345
479
|
<form method="post" action="/pull_lora" style="margin-bottom: 15px;">
|
|
@@ -425,7 +559,7 @@
|
|
|
425
559
|
</div>
|
|
426
560
|
{% endif %}
|
|
427
561
|
|
|
428
|
-
<form method="post" action="/generate" id="generateForm">
|
|
562
|
+
<form method="post" action="/generate" id="generateForm" enctype="multipart/form-data">
|
|
429
563
|
<div class="form-group">
|
|
430
564
|
<label for="prompt">Prompt</label>
|
|
431
565
|
<textarea name="prompt" id="prompt" rows="3" placeholder="Describe the image you want to generate..." required>{{ prompt or '' }}</textarea>
|
|
@@ -439,24 +573,119 @@
|
|
|
439
573
|
<div class="form-row">
|
|
440
574
|
<div class="form-group">
|
|
441
575
|
<label for="width">Width</label>
|
|
442
|
-
<input type="number" name="width" id="width" value="{{ width or 1024 }}" min="512" max="2048" step="64">
|
|
576
|
+
<input type="number" name="width" id="width" value="{{ width or model_parameters.get('width', 1024) }}" min="512" max="2048" step="64">
|
|
577
|
+
{% if model_parameters.get('width') %}
|
|
578
|
+
<small class="help-text">📋 Recommended for {{ current_model }}: {{ model_parameters.width }}px</small>
|
|
579
|
+
{% endif %}
|
|
443
580
|
</div>
|
|
444
581
|
<div class="form-group">
|
|
445
582
|
<label for="height">Height</label>
|
|
446
|
-
<input type="number" name="height" id="height" value="{{ height or 1024 }}" min="512" max="2048" step="64">
|
|
583
|
+
<input type="number" name="height" id="height" value="{{ height or model_parameters.get('height', 1024) }}" min="512" max="2048" step="64">
|
|
584
|
+
{% if model_parameters.get('height') %}
|
|
585
|
+
<small class="help-text">📋 Recommended for {{ current_model }}: {{ model_parameters.height }}px</small>
|
|
586
|
+
{% endif %}
|
|
447
587
|
</div>
|
|
448
588
|
</div>
|
|
449
589
|
|
|
450
590
|
<div class="form-row">
|
|
451
591
|
<div class="form-group">
|
|
452
592
|
<label for="num_inference_steps">Inference Steps</label>
|
|
453
|
-
<input type="number" name="num_inference_steps" id="num_inference_steps" value="{{ num_inference_steps or 28 }}" min="1" max="100">
|
|
593
|
+
<input type="number" name="num_inference_steps" id="num_inference_steps" value="{{ num_inference_steps or model_parameters.get('num_inference_steps', 28) }}" min="1" max="100">
|
|
594
|
+
{% if model_parameters.get('num_inference_steps') %}
|
|
595
|
+
<small class="help-text">📋 Recommended for {{ current_model }}: {{ model_parameters.num_inference_steps }}</small>
|
|
596
|
+
{% endif %}
|
|
454
597
|
</div>
|
|
455
598
|
<div class="form-group">
|
|
456
599
|
<label for="guidance_scale">Guidance Scale</label>
|
|
457
|
-
<input type="number" name="guidance_scale" id="guidance_scale" value="{{ guidance_scale or 3.5 }}" min="0" max="20" step="0.1">
|
|
600
|
+
<input type="number" name="guidance_scale" id="guidance_scale" value="{{ guidance_scale or model_parameters.get('guidance_scale', 3.5) }}" min="0" max="20" step="0.1">
|
|
601
|
+
{% if model_parameters.get('guidance_scale') %}
|
|
602
|
+
<small class="help-text">📋 Recommended for {{ current_model }}: {{ model_parameters.guidance_scale }}</small>
|
|
603
|
+
{% endif %}
|
|
604
|
+
</div>
|
|
605
|
+
</div>
|
|
606
|
+
|
|
607
|
+
<!-- ControlNet Section -->
|
|
608
|
+
{% if is_controlnet_model %}
|
|
609
|
+
<div class="controlnet-section">
|
|
610
|
+
<h3 class="controlnet-title">
|
|
611
|
+
🎛️ ControlNet Controls(structure, composition, and spatial layout) ({{ controlnet_type|title }})
|
|
612
|
+
</h3>
|
|
613
|
+
|
|
614
|
+
<!-- ControlNet Status (Compact) -->
|
|
615
|
+
{% if not controlnet_initialized %}
|
|
616
|
+
<div style="background: #fef3c7; border: 1px solid #f59e0b; border-radius: 4px; padding: 8px; margin-bottom: 10px; font-size: 12px;">
|
|
617
|
+
<span style="color: #92400e;">⚠️ Not initialized</span>
|
|
618
|
+
<button type="button" onclick="initializeControlNet()" style="margin-left: 10px; padding: 4px 8px; background: #f59e0b; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">
|
|
619
|
+
Initialize
|
|
620
|
+
</button>
|
|
621
|
+
</div>
|
|
622
|
+
{% else %}
|
|
623
|
+
<div style="background: #d1fae5; border: 1px solid #10b981; border-radius: 4px; padding: 6px; margin-bottom: 10px; font-size: 12px; color: #065f46;">
|
|
624
|
+
✅ Ready
|
|
625
|
+
</div>
|
|
626
|
+
{% endif %}
|
|
627
|
+
|
|
628
|
+
<!-- Tabbed Interface -->
|
|
629
|
+
<div style="margin-bottom: 15px;">
|
|
630
|
+
<div style="display: flex; border-bottom: 1px solid #e5e7eb; margin-bottom: 10px;">
|
|
631
|
+
<button type="button" onclick="switchControlTab('upload')" id="uploadTab" class="control-tab active-tab" style="flex: 1; padding: 8px 12px; border: none; background: #f8fafc; border-bottom: 2px solid #7c3aed; cursor: pointer; font-size: 13px; font-weight: 500;">
|
|
632
|
+
📁 Upload Image
|
|
633
|
+
</button>
|
|
634
|
+
<button type="button" onclick="switchControlTab('samples')" id="samplesTab" class="control-tab" style="flex: 1; padding: 8px 12px; border: none; background: #f8fafc; border-bottom: 2px solid transparent; cursor: pointer; font-size: 13px; color: #6b7280;">
|
|
635
|
+
🎨 Samples ({{ sample_metadata[controlnet_type]|length if sample_metadata and controlnet_type in sample_metadata else 0 }})
|
|
636
|
+
</button>
|
|
637
|
+
<button type="button" onclick="switchControlTab('settings')" id="settingsTab" class="control-tab" style="flex: 1; padding: 8px 12px; border: none; background: #f8fafc; border-bottom: 2px solid transparent; cursor: pointer; font-size: 13px; color: #6b7280;">
|
|
638
|
+
⚙️ Settings
|
|
639
|
+
</button>
|
|
640
|
+
</div>
|
|
641
|
+
|
|
642
|
+
<!-- Upload Tab -->
|
|
643
|
+
<div id="uploadTabContent" class="tab-content">
|
|
644
|
+
<div class="form-group" style="margin-bottom: 10px;">
|
|
645
|
+
<input type="file" name="control_image" id="control_image" accept="image/*" class="file-input" style="padding: 8px; font-size: 13px;">
|
|
646
|
+
<small class="help-text" style="font-size: 11px;">Upload image for {{ controlnet_type }} control</small>
|
|
647
|
+
</div>
|
|
648
|
+
</div>
|
|
649
|
+
|
|
650
|
+
<!-- Samples Tab -->
|
|
651
|
+
<div id="samplesTabContent" class="tab-content" style="display: none;">
|
|
652
|
+
{% if sample_metadata and controlnet_type in sample_metadata %}
|
|
653
|
+
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; max-height: 200px; overflow-y: auto;">
|
|
654
|
+
{% for filename, metadata in sample_metadata[controlnet_type].items() %}
|
|
655
|
+
<div class="sample-item" onclick="loadSampleImage('{{ controlnet_type }}', '{{ filename }}')" style="padding: 6px;">
|
|
656
|
+
<img src="/samples/{{ controlnet_type }}/{{ filename }}" alt="{{ metadata.title }}" style="width: 100%; height: 80px; object-fit: cover; border-radius: 3px; margin-bottom: 4px;">
|
|
657
|
+
<div style="text-align: center; font-size: 10px; color: #374151; font-weight: 500;">{{ metadata.title }}</div>
|
|
658
|
+
</div>
|
|
659
|
+
{% endfor %}
|
|
660
|
+
</div>
|
|
661
|
+
{% else %}
|
|
662
|
+
<p style="color: #6b7280; font-size: 12px; text-align: center; margin: 20px 0;">No samples available</p>
|
|
663
|
+
{% endif %}
|
|
664
|
+
</div>
|
|
665
|
+
|
|
666
|
+
<!-- Settings Tab -->
|
|
667
|
+
<div id="settingsTabContent" class="tab-content" style="display: none;">
|
|
668
|
+
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px;">
|
|
669
|
+
<div>
|
|
670
|
+
<label style="font-size: 11px; color: #374151; display: block; margin-bottom: 3px;">Strength</label>
|
|
671
|
+
<input type="number" name="controlnet_conditioning_scale" id="controlnet_conditioning_scale" value="{{ controlnet_conditioning_scale or 1.0 }}" min="0.0" max="2.0" step="0.1" style="width: 100%; padding: 4px 6px; font-size: 12px; border: 1px solid #d1d5db; border-radius: 3px;">
|
|
672
|
+
</div>
|
|
673
|
+
<div>
|
|
674
|
+
<label style="font-size: 11px; color: #374151; display: block; margin-bottom: 3px;">Start</label>
|
|
675
|
+
<input type="number" name="control_guidance_start" id="control_guidance_start" value="{{ control_guidance_start or 0.0 }}" min="0.0" max="1.0" step="0.1" style="width: 100%; padding: 4px 6px; font-size: 12px; border: 1px solid #d1d5db; border-radius: 3px;">
|
|
676
|
+
</div>
|
|
677
|
+
<div>
|
|
678
|
+
<label style="font-size: 11px; color: #374151; display: block; margin-bottom: 3px;">End</label>
|
|
679
|
+
<input type="number" name="control_guidance_end" id="control_guidance_end" value="{{ control_guidance_end or 1.0 }}" min="0.0" max="1.0" step="0.1" style="width: 100%; padding: 4px 6px; font-size: 12px; border: 1px solid #d1d5db; border-radius: 3px;">
|
|
680
|
+
</div>
|
|
681
|
+
</div>
|
|
682
|
+
<div style="margin-top: 8px; padding: 6px; background: #f0f9ff; border-radius: 3px; font-size: 10px; color: #0369a1;">
|
|
683
|
+
💡 Strength: How much control to apply | Start/End: When to apply control during generation
|
|
684
|
+
</div>
|
|
685
|
+
</div>
|
|
458
686
|
</div>
|
|
459
687
|
</div>
|
|
688
|
+
{% endif %}
|
|
460
689
|
|
|
461
690
|
<button type="submit" class="btn" {{ 'disabled' if not model_loaded }}>
|
|
462
691
|
🎨 Generate Image
|
|
@@ -472,7 +701,29 @@
|
|
|
472
701
|
<p>Generating image, please wait...</p>
|
|
473
702
|
</div>
|
|
474
703
|
|
|
475
|
-
{% if image_b64 %}
|
|
704
|
+
{% if control_image_b64 and image_b64 %}
|
|
705
|
+
<!-- ControlNet Results: Show control image and generated image side by side -->
|
|
706
|
+
<div class="controlnet-results">
|
|
707
|
+
<div>
|
|
708
|
+
<h3 style="color: #374151; margin-bottom: 10px; text-align: center;">🎛️ Control Image</h3>
|
|
709
|
+
<img src="data:image/png;base64,{{ control_image_b64 }}" alt="Control Image" class="result-image">
|
|
710
|
+
<p style="color: #6b7280; text-align: center; font-size: 14px; margin-top: 10px;">
|
|
711
|
+
Preprocessed for {{ controlnet_type }} control
|
|
712
|
+
</p>
|
|
713
|
+
</div>
|
|
714
|
+
<div>
|
|
715
|
+
<h3 style="color: #374151; margin-bottom: 10px; text-align: center;">🎨 Generated Image</h3>
|
|
716
|
+
<img src="data:image/png;base64,{{ image_b64 }}" alt="Generated Image" class="result-image">
|
|
717
|
+
<p style="color: #6b7280; text-align: center; font-size: 14px; margin-top: 10px;">
|
|
718
|
+
Conditioning Scale: {{ controlnet_conditioning_scale }}
|
|
719
|
+
</p>
|
|
720
|
+
</div>
|
|
721
|
+
</div>
|
|
722
|
+
<p style="color: #6b7280; margin-top: 15px; text-align: center;">
|
|
723
|
+
<strong>Prompt:</strong> {{ prompt }}
|
|
724
|
+
</p>
|
|
725
|
+
{% elif image_b64 %}
|
|
726
|
+
<!-- Regular Generation Result -->
|
|
476
727
|
<img src="data:image/png;base64,{{ image_b64 }}" alt="Generated Image" class="result-image">
|
|
477
728
|
<p style="color: #6b7280; margin-top: 15px;">
|
|
478
729
|
<strong>Prompt:</strong> {{ prompt }}
|
|
@@ -481,6 +732,11 @@
|
|
|
481
732
|
<div style="background: #f9fafb; border: 2px dashed #d1d5db; border-radius: 12px; padding: 60px 20px; color: #6b7280;">
|
|
482
733
|
<div style="font-size: 3em; margin-bottom: 15px;">🖼️</div>
|
|
483
734
|
<p>Generated image will be displayed here</p>
|
|
735
|
+
{% if is_controlnet_model %}
|
|
736
|
+
<p style="margin-top: 10px; font-size: 14px;">
|
|
737
|
+
💡 Upload a control image to guide the generation process
|
|
738
|
+
</p>
|
|
739
|
+
{% endif %}
|
|
484
740
|
</div>
|
|
485
741
|
{% endif %}
|
|
486
742
|
</div>
|
|
@@ -491,6 +747,127 @@
|
|
|
491
747
|
document.getElementById('generateForm').addEventListener('submit', function() {
|
|
492
748
|
document.getElementById('loading').classList.add('show');
|
|
493
749
|
});
|
|
750
|
+
|
|
751
|
+
async function initializeControlNet() {
|
|
752
|
+
const button = event.target;
|
|
753
|
+
const originalText = button.textContent;
|
|
754
|
+
|
|
755
|
+
// Show loading state
|
|
756
|
+
button.textContent = 'Initializing...';
|
|
757
|
+
button.disabled = true;
|
|
758
|
+
|
|
759
|
+
try {
|
|
760
|
+
// Call the initialization endpoint
|
|
761
|
+
const response = await fetch('/api/controlnet/initialize', {
|
|
762
|
+
method: 'POST'
|
|
763
|
+
});
|
|
764
|
+
const data = await response.json();
|
|
765
|
+
|
|
766
|
+
if (data.success && data.initialized) {
|
|
767
|
+
// Reload page to update UI
|
|
768
|
+
window.location.reload();
|
|
769
|
+
} else {
|
|
770
|
+
alert(data.message || 'Failed to initialize ControlNet preprocessors. Please check the console for errors.');
|
|
771
|
+
button.textContent = originalText;
|
|
772
|
+
button.disabled = false;
|
|
773
|
+
}
|
|
774
|
+
} catch (error) {
|
|
775
|
+
console.error('Error initializing ControlNet:', error);
|
|
776
|
+
alert('Error initializing ControlNet preprocessors.');
|
|
777
|
+
button.textContent = originalText;
|
|
778
|
+
button.disabled = false;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function switchControlTab(tab) {
|
|
783
|
+
// Remove active class from all tabs
|
|
784
|
+
const tabs = document.querySelectorAll('.control-tab');
|
|
785
|
+
tabs.forEach(t => t.classList.remove('active-tab'));
|
|
786
|
+
|
|
787
|
+
// Add active class to the clicked tab
|
|
788
|
+
const clickedTab = document.getElementById(tab + 'Tab');
|
|
789
|
+
clickedTab.classList.add('active-tab');
|
|
790
|
+
|
|
791
|
+
// Hide all tab contents
|
|
792
|
+
const contents = document.querySelectorAll('.tab-content');
|
|
793
|
+
contents.forEach(content => content.style.display = 'none');
|
|
794
|
+
|
|
795
|
+
// Show the selected tab content
|
|
796
|
+
const selectedContent = document.getElementById(tab + 'TabContent');
|
|
797
|
+
if (selectedContent) {
|
|
798
|
+
selectedContent.style.display = 'block';
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// Initialize the first tab as active when page loads
|
|
803
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
804
|
+
const uploadContent = document.getElementById('uploadTabContent');
|
|
805
|
+
if (uploadContent) {
|
|
806
|
+
uploadContent.style.display = 'block';
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
async function loadSampleImage(controlnetType, filename) {
|
|
811
|
+
try {
|
|
812
|
+
// Fetch the sample image
|
|
813
|
+
const response = await fetch(`/samples/${controlnetType}/${filename}`);
|
|
814
|
+
if (!response.ok) {
|
|
815
|
+
throw new Error('Failed to load sample image');
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// Convert to blob and create file object
|
|
819
|
+
const blob = await response.blob();
|
|
820
|
+
const file = new File([blob], filename, { type: blob.type });
|
|
821
|
+
|
|
822
|
+
// Create a DataTransfer object to simulate file selection
|
|
823
|
+
const dataTransfer = new DataTransfer();
|
|
824
|
+
dataTransfer.items.add(file);
|
|
825
|
+
|
|
826
|
+
// Set the file input
|
|
827
|
+
const fileInput = document.getElementById('control_image');
|
|
828
|
+
fileInput.files = dataTransfer.files;
|
|
829
|
+
|
|
830
|
+
// Trigger change event to update any listeners
|
|
831
|
+
const changeEvent = new Event('change', { bubbles: true });
|
|
832
|
+
fileInput.dispatchEvent(changeEvent);
|
|
833
|
+
|
|
834
|
+
// Visual feedback - find the clicked sample item
|
|
835
|
+
const sampleItems = document.querySelectorAll('.sample-item');
|
|
836
|
+
sampleItems.forEach(item => item.style.borderColor = '#e5e7eb');
|
|
837
|
+
|
|
838
|
+
// Find the clicked sample item by looking for the image with matching src
|
|
839
|
+
const clickedImg = document.querySelector(`img[src="/samples/${controlnetType}/${filename}"]`);
|
|
840
|
+
if (clickedImg) {
|
|
841
|
+
const sampleItem = clickedImg.closest('.sample-item');
|
|
842
|
+
if (sampleItem) {
|
|
843
|
+
sampleItem.style.borderColor = '#7c3aed';
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// Show success message
|
|
848
|
+
const successDiv = document.createElement('div');
|
|
849
|
+
successDiv.style.cssText = 'position: fixed; top: 20px; right: 20px; background: #10b981; color: white; padding: 12px 20px; border-radius: 6px; z-index: 1000; font-size: 14px;';
|
|
850
|
+
successDiv.textContent = `✅ Sample "${filename.replace('.png', '').replace(/_/g, ' ')}" loaded!`;
|
|
851
|
+
document.body.appendChild(successDiv);
|
|
852
|
+
|
|
853
|
+
setTimeout(() => {
|
|
854
|
+
successDiv.remove();
|
|
855
|
+
}, 3000);
|
|
856
|
+
|
|
857
|
+
} catch (error) {
|
|
858
|
+
console.error('Error loading sample image:', error);
|
|
859
|
+
|
|
860
|
+
// Show detailed error message
|
|
861
|
+
const errorDiv = document.createElement('div');
|
|
862
|
+
errorDiv.style.cssText = 'position: fixed; top: 20px; right: 20px; background: #dc2626; color: white; padding: 12px 20px; border-radius: 6px; z-index: 1000; font-size: 14px;';
|
|
863
|
+
errorDiv.textContent = `❌ Failed to load sample: ${error.message}`;
|
|
864
|
+
document.body.appendChild(errorDiv);
|
|
865
|
+
|
|
866
|
+
setTimeout(() => {
|
|
867
|
+
errorDiv.remove();
|
|
868
|
+
}, 5000);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
494
871
|
</script>
|
|
495
872
|
</body>
|
|
496
873
|
</html>
|