Skip to main content

IFieldsPluginInfo

This page contains the specific properties of the IFieldsPlugin.

[ IStr, default: None ]

Header of the plugin.

note

If it is None, we will use the registered name of the plugin as the header (with the first letter capitalized).

definitions​

[ Dict[str, IFieldDefinition], default: None ]

Field definitions, this is what we customize most.

tip

The keys of this property will be the keys of the extraData. Pseudo code:

app.py
class Plugin(IFieldsPlugin):
@property
def settings(self) -> IPluginSettings:
return IPluginSettings(
...,
pluginInfo=IFieldsPluginInfo(definitions=dict(
a=...,
b=...,
c=...,
)),
)

async def process(self, data: ISocketRequest):
a = data.extraData["a"]
b = data.extraData["b"]
c = data.extraData["c"]
...

numColumns​

[ int | None, default: None ]

Number of columns that will be used to arrange the input fields.

closeOnSubmit​

[ bool, default: True ]

Whether close the expand panel when the submit button is clicked.

toastOnSubmit​

[ bool, default: True ]

Whether trigger a toast message when the submit button is clicked.

toastMessageOnSubmit​

[ str, default: None ]

The message of the toast.

note
  • If it is None, default toast message will be used.
  • Only take effect when toastOnSubmit is True.

Available Fields​

There are plenty of available fields and they should be able to cover most of your use cases.

IBaseField​

Every I*Field inherit from IBaseField, and they will all share the following common properties:

label​

[ IStr | None, default: None ]

The label of the field.

Different fields may render the label differently, but we'll make sure the label is noticable.

tooltip​

[ IStr | None, default: None ]

The tooltip of the field.

Different fields may have different tooltip trigger area, but we'll make sure the trigger area is natural.

props​

[ IChakra | None, default: None ]

Extra chakra props for the component.

numRows​

[ int | None, default: None ]

Number of rows that will be occupied by this field.

This is useful when we want our field to be larger. Typically, we can set it to a number not smaller than 2 to make the ITextField larger, so users can input text more comfortably.

condition​

[ str | None, default: None ]

Whether the field is conditioned on another field (i.e., show this field only when the condition field has a True value).

This is useful when the settings are hierarchical. For example, we may want to show the model field only when the useModel field has a True value.

ITextField​

This is used to collect a piece of text from the user.

default​

[ IStr, default: None ]

Default text of the field.

IImageField​

This is used to collect an image url from the user.

default​

[ IStr, default: None ]

Default url of the field.

INumberField​

This is used to collect a number from the user.

default​

[ float, required ]

Default value of the field.

min​

[ float | None, default: None ]

Minimum value of the field.

max​

[ float | None, default: None ]

Maximum value of the field.

tip

If min & max are both provided, we will render a Slider instead of a number Input.

step​

[ float | None, default: None ]

Step of the Slider, only take effect when min & max are both provided.

isInt​

[ bool | None, default: None ]

Whether the number should be an integer.

scale​

[ NumberScale | None, default: None ]

Scale of the number.

class NumberScale(str, Enum):
LINEAR = "linear"
LOGARITHMIC = "logarithmic"

precision​

[ int | None, default: None ]

Precision of the Slider caption, only take effect when min & max are both provided.

ISelectField​

This is used to collect a / some selection(s) from the user.

options​

[ List[IStr], required ]

The options of the field.

default​

[ IStr, required ]

The default value of the field.

isMulti​

[ bool | None, default: None ]

Whether use multi-select.

caution

Currently multi-select is not supported.

I18NSelectField​

This is mainly for accessibility when we want to use I18N as the options.

class I18NSelectField(IBaseField):
mapping: Union[Dict[str, I18N], str] = Field(
...,
description=(
"The mapping of the options. "
"The key is the 'actual' option, and the value is the i18n object to be displayed\n"
"> If `str` is provided, it represents the path to the mapping json file."
),
)
default: str = Field(..., description="The default 'actual' option of the field")
isMulti: Optional[bool] = Field(None, description="Whether use multi-select")

mapping​

[ Dict[str, I18N], required ]

The mapping of the options. The key is the actual option, and the value is the I18N object to be displayed.

info

You can assign this field with an absolute path to a json file, and we'll dynamically load the mapping from it.

This is extremely useful when we want to hot-reload the select options without restarting the server.

default​

[ str, required ]

The default actual option of the field.

isMulti​

[ bool | None, default: None ]

Whether use multi-select.

caution

Currently multi-select is not supported.

Example​

To use I18NSelectField, we often need to define the 'field' separately:

app.py
from cfdraw import *


foo_field = I18NSelectField(
mapping=dict(
bar1=I18N(zh="bar1_δΈ­ζ–‡", en="bar1_en"),
bar2=I18N(zh="bar2_δΈ­ζ–‡", en="bar2_en"),
),
default="bar2",
)

Let's break it down. First, the mapping:

app.py
from cfdraw import *


foo_field = I18NSelectField(
mapping=dict(
bar1=I18N(zh="bar1_δΈ­ζ–‡", en="bar1_en"),
bar2=I18N(zh="bar2_δΈ­ζ–‡", en="bar2_en"),
),
default="bar2",
)

indicates that there will be two options: bar1 and bar2, and:

  • The bar1 option will be displayed as bar1_δΈ­ζ–‡ in Chinese and bar1_en in English.
  • The bar2 option will be displayed as bar2_δΈ­ζ–‡ in Chinese and bar2_en in English.

Then, the default:

app.py
from cfdraw import *


foo_field = I18NSelectField(
mapping=dict(
bar1=I18N(zh="bar1_δΈ­ζ–‡", en="bar1_en"),
bar2=I18N(zh="bar2_δΈ­ζ–‡", en="bar2_en"),
),
default="bar2",
)

indicates that the default option is bar2.

Here's the complete code that utilizes this foo_field:

app.py
from cfdraw import *


foo_field = I18NSelectField(
mapping=dict(
bar1=I18N(zh="bar1_δΈ­ζ–‡", en="bar1_en"),
bar2=I18N(zh="bar2_δΈ­ζ–‡", en="bar2_en"),
),
default="bar2",
)


class Plugin(IFieldsPlugin):
@property
def settings(self) -> IPluginSettings:
return IPluginSettings(
w=300,
h=180,
pivot=PivotType.LEFT,
pluginInfo=IFieldsPluginInfo(definitions=dict(foo=foo_field)),
)

async def process(self, data: ISocketRequest) -> str:
foo_i18n_d = data.extraData["foo"]
foo = foo_field.parse(foo_i18n_d)
return f"foo: {foo}"


register_plugin("foo")(Plugin)
app = App()

As the highlighted lines shows:

  • In settings, we define the foo_field as the foo field of the plugin.
  • In process, we first get the I18N object from the foo key of extraData, then we use the parse method of the foo_field to get the actual option.

And here's a demo video of how this plugin works:


ISelectLocalField​

This is used to collect local files specified by the user.

tip

This is handy when we need to load local models for the plugin.

path​

[ str, required ]

The local path that you want to read from.

default​

[ str, required ]

The default value of the field.

regex​

[ str, required ]

The regex to filter the files / folders.

noExt​

[ bool, required ]

Whether to remove the extension.

onlyFiles​

[ bool, required ]

Whether only consider the files.

defaultPlaceholder​

[ str, required ]

If provided, it will be inserted to the first of the options and serve as the default value.

isMulti​

[ bool, required ]

Whether use multi-select.

caution

Currently multi-select is not supported.

info

See Example for the usage of this field.

IBooleanField​

This is used to collect a boolean flag from the user.

default​

[ bool, required ]

The default value of the field.

IColorField​

This is used to collect a color from the user.

default​

[ IStr, default: "#ffffff" ]

The default value of the field.

IListField​

This is used to collect a list of 'forms' from the user.

tip

This is handy if the corresponding field can be 'stacked' (e.g., multi LoRA, multi ControlNet).

item​

[ Dict[str, IFieldDefinition], required ]

The definition of the 'form'.

displayKey​

[ str, default: None ]

The key of the field to be displayed when collapsed.

default​

[ List[Any], default: [] ]

The default value of the field.

note

In most cases, default should be an empty list.

Example​

Let's show an example on how to implement a field which aims to support multi LoRA:

app.py
from cfdraw import *
from pathlib import Path

lora_field = IListField(
item=dict(
model=ISelectLocalField(
path=str(Path(__file__).parent / "lora"),
noExt=True,
onlyFiles=True,
regex=".*\\.safetensors",
defaultPlaceholder="None",
),
strength=INumberField(
default=1.0,
min=0.0,
max=4.0,
step=0.1,
precision=1,
),
),
)

Let's break it down. First, the model:

app.py
lora_field = IListField(
item=dict(
model=ISelectLocalField(
path=str(Path(__file__).parent / "lora"),
noExt=True,
onlyFiles=True,
regex=".*\\.safetensors",
defaultPlaceholder="None",
),
...
),
)

These codes do the following things:

  • We'll read the files from the lora folder in the same directory of app.py.
  • The extensions will not be used.
  • Only files will be considered.
  • Only files with the extension .safetensors will be considered.
  • We'll add a None option to the first of the options, and it will be the default value.

Then, the strength:

app.py
lora_field = IListField(
item=dict(
...,
strength=INumberField(
default=1.0,
min=0.0,
max=4.0,
step=0.1,
precision=1,
),
),
)

Nothing special, just a common use case of INumberField.

Now, let's see the complete code on how to use this field:

app.py
import json
from cfdraw import *
from pathlib import Path


lora_field = IListField(
item=dict(
model=ISelectLocalField(
path=str(Path(__file__).parent / "lora"),
noExt=True,
onlyFiles=True,
regex=".*\\.safetensors",
defaultPlaceholder="None",
),
strength=INumberField(
default=1.0,
min=0.0,
max=4.0,
step=0.1,
precision=1,
),
),
)


class Plugin(IFieldsPlugin):
@property
def settings(self) -> IPluginSettings:
return IPluginSettings(
w=400,
h=300,
pivot=PivotType.LEFT,
useModal=True,
pluginInfo=IFieldsPluginInfo(definitions=dict(foo=lora_field)),
)

async def process(self, data: ISocketRequest) -> str:
json_str = json.dumps(data.extraData["foo"], indent=2)
return f"foo: {json_str}"


register_plugin("foo")(Plugin)
app = App()

As the highlighted lines shows:

  • In settings, we define the lora_field as the foo field of the plugin.
  • In process, we dumps the object of the foo field to a JSON string.

And here's a demo video of how this plugin works: