Skip to content

Commit

Permalink
Feat: Add descriptions for individual fields (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
RikudouSage committed Mar 28, 2024
1 parent 22bbb51 commit f8d3511
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 28 deletions.
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -36,6 +36,7 @@
"autosize": "^6.0.1",
"express": "^4.18.2",
"lodash": "^4.17.21",
"ngx-tippy-wrapper": "^6.3.0",
"ngx-toastr": "^18.0.0",
"ngx-transloco-markup": "^5.1.0",
"rxjs": "~7.8.0",
Expand Down
Expand Up @@ -2,4 +2,5 @@
<input class="toggle-checkbox" type="checkbox" id="input{{random()}}" [checked]="value()" (change)="value.set(input.checked)" #input>
<div class="toggle-switch"></div>
<span class="toggle-label">{{description()}}</span>
<ng-content />
</label>
Expand Up @@ -48,4 +48,5 @@
margin-left: 5px;
position: relative;
top: 2px;
margin-right: 4px;
}
1 change: 1 addition & 0 deletions src/app/components/tooltip/tooltip.component.html
@@ -0,0 +1 @@
<fa-icon [icon]="icon()" [ngxTippy]="text()" />
3 changes: 3 additions & 0 deletions src/app/components/tooltip/tooltip.component.scss
@@ -0,0 +1,3 @@
fa-icon {
cursor: help;
}
19 changes: 19 additions & 0 deletions src/app/components/tooltip/tooltip.component.ts
@@ -0,0 +1,19 @@
import {Component, input, signal} from '@angular/core';
import {faCircleInfo} from "@fortawesome/free-solid-svg-icons";
import {FaIconComponent} from "@fortawesome/angular-fontawesome";
import {NgxTippyModule} from "ngx-tippy-wrapper";

@Component({
selector: 'app-tooltip',
standalone: true,
imports: [
FaIconComponent,
NgxTippyModule
],
templateUrl: './tooltip.component.html',
styleUrl: './tooltip.component.scss'
})
export class TooltipComponent {
public text = input.required<string>();
public icon = signal(faCircleInfo);
}
66 changes: 40 additions & 26 deletions src/app/pages/generate-image/generate-image.component.html
Expand Up @@ -5,7 +5,7 @@
<div class="row">
<div class="col-md-5">
<div class="form-group">
<label for="inputPrompt">{{ 'app.generate.prompt' | transloco }}</label>
<label for="inputPrompt">{{ 'app.generate.prompt' | transloco }} <app-tooltip [text]="'app.generate.help.prompt' | transloco" /></label>
<textarea required type="text" class="form-control" formControlName="prompt" id="inputPrompt" auto-grow></textarea>
<div class="flex">
<button type="button" class="btn mr-05" (click)="openModal(styleModal)">{{'app.generate.use_style' | transloco}}</button>
Expand All @@ -22,16 +22,16 @@
<app-effective-value [value]="modifiedOptions()?.prompt" [original]="form.value.prompt" />
</div>
<div class="form-group">
<label for="inputNegativePrompt">{{ 'app.generate.negative_prompt' | transloco }}</label>
<label for="inputNegativePrompt">{{ 'app.generate.negative_prompt' | transloco }} <app-tooltip [text]="'app.generate.help.negative_prompt' | transloco" /></label>
<textarea type="text" class="form-control" formControlName="negativePrompt" id="inputNegativePrompt" auto-grow></textarea>
<app-effective-value [value]="modifiedOptions()?.negativePrompt" [original]="form.value.negativePrompt" />
</div>
<div class="form-group">
<label for="inputSeed">{{'app.generate.seed' | transloco}}</label>
<label for="inputSeed">{{'app.generate.seed' | transloco}} <app-tooltip [text]="'app.generate.help.seed' | transloco" /></label>
<input type="text" class="form-control" formControlName="seed" id="inputSeed" autocomplete="off" />
</div>
<div class="form-group">
<label for="inputSampler">{{ 'app.generate.sampler' | transloco }}</label>
<label for="inputSampler">{{ 'app.generate.sampler' | transloco }} <app-tooltip [text]="'app.generate.help.sampler' | transloco" /></label>
<select id="inputSampler" formControlName="sampler" required tom-select [maxItems]="1">
@for (enumCase of Sampler | keyvalue; track enumCase.key) {
<option [value]="enumCase.value">{{ enumCase.value }}</option>
Expand All @@ -40,7 +40,7 @@
<app-effective-value [value]="modifiedOptions()?.sampler" [original]="form.value.sampler" />
</div>
<div class="form-group">
<label for="inputModel">{{ 'app.generate.model' | transloco }}</label>
<label for="inputModel">{{ 'app.generate.model' | transloco }} <app-tooltip [text]="'app.generate.help.model' | transloco" /></label>
<select id="inputModel" formControlName="model" required tom-select [maxItems]="1" [maxOptions]="null">
@for (item of groupedModels() | keyvalue; track item.key) {
<optgroup [label]="item.key">
Expand Down Expand Up @@ -106,7 +106,7 @@
}
</div>
<div class="form-group">
<label>{{'app.generate.lora_list' | transloco}}</label>
<label>{{'app.generate.lora_list' | transloco}} <app-tooltip [text]="'app.generate.help.lora_list' | transloco" /></label>
<button [disabled]="(form.value.loraList?.length ?? 0) >= 5" class="btn" type="button"
(click)="openModal(addLoraModal)">{{'app.generate.add_loras' | transloco}}</button>
<ng-template #addLoraModal>
Expand Down Expand Up @@ -136,7 +136,7 @@
}
</div>
<div class="form-group">
<label for="inputFaceFixers">{{'app.generate.face_fixers' | transloco}}</label>
<label for="inputFaceFixers">{{'app.generate.face_fixers' | transloco}} <app-tooltip [text]="'app.generate.help.face_fixers' | transloco" /></label>
<select id="inputFaceFixers" multiple tom-select formControlName="faceFixers">
@for (enumCase of PostProcessor | keyvalue; track enumCase.key) {
@if (enumCase.value | isFaceFixer) {
Expand All @@ -148,12 +148,12 @@
@if (form.value.faceFixers?.length) {
<div class="form-group">
<!--suppress XmlInvalidId -->
<label for="inputFaceFixerStrength">{{ 'app.generate.face_fixer_strength' | transloco }}</label>
<label for="inputFaceFixerStrength">{{ 'app.generate.face_fixer_strength' | transloco }} <app-tooltip [text]="'app.generate.help.face_fixer_strength' | transloco" /></label>
<app-slider-with-value [min]="0" [max]="1" [step]="0.01" formControlName="faceFixerStrength" inputId="inputFaceFixerStrength" />
</div>
}
<div class="form-group">
<label for="inputUpscaler">{{'app.generate.upscaler' | transloco}}</label>
<label for="inputUpscaler">{{'app.generate.upscaler' | transloco}} <app-tooltip [text]="'app.generate.help.upscaler' | transloco" /></label>
<select id="inputUpscaler" tom-select formControlName="upscaler" [maxItems]="1">
<option [value]="null">{{'app.select.empty_option' | transloco}}</option>
@for (enumCase of PostProcessor | keyvalue; track enumCase.key) {
Expand All @@ -164,7 +164,7 @@
</select>
</div>
<div class="form-group">
<label for="inputPostProcessors">{{'app.generate.post_processors' | transloco}}</label>
<label for="inputPostProcessors">{{'app.generate.post_processors' | transloco}} <app-tooltip [text]="'app.generate.help.post_processors' | transloco" /></label>
<select id="inputPostProcessors" multiple tom-select formControlName="genericPostProcessors">
@for (enumCase of PostProcessor | keyvalue; track enumCase.key) {
@if (!(enumCase.value | isFaceFixer) && !(enumCase.value | isUpscaler)) {
Expand All @@ -174,63 +174,77 @@
</select>
</div>
<div class="form-group">
<label for="inputWidth">{{ 'app.generate.width' | transloco }}</label>
<label for="inputWidth">{{ 'app.generate.width' | transloco }} <app-tooltip [text]="'app.generate.help.width' | transloco" /></label>
<input type="number" min="64" max="3072" step="64" formControlName="width" id="inputWidth"
class="form-control"/>
<app-effective-value [value]="modifiedOptions()?.width" [original]="form.value.width" />
</div>
<div class="form-group">
<label for="inputHeight">{{ 'app.generate.height' | transloco }}</label>
<label for="inputHeight">{{ 'app.generate.height' | transloco }} <app-tooltip [text]="'app.generate.help.height' | transloco" /></label>
<input type="number" min="64" max="3072" step="64" formControlName="height" id="inputHeight"
class="form-control"/>
<app-effective-value [value]="modifiedOptions()?.height" [original]="form.value.height" />
</div>
<div class="form-group">
<!-- <div class="form-group">-->
<!--suppress XmlInvalidId -->
<label for="inputDenoisingStrength">{{ 'app.generate.denoising_strength' | transloco }})</label>
<app-slider-with-value [min]="0.01" [max]="1" [step]="0.01" formControlName="denoisingStrength" inputId="inputDenoisingStrength" />
</div>
<!-- <label for="inputDenoisingStrength">{{ 'app.generate.denoising_strength' | transloco }} <app-tooltip [text]="'app.generate.help.denoising_strength' | transloco" /></label>-->
<!-- <app-slider-with-value [min]="0.01" [max]="1" [step]="0.01" formControlName="denoisingStrength" inputId="inputDenoisingStrength" />-->
<!-- </div>-->
<div class="form-group">
<!--suppress XmlInvalidId -->
<label for="inputCfgScale">{{ 'app.generate.cfg_scale' | transloco }}</label>
<label for="inputCfgScale">{{ 'app.generate.cfg_scale' | transloco }} <app-tooltip [text]="'app.generate.help.cfg_scale' | transloco" /></label>
<app-slider-with-value [min]="0" [max]="100" [step]="0.5" formControlName="cfgScale" inputId="inputCfgScale" />
<app-effective-value [value]="modifiedOptions()?.cfgScale" [original]="form.value.cfgScale" />
</div>
<div class="form-group">
<!--suppress XmlInvalidId -->
<label for="inputSteps">{{ 'app.generate.steps' | transloco }}</label>
<label for="inputSteps">{{ 'app.generate.steps' | transloco }} <app-tooltip [text]="'app.generate.help.steps' | transloco" /></label>
<app-slider-with-value [min]="1" [max]="500" [step]="1" formControlName="steps" inputId="inputSteps" />
<app-effective-value [value]="modifiedOptions()?.steps" [original]="form.value.steps" />
</div>
<div class="form-group">
<!--suppress XmlInvalidId -->
<label for="inputClipSkip">{{ 'app.generate.clip_skip' | transloco }}</label>
<label for="inputClipSkip">{{ 'app.generate.clip_skip' | transloco }} <app-tooltip [text]="'app.generate.help.clip_skip' | transloco" /></label>
<app-slider-with-value [min]="1" [max]="12" [step]="1" formControlName="clipSkip" inputId="inputClipSkip" />
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<app-toggle-checkbox [description]="'app.generate.karras' | transloco" formControlName="karras" />
<app-toggle-checkbox [description]="'app.generate.karras' | transloco" formControlName="karras">
<app-tooltip [text]="'app.generate.help.karras' | transloco" />
</app-toggle-checkbox>
</div>
<div class="form-group">
<app-toggle-checkbox [description]="'app.generate.hires_fix' | transloco" formControlName="hiresFix" />
<app-toggle-checkbox [description]="'app.generate.hires_fix' | transloco" formControlName="hiresFix">
<app-tooltip [text]="'app.generate.help.hires_fix' | transloco" />
</app-toggle-checkbox>
</div>
<div class="form-group">
<app-toggle-checkbox [description]="'app.generate.nsfw' | transloco" formControlName="nsfw" />
<app-toggle-checkbox [description]="'app.generate.nsfw' | transloco" formControlName="nsfw">
<app-tooltip [text]="'app.generate.help.nsfw' | transloco" />
</app-toggle-checkbox>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<app-toggle-checkbox [description]="'app.generate.slow_workers' | transloco" formControlName="slowWorkers" />
<app-toggle-checkbox [description]="'app.generate.slow_workers' | transloco" formControlName="slowWorkers">
<app-tooltip [text]="'app.generate.help.slow_workers' | transloco" />
</app-toggle-checkbox>
</div>
<div class="form-group">
<app-toggle-checkbox [description]="'app.generate.censor_nsfw' | transloco" formControlName="censorNsfw" />
<app-toggle-checkbox [description]="'app.generate.censor_nsfw' | transloco" formControlName="censorNsfw">
<app-tooltip [text]="'app.generate.help.censor_nsfw' | transloco" />
</app-toggle-checkbox>
</div>
<div class="form-group">
<app-toggle-checkbox [description]="'app.generate.trusted_workers' | transloco" formControlName="trustedWorkers" />
<app-toggle-checkbox [description]="'app.generate.trusted_workers' | transloco" formControlName="trustedWorkers">
<app-tooltip [text]="'app.generate.help.trusted_workers' | transloco" />
</app-toggle-checkbox>
</div>
<div class="form-group">
<app-toggle-checkbox [description]="'app.generate.allow_downgrade' | transloco" formControlName="allowDowngrade" />
<app-toggle-checkbox [description]="'app.generate.allow_downgrade' | transloco" formControlName="allowDowngrade">
<app-tooltip [text]="'app.generate.help.allow_downgrade' | transloco" />
</app-toggle-checkbox>
</div>
</div>
</div>
Expand Down
4 changes: 3 additions & 1 deletion src/app/pages/generate-image/generate-image.component.ts
Expand Up @@ -63,6 +63,7 @@ import _ from 'lodash';
import {BaselineModel} from "../../types/sd-repo/baseline-model";
import {AutoGrowDirective} from "../../directives/auto-grow.directive";
import {SliderWithValueComponent} from "../../components/slider-with-value/slider-with-value.component";
import {TooltipComponent} from "../../components/tooltip/tooltip.component";

interface Result {
width: number;
Expand Down Expand Up @@ -105,7 +106,8 @@ interface Result {
IsFaceFixerPipe,
IsUpscalerPipe,
AutoGrowDirective,
SliderWithValueComponent
SliderWithValueComponent,
TooltipComponent
],
templateUrl: './generate-image.component.html',
styleUrl: './generate-image.component.scss'
Expand Down
22 changes: 22 additions & 0 deletions src/assets/i18n/en.json
Expand Up @@ -210,5 +210,27 @@
"app.worker.pause": "Pause worker",
"app.worker.resume": "Resume worker",
"app.lora.broken": "The CivitAI api is currently broken and you can only see the latest version of a LoRa. This needs to be fixed on CivitAI's side and there's nothing we can do.",
"app.generate.help.prompt": "{{app.generate.prompt}} is where you describe what you want in the picture. Depending on the model it could be one or multiple sentences or, more commonly with Stable Diffusion, a comma separated list of keywords.",
"app.generate.help.negative_prompt": "Here you can (optionally) describe what you don't want the image to contain. The format is the same as for the {{app.generate.prompt}}. A good starting point is checking the {{app.generate.model}}'s homepage for any instructions by the author.",
"app.generate.help.seed": "The Seed is like a unique ID for your image. If you repeat all the parameters (including the same {{app.generate.seed}}), the AI will generate the same image every time. If you don’t provide a seed, the AI will use a random one, assuring that the image generated will always be unique, even if every other parameter remains the same.",
"app.generate.help.sampler": "Consider the {{app.generate.sampler}} as the engine that powers image generation. It's the technical tool that uses your given instructions to create the image. Different samplers follow different strategies to make this happen. There isn't a one-size-fits-all guide to choosing a sampler, so it's best to experiment and find one that works best for you.",
"app.generate.help.model": "The {{app.generate.model}} essentially shapes what the final image will look like. Each model has been trained with different images, which means they all interpret prompts differently and produce unique results. For the best outcomes, feel free to experiment with various models.",
"app.generate.help.lora_list": "{{app.generate.lora_list}} allow you to tweak the image with capabilities or concepts the base model itself might not have.",
"app.generate.help.face_fixers": "{{app.generate.face_fixers}} are specialized post processors that aim to fix deformed faces in generated images. Only useful if you're generating images of people.",
"app.generate.help.face_fixer_strength": "The strength of the above post processors. Play with the value until you're happy.",
"app.generate.help.upscaler": "A post processor that makes the image a higher resolution by upscaling it.",
"app.generate.help.post_processors": "All other generic post processors that modify the image after it has been generated.",
"app.generate.help.width": "The target width of the image, must be divisible by 64.",
"app.generate.help.height": "The target height of the image, must be divisible by 64.",
"app.generate.help.cfg_scale": "{{app.generate.cfg_scale}} controls how closely the AI follows your prompt. Lower values mean the AI is more creative, while higher values mean it will try to follow your prompt more closely.",
"app.generate.help.steps": "{{app.generate.steps}} controls how many steps the AI takes to generate your image. In each step the image is generated according to your prompt while taking the image from the previous step into account. In simple terms, after each step it tries to improve the previous image to match your prompt more closely. Note that it can also go overboard and make the image worse. Around 20-30 steps is usually the lower threshold for a good image while going above 60 rarely makes sense.",
"app.generate.help.clip_skip": "{{app.generate.clip_skip}} controls how the AI interprets your prompt. In the background your text is converted to a numerical representation that the AI then works with. This parameters allows you to skip some layers, essentially making the prompt different from the point of view of the AI.",
"app.generate.help.karras": "{{app.generate.karras}} improves the sampler with a different noise generating function. I can't think of a simple explanation, sorry. Basically there's no reason to turn it off.",
"app.generate.help.nsfw": "If you check this, only workers that allow NSFW content will be able to pick up your request.",
"app.generate.help.slow_workers": "Toggle whether you want to include slow workers in your request. Costs more kudos if you don't.",
"app.generate.help.censor_nsfw": "Whether you want to censor NSFW images. Sometimes even SFW prompts may lead to NSFW results, this will attempt to censor them.",
"app.generate.help.trusted_workers": "When enabled, only users that are trusted can process your request. Generally not needed.",
"app.generate.help.allow_downgrade": "When enabled, your request can be downgraded to fit within the available criteria if you don't have enough kudos.",
"app.generate.help.hires_fix": "HiRes fix works by generating your image at a lower resolution and then upscaling it.",
"app.generate.style_name": "Style name"
}
1 change: 1 addition & 0 deletions src/styles.scss
@@ -1,5 +1,6 @@
@import 'ngx-toastr/toastr';
@import 'tom-select/dist/scss/tom-select.default.scss';
@import "tippy.js/dist/tippy.css";

@import "scss/colors";

Expand Down

0 comments on commit f8d3511

Please sign in to comment.