@@ -16,6 +16,9 @@ let selectedComfyImages = new Set(); // Stores the URLs of selected images
1616
1717let isComfyGenerating = false ;
1818
19+ let selectedTemplates = new Set ( ) ;
20+ let isTmplSelectionMode = false ;
21+
1922// --- 1. CONNECTION & SETUP ---
2023
2124function toggleComfyConfig ( ) {
@@ -135,6 +138,7 @@ function loadWorkflowFile(event) {
135138
136139 // Save Persistence
137140 saveComfySession ( fileName , jsonStr ) ;
141+ saveTemplateToDB ( fileName , jsonStr ) ; // This saves the file to your new database box
138142
139143 if ( comfySocket && comfySocket . readyState === WebSocket . OPEN ) {
140144 document . getElementById ( 'comfyQueueBtn' ) . disabled = false ;
@@ -628,18 +632,28 @@ function saveComfyToMainGallery(url) {
628632 xhr . onload = function ( ) {
629633 const reader = new FileReader ( ) ;
630634 reader . onloadend = function ( ) {
631- const request = indexedDB . open ( "BojroHybridDB" , 1 ) ;
632- request . onsuccess = function ( event ) {
633- const db = event . target . result ;
635+ // FIX: We use the 'db' variable that is already open at version 2
636+ if ( ! db ) {
637+ console . error ( "Database connection not ready" ) ;
638+ return ;
639+ }
640+
641+ try {
634642 const tx = db . transaction ( [ "images" ] , "readwrite" ) ;
635643 const store = tx . objectStore ( "images" ) ;
636644 store . add ( {
637645 data : reader . result ,
638646 date : new Date ( ) . toLocaleString ( )
639647 } ) ;
640- console . log ( "Saved Comfy image to History" ) ;
641- } ;
642- }
648+ console . log ( "Saved to Gallery Successfully!" ) ;
649+
650+ // This tells the GAL tab to refresh so you see the image immediately
651+ if ( typeof loadGallery === 'function' ) loadGallery ( ) ;
652+
653+ } catch ( e ) {
654+ console . error ( "Database Error:" , e ) ;
655+ }
656+ } ;
643657 reader . readAsDataURL ( xhr . response ) ;
644658 } ;
645659 xhr . open ( 'GET' , url ) ;
@@ -761,24 +775,20 @@ function toggleComfySelectionMode() {
761775 const gallery = document . getElementById ( 'comfyGalleryContainer' ) ;
762776 const selectBtn = document . getElementById ( 'comfySelectBtn' ) ;
763777 const saveBtn = document . getElementById ( 'comfySaveSelectedBtn' ) ;
778+
779+ // 1. Toggle the CSS class (This uses your new style.css rules)
780+ selectBtn . classList . toggle ( 'active' , isComfySelectionMode ) ;
781+
782+ // 2. Toggle the text
783+ selectBtn . innerText = isComfySelectionMode ? "CANCEL" : "SELECT" ;
764784
765785 if ( isComfySelectionMode ) {
766- // Enter Selection Mode
767786 gallery . classList . add ( 'selection-mode' ) ;
768- selectBtn . style . background = 'var(--text-main)' ;
769- selectBtn . style . color = 'var(--bg-glass)' ;
770- selectBtn . innerText = "CANCEL" ;
771- selectedComfyImages . clear ( ) ; // Reset selection
787+ selectedComfyImages . clear ( ) ;
772788 updateComfySelectionUI ( ) ;
773789 } else {
774- // Exit Selection Mode
775790 gallery . classList . remove ( 'selection-mode' ) ;
776- // Remove visual selection from all items
777791 Array . from ( gallery . children ) . forEach ( el => el . classList . remove ( 'selected' ) ) ;
778-
779- selectBtn . style . background = '' ;
780- selectBtn . style . color = '' ;
781- selectBtn . innerText = "SELECT" ;
782792 saveBtn . classList . add ( 'hidden' ) ;
783793 }
784794}
@@ -834,5 +844,207 @@ function saveSelectedComfyImages() {
834844 } , 1000 ) ;
835845}
836846
847+
848+ // 1. Function to actually put the file in the database
849+ function saveTemplateToDB ( name , json ) {
850+ if ( ! db ) return ;
851+ const tx = db . transaction ( [ "comfy_templates" ] , "readwrite" ) ;
852+ const store = tx . objectStore ( "comfy_templates" ) ;
853+ store . put ( { name : name , data : json , date : new Date ( ) . toLocaleString ( ) } ) ;
854+ }
855+
856+ // 2. Open the popup and show the list
857+ async function openComfyTemplateModal ( ) {
858+ document . getElementById ( 'comfyTemplateModal' ) . classList . remove ( 'hidden' ) ;
859+ renderTemplateList ( ) ;
860+ if ( window . lucide ) lucide . createIcons ( ) ;
861+ }
862+
863+ // 3. Close the popup and reset settings
864+ function closeComfyTemplateModal ( ) {
865+ document . getElementById ( 'comfyTemplateModal' ) . classList . add ( 'hidden' ) ;
866+ isTmplSelectionMode = false ;
867+ selectedTemplates . clear ( ) ;
868+ const btn = document . getElementById ( 'tmplSelectBtn' ) ;
869+ btn . innerText = "SELECT" ;
870+ document . getElementById ( 'tmplDeleteBtn' ) . classList . add ( 'hidden' ) ;
871+ }
872+
873+ // 4. Create the list items you see on screen
874+ async function renderTemplateList ( ) {
875+ const list = document . getElementById ( 'tmplList' ) ;
876+ list . innerHTML = "" ;
877+
878+ if ( ! db ) return ;
879+ const tx = db . transaction ( [ "comfy_templates" ] , "readonly" ) ;
880+ const store = tx . objectStore ( "comfy_templates" ) ;
881+
882+ // Get everything from the box
883+ store . getAll ( ) . onsuccess = ( e ) => {
884+ const all = e . target . result ;
885+
886+ if ( all . length === 0 ) {
887+ list . innerHTML = '<div style="text-align:center; color:var(--text-muted); margin-top:20px;">No templates saved yet.</div>' ;
888+ return ;
889+ }
890+
891+ all . forEach ( tmpl => {
892+ const div = document . createElement ( 'div' ) ;
893+ // If it's selected, give it the 'selected' look from your CSS
894+ div . className = `tmpl-item ${ selectedTemplates . has ( tmpl . name ) ? 'selected' : '' } ` ;
895+ div . style . marginBottom = "8px" ;
896+
897+ div . innerHTML = `
898+ <div class="tmpl-info">
899+ <span class="tmpl-name">${ tmpl . name } </span>
900+ <span style="font-size:9px; color:var(--text-muted);">${ tmpl . date } </span>
901+ </div>
902+ ${ selectedTemplates . has ( tmpl . name ) ? '<i data-lucide="check-circle" size="14" style="color:var(--error);"></i>' : '' }
903+ ` ;
904+
905+ div . onclick = ( ) => handleTmplClick ( tmpl ) ;
906+ list . appendChild ( div ) ;
907+ } ) ;
908+ if ( window . lucide ) lucide . createIcons ( ) ;
909+ } ;
910+ }
911+
912+ // 5. What happens when you tap a template in the list
913+ function handleTmplClick ( tmpl ) {
914+ if ( isTmplSelectionMode ) {
915+ // Just highlight/unhighlight if we are in deleting mode
916+ if ( selectedTemplates . has ( tmpl . name ) ) {
917+ selectedTemplates . delete ( tmpl . name ) ;
918+ } else {
919+ selectedTemplates . add ( tmpl . name ) ;
920+ }
921+ updateTmplUI ( ) ;
922+ } else {
923+ // Actually LOAD the template if we are in normal mode
924+ try {
925+ comfyLoadedWorkflow = JSON . parse ( tmpl . data ) ;
926+ document . getElementById ( 'comfyLoadedFileName' ) . innerText = tmpl . name ;
927+ buildComfyUI ( comfyLoadedWorkflow ) ;
928+
929+ // Re-enable generate button if connected
930+ if ( comfySocket && comfySocket . readyState === WebSocket . OPEN ) {
931+ document . getElementById ( 'comfyQueueBtn' ) . disabled = false ;
932+ }
933+
934+ closeComfyTemplateModal ( ) ;
935+ } catch ( e ) {
936+ alert ( "Error loading template: " + e . message ) ;
937+ }
938+ }
939+ }
940+
941+ // 6. Turn selection mode ON or OFF
942+ function toggleTmplSelectionMode ( ) {
943+ isTmplSelectionMode = ! isTmplSelectionMode ;
944+ const btn = document . getElementById ( 'tmplSelectBtn' ) ;
945+
946+ if ( isTmplSelectionMode ) {
947+ // We only change the text and add the CSS class
948+ btn . innerText = "CANCEL" ;
949+ btn . classList . add ( 'btn-cancel-active' ) ;
950+ } else {
951+ // We change the text back and remove the CSS class
952+ btn . innerText = "SELECT" ;
953+ btn . classList . remove ( 'btn-cancel-active' ) ;
954+ selectedTemplates . clear ( ) ;
955+ }
956+ updateTmplUI ( ) ;
957+ }
958+
959+ // 7. Refresh the screen and show/hide the Delete button
960+ function updateTmplUI ( ) {
961+ renderTemplateList ( ) ;
962+ const count = selectedTemplates . size ;
963+ document . getElementById ( 'tmplSelCount' ) . innerText = count ;
964+
965+ const delBtn = document . getElementById ( 'tmplDeleteBtn' ) ;
966+ if ( count > 0 && isTmplSelectionMode ) {
967+ delBtn . classList . remove ( 'hidden' ) ;
968+ } else {
969+ delBtn . classList . add ( 'hidden' ) ;
970+ }
971+ }
972+
973+ // 8. Delete only the ones you checked
974+ function deleteSelectedTemplates ( ) {
975+ if ( selectedTemplates . size === 0 ) return ;
976+
977+ if ( confirm ( `Delete ${ selectedTemplates . size } selected templates?` ) ) {
978+ const tx = db . transaction ( [ "comfy_templates" ] , "readwrite" ) ;
979+ const store = tx . objectStore ( "comfy_templates" ) ;
980+
981+ selectedTemplates . forEach ( name => {
982+ store . delete ( name ) ;
983+ } ) ;
984+
985+ tx . oncomplete = ( ) => {
986+ selectedTemplates . clear ( ) ;
987+ isTmplSelectionMode = false ;
988+ updateTmplUI ( ) ;
989+ const btn = document . getElementById ( 'tmplSelectBtn' ) ;
990+ btn . innerText = "SELECT" ;
991+ btn . style . background = "" ;
992+ btn . style . color = "" ;
993+ } ;
994+ }
995+ }
996+
997+ // 9. Wipe everything
998+ function clearAllTemplates ( ) {
999+ if ( confirm ( "Delete ALL saved templates? This cannot be undone." ) ) {
1000+ const tx = db . transaction ( [ "comfy_templates" ] , "readwrite" ) ;
1001+ tx . objectStore ( "comfy_templates" ) . clear ( ) ;
1002+ tx . oncomplete = ( ) => {
1003+ selectedTemplates . clear ( ) ;
1004+ isTmplSelectionMode = false ;
1005+ renderTemplateList ( ) ;
1006+ updateTmplUI ( ) ;
1007+ } ;
1008+ }
1009+ }
1010+
1011+ // Function to import many JSON files at once
1012+ // Upgraded Import Function
1013+ async function importMultipleTemplates ( event ) {
1014+ const files = event . target . files ;
1015+ if ( ! files || files . length === 0 ) return ;
1016+
1017+ // Create a list of "tasks" for every file
1018+ const tasks = Array . from ( files ) . map ( file => {
1019+ return new Promise ( ( resolve ) => {
1020+ const reader = new FileReader ( ) ;
1021+ reader . onload = ( e ) => {
1022+ try {
1023+ const jsonStr = e . target . result ;
1024+ JSON . parse ( jsonStr ) ; // Check if it's a real workflow
1025+ saveTemplateToDB ( file . name . toUpperCase ( ) , jsonStr ) ;
1026+ } catch ( err ) {
1027+ console . error ( "Skipped bad file: " + file . name ) ;
1028+ }
1029+ resolve ( ) ; // Always finish, even if the file was bad
1030+ } ;
1031+ reader . readAsText ( file ) ;
1032+ } ) ;
1033+ } ) ;
1034+
1035+ // Wait for ALL files to finish loading
1036+ await Promise . all ( tasks ) ;
1037+
1038+ // Refresh the list once at the end
1039+ renderTemplateList ( ) ;
1040+
1041+ // Clear the button so you can import again
1042+ event . target . value = "" ;
1043+
1044+ if ( typeof Toast !== 'undefined' ) {
1045+ Toast . show ( { text : `Import process complete` , duration : 'short' } ) ;
1046+ }
1047+ }
1048+
8371049// 8. AUTO-INIT ON LOAD
8381050document . addEventListener ( 'DOMContentLoaded' , restoreComfySession ) ;
0 commit comments