IFieldsPluginInfo
This page contains the specific properties of the IFieldsPlugin.
headerβ
[ IStr, default: None ]
Header of the plugin.
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.
The keys of this property will be the keys of the extraData. Pseudo code:
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.
- 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.
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.
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.
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.
Currently multi-select is not supported.
Exampleβ
To use I18NSelectField, we often need to define the 'field' separately:
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:
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 
bar1option will be displayed asbar1_δΈζin Chinese andbar1_enin English. - The 
bar2option will be displayed asbar2_δΈζin Chinese andbar2_enin English. 
Then, the default:
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:
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 thefoo_fieldas thefoofield of the plugin. - In 
process, we first get theI18Nobject from thefookey ofextraData, then we use theparsemethod of thefoo_fieldto get theactualoption. 
And here's a demo video of how this plugin works:
ISelectLocalFieldβ
This is used to collect local files specified by the user.
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.
Currently multi-select is not supported.
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.
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.
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:
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:
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 
lorafolder in the same directory ofapp.py. - The extensions will not be used.
 - Only files will be considered.
 - Only files with the extension 
.safetensorswill be considered. - We'll add a 
Noneoption to the first of the options, and it will be the default value. 
Then, the strength:
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:
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 thelora_fieldas thefoofield of the plugin. - In 
process, we dumps the object of thefoofield to a JSON string. 
And here's a demo video of how this plugin works: