main 71e5cf35a67e cached
159 files
3.9 MB
1.0M tokens
1377 symbols
1 requests
Download .txt
Showing preview only (4,092K chars total). Download the full file or copy to clipboard to get everything.
Repository: eideehi/sd-webui-better-prompt
Branch: main
Commit: 71e5cf35a67e
Files: 159
Total size: 3.9 MB

Directory structure:
gitextract_tvvntrbx/

├── .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/
│   │   │           ├── alternatePromptToString.ts
│   │   │           ├── emphasizedPromptToString.ts
│   │   │           ├── extraNetworksPromptToString.ts
│   │   │           ├── index.ts
│   │   │           ├── plainPromptToString.ts
│   │   │           ├── promptCombinationToString.ts
│   │   │           └── scheduledPromptToString.ts
│   │   └── 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
├── index.html
├── javascript/
│   └── betterPrompt.js
├── locales/
│   └── ja_JP.json
├── package.json
├── parser-src/
│   └── prompt-parser.lark
├── postcss.config.cjs
├── scripts/
│   └── better_prompt.py
├── style.css
├── svelte.config.js
├── tailwind.config.cjs
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintignore
================================================
.DS_Store
node_modules
/javascript
.env
.env.*
!.env.example

# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

# Config files
.eslintrc.cjs
vite.config.ts
svelte.config.js
*.config.cjs


================================================
FILE: .eslintrc.cjs
================================================
module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    tsconfigRootDir: __dirname,
    project: ["./tsconfig.json"],
    extraFileExtensions: [".svelte"],
  },
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking",
    "plugin:svelte/recommended",
    "plugin:svelte/prettier",
    "prettier",
  ],
  plugins: ["@typescript-eslint"],
  overrides: [
    {
      files: ["**/*.svelte"],
      parser: "svelte-eslint-parser",
      parserOptions: {
        parser: "@typescript-eslint/parser",
      },
    },
    {
      files: ["**/*.ts", "**/*.svelte"],
      rules: {
        "no-undef": "off",
      },
    },
  ]
};


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dev-scripts/tmp
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# Directories created at runtime
__pycache__/
user-data/


================================================
FILE: .husky/pre-commit
================================================
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

pnpm exec lint-staged


================================================
FILE: .lintstagedrc.json
================================================
{
  "client-src/**/*.{js,ts,svelte}": [
    "pnpm run check",
    "prettier --write",
    "eslint --fix"
  ]
}


================================================
FILE: .prettierignore
================================================
.DS_Store
node_modules
/javascript
.env
.env.*
!.env.example

# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock


================================================
FILE: .prettierrc
================================================
{
  "useTabs": false,
  "singleQuote": false,
  "trailingComma": "es5",
  "printWidth": 100,
  "plugins": [
    "prettier-plugin-svelte",
    "prettier-plugin-tailwindcss"
  ],
  "pluginSearchDirs": ["."],
  "overrides": [
    {
      "files": "*.svelte",
      "options": {
        "parser": "svelte"
      }
    }
  ]
}


================================================
FILE: CHANGELOG.md
================================================
## v0.4.1
### Add
- Added a cancel button to the registration dialog of "My Prompt".
## Fix
- Fixed an issue where the option values were not reflected in the new version of the Web UI.
### Remove
- Removed version change feature.
- Removed update notification feature.

## v0.4.0
### Add
- 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.
### Change
- Changed the format of the translation file.
### Fix
- When setting the default weight for LoRA, it now references the Web UI configuration. **([#13])**
- Fixed the issue where the prompt input area didn't maximize its width when wrapped.

## v0.3.0
### Update
- Updated Danbooru tags.
### Change
- (For developers) Introduced the Svelte framework.
- Changed the word "Multiplier" to "Weight".
- Enabled mouse wheel control for the Weight slider.
- Improved prompt parsing performance. **([#12])**
### Fix
- Improved synchronization with the original prompt area. **([#8])**
### Remove
- Temporarily removed the Undo Redo feature.

## v0.2.0
### Add
- Added a button to display metadata in thumbnail preview for Textual Inversion and LoRA.
- Added undo/redo functionality to the prompt.
### Fix
- Fixed an issue where list elements would flicker when updating the prompt.
- Fixed bugs related to Git.

## v0.1.0
This is the initial release of Better Prompt.

<!-- Issue links -->
[#8]: https://github.com/eideehi/sd-webui-better-prompt/issues/8
[#12]: https://github.com/eideehi/sd-webui-better-prompt/issues/12
[#13]: https://github.com/eideehi/sd-webui-better-prompt/issues/13


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2023 EideeHi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

================================================
FILE: README.adoc
================================================
= Better Prompt

English | link:docs/README-ja.adoc[日本語]

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.

image::docs/images/overview.png[Image - Overview]

== Overview
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.

== Precautions
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.

== Installation
=== Installing from the Browser (Recommended)
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.

image::docs/images/install.png[Image - Install]

=== Installing using Git
You can also install it by executing the following command in the directory where Stable Diffusion web UI is installed.
[source,shell]
----
git clone https://github.com/eideehi/sd-webui-better-prompt.git extensions/sd-webui-better-prompt
----

=== Better Prompt is not displayed or the display is broken
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.

TIP: You can change the version of Better Prompt from the Settings tab. For more information, refer to <<version-change>>.

== Usage
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.

image::docs/images/components.png[Image - Components]

=== Prompt Edit Tab
This is the default content displayed where you can edit the prompt.

==== Prompt Addition Form [[input-form]]
Adding a prompt is done using the three elements shown in the following image.

image::docs/images/input-component.png[Image - Input Component]

===== [1] Prompt Input Field [[input-field]]
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 <<suggest-area>>.

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].

TIP: Press the Tab key to move the focus to the elements in <<suggest-area>>, 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).

===== [2] Suggest Filters
Only elements with a check in these filters will be displayed in <<suggest-area>>.

===== [3] Suggest Area [[suggest-area]]
Displays a list of up to 20 elements similar to the content entered in <<input-field>>. An example of the items to be added is shown in the following image.

image::docs/images/suggest-items.png[Image - Suggest Items]

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.

NOTE: LoRA cannot be added to the negative prompt, so please be careful.

TIP: You can check the thumbnail by right-clicking on the elements of Textual Inversion and LoRA.

==== Prompt Editing
Prompt editing is done using the two elements shown in the following image.

image::docs/images/prompt-component.png[Image - Prompt Component]

===== [1] Positive Prompt [[positive-prompt]]
Prompts added using <<input-form>> are displayed in this area. This element is synchronized with the positive prompt input area of the web UI.

TIP: Each prompt can be reordered by drag and drop, and can be deleted by clicking while holding the Shift key.

TIP: Right-clicking on LoRA and normal prompts displays a popup for adjusting the emphasis level.

===== [2] Negative Prompt
Except for not being able to add LoRA, it is the same as <<positive-prompt>>.

=== My Prompt Tab
You can assign aliases and tags to any prompt for easy management. The registered My Prompts will be displayed in the <<suggest-area>> when editing prompts.

image::docs/images/my-prompt.png[Image - My Prompt Overview]

==== Registering My Prompt
When you click the ``Add new My Prompt`` button, a popup will appear for registering a new My Prompt.

image::docs/images/add-new-my-prompt.png[Image - Add new My Prompt]

===== Label [[my-prompt-label]]
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.

===== Tags [[my-prompt-tags]]
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.

===== Prompt [[my-prompt-prompt]]
Set any prompt for the My Prompt. When you click on a My Prompt displayed in the <<suggest-area>>, the prompt you set here will be expanded. This field is required and cannot be omitted.

==== Deleting My Prompt
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.

image::docs/images/delete-my-prompts.png[Image - Delete My Prompts]

==== My Prompt Search
The search for My Prompts references the elements <<my-prompt-label>>, <<my-prompt-tags>>, and <<my-prompt-prompt>>. 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.

== Configuration
Better Prompt creates its own configuration section in the Settings tab. Here, we will explain each item.

image::docs/images/settings.png[Image - Settings]

=== Version of Better Prompt [[version-change]]
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)

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.

|===
| Version       | Web UI Version (Minimum) | Web UI Version (Maximum)
| 0.1.0 - 0.3.0 | 9e1afa9e (2023-03-25)    | 1.3.2
| 0.4.0         | 9e1afa9e (2023-03-25)    | 1.4.0
| 0.4.1         | 1.1.1                    | ~
|===

=== Display update notifications
If checked, it will display notifications when updates are available.

=== Notify of updates only once per version
If checked, it will only notify once for each version when updates are available.

=== Interval at which to display update notifications
Specify the interval for displaying update notifications. The unit is "days", and the default value is 1 day.

=== Language of Better Prompt
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.

== License
Better Prompt is developed and published under the MIT license. For details on the license, please refer to the link below.

link:./LICENSE[MIT License]


================================================
FILE: client-src/@types/global.d.ts
================================================
type ExtensionAvailableTab = "txt2img" | "img2img";
type WebUiTab = ExtensionAvailableTab | "other";

type Nullable<T> = T | null | undefined;
type Callback = () => unknown;
type Callback1<T> = (arg1: T) => unknown;

type OneOrMany<T> = T | T[];


================================================
FILE: client-src/@types/modules.ts
================================================
declare module "#/widgets/Toast.svelte" {
  export { SvelteComponentDev as default } from "svelte/internal";

  export type ToastType = "info" | "success" | "warning" | "error";

  export type ToastMessage = {
    type?: ToastType;
    text: string;
    duration?: number;
  };

  export function showToast(message: ToastMessage): void;

  export function closeToast(message: ToastMessage): void;
}


================================================
FILE: client-src/@types/vite-env.d.ts
================================================
/// <reference types="svelte" />
/// <reference types="vite/client" />


================================================
FILE: client-src/@types/webui.d.ts
================================================
// injected-script
declare let localization: Record<string, string>;

// script.js
declare function gradioApp(): Document | ShadowRoot;
declare function get_uiCurrentTabContent(): Nullable<Element>;
declare function onUiLoaded(callback: Callback): void;
declare function onUiTabChange(callback: Callback): void;
declare function onUiUpdate(callback: Callback): void;

// javascript/extraNetworks.js
declare function extraNetworksRequestMetadata(
  event: Event,
  extraPage: string,
  cardName: string
): void;

// javascript/localization.js
declare function getTranslation(text: string): Nullable<string>;

// javascript/ui.js
declare type OptionValue = string | number | boolean;
declare let opts: Record<string, OptionValue>;
declare function updateInput(target: HTMLElement): void;


================================================
FILE: client-src/components/better-prompt/BetterPrompt.svelte
================================================
<script lang="ts">
  import type { Prompt } from "@/libs/prompt";
  import { setContext } from "svelte";
  import { writable } from "svelte/store";
  import { type BetterPromptContext, betterPromptContextKey } from "./_logic/context";
  import { myPrompt, promptEdit } from "./_logic/messages";
  import PromptEdit from "./prompt-edit/PromptEdit.svelte";
  import MyPrompt from "./my-prompt/MyPrompt.svelte";

  export let tabName: ExtensionAvailableTab;

  const positive = writable<Prompt[]>([]);
  const negative = writable<Prompt[]>([]);

  setContext<BetterPromptContext>(betterPromptContextKey, {
    tabName,
    prompts: { positive, negative },
  });

  type BetterPromptTab = "prompt-edit" | "my-prompt";
  let activeTab: BetterPromptTab = "prompt-edit";
  const openTab = (tab: BetterPromptTab) => () => (activeTab = tab);
</script>

<div class="better-prompt" id="{tabName}-better-prompt">
  <div class="tabs scroll-hide">
    <button
      class="tab"
      class:selected={activeTab === "prompt-edit"}
      on:click={openTab("prompt-edit")}
    >
      {promptEdit.translate()}
    </button>
    <button class="tab" class:selected={activeTab === "my-prompt"} on:click={openTab("my-prompt")}>
      {myPrompt.translate()}
    </button>
  </div>
  <div class="content-area">
    <PromptEdit active={activeTab === "prompt-edit"} />
    <MyPrompt active={activeTab === "my-prompt"} />
  </div>
</div>

<style lang="postcss">
  .better-prompt {
    @apply w-full;
  }

  .tabs {
    @apply flex flex-wrap whitespace-nowrap border-b border-solid border-[--border-color-primary];
  }

  .tab {
    @apply -mb-[1px] rounded-t-[--container-radius] border border-b-0 border-solid border-transparent px-[--size-4] py-[--size-1] text-[length:--section-header-text-size] font-[--section-header-text-weight] text-[--body-text-color-subdued];
  }

  .tab.selected {
    @apply border-[--border-color-primary] text-[--body-text-color] [background:--background-fill-primary];
  }

  .tab:not(.active):hover {
    @apply text-[--body-text-color];
  }

  .content-area {
    @apply rounded-b-[--container-radius] border border-t-0 border-solid border-[--border-color-primary] p-[--block-padding];
  }
</style>


================================================
FILE: client-src/components/better-prompt/_logic/adjustPrompt.ts
================================================
import type { Prompt } from "@/libs/prompt";
import type { ExtraNetworksData } from "@/libs/extra-networks";

export function adjustPrompt(prompt: Prompt, textualInversions: ExtraNetworksData[]): Prompt {
  if (prompt.type !== "plain") return prompt;
  const data = textualInversions.find((data) => data.name === prompt.value);
  if (data == null) return prompt;
  return {
    type: "extra-networks",
    name: data.type,
    args: [data.name],
  };
}


================================================
FILE: client-src/components/better-prompt/_logic/betterPrompt.ts
================================================
import type { ExtraNetworksData, ExtraNetworksType } from "@/libs/extra-networks";
import { type Readable, readable } from "svelte/store";
import { getElement, getElementAll } from "@/libs/util/dom";
import { generateHashCode } from "@/libs/util/string";

export function createLoraReadable(tabName: ExtensionAvailableTab): Readable<ExtraNetworksData[]> {
  return createReadable(`#${tabName}_lora_cards`, "lora");
}

export function createTextualInversionReadable(
  tabName: ExtensionAvailableTab
): Readable<ExtraNetworksData[]> {
  return createReadable(`#${tabName}_textual_inversion_cards`, "textual-inversion");
}

function createReadable(selector: string, type: ExtraNetworksType): Readable<ExtraNetworksData[]> {
  return readable<ExtraNetworksData[]>([], (set) => {
    let timeout: number;

    let currentHash = -1;
    const update = () => {
      const element = getElement(selector);
      if (element == null) {
        timeout = window.setTimeout(update, 1000);
        return;
      }

      const hash = generateHashCode(element.innerHTML);
      if (hash === currentHash) {
        timeout = window.setTimeout(update, 1000);
        return;
      }
      currentHash = hash;

      set(
        getElementAll(`${selector} > .card`).map((card) => {
          const name = getElement(card, ".actions > .name")?.textContent || "";
          const search_term =
            getElement(card, ".actions > .additional > .search_term")?.textContent || "";
          const thumbnail = card.style.backgroundImage.trim();
          return thumbnail.length === 0
            ? { type, name, search_term }
            : { type, name, search_term, thumbnail };
        })
      );
      timeout = window.setTimeout(update, 1000);
    };

    timeout = window.setTimeout(update, 0);
    return () => {
      window.clearTimeout(timeout);
    };
  });
}


================================================
FILE: client-src/components/better-prompt/_logic/context.ts
================================================
import type { Writable } from "svelte/store";
import type { Prompt } from "@/libs/prompt";

export type BetterPromptContext = {
  tabName: ExtensionAvailableTab;
  prompts: {
    positive: Writable<Prompt[]>;
    negative: Writable<Prompt[]>;
  };
};

export const betterPromptContextKey = Symbol();


================================================
FILE: client-src/components/better-prompt/_logic/danbooruTags.ts
================================================
import { type Readable, writable } from "svelte/store";
import type { DanbooruTag } from "@/libs/danbooru";

const _danbooruTags = writable<DanbooruTag[]>([]);
export const danbooruTags: Readable<DanbooruTag[]> = { subscribe: _danbooruTags.subscribe };

let init = false;

export function initDanbooruTags(tags: DanbooruTag[]): void {
  if (init) return;
  init = true;
  _danbooruTags.set(tags);
}


================================================
FILE: client-src/components/better-prompt/_logic/extraNetworks.ts
================================================
import type { ExtraNetworksData } from "@/libs/extra-networks";
import { type Readable, writable } from "svelte/store";

const _lora = writable<ExtraNetworksData[]>([]);
export const lora: Readable<ExtraNetworksData[]> = { subscribe: _lora.subscribe };

const _textualInversion = writable<ExtraNetworksData[]>([]);
export const textualInversion: Readable<ExtraNetworksData[]> = {
  subscribe: _textualInversion.subscribe,
};

let loraInit = false;
let textualInversionInit = false;

export function initLora(tags: ExtraNetworksData[]): void {
  if (loraInit) return;
  loraInit = true;
  _lora.set(tags);
}

export function initTextualInversion(tags: ExtraNetworksData[]): void {
  if (textualInversionInit) return;
  textualInversionInit = true;
  _textualInversion.set(tags);
}


================================================
FILE: client-src/components/better-prompt/_logic/messages.ts
================================================
import { t } from "@/libs/util/webui";

export interface Message {
  translate(args?: Array<string | number | boolean>): string;
}

class MessageImpl implements Message {
  private readonly key: string;
  private readonly defaultValue: string;

  constructor(key: string, defaultValue: string) {
    this.key = key;
    this.defaultValue = defaultValue;
  }

  translate(args?: Array<string | number | boolean>): string {
    return t(this.key, { defaultValue: this.defaultValue, args });
  }
}

const addNewMyPrompt = new MessageImpl("add-new-my-prompt", "Add new My Prompt");
const addThisMyPrompt = new MessageImpl("add-this-my-prompt", "Add this My Prompt");
const cancelAddingMyPrompt = new MessageImpl("cancel-adding-my-prompt", "Cancel adding My Prompt");
const cancelMyPromptDeletion = new MessageImpl(
  "cancel-my-prompt-deletion",
  "Cancel My Prompt deletion"
);
const deleteSelectedMyPrompt = new MessageImpl(
  "delete-selected-my-prompt",
  "Delete selected My Prompts"
);
const editorNegativePrompt = new MessageImpl("editor-negative-prompt", "Negative prompt");
const editorPrompt = new MessageImpl("editor-prompt", "Prompt");
const emptyMyPrompt = new MessageImpl(
  "empty-my-prompts",
  'You don\'t have any My Prompt yet. Press the "Add My Prompt" button to add your My Prompt.'
);
const inputPrompt = new MessageImpl("input-prompt", "Input prompt...");
const loraNegativePromptError = new MessageImpl(
  "lora-negative-prompt-error",
  "LoRA cannot be add to negative prompt"
);
const myPrompt = new MessageImpl("my-prompt", "My Prompt");
const myPromptLabel = new MessageImpl("my-prompt-label", "Label");
const myPromptPrompt = new MessageImpl("my-prompt-prompt", "Prompt");
const myPromptTags = new MessageImpl("my-prompt-tags", "Tags");
const myPromptTagsEmpty = new MessageImpl("my-prompt-tags-empty", "No tags are set");
const pagenationEllipsis = new MessageImpl("pagenation-ellipsis", "…");
const pagenationNext = new MessageImpl("pagenation-next", "Next");
const pagenationPrevious = new MessageImpl("pagenation-previous", "Previous");
const promptEdit = new MessageImpl("prompt-edit", "Prompt Edit");
const searchMyPrompts = new MessageImpl("search-my-prompts", "Search my prompts...");
const selectAndDeleteMyPrompt = new MessageImpl(
  "select-and-delete-my-prompt",
  "Select and delete My Prompt"
);
const suggestFilterAll = new MessageImpl("suggest-filter-all", "All");
const suggestFilterDanbooruCharacter = new MessageImpl(
  "suggest-filter-danbooru-character",
  "Danbooru Tag (Character)"
);
const suggestFilterDanbooruCopyright = new MessageImpl(
  "suggest-filter-danbooru-copyright",
  "Danbooru Tag (Copyright)"
);
const suggestFilterDanbooruGeneral = new MessageImpl(
  "suggest-filter-danbooru-general",
  "Danbooru Tag (General)"
);
const suggestFilterLora = new MessageImpl("suggest-filter-lora", "LoRA");
const suggestFilterMyPrompt = new MessageImpl("suggest-filter-my-prompt", "My Prompt");
const suggestFilterTextualInversion = new MessageImpl(
  "suggest-filter-textual-inversion",
  "Textual Inversion"
);
const weight = new MessageImpl("weight", "Weight");

export {
  addNewMyPrompt,
  addThisMyPrompt,
  cancelAddingMyPrompt as cancelAndClose,
  cancelMyPromptDeletion,
  deleteSelectedMyPrompt,
  editorNegativePrompt,
  editorPrompt,
  emptyMyPrompt,
  inputPrompt,
  loraNegativePromptError,
  myPrompt,
  myPromptLabel,
  myPromptPrompt,
  myPromptTags,
  myPromptTagsEmpty,
  pagenationEllipsis,
  pagenationNext,
  pagenationPrevious,
  promptEdit,
  searchMyPrompts,
  selectAndDeleteMyPrompt,
  suggestFilterAll,
  suggestFilterDanbooruCharacter,
  suggestFilterDanbooruCopyright,
  suggestFilterDanbooruGeneral,
  suggestFilterLora,
  suggestFilterMyPrompt,
  suggestFilterTextualInversion,
  weight,
};


================================================
FILE: client-src/components/better-prompt/_logic/myPrompts.ts
================================================
import { type Readable, writable } from "svelte/store";
import type { MyPrompt } from "@/libs/my-prompt";
import { updateMyPrompts } from "@/libs/api";

let values: MyPrompt[] = [];
const _myPrompts = writable(values);
export const myPrompts: Readable<MyPrompt[]> = { subscribe: _myPrompts.subscribe };

const _allMyPromptTags = writable<string[]>([]);
export const allMyPromptTags: Readable<string[]> = { subscribe: _allMyPromptTags.subscribe };

_myPrompts.subscribe((values) => {
  _allMyPromptTags.set([...new Set(values.flatMap((value) => value.tags))].sort());
});

let init = false;

export function initMyPrompts(myPrompts: MyPrompt[]): void {
  if (init) return;
  init = true;

  values = [...myPrompts];
  _myPrompts.set(values);
}

export function addMyPrompt(myPrompt: MyPrompt): void {
  if (!init) return;

  const newValues = [...values, myPrompt];
  void updateMyPrompts(newValues).then((success) => {
    if (!success) return;
    values = newValues;
    _myPrompts.set(values);
  });
}

export function removeMyPrompts(myPrompts: MyPrompt[]): void {
  if (!init) return;

  const newValues = values.filter((value) => !myPrompts.includes(value));
  void updateMyPrompts(newValues).then((success) => {
    if (!success) return;
    values = newValues;
    _myPrompts.set(values);
  });
}


================================================
FILE: client-src/components/better-prompt/_logic/prompt.ts
================================================
import { type ExtraNetworksPrompt, type Prompt, toString } from "@/libs/prompt";
import {
  alternatePromptToString,
  emphasizedNegativePromptToString,
  emphasizedPositivePromptToString,
  plainPromptToString,
  promptCombinationToString,
  scheduledPromptToString,
} from "@/libs/prompt/util/toString";

export function getTextContent(prompt: Prompt): string {
  return promptToText(prompt).replaceAll("\\", "");
}

function promptToText(prompt: Prompt): string {
  switch (prompt.type) {
    case "alternate":
      return alternatePromptToString(prompt);
    case "combination":
      return promptCombinationToString(prompt);
    case "emphasized-positive":
      return emphasizedPositivePromptToString(prompt);
    case "emphasized-negative":
      return emphasizedNegativePromptToString(prompt);
    case "emphasized-weighted":
      return `${toString(prompt.values)}: ${prompt.weight}`;
    case "extra-networks":
      return extraNetworksPromptToText(prompt);
    case "plain":
      return plainPromptToString(prompt);
    case "scheduled":
      return scheduledPromptToString(prompt);
  }
  return "";
}

function extraNetworksPromptToText(prompt: ExtraNetworksPrompt): string {
  if (prompt.name === "textual-inversion") {
    return prompt.args[0];
  } else if (prompt.name === "lora") {
    return `${prompt.args[0]}: ${prompt.args[1]}`;
  }
  return toString(prompt);
}

export function createDataset(prompt: Prompt): object {
  const dataset = {};
  dataset["data-prompt-type"] = prompt.type;
  dataset["data-prompt-value"] = toString(prompt);
  switch (prompt.type) {
    case "extra-networks":
      dataset["data-extra-networks"] = prompt.name;
      break;
  }
  return dataset;
}


================================================
FILE: client-src/components/better-prompt/my-prompt/AddNewMyPrompt.svelte
================================================
<script lang="ts">
  import type { MyPrompt } from "@/libs/my-prompt";
  import { createEventDispatcher } from "svelte";
  import { addMyPrompt, allMyPromptTags, myPrompts } from "#/better-prompt/_logic/myPrompts";
  import {
    addNewMyPrompt,
    addThisMyPrompt,
    cancelAndClose,
    myPromptLabel,
    myPromptPrompt,
    myPromptTags,
  } from "#/better-prompt/_logic/messages";
  import TextInput from "#/widgets/TextInput.svelte";
  import PopupWindow from "#/widgets/PopupWindow.svelte";
  import MultiInput from "#/widgets/MultiInput.svelte";
  import TextArea from "#/widgets/TextArea.svelte";

  let showPopup = false;
  let myPrompt: MyPrompt = {
    label: "",
    tags: [],
    prompt: "",
  };

  const dispatch = createEventDispatcher();

  $: {
    if (showPopup) {
      dispatch("popupopen");
    } else {
      myPrompt = {
        label: "",
        tags: [],
        prompt: "",
      };
    }
  }

  export function closePopup(): void {
    showPopup = false;
  }

  function isValid({ label, prompt }: MyPrompt): boolean {
    if (!label || $myPrompts.find((myPrompt) => myPrompt.label === label) != null) return false;
    return prompt.length > 0;
  }

  function onClickConfirm(): void {
    if (!isValid(myPrompt)) return;

    addMyPrompt(myPrompt);

    showPopup = false;
  }
</script>

<button class="button secondary lg" on:click={() => (showPopup = true)}>
  {addNewMyPrompt.translate()}
</button>

<PopupWindow title={addNewMyPrompt.translate()} bind:show={showPopup}>
  <div class="add-new-my-prompt">
    <TextInput label={myPromptLabel.translate()} bind:value={myPrompt.label} />
    <MultiInput
      label={myPromptTags.translate()}
      bind:values={myPrompt.tags}
      list={$allMyPromptTags}
    />
    <TextArea label={myPromptPrompt.translate()} bind:value={myPrompt.prompt} />
    <div class="buttons">
      <button class="button primary lg" on:click={onClickConfirm} disabled={!isValid(myPrompt)}>
        {addThisMyPrompt.translate()}
      </button>
      <button class="button secondary lg" on:click={closePopup}>
        {cancelAndClose.translate()}
      </button>
    </div>
  </div>
</PopupWindow>

<style lang="postcss">
  .add-new-my-prompt {
    @apply flex w-[256px] flex-col gap-[--layout-gap] px-3 pb-2 sm:w-[384px] md:w-[512px] lg:w-[640px];
  }

  .buttons {
    @apply flex gap-3;
  }

  .buttons > .button {
    @apply grow basis-0;
  }
</style>


================================================
FILE: client-src/components/better-prompt/my-prompt/MyPrompt.svelte
================================================
<script lang="ts">
  import type { MyPrompt } from "@/libs/my-prompt";
  import Fuse from "fuse.js";
  import { myPrompts } from "#/better-prompt/_logic/myPrompts";
  import { emptyMyPrompt, searchMyPrompts } from "#/better-prompt/_logic/messages";
  import MyPromptItem from "./MyPromptItem.svelte";
  import TextInput from "#/widgets/TextInput.svelte";
  import Pagenation from "#/widgets/Pagenation.svelte";
  import AddNewMyPrompt from "./AddNewMyPrompt.svelte";
  import SelectAndDeleteMyPrompt from "./SelectAndDeleteMyPrompt.svelte";

  export let active: boolean;

  let addNewMyPrompt: Nullable<AddNewMyPrompt> = null;

  let searchKeyword = "";
  let fuse: Fuse<MyPrompt>;
  $: fuse = new Fuse($myPrompts, {
    useExtendedSearch: true,
    threshold: 0.1,
    keys: ["label", "tags", "prompt"],
  });

  let displayableMyPrompts: MyPrompt[];
  $: {
    if (searchKeyword) {
      displayableMyPrompts = [];
      fuse.search(searchKeyword).forEach((result) => {
        displayableMyPrompts.push(result.item);
      });
    } else {
      displayableMyPrompts = [...$myPrompts];
    }
  }

  let page = 1;
  let displayLimit = 20;

  let displayMyPrompts: MyPrompt[];
  $: {
    displayMyPrompts = [];
    const offset = (page - 1) * displayLimit;
    const max = offset + displayLimit;
    for (let i = offset; i < max; i++) {
      if (i >= displayableMyPrompts.length) break;
      displayMyPrompts.push(displayableMyPrompts[i]);
    }
  }

  let deleteMode = false;
  let selectedMyPrompts: MyPrompt[] = [];

  $: if (!active) {
    deleteMode = false;
    if (addNewMyPrompt != null) {
      // eslint-disable-next-line @typescript-eslint/no-unsafe-call
      addNewMyPrompt.closePopup();
    }
  }

  $: {
    if (deleteMode) {
      if (addNewMyPrompt != null) {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-call
        addNewMyPrompt.closePopup();
      }
    } else {
      selectedMyPrompts = [];
    }
  }

  function onMyPromptSelect(event: CustomEvent<{ selected: boolean; myPrompt: MyPrompt }>): void {
    const { selected, myPrompt } = event.detail;
    const index = selectedMyPrompts.indexOf(myPrompt);
    if (selected && index === -1) {
      selectedMyPrompts = [...selectedMyPrompts, myPrompt];
    } else if (!selected && index >= 0) {
      selectedMyPrompts.splice(index, 1);
      selectedMyPrompts = [...selectedMyPrompts];
    }
  }
</script>

<div class="my-prompt" class:active>
  <div class="tools">
    <TextInput bind:value={searchKeyword} options={{ placeholder: searchMyPrompts.translate() }} />
    <AddNewMyPrompt bind:this={addNewMyPrompt} on:popupopen={() => (deleteMode = false)} />
    {#if $myPrompts.length > 0}
      <SelectAndDeleteMyPrompt bind:deleteMode bind:selectedMyPrompts />
    {/if}
  </div>
  {#if displayableMyPrompts.length > 0}
    <Pagenation bind:page totalCount={displayableMyPrompts.length} {displayLimit} />
    <div class="my-prompt-views">
      {#each displayMyPrompts as myPrompt}
        <MyPromptItem {myPrompt} selectable={deleteMode} on:change={onMyPromptSelect} />
      {/each}
    </div>
    <Pagenation bind:page totalCount={displayableMyPrompts.length} {displayLimit} />
  {:else if $myPrompts.length === 0}
    <div class="empty-my-prompts">
      {emptyMyPrompt.translate()}
    </div>
  {/if}
</div>

<style lang="postcss">
  .my-prompt {
    @apply hidden w-full flex-col gap-[--layout-gap];
  }

  .my-prompt.active {
    @apply flex;
  }

  .tools {
    @apply flex flex-wrap gap-1.5;
  }

  .empty-my-prompts {
    @apply text-2xl;
  }

  .my-prompt-views {
    @apply flex flex-wrap gap-[1.5rem];
  }
</style>


================================================
FILE: client-src/components/better-prompt/my-prompt/MyPromptItem.svelte
================================================
<script lang="ts">
  import type { MyPrompt } from "@/libs/my-prompt";
  import { createEventDispatcher } from "svelte";
  import { parsePrompt, type Prompt } from "@/libs/prompt";
  import { textualInversion } from "#/better-prompt/_logic/extraNetworks";
  import { myPromptTagsEmpty } from "#/better-prompt/_logic/messages";
  import { createDataset, getTextContent } from "#/better-prompt/_logic/prompt";
  import { adjustPrompt } from "#/better-prompt/_logic/adjustPrompt";

  export let myPrompt: MyPrompt;
  export let selectable = false;

  let prompts: Prompt[];
  $: prompts = (parsePrompt(myPrompt.prompt) || []).map((prompt) =>
    adjustPrompt(prompt, $textualInversion)
  );

  let selected = false;
  const dispatch = createEventDispatcher();

  function onClick(): void {
    if (!selectable) return;
    selected = !selected;
    dispatch("change", { selected, myPrompt });
  }

  let cache = myPrompt;
  $: if (!selectable || cache !== myPrompt) {
    selected = false;
    dispatch("change", { selected: false, myPrompt: cache });
  }
</script>

<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="my-prompt-item" class:selectable class:selected on:click={onClick}>
  <div class="label">{myPrompt.label}</div>
  <div class="tags">
    <span class="tags-icon">🔖</span>
    {#if myPrompt.tags.length > 0}
      {#each myPrompt.tags as tag}
        <div class="tag">{tag}</div>
      {/each}
    {:else}
      <div class="tag empty">
        {myPromptTagsEmpty.translate()}
      </div>
    {/if}
  </div>
  <div class="prompt">
    {#each prompts as prompt}
      <div {...createDataset(prompt)} class="prompt-item">
        {getTextContent(prompt)}
      </div>
    {/each}
  </div>
</div>

<style lang="postcss">
  .my-prompt-item {
    @apply min-h-[144px] w-full min-w-[256px] max-w-[256px] transform-none rounded-[--container-radius] border-2 border-solid border-[--input-border-color] p-[--block-padding] shadow-none transition-[box-shadow,transform] duration-200 ease-in-out;
  }

  .my-prompt-item.selectable {
    @apply cursor-pointer select-none shadow-md shadow-[rgba(0,0,0,0.33)] [transform:translate(0,-2px)_scale(1.01)];
  }

  :global(.dark) .my-prompt-item.selectable {
    @apply shadow-[rgba(0,0,0,0.66)];
  }

  .my-prompt-item.selectable:not(.selected):hover {
    @apply [box-shadow:0_0_8px_var(--secondary-600)];
  }

  .my-prompt-item.selected {
    @apply outline outline-[3px] outline-offset-2 outline-[--color-accent];
  }

  .label {
    @apply text-2xl font-bold;
  }

  .tags {
    @apply mt-1 flex select-none flex-wrap items-center gap-1.5;
  }

  .tags-icon {
    @apply text-xl;
  }

  .tag {
    @apply flex items-center rounded-[--button-small-radius] border-[length:--checkbox-label-border-width] border-solid border-[--checkbox-label-border-color] px-2 py-0.5 text-[--checkbox-label-text-color] [background:--checkbox-label-background-fill];
  }

  .tag.empty {
    @apply opacity-75 [background:none];
  }

  .prompt {
    @apply mt-2 flex flex-wrap gap-1.5;
  }
</style>


================================================
FILE: client-src/components/better-prompt/my-prompt/SelectAndDeleteMyPrompt.svelte
================================================
<script lang="ts">
  import type { MyPrompt } from "@/libs/my-prompt";
  import { removeMyPrompts } from "#/better-prompt/_logic/myPrompts";
  import {
    cancelMyPromptDeletion,
    deleteSelectedMyPrompt,
    selectAndDeleteMyPrompt,
  } from "#/better-prompt/_logic/messages";

  export let deleteMode: boolean;
  export let selectedMyPrompts: MyPrompt[];

  function deleteMyPrompts(): void {
    if (!deleteMode) return;
    if (selectedMyPrompts.length === 0) return;

    removeMyPrompts(selectedMyPrompts);
    deleteMode = false;
    selectedMyPrompts = [];
  }
</script>

<button class="button secondary lg" on:click={() => (deleteMode = !deleteMode)}>
  {#if deleteMode}
    {cancelMyPromptDeletion.translate()}
  {:else}
    {selectAndDeleteMyPrompt.translate()}
  {/if}
</button>

{#if deleteMode}
  <button
    class="button primary lg"
    on:click={deleteMyPrompts}
    disabled={selectedMyPrompts.length === 0}
  >
    {deleteSelectedMyPrompt.translate()}
  </button>
{/if}


================================================
FILE: client-src/components/better-prompt/prompt-edit/PromptEdit.svelte
================================================
<script lang="ts">
  import Editor from "#/better-prompt/prompt-edit/editor/Editor.svelte";
  import Input from "#/better-prompt/prompt-edit/input/Input.svelte";

  export let active: boolean;
</script>

<div class="prompt-edit" class:active>
  <div class="editor-container">
    <Editor />
    <Editor negative={true} />
  </div>
  <div class="input-container">
    <Input />
  </div>
</div>

<style lang="postcss">
  .prompt-edit {
    @apply hidden w-full flex-wrap gap-[--layout-gap];
  }

  .prompt-edit.active {
    @apply flex;
  }

  .editor-container {
    @apply flex min-w-[min(640px,100%)] grow-[6] basis-0 flex-col gap-[--layout-gap];
  }

  .input-container {
    @apply flex w-min min-w-[min(640px,100%)] grow;
  }
</style>


================================================
FILE: client-src/components/better-prompt/prompt-edit/_logic/purgeEmphasizedPrompt.ts
================================================
import type {
  EmphasizedNegativePrompt,
  EmphasizedPositivePrompt,
  InnerPrompt,
} from "@/libs/prompt";

export function purgeEmphasizedPrompt(
  prompt: EmphasizedPositivePrompt | EmphasizedNegativePrompt
): InnerPrompt[] {
  if (prompt.values.length === 1) {
    switch (prompt.values[0].type) {
      case "emphasized-positive":
      case "emphasized-negative":
        return purgeEmphasizedPrompt(prompt.values[0]);
    }
  }
  return prompt.values;
}


================================================
FILE: client-src/components/better-prompt/prompt-edit/_logic/undoRedo.ts
================================================
import type { Prompt } from "@/libs/prompt";
import type { Writable } from "svelte/store";
import { isEquals } from "@/libs/prompt";

class History<T> {
  private readonly history: T[] = [];
  private index = -1;

  constructor(private readonly maxLength: number) {}

  get current(): Nullable<T> {
    if (this.index < 0 || this.index >= this.history.length) return null;
    return this.history[this.index];
  }

  get canUndo(): boolean {
    return this.index > 0;
  }

  get canRedo(): boolean {
    return this.index < this.history.length - 1;
  }

  undo(): Nullable<T> {
    if (!this.canUndo) return null;
    this.index--;
    return this.current;
  }

  redo(): Nullable<T> {
    if (!this.canRedo) return null;
    this.index++;
    return this.current;
  }

  push(value: T): void {
    if (this.index === this.maxLength - 1) {
      this.history.shift();
    } else {
      this.history.splice(this.index + 1, this.history.length - this.index - 1);
      this.index++;
    }

    this.history.push(value);
  }
}

type PromptHistory = {
  type: "positive" | "negative";
  values: Prompt[];
};

const promptHistory = new History<PromptHistory>(40);

let positive: Nullable<Writable<Prompt[]>> = null;
let negative: Nullable<Writable<Prompt[]>> = null;
let skipUpdate = false;

export function initUndoRedo(args: {
  positive: Writable<Prompt[]>;
  negative: Writable<Prompt[]>;
}): void {
  ({ positive, negative } = args);

  let cachedPositive: Nullable<Prompt[]> = null;
  positive.subscribe((prompts) => {
    if (skipUpdate) return;
    if (cachedPositive == null) {
      cachedPositive = [];
      promptHistory.push({ type: "positive", values: [] });
    }

    if (isEquals(cachedPositive, prompts)) return;
    cachedPositive = prompts;

    promptHistory.push({ type: "positive", values: [...prompts] });
  });

  let cachedNegative: Nullable<Prompt[]> = null;
  negative.subscribe((prompts) => {
    if (skipUpdate) return;
    if (cachedNegative == null) {
      cachedNegative = [];
      promptHistory.push({ type: "negative", values: [] });
    }

    if (isEquals(cachedNegative, prompts)) return;
    cachedNegative = prompts;

    promptHistory.push({ type: "negative", values: prompts });
  });
}

export function undoRedo(event: KeyboardEvent): void {
  if (positive == null || negative == null) return;

  const { target, ctrlKey, metaKey, key } = event;
  if (target instanceof HTMLInputElement) {
    if (target.type === "text") return;
    if (target.type === "number") return;
  }
  if (target instanceof HTMLTextAreaElement) return;

  if (!ctrlKey && !metaKey) return;
  if (key !== "z" && key !== "y") return;

  event.preventDefault();

  const undo = event.key === "z";
  const history = undo ? promptHistory.undo() : promptHistory.redo();
  if (history == null) return;

  skipUpdate = true;
  (history.type === "positive" ? positive : negative).set(history.values);
  skipUpdate = false;
}


================================================
FILE: client-src/components/better-prompt/prompt-edit/editor/Editor.svelte
================================================
<script lang="ts">
  import { getContext, onMount, setContext } from "svelte";
  import { isEquals, parsePrompt, toString } from "@/libs/prompt";
  import { getElement } from "@/libs/util/dom";
  import { dispatchEvent } from "@/libs/util/webui";
  import { type BetterPromptContext, betterPromptContextKey } from "#/better-prompt/_logic/context";
  import { adjustPrompt } from "#/better-prompt/_logic/adjustPrompt";
  import { textualInversion } from "#/better-prompt/_logic/extraNetworks";
  import { editorNegativePrompt, editorPrompt } from "#/better-prompt/_logic/messages";
  import { type EditorContext, editorContextKey } from "./_logic/context";
  import TokenCounter from "./TokenCounter.svelte";
  import PromptList from "./prompt-list/PromptList.svelte";

  export let negative = false;

  const caption = negative ? editorNegativePrompt : editorPrompt;

  const {
    tabName,
    prompts: { positive: positivePrompts, negative: negativePrompts },
  } = getContext<BetterPromptContext>(betterPromptContextKey);
  const prompts = negative ? negativePrompts : positivePrompts;

  setContext<EditorContext>(editorContextKey, { negative, prompts });

  let observeId = -1;
  let updateId = -1;
  let cachedPrompt = "";
  let skipUpdate = false;

  const selector = negative ? `#${tabName}_neg_prompt textarea` : `#${tabName}_prompt textarea`;

  const update = () => {
    const textarea = getElement(selector);
    if (!(textarea instanceof HTMLTextAreaElement)) {
      updateId = -1;
      return;
    }

    const prompt = textarea.value.trim();
    if (prompt === cachedPrompt) {
      updateId = -1;
      return;
    }
    cachedPrompt = prompt;

    parsePrompt(prompt, (_prompts) => {
      _prompts = _prompts.map((prompt) => adjustPrompt(prompt, $textualInversion));
      if (isEquals($prompts, _prompts)) return;

      skipUpdate = true;
      prompts.set(_prompts);
      skipUpdate = false;
    });
    updateId = -1;
  };

  const triggerUpdate = () => {
    if (updateId !== -1) {
      clearTimeout(updateId);
    }
    updateId = window.setTimeout(update, 750);
  };

  const observe = () => {
    if (updateId === -1) {
      update();
    }
    observeId = window.setTimeout(observe, 1000);
  };

  prompts.subscribe((value) => {
    if (skipUpdate) return;

    const textarea = getElement(selector);
    if (!(textarea instanceof HTMLTextAreaElement)) return;

    textarea.value = cachedPrompt = toString(value);
    dispatchEvent(textarea, "input");
  });

  onMount(() => {
    observeId = window.setTimeout(observe, 0);

    const textarea = getElement(selector);
    if (textarea instanceof HTMLTextAreaElement) {
      textarea.addEventListener("input", triggerUpdate);
    }

    return () => {
      clearTimeout(observeId);
      if (textarea instanceof HTMLTextAreaElement) {
        textarea.removeEventListener("input", triggerUpdate);
      }
    };
  });
</script>

<div class="editor">
  <span>{caption.translate()}</span>
  <div class="content">
    <TokenCounter />
    <PromptList />
  </div>
</div>

<style lang="postcss">
  .editor {
    @apply flex flex-col;
  }

  .content {
    @apply relative;
  }
</style>


================================================
FILE: client-src/components/better-prompt/prompt-edit/editor/TokenCounter.svelte
================================================
<script lang="ts">
  import { getContext } from "svelte";
  import { readable } from "svelte/store";
  import { getElement, hasElement } from "@/libs/util/dom";
  import { type BetterPromptContext, betterPromptContextKey } from "#/better-prompt/_logic/context";
  import { type EditorContext, editorContextKey } from "./_logic/context";

  let error = false;

  const { tabName } = getContext<BetterPromptContext>(betterPromptContextKey);
  const { negative } = getContext<EditorContext>(editorContextKey);

  const tokenCounterId = `${tabName}${negative ? "_negative" : ""}_token_counter`;
  const errorSelector = `#${tokenCounterId}.error`;
  const countSelector = `#${tokenCounterId} > span`;

  const tokenCount = readable("0/75", (set) => {
    let current = "0/75";
    const interval = setInterval(() => {
      const element = getElement(countSelector);
      if (element == null) return;
      error = hasElement(errorSelector);
      const count = element.textContent || "0/75";
      if (count === current) return;
      current = count;
      set(count);
    }, 250);

    return () => {
      clearInterval(interval);
    };
  });
</script>

<div class="token-counter block" class:error>
  <span class="value">{$tokenCount}</span>
</div>


================================================
FILE: client-src/components/better-prompt/prompt-edit/editor/_logic/context.ts
================================================
import type { Writable } from "svelte/store";
import type { Prompt } from "@/libs/prompt";

export type EditorContext = {
  negative: boolean;
  prompts: Writable<Prompt[]>;
};

export const editorContextKey = Symbol();


================================================
FILE: client-src/components/better-prompt/prompt-edit/editor/prompt-list/ListItem.svelte
================================================
<script lang="ts">
  import type { Prompt } from "@/libs/prompt";
  import { createDataset, getTextContent } from "#/better-prompt/_logic/prompt";
  import { isPopupEnabled } from "./_logic/listItem";
  import Popup from "#/widgets/Popup.svelte";
  import WeightInput from "#/better-prompt/prompt-edit/editor/prompt-list/WeightInput.svelte";

  export let prompt: Prompt;

  let ref: HTMLElement;

  let showPopup = false;

  function onContextmenu(event: MouseEvent): void {
    if (event.ctrlKey) return;
    if (!isPopupEnabled(prompt)) return;
    event.preventDefault();
    showPopup = !showPopup;
  }
</script>

<button
  {...createDataset(prompt)}
  bind:this={ref}
  class="prompt-item"
  on:click
  on:contextmenu={onContextmenu}
>
  {getTextContent(prompt)}
</button>

{#if ref != null}
  <Popup parent={ref} bind:show={showPopup}>
    <WeightInput bind:prompt />
  </Popup>
{/if}

<style lang="postcss">
  .prompt-item {
    @apply cursor-grab;
  }
</style>


================================================
FILE: client-src/components/better-prompt/prompt-edit/editor/prompt-list/PromptList.svelte
================================================
<script lang="ts">
  import Sortable from "sortablejs";
  import { getContext, onMount } from "svelte";
  import { sortByIndexes } from "@/libs/util/array";
  import { getElementAll } from "@/libs/util/dom";
  import {
    type EditorContext,
    editorContextKey,
  } from "#/better-prompt/prompt-edit/editor/_logic/context";
  import { promptIdentifier } from "./_logic/promptList";
  import ListItem from "./ListItem.svelte";

  const { prompts } = getContext<EditorContext>(editorContextKey);

  const onItemClick =
    (index: number) =>
    (event: MouseEvent): void => {
      if (!event.shiftKey || event.button !== 0) return;
      event.preventDefault();
      prompts.update((values) => values.filter((_, i) => i !== index));
    };

  let ref: Nullable<HTMLElement> = null;

  onMount(() => {
    if (ref == null) return;
    const sortable = new Sortable(ref, {
      animation: 150,
      swapThreshold: 1.1,
      ghostClass: "ghost",
      onSort: () => {
        if (ref == null) return;
        const indexes = getElementAll(ref, "li").map((element) => Number(element.dataset.index));
        prompts.set(sortByIndexes($prompts, indexes));
      },
    });
    return () => sortable.destroy();
  });
</script>

<ul bind:this={ref} class="prompt-list">
  {#each $prompts as prompt, i (promptIdentifier(i, prompt))}
    <li data-index={i}>
      <ListItem bind:prompt on:click={onItemClick(i)} />
    </li>
  {/each}
</ul>

<style lang="postcss">
  .prompt-list {
    @apply flex min-h-[84px] w-full list-none flex-wrap gap-2 rounded-[--input-radius] border-[length:--input-border-width] border-solid border-[--input-border-color] p-[--input-padding] text-[length:--input-text-size] font-[--input-text-weight] leading-[--line-sm] text-[--body-text-color] outline-none [background:--input-background-fill] [box-shadow:--input-shadow];
  }

  li:global(.ghost) {
    @apply opacity-50;
  }
</style>


================================================
FILE: client-src/components/better-prompt/prompt-edit/editor/prompt-list/WeightInput.svelte
================================================
<script lang="ts">
  import type { Prompt } from "@/libs/prompt";
  import { weight as weightLabel } from "#/better-prompt/_logic/messages";
  import { getWeight, updateWeight } from "./_logic/weightInput";
  import NumberInput from "#/widgets/NumberInput.svelte";

  export let prompt: Prompt;

  let weight: number;
  $: weight = getWeight(prompt);

  function onWeightUpdate(event: InputEvent): void {
    if (!(event.target instanceof HTMLInputElement)) return;
    prompt = updateWeight(prompt, event.target.valueAsNumber);
  }
</script>

{#if !isNaN(weight)}
  <NumberInput
    label={weightLabel.translate()}
    value={weight}
    options={{ min: -2, max: 2, input: { step: 0.01 }, slider: { step: 0.05 } }}
    on:input={onWeightUpdate}
  />
{/if}


================================================
FILE: client-src/components/better-prompt/prompt-edit/editor/prompt-list/_logic/listItem.ts
================================================
import { type Prompt } from "@/libs/prompt";

export function isPopupEnabled(prompt: Prompt): boolean {
  switch (prompt.type) {
    case "plain":
    case "emphasized-positive":
    case "emphasized-negative":
    case "emphasized-weighted":
      return true;
    case "extra-networks":
      return prompt.name === "lora";
  }
  return false;
}


================================================
FILE: client-src/components/better-prompt/prompt-edit/editor/prompt-list/_logic/promptList.ts
================================================
import type { Prompt, ScheduledPrompt } from "@/libs/prompt";
import { toString } from "@/libs/prompt";
import { purgeEmphasizedPrompt } from "#/better-prompt/prompt-edit/_logic/purgeEmphasizedPrompt";

export function promptIdentifier(index: number, prompt: Prompt): string {
  switch (prompt.type) {
    case "alternate":
    case "combination":
    case "plain":
      return `${index}:${toString(prompt)}`;
    case "extra-networks":
      if (prompt.name !== "lora") return `${index}:${prompt.name}`;
      return `${index}:${prompt.name}:${prompt.args[0]}`;
    case "emphasized-positive":
    case "emphasized-negative":
      return `${index}:${toString(purgeEmphasizedPrompt(prompt))}`;
    case "emphasized-weighted":
      return `${index}:${toString(prompt.values)}`;
    case "scheduled":
      return scheduledIdentifier(index, prompt);
  }
}

function scheduledIdentifier(index: number, prompt: ScheduledPrompt): string {
  let buffer = "";
  if (prompt.from != null) {
    buffer += `:${toString(prompt.from)}`;
  }
  if (prompt.to != null) {
    buffer += `:${toString(prompt.to)}`;
  }
  return `${index}${buffer}`;
}


================================================
FILE: client-src/components/better-prompt/prompt-edit/editor/prompt-list/_logic/weightInput.ts
================================================
import type { EmphasizedNegativePrompt, EmphasizedPositivePrompt, Prompt } from "@/libs/prompt";
import Big from "big.js";
import { toString } from "@/libs/prompt";
import { purgeEmphasizedPrompt } from "#/better-prompt/prompt-edit/_logic/purgeEmphasizedPrompt";

export function getWeight(prompt: Prompt): number {
  switch (prompt.type) {
    case "plain":
      return 1;
    case "emphasized-positive":
      return getPositiveWeight(prompt, 1);
    case "emphasized-negative":
      return getNegativeWeight(prompt, 1);
    case "emphasized-weighted":
      return prompt.weight;
    case "extra-networks":
      if (prompt.name !== "lora") break;
      return Number(prompt.args[1]);
  }
  return NaN;
}

function getPositiveWeight(prompt: EmphasizedPositivePrompt, weight: number): number {
  const value = new Big(weight).mul(1.1).round(2, 1).toNumber();
  if (prompt.values.length !== 1) return value;
  const child = prompt.values[0];
  if (child.type !== "emphasized-positive") return value;
  return getPositiveWeight(child, value);
}

function getNegativeWeight(prompt: EmphasizedNegativePrompt, weight: number): number {
  const value = new Big(weight).div(1.1).round(2, 1).toNumber();
  if (prompt.values.length !== 1) return value;
  const child = prompt.values[0];
  if (child.type !== "emphasized-negative") return value;
  return getNegativeWeight(child, value);
}

export function updateWeight(prompt: Prompt, weight: number): Prompt {
  switch (prompt.type) {
    case "plain":
      if (weight === 1) break;
      return { type: "emphasized-weighted", weight, values: [prompt] };
    case "emphasized-positive":
    case "emphasized-negative":
      if (weight === 1) return { type: "plain", value: toString(purgeEmphasizedPrompt(prompt)) };
      return { type: "emphasized-weighted", weight, values: purgeEmphasizedPrompt(prompt) };
    case "emphasized-weighted":
      if (weight === 1) return { type: "plain", value: toString(prompt.values) };
      prompt.weight = weight;
      break;
    case "extra-networks":
      if (prompt.name !== "lora") break;
      prompt.args[1] = weight.toString();
      break;
  }
  return prompt;
}


================================================
FILE: client-src/components/better-prompt/prompt-edit/input/Input.svelte
================================================
<script lang="ts">
  import { setContext } from "svelte";
  import { writable } from "svelte/store";
  import { type PromptInputContext, promptInputContextKey } from "./_logic/context";
  import PromptInput from "./PromptInput.svelte";
  import Suggest from "./suggest/Suggest.svelte";

  let promptText = "";

  setContext<PromptInputContext>(promptInputContextKey, {
    pseudoFocus: writable(-1),
    currentFocus: writable(null),
    movePseudoFocus: writable(null),
  });
</script>

<div class="input">
  <PromptInput bind:promptText />
  <Suggest {promptText} />
</div>

<style lang="postcss">
  .input {
    @apply flex flex-grow flex-col gap-2;
  }
</style>


================================================
FILE: client-src/components/better-prompt/prompt-edit/input/PromptInput.svelte
================================================
<script lang="ts">
  import { getContext } from "svelte";
  import { parsePrompt } from "@/libs/prompt";
  import { type BetterPromptContext, betterPromptContextKey } from "#/better-prompt/_logic/context";
  import { adjustPrompt } from "#/better-prompt/_logic/adjustPrompt";
  import { textualInversion } from "#/better-prompt/_logic/extraNetworks";
  import { inputPrompt } from "#/better-prompt/_logic/messages";
  import {
    type PromptInputContext,
    type PseudoFocusMoveDirection,
    promptInputContextKey,
  } from "./_logic/context";
  import TextInput from "#/widgets/TextInput.svelte";

  export let promptText: string;

  const {
    prompts: { positive, negative },
  } = getContext<BetterPromptContext>(betterPromptContextKey);
  const { pseudoFocus, movePseudoFocus, currentFocus } =
    getContext<PromptInputContext>(promptInputContextKey);

  function setSelection({ target }: FocusEvent): void {
    if (!(target instanceof HTMLInputElement)) return;
    target.selectionStart = 0;
    target.selectionEnd = target.value.length;
  }

  function clearPseudoFocus(): void {
    pseudoFocus.set(-1);
    currentFocus.set(null);
  }

  function confirmPrompt(event: KeyboardEvent): void {
    const { target } = event;
    if (!(target instanceof HTMLInputElement)) return;
    const focus = $currentFocus;
    if (focus == null) {
      parsePrompt(target.value, (prompts) => {
        prompts = prompts.map((prompt) => adjustPrompt(prompt, $textualInversion));
        if (event.shiftKey) {
          negative.update((current) => current.concat(prompts));
        } else {
          positive.update((current) => current.concat(prompts));
        }
      });
    } else {
      focus.dispatchEvent(new MouseEvent("click", { button: 0, shiftKey: event.shiftKey }));
    }
    promptText = "";
  }

  function onKeyDown(event: KeyboardEvent): void {
    const prevent = () => {
      event.preventDefault();
      event.stopPropagation();
    };
    const move = (direction: PseudoFocusMoveDirection) => {
      if ($pseudoFocus === -1) return;
      prevent();
      movePseudoFocus.set(direction);
    };
    switch (event.key) {
      case "Enter":
        prevent();
        confirmPrompt(event);
        clearPseudoFocus();
        break;
      case "Escape":
        prevent();
        clearPseudoFocus();
        break;
      case "Tab":
        prevent();
        movePseudoFocus.set(event.shiftKey ? "previous" : "next");
        break;
      case "ArrowDown":
        move("down");
        break;
      case "ArrowUp":
        move("up");
        break;
      case "ArrowRight":
        move("right");
        break;
      case "ArrowLeft":
        move("left");
        break;
    }
  }
</script>

<TextInput
  bind:value={promptText}
  on:focusin={setSelection}
  on:focusout={clearPseudoFocus}
  on:input={clearPseudoFocus}
  on:keydown={onKeyDown}
  options={{ placeholder: inputPrompt.translate() }}
/>


================================================
FILE: client-src/components/better-prompt/prompt-edit/input/_logic/context.ts
================================================
import type { Writable } from "svelte/store";

export type PseudoFocusMoveDirection = "next" | "previous" | "up" | "down" | "right" | "left";

export type PromptInputContext = {
  pseudoFocus: Writable<number>;
  currentFocus: Writable<Nullable<HTMLElement>>;
  movePseudoFocus: Writable<Nullable<PseudoFocusMoveDirection>>;
};

export const promptInputContextKey = Symbol();


================================================
FILE: client-src/components/better-prompt/prompt-edit/input/suggest/DanbooruItem.svelte
================================================
<script lang="ts">
  import type { DanbooruTag } from "@/libs/danbooru";
  import { getContext } from "svelte";
  import { tagToPrompt } from "@/libs/danbooru";
  import { type BetterPromptContext, betterPromptContextKey } from "#/better-prompt/_logic/context";
  import {
    type PromptInputContext,
    promptInputContextKey,
  } from "#/better-prompt/prompt-edit/input/_logic/context";

  export let data: DanbooruTag;
  export let index: number;

  let ref: Nullable<HTMLButtonElement> = null;

  const {
    prompts: { positive, negative },
  } = getContext<BetterPromptContext>(betterPromptContextKey);
  const { pseudoFocus, currentFocus } = getContext<PromptInputContext>(promptInputContextKey);

  pseudoFocus.subscribe((focus) => {
    if (focus !== index) return;
    if (ref === null) return;
    currentFocus.set(ref);
  });

  function onClick(event: MouseEvent): void {
    if (!(event.target instanceof HTMLButtonElement)) return;
    if (event.button !== 0) return;
    (event.shiftKey ? negative : positive).update((prompts) => {
      prompts.push({
        type: "plain",
        value: tagToPrompt(data.name),
      });
      return prompts;
    });
  }

  const toLabel = (tag: DanbooruTag) => {
    return `${tag.name.replaceAll("_", " ")}`;
  };

  const toCount = (tag: DanbooruTag) => {
    let count = tag.post_count;
    if (count >= 1000000) {
      count /= 1000000;
      const str = count.toFixed(count < 10.0 ? 1 : 0);
      return `${str}M`;
    } else if (count >= 1000) {
      count /= 1000;
      const str = count.toFixed(count < 10.0 ? 1 : 0);
      return `${str}k`;
    } else {
      return count.toString();
    }
  };
</script>

<button
  bind:this={ref}
  class="prompt-item suggest-item"
  data-category={data.category}
  on:click={onClick}
>
  <span class="label">{toLabel(data)}</span>
  <span class="count">{toCount(data)}</span>
</button>

<style lang="postcss">
  .suggest-item {
    @apply cursor-pointer truncate;
  }

  .suggest-item:focus-visible {
    @apply outline-none;
  }

  .suggest-item {
    @apply flex gap-2 border-[--danbooru-border-color] [background:--danbooru-background];
  }

  .suggest-item > span {
    @apply pointer-events-none;
  }

  .suggest-item[data-category="0"] > .label {
    @apply text-[--danbooru-general-text-color];
  }

  .suggest-item[data-category="1"] > .label {
    @apply text-[--danbooru-artist-text-color];
  }

  .suggest-item[data-category="3"] > .label {
    @apply text-[--danbooru-copyright-text-color];
  }

  .suggest-item[data-category="4"] > .label {
    @apply text-[--danbooru-character-text-color];
  }

  .suggest-item > .count {
    @apply text-[--danbooru-count-text-color];
  }
</style>


================================================
FILE: client-src/components/better-prompt/prompt-edit/input/suggest/ExtraNetworksItem.svelte
================================================
<script lang="ts">
  import type { ExtraNetworksData } from "@/libs/extra-networks";
  import { getContext } from "svelte";
  import { getOption } from "@/libs/util/webui";
  import { type BetterPromptContext, betterPromptContextKey } from "#/better-prompt/_logic/context";
  import { loraNegativePromptError } from "#/better-prompt/_logic/messages";
  import {
    type PromptInputContext,
    promptInputContextKey,
  } from "#/better-prompt/prompt-edit/input/_logic/context";
  import { showToast } from "#/widgets/Toast.svelte";
  import Popup from "#/widgets/Popup.svelte";

  export let data: ExtraNetworksData;
  export let index: number;

  let ref: Nullable<HTMLButtonElement> = null;
  let showPopup = false;

  const {
    prompts: { positive, negative },
  } = getContext<BetterPromptContext>(betterPromptContextKey);
  const { pseudoFocus, currentFocus } = getContext<PromptInputContext>(promptInputContextKey);

  pseudoFocus.subscribe((focus) => {
    if (focus !== index) return;
    if (ref === null) return;
    currentFocus.set(ref);
  });

  function onClick(event: MouseEvent): void {
    const { target, button, shiftKey } = event;
    if (!(target instanceof HTMLButtonElement)) return;

    if (button === 0) {
      showPopup = false;

      const isLora = data.type === "lora";
      if (isLora && shiftKey) {
        showToast({
          type: "warning",
          text: loraNegativePromptError.translate(),
        });
        return;
      }

      (shiftKey ? negative : positive).update((prompts) => {
        prompts.push({
          type: "extra-networks",
          name: data.type,
          args: isLora
            ? [data.name, getOption("extra_networks_default_multiplier", 1).toString()]
            : [data.name],
        });
        return prompts;
      });
    }
  }

  function showLoraMetadata(event: Event): void {
    extraNetworksRequestMetadata(event, "lora", data.name);
  }
</script>

<button
  bind:this={ref}
  class="prompt-item suggest-item"
  data-extra-networks={data.type}
  on:click={onClick}
  on:contextmenu|preventDefault={() => (showPopup = !showPopup)}
>
  {data.name}
</button>

{#if ref != null}
  <Popup parent={ref} bind:show={showPopup}>
    <div class="thumbnail-preview">
      <div
        class="thumbnail-card"
        style:background-image={data.thumbnail || "url(/file=html/card-no-preview.png)"}
      >
        {#if data.type === "lora"}
          <button class="metadata-button" on:click={showLoraMetadata} />
        {/if}
      </div>
    </div>
  </Popup>
{/if}

<style lang="postcss">
  .suggest-item {
    @apply cursor-pointer truncate;
  }

  .suggest-item:focus-visible {
    @apply outline-none;
  }

  .thumbnail-card {
    @apply relative m-[0.5em] inline-block h-[24em] w-[16em] cursor-default overflow-hidden rounded-[0.2rem] bg-[auto_100%] bg-center text-[length:--text-md];
  }

  .metadata-button {
    @apply cursor-pointer border-none bg-transparent;
  }
</style>


================================================
FILE: client-src/components/better-prompt/prompt-edit/input/suggest/Filters.svelte
================================================
<script lang="ts">
  import { type FilterType, FilterTypes, typeToLabel } from "./_logic/filters";
  import Checkbox from "#/widgets/Checkbox.svelte";
  import { toggleValue } from "@/libs/util/array";

  export let filters: FilterType[];

  function onFilterClick(type: FilterType): (event: InputEvent) => void {
    return ({ target }) => {
      if (!(target instanceof HTMLInputElement)) return;

      if (target.checked) {
        if (type === "all") {
          filters = ["all"];
        } else {
          filters = [...filters.filter((filter) => filter !== "all"), type];
        }
      } else {
        filters = toggleValue<FilterType>(filters, type);
      }
    };
  }
</script>

<ul class="suggest-filter">
  {#each FilterTypes as type}
    <li>
      <Checkbox
        label={typeToLabel(type)}
        on:change={onFilterClick(type)}
        value={filters.includes(type)}
      />
    </li>
  {/each}
</ul>

<style lang="postcss">
  .suggest-filter {
    @apply flex list-none flex-wrap gap-x-4 gap-y-2;
  }
</style>


================================================
FILE: client-src/components/better-prompt/prompt-edit/input/suggest/List.svelte
================================================
<script lang="ts">
  import type { FilterType } from "./_logic/filters";
  import type { SuggestData } from "./_logic/suggest";
  import type { ExtraNetworksData } from "@/libs/extra-networks";
  import type { DanbooruTag } from "@/libs/danbooru";
  import Fuse from "fuse.js";
  import { getContext } from "svelte";
  import { writable } from "svelte/store";
  import { getElement, getElementAll, rotateElement } from "@/libs/util/dom";
  import { danbooruTags } from "#/better-prompt/_logic/danbooruTags";
  import { lora, textualInversion } from "#/better-prompt/_logic/extraNetworks";
  import { myPrompts } from "#/better-prompt/_logic/myPrompts";
  import {
    type PromptInputContext,
    promptInputContextKey,
  } from "#/better-prompt/prompt-edit/input/_logic/context";
  import ListItem from "./ListItem.svelte";
  import type { MyPrompt } from "@/libs/my-prompt";

  export let promptText: string;
  export let filters: FilterType[];

  const { pseudoFocus, movePseudoFocus } = getContext<PromptInputContext>(promptInputContextKey);

  let suggestsRef: Nullable<HTMLElement> = null;
  movePseudoFocus.subscribe((direction) => {
    movePseudoFocus.set(null);
    if (direction == null) return;
    if (suggestsRef == null) return;

    if (direction === "next" || direction === "previous") {
      let index = $pseudoFocus + (direction === "next" ? 1 : -1);
      const items = getElementAll(suggestsRef, "li");
      if (index < 0) {
        index = items.length - 1;
      } else if (index >= items.length) {
        index = 0;
      }
      pseudoFocus.set(index);
    } else {
      const selected = getElement(suggestsRef, `li[data-index="${$pseudoFocus}"]`);
      if (selected == null) {
        pseudoFocus.set(0);
        return;
      }
      const rotate = rotateElement(selected, direction);
      pseudoFocus.set(Number(rotate?.dataset.index || "0"));
    }
  });

  const suggests = writable<SuggestData[]>([]);

  let loraFuse: Fuse<ExtraNetworksData>;
  $: loraFuse = new Fuse($lora, {
    useExtendedSearch: true,
    threshold: 0.3,
    keys: ["name", "search_term"],
  });

  let textualInversionFuse: Fuse<ExtraNetworksData>;
  $: textualInversionFuse = new Fuse($textualInversion, {
    useExtendedSearch: true,
    threshold: 0.3,
    keys: ["name", "search_term"],
  });

  let danbooruTagFuse: Fuse<DanbooruTag>;
  $: danbooruTagFuse = new Fuse($danbooruTags, {
    useExtendedSearch: true,
    threshold: 0.3,
    keys: ["name", "category"],
  });

  let myPromptFuse: Fuse<MyPrompt>;
  $: myPromptFuse = new Fuse($myPrompts, {
    useExtendedSearch: true,
    threshold: 0.1,
    keys: ["label", "tags", "prompt"],
  });

  function updateSuggest() {
    if (!promptText || filters.length === 0) {
      suggests.set([]);
      return;
    }

    const results: SuggestData[] = [];
    const allowAllType = filters.includes("all");

    const limit = 20;
    let count = 0;
    const withinLimit = () => count < limit;

    if (withinLimit() && (allowAllType || filters.includes("textual-inversion"))) {
      textualInversionFuse.search(promptText, { limit: limit - count }).forEach(({ item }) => {
        results.push({ type: "extra-networks", value: item });
        count++;
      });
    }
    if (withinLimit() && (allowAllType || filters.includes("lora"))) {
      loraFuse.search(promptText, { limit: limit - count }).forEach(({ item }) => {
        results.push({ type: "extra-networks", value: item });
        count++;
      });
    }

    if (withinLimit()) {
      let tagCategory = "";
      if (allowAllType || filters.includes("danbooru-general")) {
        tagCategory += "'0|";
      }
      if (allowAllType || filters.includes("danbooru-copyright")) {
        tagCategory += "'3|";
      }
      if (allowAllType || filters.includes("danbooru-character")) {
        tagCategory += "'4|";
      }
      if (tagCategory.endsWith("|")) {
        tagCategory = tagCategory.slice(0, -1);
      }

      if (tagCategory) {
        danbooruTagFuse
          .search(
            { $and: [{ category: tagCategory }, { name: promptText }] },
            { limit: limit - count }
          )
          .forEach(({ item }) => {
            results.push({ type: "danbooru", value: item });
            count++;
          });
      }
    }

    if (withinLimit()) {
      myPromptFuse.search(promptText, { limit: limit - count }).forEach(({ item }) => {
        results.push({ type: "my-prompt", value: item });
        count++;
      });
    }

    suggests.set(results);
  }

  let updateId = -1;
  $: if (promptText || filters) {
    if (updateId !== -1) {
      window.clearTimeout(updateId);
    }
    updateId = window.setTimeout(updateSuggest, 500);
  }
</script>

<div class="suggest-list">
  <ul bind:this={suggestsRef} class="suggests">
    {#each $suggests as suggest, i}
      <li class:pseudo-focus={$pseudoFocus === i} data-index={i}>
        <ListItem index={i} data={suggest} />
      </li>
    {/each}
  </ul>
</div>

<style lang="postcss">
  .suggest-list {
    @apply m-0 min-h-[84px] w-full grow rounded-[--block-radius] border-[length:--block-border-width] border-[--block-border-color] leading-[--line-sm] [background:--block-background-fill] [box-shadow:--block-shadow];
  }

  .suggests {
    @apply flex list-none flex-wrap gap-2 p-[--input-padding];
  }

  .pseudo-focus :global(.suggest-item) {
    @apply outline outline-2 outline-offset-2 outline-[--input-background-fill-focus];
  }
</style>


================================================
FILE: client-src/components/better-prompt/prompt-edit/input/suggest/ListItem.svelte
================================================
<script lang="ts">
  import type { SuggestData } from "./_logic/suggest";
  import ExtraNetworksItem from "./ExtraNetworksItem.svelte";
  import DanbooruItem from "./DanbooruItem.svelte";
  import MyPromptItem from "./MyPromptItem.svelte";

  export let data: SuggestData;
  export let index: number;
</script>

{#if data.type === "extra-networks"}
  <ExtraNetworksItem {index} data={data.value} />
{:else if data.type === "danbooru"}
  <DanbooruItem {index} data={data.value} />
{:else if data.type === "my-prompt"}
  <MyPromptItem {index} data={data.value} />
{/if}


================================================
FILE: client-src/components/better-prompt/prompt-edit/input/suggest/MyPromptItem.svelte
================================================
<script lang="ts">
  import type { MyPrompt } from "@/libs/my-prompt";
  import { getContext } from "svelte";
  import { parsePrompt, type Prompt } from "@/libs/prompt";
  import { type BetterPromptContext, betterPromptContextKey } from "#/better-prompt/_logic/context";
  import { createDataset, getTextContent } from "#/better-prompt/_logic/prompt";
  import { adjustPrompt } from "#/better-prompt/_logic/adjustPrompt";
  import { textualInversion } from "#/better-prompt/_logic/extraNetworks";
  import {
    type PromptInputContext,
    promptInputContextKey,
  } from "#/better-prompt/prompt-edit/input/_logic/context";
  import Popup from "#/widgets/Popup.svelte";

  export let data: MyPrompt;
  export let index: number;

  const {
    prompts: { positive, negative },
  } = getContext<BetterPromptContext>(betterPromptContextKey);
  const { pseudoFocus, currentFocus } = getContext<PromptInputContext>(promptInputContextKey);

  let ref: Nullable<HTMLButtonElement> = null;

  let showPopup = false;

  let parsedPrompts: Prompt[];
  $: parsedPrompts = (parsePrompt(data.prompt) || []).map((prompt) =>
    adjustPrompt(prompt, $textualInversion)
  );

  pseudoFocus.subscribe((focus) => {
    if (focus !== index) return;
    if (ref === null) return;
    currentFocus.set(ref);
  });

  function onClick(event: MouseEvent): void {
    const { target, button, shiftKey } = event;
    if (!(target instanceof HTMLButtonElement)) return;

    if (button === 0) {
      showPopup = false;

      (shiftKey ? negative : positive).update((prompts) => {
        return [...prompts, ...parsedPrompts];
      });
    }
  }
</script>

<button
  bind:this={ref}
  class="prompt-item suggest-item"
  on:click={onClick}
  on:contextmenu|preventDefault={() => (showPopup = !showPopup)}
>
  {data.label}
</button>

{#if ref != null}
  <Popup parent={ref} bind:show={showPopup}>
    <div class="my-prompt-view">
      {#each parsedPrompts as prompt}
        <div {...createDataset(prompt)} class="prompt-item">
          {getTextContent(prompt)}
        </div>
      {/each}
    </div>
  </Popup>
{/if}

<style lang="postcss">
  .my-prompt-view {
    @apply flex flex-wrap gap-1.5;
  }
</style>


================================================
FILE: client-src/components/better-prompt/prompt-edit/input/suggest/Suggest.svelte
================================================
<script lang="ts">
  import { type FilterType } from "./_logic/filters";
  import Filters from "./Filters.svelte";
  import List from "./List.svelte";

  export let promptText: string;

  let filters: FilterType[] = ["all"];
</script>

<Filters bind:filters />
<List {filters} {promptText} />


================================================
FILE: client-src/components/better-prompt/prompt-edit/input/suggest/_logic/filters.ts
================================================
import {
  suggestFilterAll,
  suggestFilterDanbooruCharacter,
  suggestFilterDanbooruCopyright,
  suggestFilterDanbooruGeneral,
  suggestFilterLora,
  suggestFilterMyPrompt,
  suggestFilterTextualInversion,
} from "@/components/better-prompt/_logic/messages";

export type FilterType =
  | "all"
  | "textual-inversion"
  | "lora"
  | "danbooru-general"
  | "danbooru-character"
  | "danbooru-copyright"
  | "my-prompt";

export const FilterTypes: readonly FilterType[] = [
  "all",
  "textual-inversion",
  "lora",
  "danbooru-general",
  "danbooru-character",
  "danbooru-copyright",
  "my-prompt",
] as const;

export function typeToLabel(type: FilterType): string {
  switch (type) {
    case "all":
      return suggestFilterAll.translate();
    case "textual-inversion":
      return suggestFilterTextualInversion.translate();
    case "lora":
      return suggestFilterLora.translate();
    case "danbooru-general":
      return suggestFilterDanbooruGeneral.translate();
    case "danbooru-character":
      return suggestFilterDanbooruCharacter.translate();
    case "danbooru-copyright":
      return suggestFilterDanbooruCopyright.translate();
    case "my-prompt":
      return suggestFilterMyPrompt.translate();
  }
}


================================================
FILE: client-src/components/better-prompt/prompt-edit/input/suggest/_logic/suggest.ts
================================================
import type { ExtraNetworksData } from "@/libs/extra-networks";
import type { DanbooruTag } from "@/libs/danbooru";
import type { MyPrompt } from "@/libs/my-prompt";

export type SuggestDataType = "extra-networks" | "danbooru" | "my-prompt";

interface AnySuggestData {
  type: SuggestDataType;
}

export interface ExtraNetworksSuggestData extends AnySuggestData {
  type: "extra-networks";
  value: ExtraNetworksData;
}

export interface DanbooruSuggestData extends AnySuggestData {
  type: "danbooru";
  value: DanbooruTag;
}

export interface MyPromptSuggestData extends AnySuggestData {
  type: "my-prompt";
  value: MyPrompt;
}

export type SuggestData = ExtraNetworksSuggestData | DanbooruSuggestData | MyPromptSuggestData;


================================================
FILE: client-src/components/widgets/Checkbox.svelte
================================================
<script lang="ts">
  export let label = "";
  export let value = false;
</script>

<label class="checkbox">
  <input bind:checked={value} class="input" on:change type="checkbox" />
  <span class="label">{label}</span>
</label>

<style lang="postcss">
  .checkbox {
    @apply flex cursor-pointer select-none items-center text-[length:--checkbox-label-text-size] font-[--checkbox-label-text-weight] leading-[--line-md] text-[--body-text-color];
  }

  .checkbox > .input {
    @apply relative inline-block h-4 w-4 flex-shrink-0 select-none appearance-none rounded-[--checkbox-border-radius] border border-solid border-[--checkbox-border-color] bg-[--checkbox-background-color] bg-origin-border p-0 align-middle leading-[--line-sm] text-[#2563eb] [--ring-color:transparent] [box-shadow:--input-shadow];
  }

  .checkbox > .input:checked {
    @apply border-[--checkbox-border-color-selected] bg-[--checkbox-background-color-selected] bg-[url:--checkbox-check];
  }

  .checkbox > .input:not(:checked):hover {
    @apply border-[--checkbox-border-color-hover] bg-[--checkbox-background-color-hover];
  }

  .checkbox > .input:not(:checked):focus {
    @apply border-[--checkbox-border-color-focus] bg-[--checkbox-background-color-focus];
  }

  .checkbox > .label {
    @apply ml-[--size-2] flex cursor-pointer items-center whitespace-nowrap text-[length:--checkbox-label-text-size] font-[--checkbox-label-text-weight] leading-[--line-md] text-[--body-text-color];
  }
</style>


================================================
FILE: client-src/components/widgets/MultiInput.svelte
================================================
<script lang="ts">
  import { toggleValue } from "@/libs/util/array";
  import Popup from "./Popup.svelte";
  import { getElement, hasChild, scrollIntoViewIfNeeded } from "@/libs/util/dom";

  export let label: Nullable<string> = null;
  export let values: string[];
  export let list: string[] = [];
  export let options: { disabled?: boolean } = {};

  let { disabled } = Object.assign({ disabled: false }, options);

  let ref: Nullable<HTMLElement> = null;
  let popup: Nullable<HTMLElement> = null;

  let inputValue = "";
  let suggests: string[] = [];
  let showSuggests = false;
  let virtualFocus = -1;

  $: {
    if (virtualFocus >= suggests.length) {
      virtualFocus = suggests.length - 1;
    }
  }

  function isSelected(value: string, values: string[]): boolean {
    return values.includes(value);
  }

  function toggleSelect(value: string): () => void {
    return () => (values = toggleValue(values, value));
  }

  function removeValue(value: string): () => void {
    return () => (values = values.filter((_value) => _value !== value));
  }

  function suggestUpdate(): void {
    const input = inputValue.toLowerCase();
    suggests = list.filter((value) => value.toLowerCase().includes(input));
    showSuggests = suggests.length > 0;
  }

  function focusOnInput(): void {
    if (ref == null) return;

    const input = getElement(ref, ".input");
    if (input instanceof HTMLInputElement) {
      input.focus();
    }
  }

  function onFocusIn(): void {
    if (ref == null) return;
    if (showSuggests) return;
    if (!list) return;

    virtualFocus = -1;
    suggestUpdate();
  }

  function onFocusOut({ relatedTarget }: FocusEvent): void {
    if (popup != null && relatedTarget instanceof HTMLElement) {
      if (hasChild(popup, relatedTarget)) return;
    }
    showSuggests = false;
  }

  function onMouseDown(): void {
    if (ref == null) return;
    if (!list) return;

    showSuggests = !showSuggests;
    if (showSuggests) {
      suggestUpdate();
    }
  }

  function onInput(): void {
    if (ref == null) return;
    if (!showSuggests) return;
    if (!list) return;

    suggestUpdate();
  }

  function onKeyDown(event: KeyboardEvent): void {
    const { key, target } = event;

    if (virtualFocus < 0) {
      switch (key) {
        case "Enter":
          if (!(target instanceof HTMLInputElement)) return;
          if (!values.includes(target.value)) {
            values = [...values, target.value];
            target.value = "";
          }
          break;
        case "ArrowDown":
          if (!suggests) break;
          virtualFocus = 0;
          event.preventDefault();
          break;
        case "ArrowUp":
          if (!suggests) break;
          virtualFocus = suggests.length - 1;
          event.preventDefault();
          break;
      }
      return;
    }

    if (!suggests) return;

    switch (key) {
      case "Escape":
        virtualFocus = -1;
        event.preventDefault();
        break;
      case "Enter":
        values = toggleValue(values, suggests[virtualFocus]);
        break;
      case "ArrowDown":
        if (!suggests) break;
        if (++virtualFocus >= suggests.length) {
          virtualFocus = 0;
        }
        focusOnSelectedSuggestItem();
        event.preventDefault();
        break;
      case "ArrowUp":
        if (!suggests) break;
        if (--virtualFocus < 0) {
          virtualFocus = suggests.length - 1;
        }
        focusOnSelectedSuggestItem();
        event.preventDefault();
        break;
    }
  }

  const focusOnSelectedSuggestItem = () => {
    if (popup == null) return;
    if (virtualFocus < 0 && virtualFocus >= suggests.length) return;

    const item = getElement(popup, `.suggest-list > .suggest-item:nth-child(${virtualFocus + 1})`);
    if (item != null && item.parentElement != null) {
      scrollIntoViewIfNeeded(item, item.parentElement);
    }
  };
</script>

<div class="multi-input" bind:this={ref} on:focusin={onFocusIn} on:focusout={onFocusOut}>
  {#if label}
    <span class="label">{label}</span>
  {/if}
  <div class="content" class:disabled>
    {#each values as value}
      <button class="selected-value" on:click={removeValue(value)}>
        <span class="value">{value}</span>
        <span class="remove-icon">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
            <path
              d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
            />
          </svg>
        </span>
      </button>
    {/each}
    <input
      type="text"
      class="input"
      bind:value={inputValue}
      on:mousedown={onMouseDown}
      on:input={onInput}
      on:keydown={onKeyDown}
      {disabled}
    />
  </div>
</div>

{#if ref != null && !disabled}
  <Popup parent={ref} bind:show={showSuggests} options={{ closeOnClickOutside: false }}>
    <ul class="suggest-list" bind:this={popup} on:focusin={focusOnInput}>
      {#each suggests as suggest, i}
        <li class="suggest-item" class:focused={virtualFocus === i}>
          <button class="suggest" on:click={toggleSelect(suggest)}>
            <span class="check-icon" class:checked={isSelected(suggest, values)}>✓</span>
            <span>{suggest}</span>
          </button>
        </li>
      {/each}
    </ul>
  </Popup>
{/if}

<style lang="postcss">
  .content {
    @apply flex flex-wrap gap-2 rounded-[--input-radius] border-[length:--input-border-width] border-solid border-[--input-border-color] p-1.5 text-[length:--input-text-size] leading-[--line-sm] [background:--input-background-fill] [box-shadow:--input-shadow];
  }

  .content.disabled {
    @apply opacity-50;
  }

  .content::after {
    @apply block h-[calc(var(--text-md)_*_var(--line-md))] py-[calc(var(--spacing-md)_+_2px)] [content:""];
  }

  .selected-value {
    @apply flex cursor-pointer items-center rounded-[--button-small-radius] border-[length:--checkbox-label-border-width] border-solid border-[--checkbox-label-border-color] p-[--checkbox-label-padding] text-[length:--checkbox-label-text-size] font-[--checkbox-label-text-weight] leading-[--line-md] text-[--checkbox-label-text-color] [background:--checkbox-label-background-fill] [box-shadow:--checkbox-label-shadow];
  }

  .remove-icon {
    @apply ml-[--size-2] box-border flex h-[18px] w-[18px] items-center justify-center rounded-[--radius-full] border-[length:--checkbox-border-width] border-solid border-[--border-color-primary] fill-[--body-text-color] p-[--size-0-5] [background:--background-fill-primary];
  }

  .remove-icon > svg {
    @apply pointer-events-none;
  }

  .input {
    @apply w-32 grow px-1 text-[length:--input-text-size] leading-[--line-sm] text-[--body-text-color] outline-none [background:--input-background-fill] [border:none];
  }

  .suggest-list {
    @apply m-0 max-h-64 list-none overflow-y-auto p-0;
  }

  .suggest-item {
    --suggest-background: var(--neutral-100);
  }

  :global(.dark) .suggest-item {
    --suggest-background: var(--neutral-800);
  }

  .suggest-item.focused,
  .suggest-item:hover {
    @apply [background:--suggest-background];
  }

  .suggest {
    @apply flex w-full cursor-pointer items-center gap-x-1 pr-6 text-[length:--checkbox-label-text-size] font-[--checkbox-label-text-weight] leading-[--line-md] text-[--checkbox-label-text-color] [border:none] [background:none];
  }

  .check-icon {
    @apply invisible;
  }

  .check-icon.checked {
    @apply visible;
  }
</style>


================================================
FILE: client-src/components/widgets/NumberInput.svelte
================================================
<script lang="ts">
  import Big from "big.js";
  import { dispatchEvent } from "@/libs/util/webui";

  type Options = {
    min?: number;
    max?: number;
    input?: { step?: number };
    slider?: { step?: number };
  };

  export let label: Nullable<string> = null;
  export let value = 0;
  export let options: Options = {};

  let { min, max, input, slider } = Object.assign({ input: {}, slider: {} } as Options, options);

  let sliderRef: Nullable<HTMLInputElement> = null;
  function onWheel(event: WheelEvent): void {
    if (sliderRef == null) return;

    const direction = event.deltaY < 0 ? "up" : "down";
    if (direction === "up" && max != null && value >= max) return;
    if (direction === "down" && min != null && value <= min) return;
    event.preventDefault();

    let val = new Big(value);
    const step = Number(slider?.step) || 1;
    if (direction === "up") {
      val = val.plus(step);
    } else {
      val = val.minus(step);
    }

    sliderRef.valueAsNumber = value = val.toNumber();
    dispatchEvent(sliderRef, "input");
  }
</script>

<div class="number-input" on:wheel={onWheel}>
  <div class="input-row">
    {#if label}
      <span class="label">{label}</span>
    {/if}
    <input
      bind:value
      class="input"
      {max}
      {min}
      on:change
      on:focusin
      on:focusout
      on:input
      on:keydown
      on:keyup
      step={input?.step}
      type="number"
    />
  </div>
  <div class="slider-row">
    <input
      bind:this={sliderRef}
      bind:value
      class="slider"
      {max}
      {min}
      on:input
      step={slider?.step}
      type="range"
    />
  </div>
</div>

<style lang="postcss">
  .number-input {
    @apply flex flex-col gap-2;
  }

  .input-row {
    @apply flex items-center justify-between;
  }

  .label {
    @apply mr-8;
  }

  .input {
    @apply h-[--size-6] w-[6rem] rounded-[--input-radius] border-[length:--input-border-width] border-solid border-[--input-border-color] p-[--input-padding] px-[--size-2] py-0 text-center text-[length:--input-text-size] leading-[--line-sm] text-[--body-text-color] outline-none [box-shadow:--input-shadow] [background:--input-background-fill];
  }

  .input:focus {
    @apply border-[--input-border-color-focus] [box-shadow:--input-shadow-focus];
  }

  .slider {
    @apply m-0 w-full p-0 [accent-color:--slider-color];
  }
</style>


================================================
FILE: client-src/components/widgets/Pagenation.svelte
================================================
<script lang="ts">
  import {
    pagenationEllipsis,
    pagenationNext,
    pagenationPrevious,
  } from "#/better-prompt/_logic/messages";

  export let page: number;
  export let totalCount: number;
  export let displayLimit: number;

  let lastPage: number;
  $: lastPage = Math.ceil(totalCount / displayLimit);

  let pages: number[];
  $: {
    pages = [];
    for (let i = page - 2; i <= page + 2; i++) {
      if (i <= 0 || i > lastPage) continue;
      pages.push(i);
    }
  }
</script>

{#if totalCount > 0}
  <div class="pagenation">
    {#if page > 1}
      <button class="button secondary" on:click={() => (page = page - 1)}>
        {pagenationPrevious.translate()}
      </button>
    {/if}
    {#if page >= 4}
      <button class="button secondary" on:click={() => (page = 1)}>1</button>
      {#if page >= 5}
        <span class="ellipsis">
          {pagenationEllipsis.translate()}
        </span>
      {/if}
    {/if}
    {#each pages as _page}
      <button
        class="button secondary"
        class:active={_page === page}
        on:click={() => (page = _page)}
      >
        {_page}
      </button>
    {/each}
    {#if page < lastPage}
      <button class="button secondary" on:click={() => (page = page + 1)}>
        {pagenationNext.translate()}
      </button>
    {/if}
  </div>
{/if}

<style lang="postcss">
  .pagenation {
    @apply flex select-none flex-wrap gap-2;
  }

  .button {
    --active-background: linear-gradient(to_bottom_right, var(--neutral-50), var(--neutral-100));
    @apply rounded-sm px-[calc(var(--spacing-md)_*_2)] py-[--spacing-md] text-[length:--text-md] font-[--button-large-text-weight];
  }

  :global(.dark) .button {
    --active-background: linear-gradient(to_bottom_right, var(--neutral-900), var(--neutral-950));
  }

  .button.active {
    @apply cursor-default ![background:--active-background];
  }

  .ellipsis {
    @apply px-[calc(var(--spacing-md)_*_2)] py-[--spacing-md] text-[length:--text-md] font-[--button-large-text-weight];
  }
</style>


================================================
FILE: client-src/components/widgets/Popup.svelte
================================================
<script lang="ts">
  import { onDestroy } from "svelte";
  import { getScreenPosition, hasChild } from "@/libs/util/dom";
  import { isDarkMode } from "@/libs/util/webui";

  export let parent: HTMLElement;
  export let show: boolean;
  export let options: Nullable<{
    closeOnClickOutside?: boolean;
  }> = null;

  let { closeOnClickOutside } = Object.assign({ closeOnClickOutside: true }, options || {});

  let ref: Nullable<HTMLElement> = null;
  let left = 0;
  let top = 0;

  const move = () => {
    if (ref == null) return;
    const { parentElement } = ref;
    if (parentElement && parentElement !== document.body) {
      parentElement.removeChild(ref);
      document.body.appendChild(ref);
    }
  };

  const update = () => {
    if (ref == null) return;
    const { left: parentLeft, bottom: parentBottom } = getScreenPosition(parent);
    const maxWidth = document.body.clientWidth;
    const width = ref.clientWidth;
    left = parentLeft + width > maxWidth ? maxWidth - width - 4 : parentLeft;
    top = parentBottom + 4;
  };

  const onMouseDown = (event: MouseEvent) => {
    if (ref == null) return;
    if (!closeOnClickOutside) return;
    const { target } = event;
    if (!(target instanceof HTMLElement)) return;
    if (event.button !== 1 && target !== ref && target !== parent && !hasChild(ref, target)) {
      show = false;
    }
  };

  $: if (ref != null) {
    move();
    update();
  }

  onDestroy(() => {
    if (ref != null) {
      document.body.removeChild(ref);
    }
  });
</script>

<svelte:window on:resize={update} />
<svelte:body on:mousedown={onMouseDown} />

{#if show}
  <div
    class="better-prompt popup"
    class:dark={isDarkMode()}
    style:--left="{left}px"
    style:--top="{top}px"
    bind:this={ref}
  >
    <slot />
  </div>
{/if}

<style lang="postcss">
  .popup {
    @apply absolute left-[--left] top-[--top] z-[1000] rounded border border-solid border-[--block-border-color] p-[--input-padding] text-[length:--body-text-size] font-[--body-text-weight] text-[--body-text-color] shadow-sm shadow-black/50 [background:--body-background-fill];
  }
</style>


================================================
FILE: client-src/components/widgets/PopupWindow.svelte
================================================
<script lang="ts">
  import { onDestroy } from "svelte";
  import { scale } from "svelte/transition";
  import { quintOut } from "svelte/easing";
  import { isDarkMode } from "@/libs/util/webui";

  export let title: string;
  export let show: boolean;

  let ref: Nullable<HTMLElement> = null;
  let left = 0;
  let top = 0;

  let dragging = false;
  let leftOrigin = -1;
  let topOrigin = -1;
  let draggingStartX = -1;
  let draggingStartY = -1;

  const init = () => {
    if (ref == null) return;
    const { parentElement } = ref;
    if (parentElement && parentElement !== document.body) {
      left = (window.innerWidth - ref.clientWidth) / 2 + window.scrollX;
      top = (window.innerHeight - ref.clientHeight) / 2 + window.scrollY;
      parentElement.removeChild(ref);
      document.body.appendChild(ref);
    }
  };

  const onMouseDown = (event: MouseEvent) => {
    if (ref == null) return;
    if (event.button !== 0) return;

    dragging = true;
    leftOrigin = left;
    topOrigin = top;
    draggingStartX = event.clientX;
    draggingStartY = event.clientY;
  };

  const onMouseMove = (event: MouseEvent) => {
    if (ref == null) return;
    if (!dragging) return;

    left = Math.min(
      Math.max(leftOrigin + (event.clientX - draggingStartX), 0),
      document.documentElement.scrollWidth - ref.clientWidth - 2
    );
    top = Math.min(
      Math.max(topOrigin + (event.clientY - draggingStartY), 0),
      document.documentElement.scrollHeight - ref.clientHeight - 2
    );
  };

  const onMouseUp = () => {
    if (ref == null) return;
    dragging = false;
  };

  $: if (ref != null) {
    init();
  }

  onDestroy(() => {
    if (ref != null) {
      document.body.removeChild(ref);
    }
  });
</script>

<svelte:window on:mousemove={onMouseMove} on:mouseup={onMouseUp} />

{#if show}
  <div
    class="better-prompt popup-window"
    class:dark={isDarkMode()}
    style:--left="{left}px"
    style:--top="{top}px"
    bind:this={ref}
    transition:scale={{ duration: 250, opacity: 0, start: 0.25, easing: quintOut }}
  >
    <!-- svelte-ignore a11y-no-static-element-interactions -->
    <div class="title-bar" on:mousedown={onMouseDown}>
      <span class="title">{title}</span>
      <button class="close-button" on:mousedown|stopPropagation on:click={() => (show = false)}>
        ×
      </button>
    </div>
    <div>
      <slot />
    </div>
  </div>
{/if}

<style lang="postcss">
  .popup-window {
    @apply absolute left-[--left] top-[--top] z-[1000] rounded border border-solid border-[--block-border-color] text-[length:--body-text-size] font-[--body-text-weight] text-[--body-text-color] shadow-md shadow-black/50 [background:--body-background-fill];
  }

  .title-bar {
    @apply flex select-none items-center justify-between;
  }

  .title {
    @apply ml-3 opacity-75;
  }

  .close-button {
    @apply w-8 cursor-pointer border-none text-[--body-text-color] [background:none] [font-size:2rem] [line-height:1];
  }
</style>


================================================
FILE: client-src/components/widgets/TextArea.svelte
================================================
<script lang="ts">
  export let label: Nullable<string> = null;
  export let value = "";
  export let options: Nullable<{ placeholder?: string }> = null;

  let { placeholder } = Object.assign({ placeholder: "" }, options || {});
</script>

<div class="text-area">
  {#if label}
    <span class="label">{label}</span>
  {/if}
  <textarea
    bind:value
    class="input"
    rows="3"
    on:change
    on:focusin
    on:focusout
    on:input
    on:keydown
    on:keyup
    {placeholder}
  />
</div>

<style lang="postcss">
  .text-area {
    @apply flex flex-col;
  }

  .input {
    @apply h-[86px] grow overflow-y-scroll rounded-[--input-radius] border-[length:--input-border-width] border-solid border-[--input-border-color] p-[--input-padding] text-[length:--input-text-size] leading-[--line-sm] text-[--body-text-color] outline-none [background:--input-background-fill] [box-shadow:--input-shadow] [-ms-overflow-style:none] [scrollbar-width:none];
  }

  .input::-webkit-scrollbar {
    @apply hidden;
  }

  .input:focus {
    @apply border-[--input-border-color-focus] [box-shadow:--input-shadow-focus];
  }
</style>


================================================
FILE: client-src/components/widgets/TextInput.svelte
================================================
<script lang="ts">
  export let label: Nullable<string> = null;
  export let value = "";
  export let options: Nullable<{ placeholder?: string }> = null;

  let { placeholder } = Object.assign({ placeholder: "" }, options || {});
</script>

<div class="text-input">
  {#if label}
    <span class="label">{label}</span>
  {/if}
  <input
    bind:value
    class="input"
    on:change
    on:focusin
    on:focusout
    on:input
    on:keydown
    on:keyup
    {placeholder}
    type="text"
  />
</div>

<style lang="postcss">
  .text-input {
    @apply flex flex-col;
  }

  .text-input > .input {
    @apply grow rounded-[--input-radius] border-[length:--input-border-width] border-solid border-[--input-border-color] p-[--input-padding] text-[length:--input-text-size] leading-[--line-sm] text-[--body-text-color] outline-none [background:--input-background-fill] [box-shadow:--input-shadow];
  }

  .text-input > .input:focus {
    @apply border-[--input-border-color-focus] [box-shadow:--input-shadow-focus];
  }
</style>


================================================
FILE: client-src/components/widgets/Toast.svelte
================================================
<script context="module" lang="ts">
  import type { ToastMessage } from "#/widgets/Toast.svelte";
  import { writable } from "svelte/store";

  const messages = writable<ToastMessage[]>([]);

  export function showToast(message: ToastMessage): void {
    messages.update((values) => {
      if (values.some((_message) => _message.text === message.text)) return values;
      return [...values, Object.assign({ type: "info", duration: 3000 }, message)];
    });
  }

  export function closeToast(message: ToastMessage): void {
    messages.update((value) => value.filter((_message) => _message !== message));
  }
</script>

<script lang="ts">
  import { fade, fly } from "svelte/transition";
  import { isDarkMode } from "@/libs/util/webui";

  function setCloseTimer(message: ToastMessage): void {
    if ((message?.duration || 0) <= 0) return;
    window.setTimeout(() => closeToast(message), message.duration);
  }
</script>

<div class="toast-area" id="better-prompt-toast">
  {#each $messages as message}
    <button
      class="message {message.type}"
      class:dark={isDarkMode()}
      in:fly={{ y: 128, duration: 250 }}
      out:fade
      on:introend={() => setCloseTimer(message)}
      on:click={() => closeToast(message)}
    >
      {message.text}
    </button>
  {/each}
</div>

<style lang="postcss">
  .toast-area {
    @apply fixed inset-x-0 bottom-4 z-[1000] m-auto flex w-fit flex-col gap-y-2;
  }

  .message {
    @apply cursor-pointer rounded border-none px-4 py-2 text-xl font-bold text-white;
  }

  .message.info {
    @apply bg-[#323bfa];
  }

  .message.success {
    @apply bg-[#1ced41];
  }

  .message.warning {
    @apply bg-[#f97316];
  }

  .message.error {
    @apply bg-[#e44939];
  }
</style>


================================================
FILE: client-src/libs/api/getDanbooruTags.ts
================================================
import { type DanbooruTag, isDanbooruTag } from "@/libs/danbooru";

export function getDanbooruTags(): Promise<DanbooruTag[]> {
  const promise = fetch(`/better-prompt-api/v1/get-danbooru-tags?ts=${new Date().getTime()}`);
  return promise.then((response) => response.json()).then(parseDanbooruTags);
}

function parseDanbooruTags(json: unknown): DanbooruTag[] {
  return !Array.isArray(json) ? [] : json.filter(isDanbooruTag).sort(sortByCategory);
}

function sortByCategory(a: DanbooruTag, b: DanbooruTag): number {
  const sortIndex = (category: number): number => {
    switch (category) {
      case 3:
        return 100;
      case 4:
        return 10;
      default:
        return category;
    }
  };
  return sortIndex(a.category) - sortIndex(b.category);
}


================================================
FILE: client-src/libs/api/getExtraNetworks.ts
================================================
import type { ExtraNetworksData, ExtraNetworksType } from "../extra-networks";
import { omitNulls } from "@/libs/util/array";
import { isObject, isString } from "@/libs/util/types";

export function getExtraNetworks(type: ExtraNetworksType): Promise<ExtraNetworksData[]> {
  const promise = fetch(
    `/better-prompt-api/v1/get-extra-networks/${type}?ts=${new Date().getTime()}`
  );
  return promise
    .then((response) => response.json())
    .then((json) =>
      omitNulls(parseResponse(json).map((element) => parseExtraNetworksData(element, type)))
    );
}

function parseResponse(json: unknown): unknown[] {
  if (json == null || !Array.isArray(json)) return [];
  return json;
}

function parseExtraNetworksData(
  obj: unknown,
  type: ExtraNetworksType
): Nullable<ExtraNetworksData> {
  if (!isObject(obj)) return null;
  if (!isString(obj["name"])) return null;
  if (!isString(obj["search_term"])) return null;

  if (!isString(obj["preview"])) {
    return {
      type,
      name: obj["name"],
      search_term: obj["search_term"],
    };
  }

  return {
    type,
    name: obj["name"],
    search_term: obj["search_term"],
    thumbnail: `url("${obj["preview"]}")`,
  };
}


================================================
FILE: client-src/libs/api/getLocalization.ts
================================================
export function getLocalization(): Promise<Record<string, string>> {
  const promise = fetch(`/better-prompt-api/v1/get-localization?ts=${new Date().getTime()}`);
  return promise.then((response) => response.json()).then(parseLocalization);
}

function parseLocalization(json: unknown): Record<string, string> {
  if (json == null || typeof json !== "object") return {};
  return isLocalization(json) ? json : {};
}

function isLocalization(obj: object): obj is Record<string, string> {
  return Object.values(obj).every((value) => typeof value === "string");
}


================================================
FILE: client-src/libs/api/getMyPrompts.ts
================================================
import type { MyPrompt } from "@/libs/my-prompt";
import { isArray, isObject, isString } from "@/libs/util/types";

export function getMyPrompts(): Promise<MyPrompt[]> {
  const promise = fetch(`/better-prompt-api/v1/get-my-prompts?ts=${new Date().getTime()}`);
  return promise.then((response) => response.json()).then(parseMyPrompts);
}

function parseMyPrompts(json: unknown): MyPrompt[] {
  return isArray(json) ? json.filter(isMyPrompt) : [];
}

function isMyPrompt(obj: unknown): obj is MyPrompt {
  if (!isObject(obj)) return false;
  if (!isString(obj["label"])) return false;
  if (!isArray(obj["tags"], isString)) return false;
  return isString(obj["prompt"]);
}


================================================
FILE: client-src/libs/api/index.ts
================================================
export { getDanbooruTags } from "./getDanbooruTags";
export { getExtraNetworks } from "./getExtraNetworks";
export { getLocalization } from "./getLocalization";
export { getMyPrompts } from "./getMyPrompts";
export { updateMyPrompts } from "./updateMyPrompts";


================================================
FILE: client-src/libs/api/updateMyPrompts.ts
================================================
import type { MyPrompt } from "../my-prompt";
import { isBoolean, isObject } from "../util/types";

export function updateMyPrompts(myPrompts: MyPrompt[]): Promise<boolean> {
  const promise = fetch(`/better-prompt-api/v1/update-my-prompts`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(myPrompts),
  });
  return promise.then((response) => response.json()).then(parseResponse);
}

function parseResponse(json: unknown): boolean {
  if (!isObject(json)) return false;
  return isBoolean(json["success"]) && json["success"];
}


================================================
FILE: client-src/libs/danbooru/danbooruTag.ts
================================================
export type DanbooruTag = {
  name: string;
  post_count: number;
  category: 0 | 3 | 4;
};


================================================
FILE: client-src/libs/danbooru/index.ts
================================================
export type { DanbooruTag } from "./danbooruTag";
export { isDanbooruTag } from "./isDanbooruTag";
export { tagToPrompt } from "./tagToPrompt";


================================================
FILE: client-src/libs/danbooru/isDanbooruTag.ts
================================================
import type { DanbooruTag } from "./danbooruTag";

export function isDanbooruTag(obj: unknown): obj is DanbooruTag {
  if (obj == null || typeof obj !== "object") return false;
  if (typeof obj["name"] !== "string") return false;
  if (typeof obj["post_count"] !== "number") return false;
  return typeof obj["category"] === "number" && [0, 3, 4].includes(obj["category"]);
}


================================================
FILE: client-src/libs/danbooru/tagToPrompt.ts
================================================
export function tagToPrompt(tag: string): string {
  return replaceAll(tag, {
    _: " ",
    "(": "\\(",
    ")": "\\)",
  });
}

function replaceAll(text: string, dic: Record<string, string>): string {
  let result = text;
  Object.entries(dic).forEach(([key, value]) => {
    result = result.split(key).join(value);
  });
  return result;
}


================================================
FILE: client-src/libs/extra-networks/extraNetworks.ts
================================================
export type ExtraNetworksType = "textual-inversion" | "lora";

export type ExtraNetworksData = {
  type: ExtraNetworksType;
  name: string;
  search_term: string;
  thumbnail?: string;
};


================================================
FILE: client-src/libs/extra-networks/index.ts
================================================
export type { ExtraNetworksData, ExtraNetworksType } from "./extraNetworks";


================================================
FILE: client-src/libs/my-prompt/index.ts
================================================
export type { MyPrompt } from "./myPrompt";


================================================
FILE: client-src/libs/my-prompt/myPrompt.ts
================================================
export interface MyPrompt {
  label: string;
  tags: string[];
  prompt: string;
}


================================================
FILE: client-src/libs/prompt/allPrompt.ts
================================================
import type { BasicPrompt } from "./basicPrompt";
import type { ExtraNetworksPrompt } from "./extraNetworksPrompt";
import type { PromptCombination } from "@/libs/prompt/promptCombination";

export type AllPrompt = BasicPrompt | PromptCombination | ExtraNetworksPrompt;


================================================
FILE: client-src/libs/prompt/alternatePrompt.ts
================================================
import type { Prompt } from "./prompt";
import type { InnerPrompt } from "./innerPrompt";

export interface AlternatePrompt extends Prompt {
  type: "alternate";
  values: InnerPrompt[];
}


================================================
FILE: client-src/libs/prompt/basicPrompt.ts
================================================
import type { AlternatePrompt } from "./alternatePrompt";
import type { EmphasizedPrompt } from "./emphasizedPrompt";
import type { PlainPrompt } from "./plainPrompt";
import type { ScheduledPrompt } from "./scheduledPrompt";

export type BasicPrompt = AlternatePrompt | EmphasizedPrompt | PlainPrompt | ScheduledPrompt;


================================================
FILE: client-src/libs/prompt/emphasizedPrompt.ts
================================================
import type { Prompt } from "./prompt";
import type { InnerPrompt } from "./innerPrompt";

export type EmphasizedPrompt =
  | EmphasizedPositivePrompt
  | EmphasizedNegativePrompt
  | EmphasizedWeightedPrompt;

export interface EmphasizedPositivePrompt extends Prompt {
  type: "emphasized-positive";
  values: InnerPrompt[];
}

export interface EmphasizedNegativePrompt extends Prompt {
  type: "emphasized-negative";
  values: InnerPrompt[];
}

export interface EmphasizedWeightedPrompt extends Prompt {
  type: "emphasized-weighted";
  values: InnerPrompt[];
  weight: number;
}


================================================
FILE: client-src/libs/prompt/extraNetworksPrompt.ts
================================================
import type { Prompt } from "./prompt";

export interface ExtraNetworksPrompt extends Prompt {
  type: "extra-networks";
  name: string;
  args: string[];
}


================================================
FILE: client-src/libs/prompt/index.ts
================================================
export type { PromptType } from "./prompt";
export type { AllPrompt as Prompt } from "./allPrompt";
export type { AlternatePrompt } from "./alternatePrompt";
export type { BasicPrompt } from "./basicPrompt";
export type {
  EmphasizedPrompt,
  EmphasizedPositivePrompt,
  EmphasizedNegativePrompt,
  EmphasizedWeightedPrompt,
} from "./emphasizedPrompt";
export type { ExtraNetworksPrompt } from "./extraNetworksPrompt";
export type { InnerPrompt } from "./innerPrompt";
export type { PlainPrompt } from "./plainPrompt";
export type { PromptCombination } from "./promptCombination";
export type { ScheduledPrompt } from "./scheduledPrompt";

export * from "./util";


================================================
FILE: client-src/libs/prompt/innerPrompt.ts
================================================
import type { BasicPrompt } from "./basicPrompt";
import type { PromptCombination } from "./promptCombination";

export type InnerPrompt = BasicPrompt | PromptCombination;


================================================
FILE: client-src/libs/prompt/plainPrompt.ts
================================================
import type { Prompt } from "./prompt";

export interface PlainPrompt extends Prompt {
  type: "plain";
  value: string;
}


================================================
FILE: client-src/libs/prompt/prompt.ts
================================================
export type PromptType =
  | "alternate"
  | "combination"
  | "emphasized-positive"
  | "emphasized-negative"
  | "emphasized-weighted"
  | "extra-networks"
  | "scheduled"
  | "plain";

export interface Prompt {
  type: PromptType;
}


================================================
FILE: client-src/libs/prompt/promptCombination.ts
================================================
import type { Prompt } from "@/libs/prompt/prompt";
import type { BasicPrompt } from "@/libs/prompt/basicPrompt";

export interface PromptCombination extends Prompt {
  type: "combination";
  values: BasicPrompt[];
}


================================================
FILE: client-src/libs/prompt/scheduledPrompt.ts
================================================
import type { Prompt } from "./prompt";
import type { InnerPrompt } from "./innerPrompt";

export interface ScheduledPrompt extends Prompt {
  type: "scheduled";
  from?: InnerPrompt;
  to?: InnerPrompt;
  when: number;
}


================================================
FILE: client-src/libs/prompt/util/concatPrompt.ts
================================================
/**
 * A regular expression to match a trailing comma followed by optional whitespace.
 */
const TRAILING_COMMA_REGEX = /,(\s+)?$/g;

/**
 * A regular expression to match a leading comma preceded by optional whitespace.
 */
const LEADING_COMMA_REGEX = /^(\s+)?,/g;

export function concatPrompt(prompt1: string, prompt2: string): string {
  if (!prompt1 || TRAILING_COMMA_REGEX.test(prompt1) || LEADING_COMMA_REGEX.test(prompt2)) {
    return prompt1 + prompt2;
  }
  return `${prompt1}, ${prompt2}`;
}


================================================
FILE: client-src/libs/prompt/util/index.ts
================================================
export { concatPrompt } from "./concatPrompt";
export { isEquals } from "./isEquals";
export { isPromptType } from "./isPromptType";
export { parsePrompt } from "./parsePrompt";
export { toString } from "./toString";


================================================
FILE: client-src/libs/prompt/util/isEquals/alternatePromptEquals.ts
================================================
import type { AlternatePrompt } from "@/libs/prompt";
import { isEquals } from "./index";

export function alternatePromptEquals(prompt1: AlternatePrompt, prompt2: AlternatePrompt): boolean {
  if (prompt1.values.length !== prompt2.values.length) return false;
  return prompt1.values.every((prompt, i) => isEquals(prompt, prompt2.values[i]));
}


================================================
FILE: client-src/libs/prompt/util/isEquals/emphasizedPromptEquals.ts
================================================
import type {
  EmphasizedPrompt,
  EmphasizedNegativePrompt,
  EmphasizedPositivePrompt,
  EmphasizedWeightedPrompt,
} from "@/libs/prompt";
import { isEquals } from "./index";

export function emphasizedPromptEquals(
  prompt1: EmphasizedPrompt,
  prompt2: EmphasizedPrompt
): boolean {
  if (prompt1.type !== prompt2.type) return false;
  switch (prompt1.type) {
    case "emphasized-positive":
      return emphasizedPositivePromptEquals(prompt1, prompt2 as EmphasizedPositivePrompt);
    case "emphasized-negative":
      return emphasizedNegativePromptEquals(prompt1, prompt2 as EmphasizedNegativePrompt);
    case "emphasized-weighted":
      return emphasizedWeightedPromptEquals(prompt1, prompt2 as EmphasizedWeightedPrompt);
  }
  return false;
}

export function emphasizedPositivePromptEquals(
  prompt1: EmphasizedPositivePrompt,
  prompt2: EmphasizedPositivePrompt
): boolean {
  if (prompt1.values.length !== prompt2.values.length) return false;
  return prompt1.values.every((prompt, i) => isEquals(prompt, prompt2.values[i]));
}

export function emphasizedNegativePromptEquals(
  prompt1: EmphasizedNegativePrompt,
  prompt2: EmphasizedNegativePrompt
): boolean {
  if (prompt1.values.length !== prompt2.values.length) return false;
  return prompt1.values.every((prompt, i) => isEquals(prompt, prompt2.values[i]));
}

export function emphasizedWeightedPromptEquals(
  prompt1: EmphasizedWeightedPrompt,
  prompt2: EmphasizedWeightedPrompt
): boolean {
  if (prompt1.values.length !== prompt2.values.length) return false;
  if (prompt1.weight !== prompt2.weight) return false;
  return prompt1.values.every((prompt, i) => isEquals(prompt, prompt2.values[i]));
}


================================================
FILE: client-src/libs/prompt/util/isEquals/extraNetworksPromptEquals.ts
================================================
import type { ExtraNetworksPrompt } from "@/libs/prompt";

export function extraNetworksPromptEquals(
  prompt1: ExtraNetworksPrompt,
  prompt2: ExtraNetworksPrompt
): boolean {
  if (prompt1.name !== prompt2.name) return false;
  if (prompt1.name === "textual-inversion") return textualInversionPromptEquals(prompt1, prompt2);
  if (prompt1.name === "lora") return loraPromptEquals(prompt1, prompt2);
  return prompt1.args.every((value, i) => value === prompt2.args[i]);
}

function textualInversionPromptEquals(
  prompt1: ExtraNetworksPrompt,
  prompt2: ExtraNetworksPrompt
): boolean {
  return prompt1.args[0] === prompt2.args[0];
}

function loraPromptEquals(prompt1: ExtraNetworksPrompt, prompt2: ExtraNetworksPrompt): boolean {
  if (prompt1.args[0] !== prompt2.args[0]) return false;
  return Number(prompt1.args[1]) === Number(prompt2.args[1]);
}


================================================
FILE: client-src/libs/prompt/util/isEquals/index.ts
================================================
import type {
  AlternatePrompt,
  EmphasizedNegativePrompt,
  EmphasizedPositivePrompt,
  EmphasizedWeightedPrompt,
  ExtraNetworksPrompt,
  PlainPrompt,
  Prompt,
  PromptCombination,
  ScheduledPrompt,
} from "@/libs/prompt";
import { alternatePromptEquals } from "./alternatePromptEquals";
import { promptCombinationEquals } from "./promptCombinationEquals";
import {
  emphasizedNegativePromptEquals,
  emphasizedPositivePromptEquals,
  emphasizedWeightedPromptEquals,
} from "./emphasizedPromptEquals";
import { extraNetworksPromptEquals } from "./extraNetworksPromptEquals";
import { plainPromptEquals } from "./plainPromptEquals";
import { scheduledPromptEquals } from "./scheduledPromptEquals";

export function isEquals(prompt1: Prompt, prompt2: Prompt): boolean;
export function isEquals(prompts1: Prompt[], prompts2: Prompt[]): boolean;
export function isEquals(arg1: OneOrMany<Prompt>, arg2: OneOrMany<Prompt>): boolean {
  if (Array.isArray(arg1)) {
    if (Array.isArray(arg2)) return isEquals2(arg1, arg2);
    throw new Error("Illegal arguments");
  }
  if (!Array.isArray(arg2)) return isEquals1(arg1, arg2);
  throw new Error("Illegal arguments");
}

function isEquals1(prompt1: Prompt, prompt2: Prompt): boolean {
  if (prompt1 === prompt2) return true;
  if (prompt1.type !== prompt2.type) return false;
  switch (prompt1.type) {
    case "alternate":
      return alternatePromptEquals(prompt1, prompt2 as AlternatePrompt);
    case "combination":
      return promptCombinationEquals(prompt1, prompt2 as PromptCombination);
    case "emphasized-positive":
      return emphasizedPositivePromptEquals(prompt1, prompt2 as EmphasizedPositivePrompt);
    case "emphasized-negative":
      return emphasizedNegativePromptEquals(prompt1, prompt2 as EmphasizedNegativePrompt);
    case "emphasized-weighted":
      return emphasizedWeightedPromptEquals(prompt1, prompt2 as EmphasizedWeightedPrompt);
    case "extra-networks":
      return extraNetworksPromptEquals(prompt1, prompt2 as ExtraNetworksPrompt);
    case "plain":
      return plainPromptEquals(prompt1, prompt2 as PlainPrompt);
    case "scheduled":
      return scheduledPromptEquals(prompt1, prompt2 as ScheduledPrompt);
  }
  return false;
}

function isEquals2(prompts1: Prompt[], prompts2: Prompt[]): boolean {
  if (prompts1.length !== prompts2.length) return false;
  return prompts1.every((value, i) => isEquals1(value, prompts2[i]));
}


================================================
FILE: client-src/libs/prompt/util/isEquals/plainPromptEquals.ts
================================================
import type { PlainPrompt } from "@/libs/prompt";

export function plainPromptEquals(prompt1: PlainPrompt, prompt2: PlainPrompt): boolean {
  return prompt1.value === prompt2.value;
}


================================================
FILE: client-src/libs/prompt/util/isEquals/promptCombinationEquals.ts
================================================
import type { PromptCombination } from "@/libs/prompt";
import { isEquals } from "./index";

export function promptCombinationEquals(
  prompt1: PromptCombination,
  prompt2: PromptCombination
): boolean {
  if (prompt1.values.length !== prompt2.values.length) return false;
  return prompt1.values.every((prompt, i) => isEquals(prompt, prompt2.values[i]));
}


================================================
FILE: client-src/libs/prompt/util/isEquals/scheduledPromptEquals.ts
================================================
import type { InnerPrompt, ScheduledPrompt } from "@/libs/prompt";
import { isEquals } from "./index";

export function scheduledPromptEquals(prompt1: ScheduledPrompt, prompt2: ScheduledPrompt): boolean {
  if (prompt1.when !== prompt2.when) return false;
  if ((prompt1.to == null) !== (prompt2.to == null)) return false;
  if (prompt1.to != null && !isEquals(prompt1.to, prompt2.to as InnerPrompt)) return false;
  if ((prompt1.from == null) !== (prompt2.from == null)) return false;
  return prompt1.from == null || isEquals(prompt1.from, prompt2.from as InnerPrompt);
}


================================================
FILE: client-src/libs/prompt/util/isPromptType.ts
================================================
import type { PromptType } from "@/libs/prompt";

const promptTypes: Set<string> = new Set<PromptType>([
  "alternate",
  "combination",
  "emphasized-positive",
  "emphasized-negative",
  "emphasized-weighted",
  "plain",
  "scheduled",
  "extra-networks",
]);

export function isPromptType(type: string): type is PromptType {
  return promptTypes.has(type);
}


================================================
FILE: client-src/libs/prompt/util/parsePrompt/index.ts
================================================
import type {
  AlternatePrompt,
  BasicPrompt,
  EmphasizedPrompt,
  ExtraNetworksPrompt,
  InnerPrompt,
  PlainPrompt,
  Prompt,
  PromptCombination,
  ScheduledPrompt,
} from "@/libs/prompt";
import { getOption } from "@/libs/util/webui";
import { get_parser } from "./prompt-parser";

export function parsePrompt(text: string, callback: Callback1<Prompt[]>): void;
export function parsePrompt(text: string): Nullable<Prompt[]>;
export function parsePrompt(
  text: string,
  callback?: Callback1<Prompt[]>
): void | Nullable<Prompt[]> {
  const prompts = parse(text);
  if (prompts != null && callback != null) {
    callback(prompts);
    return void 0;
  }
  return prompts;
}

function parse(text: string): Nullable<Prompt[]> {
  const parser = get_parser({
    transformer: {
      start: (values: unknown[]) => Array.from(values).flat(),
      extra_networks_prompts: (values: unknown[]) => Array.from(values).flat(),
      extra_networks: ([name, args]: [string, string[]]): ExtraNetworksPrompt => {
        if (name !== "lora") return { type: "extra-networks", name, args };
        const weight = getOption("extra_networks_default_multiplier", 1).toString();
        return {
          type: "extra-networks",
          name,
          args: args.length === 1 ? [args[0], weight] : args,
        };
      },
      extra_networks_name: ([{ value }]: Array<{ value: string }>) => value.toLowerCase().trim(),
      extra_networks_args: (values: Array<{ value: string }>) => values.map((x) => x.value.trim()),
      multiple: (values: unknown[]) => Array.from(values).flat(),
      combination: (values: BasicPrompt[]): PromptCombination | PlainPrompt => {
        if (values.every((value) => value.type === "plain")) {
          return {
            type: "plain",
            value: (values as PlainPrompt[]).map(({ value }) => value).join(" "),
          };
        }
        return { type: "combination", values };
      },
      alternate: (values: InnerPrompt[]): AlternatePrompt => ({ type: "alternate", values }),
      scheduled_full: (values: [InnerPrompt, InnerPrompt, number]): ScheduledPrompt => ({
        type: "scheduled",
        from: values[0],
        to: values[1],
        when: values[2],
      }),
      scheduled_to: (values: [InnerPrompt, number]): ScheduledPrompt => ({
        type: "scheduled",
        to: values[0],
        when: values[1],
      }),
      scheduled_from: (values: [InnerPrompt, number]): ScheduledPrompt => ({
        type: "scheduled",
        from: values[0],
        when: values[1],
      }),
      emphasized_positive: ([value]: [OneOrMany<InnerPrompt>]): EmphasizedPrompt => ({
        type: "emphasized-positive",
        values: Array.isArray(value) ? value : [value],
      }),
      emphasized_negative: ([value]: [OneOrMany<InnerPrompt>]): EmphasizedPrompt => ({
        type: "emphasized-negative",
        values: Array.isArray(value) ? value : [value],
      }),
      emphasized_weighted: ([value, weight]: [
        OneOrMany<InnerPrompt>,
        number
      ]): EmphasizedPrompt => ({
        type: "emphasized-weighted",
        values: Array.isArray(value) ? value : [value],
        weight,
      }),
      plain: ([{ value }]): PlainPrompt => ({ type: "plain", value: String(value).trim() }),
      number: ([{ value }]) => Number(value),
    },
  });

  try {
    return parser.parse(text) as Prompt[];
  } catch {
    return null;
  }
}


================================================
FILE: client-src/libs/prompt/util/parsePrompt/prompt-parser.js
================================================
/* eslint-disable */
// @ts-nocheck @typescript-eslint/ban-ts-comment
//
//  Lark.js stand-alone parser
//===============================

"use strict";

/**
 This is the main entrypoint into the generated Lark parser.

 @param {object} options An object with the following optional properties:

 - transformer: an object of {rule: callback}, or an instance of Transformer
 - propagate_positions (bool): should all tree nodes calculate line/column info?
 - tree_class (Tree): a class that extends Tree, to be used for creating the parse tree.
 - debug (bool): in case of error, should the parser output debug info to the console?

 @returns {Lark} an object which provides the following methods:

 - parse
 - parse_interactive
 - lex

 */
function get_parser(options = {}) {
  if (options.transformer && options.transformer.constructor.name === "object") {
    options.transformer = Transformer.fromObj(options.transformer);
  }

  return Lark._load_from_dict({ data: DATA, memo: MEMO, ...options });
}

const NO_VALUE = {};
class _Decoratable {}
const Discard = {};

//
//   Implementation of Scanner + module emulation for Python's stdlib re
// -------------------------------------------------------------------------

const re = {
  escape(string) {
    // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
    return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
  },
  compile(regex, flags) {
    // May throw re.error
    return new RegExp(regex, flags);
  },
  error: SyntaxError,
};

function _get_match(re_, regexp, s, flags) {
  const m = re_.compile(regexp, flags).exec(s);
  if (m != null) return m[0];
}

class Scanner {
  constructor(terminals, g_regex_flags, re_, use_bytes, match_whole = false) {
    this.terminals = terminals;
    this.g_regex_flags = g_regex_flags;
    this.re_ = re_;
    this.use_bytes = use_bytes;
    this.match_whole = match_whole;
    this.allowed_types = new Set(this.terminals.map((t) => t.name));

    this._regexps = this._build_mres(terminals);
  }

  _build_mres(terminals) {
    // TODO deal with priorities!
    let postfix = this.match_whole ? "$" : "";
    let patterns_by_flags = segment_by_key(terminals, (t) => t.pattern.flags.join(""));

    let regexps = [];
    for (let [flags, patterns] of patterns_by_flags) {
      const pattern = patterns
        .map((t) => `(?<${t.name}>${t.pattern.to_regexp() + postfix})`)
        .join("|");
      regexps.push(new RegExp(pattern, this.g_regex_flags + flags + "y"));
    }

    return regexps;
  }

  match(text, pos) {
    for (const re of this._regexps) {
      re.lastIndex = pos;
      let m = re.exec(text);
      if (m) {
        // Find group. Ugly hack, but javascript is forcing my hand.
        let group = null;
        for (let [k, v] of Object.entries(m.groups)) {
          if (v) {
            group = k;
            break;
          }
        }
        return [m[0], group];
      }
    }
  }
}
//
//  Start of library code
// --------------------------

const util = typeof require !== "undefined" && require("util");

class ABC {}

const NotImplemented = {};

function dict_items(d) {
  return Object.entries(d);
}
function dict_keys(d) {
  return Object.keys(d);
}
function dict_values(d) {
  return Object.values(d);
}

function dict_pop(d, key) {
  if (key === undefined) {
    key = Object.keys(d)[0];
  }
  let value = d[key];
  delete d[key];
  return value;
}

function dict_get(d, key, otherwise = null) {
  return d[key] || otherwise;
}

function dict_update(self, other) {
  if (self.constructor.name === "Map") {
    for (const [k, v] of dict_items(other)) {
      self.set(k, v);
    }
  } else {
    for (const [k, v] of dict_items(other)) {
      self[k] = v;
    }
  }
}

function make_constructor(cls) {
  return function () {
    return new cls(...arguments);
  };
}

function range(start, end) {
  if (end === undefined) {
    end = start;
    start = 0;
  }
  const res = [];
  for (let i = start; i < end; i++) res.push(i);
  return res;
}

function format(s) {
  let counter = 0;
  let args = [...arguments].slice(1);

  return s.replace(/%([sr])/g, function () {
    const t = arguments[1];
    const item = args[counter++];
    if (t === "r") {
      return util ? util.inspect(item, false, null, true) : JSON.stringify(item, null, 0);
    } else {
      return item;
    }
  });
}

function union(setA, setB) {
  let _union = new Set(setA);
  for (const elem of setB) {
    _union.add(elem);
  }
  return _union;
}

function intersection(setA, setB) {
  let _intersection = new Set();
  for (const elem of setB) {
    if (setA.has(elem)) {
      _intersection.add(elem);
    }
  }
  return _intersection;
}

function set_subtract(a, b) {
  return [...a].filter((e) => !b.has(e));
}

function dict(d) {
  return { ...d };
}

function bool(x) {
  return !!x;
}

function new_object(cls) {
  return Object.create(cls.prototype);
}

function copy(obj) {
  if (typeof obj == "object") {
    let empty_clone = Object.create(Object.getPrototypeOf(obj));
    return Object.assign(empty_clone, obj);
  }
  return obj;
}

function map_pop(key) {
  let value = this.get(key);
  this.delete(key);
  return value;
}

function hash(x) {
  return x;
}
function tuple(x) {
  return x;
}
function frozenset(x) {
  return new Set(x);
}

function is_dict(x) {
  return x && x.constructor.name === "Object";
}
function is_array(x) {
  return x && x.constructor.name === "Array";
}
function callable(x) {
  return typeof x === "function";
}

function* enumerate(it, start = 0) {
  // Taken from: https://stackoverflow.com/questions/34336960/what-is-the-es6-equivalent-of-python-enumerate-for-a-sequence
  let i = start;
  for (const x of it) {
    yield [i++, x];
  }
}

function any(lst) {
  for (const item of lst) {
    if (item) {
      return true;
    }
  }
  return false;
}

function all(lst) {
  for (const item of lst) {
    if (!item) {
      return false;
    }
  }
  return true;
}

function filter(pred, lst) {
  return lst.filter(pred || bool);
}

function partial(f) {
  let args = [...arguments].slice(1);
  return function () {
    return f(...args, ...arguments);
  };
}

class EOFError extends Error {}

function last_item(a) {
  return a[a.length - 1];
}

function callable_class(cls) {
  return function () {
    let inst = new cls(...arguments);
    return inst.__call__.bind(inst);
  };
}

function list_repeat(list, count) {
  return Array.from({ length: count }, () => list).flat();
}

function isupper(a) {
  return /^[A-Z_$]*$/.test(a);
}

function rsplit(s, delimiter, limit) {
  const arr = s.split(delimiter);
  return limit ? arr.splice(-limit - 1) : arr;
}

function str_count(s, substr) {
  let re = new RegExp(substr, "g");
  return (s.match(re) || []).length;
}

function list_count(list, elem) {
  let count = 0;
  for (const e of list) {
    if (e === elem) {
      count++;
    }
  }
  return count;
}

function isSubset(subset, set) {
  for (let elem of subset) {
    if (!set.has(elem)) {
      return false;
    }
  }
  return true;
}

function* segment_by_key(a, key) {
  let buffer = [];
  let last_k = null;
  for (const item of a) {
    const k = key(item);
    if (last_k && k != last_k) {
      yield [last_k, buffer];
      buffer = [];
    }
    buffer.push(item);
    last_k = k;
  }
  yield [last_k, buffer];
}

// --------------------------
//  End of library code
//

//
// Exceptions
//

class LarkError extends Error {
  // pass
}

class ConfigurationError extends LarkError {
  // pass
}

function assert_config(value, options, msg = "Got %r, expected one of %s") {
  if (!options.includes(value)) {
    throw new ConfigurationError(format(msg, value, options));
  }
}

class GrammarError extends LarkError {
  // pass
}

class ParseError extends LarkError {
  // pass
}

class LexError extends LarkError {
  // pass
}

/**
 UnexpectedInput Error.

 Used as a base class for the following exceptions:

 - ``UnexpectedCharacters``: The lexer encountered an unexpected string
 - ``UnexpectedToken``: The parser received an unexpected token
 - ``UnexpectedEOF``: The parser expected a token, but the input ended

 After catching one of these exceptions, you may call the following helper methods to create a nicer error message.

 */

class UnexpectedInput extends LarkError {
  pos_in_stream = null;
  _terminals_by_name = null;
  /**
   Returns a pretty string pinpointing the error in the text,
   with span amount of context characters around it.

   Note:
   The parser doesn't hold a copy of the text it has to parse,
   so you have to provide it again

   */
  get_context(text, span = 40) {
    let after, before;
    let pos = this.pos_in_stream;
    let start = max(pos - span, 0);
    let end = pos + span;
    if (!(text instanceof bytes)) {
      before = last_item(rsplit(text.slice(start, pos), "\n", 1));
      after = text.slice(pos, end).split("\n", 1)[0];
      return before + after + "\n" + " " * before.expandtabs().length + "^\n";
    } else {
      before = last_item(rsplit(text.slice(start, pos), "\n", 1));
      after = text.slice(pos, end).split("\n", 1)[0];
      return (before + after + "\n" + " " * before.expandtabs().length + "^\n").decode(
        "ascii",
        "backslashreplace"
      );
    }
  }

  /**
   Allows you to detect what's wrong in the input text by matching
   against example errors.

   Given a parser instance and a dictionary mapping some label with
   some malformed syntax examples, it'll return the label for the
   example that bests matches the current error. The function will
   iterate the dictionary until it finds a matching error, and
   return the corresponding value.

   For an example usage, see `examples/error_reporting_lalr.py`

   Parameters:
   parse_fn: parse function (usually ``lark_instance.parse``)
   examples: dictionary of ``{'example_string': value}``.
   use_accepts: Recommended to keep this as ``use_accepts=True``.

   */
  match_examples(parse_fn, examples, token_type_match_fallback = false) {
    if (is_dict(examples)) {
      examples = dict_items(examples);
    }

    let candidate = [null, false];
    for (const [i, [label, example]] of enumerate(examples)) {
      for (const [j, malformed] of enumerate(example)) {
        try {
          parse_fn(malformed);
        } catch (ut) {
          if (ut instanceof UnexpectedInput) {
            if (ut.state.eq(this.state)) {
              if (ut.token === this.token) {
                return label;
              }

              if (token_type_match_fallback) {
                // Fallback to token types match
                if (ut.token.type === this.token.type && !last_item(candidate)) {
                  candidate = [label, true];
                }
              }
              if (candidate[0] === null) {
                candidate = [label, false];
              }
            }
          } else {
            throw ut;
          }
        }
      }
    }

    return candidate[0];
  }

  _format_expected(expected) {
    let d;
    if (this._terminals_by_name) {
      d = this._terminals_by_name;
      expected = expected.map((t_name) => (t_name in d ? d[t_name].user_repr() : t_name));
    }

    return format("Expected one of: \n\t* %s\n", expected.join("\n\t* "));
  }
}

/**
 An exception that is raised by the parser, when the input ends while it still expects a token.

 */

class UnexpectedEOF extends UnexpectedInput {
  constructor(expected, state = null, terminals_by_name = null) {
    super();
    this.expected = expected;
    this.state = state;
    this.token = new Token("<EOF>", "");
    // , line=-1, column=-1, pos_in_stream=-1)
    this.pos_in_stream = -1;
    this.line = -1;
    this.column = -1;
    this._terminals_by_name = terminals_by_name;
  }
}

/**
 An exception that is raised by the lexer, when it cannot match the next
 string of characters to any of its terminals.

 */

class UnexpectedCharacters extends UnexpectedInput {
  constructor({
    seq,
    lex_pos,
    line,
    column,
    allowed = null,
    considered_tokens = null,
    state = null,
    token_history = null,
    terminals_by_name = null,
    considered_rules = null,
  } = {}) {
    super();
    // TODO considered_tokens and allowed can be figured out using state
    this.line = line;
    this.column = column;
    this.pos_in_stream = lex_pos;
    this.state = state;
    this._terminals_by_name = terminals_by_name;
    this.allowed = allowed;
    this.considered_tokens = considered_tokens;
    this.considered_rules = considered_rules;
    this.token_history = token_history;
    this.char = seq[lex_pos];
    // this._context = this.get_context(seq);
  }
}

/**
 An exception that is raised by the parser, when the token it received
 doesn't match any valid step forward.

 Parameters:
 token: The mismatched token
 expected: The set of expected tokens
 considered_rules: Which rules were considered, to deduce the expected tokens
 state: A value representing the parser state. Do not rely on its value or type.
 interactive_parser: An instance of ``InteractiveParser``, that is initialized to the point of failture,
 and can be used for debugging and error handling.

 Note: These parameters are available as attributes of the instance.

 */

class UnexpectedToken extends UnexpectedInput {
  constructor({
    token,
    expected,
    considered_rules = null,
    state = null,
    interactive_parser = null,
    terminals_by_name = null,
    token_history = null,
  } = {}) {
    super();
    // TODO considered_rules and expected can be figured out using state
    this.line = (token && token["line"]) || "?";
    this.column = (token && token["column"]) || "?";
    this.pos_in_stream = (token && token["start_pos"]) || null;
    this.state = state;
    this.token = token;
    this.expected = expected;
    // XXX deprecate? `accepts` is better
    this._accepts = NO_VALUE;
    this.considered_rules = considered_rules;
    this.interactive_parser = interactive_parser;
    this._terminals_by_name = terminals_by_name;
    this.token_history = token_history;
  }

  get accepts() {
    if (this._accepts === NO_VALUE) {
      this._accepts = this.interactive_parser && this.interactive_parser.accepts();
    }

    return this._accepts;
  }
}

/**
 VisitError is raised when visitors are interrupted by an exception

 It provides the following attributes for inspection:

 Parameters:
 rule: the name of the visit rule that failed
 obj: the tree-node or token that was being processed
 orig_exc: the exception that cause it to fail

 Note: These parameters are available as attributes

 */

class VisitError extends LarkError {
  constructor(rule, obj, orig_exc) {
    let message = format('Error trying to process rule "%s":\n\n%s', rule, orig_exc);
    super(message);
    this.rule = rule;
    this.obj = obj;
    this.orig_exc = orig_exc;
  }
}

//
// Utils
//

function classify(seq, key = null, value = null) {
  let k, v;
  let d = new Map();
  for (const item of seq) {
    k = key !== null ? key(item) : item;
    v = value !== null ? value(item) : item;
    if (d.has(k)) {
      d.get(k).push(v);
    } else {
      d.set(k, [v]);
    }
  }

  return d;
}

function _deserialize(data, namespace, memo) {
  let class_;
  if (is_dict(data)) {
    if ("__type__" in data) {
      // Object
      class_ = namespace[data["__type__"]];
      return class_.deserialize(data, memo);
    } else if ("@" in data) {
      return memo[data["@"]];
    }

    return Object.fromEntries(
      dict_items(data).map(([key, value]) => [key, _deserialize(value, namespace, memo)])
    );
  } else if (is_array(data)) {
    return data.map((value) => _deserialize(value, namespace, memo));
  }

  return data;
}

/**
 Safe-ish serialization interface that doesn't rely on Pickle

 Attributes:
 __serialize_fields__ (List[str]): Fields (aka attributes) to serialize.
 __serialize_namespace__ (list): List of classes that deserialization is allowed to instantiate.
 Should include all field types that aren't builtin types.

 */

class Serialize {
  static deserialize(data, memo) {
    const cls = this;
    let fields = cls && cls["__serialize_fields__"];
    if ("@" in data) {
      return memo[data["@"]];
    }

    let inst = new_object(cls);
    for (const f of fields) {
      if (data && f in data) {
        inst[f] = _deserialize(data[f], NAMESPACE, memo);
      } else {
        throw new KeyError("Cannot find key for class", cls, e);
      }
    }

    if ("_deserialize" in inst) {
      inst._deserialize();
    }

    return inst;
  }
}

/**
 A version of serialize that memoizes objects to reduce space
 */

class SerializeMemoizer extends Serialize {
  static get __serialize_fields__() {
    return ["memoized"];
  }
  constructor(types_to_memoize) {
    super();
    this.types_to_memoize = tuple(types_to_memoize);
    this.memoized = new Enumerator();
  }

  in_types(value) {
    return value instanceof this.types_to_memoize;
  }

  serialize() {
    return _serialize(this.memoized.reversed(), null);
  }

  static deserialize(data, namespace, memo) {
    const cls = this;
    return _deserialize(data, namespace, memo);
  }
}

//
// Tree
//

class Meta {
  constructor() {
    this.empty = true;
  }
}

/**
 The main tree class.

 Creates a new tree, and stores "data" and "children" in attributes of the same name.
 Trees can be hashed and compared.

 Parameters:
 data: The name of the rule or alias
 children: List of matched sub-rules and terminals
 meta: Line & Column numbers (if ``propagate_positions`` is enabled).
 meta attributes: line, column, start_pos, end_line, end_column, end_pos

 */

class Tree {
  constructor(data, children, meta = null) {
    this.data = data;
    this.children = children;
    this._meta = meta;
  }

  get meta() {
    if (this._meta === null) {
      this._meta = new Meta();
    }

    return this._meta;
  }

  repr() {
    return format("Tree(%r, %r)", this.data, this.children);
  }

  _pretty_label() {
    return this.data;
  }

  _pretty(level, indent_str) {
    if (this.children.length === 1 && !(this.children[0] instanceof Tree)) {
      return [
        list_repeat(indent_str, level).join(""),
        this._pretty_label(),
        "\t",
        format("%s", this.children[0].value),
        "\n",
      ];
    }

    let l = [list_repeat(indent_str, level).join(""), this._pretty_label(), "\n"];
    for (const n of this.children) {
      if (n instanceof Tree) {
        l.push(...n._pretty(level + 1, indent_str));
      } else {
        l.push(...[list_repeat(indent_str, level + 1).join(""), format("%s", n.value), "\n"]);
      }
    }

    return l;
  }

  /**
   Returns an indented string representation of the tree.

   Great for debugging.

   */
  pretty(indent_str = "  ") {
    return this._pretty(0, indent_str).join("");
  }

  eq(other) {
    if (
      other &&
      this &&
      other &&
      this &&
      other.children &&
      this.children &&
      other.data &&
      this.data
    ) {
      return this.data === other.data && this.children === other.children;
    } else {
      return false;
    }
  }

  /**
   Depth-first iteration.

   Iterates over all the subtrees, never returning to the same node twice (Lark's parse-tree is actually a DAG).

   */
  iter_subtrees() {
    let queue = [this];
    let subtrees = new Map();
    for (const subtree of queue) {
      subtrees.set(subtree, subtree);
      queue.push(
        ...[...subtree.children]
          .reverse()
          .filter((c) => c instanceof Tree && !subtrees.has(c))
          .map((c) => c)
      );
    }

    queue = undefined;
    return [...subtrees.values()].reverse();
  }

  /**
   Returns all nodes of the tree that evaluate pred(node) as true.
   */
  find_pred(pred) {
    return filter(pred, this.iter_subtrees());
  }

  /**
   Returns all nodes of the tree whose data equals the given data.
   */
  find_data(data) {
    return this.find_pred((t) => t.data === data);
  }

  /**
   Return all values in the tree that evaluate pred(value) as true.

   This can be used to find all the tokens in the tree.

   Example:
   >>> all_tokens = tree.scan_values(lambda v: isinstance(v, Token))

   */
  *scan_values(pred) {
    for (const c of this.children) {
      if (c instanceof Tree) {
        for (const t of c.scan_values(pred)) {
          yield t;
        }
      } else {
        if (pred(c)) {
          yield c;
        }
      }
    }
  }

  /**
   Breadth-first iteration.

   Iterates over all the subtrees, return nodes in order like pretty() does.

   */
  *iter_subtrees_topdown() {
    let node;
    let stack = [this];
    while (stack.length) {
      node = stack.pop();
      if (!(node instanceof Tree)) {
        continue;
      }

      yield node;
      for (const child of [...node.children].reverse()) {
        stack.push(child);
      }
    }
  }

  copy() {
    return type(this)(this.data, this.children);
  }

  set(data, children) {
    this.data = data;
    this.children = children;
  }
}

//
// Visitors
//

/**
 Transformers work bottom-up (or depth-first), starting with visiting the leaves and working
 their way up until ending at the root of the tree.

 For each node visited, the transformer will call the appropriate method (callbacks), according to the
 node's ``data``, and use the returned value to replace the node, thereby creating a new tree structure.

 Transformers can be used to implement map & reduce patterns. Because nodes are reduced from leaf to root,
 at any point the callbacks may assume the children have already been transformed (if applicable).

 If the transformer cannot find a method with the right name, it will instead call ``__default__``, which by
 default creates a copy of the node.

 To discard a node, return Discard (``lark.visitors.Discard``).

 ``Transformer`` can do anything ``Visitor`` can do, but because it reconstructs the tree,
 it is slightly less efficient.

 A transformer without methods essentially performs a non-memoized partial deepcopy.

 All these classes implement the transformer interface:

 - ``Transformer`` - Recursively transforms the tree. This is the one you probably want.
 - ``Transformer_InPlace`` - Non-recursive. Changes the tree in-place instead of returning new instances
 - ``Transformer_InPlaceRecursive`` - Recursive. Changes the tree in-place instead of returning new instances

 Parameters:
 visit_tokens (bool, optional): Should the transformer visit tokens in addition to rules.
 Setting this to ``False`` is slightly faster. Defaults to ``True``.
 (For processing ignored tokens, use the ``lexer_callbacks`` options)


 */

class Transformer extends _Decoratable {
  static get __visit_tokens__() {
    return true;
  }
  // For backwards compatibility

  constructor(visit_tokens = true) {
    super();
    this.__visit_tokens__ = visit_tokens;
  }

  static fromObj(obj, ...args) {
    class _T extends this {}
    for (let [k, v] of Object.entries(obj)) {
      _T.prototype[k] = v;
    }
    return new _T(...args);
  }

  _call_userfunc(tree, new_children = null) {
    let f, wrapper;
    // Assumes tree is already transformed
    let children = new_children !== null ? new_children : tree.children;
    if (tree && tree.data && this && this[tree.data]) {
      f = this && this[tree.data];
      try {
        wrapper = (f && f["visit_wrapper"]) || null;
        if (wrapper !== null) {
          return f.visit_wrapper(f, tree.data, children, tree.meta);
        } else {
          return f(children);
        }
      } catch (e) {
        if (e instanceof GrammarError) {
          throw e;
        } else if (e instanceof Error) {
          throw new VisitError(tree.data, tree, e);
        } else {
          throw e;
        }
      }
    } else {
      return this.__default__(tree.data, children, tree.meta);
    }
  }

  _call_userfunc_token(token) {
    let f;
    if (token && token.type && this && this[token.type]) {
      f = this && this[token.type];
      try {
        return f(token);
      } catch (e) {
        if (e instanceof GrammarError) {
          throw e;
        } else if (e instanceof Error) {
          throw new VisitError(token.type, token, e);
        } else {
          throw e;
        }
      }
    } else {
      return this.__default_token__(token);
    }
  }

  *_transform_children(children) {
    let res;
    for (const c of children) {
      if (c instanceof Tree) {
        res = this._transform_tree(c);
      } else if (this.__visit_tokens__ && c instanceof Token) {
        res = this._call_userfunc_token(c);
      } else {
        res = c;
      }
      if (res !== Discard) {
        yield res;
      }
    }
  }

  _transform_tree(tree) {
    let children = [...this._transform_children(tree.children)];
    return this._call_userfunc(tree, children);
  }

  /**
   Transform the given tree, and return the final result
   */
  transform(tree) {
    return this._transform_tree(tree);
  }

  /**
   Default function that is called if there is no attribute matching ``data``

   Can be overridden. Defaults to creating a new copy of the tree node (i.e. ``return Tree(data, children, meta)``)

   */
  __default__(data, children, meta) {
    return new Tree(data, children, meta);
  }

  /**
   Default function that is called if there is no attribute matching ``token.type``

   Can be overridden. Defaults to returning the token as-is.

   */
  __default_token__(token) {
    return token;
  }
}

/**
 Same as Transformer, but non-recursive, and changes the tree in-place instead of returning new instances

 Useful for huge trees. Conservative in memory.

 */

class Transformer_InPlace extends Transformer {
  _transform_tree(tree) {
    // Cancel recursion
    return this._call_userfunc(tree);
  }

  transform(tree) {
    for (const subtree of tree.iter_subtrees()) {
      subtree.children = [...this._transform_children(subtree.children)];
    }

    return this._transform_tree(tree);
  }
}

/**
 Same as Transformer but non-recursive.

 Like Transformer, it doesn't change the original tree.

 Useful for huge trees.

 */

class Transformer_NonRecursive extends Transformer {
  transform(tree) {
    let args, res, size;
    // Tree to postfix
    let rev_postfix = [];
    let q = [tree];
    while (q.length) {
      const t = q.pop();
      rev_postfix.push(t);
      if (t instanceof Tree) {
        q.push(...t.children);
      }
    }

    // Postfix to tree
    let stack = [];
    for (const x of [...rev_postfix].reverse()) {
      if (x instanceof Tree) {
        size = x.children.length;
        if (size) {
          args = stack.slice(-size);
          stack.splice(-size);
        } else {
          args = [];
        }
        res = this._call_userfunc(x, args);
        if (res !== Discard) {
          stack.push(res);
        }
      } else if (this.__visit_tokens__ && x instanceof Token) {
        res = this._call_userfunc_token(x);
        if (res !== Discard) {
          stack.push(res);
        }
      } else {
        stack.push(x);
      }
    }

    let [t] = stack;
    // We should have only one tree remaining
    return t;
  }
}

/**
 Same as Transformer, recursive, but changes the tree in-place instead of returning new instances
 */

class Transformer_InPlaceRecursive extends Transformer {
  _transform_tree(tree) {
    tree.children = [...this._transform_children(tree.children)];
    return this._call_userfunc(tree);
  }
}

// Visitors

class VisitorBase {
  _call_userfunc(tree) {
    const callback = this[tree.data];
    if (callback) {
      return callback(tree);
    } else {
      return this.__default__(tree);
    }
  }

  /**
   Default function that is called if there is no attribute matching ``tree.data``

   Can be overridden. Defaults to doing nothing.

   */
  __default__(tree) {
    return tree;
  }

  __class_getitem__(_) {
    return cls;
  }
}

/**
 Tree visitor, non-recursive (can handle huge trees).

 Visiting a node calls its methods (provided by the user via inheritance) according to ``tree.data``

 */

class Visitor extends VisitorBase {
  /**
   Visits the tree, starting with the leaves and finally the root (bottom-up)
   */
  visit(tree) {
    for (const subtree of tree.iter_subtrees()) {
      this._call_userfunc(subtree);
    }

    return tree;
  }

  /**
   Visit the tree, starting at the root, and ending at the leaves (top-down)
   */
  visit_topdown(tree) {
    for (const subtree of tree.iter_subtrees_topdown()) {
      this._call_userfunc(subtree);
    }

    return tree;
  }
}

/**
 Bottom-up visitor, recursive.

 Visiting a node calls its methods (provided by the user via inheritance) according to ``tree.data``

 Slightly faster than the non-recursive version.

 */

class Visitor_Recursive extends VisitorBase {
  /**
   Visits the tree, starting with the leaves and finally the root (bottom-up)
   */
  visit(tree) {
    for (const child of tree.children) {
      if (child instanceof Tree) {
        this.visit(child);
      }
    }

    this._call_userfunc(tree);
    return tree;
  }

  /**
   Visit the tree, starting at the root, and ending at the leaves (top-down)
   */
  visit_topdown(tree) {
    this._call_userfunc(tree);
    for (const child of tree.children) {
      if (child instanceof Tree) {
        this.visit_topdown(child);
      }
    }

    return tree;
  }
}

/**
 Interpreter walks the tree starting at the root.

 Visits the tree, starting with the root and finally the leaves (top-down)

 For each tree node, it calls its methods (provided by user via inheritance) according to ``tree.data``.

 Unlike ``Transformer`` and ``Visitor``, the Interpreter doesn't automatically visit its sub-branches.
 The user has to explicitly call ``visit``, ``visit_children``, or use the ``@visit_children_decor``.
 This allows the user to implement branching and loops.

 */

class Interpreter extends _Decoratable {
  visit(tree) {
    if (tree.data in this) {
      return this[tree.data](tree);
    } else {
      return this.__default__(tree);
    }
  }

  visit_children(tree) {
    return tree.children.map((child) => (child instanceof Tree ? this.visit(child) : child));
  }

  __default__(tree) {
    return this.visit_children(tree);
  }
}

//
// Grammar
//

var TOKEN_DEFAULT_PRIORITY = 0;
class Symbol extends Serialize {
  is_term = NotImplemented;
  constructor(name) {
    super();
    this.name = name;
  }

  eq(other) {
    return this.is_term === other.is_term && this.name === other.name;
  }

  repr() {
    return format("%s(%r)", type(this).name, this.name);
  }

  static get fullrepr() {
    return property(__repr__);
  }
  get fullrepr() {
    return this.constructor.fullrepr;
  }
  renamed(f) {
    return type(this)(f(this.name));
  }
}

class Terminal extends Symbol {
  static get __serialize_fields__() {
    return ["name", "filter_out"];
  }
  get is_term() {
    return true;
  }

  constructor(name, filter_out = false) {
    super();
    this.name = name;
    this.filter_out = filter_out;
  }

  get fullrepr() {
    return format("%s(%r, %r)", type(this).name, this.name, this.filter_out);
  }

  renamed(f) {
    return type(this)(f(this.name), this.filter_out);
  }
}

class NonTerminal extends Symbol {
  static get __serialize_fields__() {
    return ["name"];
  }
  get is_term() {
    return false;
  }
}

class RuleOptions extends Serialize {
  static get __serialize_fields__() {
    return ["keep_all_tokens", "expand1", "priority", "template_source", "empty_indices"];
  }
  constructor(
    keep_all_tokens = false,
    expand1 = false,
    priority = null,
    template_source = null,
    empty_indices = []
  ) {
    super();
    this.keep_all_tokens = keep_all_tokens;
    this.expand1 = expand1;
    this.priority = priority;
    this.template_source = template_source;
    this.empty_indices = empty_indices;
  }

  repr() {
    return format(
      "RuleOptions(%r, %r, %r, %r)",
      this.keep_all_tokens,
      this.expand1,
      this.priority,
      this.template_source
    );
  }
}

/**

 origin : a symbol
 expansion : a list of symbols
 order : index of this expansion amongst all rules of the same name

 */

class Rule extends Serialize {
  static get __serialize_fields__() {
    return ["origin", "expansion", "order", "alias", "options"];
  }
  static get __serialize_namespace__() {
    return [Terminal, NonTerminal, RuleOptions];
  }
  constructor(origin, expansion, order = 0, alias = null, options = null) {
    super();
    this.origin = origin;
    this.expansion = expansion;
    this.alias = alias;
    this.order = order;
    this.options = options || new RuleOptions();
    this._hash = hash([this.origin, tuple(this.expansion)]);
  }

  _deserialize() {
    this._hash = hash([this.origin, tuple(this.expansion)]);
  }

  repr() {
    return format("Rule(%r, %r, %r, %r)", this.origin, this.expansion, this.alias, this.options);
  }

  eq(other) {
    if (!(other instanceof Rule)) {
      return false;
    }

    return this.origin === other.origin && this.expansion === other.expansion;
  }
}

//
// Lexer
//

// Lexer Implementation

class Pattern extends Serialize {
  constructor(value, flags = [], raw = null) {
    super();
    this.value = value;
    this.flags = frozenset(flags);
    this.raw = raw;
  }

  repr() {
    return repr(this.to_regexp());
  }

  eq(other) {
    return type(this) === type(other) && this.value === other.value && this.flags === other.flags;
  }

  to_regexp() {
    throw new NotImplementedError();
  }

  get min_width() {
    throw new NotImplementedError();
  }

  get max_width() {
    throw new NotImplementedError();
  }

  _get_flags(value) {
    return value;
  }
}

class PatternStr extends Pattern {
  static get __serialize_fields__() {
    return ["value", "flags"];
  }
  static get type() {
    return "str";
  }
  to_regexp() {
    return this._get_flags(re.escape(this.value));
  }

  get min_width() {
    return this.value.length;
  }

  get max_width() {
    return this.value.length;
  }
}

class PatternRE extends Pattern {
  static get __serialize_fields__() {
    return ["value", "flags", "_width"];
  }
  static get type() {
    return "re";
  }
  to_regexp() {
    return this._get_flags(this.value);
  }

  _get_width() {
    if (this._width === null) {
      this._width = get_regexp_width(this.to_regexp());
    }

    return this._width;
  }

  get min_width() {
    return this._get_width()[0];
  }

  get max_width() {
    return this._get_width()[1];
  }
}

class TerminalDef extends Serialize {
  static get __serialize_fields__() {
    return ["name", "pattern", "priority"];
  }
  static get __serialize_namespace__() {
    return [PatternStr, PatternRE];
  }
  constructor(name, pattern, priority = TOKEN_DEFAULT_PRIORITY) {
    super();
    this.name = name;
    this.pattern = pattern;
    this.priority = priority;
  }

  repr() {
    return format("%s(%r, %r)", type(this).name, this.name, this.pattern);
  }

  user_repr() {
    if (this.name.startsWith("__")) {
      // We represent a generated terminal
      return this.pattern.raw || this.name;
    } else {
      return this.name;
    }
  }
}

/**
 A string with meta-information, that is produced by the lexer.

 When parsing text, the resulting chunks of the input that haven't been discarded,
 will end up in the tree as Token instances. The Token class inherits from Python's ``str``,
 so normal string comparisons and operations will work as expected.

 Attributes:
 type: Name of the token (as specified in grammar)
 value: Value of the token (redundant, as ``token.value == token`` will always be true)
 start_pos: The index of the token in the text
 line: The line of the token in the text (starting with 1)
 column: The column of the token in the text (starting with 1)
 end_line: The line where the token ends
 end_column: The next column after the end of the token. For example,
 if the token is a single character with a column value of 4,
 end_column will be 5.
 end_pos: the index where the token ends (basically ``start_pos + len(token)``)

 */

class Token {
  constructor(
    type_,
    value,
    start_pos = null,
    line = null,
    column = null,
    end_line = null,
    end_column = null,
    end_pos = null
  ) {
    this.type = type_;
    this.start_pos = start_pos;
    this.value = value;
    this.line = line;
    this.column = column;
    this.end_line = end_line;
    this.end_column = end_column;
    this.end_pos = end_pos;
  }

  update(type_ = null, value = null) {
    return Token.new_borrow_pos(
      type_ !== null ? type_ : this.type,
      value !== null ? value : this.value,
      this
    );
  }

  static new_borrow_pos(type_, value, borrow_t) {
    const cls = this;
    return new cls(
      type_,
      value,
      borrow_t.start_pos,
      borrow_t.line,
      borrow_t.column,
      borrow_t.end_line,
      borrow_t.end_column,
      borrow_t.end_pos
    );
  }

  repr() {
    return format("Token(%r, %r)", this.type, this.value);
  }

  eq(other) {
    if (other instanceof Token && this.type !== other.type) {
      return false;
    }

    return str.__eq__(this, other);
  }

  static get __hash__() {
    return str.__hash__;
  }
}

class LineCounter {
  constructor(newline_char) {
    this.newline_char = newline_char;
    this.char_pos = 0;
    this.line = 1;
    this.column = 1;
    this.line_start_pos = 0;
  }

  eq(other) {
    if (!(other instanceof LineCounter)) {
      return NotImplemented;
    }

    return this.char_pos === other.char_pos && this.newline_char === other.newline_char;
  }

  /**
   Consume a token and calculate the new line & column.

   As an optional optimization, set test_newline=False if token doesn't contain a newline.

   */
  feed(token, test_newline = true) {
    let newlines;
    if (test_newline) {
      newlines = str_count(token, this.newline_char);
      if (newlines) {
        this.line += newlines;
        this.line_start_pos = this.char_pos + token.lastIndexOf(this.newline_char) + 1;
      }
    }

    this.char_pos += token.length;
    this.column = this.char_pos - this.line_start_pos + 1;
  }
}

class _UnlessCallback {
  constructor(scanner) {
    this.scanner = scanner;
  }

  __call__(t) {
    let _value;
    let res = this.scanner.match(t.value, 0);
    if (res) {
      [_value, t.type] = res;
    }

    return t;
  }
}

const UnlessCallback = callable_class(_UnlessCallback);
class _CallChain {
  constructor(callback1, callback2, cond) {
    this.callback1 = callback1;
    this.callback2 = callback2;
    this.cond = cond;
  }

  __call__(t) {
    let t2 = this.callback1(t);
    return this.cond(t2) ? this.callback2(t) : t2;
  }
}

const CallChain = callable_class(_CallChain);
function _create_unless(terminals, g_regex_flags, re_, use_bytes) {
  let s, unless;
  let tokens_by_type = classify(terminals, (t) => t.pattern.constructor.type);
  let embedded_strs = new Set();
  let callback = {};
  for (const retok of tokens_by_type.get("re") || []) {
    unless = [];
    for (const strtok of tokens_by_type.get("str") || []) {
      if (strtok.priority !== retok.priority) {
        continue;
      }

      s = strtok.pattern.value;
      if (s === _get_match(re_, retok.pattern.to_regexp(), s, g_regex_flags)) {
        unless.push(strtok);
        if (isSubset(new Set(strtok.pattern.flags), new Set(retok.pattern.flags))) {
          embedded_strs.add(strtok);
        }
      }
    }

    if (unless.length) {
      callback[retok.name] = new UnlessCallback(
        new Scanner(unless, g_regex_flags, re_, use_bytes, true)
      );
    }
  }

  let new_terminals = terminals.filter((t) => !embedded_strs.has(t)).map((t) => t);
  return [new_terminals, callback];
}

/**
 Expressions that may indicate newlines in a regexp:
 - newlines (\n)
 - escaped newline (\\n)
 - anything but ([^...])
 - any-char (.) when the flag (?s) exists
 - spaces (\s)

 */
function _regexp_has_newline(r) {
  return (
    r.includes("\n") ||
    r.includes("\\n") ||
    r.includes("\\s") ||
    r.includes("[^") ||
    (r.includes("(?s") && r.includes("."))
  );
}

/**
 Represents the current state of the lexer as it scans the text
 (Lexer objects are only instanciated per grammar, not per text)

 */

class LexerState {
  constructor(text, line_ctr = null, last_token = null) {
    this.text = text;
    this.line_ctr = line_ctr || new LineCounter("\n");
    this.last_token = last_token;
  }

  eq(other) {
    if (!(other instanceof LexerState)) {
      return NotImplemented;
    }

    return (
      this.text === other.text &&
      this.line_ctr === other.line_ctr &&
      this.last_token === other.last_token
    );
  }
}

/**
 A thread that ties a lexer instance and a lexer state, to be used by the parser

 */

class LexerThread {
  constructor(lexer, lexer_state) {
    this.lexer = lexer;
    this.state = lexer_state;
  }

  static from_text(lexer, text) {
    return new this(lexer, new LexerState(text));
  }

  lex(parser_state) {
    return this.lexer.lex(this.state, parser_state);
  }
}

/**
 Lexer interface

 Method Signatures:
 lex(self, lexer_state, parser_state) -> Iterator[Token]

 */

class Lexer extends ABC {
  lex(lexer_state, parser_state) {
    return NotImplemented;
  }
}

function sort_by_key_tuple(arr, key) {
  arr.sort((a, b) => {
    let ta = key(a);
    let tb = key(b);
    for (let i = 0; i < ta.length; i++) {
      if (ta[i] > tb[i]) {
        return 1;
      } else if (ta[i] < tb[i]) {
        return -1;
      }
    }
    return 0;
  });
}

class BasicLexer extends Lexer {
  constructor(conf) {
    super();
    let terminals = [...conf.terminals];
    this.re = conf.re_module;
    if (!conf.skip_validation) {
      // Sanitization
      for (const t of terminals) {
        try {
          this.re.compile(t.pattern.to_regexp(), conf.g_regex_flags);
        } catch (e) {
          if (e instanceof this.re.error) {
            throw new LexError(format("Cannot compile token %s: %s", t.name, t.pattern));
          } else {
            throw e;
          }
        }
        if (t.pattern.min_width === 0) {
          throw new LexError(
            format("Lexer does not allow zero-width terminals. (%s: %s)", t.name, t.pattern)
          );
        }
      }

      if (!(new Set(conf.ignore) <= new Set(terminals.map((t) => t.name)))) {
        throw new LexError(
          format(
            "Ignore terminals are not defined: %s",
            set_subtract(new Set(conf.ignore), new Set(terminals.map((t) => t.name)))
          )
        );
      }
    }

    // Init
    this.newline_types = frozenset(
      terminals.filter((t) => _regexp_has_newline(t.pattern.to_regexp())).map((t) => t.name)
    );
    this.ignore_types = frozenset(conf.ignore);
    sort_by_key_tuple(terminals, (x) => [
      -x.priority,
      -x.pattern.max_width,
      -x.pattern.value.length,
      x.name,
    ]);
    this.terminals = terminals;
    this.user_callbacks = conf.callbacks;
    this.g_regex_flags = conf.g_regex_flags;
    this.use_bytes = conf.use_bytes;
    this.terminals_by_name = conf.terminals_by_name;
    this._scanner = null;
  }

  _build_scanner() {
    let terminals;
    [terminals, this.callback] = _create_unless(
      this.terminals,
      this.g_regex_flags,
      this.re,
      this.use_bytes
    );
    for (const [type_, f] of dict_items(this.user_callbacks)) {
      if (type_ in this.callback) {
        // Already a callback there, probably UnlessCallback
        this.callback[type_] = new CallChain(this.callback[type_], f, (t) => t.type === type_);
      } else {
        this.callback[type_] = f;
      }
    }

    this._scanner = new Scanner(terminals, this.g_regex_flags, this.re, this.use_bytes);
  }

  get scanner() {
    if (this._scanner === null) {
      this._build_scanner();
    }

    return this._scanner;
  }

  match(text, pos) {
    return this.scanner.match(text, pos);
  }

  *lex(state, parser_state) {
    try {
      while (true) {
        yield this.next_token(state, parser_state);
      }
    } catch (e) {
      if (e instanceof EOFError) {
        // pass
      } else {
        throw e;
      }
    }
  }

  next_token(lex_state, parser_state = null) {
    let allowed, res, t, t2, type_, value;
    let line_ctr = lex_state.line_ctr;
    while (line_ctr.char_pos < lex_state.text.length) {
      res = this.match(lex_state.text, line_ctr.char_pos);
      if (!res) {
        allowed = set_subtract(this.scanner.allowed_types, this.ignore_types);
        if (!allowed) {
          allowed = new Set(["<END-OF-FILE>"]);
        }

        throw new UnexpectedCharacters({
          seq: lex_state.text,
          lex_pos: line_ctr.char_pos,
          line: line_ctr.line,
          column: line_ctr.column,
          allowed: allowed,
          token_history: lex_state.last_token && [lex_state.last_token],
          state: parser_state,
          terminals_by_name: this.terminals_by_name,
        });
      }

      let [value, type_] = res;
      if (!this.ignore_types.has(type_)) {
        t = new Token(type_, value, line_ctr.char_pos, line_ctr.line, line_ctr.column);
        line_ctr.feed(value, this.newline_types.has(type_));
        t.end_line = line_ctr.line;
        t.end_column = line_ctr.column;
        t.end_pos = line_ctr.char_pos;
        if (t.type in this.callback) {
          t = this.callback[t.type](t);
          if (!(t instanceof Token)) {
            throw new LexError(format("Callbacks must return a token (returned %r)", t));
          }
        }

        lex_state.last_token = t;
        return t;
      } else {
        if (type_ in this.callback) {
          t2 = new Token(type_, value, line_ctr.char_pos, line_ctr.line, line_ctr.column);
          this.callback[type_](t2);
        }

        line_ctr.feed(value, this.newline_types.has(type_));
      }
    }

    // EOF
    throw new EOFError(this);
  }
}

class ContextualLexer extends Lexer {
  constructor({ conf, states, always_accept = [] } = {}) {
    super();
    let accepts, key, lexer, lexer_conf;
    let terminals = [...conf.terminals];
    let terminals_by_name = conf.terminals_by_name;
    let trad_conf = copy(conf);
    trad_conf.terminals = terminals;
    let lexer_by_tokens = new Map();
    this.lexers = {};
    for (let [state, accepts] of dict_items(states)) {
      key = frozenset(accepts);
      if (lexer_by_tokens.has(key)) {
        lexer = lexer_by_tokens.get(key);
      } else {
        accepts = union(new Set(accepts), [...new Set(conf.ignore), ...new Set(always_accept)]);
        lexer_conf = copy(trad_conf);
        lexer_conf.terminals = [...accepts]
          .filter((n) => n in terminals_by_name)
          .map((n) => terminals_by_name[n]);
        lexer = new BasicLexer(lexer_conf);
        lexer_by_tokens.set(key, lexer);
      }
      this.lexers[state] = lexer;
    }

    this.root_lexer = new BasicLexer(trad_conf);
  }

  *lex(lexer_state, parser_state) {
    let last_token, lexer, token;
    try {
      while (true) {
        lexer = this.lexers[parser_state.position];
        yield lexer.next_token(lexer_state, parser_state);
      }
    } catch (e) {
      if (e instanceof EOFError) {
        // pass
      } else if (e instanceof UnexpectedCharacters) {
        // In the contextual lexer, UnexpectedCharacters can mean that the terminal is defined, but not in the current context.
        // This tests the input against the global context, to provide a nicer error.
        try {
          last_token = lexer_state.last_token;
          // Save last_token. Calling root_lexer.next_token will change this to the wrong token
          token = this.root_lexer.next_token(lexer_state, parser_state);
          throw new UnexpectedToken({
            token: token,
            expected: e.allowed,
            state: parser_state,
            token_history: [last_token],
            terminals_by_name: this.root_lexer.terminals_by_name,
          });
        } catch (e) {
          if (e instanceof UnexpectedCharacters) {
            throw e;
          } else {
            throw e;
          }
        }
      } else {
        throw e;
      }
    }
  }
}

//
// Common
//

class LexerConf extends Serialize {
  static get __serialize_fields__() {
    return ["terminals", "ignore", "g_regex_flags", "use_bytes", "lexer_type"];
  }
  static get __serialize_namespace__() {
    return [TerminalDef];
  }
  constructor({
    terminals,
    re_module,
    ignore = [],
    postlex = null,
    callbacks = null,
    g_regex_flags = "",
    skip_validation = false,
    use_bytes = false,
  } = {}) {
    super();
    this.terminals = terminals;
    this.terminals_by_name = Object.fromEntries(this.terminals.map((t) => [t.name, t]));
    this.ignore = ignore;
    this.postlex = postlex;
    this.callbacks = Object.keys(callbacks).length || {};
    this.g_regex_flags = g_regex_flags;
    this.re_module = re_module;
    this.skip_validation = skip_validation;
    this.use_bytes = use_bytes;
    this.lexer_type = null;
  }

  _deserialize() {
    this.terminals_by_name = Object.fromEntries(this.terminals.map((t) => [t.name, t]));
  }
}

class ParserConf extends Serialize {
  static get __serialize_fields__() {
    return ["rules", "start", "parser_type"];
  }
  constructor(rules, callbacks, start) {
    super();
    this.rules = rules;
    this.callbacks = callbacks;
    this.start = start;
    this.parser_type = null;
  }
}

//
// Parse Tree Builder
//

class _ExpandSingleChild {
  constructor(node_builder) {
    this.node_builder = node_builder;
  }

  __call__(children) {
    if (children.length === 1) {
      return children[0];
    } else {
      return this.node_builder(children);
    }
  }
}

const ExpandSingleChild = callable_class(_ExpandSingleChild);
class _PropagatePositions {
  constructor(node_builder, node_filter = null) {
    this.node_builder = node_builder;
    this.node_filter = node_filter;
  }

  __call__(children) {
    let first_meta, last_meta, res_meta;
    let res = this.node_builder(children);
    if (res instanceof Tree) {
      // Calculate positions while the tree is streaming, according to the rule:
      // - nodes start at the start of their first child's container,
      //   and end at the end of their last child's container.
      // Containers are nodes that take up space in text, but have been inlined in the tree.

      res_meta = res.meta;
      first_meta = this._pp_get_meta(children);
      if (first_meta !== null) {
        if (!("line" in res_meta)) {
          // meta was already set, probably because the rule has been inlined (e.g. `?rule`)
          res_meta.line = (first_meta && first_meta["container_line"]) || first_meta.line;
          res_meta.column = (first_meta && first_meta["container_column"]) || first_meta.column;
          res_meta.start_pos =
            (first_meta && first_meta["container_start_pos"]) || first_meta.start_pos;
          res_meta.empty = false;
        }

        res_meta.container_line = (first_meta && first_meta["container_line"]) || first_meta.line;
        res_meta.container_column =
          (first_meta && first_meta["container_column"]) || first_meta.column;
      }

      last_meta = this._pp_get_meta([...children].reverse());
      if (last_meta !== null) {
        if (!("end_line" in res_meta)) {
          res_meta.end_line = (last_meta && last_meta["container_end_line"]) || last_meta.end_line;
          res_meta.end_column =
            (last_meta && last_meta["container_end_column"]) || last_meta.end_column;
          res_meta.end_pos = (last_meta && last_meta["container_end_pos"]) || last_meta.end_pos;
          res_meta.empty = false;
        }

        res_meta.container_end_line =
          (last_meta && last_meta["container_end_line"]) || last_meta.end_line;
        res_meta.container_end_column =
          (last_meta && last_meta["container_end_column"]) || last_meta.end_column;
      }
    }

    return res;
  }

  _pp_get_meta(children) {
    for (const c of children) {
      if (this.node_filter !== null && !this.node_filter(c)) {
        continue;
      }

      if (c instanceof Tree) {
        if (!c.meta.empty) {
          return c.meta;
        }
      } else if (c instanceof Token) {
        return c;
      }
    }
  }
}

const PropagatePositions = callable_class(_PropagatePositions);
function make_propagate_positions(option) {
  if (callable(option)) {
    return partial({
      unknown_param_0: PropagatePositions,
      node_filter: option,
    });
  } else if (option === true) {
    return PropagatePositions;
  } else if (option === false) {
    return null;
  }

  throw new ConfigurationError(format("Invalid option for propagate_positions: %r", option));
}

class _ChildFilter {
  constructor(to_include, append_none, node_builder) {
    this.node_builder = node_builder;
    this.to_include = to_include;
    this.append_none = append_none;
  }

  __call__(children) {
    let filtered = [];
    for (const [i, to_expand, add_none] of this.to_include) {
      if (add_none) {
        filtered.push(...list_repeat([null], add_none));
      }

      if (to_expand) {
        filtered.push(...children[i].children);
      } else {
        filtered.push(children[i]);
      }
    }

    if (this.append_none) {
      filtered.push(...list_repeat([null], this.append_none));
    }

    return this.node_builder(filtered);
  }
}

const ChildFilter = callable_class(_ChildFilter);
/**
 Optimized childfilter for LALR (assumes no duplication in parse tree, so it's safe to change it)
 */

class _ChildFilterLALR extends _ChildFilter {
  __call__(children) {
    let filtered = [];
    for (const [i, to_expand, add_none] of this.to_include) {
      if (add_none) {
        filtered.push(...list_repeat([null], add_none));
      }

      if (to_expand) {
        if (filtered.length) {
          filtered.push(...children[i].children);
        } else {
          // Optimize for left-recursion
          filtered = children[i].children;
        }
      } else {
        filtered.push(children[i]);
      }
    }

    if (this.append_none) {
      filtered.push(...list_repeat([null], this.append_none));
    }

    return this.node_builder(filtered);
  }
}

const ChildFilterLALR = callable_class(_ChildFilterLALR);
/**
 Optimized childfilter for LALR (assumes no duplication in parse tree, so it's safe to change it)
 */

class _ChildFilterLALR_NoPlaceholders extends _ChildFilter {
  constructor(to_include, node_builder) {
    super();
    this.node_builder = node_builder;
    this.to_include = to_include;
  }

  __call__(children) {
    let filtered = [];
    for (const [i, to_expand] of this.to_include) {
      if (to_expand) {
        if (filtered.length) {
          filtered.push(...children[i].children);
        } else {
          // Optimize for left-recursion
          filtered = children[i].children;
        }
      } else {
        filtered.push(children[i]);
      }
    }

    return this.node_builder(filtered);
  }
}

const ChildFilterLALR_NoPlaceholders = callable_class(_ChildFilterLALR_NoPlaceholders);
function _should_expand(sym) {
  return !sym.is_term && sym.name.startsWith("_");
}

function maybe_create_child_filter(expansion, keep_all_tokens, ambiguous, _empty_indices) {
  let empty_indices, s;
  // Prepare empty_indices as: How many Nones to insert at each index?
  if (_empty_indices.length) {
    s = _empty_indices.map((b) => (0 + b).toString()).join("");
    empty_indices = s.split("0").map((ones) => ones.length);
  } else {
    empty_indices = list_repeat([0], expansion.length + 1);
  }
  let to_include = [];
  let nones_to_add = 0;
  for (const [i, sym] of enumerate(expansion)) {
    nones_to_add += empty_indices[i];
    if (keep_all_tokens || !(sym.is_term && sym.filter_out)) {
      to_include.push([i, _should_expand(sym), nones_to_add]);
      nones_to_add = 0;
    }
  }

  nones_to_add += empty_indices[expansion.length];
  if (
    _empty_indices.length ||
    to_include.length < expansion.length ||
    any(to_include.map(([i, to_expand, _]) => to_expand))
  ) {
    if ((_empty_indices.length || ambiguous).length) {
      return partial(ambiguous ? ChildFilter : ChildFilterLALR, to_include, nones_to_add);
    } else {
      // LALR without placeholders
      return partial(
        ChildFilterLALR_NoPlaceholders,
        to_include.map(([i, x, _]) => [i, x])
      );
    }
  }
}

/**

 Propagate ambiguous intermediate nodes and their derivations up to the
 current rule.

 In general, converts

 rule
 _iambig
 _inter
 someChildren1
 ...
 _inter
 someChildren2
 ...
 someChildren3
 ...

 to

 _ambig
 rule
 someChildren1
 ...
 someChildren3
 ...
 rule
 someChildren2
 ...
 someChildren3
 ...
 rule
 childrenFromNestedIambigs
 ...
 someChildren3
 ...
 ...

 propagating up any nested '_iambig' nodes along the way.

 */

function inplace_transformer(func) {
  function f(children) {
    // function name in a Transformer is a rule name.
    let tree = new Tree(func.name, children);
    return func(tree);
  }

  f = wraps(func)(f);
  return f;
}

function apply_visit_wrapper(func, name, wrapper) {
  if (wrapper === _vargs_meta || wrapper === _vargs_meta_inline) {
    throw new NotImplementedError("Meta args not supported for internal transformer");
  }

  function f(children) {
    return wrapper(func, name, children, null);
  }

  f = wraps(func)(f);
  return f;
}

class ParseTreeBuilder {
  constructor(
    rules,
    tree_class,
    propagate_positions = false,
    ambiguous = false,
    maybe_placeholders = false
  ) {
    this.tree_class = tree_class;
    this.propagate_positions = propagate_positions;
    this.ambiguous = ambiguous;
    this.maybe_placeholders = maybe_placeholders;
    this.rule_builders = [...this._init_builders(rules)];
  }

  *_init_builders(rules) {
    let expand_single_child, keep_all_tokens, options, wrapper_chain;
    let propagate_positions = make_propagate_positions(this.propagate_positions);
    for (const rule of rules) {
      options = rule.options;
      keep_all_tokens = options.keep_all_tokens;
      expand_single_child = options.expand1;
      wrapper_chain = [
        ...filter(null, [
          expand_single_child && !rule.alias && ExpandSingleChild,
          maybe_create_child_filter(
            rule.expansion,
            keep_all_tokens,
            this.ambiguous,
            this.maybe_placeholders ? options.empty_indices : []
          ),
          propagate_positions,
        ]),
      ];
      yield [rule, wrapper_chain];
    }
  }

  create_callback(transformer = null) {
    let f, user_callback_name, wrapper;
    let callbacks = new Map();
    for (const [rule, wrapper_chain] of this.rule_builders) {
      user_callback_name = rule.alias || rule.options.template_source || rule.origin.name;
      if (transformer && transformer[user_callback_name]) {
        f = transformer && transformer[user_callback_name];
        wrapper = (f && f["visit_wrapper"]) || null;
        if (wrapper !== null) {
          f = apply_visit_wrapper(f, user_callback_name, wrapper);
        } else if (transformer instanceof Transformer_InPlace) {
          f = inplace_transformer(f);
        }
      } else {
        f = partial(this.tree_class, user_callback_name);
      }
      for (const w of wrapper_chain) {
        f = w(f);
      }

      if (callbacks.has(rule)) {
        throw new GrammarError(format("Rule '%s' already exists", rule));
      }

      callbacks.set(rule, f);
    }

    return callbacks;
  }
}

//
// Lalr Parser
//

class LALR_Parser extends Serialize {
  constructor({ parser_conf, debug = false } = {}) {
    super();
    let analysis = new LALR_Analyzer({
      unknown_param_0: parser_conf,
      debug: debug,
    });
    analysis.compute_lalr();
    let callbacks = parser_conf.callbacks;
    this._parse_table = analysis.parse_table;
    this.parser_conf = parser_conf;
    this.parser = new _Parser(analysis.parse_table, callbacks, debug);
  }

  static deserialize(data, memo, callbacks, debug = false) {
    const cls = this;
    let inst = new_object(cls);
    inst._parse_table = IntParseTable.deserialize(data, memo);
    inst.parser = new _Parser(inst._parse_table, callbacks, debug);
    return inst;
  }

  serialize(memo) {
    return this._parse_table.serialize(memo);
  }

  parse_interactive(lexer, start) {
    return this.parser.parse({
      lexer: lexer,
      start: start,
      start_interactive: true,
    });
  }

  parse({ lexer, start, on_error = null } = {}) {
    let e, p, s;
    try {
      return this.parser.parse({ lexer: lexer, start: start });
    } catch (e) {
      if (e instanceof UnexpectedInput) {
        if (on_error === null) {
          throw e;
        }

        while (true) {
          if (e instanceof UnexpectedCharacters) {
            s = e.interactive_parser.lexer_thread.state;
            p = s.line_ctr.char_pos;
          }

          if (!on_error(e)) {
            throw e;
          }

          if (e instanceof UnexpectedCharacters) {
            // If user didn't change the character position, then we should
            if (p === s.line_ctr.char_pos) {
              s.line_ctr.feed(s.text.slice(p, p + 1));
            }
          }

          try {
            return e.interactive_parser.resume_parse();
          } catch (e2) {
            if (e2 instanceof UnexpectedToken) {
              if (
                e instanceof UnexpectedToken &&
                e.token.type === e2.token.type &&
                e2.token.type === "$END" &&
                e.interactive_parser.eq(e2.interactive_parser)
              ) {
                // Prevent infinite loop
                throw e2;
              }

              e = e2;
            } else if (e2 instanceof UnexpectedCharacters) {
              e = e2;
            } else {
              throw e2;
            }
          }
        }
      } else {
        throw e;
      }
    }
  }
}

class ParseConf {
  constructor(parse_table, callbacks, start) {
    this.parse_table = parse_table;
    this.start_state = this.parse_table.start_states[start];
    this.end_state = this.parse_table.end_states[start];
    this.states = this.parse_table.states;
    this.callbacks = callbacks;
    this.start = start;
  }
}

class ParserState {
  constructor(parse_conf, lexer, state_stack = null, value_stack = null) {
    this.parse_conf = parse_conf;
    this.lexer = lexer;
    this.state_stack = state_stack || [this.parse_conf.start_state];
    this.value_stack = value_stack || [];
  }

  get position() {
    return last_item(this.state_stack);
  }

  // Necessary for match_examples() to work

  eq(other) {
    if (!(other instanceof ParserState)) {
      return NotImplemented;
    }

    return this.state_stack.length === other.state_stack.length && this.position === other.position;
  }

  copy() {
    return copy(this);
  }

  feed_token(token, is_end = false) {
    let _action, action, arg, expected, new_state, rule, s, size, state, value;
    let state_stack = this.state_stack;
    let value_stack = this.value_stack;
    let states = this.parse_conf.states;
    let end_state = this.parse_conf.end_state;
    let callbacks = this.parse_conf.callbacks;
    while (true) {
      state = last_item(state_stack);
      if (token.type in states[state]) {
        [action, arg] = states[state][token.type];
      } else {
        expected = new Set(
          dict_keys(states[state])
            .filter((s) => isupper(s))
            .map((s) => s)
        );
        throw new UnexpectedToken({
          token: token,
          expected: expected,
          state: this,
          interactive_parser: null,
        });
      }
      if (action === Shift) {
        // shift once and return

        state_stack.push(arg);
        value_stack.push(!(token.type in callbacks) ? token : callbacks[token.type](token));
        return;
      } else {
        // reduce+shift as many times as necessary
        rule = arg;
        size = rule.expansion.length;
        if (size) {
          s = value_stack.slice(-size);
          state_stack.splice(-size);
          value_stack.splice(-size);
        } else {
          s = [];
        }
        value = callbacks.get(rule)(s);
        [_action, new_state] = states[last_item(state_stack)][rule.origin.name];
        state_stack.push(new_state);
        value_stack.push(value);
        if (is_end && last_item(state_stack) === end_state) {
          return last_item(value_stack);
        }
      }
    }
  }
}

class _Parser {
  constructor(parse_table, callbacks, debug = false) {
    this.parse_table = parse_table;
    this.callbacks = callbacks;
    this.debug = debug;
  }

  parse({ lexer, start, value_stack = null, state_stack = null, start_interactive = false } = {}) {
    let parse_conf = new ParseConf(this.parse_table, this.callbacks, start);
    let parser_state = new ParserState(parse_conf, lexer, state_stack, value_stack);
    if (start_interactive) {
      return new InteractiveParser(this, parser_state, parser_state.lexer);
    }

    return this.parse_from_state(parser_state);
  }

  parse_from_state(state) {
    let end_token, token;
    // Main LALR-parser loop
    try {
      token = null;
      for (token of state.lexer.lex(state)) {
        state.feed_token(token);
      }

      end_token = token ? Token.new_borrow_pos("$END", "", token) : new Token("$END", "", 0, 1, 1);
      return state.feed_token(end_token, true);
    } catch (e) {
      if (e instanceof UnexpectedInput) {
        try {
          e.interactive_parser = new InteractiveParser(this, state, state.lexer);
        } catch (e) {
          if (e instanceof ReferenceError) {
            // pass
          } else {
            throw e;
          }
        }
        throw e;
      } else if (e instanceof Error) {
        if (this.debug) {
          console.log("");
          console.log("STATE STACK DUMP");
          console.log("----------------");
          for (const [i, s] of enumerate(state.state_stack)) {
            console.log(format("%d)", i), s);
          }

          console.log("");
        }

        throw e;
      } else {
        throw e;
      }
    }
  }
}

//
// Lalr Interactive Parser
//

// This module provides a LALR interactive parser, which is used for debugging and error handling

/**
 InteractiveParser gives you advanced control over parsing and error handling when parsing with LALR.

 For a simpler interface, see the ``on_error`` argument to ``Lark.parse()``.

 */

class InteractiveParser {
  constructor(parser, parser_state, lexer_thread) {
    this.parser = parser;
    this.parser_state = parser_state;
    this.lexer_thread = lexer_thread;
    this.result = null;
  }

  /**
   Feed the parser with a token, and advance it to the next state, as if it received it from the lexer.

   Note that ``token`` has to be an instance of ``Token``.

   */
  feed_token(token) {
    return this.parser_state.feed_token(token, token.type === "$END");
  }

  /**
   Step through the different stages of the parse, by reading tokens from the lexer
   and feeding them to the parser, one per iteration.

   Returns an iterator of the tokens it encounters.

   When the parse is over, the resulting tree can be found in ``InteractiveParser.result``.

   */
  *iter_parse() {
    for (const token of this.lexer_thread.lex(this.parser_state)) {
      yield token;
      this.result = this.feed_token(token);
    }
  }

  /**
   Try to feed the rest of the lexer state into the interactive parser.

   Note that this modifies the instance in place and does not feed an '$END' Token

   */
  exhaust_lexer() {
    return [...this.iter_parse()];
  }

  /**
   Feed a '$END' Token. Borrows from 'last_token' if given.
   */
  feed_eof(last_token = null) {
    let eof =
      last_token !== null
        ? Token.new_borrow_pos("$END", "", last_token)
        : new Token("$END", "", 0, 1, 1);
    return this.feed_token(eof);
  }

  copy() {
    return copy(this);
  }

  eq(other) {
    if (!(other instanceof InteractiveParser)) {
      return false;
    }

    return this.parser_state === other.parser_state && this.lexer_thread === other.lexer_thread;
  }

  /**
   Convert to an ``ImmutableInteractiveParser``.
   */
  as_immutable() {
    let p = copy(this);
    return new Immut
Download .txt
gitextract_tvvntrbx/

├── .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/
│   │   │           ├── alternatePromptToString.ts
│   │   │           ├── emphasizedPromptToString.ts
│   │   │           ├── extraNetworksPromptToString.ts
│   │   │           ├── index.ts
│   │   │           ├── plainPromptToString.ts
│   │   │           ├── promptCombinationToString.ts
│   │   │           └── scheduledPromptToString.ts
│   │   └── 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
├── index.html
├── javascript/
│   └── betterPrompt.js
├── locales/
│   └── ja_JP.json
├── package.json
├── parser-src/
│   └── prompt-parser.lark
├── postcss.config.cjs
├── scripts/
│   └── better_prompt.py
├── style.css
├── svelte.config.js
├── tailwind.config.cjs
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
Download .txt
SYMBOL INDEX (1377 symbols across 90 files)

FILE: client-src/@types/global.d.ts
  type ExtensionAvailableTab (line 1) | type ExtensionAvailableTab = "txt2img" | "img2img";
  type WebUiTab (line 2) | type WebUiTab = ExtensionAvailableTab | "other";
  type Nullable (line 4) | type Nullable<T> = T | null | undefined;
  type Callback (line 5) | type Callback = () => unknown;
  type Callback1 (line 6) | type Callback1<T> = (arg1: T) => unknown;
  type OneOrMany (line 8) | type OneOrMany<T> = T | T[];

FILE: client-src/@types/modules.ts
  type ToastType (line 4) | type ToastType = "info" | "success" | "warning" | "error";
  type ToastMessage (line 6) | type ToastMessage = {

FILE: client-src/@types/webui.d.ts
  type OptionValue (line 22) | type OptionValue = string | number | boolean;

FILE: client-src/components/better-prompt/_logic/adjustPrompt.ts
  function adjustPrompt (line 4) | function adjustPrompt(prompt: Prompt, textualInversions: ExtraNetworksDa...

FILE: client-src/components/better-prompt/_logic/betterPrompt.ts
  function createLoraReadable (line 6) | function createLoraReadable(tabName: ExtensionAvailableTab): Readable<Ex...
  function createTextualInversionReadable (line 10) | function createTextualInversionReadable(
  function createReadable (line 16) | function createReadable(selector: string, type: ExtraNetworksType): Read...

FILE: client-src/components/better-prompt/_logic/context.ts
  type BetterPromptContext (line 4) | type BetterPromptContext = {

FILE: client-src/components/better-prompt/_logic/danbooruTags.ts
  function initDanbooruTags (line 9) | function initDanbooruTags(tags: DanbooruTag[]): void {

FILE: client-src/components/better-prompt/_logic/extraNetworks.ts
  function initLora (line 15) | function initLora(tags: ExtraNetworksData[]): void {
  function initTextualInversion (line 21) | function initTextualInversion(tags: ExtraNetworksData[]): void {

FILE: client-src/components/better-prompt/_logic/messages.ts
  type Message (line 3) | interface Message {
  class MessageImpl (line 7) | class MessageImpl implements Message {
    method constructor (line 11) | constructor(key: string, defaultValue: string) {
    method translate (line 16) | translate(args?: Array<string | number | boolean>): string {

FILE: client-src/components/better-prompt/_logic/myPrompts.ts
  function initMyPrompts (line 18) | function initMyPrompts(myPrompts: MyPrompt[]): void {
  function addMyPrompt (line 26) | function addMyPrompt(myPrompt: MyPrompt): void {
  function removeMyPrompts (line 37) | function removeMyPrompts(myPrompts: MyPrompt[]): void {

FILE: client-src/components/better-prompt/_logic/prompt.ts
  function getTextContent (line 11) | function getTextContent(prompt: Prompt): string {
  function promptToText (line 15) | function promptToText(prompt: Prompt): string {
  function extraNetworksPromptToText (line 37) | function extraNetworksPromptToText(prompt: ExtraNetworksPrompt): string {
  function createDataset (line 46) | function createDataset(prompt: Prompt): object {

FILE: client-src/components/better-prompt/prompt-edit/_logic/purgeEmphasizedPrompt.ts
  function purgeEmphasizedPrompt (line 7) | function purgeEmphasizedPrompt(

FILE: client-src/components/better-prompt/prompt-edit/_logic/undoRedo.ts
  class History (line 5) | class History<T> {
    method constructor (line 9) | constructor(private readonly maxLength: number) {}
    method current (line 11) | get current(): Nullable<T> {
    method canUndo (line 16) | get canUndo(): boolean {
    method canRedo (line 20) | get canRedo(): boolean {
    method undo (line 24) | undo(): Nullable<T> {
    method redo (line 30) | redo(): Nullable<T> {
    method push (line 36) | push(value: T): void {
  type PromptHistory (line 48) | type PromptHistory = {
  function initUndoRedo (line 59) | function initUndoRedo(args: {
  function undoRedo (line 94) | function undoRedo(event: KeyboardEvent): void {

FILE: client-src/components/better-prompt/prompt-edit/editor/_logic/context.ts
  type EditorContext (line 4) | type EditorContext = {

FILE: client-src/components/better-prompt/prompt-edit/editor/prompt-list/_logic/listItem.ts
  function isPopupEnabled (line 3) | function isPopupEnabled(prompt: Prompt): boolean {

FILE: client-src/components/better-prompt/prompt-edit/editor/prompt-list/_logic/promptList.ts
  function promptIdentifier (line 5) | function promptIdentifier(index: number, prompt: Prompt): string {
  function scheduledIdentifier (line 24) | function scheduledIdentifier(index: number, prompt: ScheduledPrompt): st...

FILE: client-src/components/better-prompt/prompt-edit/editor/prompt-list/_logic/weightInput.ts
  function getWeight (line 6) | function getWeight(prompt: Prompt): number {
  function getPositiveWeight (line 23) | function getPositiveWeight(prompt: EmphasizedPositivePrompt, weight: num...
  function getNegativeWeight (line 31) | function getNegativeWeight(prompt: EmphasizedNegativePrompt, weight: num...
  function updateWeight (line 39) | function updateWeight(prompt: Prompt, weight: number): Prompt {

FILE: client-src/components/better-prompt/prompt-edit/input/_logic/context.ts
  type PseudoFocusMoveDirection (line 3) | type PseudoFocusMoveDirection = "next" | "previous" | "up" | "down" | "r...
  type PromptInputContext (line 5) | type PromptInputContext = {

FILE: client-src/components/better-prompt/prompt-edit/input/suggest/_logic/filters.ts
  type FilterType (line 11) | type FilterType =
  function typeToLabel (line 30) | function typeToLabel(type: FilterType): string {

FILE: client-src/components/better-prompt/prompt-edit/input/suggest/_logic/suggest.ts
  type SuggestDataType (line 5) | type SuggestDataType = "extra-networks" | "danbooru" | "my-prompt";
  type AnySuggestData (line 7) | interface AnySuggestData {
  type ExtraNetworksSuggestData (line 11) | interface ExtraNetworksSuggestData extends AnySuggestData {
  type DanbooruSuggestData (line 16) | interface DanbooruSuggestData extends AnySuggestData {
  type MyPromptSuggestData (line 21) | interface MyPromptSuggestData extends AnySuggestData {
  type SuggestData (line 26) | type SuggestData = ExtraNetworksSuggestData | DanbooruSuggestData | MyPr...

FILE: client-src/libs/api/getDanbooruTags.ts
  function getDanbooruTags (line 3) | function getDanbooruTags(): Promise<DanbooruTag[]> {
  function parseDanbooruTags (line 8) | function parseDanbooruTags(json: unknown): DanbooruTag[] {
  function sortByCategory (line 12) | function sortByCategory(a: DanbooruTag, b: DanbooruTag): number {

FILE: client-src/libs/api/getExtraNetworks.ts
  function getExtraNetworks (line 5) | function getExtraNetworks(type: ExtraNetworksType): Promise<ExtraNetwork...
  function parseResponse (line 16) | function parseResponse(json: unknown): unknown[] {
  function parseExtraNetworksData (line 21) | function parseExtraNetworksData(

FILE: client-src/libs/api/getLocalization.ts
  function getLocalization (line 1) | function getLocalization(): Promise<Record<string, string>> {
  function parseLocalization (line 6) | function parseLocalization(json: unknown): Record<string, string> {
  function isLocalization (line 11) | function isLocalization(obj: object): obj is Record<string, string> {

FILE: client-src/libs/api/getMyPrompts.ts
  function getMyPrompts (line 4) | function getMyPrompts(): Promise<MyPrompt[]> {
  function parseMyPrompts (line 9) | function parseMyPrompts(json: unknown): MyPrompt[] {
  function isMyPrompt (line 13) | function isMyPrompt(obj: unknown): obj is MyPrompt {

FILE: client-src/libs/api/updateMyPrompts.ts
  function updateMyPrompts (line 4) | function updateMyPrompts(myPrompts: MyPrompt[]): Promise<boolean> {
  function parseResponse (line 15) | function parseResponse(json: unknown): boolean {

FILE: client-src/libs/danbooru/danbooruTag.ts
  type DanbooruTag (line 1) | type DanbooruTag = {

FILE: client-src/libs/danbooru/isDanbooruTag.ts
  function isDanbooruTag (line 3) | function isDanbooruTag(obj: unknown): obj is DanbooruTag {

FILE: client-src/libs/danbooru/tagToPrompt.ts
  function tagToPrompt (line 1) | function tagToPrompt(tag: string): string {
  function replaceAll (line 9) | function replaceAll(text: string, dic: Record<string, string>): string {

FILE: client-src/libs/extra-networks/extraNetworks.ts
  type ExtraNetworksType (line 1) | type ExtraNetworksType = "textual-inversion" | "lora";
  type ExtraNetworksData (line 3) | type ExtraNetworksData = {

FILE: client-src/libs/my-prompt/myPrompt.ts
  type MyPrompt (line 1) | interface MyPrompt {

FILE: client-src/libs/prompt/allPrompt.ts
  type AllPrompt (line 5) | type AllPrompt = BasicPrompt | PromptCombination | ExtraNetworksPrompt;

FILE: client-src/libs/prompt/alternatePrompt.ts
  type AlternatePrompt (line 4) | interface AlternatePrompt extends Prompt {

FILE: client-src/libs/prompt/basicPrompt.ts
  type BasicPrompt (line 6) | type BasicPrompt = AlternatePrompt | EmphasizedPrompt | PlainPrompt | Sc...

FILE: client-src/libs/prompt/emphasizedPrompt.ts
  type EmphasizedPrompt (line 4) | type EmphasizedPrompt =
  type EmphasizedPositivePrompt (line 9) | interface EmphasizedPositivePrompt extends Prompt {
  type EmphasizedNegativePrompt (line 14) | interface EmphasizedNegativePrompt extends Prompt {
  type EmphasizedWeightedPrompt (line 19) | interface EmphasizedWeightedPrompt extends Prompt {

FILE: client-src/libs/prompt/extraNetworksPrompt.ts
  type ExtraNetworksPrompt (line 3) | interface ExtraNetworksPrompt extends Prompt {

FILE: client-src/libs/prompt/innerPrompt.ts
  type InnerPrompt (line 4) | type InnerPrompt = BasicPrompt | PromptCombination;

FILE: client-src/libs/prompt/plainPrompt.ts
  type PlainPrompt (line 3) | interface PlainPrompt extends Prompt {

FILE: client-src/libs/prompt/prompt.ts
  type PromptType (line 1) | type PromptType =
  type Prompt (line 11) | interface Prompt {

FILE: client-src/libs/prompt/promptCombination.ts
  type PromptCombination (line 4) | interface PromptCombination extends Prompt {

FILE: client-src/libs/prompt/scheduledPrompt.ts
  type ScheduledPrompt (line 4) | interface ScheduledPrompt extends Prompt {

FILE: client-src/libs/prompt/util/concatPrompt.ts
  constant TRAILING_COMMA_REGEX (line 4) | const TRAILING_COMMA_REGEX = /,(\s+)?$/g;
  constant LEADING_COMMA_REGEX (line 9) | const LEADING_COMMA_REGEX = /^(\s+)?,/g;
  function concatPrompt (line 11) | function concatPrompt(prompt1: string, prompt2: string): string {

FILE: client-src/libs/prompt/util/isEquals/alternatePromptEquals.ts
  function alternatePromptEquals (line 4) | function alternatePromptEquals(prompt1: AlternatePrompt, prompt2: Altern...

FILE: client-src/libs/prompt/util/isEquals/emphasizedPromptEquals.ts
  function emphasizedPromptEquals (line 9) | function emphasizedPromptEquals(
  function emphasizedPositivePromptEquals (line 25) | function emphasizedPositivePromptEquals(
  function emphasizedNegativePromptEquals (line 33) | function emphasizedNegativePromptEquals(
  function emphasizedWeightedPromptEquals (line 41) | function emphasizedWeightedPromptEquals(

FILE: client-src/libs/prompt/util/isEquals/extraNetworksPromptEquals.ts
  function extraNetworksPromptEquals (line 3) | function extraNetworksPromptEquals(
  function textualInversionPromptEquals (line 13) | function textualInversionPromptEquals(
  function loraPromptEquals (line 20) | function loraPromptEquals(prompt1: ExtraNetworksPrompt, prompt2: ExtraNe...

FILE: client-src/libs/prompt/util/isEquals/index.ts
  function isEquals (line 25) | function isEquals(arg1: OneOrMany<Prompt>, arg2: OneOrMany<Prompt>): boo...
  function isEquals1 (line 34) | function isEquals1(prompt1: Prompt, prompt2: Prompt): boolean {
  function isEquals2 (line 58) | function isEquals2(prompts1: Prompt[], prompts2: Prompt[]): boolean {

FILE: client-src/libs/prompt/util/isEquals/plainPromptEquals.ts
  function plainPromptEquals (line 3) | function plainPromptEquals(prompt1: PlainPrompt, prompt2: PlainPrompt): ...

FILE: client-src/libs/prompt/util/isEquals/promptCombinationEquals.ts
  function promptCombinationEquals (line 4) | function promptCombinationEquals(

FILE: client-src/libs/prompt/util/isEquals/scheduledPromptEquals.ts
  function scheduledPromptEquals (line 4) | function scheduledPromptEquals(prompt1: ScheduledPrompt, prompt2: Schedu...

FILE: client-src/libs/prompt/util/isPromptType.ts
  function isPromptType (line 14) | function isPromptType(type: string): type is PromptType {

FILE: client-src/libs/prompt/util/parsePrompt/index.ts
  function parsePrompt (line 17) | function parsePrompt(
  function parse (line 29) | function parse(text: string): Nullable<Prompt[]> {

FILE: client-src/libs/prompt/util/parsePrompt/prompt-parser.js
  function get_parser (line 26) | function get_parser(options = {}) {
  constant NO_VALUE (line 34) | const NO_VALUE = {};
  class _Decoratable (line 35) | class _Decoratable {}
  method escape (line 43) | escape(string) {
  method compile (line 47) | compile(regex, flags) {
  function _get_match (line 54) | function _get_match(re_, regexp, s, flags) {
  class Scanner (line 59) | class Scanner {
    method constructor (line 60) | constructor(terminals, g_regex_flags, re_, use_bytes, match_whole = fa...
    method _build_mres (line 71) | _build_mres(terminals) {
    method match (line 87) | match(text, pos) {
  class ABC (line 111) | class ABC {}
  function dict_items (line 115) | function dict_items(d) {
  function dict_keys (line 118) | function dict_keys(d) {
  function dict_values (line 121) | function dict_values(d) {
  function dict_pop (line 125) | function dict_pop(d, key) {
  function dict_get (line 134) | function dict_get(d, key, otherwise = null) {
  function dict_update (line 138) | function dict_update(self, other) {
  function make_constructor (line 150) | function make_constructor(cls) {
  function range (line 156) | function range(start, end) {
  function format (line 166) | function format(s) {
  function union (line 181) | function union(setA, setB) {
  function intersection (line 189) | function intersection(setA, setB) {
  function set_subtract (line 199) | function set_subtract(a, b) {
  function dict (line 203) | function dict(d) {
  function bool (line 207) | function bool(x) {
  function new_object (line 211) | function new_object(cls) {
  function copy (line 215) | function copy(obj) {
  function map_pop (line 223) | function map_pop(key) {
  function hash (line 229) | function hash(x) {
  function tuple (line 232) | function tuple(x) {
  function frozenset (line 235) | function frozenset(x) {
  function is_dict (line 239) | function is_dict(x) {
  function is_array (line 242) | function is_array(x) {
  function callable (line 245) | function callable(x) {
  function any (line 257) | function any(lst) {
  function all (line 266) | function all(lst) {
  function filter (line 275) | function filter(pred, lst) {
  function partial (line 279) | function partial(f) {
  class EOFError (line 286) | class EOFError extends Error {}
  function last_item (line 288) | function last_item(a) {
  function callable_class (line 292) | function callable_class(cls) {
  function list_repeat (line 299) | function list_repeat(list, count) {
  function isupper (line 303) | function isupper(a) {
  function rsplit (line 307) | function rsplit(s, delimiter, limit) {
  function str_count (line 312) | function str_count(s, substr) {
  function list_count (line 317) | function list_count(list, elem) {
  function isSubset (line 327) | function isSubset(subset, set) {
  class LarkError (line 359) | class LarkError extends Error {
  class ConfigurationError (line 363) | class ConfigurationError extends LarkError {
  function assert_config (line 367) | function assert_config(value, options, msg = "Got %r, expected one of %s...
  class GrammarError (line 373) | class GrammarError extends LarkError {
  class ParseError (line 377) | class ParseError extends LarkError {
  class LexError (line 381) | class LexError extends LarkError {
  class UnexpectedInput (line 398) | class UnexpectedInput extends LarkError {
    method get_context (line 410) | get_context(text, span = 40) {
    method match_examples (line 447) | match_examples(parse_fn, examples, token_type_match_fallback = false) {
    method _format_expected (line 484) | _format_expected(expected) {
  class UnexpectedEOF (line 500) | class UnexpectedEOF extends UnexpectedInput {
    method constructor (line 501) | constructor(expected, state = null, terminals_by_name = null) {
  class UnexpectedCharacters (line 520) | class UnexpectedCharacters extends UnexpectedInput {
    method constructor (line 521) | constructor({
  class UnexpectedToken (line 565) | class UnexpectedToken extends UnexpectedInput {
    method constructor (line 566) | constructor({
    method accepts (line 591) | get accepts() {
  class VisitError (line 614) | class VisitError extends LarkError {
    method constructor (line 615) | constructor(rule, obj, orig_exc) {
  function classify (line 628) | function classify(seq, key = null, value = null) {
  function _deserialize (line 644) | function _deserialize(data, namespace, memo) {
  class Serialize (line 675) | class Serialize {
    method deserialize (line 676) | static deserialize(data, memo) {
  class SerializeMemoizer (line 704) | class SerializeMemoizer extends Serialize {
    method __serialize_fields__ (line 705) | static get __serialize_fields__() {
    method constructor (line 708) | constructor(types_to_memoize) {
    method in_types (line 714) | in_types(value) {
    method serialize (line 718) | serialize() {
    method deserialize (line 722) | static deserialize(data, namespace, memo) {
  class Meta (line 732) | class Meta {
    method constructor (line 733) | constructor() {
  class Tree (line 752) | class Tree {
    method constructor (line 753) | constructor(data, children, meta = null) {
    method meta (line 759) | get meta() {
    method repr (line 767) | repr() {
    method _pretty_label (line 771) | _pretty_label() {
    method _pretty (line 775) | _pretty(level, indent_str) {
    method pretty (line 804) | pretty(indent_str = "  ") {
    method eq (line 808) | eq(other) {
    method iter_subtrees (line 831) | iter_subtrees() {
    method find_pred (line 851) | find_pred(pred) {
    method find_data (line 858) | find_data(data) {
    method scan_values (line 871) | *scan_values(pred) {
    method iter_subtrees_topdown (line 891) | *iter_subtrees_topdown() {
    method copy (line 907) | copy() {
    method set (line 911) | set(data, children) {
  class Transformer (line 955) | class Transformer extends _Decoratable {
    method __visit_tokens__ (line 956) | static get __visit_tokens__() {
    method constructor (line 961) | constructor(visit_tokens = true) {
    method fromObj (line 966) | static fromObj(obj, ...args) {
    method _call_userfunc (line 974) | _call_userfunc(tree, new_children = null) {
    method _call_userfunc_token (line 1001) | _call_userfunc_token(token) {
    method _transform_children (line 1021) | *_transform_children(children) {
    method _transform_tree (line 1037) | _transform_tree(tree) {
    method transform (line 1045) | transform(tree) {
    method __default__ (line 1055) | __default__(data, children, meta) {
    method __default_token__ (line 1065) | __default_token__(token) {
  class Transformer_InPlace (line 1077) | class Transformer_InPlace extends Transformer {
    method _transform_tree (line 1078) | _transform_tree(tree) {
    method transform (line 1083) | transform(tree) {
  class Transformer_NonRecursive (line 1101) | class Transformer_NonRecursive extends Transformer {
    method transform (line 1102) | transform(tree) {
  class Transformer_InPlaceRecursive (line 1150) | class Transformer_InPlaceRecursive extends Transformer {
    method _transform_tree (line 1151) | _transform_tree(tree) {
  class VisitorBase (line 1159) | class VisitorBase {
    method _call_userfunc (line 1160) | _call_userfunc(tree) {
    method __default__ (line 1175) | __default__(tree) {
    method __class_getitem__ (line 1179) | __class_getitem__(_) {
  class Visitor (line 1191) | class Visitor extends VisitorBase {
    method visit (line 1195) | visit(tree) {
    method visit_topdown (line 1206) | visit_topdown(tree) {
  class Visitor_Recursive (line 1224) | class Visitor_Recursive extends VisitorBase {
    method visit (line 1228) | visit(tree) {
    method visit_topdown (line 1242) | visit_topdown(tree) {
  class Interpreter (line 1267) | class Interpreter extends _Decoratable {
    method visit (line 1268) | visit(tree) {
    method visit_children (line 1276) | visit_children(tree) {
    method __default__ (line 1280) | __default__(tree) {
  class Symbol (line 1290) | class Symbol extends Serialize {
    method constructor (line 1292) | constructor(name) {
    method eq (line 1297) | eq(other) {
    method repr (line 1301) | repr() {
    method fullrepr (line 1305) | static get fullrepr() {
    method fullrepr (line 1308) | get fullrepr() {
    method renamed (line 1311) | renamed(f) {
  class Terminal (line 1316) | class Terminal extends Symbol {
    method __serialize_fields__ (line 1317) | static get __serialize_fields__() {
    method is_term (line 1320) | get is_term() {
    method constructor (line 1324) | constructor(name, filter_out = false) {
    method fullrepr (line 1330) | get fullrepr() {
    method renamed (line 1334) | renamed(f) {
  class NonTerminal (line 1339) | class NonTerminal extends Symbol {
    method __serialize_fields__ (line 1340) | static get __serialize_fields__() {
    method is_term (line 1343) | get is_term() {
  class RuleOptions (line 1348) | class RuleOptions extends Serialize {
    method __serialize_fields__ (line 1349) | static get __serialize_fields__() {
    method constructor (line 1352) | constructor(
    method repr (line 1367) | repr() {
  class Rule (line 1386) | class Rule extends Serialize {
    method __serialize_fields__ (line 1387) | static get __serialize_fields__() {
    method __serialize_namespace__ (line 1390) | static get __serialize_namespace__() {
    method constructor (line 1393) | constructor(origin, expansion, order = 0, alias = null, options = null) {
    method _deserialize (line 1403) | _deserialize() {
    method repr (line 1407) | repr() {
    method eq (line 1411) | eq(other) {
  class Pattern (line 1426) | class Pattern extends Serialize {
    method constructor (line 1427) | constructor(value, flags = [], raw = null) {
    method repr (line 1434) | repr() {
    method eq (line 1438) | eq(other) {
    method to_regexp (line 1442) | to_regexp() {
    method min_width (line 1446) | get min_width() {
    method max_width (line 1450) | get max_width() {
    method _get_flags (line 1454) | _get_flags(value) {
  class PatternStr (line 1459) | class PatternStr extends Pattern {
    method __serialize_fields__ (line 1460) | static get __serialize_fields__() {
    method type (line 1463) | static get type() {
    method to_regexp (line 1466) | to_regexp() {
    method min_width (line 1470) | get min_width() {
    method max_width (line 1474) | get max_width() {
  class PatternRE (line 1479) | class PatternRE extends Pattern {
    method __serialize_fields__ (line 1480) | static get __serialize_fields__() {
    method type (line 1483) | static get type() {
    method to_regexp (line 1486) | to_regexp() {
    method _get_width (line 1490) | _get_width() {
    method min_width (line 1498) | get min_width() {
    method max_width (line 1502) | get max_width() {
  class TerminalDef (line 1507) | class TerminalDef extends Serialize {
    method __serialize_fields__ (line 1508) | static get __serialize_fields__() {
    method __serialize_namespace__ (line 1511) | static get __serialize_namespace__() {
    method constructor (line 1514) | constructor(name, pattern, priority = TOKEN_DEFAULT_PRIORITY) {
    method repr (line 1521) | repr() {
    method user_repr (line 1525) | user_repr() {
  class Token (line 1556) | class Token {
    method constructor (line 1557) | constructor(
    method update (line 1577) | update(type_ = null, value = null) {
    method new_borrow_pos (line 1585) | static new_borrow_pos(type_, value, borrow_t) {
    method repr (line 1599) | repr() {
    method eq (line 1603) | eq(other) {
    method __hash__ (line 1611) | static get __hash__() {
  class LineCounter (line 1616) | class LineCounter {
    method constructor (line 1617) | constructor(newline_char) {
    method eq (line 1625) | eq(other) {
    method feed (line 1639) | feed(token, test_newline = true) {
  class _UnlessCallback (line 1654) | class _UnlessCallback {
    method constructor (line 1655) | constructor(scanner) {
    method __call__ (line 1659) | __call__(t) {
  class _CallChain (line 1671) | class _CallChain {
    method constructor (line 1672) | constructor(callback1, callback2, cond) {
    method __call__ (line 1678) | __call__(t) {
  function _create_unless (line 1685) | function _create_unless(terminals, g_regex_flags, re_, use_bytes) {
  function _regexp_has_newline (line 1726) | function _regexp_has_newline(r) {
  class LexerState (line 1742) | class LexerState {
    method constructor (line 1743) | constructor(text, line_ctr = null, last_token = null) {
    method eq (line 1749) | eq(other) {
  class LexerThread (line 1767) | class LexerThread {
    method constructor (line 1768) | constructor(lexer, lexer_state) {
    method from_text (line 1773) | static from_text(lexer, text) {
    method lex (line 1777) | lex(parser_state) {
  class Lexer (line 1790) | class Lexer extends ABC {
    method lex (line 1791) | lex(lexer_state, parser_state) {
  function sort_by_key_tuple (line 1796) | function sort_by_key_tuple(arr, key) {
  class BasicLexer (line 1811) | class BasicLexer extends Lexer {
    method constructor (line 1812) | constructor(conf) {
    method _build_scanner (line 1864) | _build_scanner() {
    method scanner (line 1884) | get scanner() {
    method match (line 1892) | match(text, pos) {
    method lex (line 1896) | *lex(state, parser_state) {
    method next_token (line 1910) | next_token(lex_state, parser_state = null) {
  class ContextualLexer (line 1964) | class ContextualLexer extends Lexer {
    method constructor (line 1965) | constructor({ conf, states, always_accept = [] } = {}) {
    method lex (line 1993) | *lex(lexer_state, parser_state) {
  class LexerConf (line 2035) | class LexerConf extends Serialize {
    method __serialize_fields__ (line 2036) | static get __serialize_fields__() {
    method __serialize_namespace__ (line 2039) | static get __serialize_namespace__() {
    method constructor (line 2042) | constructor({
    method _deserialize (line 2065) | _deserialize() {
  class ParserConf (line 2070) | class ParserConf extends Serialize {
    method __serialize_fields__ (line 2071) | static get __serialize_fields__() {
    method constructor (line 2074) | constructor(rules, callbacks, start) {
  class _ExpandSingleChild (line 2087) | class _ExpandSingleChild {
    method constructor (line 2088) | constructor(node_builder) {
    method __call__ (line 2092) | __call__(children) {
  class _PropagatePositions (line 2102) | class _PropagatePositions {
    method constructor (line 2103) | constructor(node_builder, node_filter = null) {
    method __call__ (line 2108) | __call__(children) {
    method _pp_get_meta (line 2154) | _pp_get_meta(children) {
  function make_propagate_positions (line 2172) | function make_propagate_positions(option) {
  class _ChildFilter (line 2187) | class _ChildFilter {
    method constructor (line 2188) | constructor(to_include, append_none, node_builder) {
    method __call__ (line 2194) | __call__(children) {
  class _ChildFilterLALR (line 2221) | class _ChildFilterLALR extends _ChildFilter {
    method __call__ (line 2222) | __call__(children) {
  class _ChildFilterLALR_NoPlaceholders (line 2254) | class _ChildFilterLALR_NoPlaceholders extends _ChildFilter {
    method constructor (line 2255) | constructor(to_include, node_builder) {
    method __call__ (line 2261) | __call__(children) {
  function _should_expand (line 2281) | function _should_expand(sym) {
  function maybe_create_child_filter (line 2285) | function maybe_create_child_filter(expansion, keep_all_tokens, ambiguous...
  function inplace_transformer (line 2364) | function inplace_transformer(func) {
  function apply_visit_wrapper (line 2375) | function apply_visit_wrapper(func, name, wrapper) {
  class ParseTreeBuilder (line 2388) | class ParseTreeBuilder {
    method constructor (line 2389) | constructor(
    method _init_builders (line 2403) | *_init_builders(rules) {
    method create_callback (line 2426) | create_callback(transformer = null) {
  class LALR_Parser (line 2461) | class LALR_Parser extends Serialize {
    method constructor (line 2462) | constructor({ parser_conf, debug = false } = {}) {
    method deserialize (line 2475) | static deserialize(data, memo, callbacks, debug = false) {
    method serialize (line 2483) | serialize(memo) {
    method parse_interactive (line 2487) | parse_interactive(lexer, start) {
    method parse (line 2495) | parse({ lexer, start, on_error = null } = {}) {
  class ParseConf (line 2551) | class ParseConf {
    method constructor (line 2552) | constructor(parse_table, callbacks, start) {
  class ParserState (line 2562) | class ParserState {
    method constructor (line 2563) | constructor(parse_conf, lexer, state_stack = null, value_stack = null) {
    method position (line 2570) | get position() {
    method eq (line 2576) | eq(other) {
    method copy (line 2584) | copy() {
    method feed_token (line 2588) | feed_token(token, is_end = false) {
  class _Parser (line 2641) | class _Parser {
    method constructor (line 2642) | constructor(parse_table, callbacks, debug = false) {
    method parse (line 2648) | parse({ lexer, start, value_stack = null, state_stack = null, start_in...
    method parse_from_state (line 2658) | parse_from_state(state) {
  class InteractiveParser (line 2714) | class InteractiveParser {
    method constructor (line 2715) | constructor(parser, parser_state, lexer_thread) {
    method feed_token (line 2728) | feed_token(token) {
    method iter_parse (line 2741) | *iter_parse() {
    method exhaust_lexer (line 2754) | exhaust_lexer() {
    method feed_eof (line 2761) | feed_eof(last_token = null) {
    method copy (line 2769) | copy() {
    method eq (line 2773) | eq(other) {
    method as_immutable (line 2784) | as_immutable() {
    method pretty (line 2792) | pretty() {
    method choices (line 2810) | choices() {
    method accepts (line 2817) | accepts() {
    method resume_parse (line 2847) | resume_parse() {
  class ImmutableInteractiveParser (line 2858) | class ImmutableInteractiveParser extends InteractiveParser {
    method feed_token (line 2860) | feed_token(token) {
    method exhaust_lexer (line 2871) | exhaust_lexer() {
    method as_mutable (line 2880) | as_mutable() {
  class Action (line 2890) | class Action {
    method constructor (line 2891) | constructor(name) {
    method repr (line 2895) | repr() {
  class ParseTable (line 2902) | class ParseTable {
    method constructor (line 2903) | constructor(states, start_states, end_states) {
    method serialize (line 2909) | serialize(memo) {
    method deserialize (line 2930) | static deserialize(data, memo) {
  class IntParseTable (line 2948) | class IntParseTable extends ParseTable {
    method from_ParseTable (line 2949) | static from_ParseTable(parse_table) {
  function _wrap_lexer (line 2975) | function _wrap_lexer(lexer_class) {
  class MakeParsingFrontend (line 2995) | class MakeParsingFrontend {
    method constructor (line 2996) | constructor(parser_type, lexer_type) {
    method deserialize (line 3001) | deserialize(data, memo, lexer_conf, callbacks, options) {
  function _deserialize_parsing_frontend (line 3016) | function _deserialize_parsing_frontend(data, memo, lexer_conf, callbacks...
  class ParsingFrontend (line 3030) | class ParsingFrontend extends Serialize {
    method __serialize_fields__ (line 3031) | static get __serialize_fields__() {
    method constructor (line 3034) | constructor({ lexer_conf, parser_conf, options, parser = null } = {}) {
    method _verify_start (line 3071) | _verify_start(start = null) {
    method _make_lexer_thread (line 3092) | _make_lexer_thread(text) {
    method parse (line 3096) | parse(text, start = null, on_error = null) {
    method parse_interactive (line 3107) | parse_interactive(text = null, start = null) {
  function _validate_frontend_args (line 3118) | function _validate_frontend_args(parser, lexer) {
  function _get_lexer_callbacks (line 3136) | function _get_lexer_callbacks(transformer, terminals) {
  class PostLexConnector (line 3149) | class PostLexConnector {
    method constructor (line 3150) | constructor(lexer, postlexer) {
    method lex (line 3155) | lex(lexer_state, parser_state) {
  function create_basic_lexer (line 3161) | function create_basic_lexer(lexer_conf, parser, postlex, options) {
  function create_contextual_lexer (line 3165) | function create_contextual_lexer(lexer_conf, parser, postlex, options) {
  function create_lalr_parser (line 3177) | function create_lalr_parser(lexer_conf, parser_conf, options = null) {
  class PostLex (line 3188) | class PostLex extends ABC {
    method process (line 3189) | process(stream) {
  class LarkOptions (line 3202) | class LarkOptions extends Serialize {
    method constructor (line 3303) | constructor(options_dict) {
    method serialize (line 3341) | serialize(memo) {
    method deserialize (line 3345) | static deserialize(data, memo) {
  class Lark (line 3381) | class Lark extends Serialize {
    method __serialize_fields__ (line 3382) | static get __serialize_fields__() {
    method _build_lexer (line 3385) | _build_lexer(dont_ignore = false) {
    method _prepare_callbacks (line 3395) | _prepare_callbacks() {
    method _deserialize_lexer_conf (line 3424) | _deserialize_lexer_conf(data, memo, options) {
    method _load (line 3435) | _load({ f, ...kwargs } = {}) {
    method _load_from_dict (line 3480) | static _load_from_dict({ data, memo, ...kwargs } = {}) {
    method repr (line 3512) | repr() {
    method lex (line 3529) | lex(text, dont_ignore = false) {
    method get_terminal (line 3548) | get_terminal(name) {
    method parse_interactive (line 3565) | parse_interactive(text = null, start = null) {
    method parse (line 3591) | parse(text, start = null, on_error = null) {
  class DedentError (line 3600) | class DedentError extends LarkError {
  class Indenter (line 3604) | class Indenter extends PostLex {
    method constructor (line 3605) | constructor() {
    method handle_NL (line 3611) | *handle_NL(token) {
    method _process (line 3641) | *_process(stream) {
    method process (line 3661) | process(stream) {
    method always_accept (line 3669) | get always_accept() {
    method NL_type (line 3673) | get NL_type() {
    method OPEN_PAREN_types (line 3677) | get OPEN_PAREN_types() {
    method CLOSE_PAREN_types (line 3681) | get CLOSE_PAREN_types() {
    method INDENT_type (line 3685) | get INDENT_type() {
    method DEDENT_type (line 3689) | get DEDENT_type() {
    method tab_len (line 3693) | get tab_len() {
  class PythonIndenter (line 3698) | class PythonIndenter extends Indenter {
    method NL_type (line 3699) | static get NL_type() {
    method NL_type (line 3702) | get NL_type() {
    method OPEN_PAREN_types (line 3705) | static get OPEN_PAREN_types() {
    method OPEN_PAREN_types (line 3708) | get OPEN_PAREN_types() {
    method CLOSE_PAREN_types (line 3711) | static get CLOSE_PAREN_types() {
    method CLOSE_PAREN_types (line 3714) | get CLOSE_PAREN_types() {
    method INDENT_type (line 3717) | static get INDENT_type() {
    method INDENT_type (line 3720) | get INDENT_type() {
    method DEDENT_type (line 3723) | static get DEDENT_type() {
    method DEDENT_type (line 3726) | get DEDENT_type() {
    method tab_len (line 3729) | static get tab_len() {
    method tab_len (line 3732) | get tab_len() {
  constant NAMESPACE (line 3737) | const NAMESPACE = {

FILE: client-src/libs/prompt/util/toString/alternatePromptToString.ts
  function alternatePromptToString (line 4) | function alternatePromptToString(prompt: AlternatePrompt): string {

FILE: client-src/libs/prompt/util/toString/emphasizedPromptToString.ts
  function emphasizedPromptToString (line 9) | function emphasizedPromptToString(prompt: EmphasizedPrompt): string {
  function emphasizedPositivePromptToString (line 20) | function emphasizedPositivePromptToString(prompt: EmphasizedPositiveProm...
  function emphasizedNegativePromptToString (line 24) | function emphasizedNegativePromptToString(prompt: EmphasizedNegativeProm...
  function emphasizedWeightedPromptToString (line 28) | function emphasizedWeightedPromptToString(prompt: EmphasizedWeightedProm...

FILE: client-src/libs/prompt/util/toString/extraNetworksPromptToString.ts
  function extraNetworksPromptToString (line 3) | function extraNetworksPromptToString(prompt: ExtraNetworksPrompt): string {

FILE: client-src/libs/prompt/util/toString/index.ts
  function toString (line 27) | function toString(arg1: Prompt | Prompt[]): string {
  function toString1 (line 31) | function toString1(prompt: Prompt): string {
  function toString2 (line 53) | function toString2(prompts: Prompt[]): string {

FILE: client-src/libs/prompt/util/toString/plainPromptToString.ts
  function plainPromptToString (line 3) | function plainPromptToString(prompt: PlainPrompt): string {

FILE: client-src/libs/prompt/util/toString/promptCombinationToString.ts
  function promptCombinationToString (line 4) | function promptCombinationToString(prompt: PromptCombination): string {

FILE: client-src/libs/prompt/util/toString/scheduledPromptToString.ts
  function scheduledPromptToString (line 4) | function scheduledPromptToString(prompt: ScheduledPrompt): string {

FILE: client-src/libs/util/array/omitNulls.ts
  function omitNulls (line 1) | function omitNulls<T>(array: Array<T | null | undefined>): Array<T> {

FILE: client-src/libs/util/array/sortByIndexes.ts
  function sortByIndexes (line 1) | function sortByIndexes<T>(array: T[], indexes: number[]): T[] {

FILE: client-src/libs/util/array/toggleValue.ts
  function toggleValue (line 1) | function toggleValue<T>(array: T[], value: T): T[] {

FILE: client-src/libs/util/dom/addClasses.ts
  function addClasses (line 9) | function addClasses(element: Element, ...classes: Nullable<string>[]): v...

FILE: client-src/libs/util/dom/applyClasses.ts
  function applyClasses (line 11) | function applyClasses(element: Element, set: boolean, ...classes: Nullab...

FILE: client-src/libs/util/dom/getElement.ts
  function getElement (line 18) | function getElement(arg1: string | Element, arg2?: string): HTMLElement ...

FILE: client-src/libs/util/dom/getElementAll.ts
  function getElementAll (line 18) | function getElementAll(arg1: string | Element, arg2?: string): HTMLEleme...

FILE: client-src/libs/util/dom/getScreenPosition.ts
  function getScreenPosition (line 7) | function getScreenPosition(element: Element): {

FILE: client-src/libs/util/dom/hasChild.ts
  function hasChild (line 8) | function hasChild(parent: Element, child: Element): boolean {

FILE: client-src/libs/util/dom/hasClass.ts
  function hasClass (line 3) | function hasClass(element: Element, ...classes: Nullable<string>[]): boo...

FILE: client-src/libs/util/dom/hasElement.ts
  function hasElement (line 20) | function hasElement(arg1: string | Element, arg2?: string): boolean {

FILE: client-src/libs/util/dom/removeAllChild.ts
  function removeAllChild (line 7) | function removeAllChild(parent: Element, selector?: string): void {

FILE: client-src/libs/util/dom/removeClasses.ts
  function removeClasses (line 9) | function removeClasses(element: Element, ...classes: Nullable<string>[])...

FILE: client-src/libs/util/dom/rotateElement.ts
  type RotateDirection (line 1) | type RotateDirection = "up" | "down" | "left" | "right";
  function rotateElement (line 3) | function rotateElement(

FILE: client-src/libs/util/dom/scrollIntoViewIfNeeded.ts
  function scrollIntoViewIfNeeded (line 1) | function scrollIntoViewIfNeeded(element: HTMLElement, parent: HTMLElemen...

FILE: client-src/libs/util/string/generateHashCode.ts
  function generateHashCode (line 7) | function generateHashCode(str: string): number {

FILE: client-src/libs/util/string/toggleValue.ts
  function toggleValue (line 14) | function toggleValue(values: string, keyword: string, force?: boolean): ...

FILE: client-src/libs/util/types/isArray.ts
  function isArray (line 5) | function isArray<T>(arg1: unknown, arg2?: (element: unknown) => element ...

FILE: client-src/libs/util/types/isBoolean.ts
  function isBoolean (line 1) | function isBoolean(arg: unknown): arg is boolean {

FILE: client-src/libs/util/types/isNumber.ts
  function isNumber (line 1) | function isNumber(arg: unknown): arg is number {

FILE: client-src/libs/util/types/isObject.ts
  function isObject (line 1) | function isObject(arg: unknown): arg is object {

FILE: client-src/libs/util/types/isString.ts
  function isString (line 1) | function isString(arg: unknown): arg is string {

FILE: client-src/libs/util/webui/dispatchEvent.ts
  function dispatchEvent (line 7) | function dispatchEvent(element: HTMLElement, type: "input" | "change"): ...

FILE: client-src/libs/util/webui/getCurrentTabName.ts
  function getCurrentTabName (line 4) | function getCurrentTabName(): WebUiTab {

FILE: client-src/libs/util/webui/getOption.ts
  function getOption (line 4) | function getOption(optionName: string, defaultValue: OptionValue): Optio...

FILE: client-src/libs/util/webui/isDarkMode.ts
  function isDarkMode (line 3) | function isDarkMode(): boolean {

FILE: client-src/libs/util/webui/t.ts
  type Arg (line 1) | type Arg = string | number | boolean;
  function t (line 3) | function t(key: string, options?: { defaultValue?: string; args?: Arg[] ...

FILE: client-src/libs/util/webui/withBooleanOption.ts
  function withBooleanOption (line 7) | function withBooleanOption(optionName: string, callback: Callback1<boole...

FILE: client-src/main.ts
  function initWidgets (line 15) | function initWidgets(): void {
  function initialize (line 22) | function initialize(tabName: ExtensionAvailableTab): void {
  function initBetterPrompt (line 35) | function initBetterPrompt(tabName: ExtensionAvailableTab): void {
  function hiddenOriginalComponents (line 43) | function hiddenOriginalComponents(tabName: ExtensionAvailableTab): void {

FILE: dev-scripts/crawl-danbooru-tags.mjs
  function updateProgressBar (line 31) | function updateProgressBar(current, total) {
  function writeToFile (line 50) | function writeToFile(filename, data) {
  function categoryAsParameter (line 67) | function categoryAsParameter(category) {
  function removeDuplicateObjects (line 85) | function removeDuplicateObjects(array) {
  function crawlDanbooruTags (line 101) | async function crawlDanbooruTags(category, options) {

FILE: javascript/betterPrompt.js
  function Ze (line 1) | function Ze(n){return n==null||typeof n!="object"||typeof n.name!="strin...
  function dt (line 1) | function dt(n){return uo(n,{_:" ","(":"\\(",")":"\\)"})}
  function uo (line 1) | function uo(n,t){let r=n;return Object.entries(t).forEach(([i,s])=>{r=r....
  function co (line 1) | function co(){return fetch(`/better-prompt-api/v1/get-danbooru-tags?ts=$...
  function fo (line 1) | function fo(n){return Array.isArray(n)?n.filter(Ze).sort(po):[]}
  function po (line 1) | function po(n,t){const r=i=>{switch(i){case 3:return 100;case 4:return 1...
  function _o (line 1) | function _o(n){return n.filter(t=>t!=null)}
  function ho (line 1) | function ho(n,t){const r=[];for(const i of t)r.push(n[i]);return r}
  function rr (line 1) | function rr(n,t){const r=n.indexOf(t);return r!==-1?n.splice(r,1):n.push...
  function ai (line 1) | function ai(n,t){return n==null||!Array.isArray(n)?!1:t==null?!0:n.every...
  function mo (line 1) | function mo(n){return n!=null&&typeof n=="boolean"}
  function ir (line 1) | function ir(n){return n!=null&&typeof n=="object"}
  function Mt (line 1) | function Mt(n){return n!=null&&typeof n=="string"}
  function ui (line 1) | function ui(n){return fetch(`/better-prompt-api/v1/get-extra-networks/${...
  function go (line 1) | function go(n){return n==null||!Array.isArray(n)?[]:n}
  function yo (line 1) | function yo(n,t){return!ir(n)||!Mt(n.name)||!Mt(n.search_term)?null:Mt(n...
  function bo (line 1) | function bo(){return fetch(`/better-prompt-api/v1/get-localization?ts=${...
  function wo (line 1) | function wo(n){return n==null||typeof n!="object"?{}:vo(n)?n:{}}
  function vo (line 1) | function vo(n){return Object.values(n).every(t=>typeof t=="string")}
  function ko (line 1) | function ko(){return fetch(`/better-prompt-api/v1/get-my-prompts?ts=${ne...
  function Eo (line 1) | function Eo(n){return ai(n)?n.filter(To):[]}
  function To (line 1) | function To(n){return!ir(n)||!Mt(n.label)||!ai(n.tags,Mt)?!1:Mt(n.prompt)}
  function ci (line 1) | function ci(n){return fetch("/better-prompt-api/v1/update-my-prompts",{m...
  function xo (line 1) | function xo(n){return ir(n)?mo(n.success)&&n.success:!1}
  function Be (line 1) | function Be(n,t){return typeof n=="string"?gradioApp().querySelector(n):...
  function fi (line 1) | function fi(n,t){return typeof n=="string"?Array.from(gradioApp().queryS...
  function So (line 1) | function So(n){const t=n.getBoundingClientRect(),r=window.scrollY||docum...
  function pi (line 1) | function pi(n,t){let r=t.parentNode;for(;r!=null;){if(r===n)return!0;r=r...
  function yn (line 1) | function yn(n,t){return typeof n=="string"?Be(n)!=null:t!=null?Be(n,t)!=...
  function No (line 1) | function No(n,t){const r=n==null?void 0:n.parentElement;if(!n||!r)return...
  function Po (line 1) | function Po(n,t){const{top:r,bottom:i}=t.getBoundingClientRect(),{top:s,...
  function _i (line 1) | function _i(n,t){if(t==="input")updateInput(n);else{const r=new Event(t)...
  function hi (line 1) | function hi(){const n=get_uiCurrentTabContent();if(n==null)return"other"...
  function di (line 1) | function di(n,t){const r=opts[n];return r==null||typeof r!=typeof t?t:r}
  function bn (line 1) | function bn(){return yn(".gradio-container.dark")}
  function Mo (line 1) | function Mo(n,t){const r=`better-prompt.${n}`,i=()=>t!=null&&t.defaultVa...
  function Oo (line 1) | function Oo(n,t){const r=opts[n];typeof r=="boolean"&&t(r)}
  function X (line 1) | function X(){}
  function vn (line 1) | function vn(n,t){for(const r in t)n[r]=t[r];return n}
  function mi (line 1) | function mi(n){return n()}
  function gi (line 1) | function gi(){return Object.create(null)}
  function ae (line 1) | function ae(n){n.forEach(mi)}
  function lt (line 1) | function lt(n){return typeof n=="function"}
  function Q (line 1) | function Q(n,t){return n!=n?t==t:n!==t||n&&typeof n=="object"||typeof n=...
  function Do (line 1) | function Do(n){return Object.keys(n).length===0}
  function Ao (line 1) | function Ao(n,...t){if(n==null){for(const i of t)i(void 0);return X}cons...
  function he (line 1) | function he(n,t,r){n.$$.on_destroy.push(Ao(t,r))}
  function yi (line 1) | function yi(n,t,r,i){if(n){const s=bi(n,t,r,i);return n[0](s)}}
  function bi (line 1) | function bi(n,t,r,i){return n[1]&&i?vn(r.ctx.slice(),n[1](i(t))):r.ctx}
  function wi (line 1) | function wi(n,t,r,i){if(n[2]&&i){const s=n[2](i(r));if(t.dirty===void 0)...
  function vi (line 1) | function vi(n,t,r,i,s,l){if(s){const o=bi(t,r,i,l);n.p(o,s)}}
  function ki (line 1) | function ki(n){if(n.ctx.length>32){const t=[],r=n.ctx.length/32;for(let ...
  function Ei (line 1) | function Ei(n){const t=typeof n=="string"&&n.match(/^\s*(-?[\d.]+)([^\s]...
  function xi (line 1) | function xi(n){Ot.forEach(t=>{t.c(n)||(Ot.delete(t),t.f())}),Ot.size!==0...
  function or (line 1) | function or(n){let t;return Ot.size===0&&lr(xi),{promise:new Promise(r=>...
  function T (line 1) | function T(n,t){n.appendChild(t)}
  function Si (line 1) | function Si(n){if(!n)return document;const t=n.getRootNode?n.getRootNode...
  function Io (line 1) | function Io(n){const t=x("style");return t.textContent="/* empty */",Ro(...
  function Ro (line 1) | function Ro(n,t){return T(n.head||n,t),t.sheet}
  function M (line 1) | function M(n,t,r){n.insertBefore(t,r||null)}
  function N (line 1) | function N(n){n.parentNode&&n.parentNode.removeChild(n)}
  function Xe (line 1) | function Xe(n,t){for(let r=0;r<n.length;r+=1)n[r]&&n[r].d(t)}
  function x (line 1) | function x(n){return document.createElement(n)}
  function re (line 1) | function re(n){return document.createTextNode(n)}
  function z (line 1) | function z(){return re(" ")}
  function Re (line 1) | function Re(){return re("")}
  function C (line 1) | function C(n,t,r,i){return n.addEventListener(t,r,i),()=>n.removeEventLi...
  function Ni (line 1) | function Ni(n){return function(t){return t.preventDefault(),n.call(this,...
  function Lo (line 1) | function Lo(n){return function(t){return t.stopPropagation(),n.call(this...
  function E (line 1) | function E(n,t,r){r==null?n.removeAttribute(t):n.getAttribute(t)!==r&&n....
  function Dt (line 1) | function Dt(n,t){const r=Object.getOwnPropertyDescriptors(n.__proto__);f...
  function ur (line 1) | function ur(n){return n===""?null:+n}
  function Fo (line 1) | function Fo(n){return Array.from(n.childNodes)}
  function me (line 1) | function me(n,t){t=""+t,n.data!==t&&(n.data=t)}
  function Bo (line 1) | function Bo(n,t){t=""+t,n.wholeText!==t&&(n.data=t)}
  function cr (line 1) | function cr(n,t,r){~Co.indexOf(r)?Bo(n,t):me(n,t)}
  function He (line 1) | function He(n,t){n.value=t??""}
  function qe (line 1) | function qe(n,t,r,i){r==null?n.style.removeProperty(t):n.style.setProper...
  function U (line 1) | function U(n,t,r){n.classList.toggle(t,!!r)}
  function Pi (line 1) | function Pi(n,t,{bubbles:r=!1,cancelable:i=!1}={}){return new CustomEven...
  function $o (line 1) | function $o(n){let t=5381,r=n.length;for(;r--;)t=(t<<5)-t^n.charCodeAt(r...
  function jo (line 1) | function jo(n,t){const r={stylesheet:Io(t),rules:{}};return kn.set(n,r),r}
  function Tn (line 1) | function Tn(n,t,r,i,s,l,o,a=0){const u=16.666/i;let c=`{
  function xn (line 4) | function xn(n,t){const r=(n.style.animation||"").split(", "),i=r.filter(...
  function Wo (line 4) | function Wo(){lr(()=>{En||(kn.forEach(n=>{const{ownerNode:t}=n.styleshee...
  function Gt (line 4) | function Gt(n){Kt=n}
  function Vt (line 4) | function Vt(){if(!Kt)throw new Error("Function called outside component ...
  function Mi (line 4) | function Mi(n){Vt().$$.on_mount.push(n)}
  function Oi (line 4) | function Oi(n){Vt().$$.on_destroy.push(n)}
  function Di (line 4) | function Di(){const n=Vt();return(t,r,{cancelable:i=!1}={})=>{const s=n....
  function fr (line 4) | function fr(n,t){return Vt().$$.context.set(n,t),t}
  function Ne (line 4) | function Ne(n){return Vt().$$.context.get(n)}
  function ue (line 4) | function ue(n,t){const r=n.$$.callbacks[t.type];r&&r.slice().forEach(i=>...
  function Ho (line 4) | function Ho(){_r||(_r=!0,Xo.then(Ai))}
  function ot (line 4) | function ot(n){Ct.push(n)}
  function ge (line 4) | function ge(n){pr.push(n)}
  function Ai (line 4) | function Ai(){if(It!==0)return;const n=Kt;do{try{for(;It<At.length;){con...
  function qo (line 4) | function qo(n){if(n.fragment!==null){n.update(),ae(n.before_update);cons...
  function Yo (line 4) | function Yo(n){const t=[],r=[];Ct.forEach(i=>n.indexOf(i)===-1?t.push(i)...
  function dr (line 4) | function dr(){return Qt||(Qt=Promise.resolve(),Qt.then(()=>{Qt=null})),Qt}
  function mt (line 4) | function mt(n,t,r){n.dispatchEvent(Pi(`${t?"intro":"outro"}${r}`))}
  function Ee (line 4) | function Ee(){Ye={r:0,c:[],p:Ye}}
  function Te (line 4) | function Te(){Ye.r||ae(Ye.c),Ye=Ye.p}
  function P (line 4) | function P(n,t){n&&n.i&&(Sn.delete(n),n.i(t))}
  function A (line 4) | function A(n,t,r,i){if(n&&n.o){if(Sn.has(n))return;Sn.add(n),Ye.c.push((...
  function Uo (line 4) | function Uo(n,t,r){const i={direction:"in"};let s=t(n,r,i),l=!1,o,a,u=0;...
  function Ko (line 4) | function Ko(n,t,r){const i={direction:"out"};let s=t(n,r,i),l=!0,o;const...
  function Ci (line 4) | function Ci(n,t,r,i){let l=t(n,r,{direction:"both"}),o=i?0:1,a=null,u=nu...
  function ce (line 4) | function ce(n){return(n==null?void 0:n.length)!==void 0?n:Array.from(n)}
  function Go (line 4) | function Go(n,t){A(n,1,1,()=>{t.delete(n.key)})}
  function Vo (line 4) | function Vo(n,t,r,i,s,l,o,a,u,c,f,p){let _=n.length,h=l.length,m=_;const...
  function gr (line 4) | function gr(n,t){const r={},i={},s={$$scope:1};let l=n.length;for(;l--;)...
  function ye (line 4) | function ye(n,t,r){const i=n.$$.props[t];i!==void 0&&(n.$$.bound[i]=r,r(...
  function Y (line 4) | function Y(n){n&&n.c()}
  function H (line 4) | function H(n,t,r){const{fragment:i,after_update:s}=n.$$;i&&i.m(t,r),ot((...
  function q (line 4) | function q(n,t){const r=n.$$;r.fragment!==null&&(Yo(r.after_update),ae(r...
  function Qo (line 4) | function Qo(n,t){n.$$.dirty[0]===-1&&(At.push(n),Ho(),n.$$.dirty.fill(0)...
  function J (line 4) | function J(n,t,r,i,s,l,o,a=[-1]){const u=Kt;Gt(n);const c=n.$$={fragment...
  class Z (line 4) | class Z{constructor(){We(this,"$$");We(this,"$$set")}$destroy(){q(this,1...
    method constructor (line 4) | constructor(){We(this,"$$");We(this,"$$set")}
    method $destroy (line 4) | $destroy(){q(this,1),this.$destroy=X}
    method $on (line 4) | $on(t,r){if(!lt(r))return X;const i=this.$$.callbacks[t]||(this.$$.cal...
    method $set (line 4) | $set(t){this.$$set&&!Do(t)&&(this.$$.skip_bound=!0,this.$$set(t),this....
  function Zo (line 4) | function Zo(n,t){return{subscribe:Pe(n,t).subscribe}}
  function Pe (line 4) | function Pe(n,t=X){let r;const i=new Set;function s(a){if(Q(n,a)&&(n=a,r...
  function ta (line 4) | function ta(n){Ri||(Ri=!0,Ii.set(n))}
  function ra (line 4) | function ra(n){Fi||(Fi=!0,Li.set(n))}
  function ia (line 4) | function ia(n){Bi||(Bi=!0,zi.set(n))}
  function la (line 4) | function la(n){Nn||(Nn=!0,et=[...n],Zt.set(et))}
  function oa (line 4) | function oa(n){if(!Nn)return;const t=[...et,n];ci(t).then(r=>{r&&(et=t,Z...
  function aa (line 4) | function aa(n){if(!Nn)return;const t=et.filter(r=>!n.includes(r));ci(t)....
  function ji (line 4) | function ji(n){const t=n-1;return t*t*t+1}
  function Wi (line 4) | function Wi(n){return--n*n*n*n*n+1}
  function ua (line 4) | function ua(n,{delay:t=0,duration:r=400,easing:i=wn}={}){const s=+getCom...
  function ca (line 4) | function ca(n,{delay:t=0,duration:r=400,easing:i=ji,x:s=0,y:l=0,opacity:...
  function Xi (line 6) | function Xi(n,{delay:t=0,duration:r=400,easing:i=ji,start:s=0,opacity:l=...
  function Hi (line 9) | function Hi(n,t,r){const i=n.slice();return i[3]=t[r],i}
  function qi (line 9) | function qi(n){let t,r=n[3].text+"",i,s,l,o,a,u,c,f;function p(){return ...
  function fa (line 9) | function fa(n){let t,r,i=ce(n[0]),s=[];for(let o=0;o<i.length;o+=1)s[o]=...
  function pa (line 9) | function pa(n){br.update(t=>t.some(r=>r.text===n.text)?t:[...t,Object.as...
  function Yi (line 9) | function Yi(n){br.update(t=>t.filter(r=>r!==n))}
  function _a (line 9) | function _a(n){((n==null?void 0:n.duration)||0)<=0||window.setTimeout(()...
  function ha (line 9) | function ha(n,t,r){let i;return he(n,br,o=>r(0,i=o)),[i,o=>_a(o),o=>Yi(o)]}
  class da (line 9) | class da extends Z{constructor(t){super(),J(this,t,ha,fa,Q,{})}}
    method constructor (line 9) | constructor(t){super(),J(this,t,ha,fa,Q,{})}
  class ee (line 9) | class ee{constructor(t,r){We(this,"key");We(this,"defaultValue");this.ke...
    method constructor (line 9) | constructor(t,r){We(this,"key");We(this,"defaultValue");this.key=t,thi...
    method translate (line 9) | translate(t){return Mo(this.key,{defaultValue:this.defaultValue,args:t})}
  function Ya (line 9) | function Ya(n,t){return!n||Ha.test(n)||qa.test(t)?n+t:`${n}, ${t}`}
  function Ua (line 9) | function Ua(n,t){return n.values.length!==t.values.length?!1:n.values.ev...
  function Ka (line 9) | function Ka(n,t){return n.values.length!==t.values.length?!1:n.values.ev...
  function Ga (line 9) | function Ga(n,t){return n.values.length!==t.values.length?!1:n.values.ev...
  function Va (line 9) | function Va(n,t){return n.values.length!==t.values.length?!1:n.values.ev...
  function Qa (line 9) | function Qa(n,t){return n.values.length!==t.values.length||n.weight!==t....
  function Ja (line 9) | function Ja(n,t){return n.name!==t.name?!1:n.name==="textual-inversion"?...
  function Za (line 9) | function Za(n,t){return n.args[0]===t.args[0]}
  function eu (line 9) | function eu(n,t){return n.args[0]!==t.args[0]?!1:Number(n.args[1])===Num...
  function tu (line 9) | function tu(n,t){return n.value===t.value}
  function nu (line 9) | function nu(n,t){return n.when!==t.when||n.to==null!=(t.to==null)||n.to!...
  function at (line 9) | function at(n,t){if(Array.isArray(n)){if(Array.isArray(t))return ru(n,t)...
  function Ki (line 9) | function Ki(n,t){if(n===t)return!0;if(n.type!==t.type)return!1;switch(n....
  function ru (line 9) | function ru(n,t){return n.length!==t.length?!1:n.every((r,i)=>Ki(r,t[i]))}
  function iu (line 9) | function iu(n={}){return n.transformer&&n.transformer.constructor.name==...
  class su (line 9) | class su{}
  method escape (line 9) | escape(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}
  method compile (line 9) | compile(n,t){return new RegExp(n,t)}
  function ou (line 9) | function ou(n,t,r,i){const s=n.compile(t,i).exec(r);if(s!=null)return s[0]}
  class Qi (line 9) | class Qi{constructor(t,r,i,s,l=!1){this.terminals=t,this.g_regex_flags=r...
    method constructor (line 9) | constructor(t,r,i,s,l=!1){this.terminals=t,this.g_regex_flags=r,this.r...
    method _build_mres (line 9) | _build_mres(t){let r=this.match_whole?"$":"",i=gu(t,l=>l.pattern.flags...
    method match (line 9) | match(t,r){for(const i of this._regexps){i.lastIndex=r;let s=i.exec(t)...
  class au (line 9) | class au{}
  function we (line 9) | function we(n){return Object.entries(n)}
  function wr (line 9) | function wr(n){return Object.keys(n)}
  function uu (line 9) | function uu(n,t){t===void 0&&(t=Object.keys(n)[0]);let r=n[t];return del...
  function Zi (line 9) | function Zi(n,t,r=null){return n[t]||r}
  function es (line 9) | function es(n,t){if(n.constructor.name==="Map")for(const[r,i]of we(t))n....
  function cu (line 9) | function cu(n){return function(){return new n(...arguments)}}
  function ie (line 9) | function ie(n){let t=0,r=[...arguments].slice(1);return n.replace(/%([sr...
  function fu (line 9) | function fu(n,t){let r=new Set(n);for(const i of t)r.add(i);return r}
  function ts (line 9) | function ts(n,t){return[...n].filter(r=>!t.has(r))}
  function ns (line 9) | function ns(n){return{...n}}
  function rs (line 9) | function rs(n){return!!n}
  function vr (line 9) | function vr(n){return Object.create(n.prototype)}
  function tt (line 9) | function tt(n){if(typeof n=="object"){let t=Object.create(Object.getProt...
  function F_ (line 9) | function F_(n){return n}
  function B_ (line 9) | function B_(n){return n}
  function Pn (line 9) | function Pn(n){return new Set(n)}
  function kr (line 9) | function kr(n){return n&&n.constructor.name==="Object"}
  function pu (line 9) | function pu(n){return n&&n.constructor.name==="Array"}
  function _u (line 9) | function _u(n){return typeof n=="function"}
  function hu (line 9) | function hu(n){for(const t of n)if(t)return!0;return!1}
  function is (line 9) | function is(n,t){return t.filter(n||rs)}
  function Mn (line 9) | function Mn(n){let t=[...arguments].slice(1);return function(){return n(...
  class Er (line 9) | class Er extends Error{}
  function ut (line 9) | function ut(n){return n[n.length-1]}
  function yt (line 9) | function yt(n){return function(){let t=new n(...arguments);return t.__ca...
  function ct (line 9) | function ct(n,t){return Array.from({length:t},()=>n).flat()}
  function ss (line 9) | function ss(n){return/^[A-Z_$]*$/.test(n)}
  function ls (line 9) | function ls(n,t,r){const i=n.split(t);return r?i.splice(-r-1):i}
  function du (line 9) | function du(n,t){let r=new RegExp(t,"g");return(n.match(r)||[]).length}
  function mu (line 9) | function mu(n,t){for(let r of n)if(!t.has(r))return!1;return!0}
  class nn (line 9) | class nn extends Error{}
  class bt (line 9) | class bt extends nn{}
  function Tr (line 9) | function Tr(n,t,r="Got %r, expected one of %s"){if(!t.includes(n))throw ...
  class xr (line 9) | class xr extends nn{}
  class On (line 9) | class On extends nn{}
  class Lt (line 9) | class Lt extends nn{constructor(){super(...arguments);We(this,"pos_in_st...
    method constructor (line 9) | constructor(){super(...arguments);We(this,"pos_in_stream",null);We(thi...
    method get_context (line 9) | get_context(r,i=40){let s,l,o=this.pos_in_stream,a=max(o-i,0),u=o+i;re...
    method match_examples (line 17) | match_examples(r,i,s=!1){kr(i)&&(i=we(i));let l=[null,!1];for(const[o,...
    method _format_expected (line 17) | _format_expected(r){let i;return this._terminals_by_name&&(i=this._ter...
  class zt (line 20) | class zt extends Lt{constructor({seq:t,lex_pos:r,line:i,column:s,allowed...
    method constructor (line 20) | constructor({seq:t,lex_pos:r,line:i,column:s,allowed:l=null,considered...
  class rn (line 20) | class rn extends Lt{constructor({token:t,expected:r,considered_rules:i=n...
    method constructor (line 20) | constructor({token:t,expected:r,considered_rules:i=null,state:s=null,i...
    method accepts (line 20) | get accepts(){return this._accepts===Gi&&(this._accepts=this.interacti...
  class os (line 20) | class os extends nn{constructor(t,r,i){let s=ie(`Error trying to process...
    method constructor (line 20) | constructor(t,r,i){let s=ie(`Error trying to process rule "%s":
  function yu (line 22) | function yu(n,t=null,r=null){let i,s,l=new Map;for(const o of n)i=t!==nu...
  function Dn (line 22) | function Dn(n,t,r){let i;return kr(n)?"__type__"in n?(i=t[n.__type__],i....
  class Le (line 22) | class Le{static deserialize(t,r){const i=this;let s=i&&i.__serialize_fie...
    method deserialize (line 22) | static deserialize(t,r){const i=this;let s=i&&i.__serialize_fields__;i...
  class bu (line 22) | class bu extends Le{static get __serialize_fields__(){return["memoized"]...
    method __serialize_fields__ (line 22) | static get __serialize_fields__(){return["memoized"]}
    method constructor (line 22) | constructor(t){super(),this.types_to_memoize=t,this.memoized=new Enume...
    method in_types (line 22) | in_types(t){return t instanceof this.types_to_memoize}
    method serialize (line 22) | serialize(){return _serialize(this.memoized.reversed(),null)}
    method deserialize (line 22) | static deserialize(t,r,i){return Dn(t,r,i)}
  class wu (line 22) | class wu{constructor(){this.empty=!0}}
    method constructor (line 22) | constructor(){this.empty=!0}
  class ze (line 22) | class ze{constructor(t,r,i=null){this.data=t,this.children=r,this._meta=...
    method constructor (line 22) | constructor(t,r,i=null){this.data=t,this.children=r,this._meta=i}
    method meta (line 22) | get meta(){return this._meta===null&&(this._meta=new wu),this._meta}
    method repr (line 22) | repr(){return ie("Tree(%r, %r)",this.data,this.children)}
    method _pretty_label (line 22) | _pretty_label(){return this.data}
    method _pretty (line 22) | _pretty(t,r){if(this.children.length===1&&!(this.children[0]instanceof...
    method pretty (line 25) | pretty(t="  "){return this._pretty(0,t).join("")}
    method eq (line 25) | eq(t){return t&&this&&t&&this&&t.children&&this.children&&t.data&&this...
    method iter_subtrees (line 25) | iter_subtrees(){let t=[this],r=new Map;for(const i of t)r.set(i,i),t.p...
    method find_pred (line 25) | find_pred(t){return is(t,this.iter_subtrees())}
    method find_data (line 25) | find_data(t){return this.find_pred(r=>r.data===t)}
    method scan_values (line 25) | *scan_values(t){for(const r of this.children)if(r instanceof ze)for(co...
    method iter_subtrees_topdown (line 25) | *iter_subtrees_topdown(){let t,r=[this];for(;r.length;)if(t=r.pop(),t ...
    method copy (line 25) | copy(){return type(this)(this.data,this.children)}
    method set (line 25) | set(t,r){this.data=t,this.children=r}
  class as (line 25) | class as extends su{static get __visit_tokens__(){return!0}constructor(t...
    method __visit_tokens__ (line 25) | static get __visit_tokens__(){return!0}
    method constructor (line 25) | constructor(t=!0){super(),this.__visit_tokens__=t}
    method fromObj (line 25) | static fromObj(t,...r){class i extends this{}for(let[s,l]of Object.ent...
    method _call_userfunc (line 25) | _call_userfunc(t,r=null){let i,s,l=r!==null?r:t.children;if(t&&t.data&...
    method _call_userfunc_token (line 25) | _call_userfunc_token(t){let r;if(t&&t.type&&this&&this[t.type]){r=this...
    method _transform_children (line 25) | *_transform_children(t){let r;for(const i of t)i instanceof ze?r=this....
    method _transform_tree (line 25) | _transform_tree(t){let r=[...this._transform_children(t.children)];ret...
    method transform (line 25) | transform(t){return this._transform_tree(t)}
    method __default__ (line 25) | __default__(t,r,i){return new ze(t,r,i)}
    method __default_token__ (line 25) | __default_token__(t){return t}
  class vu (line 25) | class vu extends as{_transform_tree(t){return this._call_userfunc(t)}tra...
    method _transform_tree (line 25) | _transform_tree(t){return this._call_userfunc(t)}
    method transform (line 25) | transform(t){for(const r of t.iter_subtrees())r.children=[...this._tra...
  method constructor (line 25) | constructor(r){super();We(this,"is_term",en);this.name=r}
  method eq (line 25) | eq(r){return this.is_term===r.is_term&&this.name===r.name}
  method repr (line 25) | repr(){return ie("%s(%r)",type(this).name,this.name)}
  method fullrepr (line 25) | static get fullrepr(){return property(__repr__)}
  method fullrepr (line 25) | get fullrepr(){return this.constructor.fullrepr}
  method renamed (line 25) | renamed(r){return type(this)(r(this.name))}
  class cs (line 25) | class cs extends us{static get __serialize_fields__(){return["name","fil...
    method __serialize_fields__ (line 25) | static get __serialize_fields__(){return["name","filter_out"]}
    method is_term (line 25) | get is_term(){return!0}
    method constructor (line 25) | constructor(t,r=!1){super(),this.name=t,this.filter_out=r}
    method fullrepr (line 25) | get fullrepr(){return ie("%s(%r, %r)",type(this).name,this.name,this.f...
    method renamed (line 25) | renamed(t){return type(this)(t(this.name),this.filter_out)}
  class fs (line 25) | class fs extends us{static get __serialize_fields__(){return["name"]}get...
    method __serialize_fields__ (line 25) | static get __serialize_fields__(){return["name"]}
    method is_term (line 25) | get is_term(){return!1}
  class Sr (line 25) | class Sr extends Le{static get __serialize_fields__(){return["keep_all_t...
    method __serialize_fields__ (line 25) | static get __serialize_fields__(){return["keep_all_tokens","expand1","...
    method constructor (line 25) | constructor(t=!1,r=!1,i=null,s=null,l=[]){super(),this.keep_all_tokens...
    method repr (line 25) | repr(){return ie("RuleOptions(%r, %r, %r, %r)",this.keep_all_tokens,th...
  class sn (line 25) | class sn extends Le{static get __serialize_fields__(){return["origin","e...
    method __serialize_fields__ (line 25) | static get __serialize_fields__(){return["origin","expansion","order",...
    method __serialize_namespace__ (line 25) | static get __serialize_namespace__(){return[cs,fs,Sr]}
    method constructor (line 25) | constructor(t,r,i=0,s=null,l=null){super(),this.origin=t,this.expansio...
    method _deserialize (line 25) | _deserialize(){this._hash=[this.origin,this.expansion]}
    method repr (line 25) | repr(){return ie("Rule(%r, %r, %r, %r)",this.origin,this.expansion,thi...
    method eq (line 25) | eq(t){return t instanceof sn?this.origin===t.origin&&this.expansion===...
  class ps (line 25) | class ps extends Le{constructor(t,r=[],i=null){super(),this.value=t,this...
    method constructor (line 25) | constructor(t,r=[],i=null){super(),this.value=t,this.flags=Pn(r),this....
    method repr (line 25) | repr(){return repr(this.to_regexp())}
    method eq (line 25) | eq(t){return type(this)===type(t)&&this.value===t.value&&this.flags===...
    method to_regexp (line 25) | to_regexp(){throw new NotImplementedError}
    method min_width (line 25) | get min_width(){throw new NotImplementedError}
    method max_width (line 25) | get max_width(){throw new NotImplementedError}
    method _get_flags (line 25) | _get_flags(t){return t}
  class _s (line 25) | class _s extends ps{static get __serialize_fields__(){return["value","fl...
    method __serialize_fields__ (line 25) | static get __serialize_fields__(){return["value","flags"]}
    method type (line 25) | static get type(){return"str"}
    method to_regexp (line 25) | to_regexp(){return this._get_flags(Vi.escape(this.value))}
    method min_width (line 25) | get min_width(){return this.value.length}
    method max_width (line 25) | get max_width(){return this.value.length}
  class hs (line 25) | class hs extends ps{static get __serialize_fields__(){return["value","fl...
    method __serialize_fields__ (line 25) | static get __serialize_fields__(){return["value","flags","_width"]}
    method type (line 25) | static get type(){return"re"}
    method to_regexp (line 25) | to_regexp(){return this._get_flags(this.value)}
    method _get_width (line 25) | _get_width(){return this._width===null&&(this._width=get_regexp_width(...
    method min_width (line 25) | get min_width(){return this._get_width()[0]}
    method max_width (line 25) | get max_width(){return this._get_width()[1]}
  class Nr (line 25) | class Nr extends Le{static get __serialize_fields__(){return["name","pat...
    method __serialize_fields__ (line 25) | static get __serialize_fields__(){return["name","pattern","priority"]}
    method __serialize_namespace__ (line 25) | static get __serialize_namespace__(){return[_s,hs]}
    method constructor (line 25) | constructor(t,r,i=ku){super(),this.name=t,this.pattern=r,this.priority=i}
    method repr (line 25) | repr(){return ie("%s(%r, %r)",type(this).name,this.name,this.pattern)}
    method user_repr (line 25) | user_repr(){return this.name.startsWith("__")?this.pattern.raw||this.n...
  class Me (line 25) | class Me{constructor(t,r,i=null,s=null,l=null,o=null,a=null,u=null){this...
    method constructor (line 25) | constructor(t,r,i=null,s=null,l=null,o=null,a=null,u=null){this.type=t...
    method update (line 25) | update(t=null,r=null){return Me.new_borrow_pos(t!==null?t:this.type,r!...
    method new_borrow_pos (line 25) | static new_borrow_pos(t,r,i){const s=this;return new s(t,r,i.start_pos...
    method repr (line 25) | repr(){return ie("Token(%r, %r)",this.type,this.value)}
    method eq (line 25) | eq(t){return t instanceof Me&&this.type!==t.type?!1:str.__eq__(this,t)}
    method __hash__ (line 25) | static get __hash__(){return str.__hash__}
  class Pr (line 25) | class Pr{constructor(t){this.newline_char=t,this.char_pos=0,this.line=1,...
    method constructor (line 25) | constructor(t){this.newline_char=t,this.char_pos=0,this.line=1,this.co...
    method eq (line 25) | eq(t){return t instanceof Pr?this.char_pos===t.char_pos&&this.newline_...
    method feed (line 25) | feed(t,r=!0){let i;r&&(i=du(t,this.newline_char),i&&(this.line+=i,this...
  class Eu (line 25) | class Eu{constructor(t){this.scanner=t}__call__(t){let r,i=this.scanner....
    method constructor (line 25) | constructor(t){this.scanner=t}
    method __call__ (line 25) | __call__(t){let r,i=this.scanner.match(t.value,0);return i&&([r,t.type...
  class xu (line 25) | class xu{constructor(t,r,i){this.callback1=t,this.callback2=r,this.cond=...
    method constructor (line 25) | constructor(t,r,i){this.callback1=t,this.callback2=r,this.cond=i}
    method __call__ (line 25) | __call__(t){let r=this.callback1(t);return this.cond(r)?this.callback2...
  function Nu (line 25) | function Nu(n,t,r,i){let s,l,o=yu(n,f=>f.pattern.constructor.type),a=new...
  function Pu (line 25) | function Pu(n){return n.includes(`
  class Mr (line 26) | class Mr{constructor(t,r=null,i=null){this.text=t,this.line_ctr=r||new Pr(`
    method constructor (line 26) | constructor(t,r=null,i=null){this.text=t,this.line_ctr=r||new Pr(`
    method eq (line 27) | eq(t){return t instanceof Mr?this.text===t.text&&this.line_ctr===t.lin...
  class ds (line 27) | class ds{constructor(t,r){this.lexer=t,this.state=r}static from_text(t,r...
    method constructor (line 27) | constructor(t,r){this.lexer=t,this.state=r}
    method from_text (line 27) | static from_text(t,r){return new this(t,new Mr(r))}
    method lex (line 27) | lex(t){return this.lexer.lex(this.state,t)}
  class Or (line 27) | class Or extends au{lex(t,r){return en}}
    method lex (line 27) | lex(t,r){return en}
  function Mu (line 27) | function Mu(n,t){n.sort((r,i)=>{let s=t(r),l=t(i);for(let o=0;o<s.length...
  class An (line 27) | class An extends Or{constructor(t){super();let r=[...t.terminals];if(thi...
    method constructor (line 27) | constructor(t){super();let r=[...t.terminals];if(this.re=t.re_module,!...
    method _build_scanner (line 27) | _build_scanner(){let t;[t,this.callback]=Nu(this.terminals,this.g_rege...
    method scanner (line 27) | get scanner(){return this._scanner===null&&this._build_scanner(),this....
    method match (line 27) | match(t,r){return this.scanner.match(t,r)}
    method lex (line 27) | *lex(t,r){try{for(;;)yield this.next_token(t,r)}catch(i){if(!(i instan...
    method next_token (line 27) | next_token(t,r=null){let i,s,l,o,a=t.line_ctr;for(;a.char_pos<t.text.l...
  class Ou (line 27) | class Ou extends Or{constructor({conf:t,states:r,always_accept:i=[]}={})...
    method constructor (line 27) | constructor({conf:t,states:r,always_accept:i=[]}={}){super();let s,l,o...
    method lex (line 27) | *lex(t,r){let i,s,l;try{for(;;)s=this.lexers[r.position],yield s.next_...
  class Du (line 27) | class Du extends Le{static get __serialize_fields__(){return["terminals"...
    method __serialize_fields__ (line 27) | static get __serialize_fields__(){return["terminals","ignore","g_regex...
    method __serialize_namespace__ (line 27) | static get __serialize_namespace__(){return[Nr]}
    method constructor (line 27) | constructor({terminals:t,re_module:r,ignore:i=[],postlex:s=null,callba...
    method _deserialize (line 27) | _deserialize(){this.terminals_by_name=Object.fromEntries(this.terminal...
  class Au (line 27) | class Au extends Le{static get __serialize_fields__(){return["rules","st...
    method __serialize_fields__ (line 27) | static get __serialize_fields__(){return["rules","start","parser_type"]}
    method constructor (line 27) | constructor(t,r,i){super(),this.rules=t,this.callbacks=r,this.start=i,...
  class Cu (line 27) | class Cu{constructor(t){this.node_builder=t}__call__(t){return t.length=...
    method constructor (line 27) | constructor(t){this.node_builder=t}
    method __call__ (line 27) | __call__(t){return t.length===1?t[0]:this.node_builder(t)}
  class Ru (line 27) | class Ru{constructor(t,r=null){this.node_builder=t,this.node_filter=r}__...
    method constructor (line 27) | constructor(t,r=null){this.node_builder=t,this.node_filter=r}
    method __call__ (line 27) | __call__(t){let r,i,s,l=this.node_builder(t);return l instanceof ze&&(...
    method _pp_get_meta (line 27) | _pp_get_meta(t){for(const r of t)if(!(this.node_filter!==null&&!this.n...
  function Lu (line 27) | function Lu(n){if(_u(n))return Mn({unknown_param_0:ms,node_filter:n});if...
  class Dr (line 27) | class Dr{constructor(t,r,i){this.node_builder=i,this.to_include=t,this.a...
    method constructor (line 27) | constructor(t,r,i){this.node_builder=i,this.to_include=t,this.append_n...
    method __call__ (line 27) | __call__(t){let r=[];for(const[i,s,l]of this.to_include)l&&r.push(...c...
  class Fu (line 27) | class Fu extends Dr{__call__(t){let r=[];for(const[i,s,l]of this.to_incl...
    method __call__ (line 27) | __call__(t){let r=[];for(const[i,s,l]of this.to_include)l&&r.push(...c...
  class $u (line 27) | class $u extends Dr{constructor(t,r){super(),this.node_builder=r,this.to...
    method constructor (line 27) | constructor(t,r){super(),this.node_builder=r,this.to_include=t}
    method __call__ (line 27) | __call__(t){let r=[];for(const[i,s]of this.to_include)s?r.length?r.pus...
  function Wu (line 27) | function Wu(n){return!n.is_term&&n.name.startsWith("_")}
  function Xu (line 27) | function Xu(n,t,r,i){let s,l;i.length?(l=i.map(u=>(0+u).toString()).join...
  function Hu (line 27) | function Hu(n){function t(r){let i=new ze(n.name,r);return n(i)}return t...
  function qu (line 27) | function qu(n,t,r){if(r===_vargs_meta||r===_vargs_meta_inline)throw new ...
  class Yu (line 27) | class Yu{constructor(t,r,i=!1,s=!1,l=!1){this.tree_class=r,this.propagat...
    method constructor (line 27) | constructor(t,r,i=!1,s=!1,l=!1){this.tree_class=r,this.propagate_posit...
    method _init_builders (line 27) | *_init_builders(t){let r,i,s,l,o=Lu(this.propagate_positions);for(cons...
    method create_callback (line 27) | create_callback(t=null){let r,i,s,l=new Map;for(const[o,a]of this.rule...
  class gs (line 27) | class gs extends Le{constructor({parser_conf:t,debug:r=!1}={}){super();l...
    method constructor (line 27) | constructor({parser_conf:t,debug:r=!1}={}){super();let i=new LALR_Anal...
    method deserialize (line 27) | static deserialize(t,r,i,s=!1){let o=vr(this);return o._parse_table=Vu...
    method serialize (line 27) | serialize(t){return this._parse_table.serialize(t)}
    method parse_interactive (line 27) | parse_interactive(t,r){return this.parser.parse({lexer:t,start:r,start...
    method parse (line 27) | parse({lexer:t,start:r,on_error:i=null}={}){let s,l;try{return this.pa...
  class Uu (line 27) | class Uu{constructor(t,r,i){this.parse_table=t,this.start_state=this.par...
    method constructor (line 27) | constructor(t,r,i){this.parse_table=t,this.start_state=this.parse_tabl...
  class Ar (line 27) | class Ar{constructor(t,r,i=null,s=null){this.parse_conf=t,this.lexer=r,t...
    method constructor (line 27) | constructor(t,r,i=null,s=null){this.parse_conf=t,this.lexer=r,this.sta...
    method position (line 27) | get position(){return ut(this.state_stack)}
    method eq (line 27) | eq(t){return t instanceof Ar?this.state_stack.length===t.state_stack.l...
    method copy (line 27) | copy(){return tt(this)}
    method feed_token (line 27) | feed_token(t,r=!1){let i,s,l,o,a,u,c,f,p,_,h=this.state_stack,m=this.v...
  class ys (line 27) | class ys{constructor(t,r,i=!1){this.parse_table=t,this.callbacks=r,this....
    method constructor (line 27) | constructor(t,r,i=!1){this.parse_table=t,this.callbacks=r,this.debug=i}
    method parse (line 27) | parse({lexer:t,start:r,value_stack:i=null,state_stack:s=null,start_int...
    method parse_from_state (line 27) | parse_from_state(t){let r,i;try{i=null;for(i of t.lexer.lex(t))t.feed_...
  class wt (line 27) | class wt{constructor(t,r,i){this.parser=t,this.parser_state=r,this.lexer...
    method constructor (line 27) | constructor(t,r,i){this.parser=t,this.parser_state=r,this.lexer_thread...
    method feed_token (line 27) | feed_token(t){return this.parser_state.feed_token(t,t.type==="$END")}
    method iter_parse (line 27) | *iter_parse(){for(const t of this.lexer_thread.lex(this.parser_state))...
    method exhaust_lexer (line 27) | exhaust_lexer(){return[...this.iter_parse()]}
    method feed_eof (line 27) | feed_eof(t=null){let r=t!==null?Me.new_borrow_pos("$END","",t):new Me(...
    method copy (line 27) | copy(){return tt(this)}
    method eq (line 27) | eq(t){return t instanceof wt?this.parser_state===t.parser_state&&this....
    method as_immutable (line 27) | as_immutable(){let t=tt(this);return new Ku(t.parser,t.parser_state,t....
    method pretty (line 27) | pretty(){let t=["Parser choices:"];for(const[r,i]of we(this.choices())...
    method choices (line 28) | choices(){return this.parser_state.parse_conf.parse_table.states[this....
    method accepts (line 28) | accepts(){let t,r=new Set;for(const i of this.choices())if(ss(i)){t=tt...
    method resume_parse (line 28) | resume_parse(){return this.parser.parse_from_state(this.parser_state)}
  class Ku (line 28) | class Ku extends wt{constructor(){super(...arguments);We(this,"result",n...
    method constructor (line 28) | constructor(){super(...arguments);We(this,"result",null)}
    method feed_token (line 28) | feed_token(r){let i=tt(this);return i.result=wt.feed_token(i,r),i}
    method exhaust_lexer (line 28) | exhaust_lexer(){let r=this.as_mutable();return r.exhaust_lexer(),r.as_...
    method as_mutable (line 28) | as_mutable(){let r=tt(this);return new wt(r.parser,r.parser_state,r.le...
  class bs (line 28) | class bs{constructor(t){this.name=t}repr(){return this.toString()}}
    method constructor (line 28) | constructor(t){this.name=t}
    method repr (line 28) | repr(){return this.toString()}
  class Gu (line 28) | class Gu{constructor(t,r,i){this.states=t,this.start_states=r,this.end_s...
    method constructor (line 28) | constructor(t,r,i){this.states=t,this.start_states=r,this.end_states=i}
    method serialize (line 28) | serialize(t){let r=new Enumerator,i=Object.fromEntries(we(this.states)...
    method deserialize (line 28) | static deserialize(t,r){const i=this;let s=t.tokens,l=Object.fromEntri...
  class Vu (line 28) | class Vu extends Gu{static from_ParseTable(t){const r=this;let i=[...t.s...
    method from_ParseTable (line 28) | static from_ParseTable(t){const r=this;let i=[...t.states],s=Object.fr...
  function Qu (line 28) | function Qu(n){if(n&&n.__future_interface__||!1)return n;{class r extend...
  function Ju (line 28) | function Ju(n,t,r,i,s){let l=Au.deserialize(n.parser_conf,t),o=gs.deseri...
  class Zu (line 28) | class Zu extends Le{static get __serialize_fields__(){return["lexer_conf...
    method __serialize_fields__ (line 28) | static get __serialize_fields__(){return["lexer_conf","parser_conf","p...
    method constructor (line 28) | constructor({lexer_conf:t,parser_conf:r,options:i,parser:s=null}={}){s...
    method _verify_start (line 28) | _verify_start(t=null){let r;if(t===null){if(r=this.parser_conf.start,r...
    method _make_lexer_thread (line 28) | _make_lexer_thread(t){return this.skip_lexer?t:ds.from_text(this.lexer...
    method parse (line 28) | parse(t,r=null,i=null){let s=this._verify_start(r),l=i===null?{}:{on_e...
    method parse_interactive (line 28) | parse_interactive(t=null,r=null){let i=this._verify_start(r);if(this.p...
  function ec (line 28) | function ec(n,t){let r;Tr(n,["lalr","earley","cyk"]),typeof t!="object"&...
  function tc (line 28) | function tc(n,t){let r,i={};for(const s of t)r=n&&n[s.name]||null,r!==nu...
  class nc (line 28) | class nc{constructor(t,r){this.lexer=t,this.postlexer=r}lex(t,r){let i=t...
    method constructor (line 28) | constructor(t,r){this.lexer=t,this.postlexer=r}
    method lex (line 28) | lex(t,r){let i=this.lexer.lex(t,r);return this.postlexer.process(i)}
  function rc (line 28) | function rc(n,t,r,i){return new An(n)}
  function ic (line 28) | function ic(n,t,r,i){let s=Object.fromEntries(we(t._parse_table.states)....
  function sc (line 28) | function sc(n,t,r=null){let i=r?r.debug:!1;return new gs({parser_conf:t,...
  class lc (line 28) | class lc extends Le{constructor(r){super();We(this,"OPTIONS_DOC",'\n    ...
    method constructor (line 28) | constructor(r){super();We(this,"OPTIONS_DOC",'\n    **===  General Opt...
    method serialize (line 28) | serialize(r){return this.options}
    method deserialize (line 28) | static deserialize(r,i){const s=this;return new s(r)}
  class oc (line 28) | class oc extends Le{static get __serialize_fields__(){return["parser","r...
    method __serialize_fields__ (line 28) | static get __serialize_fields__(){return["parser","rules","options"]}
    method _build_lexer (line 28) | _build_lexer(t=!1){let r=this.lexer_conf;return t&&(r=tt(r),r.ignore=[...
    method _prepare_callbacks (line 28) | _prepare_callbacks(){this._callbacks=new Map,this.options.ambiguity!==...
    method _deserialize_lexer_conf (line 28) | _deserialize_lexer_conf(t,r,i){let s=Du.deserialize(t.lexer_conf,r);re...
    method _load (line 28) | _load({f:t,...r}={}){let i;kr(t)?i=t:i=pickle.load(t);let s=i.memo,l=i...
    method _load_from_dict (line 28) | static _load_from_dict({data:t,memo:r,...i}={}){return vr(this)._load(...
    method repr (line 28) | repr(){return ie("Lark(open(%r), parser=%r, lexer=%r, ...)",this.sourc...
    method lex (line 28) | lex(t,r=!1){let i;!("lexer"in this)||r?i=this._build_lexer(r):i=this.l...
    method get_terminal (line 28) | get_terminal(t){return this._terminals_dict[t]}
    method parse_interactive (line 28) | parse_interactive(t=null,r=null){return this.parser.parse_interactive(...
    method parse (line 28) | parse(t,r=null,i=null){return this.parser.parse(t,r,i)}
  function Cn (line 29) | function Cn(n,t){const r=fc(n);if(r!=null&&t!=null){t(r);return}return r}
  function fc (line 29) | function fc(n){const t=iu({transformer:{start:r=>Array.from(r).flat(),ex...
  function ks (line 29) | function ks(n){return`[${n.values.map(be).join("|")}]`}
  function Es (line 29) | function Es(n){return`(${be(n.values)})`}
  function Ts (line 29) | function Ts(n){return`[${be(n.values)}]`}
  function pc (line 29) | function pc(n){return`(${n.values.map(be).join(", ")}:${n.weight})`}
  function _c (line 29) | function _c(n){const{name:t,args:r}=n;if(t==="textual-inversion")return ...
  function xs (line 29) | function xs(n){return n.value}
  function Ss (line 29) | function Ss(n){return n.values.map(be).join(" ")}
  function Ns (line 29) | function Ns(n){let t="";return n.from!=null&&(t+=`${be(n.from)}:`),n.to!...
  function be (line 29) | function be(n){return Array.isArray(n)?hc(n):Ir(n)}
  function Ir (line 29) | function Ir(n){switch(n.type){case"alternate":return ks(n);case"combinat...
  function hc (line 29) | function hc(n){if(n.length<1)return"";let t=!1,r=0,i="";do{const s=n[r];...
  function In (line 29) | function In(n,t){if(n.type!=="plain")return n;const r=t.find(i=>i.name==...
  function dc (line 29) | function dc(n){let t,r,i;return{c(){t=x("div"),r=x("span"),i=re(n[1]),E(...
  function mc (line 29) | function mc(n,t,r){let i,s=!1;const{tabName:l}=Ne(gt),{negative:o}=Ne(Rr...
  class gc (line 29) | class gc extends Z{constructor(t){super(),J(this,t,mc,dc,Q,{})}}
    method constructor (line 29) | constructor(t){super(),J(this,t,mc,dc,Q,{})}
  function Ps (line 34) | function Ps(n,t){var r=Object.keys(n);if(Object.getOwnPropertySymbols){v...
  function Ue (line 34) | function Ue(n){for(var t=1;t<arguments.length;t++){var r=arguments[t]!=n...
  function Rn (line 34) | function Rn(n){return typeof Symbol=="function"&&typeof Symbol.iterator=...
  function yc (line 34) | function yc(n,t,r){return t in n?Object.defineProperty(n,t,{value:r,enum...
  function nt (line 34) | function nt(){return nt=Object.assign||function(n){for(var t=1;t<argumen...
  function bc (line 34) | function bc(n,t){if(n==null)return{};var r={},i=Object.keys(n),s,l;for(l...
  function wc (line 34) | function wc(n,t){if(n==null)return{};var r=bc(n,t),i,s;if(Object.getOwnP...
  function rt (line 34) | function rt(n){if(typeof window<"u"&&window.navigator)return!!navigator....
  function V (line 34) | function V(n,t,r){n.addEventListener(t,r,!it&&As)}
  function K (line 34) | function K(n,t,r){n.removeEventListener(t,r,!it&&As)}
  function Ln (line 34) | function Ln(n,t){if(t){if(t[0]===">"&&(t=t.substring(1)),n)try{if(n.matc...
  function kc (line 34) | function kc(n){return n.host&&n!==document&&n.host.nodeType?n.host:n.par...
  function Ke (line 34) | function Ke(n,t,r,i){if(n){r=r||document;do{if(t!=null&&(t[0]===">"?n.pa...
  function Oe (line 34) | function Oe(n,t,r){if(n&&t)if(n.classList)n.classList[r?"add":"remove"](...
  function B (line 34) | function B(n,t,r){var i=n&&n.style;if(i){if(r===void 0)return document.d...
  function Ft (line 34) | function Ft(n,t){var r="";if(typeof n=="string")r=n;else do{var i=B(n,"t...
  function Is (line 34) | function Is(n,t,r){if(n){var i=n.getElementsByTagName(t),s=0,l=i.length;...
  function Ge (line 34) | function Ge(){var n=document.scrollingElement;return n||document.documen...
  function de (line 34) | function de(n,t,r,i,s){if(!(!n.getBoundingClientRect&&n!==window)){var l...
  function Rs (line 34) | function Rs(n,t,r){for(var i=ft(n,!0),s=de(n)[t];i;){var l=de(i)[r],o=vo...
  function Bt (line 34) | function Bt(n,t,r,i){for(var s=0,l=0,o=n.children;l<o.length;){if(o[l].s...
  function Lr (line 34) | function Lr(n,t){for(var r=n.lastElementChild;r&&(r===$.ghost||B(r,"disp...
  function Fe (line 34) | function Fe(n,t){var r=0;if(!n||!n.parentNode)return-1;for(;n=n.previous...
  function Ls (line 34) | function Ls(n){var t=0,r=0,i=Ge();if(n)do{var s=Ft(n),l=s.a,o=s.d;t+=n.s...
  function Ec (line 34) | function Ec(n,t){for(var r in n)if(n.hasOwnProperty(r)){for(var i in t)i...
  function ft (line 34) | function ft(n,t){if(!n||!n.getBoundingClientRect)return Ge();var r=n,i=!...
  function Tc (line 34) | function Tc(n,t){if(n&&t)for(var r in t)t.hasOwnProperty(r)&&(n[r]=t[r])...
  function zr (line 34) | function zr(n,t){return Math.round(n.top)===Math.round(t.top)&&Math.roun...
  function zs (line 34) | function zs(n,t){return function(){if(!an){var r=arguments,i=this;r.leng...
  function xc (line 34) | function xc(){clearTimeout(an),an=void 0}
  function Fs (line 34) | function Fs(n,t,r){n.scrollLeft+=t,n.scrollTop+=r}
  function Bs (line 34) | function Bs(n){var t=window.Polymer,r=window.jQuery||window.Zepto;return...
  function Sc (line 34) | function Sc(){var n=[],t;return{captureAnimationState:function(){if(n=[]...
  function Nc (line 34) | function Nc(n){return n.offsetWidth}
  function Pc (line 34) | function Pc(n,t,r,i){return Math.sqrt(Math.pow(t.top-n.top,2)+Math.pow(t...
  function Mc (line 34) | function Mc(n){var t=n.sortable,r=n.rootEl,i=n.name,s=n.targetEl,l=n.clo...
  function xe (line 34) | function xe(n){Mc(Ue({putSortable:ve,cloneEl:le,targetEl:S,rootEl:se,old...
  function r (line 34) | function r(l,o){return function(a,u,c,f){var p=a.options.group.name&&u.o...
  function $ (line 34) | function $(n,t){if(!(n&&n.nodeType&&n.nodeType===1))throw"Sortable: `el`...
  function d (line 34) | function d(gn,A_){Se(gn,m,Ue({evt:t,isOwner:f,axis:h?"vertical":"horizon...
  function y (line 34) | function y(){d("dragOverAnimationCapture"),m.captureAnimationState(),m!=...
  function b (line 34) | function b(gn){return d("dragOverCompleted",{insertion:gn}),gn&&(f?c._hi...
  function w (line 34) | function w(){Ae=Fe(S),_t=Fe(S,a.draggable),xe({sortable:m,name:"change",...
  function Rc (line 34) | function Rc(n){n.dataTransfer&&(n.dataTransfer.dropEffect="move"),n.canc...
  function Yn (line 34) | function Yn(n,t,r,i,s,l,o,a){var u,c=n[De],f=c.options.onMove,p;return w...
  function Xr (line 34) | function Xr(n){n.draggable=!1}
  function Lc (line 34) | function Lc(){Wr=!1}
  function zc (line 34) | function zc(n,t,r){var i=de(Bt(r.el,0,r.options,!0)),s=10;return t?n.cli...
  function Fc (line 34) | function Fc(n,t,r){var i=de(Lr(r.el,r.options.draggable)),s=10;return t?...
  function Bc (line 34) | function Bc(n,t,r,i,s,l,o,a){var u=i?n.clientY:n.clientX,c=i?r.height:r....
  function $c (line 34) | function $c(n){return Fe(S)<Fe(n)?1:-1}
  function jc (line 34) | function jc(n){for(var t=n.tagName+n.className+n.src+n.href+n.textConten...
  function Wc (line 34) | function Wc(n){Xn.length=0;for(var t=n.getElementsByTagName("input"),r=t...
  function Un (line 34) | function Un(n){return setTimeout(n,0)}
  function Hr (line 34) | function Hr(n){return clearTimeout(n)}
  function Xc (line 34) | function Xc(){function n(){this.defaults={scroll:!0,forceAutoScrollFallb...
  function Gn (line 34) | function Gn(){pe.forEach(function(n){clearInterval(n.pid)}),pe=[]}
  function Ks (line 34) | function Ks(){clearInterval(dn)}
  function Vr (line 34) | function Vr(){}
  function Qr (line 34) | function Qr(){}
  function Vn (line 34) | function Vn(n){if(n.values.length===1)switch(n.values[0].type){case"emph...
  function Hc (line 34) | function Hc(n,t){switch(t.type){case"alternate":case"combination":case"p...
  function qc (line 34) | function qc(n,t){let r="";return t.from!=null&&(r+=`:${be(t.from)}`),t.t...
  function Ht (line 34) | function Ht(n){return Yc(n).replaceAll("\\","")}
  function Yc (line 34) | function Yc(n){switch(n.type){case"alternate":return ks(n);case"combinat...
  function Uc (line 34) | function Uc(n){return n.name==="textual-inversion"?n.args[0]:n.name==="l...
  function qt (line 34) | function qt(n){const t={};switch(t["data-prompt-type"]=n.type,t["data-pr...
  function Kc (line 34) | function Kc(n){switch(n.type){case"plain":case"emphasized-positive":case...
  function Vs (line 34) | function Vs(n){let t,r=`${n[2]}px`,i=`${n[3]}px`,s;const l=n[9].default,...
  function Vc (line 34) | function Vc(n){let t,r,i,s,l,o=n[0]&&Vs(n);return{c(){t=z(),o&&o.c(),r=R...
  function Qc (line 34) | function Qc(n,t,r){let{$$slots:i={},$$scope:s}=t,{parent:l}=t,{show:o}=t...
  class Qn (line 34) | class Qn extends Z{constructor(t){super(),J(this,t,Qc,Vc,Q,{parent:6,sho...
    method constructor (line 34) | constructor(t){super(),J(this,t,Qc,Vc,Q,{parent:6,show:0,options:7})}
  function Zs (line 34) | function Zs(){function n(t){var r=this;if(!(r instanceof n))return t===V...
  function lf (line 34) | function lf(n,t){var r,i,s;if(!sf.test(t))throw Error(xt+"number");for(n...
  function St (line 34) | function St(n,t,r,i){var s=n.c;if(r===Ve&&(r=n.constructor.RM),r!==0&&r!...
  function Yt (line 34) | function Yt(n,t,r){var i=n.e,s=n.c.join(""),l=s.length;if(t)s=s.charAt(0...
  function of (line 34) | function of(n){switch(n.type){case"plain":return 1;case"emphasized-posit...
  function el (line 34) | function el(n,t){const r=new Jr(t).mul(1.1).round(2,1).toNumber();if(n.v...
  function tl (line 34) | function tl(n,t){const r=new Jr(t).div(1.1).round(2,1).toNumber();if(n.v...
  function af (line 34) | function af(n,t){switch(n.type){case"plain":if(t===1)break;return{type:"...
  function nl (line 34) | function nl(n){let t,r;return{c(){t=x("span"),r=re(n[1]),E(t,"class","la...
  function uf (line 34) | function uf(n){let t,r,i,s,l,o,a,u,c,f=n[1]&&nl(n);return{c(){var p,_;t=...
  function cf (line 34) | function cf(n,t,r){let{label:i=null}=t,{value:s=0}=t,{options:l={}}=t,{m...
  class ff (line 34) | class ff extends Z{constructor(t){super(),J(this,t,cf,uf,Q,{label:1,valu...
    method constructor (line 34) | constructor(t){super(),J(this,t,cf,uf,Q,{label:1,value:0,options:8})}
  function rl (line 34) | function rl(n){let t,r;return t=new ff({props:{label:Xa.translate(),valu...
  function pf (line 34) | function pf(n){let t=!isNaN(n[0]),r,i,s=t&&rl(n);return{c(){s&&s.c(),r=R...
  function _f (line 34) | function _f(n,t,r){let{prompt:i}=t,s;function l(o){o.target instanceof H...
  class hf (line 34) | class hf extends Z{constructor(t){super(),J(this,t,_f,pf,Q,{prompt:2})}}
    method constructor (line 34) | constructor(t){super(),J(this,t,_f,pf,Q,{prompt:2})}
  function il (line 34) | function il(n){let t,r,i;function s(o){n[7](o)}let l={parent:n[1],$$slot...
  function df (line 34) | function df(n){let t,r,i;function s(o){n[6](o)}let l={};return n[0]!==vo...
  function mf (line 34) | function mf(n){let t,r=Ht(n[0])+"",i,s,l,o,a,u,c=[qt(n[0]),{class:"promp...
  function gf (line 34) | function gf(n,t,r){let{prompt:i}=t,s,l=!1;function o(p){p.ctrlKey||Kc(i)...
  method constructor (line 34) | constructor(t){super(),J(this,t,gf,mf,Q,{prompt:0})}
  function sl (line 34) | function sl(n,t,r){const i=n.slice();return i[6]=t[r],i[7]=t,i[8]=r,i}
  function ll (line 34) | function ll(n,t){let r,i,s,l,o,a;function u(f){t[4](f,t[6],t[7],t[8])}le...
  function bf (line 34) | function bf(n){let t,r=[],i=new Map,s,l=ce(n[1]);const o=a=>Hc(a[8],a[6]...
  function wf (line 34) | function wf(n,t,r){let i;const{prompts:s}=Ne(Rr);he(n,s,c=>r(1,i=c));con...
  class vf (line 34) | class vf extends Z{constructor(t){super(),J(this,t,wf,bf,Q,{})}}
    method constructor (line 34) | constructor(t){super(),J(this,t,wf,bf,Q,{})}
  function kf (line 34) | function kf(n){let t,r,i,s,l,o,a,u;return l=new gc({}),a=new vf({}),{c()...
  function Ef (line 34) | function Ef(n,t,r){let i,s;he(n,Jt,w=>r(8,s=w));let{negative:l=!1}=t;con...
  class ol (line 34) | class ol extends Z{constructor(t){super(),J(this,t,Ef,kf,Q,{negative:2})}}
    method constructor (line 34) | constructor(t){super(),J(this,t,Ef,kf,Q,{negative:2})}
  function al (line 34) | function al(n){let t,r;return{c(){t=x("span"),r=re(n[1]),E(t,"class","la...
  function Tf (line 34) | function Tf(n){let t,r,i,s,l,o=n[1]&&al(n);return{c(){t=x("div"),o&&o.c(...
  function xf (line 34) | function xf(n,t,r){let{label:i=null}=t,{value:s=""}=t,{options:l=null}=t...
  class Zr (line 34) | class Zr extends Z{constructor(t){super(),J(this,t,xf,Tf,Q,{label:1,valu...
    method constructor (line 34) | constructor(t){super(),J(this,t,xf,Tf,Q,{label:1,value:0,options:3})}
  function Sf (line 34) | function Sf(n){let t,r,i;function s(o){n[5](o)}let l={options:{placehold...
  function Nf (line 34) | function Nf({target:n}){n instanceof HTMLInputElement&&(n.selectionStart...
  function Pf (line 34) | function Pf(n,t,r){let i,s,l;he(n,Jt,d=>r(7,s=d));let{promptText:o}=t;co...
  class Mf (line 34) | class Mf extends Z{constructor(t){super(),J(this,t,Pf,Sf,Q,{promptText:0...
    method constructor (line 34) | constructor(t){super(),J(this,t,Pf,Sf,Q,{promptText:0})}
  function Of (line 34) | function Of(n){switch(n){case"all":return La.translate();case"textual-in...
  function Df (line 34) | function Df(n){let t,r,i,s,l,o,a;return{c(){t=x("label"),r=x("input"),i=...
  function Af (line 34) | function Af(n,t,r){let{label:i=""}=t,{value:s=!1}=t;function l(a){ue.cal...
  class Cf (line 34) | class Cf extends Z{constructor(t){super(),J(this,t,Af,Df,Q,{label:1,valu...
    method constructor (line 34) | constructor(t){super(),J(this,t,Af,Df,Q,{label:1,value:0})}
  function cl (line 34) | function cl(n,t,r){const i=n.slice();return i[2]=t[r],i}
  function fl (line 34) | function fl(n){let t,r,i,s;return r=new Cf({props:{label:Of(n[2]),value:...
  function If (line 34) | function If(n){let t,r,i=ce(ul),s=[];for(let o=0;o<i.length;o+=1)s[o]=fl...
  function Rf (line 34) | function Rf(n,t,r){let{filters:i}=t;function s(l){return({target:o})=>{o...
  class Lf (line 34) | class Lf extends Z{constructor(t){super(),J(this,t,Rf,If,Q,{filters:0})}}
    method constructor (line 34) | constructor(t){super(),J(this,t,Rf,If,Q,{filters:0})}
  function st (line 34) | function st(n){return Array.isArray?Array.isArray(n):hl(n)==="[object Ar...
  function Ff (line 34) | function Ff(n){if(typeof n=="string")return n;let t=n+"";return t=="0"&&...
  function Bf (line 34) | function Bf(n){return n==null?"":Ff(n)}
  function Qe (line 34) | function Qe(n){return typeof n=="string"}
  function pl (line 34) | function pl(n){return typeof n=="number"}
  function $f (line 34) | function $f(n){return n===!0||n===!1||jf(n)&&hl(n)=="[object Boolean]"}
  function _l (line 34) | function _l(n){return typeof n=="object"}
  function jf (line 34) | function jf(n){return _l(n)&&n!==null}
  function Ce (line 34) | function Ce(n){return n!=null}
  function ei (line 34) | function ei(n){return!n.trim().length}
  function hl (line 34) | function hl(n){return n==null?n===void 0?"[object Undefined]":"[object N...
  class Uf (line 34) | class Uf{constructor(t){this._keys=[],this._keyMap={};let r=0;t.forEach(...
    method constructor (line 34) | constructor(t){this._keys=[],this._keyMap={};let r=0;t.forEach(i=>{let...
    method get (line 34) | get(t){return this._keyMap[t]}
    method keys (line 34) | keys(){return this._keys}
    method toJSON (line 34) | toJSON(){return JSON.stringify(this._keys)}
  function ml (line 34) | function ml(n){let t=null,r=null,i=null,s=1,l=null;if(Qe(n)||st(n))i=n,t...
  function gl (line 34) | function gl(n){return st(n)?n:n.split(".")}
  function ti (line 34) | function ti(n){return st(n)?n.join("."):n}
  function Kf (line 34) | function Kf(n,t){let r=[],i=!1;const s=(l,o,a)=>{if(Ce(l))if(!o[a])r.pus...
  function Vf (line 34) | function Vf(n=1,t=3){const r=new Map,i=Math.pow(10,t);return{get(s){cons...
  class ni (line 34) | class ni{constructor({getFn:t=j.getFn,fieldNormWeight:r=j.fieldNormWeigh...
    method constructor (line 34) | constructor({getFn:t=j.getFn,fieldNormWeight:r=j.fieldNormWeight}={}){...
    method setSources (line 34) | setSources(t=[]){this.docs=t}
    method setIndexRecords (line 34) | setIndexRecords(t=[]){this.records=t}
    method setKeys (line 34) | setKeys(t=[]){this.keys=t,this._keysMap={},t.forEach((r,i)=>{this._key...
    method create (line 34) | create(){this.isCreated||!this.docs.length||(this.isCreated=!0,Qe(this...
    method add (line 34) | add(t){const r=this.size();Qe(t)?this._addString(t,r):this._addObject(...
    method removeAt (line 34) | removeAt(t){this.records.splice(t,1);for(let r=t,i=this.size();r<i;r+=...
    method getValueForItemAtKeyId (line 34) | getValueForItemAtKeyId(t,r){return t[this._keysMap[r]]}
    method size (line 34) | size(){return this.records.length}
    method _addString (line 34) | _addString(t,r){if(!Ce(t)||ei(t))return;let i={v:t,i:r,n:this.norm.get...
    method _addObject (line 34) | _addObject(t,r){let i={i:r,$:{}};this.keys.forEach((s,l)=>{let o=s.get...
    method toJSON (line 34) | toJSON(){return{keys:this.keys,records:this.records}}
  function yl (line 34) | function yl(n,t,{getFn:r=j.getFn,fieldNormWeight:i=j.fieldNormWeight}={}...
  function Qf (line 34) | function Qf(n,{getFn:t=j.getFn,fieldNormWeight:r=j.fieldNormWeight}={}){...
  function Zn (line 34) | function Zn(n,{errors:t=0,currentLocation:r=0,expectedLocation:i=0,dista...
  function Jf (line 34) | function Jf(n=[],t=j.minMatchCharLength){let r=[],i=-1,s=-1,l=0;for(let ...
  function Zf (line 34) | function Zf(n,t,r,{location:i=j.location,distance:s=j.distance,threshold...
  function ep (line 34) | function ep(n){let t={};for(let r=0,i=n.length;r<i;r+=1){const s=n.charA...
  class bl (line 34) | class bl{constructor(t,{location:r=j.location,threshold:i=j.threshold,di...
    method constructor (line 34) | constructor(t,{location:r=j.location,threshold:i=j.threshold,distance:...
    method searchIn (line 34) | searchIn(t){const{isCaseSensitive:r,includeMatches:i}=this.options;if(...
  class ht (line 34) | class ht{constructor(t){this.pattern=t}static isMultiMatch(t){return wl(...
    method constructor (line 34) | constructor(t){this.pattern=t}
    method isMultiMatch (line 34) | static isMultiMatch(t){return wl(t,this.multiRegex)}
    method isSingleMatch (line 34) | static isSingleMatch(t){return wl(t,this.singleRegex)}
    method search (line 34) | search(){}
  function wl (line 34) | function wl(n,t){const r=n.match(t);return r?r[1]:null}
  class tp (line 34) | class tp extends ht{constructor(t){super(t)}static get type(){return"exa...
    method constructor (line 34) | constructor(t){super(t)}
    method type (line 34) | static get type(){return"exact"}
    method multiRegex (line 34) | static get multiRegex(){return/^="(.*)"$/}
    method singleRegex (line 34) | static get singleRegex(){return/^=(.*)$/}
    method search (line 34) | search(t){const r=t===this.pattern;return{isMatch:r,score:r?0:1,indice...
  class np (line 34) | class np extends ht{constructor(t){super(t)}static get type(){return"inv...
    method constructor (line 34) | constructor(t){super(t)}
    method type (line 34) | static get type(){return"inverse-exact"}
    method multiRegex (line 34) | static get multiRegex(){return/^!"(.*)"$/}
    method singleRegex (line 34) | static get singleRegex(){return/^!(.*)$/}
    method search (line 34) | search(t){const i=t.indexOf(this.pattern)===-1;return{isMatch:i,score:...
  class rp (line 34) | class rp extends ht{constructor(t){super(t)}static get type(){return"pre...
    method constructor (line 34) | constructor(t){super(t)}
    method type (line 34) | static get type(){return"prefix-exact"}
    method multiRegex (line 34) | static get multiRegex(){return/^\^"(.*)"$/}
    method singleRegex (line 34) | static get singleRegex(){return/^\^(.*)$/}
    method search (line 34) | search(t){const r=t.startsWith(this.pattern);return{isMatch:r,score:r?...
  class ip (line 34) | class ip extends ht{constructor(t){super(t)}static get type(){return"inv...
    method constructor (line 34) | constructor(t){super(t)}
    method type (line 34) | static get type(){return"inverse-prefix-exact"}
    method multiRegex (line 34) | static get multiRegex(){return/^!\^"(.*)"$/}
    method singleRegex (line 34) | static get singleRegex(){return/^!\^(.*)$/}
    method search (line 34) | search(t){const r=!t.startsWith(this.pattern);return{isMatch:r,score:r...
  class sp (line 34) | class sp extends ht{constructor(t){super(t)}static get type(){return"suf...
    method constructor (line 34) | constructor(t){super(t)}
    method type (line 34) | static get type(){return"suffix-exact"}
    method multiRegex (line 34) | static get multiRegex(){return/^"(.*)"\$$/}
    method singleRegex (line 34) | static get singleRegex(){return/^(.*)\$$/}
    method search (line 34) | search(t){const r=t.endsWith(this.pattern);return{isMatch:r,score:r?0:...
  class lp (line 34) | class lp extends ht{constructor(t){super(t)}static get type(){return"inv...
    method constructor (line 34) | constructor(t){super(t)}
    method type (line 34) | static get type(){return"inverse-suffix-exact"}
    method multiRegex (line 34) | static get multiRegex(){return/^!"(.*)"\$$/}
    method singleRegex (line 34) | static get singleRegex(){return/^!(.*)\$$/}
    method search (line 34) | search(t){const r=!t.endsWith(this.pattern);return{isMatch:r,score:r?0...
  class vl (line 34) | class vl extends ht{constructor(t,{location:r=j.location,threshold:i=j.t...
    method constructor (line 34) | constructor(t,{location:r=j.location,threshold:i=j.threshold,distance:...
    method type (line 34) | static get type(){return"fuzzy"}
    method multiRegex (line 34) | static get multiRegex(){return/^"(.*)"$/}
    method singleRegex (line 34) | static get singleRegex(){return/^(.*)$/}
    method search (line 34) | search(t){return this._bitapSearch.searchIn(t)}
  class kl (line 34) | class kl extends ht{constructor(t){super(t)}static get type(){return"inc...
    method constructor (line 34) | constructor(t){super(t)}
    method type (line 34) | static get type(){return"include"}
    method multiRegex (line 34) | static get multiRegex(){return/^'"(.*)"$/}
    method singleRegex (line 34) | static get singleRegex(){return/^'(.*)$/}
    method search (line 34) | search(t){let r=0,i;const s=[],l=this.pattern.length;for(;(i=t.indexOf...
  function up (line 34) | function up(n,t={}){return n.split(ap).map(r=>{let i=r.trim().split(op)....
  class fp (line 34) | class fp{constructor(t,{isCaseSensitive:r=j.isCaseSensitive,includeMatch...
    method constructor (line 34) | constructor(t,{isCaseSensitive:r=j.isCaseSensitive,includeMatches:i=j....
    method condition (line 34) | static condition(t,r){return r.useExtendedSearch}
    method searchIn (line 34) | searchIn(t){const r=this.query;if(!r)return{isMatch:!1,score:1};const{...
  function pp (line 34) | function pp(...n){ii.push(...n)}
  function si (line 34) | function si(n,t){for(let r=0,i=ii.length;r<i;r+=1){let s=ii[r];if(s.cond...
  function xl (line 34) | function xl(n,t,{auto:r=!0}={}){const i=s=>{let l=Object.keys(s);const o...
  function dp (line 34) | function dp(n,{ignoreFieldNorm:t=j.ignoreFieldNorm}){n.forEach(r=>{let i...
  function mp (line 34) | function mp(n,t){const r=n.matches;t.matches=[],Ce(r)&&r.forEach(i=>{if(...
  function gp (line 34) | function gp(n,t){t.score=n.score}
  function yp (line 34) | function yp(n,t,{includeMatches:r=j.includeMatches,includeScore:i=j.incl...
  class Je (line 34) | class Je{constructor(t,r={},i){this.options={...j,...r},this.options.use...
    method constructor (line 34) | constructor(t,r={},i){this.options={...j,...r},this.options.useExtende...
    method setCollection (line 34) | setCollection(t,r){if(this._docs=t,r&&!(r instanceof ni))throw new Err...
    method add (line 34) | add(t){Ce(t)&&(this._docs.push(t),this._myIndex.add(t))}
    method remove (line 34) | remove(t=()=>!1){const r=[];for(let i=0,s=this._docs.length;i<s;i+=1){...
    method removeAt (line 34) | removeAt(t){this._docs.splice(t,1),this._myIndex.removeAt(t)}
    method getIndex (line 34) | getIndex(){return this._myIndex}
    method search (line 34) | search(t,{limit:r=-1}={}){const{includeMatches:i,includeScore:s,should...
    method _searchStringList (line 34) | _searchStringList(t){const r=si(t,this.options),{records:i}=this._myIn...
    method _searchLogical (line 34) | _searchLogical(t){const r=xl(t,this.options),i=(a,u,c)=>{if(!a.childre...
    method _searchObjectList (line 34) | _searchObjectList(t){const r=si(t,this.options),{keys:i,records:s}=thi...
    method _findMatches (line 34) | _findMatches({key:t,value:r,searcher:i}){if(!Ce(r))return[];let s=[];i...
  function Sl (line 34) | function Sl(n){let t,r,i;function s(o){n[8](o)}let l={parent:n[1],$$slot...
  function Nl (line 34) | function Nl(n){let t,r,i;return{c(){t=x("button"),E(t,"class","metadata-...
  function bp (line 34) | function bp(n){let t,r,i=n[0].type==="lora"&&Nl(n);return{c(){t=x("div")...
  function wp (line 34) | function wp(n){let t,r=n[0].name+"",i,s,l,o,a,u,c,f=n[1]!=null&&Sl(n);re...
  function vp (line 34) | function vp(n,t,r){let{data:i}=t,{index:s}=t,l=null,o=!1;const{prompts:{...
  class kp (line 34) | class kp extends Z{constructor(t){super(),J(this,t,vp,wp,Q,{data:0,index...
    method constructor (line 34) | constructor(t){super(),J(this,t,vp,wp,Q,{data:0,index:5})}
  function Ep (line 34) | function Ep(n){let t,r,i=n[3](n[0])+"",s,l,o,a=n[4](n[0])+"",u,c,f,p;ret...
  function Tp (line 34) | function Tp(n,t,r){let{data:i}=t,{index:s}=t,l=null;const{prompts:{posit...
  class xp (line 34) | class xp extends Z{constructor(t){super(),J(this,t,Tp,Ep,Q,{data:0,index...
    method constructor (line 34) | constructor(t){super(),J(this,t,Tp,Ep,Q,{data:0,index:5})}
  function Pl (line 34) | function Pl(n,t,r){const i=n.slice();return i[14]=t[r],i}
  function Ml (line 34) | function Ml(n){let t,r,i;function s(o){n[9](o)}let l={parent:n[1],$$slot...
  function Ol (line 34) | function Ol(n){let t,r=Ht(n[14])+"",i,s,l=[qt(n[14]),{class:"prompt-item...
  function Sp (line 34) | function Sp(n){let t,r=ce(n[3]),i=[];for(let s=0;s<r.length;s+=1)i[s]=Ol...
  function Np (line 34) | function Np(n){let t,r=n[0].label+"",i,s,l,o,a,u,c=n[1]!=null&&Ml(n);ret...
  function Pp (line 34) | function Pp(n,t,r){let i;he(n,Jt,y=>r(6,i=y));let{data:s}=t,{index:l}=t;...
  method constructor (line 34) | constructor(t){super(),J(this,t,Pp,Np,Q,{data:0,index:5})}
  function Op (line 34) | function Op(n){let t,r;return t=new Mp({props:{index:n[1],data:n[0].valu...
  function Dp (line 34) | function Dp(n){let t,r;return t=new xp({props:{index:n[1],data:n[0].valu...
  function Ap (line 34) | function Ap(n){let t,r;return t=new kp({props:{index:n[1],data:n[0].valu...
  function Cp (line 34) | function Cp(n){let t,r,i,s;const l=[Ap,Dp,Op],o=[];function a(u,c){retur...
  function Ip (line 34) | function Ip(n,t,r){let{data:i}=t,{index:s}=t;return n.$$set=l=>{"data"in...
  class Rp (line 34) | class Rp extends Z{constructor(t){super(),J(this,t,Ip,Cp,Q,{data:0,index...
    method constructor (line 34) | constructor(t){super(),J(this,t,Ip,Cp,Q,{data:0,index:1})}
  function Dl (line 34) | function Dl(n,t,r){const i=n.slice();return i[19]=t[r],i[21]=r,i}
  function Al (line 34) | function Al(n){let t,r,i,s;return r=new Rp({props:{index:n[21],data:n[19...
  function Lp (line 34) | function Lp(n){let t,r,i,s=ce(n[2]),l=[];for(let a=0;a<s.length;a+=1)l[a...
  function zp (line 34) | function zp(n,t,r){let i,s,l,o,a,u;he(n,yr,k=>r(8,i=k)),he(n,ea,k=>r(9,s...
  class Fp (line 34) | class Fp extends Z{constructor(t){super(),J(this,t,zp,Lp,Q,{promptText:5...
    method constructor (line 34) | constructor(t){super(),J(this,t,zp,Lp,Q,{promptText:5,filters:6})}
  function Bp (line 34) | function Bp(n){let t,r,i,s,l;function o(u){n[2](u)}let a={};return n[1]!...
  function $p (line 34) | function $p(n,t,r){let{promptText:i}=t,s=["all"];function l(o){s=o,r(1,s...
  class jp (line 34) | class jp extends Z{constructor(t){super(),J(this,t,$p,Bp,Q,{promptText:0...
    method constructor (line 34) | constructor(t){super(),J(this,t,$p,Bp,Q,{promptText:0})}
  function Wp (line 34) | function Wp(n){let t,r,i,s,l,o;function a(c){n[1](c)}let u={};return n[0...
  function Xp (line 34) | function Xp(n,t,r){let i="";fr(Ut,{pseudoFocus:Pe(-1),currentFocus:Pe(nu...
  class Hp (line 34) | class Hp extends Z{constructor(t){super(),J(this,t,Xp,Wp,Q,{})}}
    method constructor (line 34) | constructor(t){super(),J(this,t,Xp,Wp,Q,{})}
  function qp (line 34) | function qp(n){let t,r,i,s,l,o,a,u,c;return i=new ol({}),l=new ol({props...
  function Yp (line 34) | function Yp(n,t,r){let{active:i}=t;return n.$$set=s=>{"active"in s&&r(0,...
  class Up (line 34) | class Up extends Z{constructor(t){super(),J(this,t,Yp,qp,Q,{active:0})}}
    method constructor (line 34) | constructor(t){super(),J(this,t,Yp,qp,Q,{active:0})}
  function Cl (line 34) | function Cl(n,t,r){const i=n.slice();return i[8]=t[r],i}
  function Il (line 34) | function Il(n,t,r){const i=n.slice();return i[11]=t[r],i}
  function Kp (line 34) | function Kp(n){let t;return{c(){t=x("div"),t.textContent=`${Ma.translate...
  function Gp (line 34) | function Gp(n){let t,r=ce(n[0].tags),i=[];for(let s=0;s<r.length;s+=1)i[...
  function Rl (line 34) | function Rl(n){let t,r=n[11]+"",i;return{c(){t=x("div"),i=re(r),E(t,"cla...
  function Ll (line 34) | function Ll(n){let t,r=Ht(n[8])+"",i,s,l=[qt(n[8]),{class:"prompt-item"}...
  function Vp (line 34) | function Vp(n){let t,r,i=n[0].label+"",s,l,o,a,u,c,f,p,_;function h(b,w)...
  function Qp (line 34) | function Qp(n,t,r){let i;he(n,Jt,p=>r(5,i=p));let{myPrompt:s}=t,{selecta...
  class Jp (line 34) | class Jp extends Z{constructor(t){super(),J(this,t,Qp,Vp,Q,{myPrompt:0,s...
    method constructor (line 34) | constructor(t){super(),J(this,t,Qp,Vp,Q,{myPrompt:0,selectable:1})}
  function zl (line 34) | function zl(n,t,r){const i=n.slice();return i[9]=t[r],i}
  function Fl (line 34) | function Fl(n){let t,r,i,s,l=n[0]>1&&Bl(n),o=n[0]>=4&&$l(n),a=ce(n[3]),u...
  function Bl (line 34) | function Bl(n){let t,r,i;return{c(){t=x("button"),t.textContent=`${Aa.tr...
  function $l (line 34) | function $l(n){let t,r,i,s,l,o=n[0]>=5&&jl();return{c(){t=x("button"),t....
  function jl (line 34) | function jl(n){let t;return{c(){t=x("span"),t.textContent=`${Oa.translat...
  function Wl (line 34) | function Wl(n){let t,r=n[9]+"",i,s,l;function o(){return n[7](n[9])}retu...
  function Xl (line 34) | function Xl(n){let t,r,i;return{c(){t=x("button"),t.textContent=`${Da.tr...
  function Zp (line 34) | function Zp(n){let t,r=n[1]>0&&Fl(n);return{c(){r&&r.c(),t=Re()},m(i,s){...
  function e_ (line 34) | function e_(n,t,r){let{page:i}=t,{totalCount:s}=t,{displayLimit:l}=t,o,a...
  class Hl (line 34) | class Hl extends Z{constructor(t){super(),J(this,t,e_,Zp,Q,{page:0,total...
    method constructor (line 34) | constructor(t){super(),J(this,t,e_,Zp,Q,{page:0,totalCount:1,displayLi...
  function Yl (line 34) | function Yl(n){let t,r,i,s,l,o,a,u,c,f=`${n[3]}px`,p=`${n[4]}px`,_,h,m;c...
  function t_ (line 34) | function t_(n){let t,r,i,s,l=n[0]&&Yl(n);return{c(){l&&l.c(),t=Re()},m(o...
  function n_ (line 34) | function n_(n,t,r){let{$$slots:i={},$$scope:s}=t,{title:l}=t,{show:o}=t,...
  class r_ (line 34) | class r_ extends Z{constructor(t){super(),J(this,t,n_,t_,Q,{title:1,show...
    method constructor (line 34) | constructor(t){super(),J(this,t,n_,t_,Q,{title:1,show:0})}
  function Ul (line 34) | function Ul(n,t,r){const i=n.slice();return i[25]=t[r],i[27]=r,i}
  function Kl (line 34) | function Kl(n,t,r){const i=n.slice();return i[28]=t[r],i}
  function Gl (line 34) | function Gl(n){let t,r;return{c(){t=x("span"),r=re(n[1]),E(t,"class","la...
  function Vl (line 34) | function Vl(n){let t,r,i=n[28]+"",s,l,o,a,u;return{c(){t=x("button"),r=x...
  function Ql (line 34) | function Ql(n){let t,r,i;function s(o){n[22](o)}let l={parent:n[4],optio...
  function Jl (line 34) | function Jl(n){let t,r,i,s,l,o=n[25]+"",a,u,c,f;return{c(){t=x("li"),r=x...
  function i_ (line 34) | function i_(n){let t,r,i,s=ce(n[2]),l=[];for(let o=0;o<s.length;o+=1)l[o...
  function s_ (line 34) | function s_(n){let t,r,i,s,l,o,a,u,c,f,p=n[1]&&Gl(n),_=ce(n[0]),h=[];for...
  function Zl (line 34) | function Zl(n,t){return t.includes(n)}
  function l_ (line 34) | function l_(n,t,r){let{label:i=null}=t,{values:s}=t,{list:l=[]}=t,{optio...
  class o_ (line 34) | class o_ extends Z{constructor(t){super(),J(this,t,l_,s_,Q,{label:1,valu...
    method constructor (line 34) | constructor(t){super(),J(this,t,l_,s_,Q,{label:1,values:0,list:17,opti...
  function eo (line 34) | function eo(n){let t,r;return{c(){t=x("span"),r=re(n[1]),E(t,"class","la...
  function a_ (line 34) | function a_(n){let t,r,i,s,l,o=n[1]&&eo(n);return{c(){t=x("div"),o&&o.c(...
  function u_ (line 34) | function u_(n,t,r){let{label:i=null}=t,{value:s=""}=t,{options:l=null}=t...
  class c_ (line 34) | class c_ extends Z{constructor(t){super(),J(this,t,u_,a_,Q,{label:1,valu...
    method constructor (line 34) | constructor(t){super(),J(this,t,u_,a_,Q,{label:1,value:0,options:3})}
  function f_ (line 34) | function f_(n){let t,r,i,s,l,o,a,u,c,f,p,_,h=ma.translate()+"",m,g,d,y,b...
  function p_ (line 34) | function p_(n){let t,r,i,s,l,o,a;function u(f){n[10](f)}let c={title:Ui....
  function __ (line 34) | function __(n,t,r){let i,s;he(n,yr,d=>r(11,i=d)),he(n,sa,d=>r(3,s=d));le...
  class h_ (line 34) | class h_ extends Z{constructor(t){super(),J(this,t,__,p_,Q,{closePopup:0...
    method constructor (line 34) | constructor(t){super(),J(this,t,__,p_,Q,{closePopup:0})}
    method closePopup (line 34) | get closePopup(){return this.$$.ctx[0]}
  function d_ (line 34) | function d_(n){let t=Ra.translate()+"",r;return{c(){r=re(t)},m(i,s){M(i,...
  function m_ (line 34) | function m_(n){let t=ya.translate()+"",r;return{c(){r=re(t)},m(i,s){M(i,...
  function to (line 34) | function to(n){let t,r=ba.translate()+"",i,s,l,o;return{c(){t=x("button"...
  function g_ (line 34) | function g_(n){let t,r,i,s,l;function o(f,p){return f[0]?m_:d_}let a=o(n...
  function y_ (line 34) | function y_(n,t,r){let{deleteMode:i}=t,{selectedMyPrompts:s}=t;function ...
  class b_ (line 34) | class b_ extends Z{constructor(t){super(),J(this,t,y_,g_,Q,{deleteMode:0...
    method constructor (line 34) | constructor(t){super(),J(this,t,y_,g_,Q,{deleteMode:0,selectedMyPrompt...
  function no (line 34) | function no(n,t,r){const i=n.slice();return i[18]=t[r],i}
  function ro (line 34) | function ro(n){let t,r,i,s;function l(u){n[14](u)}function o(u){n[15](u)...
  function w_ (line 34) | function w_(n){let t;return{c(){t=x("div"),t.textContent=`${ka.translate...
  function v_ (line 34) | function v_(n){let t,r,i,s,l,o,a,u;function c(d){n[16](d)}let f={totalCo...
  function io (line 34) | function io(n){let t,r;return t=new Jp({props:{myPrompt:n[18],selectable...
  function k_ (line 34) | function k_(n){let t,r,i,s,l,o,a,u,c,f,p;function _(w){n[11](w)}let h={o...
  function E_ (line 34) | function E_(n,t,r){let i;he(n,yr,O=>r(7,i=O));let{active:s}=t,l=null,o="...
  class T_ (line 34) | class T_ extends Z{constructor(t){super(),J(this,t,E_,k_,Q,{active:0})}}
    method constructor (line 34) | constructor(t){super(),J(this,t,E_,k_,Q,{active:0})}
  function x_ (line 34) | function x_(n){let t,r,i,s,l,o,a,u,c,f,p,_,h,m;return u=new Up({props:{a...
  function S_ (line 34) | function S_(n,t,r){let{tabName:i}=t;const s=Pe([]),l=Pe([]);fr(gt,{tabNa...
  class N_ (line 34) | class N_ extends Z{constructor(t){super(),J(this,t,S_,x_,Q,{tabName:0})}}
    method constructor (line 34) | constructor(t){super(),J(this,t,S_,x_,Q,{tabName:0})}
  function P_ (line 34) | function P_(){yn("#better-prompt-toast")||new da({target:gradioApp()})}
  function M_ (line 34) | function M_(n){window.setTimeout(function t(){if(Object.keys(opts).lengt...
  function O_ (line 34) | function O_(n){if(yn(`#${n}-better-prompt`))return;const t=Be(`#${n}_top...
  function D_ (line 34) | function D_(n){Oo("better_prompt_hide_original_prompt",t=>{const r=Be(`#...

FILE: scripts/better_prompt.py
  class MyPrompt (line 17) | class MyPrompt(BaseModel):
  function print_version (line 37) | def print_version() -> None:
  function do_update_my_prompts (line 57) | def do_update_my_prompts(my_prompts: List[MyPrompt]) -> Dict[str, Any]:
  function refresh_available_localization (line 66) | def refresh_available_localization() -> None:
  function load_localization (line 73) | def load_localization() -> None:
  function _ (line 87) | def _(text: str) -> str:
  function filter_none_fields (line 93) | def filter_none_fields(obj: Any) -> Any:
  function on_app_started (line 102) | def on_app_started(demo: Optional[gr.Blocks], app: FastAPI) -> None:
  function on_ui_settings (line 151) | def on_ui_settings():
  function initialize (line 161) | def initialize() -> None:
Condensed preview — 159 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,578K chars).
[
  {
    "path": ".eslintignore",
    "chars": 218,
    "preview": ".DS_Store\nnode_modules\n/javascript\n.env\n.env.*\n!.env.example\n\n# Ignore files for PNPM, NPM and YARN\npnpm-lock.yaml\npacka"
  },
  {
    "path": ".eslintrc.cjs",
    "chars": 743,
    "preview": "module.exports = {\n  parser: \"@typescript-eslint/parser\",\n  parserOptions: {\n    tsconfigRootDir: __dirname,\n    project"
  },
  {
    "path": ".gitignore",
    "chars": 327,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndev-scri"
  },
  {
    "path": ".husky/pre-commit",
    "chars": 75,
    "preview": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\npnpm exec lint-staged\n"
  },
  {
    "path": ".lintstagedrc.json",
    "chars": 111,
    "preview": "{\n  \"client-src/**/*.{js,ts,svelte}\": [\n    \"pnpm run check\",\n    \"prettier --write\",\n    \"eslint --fix\"\n  ]\n}\n"
  },
  {
    "path": ".prettierignore",
    "chars": 143,
    "preview": ".DS_Store\nnode_modules\n/javascript\n.env\n.env.*\n!.env.example\n\n# Ignore files for PNPM, NPM and YARN\npnpm-lock.yaml\npacka"
  },
  {
    "path": ".prettierrc",
    "chars": 322,
    "preview": "{\n  \"useTabs\": false,\n  \"singleQuote\": false,\n  \"trailingComma\": \"es5\",\n  \"printWidth\": 100,\n  \"plugins\": [\n    \"prettie"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1683,
    "preview": "## v0.4.1\n### Add\n- Added a cancel button to the registration dialog of \"My Prompt\".\n## Fix\n- Fixed an issue where the o"
  },
  {
    "path": "LICENSE",
    "chars": 1073,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2023 EideeHi\n\nPermission is hereby granted, free of charge, to any person obtaining"
  },
  {
    "path": "README.adoc",
    "chars": 8191,
    "preview": "= Better Prompt\n\nEnglish | link:docs/README-ja.adoc[日本語]\n\nBetter Prompt is an extension of the https://github.com/AUTOMA"
  },
  {
    "path": "client-src/@types/global.d.ts",
    "chars": 246,
    "preview": "type ExtensionAvailableTab = \"txt2img\" | \"img2img\";\ntype WebUiTab = ExtensionAvailableTab | \"other\";\n\ntype Nullable<T> ="
  },
  {
    "path": "client-src/@types/modules.ts",
    "chars": 399,
    "preview": "declare module \"#/widgets/Toast.svelte\" {\n  export { SvelteComponentDev as default } from \"svelte/internal\";\n\n  export t"
  },
  {
    "path": "client-src/@types/vite-env.d.ts",
    "chars": 71,
    "preview": "/// <reference types=\"svelte\" />\n/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "client-src/@types/webui.d.ts",
    "chars": 786,
    "preview": "// injected-script\ndeclare let localization: Record<string, string>;\n\n// script.js\ndeclare function gradioApp(): Documen"
  },
  {
    "path": "client-src/components/better-prompt/BetterPrompt.svelte",
    "chars": 2205,
    "preview": "<script lang=\"ts\">\n  import type { Prompt } from \"@/libs/prompt\";\n  import { setContext } from \"svelte\";\n  import { writ"
  },
  {
    "path": "client-src/components/better-prompt/_logic/adjustPrompt.ts",
    "chars": 453,
    "preview": "import type { Prompt } from \"@/libs/prompt\";\nimport type { ExtraNetworksData } from \"@/libs/extra-networks\";\n\nexport fun"
  },
  {
    "path": "client-src/components/better-prompt/_logic/betterPrompt.ts",
    "chars": 1858,
    "preview": "import type { ExtraNetworksData, ExtraNetworksType } from \"@/libs/extra-networks\";\nimport { type Readable, readable } fr"
  },
  {
    "path": "client-src/components/better-prompt/_logic/context.ts",
    "chars": 300,
    "preview": "import type { Writable } from \"svelte/store\";\nimport type { Prompt } from \"@/libs/prompt\";\n\nexport type BetterPromptCont"
  },
  {
    "path": "client-src/components/better-prompt/_logic/danbooruTags.ts",
    "chars": 399,
    "preview": "import { type Readable, writable } from \"svelte/store\";\nimport type { DanbooruTag } from \"@/libs/danbooru\";\n\nconst _danb"
  },
  {
    "path": "client-src/components/better-prompt/_logic/extraNetworks.ts",
    "chars": 780,
    "preview": "import type { ExtraNetworksData } from \"@/libs/extra-networks\";\nimport { type Readable, writable } from \"svelte/store\";\n"
  },
  {
    "path": "client-src/components/better-prompt/_logic/messages.ts",
    "chars": 3771,
    "preview": "import { t } from \"@/libs/util/webui\";\n\nexport interface Message {\n  translate(args?: Array<string | number | boolean>):"
  },
  {
    "path": "client-src/components/better-prompt/_logic/myPrompts.ts",
    "chars": 1305,
    "preview": "import { type Readable, writable } from \"svelte/store\";\nimport type { MyPrompt } from \"@/libs/my-prompt\";\nimport { updat"
  },
  {
    "path": "client-src/components/better-prompt/_logic/prompt.ts",
    "chars": 1707,
    "preview": "import { type ExtraNetworksPrompt, type Prompt, toString } from \"@/libs/prompt\";\nimport {\n  alternatePromptToString,\n  e"
  },
  {
    "path": "client-src/components/better-prompt/my-prompt/AddNewMyPrompt.svelte",
    "chars": 2417,
    "preview": "<script lang=\"ts\">\n  import type { MyPrompt } from \"@/libs/my-prompt\";\n  import { createEventDispatcher } from \"svelte\";"
  },
  {
    "path": "client-src/components/better-prompt/my-prompt/MyPrompt.svelte",
    "chars": 3627,
    "preview": "<script lang=\"ts\">\n  import type { MyPrompt } from \"@/libs/my-prompt\";\n  import Fuse from \"fuse.js\";\n  import { myPrompt"
  },
  {
    "path": "client-src/components/better-prompt/my-prompt/MyPromptItem.svelte",
    "chars": 3103,
    "preview": "<script lang=\"ts\">\n  import type { MyPrompt } from \"@/libs/my-prompt\";\n  import { createEventDispatcher } from \"svelte\";"
  },
  {
    "path": "client-src/components/better-prompt/my-prompt/SelectAndDeleteMyPrompt.svelte",
    "chars": 992,
    "preview": "<script lang=\"ts\">\n  import type { MyPrompt } from \"@/libs/my-prompt\";\n  import { removeMyPrompts } from \"#/better-promp"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/PromptEdit.svelte",
    "chars": 739,
    "preview": "<script lang=\"ts\">\n  import Editor from \"#/better-prompt/prompt-edit/editor/Editor.svelte\";\n  import Input from \"#/bette"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/_logic/purgeEmphasizedPrompt.ts",
    "chars": 463,
    "preview": "import type {\n  EmphasizedNegativePrompt,\n  EmphasizedPositivePrompt,\n  InnerPrompt,\n} from \"@/libs/prompt\";\n\nexport fun"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/_logic/undoRedo.ts",
    "chars": 2935,
    "preview": "import type { Prompt } from \"@/libs/prompt\";\nimport type { Writable } from \"svelte/store\";\nimport { isEquals } from \"@/l"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/editor/Editor.svelte",
    "chars": 3166,
    "preview": "<script lang=\"ts\">\n  import { getContext, onMount, setContext } from \"svelte\";\n  import { isEquals, parsePrompt, toStrin"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/editor/TokenCounter.svelte",
    "chars": 1251,
    "preview": "<script lang=\"ts\">\n  import { getContext } from \"svelte\";\n  import { readable } from \"svelte/store\";\n  import { getEleme"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/editor/_logic/context.ts",
    "chars": 220,
    "preview": "import type { Writable } from \"svelte/store\";\nimport type { Prompt } from \"@/libs/prompt\";\n\nexport type EditorContext = "
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/editor/prompt-list/ListItem.svelte",
    "chars": 970,
    "preview": "<script lang=\"ts\">\n  import type { Prompt } from \"@/libs/prompt\";\n  import { createDataset, getTextContent } from \"#/bet"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/editor/prompt-list/PromptList.svelte",
    "chars": 1913,
    "preview": "<script lang=\"ts\">\n  import Sortable from \"sortablejs\";\n  import { getContext, onMount } from \"svelte\";\n  import { sortB"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/editor/prompt-list/WeightInput.svelte",
    "chars": 757,
    "preview": "<script lang=\"ts\">\n  import type { Prompt } from \"@/libs/prompt\";\n  import { weight as weightLabel } from \"#/better-prom"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/editor/prompt-list/_logic/listItem.ts",
    "chars": 348,
    "preview": "import { type Prompt } from \"@/libs/prompt\";\n\nexport function isPopupEnabled(prompt: Prompt): boolean {\n  switch (prompt"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/editor/prompt-list/_logic/promptList.ts",
    "chars": 1136,
    "preview": "import type { Prompt, ScheduledPrompt } from \"@/libs/prompt\";\nimport { toString } from \"@/libs/prompt\";\nimport { purgeEm"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/editor/prompt-list/_logic/weightInput.ts",
    "chars": 2160,
    "preview": "import type { EmphasizedNegativePrompt, EmphasizedPositivePrompt, Prompt } from \"@/libs/prompt\";\nimport Big from \"big.js"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/input/Input.svelte",
    "chars": 666,
    "preview": "<script lang=\"ts\">\n  import { setContext } from \"svelte\";\n  import { writable } from \"svelte/store\";\n  import { type Pro"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/input/PromptInput.svelte",
    "chars": 2936,
    "preview": "<script lang=\"ts\">\n  import { getContext } from \"svelte\";\n  import { parsePrompt } from \"@/libs/prompt\";\n  import { type"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/input/_logic/context.ts",
    "chars": 376,
    "preview": "import type { Writable } from \"svelte/store\";\n\nexport type PseudoFocusMoveDirection = \"next\" | \"previous\" | \"up\" | \"down"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/input/suggest/DanbooruItem.svelte",
    "chars": 2702,
    "preview": "<script lang=\"ts\">\n  import type { DanbooruTag } from \"@/libs/danbooru\";\n  import { getContext } from \"svelte\";\n  import"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/input/suggest/ExtraNetworksItem.svelte",
    "chars": 2964,
    "preview": "<script lang=\"ts\">\n  import type { ExtraNetworksData } from \"@/libs/extra-networks\";\n  import { getContext } from \"svelt"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/input/suggest/Filters.svelte",
    "chars": 1036,
    "preview": "<script lang=\"ts\">\n  import { type FilterType, FilterTypes, typeToLabel } from \"./_logic/filters\";\n  import Checkbox fro"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/input/suggest/List.svelte",
    "chars": 5469,
    "preview": "<script lang=\"ts\">\n  import type { FilterType } from \"./_logic/filters\";\n  import type { SuggestData } from \"./_logic/su"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/input/suggest/ListItem.svelte",
    "chars": 568,
    "preview": "<script lang=\"ts\">\n  import type { SuggestData } from \"./_logic/suggest\";\n  import ExtraNetworksItem from \"./ExtraNetwor"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/input/suggest/MyPromptItem.svelte",
    "chars": 2189,
    "preview": "<script lang=\"ts\">\n  import type { MyPrompt } from \"@/libs/my-prompt\";\n  import { getContext } from \"svelte\";\n  import {"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/input/suggest/Suggest.svelte",
    "chars": 293,
    "preview": "<script lang=\"ts\">\n  import { type FilterType } from \"./_logic/filters\";\n  import Filters from \"./Filters.svelte\";\n  imp"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/input/suggest/_logic/filters.ts",
    "chars": 1231,
    "preview": "import {\n  suggestFilterAll,\n  suggestFilterDanbooruCharacter,\n  suggestFilterDanbooruCopyright,\n  suggestFilterDanbooru"
  },
  {
    "path": "client-src/components/better-prompt/prompt-edit/input/suggest/_logic/suggest.ts",
    "chars": 730,
    "preview": "import type { ExtraNetworksData } from \"@/libs/extra-networks\";\nimport type { DanbooruTag } from \"@/libs/danbooru\";\nimpo"
  },
  {
    "path": "client-src/components/widgets/Checkbox.svelte",
    "chars": 1475,
    "preview": "<script lang=\"ts\">\n  export let label = \"\";\n  export let value = false;\n</script>\n\n<label class=\"checkbox\">\n  <input bin"
  },
  {
    "path": "client-src/components/widgets/MultiInput.svelte",
    "chars": 7517,
    "preview": "<script lang=\"ts\">\n  import { toggleValue } from \"@/libs/util/array\";\n  import Popup from \"./Popup.svelte\";\n  import { g"
  },
  {
    "path": "client-src/components/widgets/NumberInput.svelte",
    "chars": 2380,
    "preview": "<script lang=\"ts\">\n  import Big from \"big.js\";\n  import { dispatchEvent } from \"@/libs/util/webui\";\n\n  type Options = {\n"
  },
  {
    "path": "client-src/components/widgets/Pagenation.svelte",
    "chars": 2025,
    "preview": "<script lang=\"ts\">\n  import {\n    pagenationEllipsis,\n    pagenationNext,\n    pagenationPrevious,\n  } from \"#/better-pro"
  },
  {
    "path": "client-src/components/widgets/Popup.svelte",
    "chars": 2123,
    "preview": "<script lang=\"ts\">\n  import { onDestroy } from \"svelte\";\n  import { getScreenPosition, hasChild } from \"@/libs/util/dom\""
  },
  {
    "path": "client-src/components/widgets/PopupWindow.svelte",
    "chars": 2986,
    "preview": "<script lang=\"ts\">\n  import { onDestroy } from \"svelte\";\n  import { scale } from \"svelte/transition\";\n  import { quintOu"
  },
  {
    "path": "client-src/components/widgets/TextArea.svelte",
    "chars": 1125,
    "preview": "<script lang=\"ts\">\n  export let label: Nullable<string> = null;\n  export let value = \"\";\n  export let options: Nullable<"
  },
  {
    "path": "client-src/components/widgets/TextInput.svelte",
    "chars": 1025,
    "preview": "<script lang=\"ts\">\n  export let label: Nullable<string> = null;\n  export let value = \"\";\n  export let options: Nullable<"
  },
  {
    "path": "client-src/components/widgets/Toast.svelte",
    "chars": 1733,
    "preview": "<script context=\"module\" lang=\"ts\">\n  import type { ToastMessage } from \"#/widgets/Toast.svelte\";\n  import { writable } "
  },
  {
    "path": "client-src/libs/api/getDanbooruTags.ts",
    "chars": 770,
    "preview": "import { type DanbooruTag, isDanbooruTag } from \"@/libs/danbooru\";\n\nexport function getDanbooruTags(): Promise<DanbooruT"
  },
  {
    "path": "client-src/libs/api/getExtraNetworks.ts",
    "chars": 1194,
    "preview": "import type { ExtraNetworksData, ExtraNetworksType } from \"../extra-networks\";\nimport { omitNulls } from \"@/libs/util/ar"
  },
  {
    "path": "client-src/libs/api/getLocalization.ts",
    "chars": 562,
    "preview": "export function getLocalization(): Promise<Record<string, string>> {\n  const promise = fetch(`/better-prompt-api/v1/get-"
  },
  {
    "path": "client-src/libs/api/getMyPrompts.ts",
    "chars": 674,
    "preview": "import type { MyPrompt } from \"@/libs/my-prompt\";\nimport { isArray, isObject, isString } from \"@/libs/util/types\";\n\nexpo"
  },
  {
    "path": "client-src/libs/api/index.ts",
    "chars": 261,
    "preview": "export { getDanbooruTags } from \"./getDanbooruTags\";\nexport { getExtraNetworks } from \"./getExtraNetworks\";\nexport { get"
  },
  {
    "path": "client-src/libs/api/updateMyPrompts.ts",
    "chars": 592,
    "preview": "import type { MyPrompt } from \"../my-prompt\";\nimport { isBoolean, isObject } from \"../util/types\";\n\nexport function upda"
  },
  {
    "path": "client-src/libs/danbooru/danbooruTag.ts",
    "chars": 92,
    "preview": "export type DanbooruTag = {\n  name: string;\n  post_count: number;\n  category: 0 | 3 | 4;\n};\n"
  },
  {
    "path": "client-src/libs/danbooru/index.ts",
    "chars": 144,
    "preview": "export type { DanbooruTag } from \"./danbooruTag\";\nexport { isDanbooruTag } from \"./isDanbooruTag\";\nexport { tagToPrompt "
  },
  {
    "path": "client-src/libs/danbooru/isDanbooruTag.ts",
    "chars": 376,
    "preview": "import type { DanbooruTag } from \"./danbooruTag\";\n\nexport function isDanbooruTag(obj: unknown): obj is DanbooruTag {\n  i"
  },
  {
    "path": "client-src/libs/danbooru/tagToPrompt.ts",
    "chars": 344,
    "preview": "export function tagToPrompt(tag: string): string {\n  return replaceAll(tag, {\n    _: \" \",\n    \"(\": \"\\\\(\",\n    \")\": \"\\\\)\""
  },
  {
    "path": "client-src/libs/extra-networks/extraNetworks.ts",
    "chars": 188,
    "preview": "export type ExtraNetworksType = \"textual-inversion\" | \"lora\";\n\nexport type ExtraNetworksData = {\n  type: ExtraNetworksTy"
  },
  {
    "path": "client-src/libs/extra-networks/index.ts",
    "chars": 77,
    "preview": "export type { ExtraNetworksData, ExtraNetworksType } from \"./extraNetworks\";\n"
  },
  {
    "path": "client-src/libs/my-prompt/index.ts",
    "chars": 44,
    "preview": "export type { MyPrompt } from \"./myPrompt\";\n"
  },
  {
    "path": "client-src/libs/my-prompt/myPrompt.ts",
    "chars": 83,
    "preview": "export interface MyPrompt {\n  label: string;\n  tags: string[];\n  prompt: string;\n}\n"
  },
  {
    "path": "client-src/libs/prompt/allPrompt.ts",
    "chars": 270,
    "preview": "import type { BasicPrompt } from \"./basicPrompt\";\nimport type { ExtraNetworksPrompt } from \"./extraNetworksPrompt\";\nimpo"
  },
  {
    "path": "client-src/libs/prompt/alternatePrompt.ts",
    "chars": 189,
    "preview": "import type { Prompt } from \"./prompt\";\nimport type { InnerPrompt } from \"./innerPrompt\";\n\nexport interface AlternatePro"
  },
  {
    "path": "client-src/libs/prompt/basicPrompt.ts",
    "chars": 321,
    "preview": "import type { AlternatePrompt } from \"./alternatePrompt\";\nimport type { EmphasizedPrompt } from \"./emphasizedPrompt\";\nim"
  },
  {
    "path": "client-src/libs/prompt/emphasizedPrompt.ts",
    "chars": 582,
    "preview": "import type { Prompt } from \"./prompt\";\nimport type { InnerPrompt } from \"./innerPrompt\";\n\nexport type EmphasizedPrompt "
  },
  {
    "path": "client-src/libs/prompt/extraNetworksPrompt.ts",
    "chars": 157,
    "preview": "import type { Prompt } from \"./prompt\";\n\nexport interface ExtraNetworksPrompt extends Prompt {\n  type: \"extra-networks\";"
  },
  {
    "path": "client-src/libs/prompt/index.ts",
    "chars": 666,
    "preview": "export type { PromptType } from \"./prompt\";\nexport type { AllPrompt as Prompt } from \"./allPrompt\";\nexport type { Altern"
  },
  {
    "path": "client-src/libs/prompt/innerPrompt.ts",
    "chars": 172,
    "preview": "import type { BasicPrompt } from \"./basicPrompt\";\nimport type { PromptCombination } from \"./promptCombination\";\n\nexport "
  },
  {
    "path": "client-src/libs/prompt/plainPrompt.ts",
    "chars": 123,
    "preview": "import type { Prompt } from \"./prompt\";\n\nexport interface PlainPrompt extends Prompt {\n  type: \"plain\";\n  value: string;"
  },
  {
    "path": "client-src/libs/prompt/prompt.ts",
    "chars": 236,
    "preview": "export type PromptType =\n  | \"alternate\"\n  | \"combination\"\n  | \"emphasized-positive\"\n  | \"emphasized-negative\"\n  | \"emph"
  },
  {
    "path": "client-src/libs/prompt/promptCombination.ts",
    "chars": 217,
    "preview": "import type { Prompt } from \"@/libs/prompt/prompt\";\nimport type { BasicPrompt } from \"@/libs/prompt/basicPrompt\";\n\nexpor"
  },
  {
    "path": "client-src/libs/prompt/scheduledPrompt.ts",
    "chars": 222,
    "preview": "import type { Prompt } from \"./prompt\";\nimport type { InnerPrompt } from \"./innerPrompt\";\n\nexport interface ScheduledPro"
  },
  {
    "path": "client-src/libs/prompt/util/concatPrompt.ts",
    "chars": 503,
    "preview": "/**\n * A regular expression to match a trailing comma followed by optional whitespace.\n */\nconst TRAILING_COMMA_REGEX = "
  },
  {
    "path": "client-src/libs/prompt/util/index.ts",
    "chars": 217,
    "preview": "export { concatPrompt } from \"./concatPrompt\";\nexport { isEquals } from \"./isEquals\";\nexport { isPromptType } from \"./is"
  },
  {
    "path": "client-src/libs/prompt/util/isEquals/alternatePromptEquals.ts",
    "chars": 346,
    "preview": "import type { AlternatePrompt } from \"@/libs/prompt\";\nimport { isEquals } from \"./index\";\n\nexport function alternateProm"
  },
  {
    "path": "client-src/libs/prompt/util/isEquals/emphasizedPromptEquals.ts",
    "chars": 1679,
    "preview": "import type {\n  EmphasizedPrompt,\n  EmphasizedNegativePrompt,\n  EmphasizedPositivePrompt,\n  EmphasizedWeightedPrompt,\n} "
  },
  {
    "path": "client-src/libs/prompt/util/isEquals/extraNetworksPromptEquals.ts",
    "chars": 857,
    "preview": "import type { ExtraNetworksPrompt } from \"@/libs/prompt\";\n\nexport function extraNetworksPromptEquals(\n  prompt1: ExtraNe"
  },
  {
    "path": "client-src/libs/prompt/util/isEquals/index.ts",
    "chars": 2424,
    "preview": "import type {\n  AlternatePrompt,\n  EmphasizedNegativePrompt,\n  EmphasizedPositivePrompt,\n  EmphasizedWeightedPrompt,\n  E"
  },
  {
    "path": "client-src/libs/prompt/util/isEquals/plainPromptEquals.ts",
    "chars": 184,
    "preview": "import type { PlainPrompt } from \"@/libs/prompt\";\n\nexport function plainPromptEquals(prompt1: PlainPrompt, prompt2: Plai"
  },
  {
    "path": "client-src/libs/prompt/util/isEquals/promptCombinationEquals.ts",
    "chars": 360,
    "preview": "import type { PromptCombination } from \"@/libs/prompt\";\nimport { isEquals } from \"./index\";\n\nexport function promptCombi"
  },
  {
    "path": "client-src/libs/prompt/util/isEquals/scheduledPromptEquals.ts",
    "chars": 574,
    "preview": "import type { InnerPrompt, ScheduledPrompt } from \"@/libs/prompt\";\nimport { isEquals } from \"./index\";\n\nexport function "
  },
  {
    "path": "client-src/libs/prompt/util/isPromptType.ts",
    "chars": 362,
    "preview": "import type { PromptType } from \"@/libs/prompt\";\n\nconst promptTypes: Set<string> = new Set<PromptType>([\n  \"alternate\",\n"
  },
  {
    "path": "client-src/libs/prompt/util/parsePrompt/index.ts",
    "chars": 3419,
    "preview": "import type {\n  AlternatePrompt,\n  BasicPrompt,\n  EmphasizedPrompt,\n  ExtraNetworksPrompt,\n  InnerPrompt,\n  PlainPrompt,"
  },
  {
    "path": "client-src/libs/prompt/util/parsePrompt/prompt-parser.js",
    "chars": 152975,
    "preview": "/* eslint-disable */\n// @ts-nocheck @typescript-eslint/ban-ts-comment\n//\n//  Lark.js stand-alone parser\n//=============="
  },
  {
    "path": "client-src/libs/prompt/util/toString/alternatePromptToString.ts",
    "chars": 223,
    "preview": "import type { AlternatePrompt } from \"@/libs/prompt\";\nimport { toString } from \"./index\";\n\nexport function alternateProm"
  },
  {
    "path": "client-src/libs/prompt/util/toString/emphasizedPromptToString.ts",
    "chars": 1008,
    "preview": "import type {\n  EmphasizedNegativePrompt,\n  EmphasizedPositivePrompt,\n  EmphasizedPrompt,\n  EmphasizedWeightedPrompt,\n} "
  },
  {
    "path": "client-src/libs/prompt/util/toString/extraNetworksPromptToString.ts",
    "chars": 360,
    "preview": "import type { ExtraNetworksPrompt } from \"@/libs/prompt\";\n\nexport function extraNetworksPromptToString(prompt: ExtraNetw"
  },
  {
    "path": "client-src/libs/prompt/util/toString/index.ts",
    "chars": 2257,
    "preview": "import type { Prompt } from \"@/libs/prompt\";\nimport { concatPrompt } from \"@/libs/prompt\";\nimport { alternatePromptToStr"
  },
  {
    "path": "client-src/libs/prompt/util/toString/plainPromptToString.ts",
    "chars": 143,
    "preview": "import type { PlainPrompt } from \"@/libs/prompt\";\n\nexport function plainPromptToString(prompt: PlainPrompt): string {\n  "
  },
  {
    "path": "client-src/libs/prompt/util/toString/promptCombinationToString.ts",
    "chars": 222,
    "preview": "import type { PromptCombination } from \"@/libs/prompt\";\nimport { toString } from \"./index\";\n\nexport function promptCombi"
  },
  {
    "path": "client-src/libs/prompt/util/toString/scheduledPromptToString.ts",
    "chars": 384,
    "preview": "import type { ScheduledPrompt } from \"@/libs/prompt\";\nimport { toString } from \"./index\";\n\nexport function scheduledProm"
  },
  {
    "path": "client-src/libs/util/array/index.ts",
    "chars": 135,
    "preview": "export { omitNulls } from \"./omitNulls\";\nexport { sortByIndexes } from \"./sortByIndexes\";\nexport { toggleValue } from \"."
  },
  {
    "path": "client-src/libs/util/array/omitNulls.ts",
    "chars": 160,
    "preview": "export function omitNulls<T>(array: Array<T | null | undefined>): Array<T> {\n  return array.filter((item): item is T => "
  },
  {
    "path": "client-src/libs/util/array/sortByIndexes.ts",
    "chars": 186,
    "preview": "export function sortByIndexes<T>(array: T[], indexes: number[]): T[] {\n  const result: T[] = [];\n\n  for (const index of "
  },
  {
    "path": "client-src/libs/util/array/toggleValue.ts",
    "chars": 204,
    "preview": "export function toggleValue<T>(array: T[], value: T): T[] {\n  const index = array.indexOf(value);\n  if (index !== -1) {\n"
  },
  {
    "path": "client-src/libs/util/dom/addClasses.ts",
    "chars": 402,
    "preview": "import { omitNulls } from \"@/libs/util/array\";\n\n/**\n * Adds one or more CSS classes to an element.\n *\n * @param element "
  },
  {
    "path": "client-src/libs/util/dom/applyClasses.ts",
    "chars": 703,
    "preview": "import { addClasses } from \"./addClasses\";\nimport { removeClasses } from \"./removeClasses\";\n\n/**\n * Adds or removes one "
  },
  {
    "path": "client-src/libs/util/dom/getElement.ts",
    "chars": 1039,
    "preview": "/**\n * Gets the first DOM element that matches the given selector, starting from the root element (if provided) or the d"
  },
  {
    "path": "client-src/libs/util/dom/getElementAll.ts",
    "chars": 1004,
    "preview": "/**\n * Gets all DOM elements that match a specified CSS selector, under a gradio app root element.\n *\n * @param selector"
  },
  {
    "path": "client-src/libs/util/dom/getScreenPosition.ts",
    "chars": 799,
    "preview": "/**\n * Gets the on-screen position of a given DOM element, taking into account the variation in position due to scrollin"
  },
  {
    "path": "client-src/libs/util/dom/hasChild.ts",
    "chars": 524,
    "preview": "/**\n * Checks if a given child element is a descendant of a given parent element.\n *\n * @param parent - The DOM element "
  },
  {
    "path": "client-src/libs/util/dom/hasClass.ts",
    "chars": 217,
    "preview": "import { omitNulls } from \"@/libs/util/array\";\n\nexport function hasClass(element: Element, ...classes: Nullable<string>["
  },
  {
    "path": "client-src/libs/util/dom/hasElement.ts",
    "chars": 963,
    "preview": "import { getElement } from \"./getElement\";\n\n/**\n * Checks if an element with the given CSS selector exists in the DOM.\n "
  },
  {
    "path": "client-src/libs/util/dom/index.ts",
    "chars": 576,
    "preview": "export { addClasses } from \"./addClasses\";\nexport { applyClasses } from \"./applyClasses\";\nexport { getElement } from \"./"
  },
  {
    "path": "client-src/libs/util/dom/removeAllChild.ts",
    "chars": 658,
    "preview": "/**\n * Removes all child elements that match the specified selector from the given parent element. If no selector is pro"
  },
  {
    "path": "client-src/libs/util/dom/removeClasses.ts",
    "chars": 423,
    "preview": "import { omitNulls } from \"@/libs/util/array\";\n\n/**\n * Removes one or more CSS classes from an element.\n *\n * @param ele"
  },
  {
    "path": "client-src/libs/util/dom/rotateElement.ts",
    "chars": 4362,
    "preview": "export type RotateDirection = \"up\" | \"down\" | \"left\" | \"right\";\n\nexport function rotateElement(\n  element: Nullable<HTML"
  },
  {
    "path": "client-src/libs/util/dom/scrollIntoViewIfNeeded.ts",
    "chars": 573,
    "preview": "export function scrollIntoViewIfNeeded(element: HTMLElement, parent: HTMLElement): void {\n  const { top: parentTop, bott"
  },
  {
    "path": "client-src/libs/util/string/generateHashCode.ts",
    "chars": 449,
    "preview": "/**\n * Generates a 32-bit hash code for a given string.\n *\n * @param str - The string to generate a hash code for.\n * @r"
  },
  {
    "path": "client-src/libs/util/string/index.ts",
    "chars": 100,
    "preview": "export { generateHashCode } from \"./generateHashCode\";\nexport { toggleValue } from \"./toggleValue\";\n"
  },
  {
    "path": "client-src/libs/util/string/toggleValue.ts",
    "chars": 1179,
    "preview": "/**\n * Toggles the presence of a keyword in a string of concatenated values separated by '|'.\n * If the keyword is prese"
  },
  {
    "path": "client-src/libs/util/types/index.ts",
    "chars": 195,
    "preview": "export { isArray } from \"./isArray\";\nexport { isBoolean } from \"./isBoolean\";\nexport { isNumber } from \"./isNumber\";\nexp"
  },
  {
    "path": "client-src/libs/util/types/isArray.ts",
    "chars": 376,
    "preview": "export function isArray(arg: unknown): arg is unknown[];\n\nexport function isArray<T>(arg: unknown, check: (element: unkn"
  },
  {
    "path": "client-src/libs/util/types/isBoolean.ts",
    "chars": 110,
    "preview": "export function isBoolean(arg: unknown): arg is boolean {\n  return arg != null && typeof arg === \"boolean\";\n}\n"
  },
  {
    "path": "client-src/libs/util/types/isNumber.ts",
    "chars": 107,
    "preview": "export function isNumber(arg: unknown): arg is number {\n  return arg != null && typeof arg === \"number\";\n}\n"
  },
  {
    "path": "client-src/libs/util/types/isObject.ts",
    "chars": 107,
    "preview": "export function isObject(arg: unknown): arg is object {\n  return arg != null && typeof arg === \"object\";\n}\n"
  },
  {
    "path": "client-src/libs/util/types/isString.ts",
    "chars": 107,
    "preview": "export function isString(arg: unknown): arg is string {\n  return arg != null && typeof arg === \"string\";\n}\n"
  },
  {
    "path": "client-src/libs/util/webui/dispatchEvent.ts",
    "chars": 472,
    "preview": "/**\n * Dispatches an event of the specified type on the specified element.\n *\n * @param element The element to dispatch "
  },
  {
    "path": "client-src/libs/util/webui/getCurrentTabName.ts",
    "chars": 410,
    "preview": "/**\n * Returns the name of the currently active tab.\n */\nexport function getCurrentTabName(): WebUiTab {\n  const content"
  },
  {
    "path": "client-src/libs/util/webui/getOption.ts",
    "chars": 455,
    "preview": "export function getOption(optionName: string, defaultValue: string): string;\nexport function getOption(optionName: strin"
  },
  {
    "path": "client-src/libs/util/webui/index.ts",
    "chars": 272,
    "preview": "export { dispatchEvent } from \"./dispatchEvent\";\nexport { getCurrentTabName } from \"./getCurrentTabName\";\nexport { getOp"
  },
  {
    "path": "client-src/libs/util/webui/isDarkMode.ts",
    "chars": 136,
    "preview": "import { hasElement } from \"@/libs/util/dom\";\n\nexport function isDarkMode(): boolean {\n  return hasElement(\".gradio-cont"
  },
  {
    "path": "client-src/libs/util/webui/t.ts",
    "chars": 531,
    "preview": "type Arg = string | number | boolean;\n\nexport function t(key: string, options?: { defaultValue?: string; args?: Arg[] })"
  },
  {
    "path": "client-src/libs/util/webui/withBooleanOption.ts",
    "chars": 516,
    "preview": "/**\n * If the specified option name exists and it is of boolean type, execute the callback. If the specified option name"
  },
  {
    "path": "client-src/main.ts",
    "chars": 2698,
    "preview": "import \"./styles/index.css\";\nimport { getDanbooruTags, getExtraNetworks, getLocalization, getMyPrompts } from \"@/libs/ap"
  },
  {
    "path": "client-src/styles/global.css",
    "chars": 2216,
    "preview": "[data-hidden-original=\"true\"] > #txt2img_prompt_container,\n[data-hidden-original=\"true\"] > #img2img_prompt_container {\n "
  },
  {
    "path": "client-src/styles/index.css",
    "chars": 43,
    "preview": "@import \"theme.css\";\n@import \"global.css\";\n"
  },
  {
    "path": "client-src/styles/theme.css",
    "chars": 3728,
    "preview": ":root {\n  --danbooru-background-color-base: 246, 246, 248;\n  --danbooru-background: linear-gradient(\n    to bottom right"
  },
  {
    "path": "data/danbooru-tags.json",
    "chars": 3500727,
    "preview": "[{\"name\":\"hatsune_miku\",\"post_count\":81774,\"category\":4},{\"name\":\"hakurei_reimu\",\"post_count\":69642,\"category\":4},{\"name"
  },
  {
    "path": "data/negative-prompts.json",
    "chars": 590,
    "preview": "[\"bad anatomy\",\"bad hands\",\"bad proportions\",\"bad quality\",\"blurry\",\"cloned face\",\"contortion\",\"contortionist\",\"cropped\""
  },
  {
    "path": "dev-scripts/crawl-danbooru-tags.mjs",
    "chars": 4023,
    "preview": "import fetch from \"node-fetch\";\nimport https from \"https\";\nimport { URLSearchParams } from \"url\";\nimport { setTimeout } "
  },
  {
    "path": "dev-scripts/merge-danbooru-tags.mjs",
    "chars": 826,
    "preview": "import fs from \"fs-extra\";\nimport path from \"path\";\n\nconst __dirname = path.resolve();\n\nconst dir = path.join(__dirname,"
  },
  {
    "path": "docs/README-ja.adoc",
    "chars": 5330,
    "preview": "= Better Prompt\n\nlink:../README.adoc[English] | 日本語\n\nBetter Promptはプロンプト入力・編集を補助するUIを追加する https://github.com/AUTOMATIC11"
  },
  {
    "path": "index.html",
    "chars": 276,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "javascript/betterPrompt.js",
    "chars": 203728,
    "preview": "var BetterPrompt=function(){\"use strict\";var I_=Object.defineProperty;var R_=(Pt,Ze,dt)=>Ze in Pt?I_(Pt,Ze,{enumerable:!"
  },
  {
    "path": "locales/ja_JP.json",
    "chars": 1839,
    "preview": "{\n  \"__The following translations are for server-side__\": \"以降の翻訳はサーバーサイド用\",\n  \"Language of Better Prompt (requires reloa"
  },
  {
    "path": "package.json",
    "chars": 1711,
    "preview": "{\n  \"private\": true,\n  \"name\": \"sd-webui-better-prompt\",\n  \"version\": \"0.4.1\",\n  \"description\": \"\",\n  \"main\": \"index.js\""
  },
  {
    "path": "parser-src/prompt-parser.lark",
    "chars": 1337,
    "preview": "start : (extra_networks_prompts | prompts)*\n\n?extra_networks_prompts : extra_networks\n                        | extra_ne"
  },
  {
    "path": "postcss.config.cjs",
    "chars": 83,
    "preview": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n};\n"
  },
  {
    "path": "scripts/better_prompt.py",
    "chars": 5927,
    "preview": "import json\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom typing import Dict, Optional, List, Any"
  },
  {
    "path": "style.css",
    "chars": 23822,
    "preview": ":root{--danbooru-background-color-base: 246, 246, 248;--danbooru-background: linear-gradient( to bottom right, rgba(var("
  },
  {
    "path": "svelte.config.js",
    "chars": 236,
    "preview": "import sveltePreprocess from \"svelte-preprocess\";\n\nexport default {\n  // Consult https://svelte.dev/docs#compile-time-sv"
  },
  {
    "path": "tailwind.config.cjs",
    "chars": 206,
    "preview": "/** @type {import(\"tailwindcss\").Config} */\nmodule.exports = {\n  content: [\"./client-src/**/*.{js,ts,svelte}\"],\n  theme:"
  },
  {
    "path": "tsconfig.json",
    "chars": 910,
    "preview": "{\n  \"extends\": \"@tsconfig/svelte/tsconfig.json\",\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFi"
  },
  {
    "path": "tsconfig.node.json",
    "chars": 168,
    "preview": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"skipLibCheck\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\""
  },
  {
    "path": "vite.config.ts",
    "chars": 561,
    "preview": "import { resolve } from \"path\";\nimport { defineConfig } from \"vite\";\nimport { svelte } from \"@sveltejs/vite-plugin-svelt"
  }
]

About this extraction

This page contains the full source code of the eideehi/sd-webui-better-prompt GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 159 files (3.9 MB), approximately 1.0M tokens, and a symbol index with 1377 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!