diff --git a/package.json b/package.json index 00c65e0..63048ab 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/app/components/toggle-checkbox/toggle-checkbox.component.html b/src/app/components/toggle-checkbox/toggle-checkbox.component.html index 3b377b9..8ef0055 100644 --- a/src/app/components/toggle-checkbox/toggle-checkbox.component.html +++ b/src/app/components/toggle-checkbox/toggle-checkbox.component.html @@ -2,4 +2,5 @@
{{description()}} + diff --git a/src/app/components/toggle-checkbox/toggle-checkbox.component.scss b/src/app/components/toggle-checkbox/toggle-checkbox.component.scss index 8de7871..8320109 100644 --- a/src/app/components/toggle-checkbox/toggle-checkbox.component.scss +++ b/src/app/components/toggle-checkbox/toggle-checkbox.component.scss @@ -48,4 +48,5 @@ margin-left: 5px; position: relative; top: 2px; + margin-right: 4px; } diff --git a/src/app/components/tooltip/tooltip.component.html b/src/app/components/tooltip/tooltip.component.html new file mode 100644 index 0000000..189678a --- /dev/null +++ b/src/app/components/tooltip/tooltip.component.html @@ -0,0 +1 @@ + diff --git a/src/app/components/tooltip/tooltip.component.scss b/src/app/components/tooltip/tooltip.component.scss new file mode 100644 index 0000000..5c8b0fa --- /dev/null +++ b/src/app/components/tooltip/tooltip.component.scss @@ -0,0 +1,3 @@ +fa-icon { + cursor: help; +} diff --git a/src/app/components/tooltip/tooltip.component.ts b/src/app/components/tooltip/tooltip.component.ts new file mode 100644 index 0000000..646c818 --- /dev/null +++ b/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(); + public icon = signal(faCircleInfo); +} diff --git a/src/app/pages/generate-image/generate-image.component.html b/src/app/pages/generate-image/generate-image.component.html index 797610e..304ef79 100644 --- a/src/app/pages/generate-image/generate-image.component.html +++ b/src/app/pages/generate-image/generate-image.component.html @@ -5,7 +5,7 @@
- +
@@ -22,16 +22,16 @@
- +
- +
- + @for (item of groupedModels() | keyvalue; track item.key) { @@ -106,7 +106,7 @@ }
- + @@ -136,7 +136,7 @@ }
- + @for (enumCase of PostProcessor | keyvalue; track enumCase.key) { @@ -164,7 +164,7 @@
- +
- +
- +
-
+ - - -
+ + +
- +
- +
- +
- + + +
- + + +
- + + +
- + + +
- + + +
- + + +
- + + +
diff --git a/src/app/pages/generate-image/generate-image.component.ts b/src/app/pages/generate-image/generate-image.component.ts index b4394a0..41f0386 100644 --- a/src/app/pages/generate-image/generate-image.component.ts +++ b/src/app/pages/generate-image/generate-image.component.ts @@ -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; @@ -105,7 +106,8 @@ interface Result { IsFaceFixerPipe, IsUpscalerPipe, AutoGrowDirective, - SliderWithValueComponent + SliderWithValueComponent, + TooltipComponent ], templateUrl: './generate-image.component.html', styleUrl: './generate-image.component.scss' diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index dc264af..77188b7 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -211,5 +211,27 @@ "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.style_name": "Style name", - "app.lora.unknown": "Unknown LoRA (failed to fetch from CivitAI)" + "app.lora.unknown": "Unknown LoRA (failed to fetch from CivitAI)", + "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." } diff --git a/src/styles.scss b/src/styles.scss index e632aa0..3f1b905 100644 --- a/src/styles.scss +++ b/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"; diff --git a/yarn.lock b/yarn.lock index 3734a18..a1b471a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2387,6 +2387,11 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@popperjs/core@^2.9.0": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + "@rollup/rollup-android-arm-eabi@4.13.0": version "4.13.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz#b98786c1304b4ff8db3a873180b778649b5dff2b" @@ -5905,6 +5910,14 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +ngx-tippy-wrapper@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/ngx-tippy-wrapper/-/ngx-tippy-wrapper-6.3.0.tgz#981b165bcaddd08f28ac5daf20125ad7cbaa32c4" + integrity sha512-+kp8o/X1u0MV4/WuecxMr8XKsCO2H/Do28IRgljV+d3nLxHzPDUz2pEI4Bo5/1JxDZ3jG8PzAhH0y2OtVFkDbA== + dependencies: + tippy.js "^6.3.7" + tslib "^2.4.0" + ngx-toastr@^18.0.0: version "18.0.0" resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-18.0.0.tgz#0016e897108817eb6b2ee16ff130b4c42fc214b0" @@ -7264,6 +7277,13 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +tippy.js@^6.3.7: + version "6.3.7" + resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c" + integrity sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ== + dependencies: + "@popperjs/core" "^2.9.0" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -7306,7 +7326,7 @@ tree-kill@1.2.2: resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== -tslib@2.6.2, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.6.2: +tslib@2.6.2, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== @@ -7661,6 +7681,7 @@ wildcard@^2.0.0: integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + name wrap-ansi-cjs version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==