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.
@@ -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>