├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc.json ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.adoc ├── client-src ├── @types │ ├── global.d.ts │ ├── modules.ts │ ├── vite-env.d.ts │ └── webui.d.ts ├── components │ ├── better-prompt │ │ ├── BetterPrompt.svelte │ │ ├── _logic │ │ │ ├── adjustPrompt.ts │ │ │ ├── betterPrompt.ts │ │ │ ├── context.ts │ │ │ ├── danbooruTags.ts │ │ │ ├── extraNetworks.ts │ │ │ ├── messages.ts │ │ │ ├── myPrompts.ts │ │ │ └── prompt.ts │ │ ├── my-prompt │ │ │ ├── AddNewMyPrompt.svelte │ │ │ ├── MyPrompt.svelte │ │ │ ├── MyPromptItem.svelte │ │ │ └── SelectAndDeleteMyPrompt.svelte │ │ └── prompt-edit │ │ │ ├── PromptEdit.svelte │ │ │ ├── _logic │ │ │ ├── purgeEmphasizedPrompt.ts │ │ │ └── undoRedo.ts │ │ │ ├── editor │ │ │ ├── Editor.svelte │ │ │ ├── TokenCounter.svelte │ │ │ ├── _logic │ │ │ │ └── context.ts │ │ │ └── prompt-list │ │ │ │ ├── ListItem.svelte │ │ │ │ ├── PromptList.svelte │ │ │ │ ├── WeightInput.svelte │ │ │ │ └── _logic │ │ │ │ ├── listItem.ts │ │ │ │ ├── promptList.ts │ │ │ │ └── weightInput.ts │ │ │ └── input │ │ │ ├── Input.svelte │ │ │ ├── PromptInput.svelte │ │ │ ├── _logic │ │ │ └── context.ts │ │ │ └── suggest │ │ │ ├── DanbooruItem.svelte │ │ │ ├── ExtraNetworksItem.svelte │ │ │ ├── Filters.svelte │ │ │ ├── List.svelte │ │ │ ├── ListItem.svelte │ │ │ ├── MyPromptItem.svelte │ │ │ ├── Suggest.svelte │ │ │ └── _logic │ │ │ ├── filters.ts │ │ │ └── suggest.ts │ └── widgets │ │ ├── Checkbox.svelte │ │ ├── MultiInput.svelte │ │ ├── NumberInput.svelte │ │ ├── Pagenation.svelte │ │ ├── Popup.svelte │ │ ├── PopupWindow.svelte │ │ ├── TextArea.svelte │ │ ├── TextInput.svelte │ │ └── Toast.svelte ├── libs │ ├── api │ │ ├── getDanbooruTags.ts │ │ ├── getExtraNetworks.ts │ │ ├── getLocalization.ts │ │ ├── getMyPrompts.ts │ │ ├── index.ts │ │ └── updateMyPrompts.ts │ ├── danbooru │ │ ├── danbooruTag.ts │ │ ├── index.ts │ │ ├── isDanbooruTag.ts │ │ └── tagToPrompt.ts │ ├── extra-networks │ │ ├── extraNetworks.ts │ │ └── index.ts │ ├── my-prompt │ │ ├── index.ts │ │ └── myPrompt.ts │ ├── prompt │ │ ├── allPrompt.ts │ │ ├── alternatePrompt.ts │ │ ├── basicPrompt.ts │ │ ├── emphasizedPrompt.ts │ │ ├── extraNetworksPrompt.ts │ │ ├── index.ts │ │ ├── innerPrompt.ts │ │ ├── plainPrompt.ts │ │ ├── prompt.ts │ │ ├── promptCombination.ts │ │ ├── scheduledPrompt.ts │ │ └── util │ │ │ ├── concatPrompt.ts │ │ │ ├── index.ts │ │ │ ├── isEquals │ │ │ ├── alternatePromptEquals.ts │ │ │ ├── emphasizedPromptEquals.ts │ │ │ ├── extraNetworksPromptEquals.ts │ │ │ ├── index.ts │ │ │ ├── plainPromptEquals.ts │ │ │ ├── promptCombinationEquals.ts │ │ │ └── scheduledPromptEquals.ts │ │ │ ├── isPromptType.ts │ │ │ ├── parsePrompt │ │ │ ├── index.ts │ │ │ └── prompt-parser.js │ │ │ └── toString │ └── util │ │ ├── array │ │ ├── index.ts │ │ ├── omitNulls.ts │ │ ├── sortByIndexes.ts │ │ └── toggleValue.ts │ │ ├── dom │ │ ├── addClasses.ts │ │ ├── applyClasses.ts │ │ ├── getElement.ts │ │ ├── getElementAll.ts │ │ ├── getScreenPosition.ts │ │ ├── hasChild.ts │ │ ├── hasClass.ts │ │ ├── hasElement.ts │ │ ├── index.ts │ │ ├── removeAllChild.ts │ │ ├── removeClasses.ts │ │ ├── rotateElement.ts │ │ └── scrollIntoViewIfNeeded.ts │ │ ├── string │ │ ├── generateHashCode.ts │ │ ├── index.ts │ │ └── toggleValue.ts │ │ ├── types │ │ ├── index.ts │ │ ├── isArray.ts │ │ ├── isBoolean.ts │ │ ├── isNumber.ts │ │ ├── isObject.ts │ │ └── isString.ts │ │ └── webui │ │ ├── dispatchEvent.ts │ │ ├── getCurrentTabName.ts │ │ ├── getOption.ts │ │ ├── index.ts │ │ ├── isDarkMode.ts │ │ ├── t.ts │ │ └── withBooleanOption.ts ├── main.ts └── styles │ ├── global.css │ ├── index.css │ └── theme.css ├── data ├── danbooru-tags.json └── negative-prompts.json ├── dev-scripts ├── crawl-danbooru-tags.mjs └── merge-danbooru-tags.mjs ├── docs ├── README-ja.adoc └── images │ ├── add-new-my-prompt.png │ ├── components.png │ ├── delete-my-prompts.png │ ├── input-component.png │ ├── install.png │ ├── my-prompt.png │ ├── overview.png │ ├── prompt-component.png │ ├── settings.png │ └── suggest-items.png ├── index.html ├── javascript └── betterPrompt.js ├── locales └── ja_JP.json ├── package.json ├── parser-src └── prompt-parser.lark ├── pnpm-lock.yaml ├── postcss.config.cjs ├── scripts └── better_prompt.py ├── style.css ├── svelte.config.js ├── tailwind.config.cjs ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /javascript 4 | .env 5 | .env.* 6 | !.env.example 7 | 8 | # Ignore files for PNPM, NPM and YARN 9 | pnpm-lock.yaml 10 | package-lock.json 11 | yarn.lock 12 | 13 | # Config files 14 | .eslintrc.cjs 15 | vite.config.ts 16 | svelte.config.js 17 | *.config.cjs 18 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: "@typescript-eslint/parser", 3 | parserOptions: { 4 | tsconfigRootDir: __dirname, 5 | project: ["./tsconfig.json"], 6 | extraFileExtensions: [".svelte"], 7 | }, 8 | extends: [ 9 | "eslint:recommended", 10 | "plugin:@typescript-eslint/recommended", 11 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 12 | "plugin:svelte/recommended", 13 | "plugin:svelte/prettier", 14 | "prettier", 15 | ], 16 | plugins: ["@typescript-eslint"], 17 | overrides: [ 18 | { 19 | files: ["**/*.svelte"], 20 | parser: "svelte-eslint-parser", 21 | parserOptions: { 22 | parser: "@typescript-eslint/parser", 23 | }, 24 | }, 25 | { 26 | files: ["**/*.ts", "**/*.svelte"], 27 | rules: { 28 | "no-undef": "off", 29 | }, 30 | }, 31 | ] 32 | }; 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dev-scripts/tmp 12 | dist 13 | dist-ssr 14 | *.local 15 | 16 | # Editor directories and files 17 | .vscode/* 18 | !.vscode/extensions.json 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | 27 | # Directories created at runtime 28 | __pycache__/ 29 | user-data/ 30 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | pnpm exec lint-staged 5 | -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "client-src/**/*.{js,ts,svelte}": [ 3 | "pnpm run check", 4 | "prettier --write", 5 | "eslint --fix" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /javascript 4 | .env 5 | .env.* 6 | !.env.example 7 | 8 | # Ignore files for PNPM, NPM and YARN 9 | pnpm-lock.yaml 10 | package-lock.json 11 | yarn.lock 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "singleQuote": false, 4 | "trailingComma": "es5", 5 | "printWidth": 100, 6 | "plugins": [ 7 | "prettier-plugin-svelte", 8 | "prettier-plugin-tailwindcss" 9 | ], 10 | "pluginSearchDirs": ["."], 11 | "overrides": [ 12 | { 13 | "files": "*.svelte", 14 | "options": { 15 | "parser": "svelte" 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.4.1 2 | ### Add 3 | - Added a cancel button to the registration dialog of "My Prompt". 4 | ## Fix 5 | - Fixed an issue where the option values were not reflected in the new version of the Web UI. 6 | ### Remove 7 | - Removed version change feature. 8 | - Removed update notification feature. 9 | 10 | ## v0.4.0 11 | ### Add 12 | - Added "My Prompt" feature, which was originally developed as "Aliases". Refer to the [My Prompt Tab](https://github.com/eideehi/sd-webui-better-prompt#my-prompt-tab) section in the README for details. 13 | ### Change 14 | - Changed the format of the translation file. 15 | ### Fix 16 | - When setting the default weight for LoRA, it now references the Web UI configuration. **([#13])** 17 | - Fixed the issue where the prompt input area didn't maximize its width when wrapped. 18 | 19 | ## v0.3.0 20 | ### Update 21 | - Updated Danbooru tags. 22 | ### Change 23 | - (For developers) Introduced the Svelte framework. 24 | - Changed the word "Multiplier" to "Weight". 25 | - Enabled mouse wheel control for the Weight slider. 26 | - Improved prompt parsing performance. **([#12])** 27 | ### Fix 28 | - Improved synchronization with the original prompt area. **([#8])** 29 | ### Remove 30 | - Temporarily removed the Undo Redo feature. 31 | 32 | ## v0.2.0 33 | ### Add 34 | - Added a button to display metadata in thumbnail preview for Textual Inversion and LoRA. 35 | - Added undo/redo functionality to the prompt. 36 | ### Fix 37 | - Fixed an issue where list elements would flicker when updating the prompt. 38 | - Fixed bugs related to Git. 39 | 40 | ## v0.1.0 41 | This is the initial release of Better Prompt. 42 | 43 | 44 | [#8]: https://github.com/eideehi/sd-webui-better-prompt/issues/8 45 | [#12]: https://github.com/eideehi/sd-webui-better-prompt/issues/12 46 | [#13]: https://github.com/eideehi/sd-webui-better-prompt/issues/13 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 EideeHi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Better Prompt 2 | 3 | English | link:docs/README-ja.adoc[日本語] 4 | 5 | Better Prompt is an extension of the https://github.com/AUTOMATIC1111/stable-diffusion-webui[Stable Diffusion web UI] that adds a UI to assist with prompt input and editing. 6 | 7 | image::docs/images/overview.png[Image - Overview] 8 | 9 | == Overview 10 | Better Prompt was created to reduce the various inconveniences of traditional prompt input and editing. It allows you to set Textual Inversion and LoRA without displaying Extra networks (🎴), rearrange the order of prompts by drag and drop, and adjust the emphasis level of prompts through a GUI. 11 | 12 | == Precautions 13 | Better Prompt is a JavaScript-based extension. Please be aware that if JavaScript files are not loaded correctly, such as when using the `--data-dir` option, Better Styles not function properly. 14 | 15 | == Installation 16 | === Installing from the Browser (Recommended) 17 | You can install it from the "Install from URL" option in the Extensions tab. Simply enter https://github.com/eideehi/sd-webui-better-prompt.git in the "URL for extension's git repository" field and press the "Install" button. 18 | 19 | image::docs/images/install.png[Image - Install] 20 | 21 | === Installing using Git 22 | You can also install it by executing the following command in the directory where Stable Diffusion web UI is installed. 23 | [source,shell] 24 | ---- 25 | git clone https://github.com/eideehi/sd-webui-better-prompt.git extensions/sd-webui-better-prompt 26 | ---- 27 | 28 | === Better Prompt is not displayed or the display is broken 29 | By installing it using the above methods, the latest version will be installed. However, it may not work well with the version of Web UI you are using. If Better Prompt is not displayed properly, it may be possible to resolve the issue by using a different version. 30 | 31 | TIP: You can change the version of Better Prompt from the Settings tab. For more information, refer to <>. 32 | 33 | == Usage 34 | Better Prompt adds the part enclosed by the red line in the image below. We will explain how to use this added content from now on. 35 | 36 | image::docs/images/components.png[Image - Components] 37 | 38 | === Prompt Edit Tab 39 | This is the default content displayed where you can edit the prompt. 40 | 41 | ==== Prompt Addition Form [[input-form]] 42 | Adding a prompt is done using the three elements shown in the following image. 43 | 44 | image::docs/images/input-component.png[Image - Input Component] 45 | 46 | ===== [1] Prompt Input Field [[input-field]] 47 | Enter a prompt in this field and press the Enter key to add the content to the positive prompt. Alternatively, press the Shift key while pressing Enter to add the content to the negative prompt. If there are *Textual Inversion*, *LoRA*, *Danbooru tags*, or *My Prompt* similar to the input content, they will be displayed in a list in <>. 48 | 49 | TIP: Fuse.js is used for similarity determination, allowing various determination methods such as "exact match," "prefix/suffix," and "AND/OR/NOT" to be used. For more information, refer to the https://fusejs.io/examples.html#extended-search[fusejs.io documentation]. 50 | 51 | TIP: Press the Tab key to move the focus to the elements in <>, and press the Escape key to return the focus. The selected element can be added to the positive (negative) prompt by pressing Enter (or Shift + Enter). 52 | 53 | ===== [2] Suggest Filters 54 | Only elements with a check in these filters will be displayed in <>. 55 | 56 | ===== [3] Suggest Area [[suggest-area]] 57 | Displays a list of up to 20 elements similar to the content entered in <>. An example of the items to be added is shown in the following image. 58 | 59 | image::docs/images/suggest-items.png[Image - Suggest Items] 60 | 61 | Green is Textual Inversion, blue is LoRA, and the last is Danbooru tags. Clicking (or Shift-clicking) these elements allows them to be added to the positive (negative) prompt. 62 | 63 | NOTE: LoRA cannot be added to the negative prompt, so please be careful. 64 | 65 | TIP: You can check the thumbnail by right-clicking on the elements of Textual Inversion and LoRA. 66 | 67 | ==== Prompt Editing 68 | Prompt editing is done using the two elements shown in the following image. 69 | 70 | image::docs/images/prompt-component.png[Image - Prompt Component] 71 | 72 | ===== [1] Positive Prompt [[positive-prompt]] 73 | Prompts added using <> are displayed in this area. This element is synchronized with the positive prompt input area of the web UI. 74 | 75 | TIP: Each prompt can be reordered by drag and drop, and can be deleted by clicking while holding the Shift key. 76 | 77 | TIP: Right-clicking on LoRA and normal prompts displays a popup for adjusting the emphasis level. 78 | 79 | ===== [2] Negative Prompt 80 | Except for not being able to add LoRA, it is the same as <>. 81 | 82 | === My Prompt Tab 83 | You can assign aliases and tags to any prompt for easy management. The registered My Prompts will be displayed in the <> when editing prompts. 84 | 85 | image::docs/images/my-prompt.png[Image - My Prompt Overview] 86 | 87 | ==== Registering My Prompt 88 | When you click the ``Add new My Prompt`` button, a popup will appear for registering a new My Prompt. 89 | 90 | image::docs/images/add-new-my-prompt.png[Image - Add new My Prompt] 91 | 92 | ===== Label [[my-prompt-label]] 93 | Set the display name for the My Prompt. This will be used when displayed in suggestions, among other places. *This field is required and cannot be omitted*. Additionally, you cannot set a label that already exists. 94 | 95 | ===== Tags [[my-prompt-tags]] 96 | By setting tags, you can filter My Prompts based on their tags. When the input field is active, existing tags will be suggested and displayed as a list. You can add a new tag by pressing the Enter key after entering the desired string. This field is optional, and you can omit the input. 97 | 98 | ===== Prompt [[my-prompt-prompt]] 99 | Set any prompt for the My Prompt. When you click on a My Prompt displayed in the <>, the prompt you set here will be expanded. This field is required and cannot be omitted. 100 | 101 | ==== Deleting My Prompt 102 | By clicking on a My Prompt in the displayed list, you can select it by clicking the ``Select and delete My Prompt`` button. Then, you can delete all the selected My Prompts by pressing the ``Delete selected My Prompts`` button. Please note that once you delete a My Prompt, it cannot be restored, so use this feature with caution. 103 | 104 | image::docs/images/delete-my-prompts.png[Image - Delete My Prompts] 105 | 106 | ==== My Prompt Search 107 | The search for My Prompts references the elements <>, <>, and <>. If you enter ``style`` in the search form, any My Prompts that contain the string ``style`` in any of the aforementioned elements will be displayed in the search results. 108 | 109 | == Configuration 110 | Better Prompt creates its own configuration section in the Settings tab. Here, we will explain each item. 111 | 112 | image::docs/images/settings.png[Image - Settings] 113 | 114 | === Version of Better Prompt [[version-change]] 115 | You can change the version of Better Prompt. Selecting a blank space will change it to the latest version at that point. If you change the settings, it is necessary to restart the Web UI. (not just reload) 116 | 117 | TIP: The current version of Better Prompt is displayed in the console of the Web UI. Refer to the table below for the Web UI versions corresponding to each version. 118 | 119 | |=== 120 | | Version | Web UI Version (Minimum) | Web UI Version (Maximum) 121 | | 0.1.0 - 0.3.0 | 9e1afa9e (2023-03-25) | 1.3.2 122 | | 0.4.0 | 9e1afa9e (2023-03-25) | 1.4.0 123 | | 0.4.1 | 1.1.1 | ~ 124 | |=== 125 | 126 | === Display update notifications 127 | If checked, it will display notifications when updates are available. 128 | 129 | === Notify of updates only once per version 130 | If checked, it will only notify once for each version when updates are available. 131 | 132 | === Interval at which to display update notifications 133 | Specify the interval for displaying update notifications. The unit is "days", and the default value is 1 day. 134 | 135 | === Language of Better Prompt 136 | Specify the language used by Better Prompt. The default value is blank (English). Currently, ja_JP language is available. If you change the settings, it is necessary to reload the Web UI. 137 | 138 | == License 139 | Better Prompt is developed and published under the MIT license. For details on the license, please refer to the link below. 140 | 141 | link:./LICENSE[MIT License] 142 | -------------------------------------------------------------------------------- /client-src/@types/global.d.ts: -------------------------------------------------------------------------------- 1 | type ExtensionAvailableTab = "txt2img" | "img2img"; 2 | type WebUiTab = ExtensionAvailableTab | "other"; 3 | 4 | type Nullable = T | null | undefined; 5 | type Callback = () => unknown; 6 | type Callback1 = (arg1: T) => unknown; 7 | 8 | type OneOrMany = T | T[]; 9 | -------------------------------------------------------------------------------- /client-src/@types/modules.ts: -------------------------------------------------------------------------------- 1 | declare module "#/widgets/Toast.svelte" { 2 | export { SvelteComponentDev as default } from "svelte/internal"; 3 | 4 | export type ToastType = "info" | "success" | "warning" | "error"; 5 | 6 | export type ToastMessage = { 7 | type?: ToastType; 8 | text: string; 9 | duration?: number; 10 | }; 11 | 12 | export function showToast(message: ToastMessage): void; 13 | 14 | export function closeToast(message: ToastMessage): void; 15 | } 16 | -------------------------------------------------------------------------------- /client-src/@types/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /client-src/@types/webui.d.ts: -------------------------------------------------------------------------------- 1 | // injected-script 2 | declare let localization: Record; 3 | 4 | // script.js 5 | declare function gradioApp(): Document | ShadowRoot; 6 | declare function get_uiCurrentTabContent(): Nullable; 7 | declare function onUiLoaded(callback: Callback): void; 8 | declare function onUiTabChange(callback: Callback): void; 9 | declare function onUiUpdate(callback: Callback): void; 10 | 11 | // javascript/extraNetworks.js 12 | declare function extraNetworksRequestMetadata( 13 | event: Event, 14 | extraPage: string, 15 | cardName: string 16 | ): void; 17 | 18 | // javascript/localization.js 19 | declare function getTranslation(text: string): Nullable; 20 | 21 | // javascript/ui.js 22 | declare type OptionValue = string | number | boolean; 23 | declare let opts: Record; 24 | declare function updateInput(target: HTMLElement): void; 25 | -------------------------------------------------------------------------------- /client-src/components/better-prompt/BetterPrompt.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 |
26 |
27 | 34 | 37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 | 69 | -------------------------------------------------------------------------------- /client-src/components/better-prompt/_logic/adjustPrompt.ts: -------------------------------------------------------------------------------- 1 | import type { Prompt } from "@/libs/prompt"; 2 | import type { ExtraNetworksData } from "@/libs/extra-networks"; 3 | 4 | export function adjustPrompt(prompt: Prompt, textualInversions: ExtraNetworksData[]): Prompt { 5 | if (prompt.type !== "plain") return prompt; 6 | const data = textualInversions.find((data) => data.name === prompt.value); 7 | if (data == null) return prompt; 8 | return { 9 | type: "extra-networks", 10 | name: data.type, 11 | args: [data.name], 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /client-src/components/better-prompt/_logic/betterPrompt.ts: -------------------------------------------------------------------------------- 1 | import type { ExtraNetworksData, ExtraNetworksType } from "@/libs/extra-networks"; 2 | import { type Readable, readable } from "svelte/store"; 3 | import { getElement, getElementAll } from "@/libs/util/dom"; 4 | import { generateHashCode } from "@/libs/util/string"; 5 | 6 | export function createLoraReadable(tabName: ExtensionAvailableTab): Readable { 7 | return createReadable(`#${tabName}_lora_cards`, "lora"); 8 | } 9 | 10 | export function createTextualInversionReadable( 11 | tabName: ExtensionAvailableTab 12 | ): Readable { 13 | return createReadable(`#${tabName}_textual_inversion_cards`, "textual-inversion"); 14 | } 15 | 16 | function createReadable(selector: string, type: ExtraNetworksType): Readable { 17 | return readable([], (set) => { 18 | let timeout: number; 19 | 20 | let currentHash = -1; 21 | const update = () => { 22 | const element = getElement(selector); 23 | if (element == null) { 24 | timeout = window.setTimeout(update, 1000); 25 | return; 26 | } 27 | 28 | const hash = generateHashCode(element.innerHTML); 29 | if (hash === currentHash) { 30 | timeout = window.setTimeout(update, 1000); 31 | return; 32 | } 33 | currentHash = hash; 34 | 35 | set( 36 | getElementAll(`${selector} > .card`).map((card) => { 37 | const name = getElement(card, ".actions > .name")?.textContent || ""; 38 | const search_term = 39 | getElement(card, ".actions > .additional > .search_term")?.textContent || ""; 40 | const thumbnail = card.style.backgroundImage.trim(); 41 | return thumbnail.length === 0 42 | ? { type, name, search_term } 43 | : { type, name, search_term, thumbnail }; 44 | }) 45 | ); 46 | timeout = window.setTimeout(update, 1000); 47 | }; 48 | 49 | timeout = window.setTimeout(update, 0); 50 | return () => { 51 | window.clearTimeout(timeout); 52 | }; 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /client-src/components/better-prompt/_logic/context.ts: -------------------------------------------------------------------------------- 1 | import type { Writable } from "svelte/store"; 2 | import type { Prompt } from "@/libs/prompt"; 3 | 4 | export type BetterPromptContext = { 5 | tabName: ExtensionAvailableTab; 6 | prompts: { 7 | positive: Writable; 8 | negative: Writable; 9 | }; 10 | }; 11 | 12 | export const betterPromptContextKey = Symbol(); 13 | -------------------------------------------------------------------------------- /client-src/components/better-prompt/_logic/danbooruTags.ts: -------------------------------------------------------------------------------- 1 | import { type Readable, writable } from "svelte/store"; 2 | import type { DanbooruTag } from "@/libs/danbooru"; 3 | 4 | const _danbooruTags = writable([]); 5 | export const danbooruTags: Readable = { subscribe: _danbooruTags.subscribe }; 6 | 7 | let init = false; 8 | 9 | export function initDanbooruTags(tags: DanbooruTag[]): void { 10 | if (init) return; 11 | init = true; 12 | _danbooruTags.set(tags); 13 | } 14 | -------------------------------------------------------------------------------- /client-src/components/better-prompt/_logic/extraNetworks.ts: -------------------------------------------------------------------------------- 1 | import type { ExtraNetworksData } from "@/libs/extra-networks"; 2 | import { type Readable, writable } from "svelte/store"; 3 | 4 | const _lora = writable([]); 5 | export const lora: Readable = { subscribe: _lora.subscribe }; 6 | 7 | const _textualInversion = writable([]); 8 | export const textualInversion: Readable = { 9 | subscribe: _textualInversion.subscribe, 10 | }; 11 | 12 | let loraInit = false; 13 | let textualInversionInit = false; 14 | 15 | export function initLora(tags: ExtraNetworksData[]): void { 16 | if (loraInit) return; 17 | loraInit = true; 18 | _lora.set(tags); 19 | } 20 | 21 | export function initTextualInversion(tags: ExtraNetworksData[]): void { 22 | if (textualInversionInit) return; 23 | textualInversionInit = true; 24 | _textualInversion.set(tags); 25 | } 26 | -------------------------------------------------------------------------------- /client-src/components/better-prompt/_logic/messages.ts: -------------------------------------------------------------------------------- 1 | import { t } from "@/libs/util/webui"; 2 | 3 | export interface Message { 4 | translate(args?: Array): string; 5 | } 6 | 7 | class MessageImpl implements Message { 8 | private readonly key: string; 9 | private readonly defaultValue: string; 10 | 11 | constructor(key: string, defaultValue: string) { 12 | this.key = key; 13 | this.defaultValue = defaultValue; 14 | } 15 | 16 | translate(args?: Array): string { 17 | return t(this.key, { defaultValue: this.defaultValue, args }); 18 | } 19 | } 20 | 21 | const addNewMyPrompt = new MessageImpl("add-new-my-prompt", "Add new My Prompt"); 22 | const addThisMyPrompt = new MessageImpl("add-this-my-prompt", "Add this My Prompt"); 23 | const cancelAddingMyPrompt = new MessageImpl("cancel-adding-my-prompt", "Cancel adding My Prompt"); 24 | const cancelMyPromptDeletion = new MessageImpl( 25 | "cancel-my-prompt-deletion", 26 | "Cancel My Prompt deletion" 27 | ); 28 | const deleteSelectedMyPrompt = new MessageImpl( 29 | "delete-selected-my-prompt", 30 | "Delete selected My Prompts" 31 | ); 32 | const editorNegativePrompt = new MessageImpl("editor-negative-prompt", "Negative prompt"); 33 | const editorPrompt = new MessageImpl("editor-prompt", "Prompt"); 34 | const emptyMyPrompt = new MessageImpl( 35 | "empty-my-prompts", 36 | 'You don\'t have any My Prompt yet. Press the "Add My Prompt" button to add your My Prompt.' 37 | ); 38 | const inputPrompt = new MessageImpl("input-prompt", "Input prompt..."); 39 | const loraNegativePromptError = new MessageImpl( 40 | "lora-negative-prompt-error", 41 | "LoRA cannot be add to negative prompt" 42 | ); 43 | const myPrompt = new MessageImpl("my-prompt", "My Prompt"); 44 | const myPromptLabel = new MessageImpl("my-prompt-label", "Label"); 45 | const myPromptPrompt = new MessageImpl("my-prompt-prompt", "Prompt"); 46 | const myPromptTags = new MessageImpl("my-prompt-tags", "Tags"); 47 | const myPromptTagsEmpty = new MessageImpl("my-prompt-tags-empty", "No tags are set"); 48 | const pagenationEllipsis = new MessageImpl("pagenation-ellipsis", "…"); 49 | const pagenationNext = new MessageImpl("pagenation-next", "Next"); 50 | const pagenationPrevious = new MessageImpl("pagenation-previous", "Previous"); 51 | const promptEdit = new MessageImpl("prompt-edit", "Prompt Edit"); 52 | const searchMyPrompts = new MessageImpl("search-my-prompts", "Search my prompts..."); 53 | const selectAndDeleteMyPrompt = new MessageImpl( 54 | "select-and-delete-my-prompt", 55 | "Select and delete My Prompt" 56 | ); 57 | const suggestFilterAll = new MessageImpl("suggest-filter-all", "All"); 58 | const suggestFilterDanbooruCharacter = new MessageImpl( 59 | "suggest-filter-danbooru-character", 60 | "Danbooru Tag (Character)" 61 | ); 62 | const suggestFilterDanbooruCopyright = new MessageImpl( 63 | "suggest-filter-danbooru-copyright", 64 | "Danbooru Tag (Copyright)" 65 | ); 66 | const suggestFilterDanbooruGeneral = new MessageImpl( 67 | "suggest-filter-danbooru-general", 68 | "Danbooru Tag (General)" 69 | ); 70 | const suggestFilterLora = new MessageImpl("suggest-filter-lora", "LoRA"); 71 | const suggestFilterMyPrompt = new MessageImpl("suggest-filter-my-prompt", "My Prompt"); 72 | const suggestFilterTextualInversion = new MessageImpl( 73 | "suggest-filter-textual-inversion", 74 | "Textual Inversion" 75 | ); 76 | const weight = new MessageImpl("weight", "Weight"); 77 | 78 | export { 79 | addNewMyPrompt, 80 | addThisMyPrompt, 81 | cancelAddingMyPrompt as cancelAndClose, 82 | cancelMyPromptDeletion, 83 | deleteSelectedMyPrompt, 84 | editorNegativePrompt, 85 | editorPrompt, 86 | emptyMyPrompt, 87 | inputPrompt, 88 | loraNegativePromptError, 89 | myPrompt, 90 | myPromptLabel, 91 | myPromptPrompt, 92 | myPromptTags, 93 | myPromptTagsEmpty, 94 | pagenationEllipsis, 95 | pagenationNext, 96 | pagenationPrevious, 97 | promptEdit, 98 | searchMyPrompts, 99 | selectAndDeleteMyPrompt, 100 | suggestFilterAll, 101 | suggestFilterDanbooruCharacter, 102 | suggestFilterDanbooruCopyright, 103 | suggestFilterDanbooruGeneral, 104 | suggestFilterLora, 105 | suggestFilterMyPrompt, 106 | suggestFilterTextualInversion, 107 | weight, 108 | }; 109 | -------------------------------------------------------------------------------- /client-src/components/better-prompt/_logic/myPrompts.ts: -------------------------------------------------------------------------------- 1 | import { type Readable, writable } from "svelte/store"; 2 | import type { MyPrompt } from "@/libs/my-prompt"; 3 | import { updateMyPrompts } from "@/libs/api"; 4 | 5 | let values: MyPrompt[] = []; 6 | const _myPrompts = writable(values); 7 | export const myPrompts: Readable = { subscribe: _myPrompts.subscribe }; 8 | 9 | const _allMyPromptTags = writable([]); 10 | export const allMyPromptTags: Readable = { subscribe: _allMyPromptTags.subscribe }; 11 | 12 | _myPrompts.subscribe((values) => { 13 | _allMyPromptTags.set([...new Set(values.flatMap((value) => value.tags))].sort()); 14 | }); 15 | 16 | let init = false; 17 | 18 | export function initMyPrompts(myPrompts: MyPrompt[]): void { 19 | if (init) return; 20 | init = true; 21 | 22 | values = [...myPrompts]; 23 | _myPrompts.set(values); 24 | } 25 | 26 | export function addMyPrompt(myPrompt: MyPrompt): void { 27 | if (!init) return; 28 | 29 | const newValues = [...values, myPrompt]; 30 | void updateMyPrompts(newValues).then((success) => { 31 | if (!success) return; 32 | values = newValues; 33 | _myPrompts.set(values); 34 | }); 35 | } 36 | 37 | export function removeMyPrompts(myPrompts: MyPrompt[]): void { 38 | if (!init) return; 39 | 40 | const newValues = values.filter((value) => !myPrompts.includes(value)); 41 | void updateMyPrompts(newValues).then((success) => { 42 | if (!success) return; 43 | values = newValues; 44 | _myPrompts.set(values); 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /client-src/components/better-prompt/_logic/prompt.ts: -------------------------------------------------------------------------------- 1 | import { type ExtraNetworksPrompt, type Prompt, toString } from "@/libs/prompt"; 2 | import { 3 | alternatePromptToString, 4 | emphasizedNegativePromptToString, 5 | emphasizedPositivePromptToString, 6 | plainPromptToString, 7 | promptCombinationToString, 8 | scheduledPromptToString, 9 | } from "@/libs/prompt/util/toString"; 10 | 11 | export function getTextContent(prompt: Prompt): string { 12 | return promptToText(prompt).replaceAll("\\", ""); 13 | } 14 | 15 | function promptToText(prompt: Prompt): string { 16 | switch (prompt.type) { 17 | case "alternate": 18 | return alternatePromptToString(prompt); 19 | case "combination": 20 | return promptCombinationToString(prompt); 21 | case "emphasized-positive": 22 | return emphasizedPositivePromptToString(prompt); 23 | case "emphasized-negative": 24 | return emphasizedNegativePromptToString(prompt); 25 | case "emphasized-weighted": 26 | return `${toString(prompt.values)}: ${prompt.weight}`; 27 | case "extra-networks": 28 | return extraNetworksPromptToText(prompt); 29 | case "plain": 30 | return plainPromptToString(prompt); 31 | case "scheduled": 32 | return scheduledPromptToString(prompt); 33 | } 34 | return ""; 35 | } 36 | 37 | function extraNetworksPromptToText(prompt: ExtraNetworksPrompt): string { 38 | if (prompt.name === "textual-inversion") { 39 | return prompt.args[0]; 40 | } else if (prompt.name === "lora") { 41 | return `${prompt.args[0]}: ${prompt.args[1]}`; 42 | } 43 | return toString(prompt); 44 | } 45 | 46 | export function createDataset(prompt: Prompt): object { 47 | const dataset = {}; 48 | dataset["data-prompt-type"] = prompt.type; 49 | dataset["data-prompt-value"] = toString(prompt); 50 | switch (prompt.type) { 51 | case "extra-networks": 52 | dataset["data-extra-networks"] = prompt.name; 53 | break; 54 | } 55 | return dataset; 56 | } 57 | -------------------------------------------------------------------------------- /client-src/components/better-prompt/my-prompt/AddNewMyPrompt.svelte: -------------------------------------------------------------------------------- 1 | 56 | 57 | 60 | 61 | 62 |
63 | 64 | 69 |