310 lines
9.7 KiB
TypeScript
310 lines
9.7 KiB
TypeScript
import { createStore, StoreApi } from "zustand";
|
|
|
|
/**
|
|
* Function used to create a `Plugin`
|
|
*
|
|
* ```ts
|
|
* import * as StableStudio from "@stability/stablestudio-plugin";
|
|
*
|
|
* // You need to export your plugin like this...
|
|
* export const createPlugin = StableStudio.createPlugin(() => ({}));
|
|
* ```
|
|
*
|
|
* It returns a function waiting for `PluginCreateContext` as an argument, which the UI provides
|
|
*/
|
|
export const createPlugin =
|
|
<P extends PluginTypeHelper>(
|
|
createPlugin: (options: {
|
|
context: PluginCreateContext;
|
|
set: StoreApi<Plugin<P>>["setState"];
|
|
get: StoreApi<Plugin<P>>["getState"];
|
|
}) => Plugin<P>
|
|
) =>
|
|
(context: PluginCreateContext) =>
|
|
createStore<Plugin<P>>((set, get) => createPlugin({ set, get, context }));
|
|
|
|
/** `PluginCreateContext` is passed to the `createPlugin` function and provides some useful functions */
|
|
type PluginCreateContext = {
|
|
/** Get the git hash of the repository */
|
|
getGitHash: () => string;
|
|
|
|
/** Get a random prompt for image generation */
|
|
getStableDiffusionRandomPrompt: () => string;
|
|
};
|
|
|
|
/** `PluginManifest` is used to describe your plugin on the settings page */
|
|
export type PluginManifest = {
|
|
/** What is your plugin called? */
|
|
name?: string;
|
|
|
|
/** A link to your plugin's icon */
|
|
icon?: URLString;
|
|
|
|
/** A link to your plugin's website */
|
|
link?: URLString;
|
|
|
|
/** Your plugin version, no versioning schema is currently enforced */
|
|
version?: string;
|
|
|
|
/** What's the license, no licensing schema is currently enforced */
|
|
license?: string;
|
|
|
|
/** Who made this plugin? */
|
|
author?: string;
|
|
|
|
/** What does your plugin do? */
|
|
description?: Markdown;
|
|
};
|
|
|
|
export type PluginSettingCommon = {
|
|
/** What is your plugin called? */
|
|
title?: string;
|
|
|
|
/** What is your plugin called? */
|
|
description?: Markdown;
|
|
|
|
/** Is this setting required? If so, users will be forced to provide an input */
|
|
required?: boolean;
|
|
};
|
|
|
|
/** `PluginSetting` allows for `string`, `number`, and `boolean` input types */
|
|
export type PluginSetting<T> =
|
|
| PluginSettingString<T>
|
|
| PluginSettingNumber<T>
|
|
| PluginSettingBoolean<T>;
|
|
|
|
/** `PluginSetting` which is displayed and input as a `string` */
|
|
export type PluginSettingString<T = string> = PluginSettingCommon & {
|
|
type: "string";
|
|
|
|
/** The current `value` */
|
|
value?: T;
|
|
|
|
/** If you provide `options`, this will become a dropdown */
|
|
options?: { label: string; value: T }[];
|
|
|
|
/** The `placeholder` input `string` */
|
|
placeholder?: string;
|
|
|
|
/** Given a `value`, how should it be displayed? Useful if your value is a `Date` for example */
|
|
formatter?: (value: T) => string;
|
|
|
|
/** Given a `string`, how should it be saved as a `value`? */
|
|
parser?: (string: string) => T;
|
|
|
|
/** Determines whether this input is displayed as a `password` type */
|
|
password?: boolean;
|
|
};
|
|
|
|
/** `PluginSetting` which is displayed and input as a `number` */
|
|
export type PluginSettingNumber<T = number> = PluginSettingCommon & {
|
|
type: "number";
|
|
|
|
/** The current `value` */
|
|
value?: T;
|
|
|
|
/** The `placeholder` input `number` */
|
|
placeholder?: number;
|
|
|
|
/** Enforces an inclusive `min` allowed `value` */
|
|
min?: number;
|
|
|
|
/** Enforces an inclusive `max` allowed `value` */
|
|
max?: number;
|
|
|
|
/** Enforces a `step` size between each `value` */
|
|
step?: number;
|
|
|
|
/** Given a `value`, how can it formatted as a `number`? */
|
|
formatter?: (value: T) => number;
|
|
|
|
/** Given a `number`, how should it be saved as a `value`? */
|
|
parser?: (value: number) => T;
|
|
|
|
/** Determines whether to use a slider or input. Uses `input` by default */
|
|
variant?: "slider" | "input";
|
|
};
|
|
|
|
/** `PluginSetting` which is displayed and input as a `boolean` */
|
|
export type PluginSettingBoolean<T = boolean> = PluginSettingCommon & {
|
|
type: "boolean";
|
|
|
|
/** The current `value` */
|
|
value?: T;
|
|
};
|
|
|
|
/** `PluginStatus` is displayed via an indicator on the `/settings` page */
|
|
export type PluginStatus = {
|
|
/** Determines the text displayed next to the `indicator` icon */
|
|
text?: string;
|
|
|
|
/** Determines the `indicator` color and icon */
|
|
indicator?: "success" | "error" | "warning" | "info" | "loading";
|
|
};
|
|
|
|
/** `PluginSettings` is a map of the settings your plugin will show on the `/settings` page */
|
|
export type PluginSettings = {
|
|
/** Each setting needs a unique `key` */
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
[settingKey: string]: PluginSetting<any>;
|
|
};
|
|
|
|
/** Useful for strictly-typing things */
|
|
export type PluginTypeHelper = { settings?: PluginSettings };
|
|
export type PluginTypeHelperDefault = { settings: undefined };
|
|
|
|
/** The `Plugin` type defines the API which StableStudio uses to provide functionality via plugins */
|
|
export type Plugin<P extends PluginTypeHelper = PluginTypeHelperDefault> = {
|
|
/** You should provide a `PluginManifest`, otherwise the UI shows a warning */
|
|
manifest?: PluginManifest;
|
|
|
|
/** The most fundamental function needed for stable diffusion generations */
|
|
createStableDiffusionImages?: (options?: {
|
|
/** The `StableDiffusionInput` you've been asked to generate, if empty, you could still return a random image */
|
|
input?: StableDiffusionInput;
|
|
|
|
/** Determines how many images will be created using the given `StableDiffusionInput` */
|
|
count?: number;
|
|
}) => MaybePromise<StableDiffusionImages | undefined>;
|
|
|
|
/** This function provides historical access to generated images. If omitted, the app will lose its history on refresh */
|
|
getStableDiffusionExistingImages?: (options?: {
|
|
/** How many images the UI is requesting */
|
|
limit?: number;
|
|
|
|
/** The `ID` of last image the UI has access to, it should be omitted from the response */
|
|
exclusiveStartImageID?: ID;
|
|
}) => MaybePromise<StableDiffusionImages[] | undefined>;
|
|
|
|
/** If more than one `StableDiffusionModel`, you can return them via this function and they will be presented as a dropdown in the UI */
|
|
getStableDiffusionModels?: () => MaybePromise<
|
|
StableDiffusionModel[] | undefined
|
|
>;
|
|
|
|
/** If more than one `StableDiffusionSampler`, you can return them via this function and they will be presented as a dropdown in the UI */
|
|
getStableDiffusionSamplers?: () => MaybePromise<
|
|
StableDiffusionSampler[] | undefined
|
|
>;
|
|
|
|
/** If more than one `StableDiffusionStyle`, you can return them via this function and they will be presented as a dropdown in the UI */
|
|
getStableDiffusionStyles?: () => MaybePromise<
|
|
StableDiffusionStyle[] | undefined
|
|
>;
|
|
|
|
/** Determines the default count passed to `createStableDiffusionImages` */
|
|
getStableDiffusionDefaultCount?: () => number | undefined;
|
|
|
|
/** Determines the default input passed to `createStableDiffusionImages` and is also used when setting up a new generation */
|
|
getStableDiffusionDefaultInput?: () => StableDiffusionInput | undefined;
|
|
|
|
/** If you support deleting existing images by `ID`, this function will enable a deletion UI */
|
|
deleteStableDiffusionImages?: (options?: {
|
|
imageIDs?: ID[];
|
|
}) => MaybePromise<void>;
|
|
|
|
/** Determines whether to display a `PluginStatus` and sets its contents */
|
|
getStatus?: () => MaybePromise<PluginStatus | undefined>;
|
|
|
|
/** If your plugin has `settings` you want to make available on the `/settings page, you can declare them here` */
|
|
settings?: P["settings"];
|
|
|
|
/** Handles changes in `settings`, you'll need to actually use Zustand's `set` to persist the change */
|
|
setSetting?: (
|
|
settingKey: keyof P["settings"],
|
|
value: string | number | boolean
|
|
) => void;
|
|
} & P;
|
|
|
|
export type StableDiffusionInput = {
|
|
prompts?: StableDiffusionPrompt[];
|
|
|
|
/** If `getStableDiffusionModels` is available and a `StableDiffusionModel` is selected, this is its `ID` */
|
|
model?: ID;
|
|
|
|
/** If `getStableDiffusionStyles` is available and a `StableDiffusionStyle` is selected, this is its `ID` */
|
|
style?: ID;
|
|
|
|
width?: number;
|
|
height?: number;
|
|
|
|
sampler?: StableDiffusionSampler;
|
|
cfgScale?: number;
|
|
steps?: number;
|
|
seed?: number;
|
|
|
|
/** `StableDiffusionInputImage` in the form of a black and white mask used for in-painting and out-painting */
|
|
maskImage?: StableDiffusionInputImage;
|
|
|
|
/** `StableDiffusionInputImage` which is used for image-to-image generations such as creating variations */
|
|
initialImage?: StableDiffusionInputImage;
|
|
};
|
|
|
|
export type StableDiffusionPrompt = {
|
|
text?: string;
|
|
|
|
/** Value between `-1` and `1` */
|
|
weight?: number;
|
|
};
|
|
|
|
/** This controls how a `StableDiffusionModel` is displayed in the UI */
|
|
export type StableDiffusionModel = {
|
|
id: ID;
|
|
name?: string;
|
|
description?: string;
|
|
image?: URLString;
|
|
};
|
|
|
|
/** This controls how a `StableDiffusionStyle` is displayed in the UI */
|
|
export type StableDiffusionStyle = {
|
|
id: ID;
|
|
name?: string;
|
|
description?: string;
|
|
image?: URLString;
|
|
};
|
|
|
|
/** This controls how a `StableDiffusionSampler` is displayed in the UI */
|
|
export declare type StableDiffusionSampler = {
|
|
id: ID;
|
|
name?: string;
|
|
};
|
|
|
|
export type StableDiffusionInputImage = {
|
|
blob?: Blob;
|
|
|
|
/** Value between `0` and `1` */
|
|
weight?: number;
|
|
};
|
|
|
|
/** Since multiple images can be generated at once, this is how they are grouped */
|
|
export type StableDiffusionImages = {
|
|
id: ID;
|
|
|
|
/** If `exclusiveStartImageID` is set, this means more images exist for this group and the `ID` is passed to `getStableDiffusionExistingImages` */
|
|
exclusiveStartImageID?: ID;
|
|
|
|
images?: StableDiffusionImage[];
|
|
};
|
|
|
|
export type StableDiffusionImage = {
|
|
id: ID;
|
|
|
|
createdAt?: Date;
|
|
|
|
/** The `StableDiffusionInput` used to create this image */
|
|
input?: StableDiffusionInput;
|
|
|
|
blob?: Blob;
|
|
};
|
|
|
|
/** Allows both asynchronous and synchronous return types */
|
|
type MaybePromise<T> = T | Promise<T>;
|
|
|
|
/** In this context, an `ID` represents a unique-within-this-plugin `ID`, no schema is enforced */
|
|
export type ID = string;
|
|
|
|
/** A valid URL */
|
|
export type URLString = string;
|
|
|
|
/** Markdown string which is rendered as markdown in the UI */
|
|
export type Markdown = string;
|