Skip to content

Commit af2ac02

Browse files
committed
allow enhanced generations
1 parent 1e9f533 commit af2ac02

File tree

3 files changed

+181
-21
lines changed

3 files changed

+181
-21
lines changed

src/app/home/options/options.component.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@
478478
.queue-toggle {
479479
width: 100%;
480480
}
481-
481+
482482
.queue-btn {
483483
flex: 1;
484484
padding: 0.625rem 0.5rem;
@@ -489,4 +489,4 @@
489489
.login-prompt {
490490
text-align: center;
491491
}
492-
}
492+
}

src/app/home/options/options.component.html

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
(click)="onQueueTypeChange('priority')">
2424
<i class="pi pi-bolt me-1"></i>
2525
Priority
26-
<span class="badge bg-dark ms-1">{{creditCost}} ⚡</span>
26+
<span class="badge bg-dark ms-1">{{hiresEligible ? hiresCreditCost : creditCost}} ⚡</span>
2727
</button>
2828
</div>
2929

@@ -86,7 +86,7 @@
8686
<!-- Upscale button -->
8787
<span class="d-inline-flex align-items-center"
8888
*ngIf="this.generationRequest.model != 'sonicDiffusionXL' && this.generationRequest.model != 'autismMix'">
89-
<p-button label="Upscale" [disabled]="!enableGenerationButton" (click)="this.submitJob(true)"></p-button>
89+
<p-button label="Upscale" [disabled]="!enableGenerationButton" (click)="this.submitJob('upscale')"></p-button>
9090
<span class="badge ms-1"
9191
[ngClass]="!isLoggedIn ? 'bg-secondary' : (userCredits < upscaleCreditCost ? 'bg-danger' : 'bg-dark')">
9292
{{upscaleCreditCost}} ⚡
@@ -247,6 +247,29 @@
247247
[(ngModel)]="generationRequest.fast_pass_code" (input)="onFastPassCodeChange($event)" />
248248
</div>
249249

250+
<!-- Hi-Res (Generate + Upscale) -->
251+
<div
252+
*ngIf="generationRequest.job_type == 'txt2img' && !referenceImage && generationRequest.model != 'sonicDiffusionXL' && generationRequest.model != 'autismMix'"
253+
class="d-flex justify-content-start mb-3">
254+
<div class="form-check me-3">
255+
<input class="form-check-input" type="checkbox" [(ngModel)]="hiresEnabled"
256+
(ngModelChange)="onHiresToggleChange($event)" id="hiresToggle" [disabled]="!isLoggedIn">
257+
<label class="form-check-label" for="hiresToggle">
258+
Enhanced Quality (Generate + Upscale)
259+
<span class="ms-1 text-primary"
260+
[pTooltip]="getHiresTooltip()"
261+
tooltipPosition="top" style="
262+
background-color: rgba(255, 255, 255, 0.8);
263+
padding: 2px 4px;
264+
border-radius: 50%;
265+
cursor: pointer;
266+
">
267+
?
268+
</span>
269+
</label>
270+
</div>
271+
</div>
272+
250273
<!-- Notifications -->
251274
<div class="d-flex justify-content-start mb-3">
252275
<div class="form-check me-3">

src/app/home/options/options.component.ts

Lines changed: 154 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)