@@ -204,8 +204,11 @@ export class OptionsComponent implements OnInit {
204204 queueType : 'free' | 'priority' = 'free' ;
205205 creditCost : number = 0 ;
206206 upscaleCreditCost : number = 0 ;
207+ hiresCreditCost : number = 0 ;
208+ hiresEnabled : boolean = false ;
207209 userCredits : number = 0 ;
208210 isLoggedIn : boolean = false ;
211+ private queueTypeBeforeHires : 'free' | 'priority' = 'free' ;
209212
210213 // Credit costs by model type
211214 private readonly creditCosts : { [ key : string ] : number } = {
@@ -504,6 +507,10 @@ export class OptionsComponent implements OnInit {
504507 this . userCredits = 0 ;
505508 this . isLoggedIn = this . authService . isLoggedIn ( ) ;
506509 }
510+ if ( ! this . isLoggedIn && this . hiresEnabled ) {
511+ this . hiresEnabled = false ;
512+ localStorage . removeItem ( "hires-enabled" ) ;
513+ }
507514 // Update credit cost display
508515 this . updateCreditCost ( ) ;
509516 } ) ;
@@ -540,7 +547,10 @@ export class OptionsComponent implements OnInit {
540547 } else {
541548 this . generationRequest . job_type = "txt2img" ;
542549 this . generationRequest . image = undefined ;
550+ this . generationRequest . mask_image = undefined ;
543551 this . referenceImage = undefined ;
552+ // If the user re-enters txt2img, validate auth/credit constraints.
553+ this . enforceHiresConstraints ( ) ;
544554 }
545555 } ) ;
546556
@@ -607,14 +617,14 @@ export class OptionsComponent implements OnInit {
607617 }
608618
609619 ngOnChanges ( changes : SimpleChanges ) {
610- if ( changes [ 'inpaintMask' ] && changes [ 'inpaintMask' ] . currentValue != undefined ) {
620+ if ( changes [ 'inpaintMask' ] ) {
611621 this . inpaintMask = changes [ 'inpaintMask' ] . currentValue ;
612- if ( changes [ 'inpaintMask' ] . currentValue == undefined ) {
622+ if ( this . inpaintMask == undefined ) {
613623 this . generationRequest . mask_image = undefined ;
614- this . generationRequest . job_type = "img2img" ;
624+ this . generationRequest . job_type = this . referenceImage ? "img2img" : "txt2img ";
615625 }
616626 else {
617- this . generationRequest . mask_image = this . inpaintMask ! ;
627+ this . generationRequest . mask_image = this . inpaintMask ;
618628 this . generationRequest . job_type = "inpainting" ;
619629 }
620630 }
@@ -654,22 +664,120 @@ export class OptionsComponent implements OnInit {
654664 const perLoraCost = this . loraCreditCosts [ modelType ] ?? 0 ;
655665 this . creditCost = baseCost + ( loraCount * perLoraCost ) ;
656666 this . upscaleCreditCost = baseCost * 3 ;
667+ this . hiresCreditCost = baseCost * 4 ;
668+ this . enforceHiresConstraints ( ) ;
657669 }
658670
659671 getUpscaleTooltip ( ) : string {
660672 const modelType = this . models_types [ this . generationRequest . model ] || 'SD 1.5' ;
661673 const cost = this . upscaleCreditCost ;
662674 if ( ! this . authService . isLoggedIn ( ) ) {
663- return `Upscale the image by 1.5x. Costs ${ cost } credits (3× ${ modelType } ). Login required.` ;
675+ return `Upscale the image by 1.5×. Costs ${ cost } credits (3× ${ modelType } ). Login required.` ;
676+ }
677+ if ( this . userCredits < cost ) {
678+ return `Upscale the image by 1.5×. Costs ${ cost } credits (3× ${ modelType } ). You have ${ this . userCredits } credits.` ;
679+ }
680+ return `Upscale the image by 1.5×. Costs ${ cost } credits (3× ${ modelType } ).` ;
681+ }
682+
683+ getHiresTooltip ( ) : string {
684+ const modelType = this . models_types [ this . generationRequest . model ] || 'SD 1.5' ;
685+ const cost = this . hiresCreditCost ;
686+ if ( ! this . authService . isLoggedIn ( ) ) {
687+ return `Generate + Upscale in one step. Costs ${ cost } credits (4× ${ modelType } ). Login required.` ;
664688 }
665689 if ( this . userCredits < cost ) {
666- return `Upscale the image by 1.5x. Costs ${ cost } credits (3× ${ modelType } ). You have ${ this . userCredits } credits.` ;
690+ return `Generate + Upscale in one step. Costs ${ cost } credits (4× ${ modelType } ). You have ${ this . userCredits } credits.` ;
691+ }
692+ return `Generate + Upscale in one step for the highest quality generations. Costs ${ cost } credits (Select priority queue to use).` ;
693+ }
694+
695+ get hiresEligible ( ) : boolean {
696+ return this . hiresEnabled && this . isHiresAvailable ( ) ;
697+ }
698+
699+ private isHiresAvailable ( ) : boolean {
700+ return (
701+ ! this . referenceImage &&
702+ this . generationRequest ?. job_type === 'txt2img' &&
703+ this . generationRequest ?. model !== 'sonicDiffusionXL' &&
704+ this . generationRequest ?. model !== 'autismMix'
705+ ) ;
706+ }
707+
708+ private restoreQueueTypeAfterHires ( ) {
709+ // If the user isn't logged in, they can't be in priority anyway.
710+ if ( ! this . authService . isLoggedIn ( ) ) {
711+ this . queueType = 'free' ;
712+ return ;
667713 }
668- return `Upscale the image by 1.5x. Costs ${ cost } credits (3× ${ modelType } ).` ;
714+
715+ // Restore what they had before enabling Hi-Res, but fall back safely.
716+ const restored = this . queueTypeBeforeHires || 'free' ;
717+ if ( restored === 'priority' && this . userCredits < this . creditCost ) {
718+ this . queueType = 'free' ;
719+ return ;
720+ }
721+ this . queueType = restored ;
722+ }
723+
724+ private enforceHiresConstraints ( ) {
725+ if ( ! this . hiresEnabled ) return ;
726+ const wasEnabled = this . hiresEnabled ;
727+
728+ if ( ! this . authService . isLoggedIn ( ) ) {
729+ this . hiresEnabled = false ;
730+ this . restoreQueueTypeAfterHires ( ) ;
731+ if ( wasEnabled ) localStorage . removeItem ( "hires-enabled" ) ;
732+ }
733+ }
734+
735+ onHiresToggleChange ( enabled : boolean ) {
736+ if ( enabled ) {
737+ if ( ! this . isHiresAvailable ( ) ) {
738+ this . hiresEnabled = false ;
739+ this . saveSettings ( ) ;
740+ return ;
741+ }
742+
743+ if ( ! this . authService . isLoggedIn ( ) ) {
744+ this . messageService . add ( {
745+ severity : 'warn' ,
746+ summary : 'Login Required' ,
747+ detail : `Please log in to use Hi-Res. Hi-Res costs ${ this . hiresCreditCost } credits.`
748+ } ) ;
749+ this . hiresEnabled = false ;
750+ this . saveSettings ( ) ;
751+ return ;
752+ }
753+
754+ if ( this . userCredits < this . hiresCreditCost ) {
755+ this . messageService . add ( {
756+ severity : 'warn' ,
757+ summary : 'Insufficient Credits' ,
758+ detail : `You need ${ this . hiresCreditCost } credits but only have ${ this . userCredits } .`
759+ } ) ;
760+ this . hiresEnabled = false ;
761+ this . saveSettings ( ) ;
762+ return ;
763+ }
764+
765+ this . queueTypeBeforeHires = this . queueType ;
766+ this . queueType = 'priority' ;
767+ this . saveSettings ( ) ;
768+ return ;
769+ }
770+
771+ // Disabled
772+ this . restoreQueueTypeAfterHires ( ) ;
773+ this . saveSettings ( ) ;
669774 }
670775
671776 onQueueTypeChange ( type : 'free' | 'priority' ) {
672777 this . queueType = type ;
778+ if ( this . hiresEnabled ) {
779+ this . queueTypeBeforeHires = type ;
780+ }
673781
674782 // If switching to priority without enough credits, show warning
675783 if ( type === 'priority' && ! this . authService . isLoggedIn ( ) ) {
@@ -683,24 +791,27 @@ export class OptionsComponent implements OnInit {
683791 return ;
684792 }
685793
686- if ( type === 'priority' && this . userCredits < this . creditCost ) {
794+ const requiredCredits = this . hiresEligible ? this . hiresCreditCost : this . creditCost ;
795+ if ( type === 'priority' && this . userCredits < requiredCredits ) {
687796 this . messageService . add ( {
688797 severity : 'warn' ,
689798 summary : 'Insufficient Credits' ,
690- detail : `You need ${ this . creditCost } credits. You have ${ this . userCredits } .` ,
799+ detail : `You need ${ requiredCredits } credits. You have ${ this . userCredits } .` ,
691800 life : 4000
692801 } ) ;
693802 this . queueType = 'free' ;
694803 }
695804 }
696805
697806 canUsePriorityQueue ( ) : boolean {
698- return this . authService . isLoggedIn ( ) && this . userCredits >= this . creditCost ;
807+ const requiredCredits = this . hiresEligible ? this . hiresCreditCost : this . creditCost ;
808+ return this . authService . isLoggedIn ( ) && this . userCredits >= requiredCredits ;
699809 }
700810
701811 getQueueTypeLabel ( ) : string {
702812 if ( this . queueType === 'priority' ) {
703- return `Priority (${ this . creditCost } credits)` ;
813+ const cost = this . hiresEligible ? this . hiresCreditCost : this . creditCost ;
814+ return `Priority (${ cost } credits)` ;
704815 }
705816 return 'Free Queue' ;
706817 }
@@ -801,6 +912,13 @@ export class OptionsComponent implements OnInit {
801912
802913 // Save notifications toggle
803914 localStorage . setItem ( "notifications-enabled" , this . enableNotifications . toString ( ) ) ;
915+
916+ // Save hi-res toggle (only when logged in)
917+ if ( this . hiresEnabled && this . authService . isLoggedIn ( ) ) {
918+ localStorage . setItem ( "hires-enabled" , "true" ) ;
919+ } else {
920+ localStorage . removeItem ( "hires-enabled" ) ;
921+ }
804922 }
805923
806924 // Load session storage info of changed settings
@@ -851,6 +969,9 @@ export class OptionsComponent implements OnInit {
851969 this . enableNotification ( ) ;
852970 }
853971 }
972+ if ( localStorage . getItem ( "hires-enabled" ) != null ) {
973+ this . hiresEnabled = localStorage . getItem ( "hires-enabled" ) == 'true' ;
974+ }
854975
855976 // Apply current LoRA filters after restoring settings
856977 this . filterLoras ( ) ;
@@ -884,6 +1005,7 @@ export class OptionsComponent implements OnInit {
8841005 localStorage . removeItem ( 'lossy-images' ) ;
8851006 localStorage . removeItem ( 'notifications-enabled' ) ;
8861007 localStorage . removeItem ( 'notifications-user-id' ) ;
1008+ localStorage . removeItem ( 'hires-enabled' ) ;
8871009 this . generationRequest . prompt = "" ;
8881010 this . generationRequest . negative_prompt = this . defaultNegativePrompt ;
8891011 this . generationRequest . strength = 0.8 ;
@@ -893,6 +1015,7 @@ export class OptionsComponent implements OnInit {
8931015 this . showNSFWLoras = false ;
8941016 this . generationRequest . lossy_images = false ;
8951017 this . enableNotifications = false ;
1018+ this . hiresEnabled = false ;
8961019 this . changeAspectRatio ( "portrait" ) ;
8971020
8981021 this . loadSettings ( ) ;
@@ -987,11 +1110,21 @@ export class OptionsComponent implements OnInit {
9871110 }
9881111
9891112 // Send job to django api and retrieve job id.
990- async submitJob ( upscale : boolean = false ) {
991- const isUpscaleJob = upscale === true ;
992- const queueTypeForRequest : 'free' | 'priority' = isUpscaleJob ? 'priority' : this . queueType ;
993- const creditCostForRequest = isUpscaleJob ? this . upscaleCreditCost : this . creditCost ;
994- const jobTypeForRequest = isUpscaleJob ? 'upscale' : this . generationRequest . job_type ;
1113+ async submitJob ( mode : 'generate' | 'upscale' | 'hires' = 'generate' ) {
1114+ const effectiveMode : 'generate' | 'upscale' | 'hires' = (
1115+ mode === 'generate'
1116+ && this . hiresEligible
1117+ && this . queueType === 'priority'
1118+ && this . authService . isLoggedIn ( )
1119+ ? 'hires'
1120+ : mode
1121+ ) ;
1122+
1123+ const isUpscaleJob = effectiveMode === 'upscale' ;
1124+ const isHiresJob = effectiveMode === 'hires' ;
1125+ const queueTypeForRequest : 'free' | 'priority' = ( isUpscaleJob || isHiresJob ) ? 'priority' : this . queueType ;
1126+ const creditCostForRequest = isUpscaleJob ? this . upscaleCreditCost : ( isHiresJob ? this . hiresCreditCost : this . creditCost ) ;
1127+ const jobTypeForRequest = isUpscaleJob ? 'upscale' : ( isHiresJob ? 'txt2img_upscale' : this . generationRequest . job_type ) ;
9951128
9961129 // Validate priority queue requirements
9971130 if ( queueTypeForRequest === 'priority' ) {
@@ -1001,7 +1134,9 @@ export class OptionsComponent implements OnInit {
10011134 summary : 'Login Required' ,
10021135 detail : isUpscaleJob
10031136 ? `Please log in to upscale images. Upscaling costs ${ creditCostForRequest } credits due to longer generation times.`
1004- : 'Please log in to use the priority queue.'
1137+ : ( isHiresJob
1138+ ? `Please log in to use Hi-Res. Hi-Res costs ${ creditCostForRequest } credits.`
1139+ : 'Please log in to use the priority queue.' )
10051140 } ) ;
10061141 return ;
10071142 }
@@ -1429,6 +1564,8 @@ export class OptionsComponent implements OnInit {
14291564 removeReferenceImage ( ) {
14301565 this . referenceImage = undefined ;
14311566 this . generationRequest . image = undefined ;
1567+ this . generationRequest . mask_image = undefined ;
1568+ this . generationRequest . job_type = "txt2img" ;
14321569 this . sharedService . setReferenceImage ( null ) ;
14331570 this . sharedService . setGenerationRequest ( this . generationRequest ) ;
14341571
0 commit comments