|
|
@ -32,6 +32,4 @@ original_mask.png
|
|||
# comments when packaging (include in the package,uxp packager will use .gitignore to ignore files):
|
||||
*/dist/*LICENSE.txt
|
||||
*/dist/*.bundle.js
|
||||
|
||||
# uncomments when packaging (don't include in the package)
|
||||
# /docs
|
||||
typescripts/dist
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
server/python_server/output/*
|
||||
*.md
|
||||
manifest.json
|
||||
manifest.json
|
||||
jimp/**
|
||||
server_env/**
|
||||
.github\workflows\wiki-sync-action.yml
|
||||
**/dist
|
||||
.github\workflows\wiki-sync-action.yml
|
||||
tsconfig.json
|
||||
10
enum.js
|
|
@ -17,6 +17,7 @@ const AutomaticStatusEnum = {
|
|||
Offline: 'offline',
|
||||
RunningNoApi: 'running_no_api',
|
||||
RunningWithApi: 'running_with_api',
|
||||
AutoPhotoshopSDExtensionMissing: 'auto_photoshop_sd_missing',
|
||||
}
|
||||
|
||||
const ViewerObjectTypeEnum = {
|
||||
|
|
@ -30,12 +31,7 @@ const RequestStateEnum = {
|
|||
Interrupted: 'interrupted', // canceled/ interrupted
|
||||
Finished: 'finished', // finished generating
|
||||
}
|
||||
const DocumentTypeEnum = {
|
||||
NoBackground: 'no_background',
|
||||
ImageBackground: 'image_background',
|
||||
SolidBackground: 'solid_background',
|
||||
ArtBoard: 'artboard',
|
||||
}
|
||||
|
||||
const BackgroundHistoryEnum = {
|
||||
CorrectBackground: 'correct_background',
|
||||
NoBackground: 'no_background',
|
||||
|
|
@ -51,7 +47,7 @@ module.exports = {
|
|||
AutomaticStatusEnum,
|
||||
ViewerObjectTypeEnum,
|
||||
RequestStateEnum,
|
||||
DocumentTypeEnum,
|
||||
|
||||
BackgroundHistoryEnum,
|
||||
PresetTypeEnum,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,184 @@
|
|||
{
|
||||
"Auto-Photoshop-SD": "SD插件(明空汉化)",
|
||||
"'A' for Automatic1111 server (webui-user.bat), Green is connected. Red Means there is a problem with your Automatic1111. Run 'webui-user.bat' and hit 'Refresh' button ": "A代表SD服务器(webui-user.bat),绿色已连接。红色表示SD服务器有问题。运行“webui-user.bat”并点击刷新按钮",
|
||||
"'P' for proxy server (start_server.bat), Green is connected. Red means you need to run 'start_server.bat' or hit Refresh button": "P为代理服务器(start_server.bat),绿色已连接。红色表示您需要运行'start_server.bat'或点击刷新按钮",
|
||||
"Stable Diffusion": "稳定扩散",
|
||||
"Stable Diffusion UI": "稳定扩散 UI",
|
||||
"Refresh": "刷新",
|
||||
"Refresh the plugin, only fixes minor issues.": "刷新插件,仅修复小问题。",
|
||||
"Update": "更新",
|
||||
"Update the plugin if you encounter bugs. Get the latest features": "如果遇到错误,请更新插件。 获取最新功能",
|
||||
"Select Lora": "选择 Lora",
|
||||
"use lora in your prompt": "在提示中使用 lora",
|
||||
"Generate": "生成",
|
||||
"Generate txt2img": "生成 txt2img",
|
||||
"Progress...": "进度...",
|
||||
"Toggle the visibility of the Preview Image on the canvas": "切换画布上预览图像的可见性",
|
||||
"Move and reSize the highlighted layer to fit into the Selection Area": "移动和调整突出显示的图层以适合选择区域",
|
||||
"create a snapshot of what you see on the canvas and place on a new layer": "创建画布上看到的快照并放置在新图层上",
|
||||
"reset the ui settings to their default values": "将 UI 设置重置为默认值",
|
||||
"Interrogate the selected area, convert Image to Prompt": "审问所选区域,将图像转换为提示",
|
||||
"use this mode to generate images from text only": "使用此模式仅从文本生成图像",
|
||||
"use this mode to generate variation of an image": "使用此模式生成图像的变体",
|
||||
"use this mode to generate variation of a small area of an image, while keeping the rest of the image intact": "使用此模式生成图像的小区域的变体,同时保持图像的其余部分完好无损",
|
||||
"use this mode to (1) fill any missing area of an image,(2) expand an image": "使用此模式来(1)填充图像的任何缺失区域,(2)扩展图像",
|
||||
"Image": "图像",
|
||||
"Mask": "蒙版",
|
||||
"Batch Size:": "批量大小:",
|
||||
"Batch Count:": "批量计数:",
|
||||
"Steps:": "步数:",
|
||||
"Selection Mode:": "选择模式:",
|
||||
"ratio": "比率",
|
||||
"precise": "精确",
|
||||
"use the selection area width and height to fill the width and height sliders": "使用选择区域的宽度和高度来填充宽度和高度滑块",
|
||||
"ignore": "忽略",
|
||||
"fill the width and height sliders manually": "手动填充宽度和高度滑块",
|
||||
"Smart Preset": "智能预设",
|
||||
"auto fill the plugin with smart settings, to speed up your working process.": "自动填充智能设置的插件,以加快您的工作流程。",
|
||||
"Width:": "宽度:",
|
||||
"maintain the ratio between width and height slider": "保持宽度和高度滑块之间的比例",
|
||||
"Height:": "高度:",
|
||||
"CFG Scale:": "CFG 比例:",
|
||||
"larger value will put more emphasis on the prompt": "较大的值将更加强调提示",
|
||||
"Denoising Strength:": "降噪强度:",
|
||||
"Image CFG Scale:": "图像 CFG 比例:",
|
||||
"Pix2Pix CFG Scale (larger value will put more emphasis on the image)": "Pix2Pix CFG 比例(较大的值将更加强调图像)",
|
||||
"Mask Blur:": "蒙版模糊:",
|
||||
"Mask Expansion:": "蒙版扩展:",
|
||||
"the larger the value the more the mask will expand, '0' means use precise masking, use in combination with the mask blur": "值越大,蒙版就会扩展得越多,“ 0”表示使用精确的蒙版,与蒙版模糊一起使用",
|
||||
"Inpainting conditioning mask strength:": "修复条件蒙版强度:",
|
||||
"0 will keep the composition; 1 will allow composition to change": "0将保持构图; 1将允许构图发生变化",
|
||||
"Mask Content:": "蒙版内容:",
|
||||
"fill": "填充",
|
||||
"original": "原始",
|
||||
"latent noise": "潜在噪声",
|
||||
"latent nothing": "潜在无",
|
||||
"Inpaint at Full Res": "在全分辨率下修复",
|
||||
"Restore Faces": "面部修复",
|
||||
"Hi Res Fix": "高分修复",
|
||||
"Upscaler: ": "放大器:",
|
||||
"Hi Res Steps:": "高分步数:",
|
||||
"Hi Res Scale:": "高分比例:",
|
||||
"Hi Res Denoising Strength:": "高分降噪强度:",
|
||||
"Hi Res Output Width:": "高分输出宽度:",
|
||||
"Hi Res Output Height:": "高分输出高度:",
|
||||
"Inpaint Padding:": "修复填充:",
|
||||
"Seed:": "种子:",
|
||||
"Random": "随机",
|
||||
"Last": "最后",
|
||||
"Show Samplers": "显示采样器",
|
||||
"Select A Script": "选择脚本",
|
||||
"Activate": "激活",
|
||||
"Viewer": "查看器",
|
||||
"Preview": "预览器",
|
||||
"View your generated images on the canvas": "在画布上查看生成的图像",
|
||||
"Set Mask": "设置蒙版",
|
||||
"Set Init Image": "设置初始图像",
|
||||
"Interrupt": "中断",
|
||||
"Selection Area": "选择区域",
|
||||
"Thumbnail Size": "缩略图大小",
|
||||
"Square 1:1": "正方形 1:1",
|
||||
"Prompts Library": "提示库",
|
||||
"Prompt Shortcut: a single word that represent a prompt": "提示快捷方式:代表提示的单个单词",
|
||||
"Key for new prompt shortcut": "新提示快捷方式的键",
|
||||
"to be replaced": "要被替换的",
|
||||
"Value for new prompt shortcut": "新提示快捷方式的值",
|
||||
"to be replaced with": "要被替换为",
|
||||
"Add to Prompt Shortcut": "添加到提示快捷方式",
|
||||
"prompt shortcut": "提示快捷方式",
|
||||
"Selection a prompt": "选择提示词",
|
||||
"Refresh Menu": "刷新菜单",
|
||||
"Load": "加载",
|
||||
"Save": "保存",
|
||||
"History": "历史记录",
|
||||
"history of all the images you generated": "您生成的所有图像的历史记录",
|
||||
"Load Previous Generations": "加载以前的生成",
|
||||
"Clear Results": "清除结果",
|
||||
"Lexica": "Lexica",
|
||||
"Explore Lexica for prompts and inspiration": "探索提示词和灵感的词典",
|
||||
"Search:": "搜索:",
|
||||
"user prompt(text) to Search Lexica": "用户提示(文本)搜索词典",
|
||||
"User the selected area (image) on canvas to Search Lexica": "用户在画布上选择的区域(图像)搜索词典",
|
||||
"Image Search": "图像搜索",
|
||||
"Image Search Engine": "图像搜索引擎",
|
||||
"ControlNet": "ControlNet",
|
||||
"The Controlnet Extension is missing from Automatic1111.Please install it to use it through the plugin.": "Automatic1111缺少ControlNet扩展。请安装它以通过插件使用它。",
|
||||
"ControlNet Preset": "ControlNet预设",
|
||||
"auto fill the ControlNet with smart settings, to speed up your working process.": "自动填充智能设置的ControlNet,以加快您的工作流程。",
|
||||
"Set All CtrlNet Images": "设置所有 CtrlNet 图像",
|
||||
"Disable ControlNet Tab": "禁用ControlNet选项卡",
|
||||
"Control Net Settings Slot 0": "ControlNet设置插槽0",
|
||||
"Set CtrlNet Img": "设置 CtrlNet 图像",
|
||||
"Preview Annotator": "预览注释器",
|
||||
"Enable": "启用",
|
||||
"Low VRAM": "低显存",
|
||||
"Guess Mode": "猜测模式",
|
||||
"Weight:": "权重:",
|
||||
"2 will keep the composition; 0 will allow composition to change": "2将保持构图; 0将允许构图发生变化",
|
||||
"Guidance strength start:": "Guidance strength start:",
|
||||
"Guidance strength end:": "Guidance strength end:",
|
||||
"Horde": "Horde",
|
||||
"Horde Key:": "Horde密钥:",
|
||||
"Select Backend:": "选择后端:",
|
||||
"Native Horde": "本机 Horde",
|
||||
"use the horde with the plugin no need to install anything else": "使用插件的 Horde,无需安装其他任何内容",
|
||||
"Auto1111 Horde Extension": "Auto1111 Horde 扩展",
|
||||
"Use the horde extension from Automatic1111 Extension tab": "使用 Automatic1111 扩展选项卡中的 Horde 扩展",
|
||||
"Auto1111 Only": "仅限 Auto1111",
|
||||
"use Auto1111 disable the Horde": "使用 Auto1111 禁用 Horde",
|
||||
"Refresh Models": "刷新模型",
|
||||
"NSFW": "NSFW",
|
||||
"Share with LION": "与 LION 共享",
|
||||
"Extras": "额外",
|
||||
"Resize": "调整大小",
|
||||
"Resize scale of current selection size": "调整当前选择大小的比例",
|
||||
"Generate upscale": "生成放大",
|
||||
"No work in progress": "没有正在进行的工作",
|
||||
"Upscaler 1:": "放大算法 1:",
|
||||
"Upscaler 2:": "放大算法 2:",
|
||||
"Upscaler 2 visibility:": "放大算法 2 可见性:",
|
||||
"GFPGAN visibility:": "GFPGAN 可见性:",
|
||||
"CodeFormer visibility:": "CodeFormer 可见性:",
|
||||
"CodeFormer weight:": "CodeFormer 权重:",
|
||||
"Settings": "设置",
|
||||
"Custom Presets": "自定义预设",
|
||||
"SD Url:": "SD Url:",
|
||||
"Submit": "提交",
|
||||
"use sharp mask": "使用锐化蒙版",
|
||||
"Smart Object": "智能对象",
|
||||
"Live Progress Image": "实时进度图像",
|
||||
"Restore Original Prompt": "恢复原始提示",
|
||||
"Image Cfg Scale Slider": "图像配置比例滑块",
|
||||
"Use Silent Mode": "使用静默模式",
|
||||
"Your PC Speed(optimization):": "电脑性能(优化):",
|
||||
"Slow PC": "节能模式",
|
||||
"Fast PC": "高性能模式",
|
||||
"Use Colab": "使用 Colab",
|
||||
"Select Extension:": "选择扩展:",
|
||||
"Proxy Server": "代理服务器",
|
||||
"use the proxy server, need to run 'start_server.bat' ": "使用代理服务器,需要运行“start_server.bat”",
|
||||
"Auto111 Extension": "SD扩展",
|
||||
"use Automatic1111 Photoshop SD Extension, need to install the extension in Auto1111": "使用Photoshop SD 扩展,需要在 Auto1111 中安装扩展",
|
||||
"None": "无",
|
||||
"Use the Plugin Only No Additional Component": "仅使用插件,无需其他组件",
|
||||
"Folder Path (read only):": "文件夹路径(只读):",
|
||||
"copy paste the address to access the folder where the images are stored": "复制粘贴地址以访问存储图像的文件夹",
|
||||
"Get Path": "获取路径",
|
||||
"Preset Name": "预设名称",
|
||||
"New Preset": "新建预设",
|
||||
"Preset Type:": "预设类型:",
|
||||
"SD Preset": "SD 预设",
|
||||
"Save Preset": "保存预设",
|
||||
"Delete Preset": "删除预设",
|
||||
"The Controlnet Extension is missing from Automatic1111.\nPlease install it to use it through the plugin.": "本地SD中缺少ControlNet扩展。\n请安装该插件后再使用。",
|
||||
"Set CtrlImg": "设置原始图",
|
||||
"ControlNet Tab": "ControlNet",
|
||||
"ControlNet Unit": "ControlNet #",
|
||||
"Select Filter": "选择过滤器",
|
||||
"Select Module": "选择预处理器",
|
||||
"Select Model": "选择模型",
|
||||
"Keep all generated images on the canvas": "在画布上保留所有生成图像",
|
||||
"Delete all generated images from the canvas": "在画布上删除所有生成图像",
|
||||
"Keep only the highlighted images": "在画布上保留选中的图像",
|
||||
"Generate More": "生成更多"
|
||||
}
|
||||
|
|
@ -1 +1,29 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><defs><style>.cls-1{fill:#ff13dc;fill-opacity:0;}.cls-2{fill:#32cd32;}.cls-3{fill:none;}.cls-4{fill:#464646;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="check_all"><rect id="Canvas" class="cls-1" width="18" height="18"/><path class="cls-2" d="M13.32,10.06a3.25,3.25,0,1,0,3.25,3.25,3.25,3.25,0,0,0-3.25-3.25Zm-.86,5.23L11,13.78a.19.19,0,0,1,0-.26l.38-.38a.19.19,0,0,1,.26,0l1,1,2.23-2.23a.19.19,0,0,1,.26,0l.38.38a.19.19,0,0,1,0,.26l-2.74,2.74A.19.19,0,0,1,12.46,15.29Z"/><path class="cls-3" d="M3.74,7.78,9,10.46l5.26-2.68L16.56,9c.06,0,.09.07.09.11v-4s0,.08-.09.11L9.21,8.91a.45.45,0,0,1-.42,0L1.44,5.18c-.12-.07-.12-.17,0-.23L8.79,1.21A.5.5,0,0,1,9,1.16H1.35v7.9s0-.08.09-.11Z"/><path class="cls-3" d="M1.44,13.18q-.09-.06-.09-.12V17H9a.39.39,0,0,1-.21-.06Z"/><path class="cls-3" d="M9.21,16.91A.39.39,0,0,1,9,17h3a3.94,3.94,0,0,1-1.3-.8Z"/><path class="cls-3" d="M16.56,5c.06,0,.09.07.09.11V1.16H9a.5.5,0,0,1,.21.05Z"/><path class="cls-3" d="M15.16,9.89a4,4,0,0,1,1.49,1.41V9.06s0,.08-.09.12Z"/><path class="cls-3" d="M3.74,11.78,9,14.46l.53-.27a3.59,3.59,0,0,1-.12-.88,3.19,3.19,0,0,1,.06-.53l-.26.13a.45.45,0,0,1-.42,0L1.44,9.18q-.09-.06-.09-.12v4s0-.08.09-.11Z"/><path class="cls-4" d="M1.44,5c-.12.06-.12.16,0,.23L8.79,8.91a.45.45,0,0,0,.42,0l7.35-3.74c.06,0,.09-.07.09-.11h0s0-.08-.09-.11L9.21,1.21A.5.5,0,0,0,9,1.16H9a.5.5,0,0,0-.21.05Z"/><path class="cls-4" d="M10.69,16.17a3.84,3.84,0,0,1-1.16-2L9,14.46,3.74,11.78,1.44,13c-.06,0-.09.07-.09.11s0,.08.09.12l7.35,3.73a.4.4,0,0,0,.42,0Z"/><path class="cls-4" d="M8.79,12.91a.45.45,0,0,0,.42,0l.26-.13a3.89,3.89,0,0,1,3.85-3.37,3.82,3.82,0,0,1,1.84.48l1.4-.71q.09-.06.09-.12h0s0-.08-.09-.11l-2.3-1.17L9,10.46,3.74,7.78,1.44,9c-.06,0-.09.07-.09.11s0,.08.09.12Z"/></g></g></svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.c, .d {
|
||||
stroke: #d6d6d6;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.c, .d, .e {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.d {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="a" data-name="frame">
|
||||
<rect class="e" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="b" data-name="Layer_+">
|
||||
<rect class="c" x="13.34" y="12.24" width="38.49" height="39.43" rx="2.69" ry="2.69"/>
|
||||
<path class="d" d="m15.96,18.88h26.45c1.49,0,2.69,1.2,2.69,2.69v27.21"/>
|
||||
<polyline class="d" points="22.29 32.85 28.56 41.81 36.62 29.27"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 746 B |
|
|
@ -1 +1,28 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><defs><style>.cls-1{fill:#ff13dc;fill-opacity:0;}.cls-2{fill:#32cd32;}.cls-3{fill:#464646;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="check_one"><rect id="Canvas" class="cls-1" width="18" height="18"/><path class="cls-2" d="M13.32,10.06a3.25,3.25,0,1,0,3.25,3.25,3.25,3.25,0,0,0-3.25-3.25Zm-.86,5.23L11,13.78a.19.19,0,0,1,0-.26l.38-.38a.19.19,0,0,1,.26,0l1,1,2.23-2.23a.19.19,0,0,1,.26,0l.38.38a.19.19,0,0,1,0,.26l-2.74,2.74A.19.19,0,0,1,12.46,15.29Z"/><path class="cls-3" d="M16.65,9.06s0-.08-.09-.11L9.21,5.21a.51.51,0,0,0-.42,0L3.71,7.8,1.44,9c-.12.06-.12.16,0,.23l7.35,3.73a.45.45,0,0,0,.42,0l.26-.13a3.89,3.89,0,0,1,3.85-3.37,3.82,3.82,0,0,1,1.84.48l1.4-.71q.09-.06.09-.12Z"/></g></g></svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.c, .d {
|
||||
stroke: #d6d6d6;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.c, .d, .e {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.d {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="a" data-name="frame">
|
||||
<rect class="e" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="b" data-name="Layer_+1">
|
||||
<rect class="c" x="13.34" y="12.24" width="38.49" height="39.43" rx="2.69" ry="2.69"/>
|
||||
<polyline class="d" points="24.98 29.27 31.25 38.23 39.31 25.68"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 770 B After Width: | Height: | Size: 670 B |
|
|
@ -1,14 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;fill-opacity:0;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<rect id="Canvas" class="st0" width="18" height="18"/>
|
||||
<path class="st1" d="M9,6C7.3,6,6,7.3,6,9s1.3,3,3,3s3-1.3,3-3S10.7,6,9,6z"/>
|
||||
<path class="st1" d="M16.5,4h-3l-1.7-1.8C11.7,2.1,11.6,2,11.4,2H6.6C6.4,2,6.3,2.1,6.2,2.2L4.5,4h-3C1.2,4,1,4.2,1,4.5v10
|
||||
C1,14.8,1.2,15,1.5,15h15c0.3,0,0.5-0.2,0.5-0.5v-10C17,4.2,16.8,4,16.5,4z M9,13.1c-2.3,0-4.1-1.8-4.1-4.1c0-2.3,1.8-4.1,4.1-4.1
|
||||
c2.3,0,4.1,1.8,4.1,4.1S11.3,13.1,9,13.1L9,13.1z"/>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.c, .d {
|
||||
stroke: #d6d6d6;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.c, .d, .e {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.d {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="a" data-name="frame">
|
||||
<rect class="e" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="b" data-name="camera">
|
||||
<circle class="c" cx="32.59" cy="34.2" r="10.02"/>
|
||||
<polyline class="d" points="15.12 20.31 22.29 20.31 26.69 12.3 38.34 12.3 42.89 20.31 50.06 20.31"/>
|
||||
<rect class="c" x="13.34" y="12.24" width="38.49" height="39.43" rx="2.69" ry="2.69"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 873 B After Width: | Height: | Size: 757 B |
|
|
@ -1,4 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M55 9.22988C53.7379 7.92856 52.227 6.89417 50.5573 6.18813C48.8875 5.48209 47.0929 5.11882 45.28 5.11988H45.17C41.3631 5.11593 37.7084 6.61447 35 9.28988L22.68 21.6399C20.458 23.8816 19.0645 26.8123 18.7283 29.9507C18.3921 33.089 19.1333 36.2484 20.83 38.9099C21.1056 39.3419 21.4755 39.7058 21.912 39.9743C22.3485 40.2428 22.8402 40.4088 23.35 40.4599C23.4765 40.4698 23.6036 40.4698 23.73 40.4599C24.65 40.4648 25.5344 40.1053 26.19 39.4599C26.7477 38.907 27.0994 38.18 27.1867 37.3995C27.274 36.6191 27.0917 35.8323 26.67 35.1699C25.8204 33.8426 25.4485 32.2653 25.6158 30.6983C25.783 29.1312 26.4794 27.668 27.59 26.5499L40.17 13.9999C40.8505 13.3239 41.6643 12.7969 42.5595 12.4526C43.4548 12.1083 44.4119 11.9541 45.37 11.9999C46.3325 12.041 47.2754 12.2849 48.1371 12.7158C48.9987 13.1466 49.7597 13.7546 50.37 14.4999C51.4441 15.873 51.9746 17.5937 51.8602 19.3332C51.7459 21.0728 50.9946 22.7092 49.75 23.9299L47.29 26.3899C46.9662 26.7115 46.7092 27.094 46.5338 27.5153C46.3585 27.9366 46.2682 28.3885 46.2682 28.8449C46.2682 29.3013 46.3585 29.7531 46.5338 30.1745C46.7092 30.5958 46.9662 30.9783 47.29 31.2999C47.9422 31.949 48.8249 32.3134 49.745 32.3134C50.6652 32.3134 51.5479 31.949 52.2 31.2999L54.83 28.6699C57.3937 26.0948 58.8471 22.6181 58.8789 18.9845C58.9106 15.351 57.5183 11.8494 55 9.22988V9.22988Z" fill="black"/>
|
||||
<path d="M40.5401 23.4198C39.8914 23.3466 39.2352 23.4579 38.6469 23.7408C38.0586 24.0238 37.5621 24.4669 37.2143 25.0194C36.8665 25.5718 36.6817 26.2112 36.6809 26.864C36.6802 27.5168 36.8636 28.1566 37.2101 28.7098C38.0618 30.0376 38.4357 31.6159 38.2703 33.1846C38.1049 34.7534 37.41 36.2189 36.3001 37.3398L23.7201 49.9198C23.0386 50.5944 22.2246 51.1203 21.3296 51.4646C20.4346 51.8088 19.478 51.9638 18.5201 51.9198C17.5573 51.8756 16.6145 51.6293 15.753 51.1969C14.8916 50.7644 14.1308 50.1556 13.5201 49.4098C12.4453 48.0408 11.9142 46.3229 12.0286 44.5862C12.143 42.8494 12.895 41.2161 14.1401 39.9998L16.6001 37.5398C17.2446 36.8837 17.604 35.9996 17.6001 35.0798C17.6029 34.6258 17.516 34.1756 17.3444 33.7552C17.1728 33.3347 16.9198 32.9523 16.6001 32.6298C15.9405 31.9949 15.0606 31.6403 14.1451 31.6403C13.2296 31.6403 12.3497 31.9949 11.6901 32.6298L9.29012 34.9998C7.95287 36.3527 6.89608 37.9564 6.18046 39.7188C5.46484 41.4812 5.10449 43.3677 5.12012 45.2698C5.11771 47.0844 5.48034 48.8809 6.18641 50.5525C6.89249 52.224 7.92759 53.7365 9.23012 54.9998C11.8477 57.5051 15.3395 58.8901 18.9626 58.8602C22.5857 58.8303 26.0542 57.3879 28.6301 54.8398L41.2101 42.2498C43.424 40.0076 44.8123 37.0811 45.1484 33.948C45.4844 30.8149 44.7482 27.6605 43.0601 24.9998C42.7867 24.5632 42.4179 24.1943 41.9815 23.9206C41.545 23.647 41.0522 23.4758 40.5401 23.4198Z" fill="black"/>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.c {
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.c, .d {
|
||||
stroke: #d6d6d6;
|
||||
stroke-linecap: round;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.c, .d, .e {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.d {
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="a" data-name="frame">
|
||||
<rect class="e" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="b" data-name="chain_black.svg">
|
||||
<path class="d" d="m36.42,34.84c2.15,1.45,3.55,3.99,3.55,6.64,0,4.41-3.57,7.98-7.98,7.98s-7.98-3.57-7.98-7.98c0-2.7,1.46-5.24,3.57-6.65"/>
|
||||
<path class="d" d="m27.59,27.46c-.31-.22-3.58-2.63-3.57-6.47.01-3.85,3.31-7.98,7.98-7.98,4.41,0,7.98,3.57,7.98,7.98,0,2.65-1.4,5.19-3.56,6.64"/>
|
||||
<line class="c" x1="32" y1="24.19" x2="32" y2="38.83"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 890 B |
|
|
@ -1,4 +1,41 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M55 9.22988C53.7379 7.92856 52.227 6.89417 50.5573 6.18813C48.8875 5.48209 47.0929 5.11882 45.28 5.11988H45.17C41.3631 5.11593 37.7084 6.61447 35 9.28988L22.68 21.6399C20.458 23.8816 19.0645 26.8123 18.7283 29.9507C18.3921 33.089 19.1333 36.2484 20.83 38.9099C21.1056 39.3419 21.4755 39.7058 21.912 39.9743C22.3485 40.2428 22.8402 40.4088 23.35 40.4599C23.4765 40.4698 23.6036 40.4698 23.73 40.4599C24.65 40.4648 25.5344 40.1053 26.19 39.4599C26.7477 38.907 27.0994 38.18 27.1867 37.3995C27.274 36.6191 27.0917 35.8323 26.67 35.1699C25.8204 33.8426 25.4485 32.2653 25.6158 30.6983C25.783 29.1312 26.4794 27.668 27.59 26.5499L40.17 13.9999C40.8505 13.3239 41.6643 12.7969 42.5595 12.4526C43.4548 12.1083 44.4119 11.9541 45.37 11.9999C46.3325 12.041 47.2754 12.2849 48.1371 12.7158C48.9987 13.1466 49.7597 13.7546 50.37 14.4999C51.4441 15.873 51.9746 17.5937 51.8602 19.3332C51.7459 21.0728 50.9946 22.7092 49.75 23.9299L47.29 26.3899C46.9662 26.7115 46.7092 27.094 46.5338 27.5153C46.3585 27.9366 46.2682 28.3885 46.2682 28.8449C46.2682 29.3013 46.3585 29.7531 46.5338 30.1745C46.7092 30.5958 46.9662 30.9783 47.29 31.2999C47.9422 31.949 48.8249 32.3134 49.745 32.3134C50.6652 32.3134 51.5479 31.949 52.2 31.2999L54.83 28.6699C57.3937 26.0948 58.8471 22.6181 58.8789 18.9845C58.9106 15.351 57.5183 11.8494 55 9.22988V9.22988Z" fill="white"/>
|
||||
<path d="M40.5401 23.4198C39.8914 23.3466 39.2352 23.4579 38.6469 23.7408C38.0586 24.0238 37.5621 24.4669 37.2143 25.0194C36.8665 25.5718 36.6817 26.2112 36.6809 26.864C36.6802 27.5168 36.8636 28.1566 37.2101 28.7098C38.0618 30.0376 38.4357 31.6159 38.2703 33.1846C38.1049 34.7534 37.41 36.2189 36.3001 37.3398L23.7201 49.9198C23.0386 50.5944 22.2246 51.1203 21.3296 51.4646C20.4346 51.8088 19.478 51.9638 18.5201 51.9198C17.5573 51.8756 16.6145 51.6293 15.753 51.1969C14.8916 50.7644 14.1308 50.1556 13.5201 49.4098C12.4453 48.0408 11.9142 46.3229 12.0286 44.5862C12.143 42.8494 12.895 41.2161 14.1401 39.9998L16.6001 37.5398C17.2446 36.8837 17.604 35.9996 17.6001 35.0798C17.6029 34.6258 17.516 34.1756 17.3444 33.7552C17.1728 33.3347 16.9198 32.9523 16.6001 32.6298C15.9405 31.9949 15.0606 31.6403 14.1451 31.6403C13.2296 31.6403 12.3497 31.9949 11.6901 32.6298L9.29012 34.9998C7.95287 36.3527 6.89608 37.9564 6.18046 39.7188C5.46484 41.4812 5.10449 43.3677 5.12012 45.2698C5.11771 47.0844 5.48034 48.8809 6.18641 50.5525C6.89249 52.224 7.92759 53.7365 9.23012 54.9998C11.8477 57.5051 15.3395 58.8901 18.9626 58.8602C22.5857 58.8303 26.0542 57.3879 28.6301 54.8398L41.2101 42.2498C43.424 40.0076 44.8123 37.0811 45.1484 33.948C45.4844 30.8149 44.7482 27.6605 43.0601 24.9998C42.7867 24.5632 42.4179 24.1943 41.9815 23.9206C41.545 23.647 41.0522 23.4758 40.5401 23.4198Z" fill="white"/>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.c {
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.c, .d, .e {
|
||||
stroke: #d6d6d6;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
|
||||
.c, .d, .e, .f {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.c, .e {
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.d {
|
||||
stroke-width: 8px;
|
||||
}
|
||||
|
||||
.d, .e {
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="a" data-name="frame">
|
||||
<rect class="f" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="b" data-name="chain_white">
|
||||
<path class="e" d="m36.42,34.84c2.15,1.45,3.55,3.99,3.55,6.64,0,4.41-3.57,7.98-7.98,7.98s-7.98-3.57-7.98-7.98c0-2.7,1.46-5.24,3.57-6.65"/>
|
||||
<path class="e" d="m27.59,27.46c-.31-.22-3.58-2.63-3.57-6.47.01-3.85,3.31-7.98,7.98-7.98,4.41,0,7.98,3.57,7.98,7.98,0,2.65-1.4,5.19-3.56,6.64"/>
|
||||
<line class="c" x1="32" y1="24.19" x2="32" y2="38.83"/>
|
||||
<line class="d" x1="44.83" y1="31.51" x2="59.93" y2="31.51"/>
|
||||
<line class="d" x1="4.02" y1="31.51" x2="19.6" y2="31.51"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 1.1 KiB |
|
|
@ -1 +1,30 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><defs><style>.cls-1{fill:#ff13dc;fill-opacity:0;}.cls-2{fill:#ff4500;}.cls-3{fill:#464646;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="cross_all"><rect id="Canvas" class="cls-1" width="18" height="18"/><path class="cls-2" d="M13.32,10.06a3.25,3.25,0,1,0,3.25,3.25A3.25,3.25,0,0,0,13.32,10.06ZM15,14.55a.31.31,0,0,1-.44.44l-1.23-1.24L12.08,15h0a.31.31,0,0,1-.44-.44l1.24-1.24-1.24-1.23a.31.31,0,0,1,0-.44.31.31,0,0,1,.44,0l1.24,1.23,1.23-1.23h0a.3.3,0,0,1,.44,0,.3.3,0,0,1,0,.44l-1.23,1.23Z"/><path class="cls-3" d="M16.56,5,9.21,1.21A.5.5,0,0,0,9,1.16H9a.5.5,0,0,0-.21.05L1.44,5c-.12.06-.12.16,0,.23L8.79,8.91a.45.45,0,0,0,.42,0l7.35-3.74c.06,0,.09-.07.09-.11h0S16.62,5,16.56,5Z"/><path class="cls-3" d="M9,14.46,3.74,11.78,1.44,13c-.06,0-.09.07-.09.11s0,.08.09.12l7.35,3.73a.4.4,0,0,0,.42,0l1.48-.74a3.84,3.84,0,0,1-1.16-2Z"/><path class="cls-3" d="M14.26,7.78,9,10.46,3.74,7.78,1.44,9c-.06,0-.09.07-.09.11s0,.08.09.12l7.35,3.73a.45.45,0,0,0,.42,0l.26-.13a3.89,3.89,0,0,1,3.85-3.37,3.82,3.82,0,0,1,1.84.48l1.4-.71q.09-.06.09-.12h0s0-.08-.09-.11Z"/></g></g></svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.c, .d {
|
||||
stroke: #d6d6d6;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.c, .d, .e {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.d {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="a" data-name="frame">
|
||||
<rect class="e" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="b" data-name="Layer_-">
|
||||
<line class="c" x1="35.42" y1="29.57" x2="23.48" y2="41.51"/>
|
||||
<line class="c" x1="35.42" y1="41.51" x2="23.48" y2="29.57"/>
|
||||
<rect class="c" x="13.34" y="12.24" width="38.49" height="39.43" rx="2.69" ry="2.69"/>
|
||||
<path class="d" d="m15.96,18.88h26.43c1.48,0,2.69,1.2,2.69,2.69v27.21"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 807 B |
|
|
@ -1 +1,25 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><defs><style>.cls-1{fill:#ff13dc;fill-opacity:0;}.cls-2{fill:#ff4500;}.cls-3{fill:#464646;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="cross_one"><rect id="Canvas" class="cls-1" width="18" height="18"/><path class="cls-2" d="M13.32,10.06a3.25,3.25,0,1,0,3.25,3.25A3.25,3.25,0,0,0,13.32,10.06ZM15,14.55a.31.31,0,0,1-.44.44l-1.23-1.24L12.08,15h0a.31.31,0,0,1-.44-.44l1.24-1.24-1.24-1.23a.31.31,0,0,1,0-.44.31.31,0,0,1,.44,0l1.24,1.23,1.23-1.23h0a.3.3,0,0,1,.44,0,.3.3,0,0,1,0,.44l-1.23,1.23Z"/><path class="cls-3" d="M16.65,9h0s0-.08-.09-.11L9.21,5.15a.45.45,0,0,0-.42,0L1.44,8.89c-.12.06-.12.16,0,.22l7.35,3.74a.45.45,0,0,0,.42,0l.26-.13a3.91,3.91,0,0,1,3.85-3.31,3.84,3.84,0,0,1,1.78.44l1.46-.74C16.62,9.08,16.65,9,16.65,9Z"/></g></g></svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.c {
|
||||
stroke: #d6d6d6;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.c, .d {
|
||||
fill: none;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="a" data-name="frame">
|
||||
<rect class="d" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="b" data-name="Layer_-1">
|
||||
<line class="c" x1="38.11" y1="25.99" x2="26.17" y2="37.92"/>
|
||||
<line class="c" x1="38.11" y1="37.92" x2="26.17" y2="25.99"/>
|
||||
<rect class="c" x="13.34" y="12.24" width="38.49" height="39.43" rx="2.69" ry="2.69"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 814 B After Width: | Height: | Size: 675 B |
|
|
@ -1,15 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" id="icon" xmlns="http://www.w3.org/2000/svg">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
.c, .d {
|
||||
stroke: #d6d6d6;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.c, .d, .e {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.d {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
|
||||
.f {
|
||||
fill: #d6d6d6;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<title>image--search</title>
|
||||
<path d="M24,14a5.99,5.99,0,0,0-4.885,9.4712L14,28.5859,15.4141,30l5.1147-5.1147A5.9971,5.9971,0,1,0,24,14Zm0,10a4,4,0,1,1,4-4A4.0045,4.0045,0,0,1,24,24Z"/>
|
||||
<path d="M17,12a3,3,0,1,0-3-3A3.0033,3.0033,0,0,0,17,12Zm0-4a1,1,0,1,1-1,1A1.0009,1.0009,0,0,1,17,8Z"/>
|
||||
<path d="M12,24H4V17.9966L9,13l5.5859,5.5859L16,17.168l-5.5859-5.5855a2,2,0,0,0-2.8282,0L4,15.168V4H24v6h2V4a2.0023,2.0023,0,0,0-2-2H4A2.002,2.002,0,0,0,2,4V24a2.0023,2.0023,0,0,0,2,2h8Z"/>
|
||||
<rect id="_Transparent_Rectangle_" data-name="<Transparent Rectangle>" class="cls-1" width="32" height="32"/>
|
||||
<g id="a" data-name="frame">
|
||||
<rect class="e" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="b" data-name="search1">
|
||||
<path class="d" d="m16.03,40.05c-1.48,0-2.69-1.23-2.69-2.74V14.98c0-1.52,1.2-2.74,2.69-2.74h33.14c1.48,0,2.69,1.23,2.69,2.74v22.33c0,1.52-1.2,2.74-2.69,2.74"/>
|
||||
<path class="f" d="m33.26,28.43c3.38,0,6.13,2.75,6.13,6.13s-2.75,6.13-6.13,6.13-6.13-2.75-6.13-6.13,2.75-6.13,6.13-6.13m0-4c-5.59,0-10.13,4.54-10.13,10.13s4.54,10.13,10.13,10.13,10.13-4.54,10.13-10.13-4.54-10.13-10.13-10.13h0Z"/>
|
||||
<line class="c" x1="25.36" y1="45.3" x2="18.08" y2="52.7"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 933 B After Width: | Height: | Size: 1012 B |
|
|
@ -1,8 +1,25 @@
|
|||
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="_Слой_1" data-name="Слой 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
stroke: #d6d6d6;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
<g>
|
||||
<title>Layer 1</title>
|
||||
<rect stroke-dasharray="2,2" stroke="#000" id="svg_5" height="299" width="299" y="56.21698" x="46.78302" stroke-width="5" fill="none"/>
|
||||
<path id="svg_7" d="m105.54971,171.35925l0,300.2682l362.99997,0l0,-300.2682l-362.99997,0zm305.86109,274.4952l-248.7222,0l0,-248.7222l248.7222,0l0,248.7222zm-188.22221,-151.24999c19.17178,0 34.727,-15.55522 34.727,-34.727c0,-19.18522 -15.55522,-34.727 -34.727,-34.727c-19.18522,0 -34.727,15.55522 -34.727,34.727c0,19.17178 15.54178,34.727 34.727,34.727zm174.79121,30.25l-66.10633,-66.10633l-92.99521,92.99521l-25.773,-25.773l-36.99911,36.99911l0,69.44055l221.87365,0l0,-107.55555z" stroke-width="2" stroke="#000" fill="#8691a0"/>
|
||||
</g>
|
||||
.cls-1, .cls-2 {
|
||||
fill: none;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="a">
|
||||
<rect class="cls-2" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="b">
|
||||
<rect class="cls-1" x="13.34" y="12.24" width="38.49" height="39.43" rx="2.69" ry="2.69"/>
|
||||
<polyline class="cls-1" points="13.33 45.4 22.29 33.75 28.56 41.81 42.89 26.58 51.85 39.12"/>
|
||||
<circle class="cls-1" cx="25.87" cy="22.1" r="1.79"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 778 B After Width: | Height: | Size: 720 B |
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
<svg
|
||||
version="1.0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18.000000pt"
|
||||
height="18.000000pt"
|
||||
viewBox="0 0 127.000000 127.000000"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
style="
|
||||
width: 32px;
|
||||
height: 27px;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: -1px;
|
||||
"
|
||||
>
|
||||
<g
|
||||
transform="translate(0.000000,115.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000"
|
||||
stroke="none"
|
||||
>
|
||||
<path
|
||||
d="M577 1172 c-10 -11 -17 -36 -17 -60 l0 -42 -133 0 c-117 0 -136 -2
|
||||
-150 -18 -15 -16 -17 -49 -17 -254 l0 -236 -24 -6 c-53 -13 -74 -67 -38 -99
|
||||
15 -13 39 -17 115 -17 53 0 97 -4 97 -9 0 -5 -13 -69 -30 -143 -35 -155 -36
|
||||
-169 -12 -191 45 -41 84 -11 104 78 l13 60 150 0 150 0 13 -60 c15 -67 35 -95
|
||||
67 -95 27 0 55 29 55 56 0 11 -13 78 -30 150 -16 71 -30 135 -30 142 0 9 26
|
||||
12 98 12 83 0 102 3 115 18 33 36 14 85 -39 98 l-24 6 0 238 c0 217 -2 238
|
||||
-18 253 -15 14 -42 17 -150 17 l-132 0 0 43 c0 30 -6 49 -18 60 -26 23 -94 22
|
||||
-115 -1z m393 -377 l0 -235 -335 0 -335 0 0 235 0 235 335 0 335 0 0 -235z
|
||||
m77 -291 c3 -9 0 -20 -8 -25 -18 -11 -790 -11 -808 0 -8 5 -11 16 -8 25 6 14
|
||||
49 16 412 16 363 0 406 -2 412 -16z m-293 -133 c9 -39 16 -75 16 -80 0 -7 -47
|
||||
-11 -135 -11 -88 0 -135 4 -135 11 0 5 7 41 16 80 l16 69 103 0 103 0 16 -69z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
<svg
|
||||
viewBox="0 0 36 36"
|
||||
style="width: 18px; height: 18px"
|
||||
>
|
||||
<path
|
||||
d="M33.567 8.2L27.8 2.432a1.215 1.215 0 0 0-.866-.353H26.9a1.371 1.371 0 0 0-.927.406L5.084 23.372a.99.99 0 0 0-.251.422L2.055 33.1c-.114.377.459.851.783.851a.251.251 0 0 0 .062-.007c.276-.063 7.866-2.344 9.311-2.778a.972.972 0 0 0 .414-.249l20.888-20.889a1.372 1.372 0 0 0 .4-.883 1.221 1.221 0 0 0-.346-.945zM11.4 29.316c-2.161.649-4.862 1.465-6.729 2.022l2.009-6.73z"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 729 B |
|
|
@ -0,0 +1,56 @@
|
|||
<svg
|
||||
version="1.0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="512.000000pt"
|
||||
height="512.000000pt"
|
||||
viewBox="0 0 512.000000 512.000000"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
style="
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: -1px;
|
||||
"
|
||||
>
|
||||
<g
|
||||
transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000"
|
||||
stroke="none"
|
||||
>
|
||||
<path
|
||||
d="M920 4311 c-118 -36 -212 -129 -259 -254 -18 -50 -21 -79 -21 -217
|
||||
l0 -160 80 0 80 0 0 128 c0 147 14 208 60 264 51 62 99 78 231 78 63 0 131 3
|
||||
152 6 l37 7 0 78 0 79 -167 -1 c-93 0 -179 -4 -193 -8z"
|
||||
/>
|
||||
<path
|
||||
d="M3840 4242 l0 -79 38 -7 c20 -3 88 -6 151 -6 132 0 180 -16 231 -78
|
||||
46 -56 60 -117 60 -264 l0 -128 80 0 80 0 0 158 c0 131 -3 169 -19 215 -36
|
||||
104 -104 182 -204 234 -50 27 -58 28 -234 31 l-183 4 0 -80z"
|
||||
/>
|
||||
<path
|
||||
d="M2450 3404 c-252 -30 -471 -107 -694 -243 -199 -122 -420 -320 -578
|
||||
-519 -32 -40 -58 -79 -58 -85 0 -20 105 -146 213 -256 296 -302 597 -483 937
|
||||
-562 122 -29 364 -37 495 -16 318 51 613 200 903 457 102 90 249 249 310 334
|
||||
l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
||||
-290 27 -353 19z m279 -317 c189 -54 340 -214 386 -409 79 -338 -192 -670
|
||||
-549 -669 -158 0 -278 47 -390 151 -123 116 -177 238 -178 400 0 161 54 284
|
||||
178 400 153 143 345 187 553 127z"
|
||||
/>
|
||||
<path
|
||||
d="M2485 2841 c-59 -17 -86 -31 -129 -71 -153 -141 -103 -397 92 -471
|
||||
116 -43 262 -7 333 82 46 59 62 106 63 184 0 52 -6 82 -23 117 -27 55 -94 121
|
||||
-148 143 -45 19 -146 28 -188 16z"
|
||||
/>
|
||||
<path
|
||||
d="M640 1280 c0 -138 3 -167 21 -217 40 -107 103 -178 202 -230 50 -27
|
||||
58 -28 235 -31 l182 -4 0 80 0 79 -37 7 c-21 3 -89 6 -152 6 -132 0 -180 16
|
||||
-231 78 -46 56 -60 117 -60 264 l0 128 -80 0 -80 0 0 -160z"
|
||||
/>
|
||||
<path
|
||||
d="M4320 1312 c0 -147 -14 -208 -60 -264 -51 -62 -99 -78 -231 -78 -63
|
||||
0 -131 -3 -151 -6 l-38 -7 0 -79 0 -80 183 4 c176 3 184 4 234 31 99 52 162
|
||||
123 202 230 18 50 21 79 21 217 l0 160 -80 0 -80 0 0 -128z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 27.6.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:none;}
|
||||
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:none;stroke:#D6D6D6;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<g id="frame">
|
||||
<rect class="st0" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="selection-area_x5F_2.svg">
|
||||
<path id="frame_00000039099898607207917570000009294483814832552347_" class="st1" d="M13.34,36.93c0-0.03,0-4.95,0-4.98
|
||||
c0-0.06,0-4.9,0-4.9"/>
|
||||
<path id="frame_00000005248667001567519180000004593825086206177449_" class="st1" d="M21.72,51.66c-1.9,0-3.8,0-5.7,0
|
||||
c-0.19,0-0.48-0.02-0.8-0.12c-0.42-0.13-0.8-0.36-1.1-0.67c-0.49-0.49-0.79-1.16-0.79-1.9c0-0.08,0-6.01,0-6.09"/>
|
||||
<path id="frame_00000114036308278317986480000014951358649188744836_" class="st1" d="M37.61,51.66c-0.04,0-5,0-5.03,0
|
||||
c-1.62,0-3.24,0-4.86,0"/>
|
||||
<path id="frame_00000079472285635594814390000004521152521064386487_" class="st1" d="M51.83,42.93c0,2.02,0,4.03,0,6.05
|
||||
c0,0.19-0.02,0.48-0.12,0.8c-0.13,0.42-0.36,0.8-0.67,1.1c-0.49,0.49-1.16,0.79-1.9,0.79c-0.07,0-5.47,0-5.54,0"/>
|
||||
<path id="frame_00000014606341256593317910000012874094123312002953_" class="st1" d="M51.83,27.01c0,0.03,0,4.91,0,4.94
|
||||
c0,1.66,0,3.31,0,4.97"/>
|
||||
<path id="frame_00000181054580322392786830000004708723869888510346_" class="st1" d="M43.59,12.24c1.85,0,3.7,0,5.55,0
|
||||
c0.19,0,0.48,0.02,0.8,0.12c0.42,0.13,0.8,0.36,1.1,0.67c0.49,0.49,0.79,1.16,0.79,1.9c0,0.08,0,6.04,0,6.12"/>
|
||||
<path id="frame_00000070799153599262733950000005754557140857329592_" class="st1" d="M27.71,12.24c0.03,0,4.84,0,4.87,0
|
||||
c1.68,0,3.36,0,5.03,0"/>
|
||||
<path id="frame_00000165930517913269344000000003886705051876999349_" class="st1" d="M13.34,21.01c0,0,0-6.03,0-6.09
|
||||
c0-0.19,0.02-0.48,0.12-0.8c0.13-0.42,0.36-0.8,0.67-1.1c0.49-0.49,1.16-0.79,1.9-0.79c0.08,0,5.64,0,5.71,0"/>
|
||||
<polyline id="frame_00000099648434244861981470000013376370528818678665_" class="st1" points="38.09,18.4 31.84,21.18
|
||||
36.01,25.35 "/>
|
||||
<path id="frame_00000128454345919547781650000004815678695790207374_" class="st1" d="M25.72,23.35c-3.21,2.29-4.3,5.62-4.46,6.13
|
||||
c-0.07,0.22-0.53,1.85-0.53,3.51c0,6.52,5.29,11.81,11.81,11.81s11.81-5.29,11.81-11.81c0-6.48-5.33-11.81-11.81-11.81"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.c, .d {
|
||||
stroke: #d6d6d6;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.c, .d, .e {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.d {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="a" data-name="frame">
|
||||
<rect class="e" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="b" data-name="reset">
|
||||
<path class="d" d="m23.51,17.36c-5.8,3.19-9.73,9.35-9.73,16.44,0,10.35,8.39,18.75,18.75,18.75s18.75-8.39,18.75-18.75-8.39-18.75-18.75-18.75"/>
|
||||
<circle class="c" cx="32.59" cy="34.2" r="4.93"/>
|
||||
<polyline class="d" points="41.36 10.64 31.43 15.06 38.05 21.67"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 777 B |
|
|
@ -1,19 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 -0.5 21 21" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
|
||||
<title>search_right [#1507]</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.c {
|
||||
stroke: #d6d6d6;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
</defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Dribbble-Light-Preview" transform="translate(-179.000000, -280.000000)" fill="#000000">
|
||||
<g id="icons" transform="translate(56.000000, 160.000000)">
|
||||
<path d="M128.93985,132.929 L130.42455,134.343 L124.4847,140 L123,138.586 L128.93985,132.929 Z M136.65,132 C133.75515,132 131.4,129.757 131.4,127 C131.4,124.243 133.75515,122 136.65,122 C139.54485,122 141.9,124.243 141.9,127 C141.9,129.757 139.54485,132 136.65,132 L136.65,132 Z M136.65,120 C132.5907,120 129.3,123.134 129.3,127 C129.3,130.866 132.5907,134 136.65,134 C140.7093,134 144,130.866 144,127 C144,123.134 140.7093,120 136.65,120 L136.65,120 Z" id="search_right-[#1507]">
|
||||
.c, .d {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
.e {
|
||||
fill: #d6d6d6;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="a" data-name="frame">
|
||||
<rect class="d" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="b" data-name="search">
|
||||
<path class="e" d="m38.57,14.45c5.98,0,10.85,4.87,10.85,10.85s-4.87,10.85-10.85,10.85-10.85-4.87-10.85-10.85,4.87-10.85,10.85-10.85m0-4c-8.2,0-14.85,6.65-14.85,14.85s6.65,14.85,14.85,14.85,14.85-6.65,14.85-14.85-6.65-14.85-14.85-14.85h0Z"/>
|
||||
<line class="c" x1="26.98" y1="41.06" x2="16.3" y2="51.91"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 803 B |
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="800px" height="800px" viewBox="0 0 68.334 68.334"
|
||||
xml:space="preserve">
|
||||
<g>
|
||||
<path style="stroke: #ffffff; fill: #ffffff; stroke-width:0.5px" d="M65.333,60.834h3v7.5h-7.5v-3h4.5V60.834z M18.167,68.334h10.667v-3H18.167V68.334z M39.501,68.334h10.666v-3H39.501
|
||||
V68.334z M3.001,60.834h-3v7.5h7.5v-3h-4.5V60.834z M3.001,39.5h-3v10.667h3V39.5z M3.001,18.167h-3v10.667h3V18.167z M0.001,7.5h3
|
||||
V3h4.5V0h-7.5V7.5z M28.833,0H18.167v3h10.666V0z M50.167,0H39.5v3h10.667V0z M60.833,0v3h4.5v4.5h3V0H60.833z M65.333,50.167h3
|
||||
V39.5h-3V50.167z M65.333,28.834h3V18.167h-3V28.834z" />
|
||||
</g>
|
||||
<g> <path style="stroke: #000000; fill: #ffffff; stroke-width:0.5px" d="M56.313,22.29l-9.031-3.358c-1.404-0.52-2.975,0.005-3.781,1.263l-5.052,7.87
|
||||
c-0.947,1.478-0.519,3.443,0.959,4.391c0.428,0.275,0.897,0.435,1.372,0.485c1.16,0.125,2.346-0.395,3.019-1.444l1.566-2.441
|
||||
c3.058,10.698-3.319,16.024-3.628,16.273c-1.357,1.095-1.592,3.088-0.508,4.456c0.551,0.697,1.334,1.104,2.154,1.193
|
||||
c0.784,0.084,1.6-0.125,2.271-0.646c3.824-2.969,9.186-11.21,5.804-23.066l2.642,0.981c1.641,0.61,3.473-0.226,4.086-1.872
|
||||
C58.798,24.731,57.96,22.901,56.313,22.29z M14.594,26.495l9.266,1.25c1.74,0.235,3.34-0.985,3.575-2.725
|
||||
c0.067-0.504,0.013-0.998-0.142-1.449c-0.376-1.104-1.348-1.958-2.583-2.124l-2.876-0.389c8.403-7.292,15.926-3.765,16.281-3.59
|
||||
c1.567,0.767,3.472,0.136,4.254-1.422c0.399-0.796,0.437-1.679,0.17-2.459c-0.255-0.747-0.788-1.397-1.546-1.787
|
||||
c-4.309-2.209-14.043-3.583-23.357,4.493l-0.227-2.807c-0.141-1.746-1.674-3.053-3.424-2.912c-1.748,0.141-3.053,1.673-2.911,3.423
|
||||
l0.775,9.604C11.973,25.094,13.111,26.296,14.594,26.495z M32.264,42.794c-0.65-1.63-2.5-2.422-4.132-1.771
|
||||
c-0.472,0.188-0.874,0.479-1.191,0.836c-0.776,0.871-1.04,2.139-0.577,3.293l1.076,2.695c-10.482-3.726-11.114-12.01-11.138-12.406
|
||||
c-0.104-1.741-1.591-3.089-3.331-3.002c-0.89,0.044-1.677,0.447-2.225,1.063c-0.523,0.59-0.828,1.374-0.795,2.225
|
||||
c0.196,4.838,3.791,13.988,15.406,18.121l-2.332,1.579c-1.45,0.981-1.834,2.959-0.85,4.413c0.983,1.454,2.96,1.835,4.412,0.852
|
||||
l7.979-5.402c1.24-0.842,1.724-2.425,1.169-3.812L32.264,42.794z"/>
|
||||
</g>
|
||||
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -1 +1,27 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 112.04"><defs><style>.cls-1{fill-rule:evenodd;}</style></defs><title>writing</title><path class="cls-1" d="M109.28,19.61l12.21,9.88a3.77,3.77,0,0,1,.56,5.29l-5.46,6.75L98.53,26.93,104,20.17a3.79,3.79,0,0,1,5.29-.56ZM21.07,30.81a3.18,3.18,0,0,1,0-6.36H74.12a3.18,3.18,0,0,1,0,6.36ZM9.49,0H85.71A9.53,9.53,0,0,1,95.2,9.49v5.63l-4.48,5.53a9.81,9.81,0,0,0-1.18,1.85c-.24.19-.48.4-.71.62V9.49a3.14,3.14,0,0,0-3.12-3.13H9.49A3.14,3.14,0,0,0,6.36,9.49v93.06a3.16,3.16,0,0,0,.92,2.21,3.11,3.11,0,0,0,2.21.92H85.71a3.12,3.12,0,0,0,3.12-3.13V88.2l1.91-.81a10,10,0,0,0,4.34-3.13l.12-.14v18.43A9.54,9.54,0,0,1,85.71,112H9.49A9.51,9.51,0,0,1,0,102.55V9.49A9.53,9.53,0,0,1,9.49,0ZM21.07,87.6a3.19,3.19,0,0,1,0-6.37H56.19a37.1,37.1,0,0,0-.3,6.37Zm0-18.93a3.19,3.19,0,0,1,0-6.37H59.22l0,.27-1.05,6.1Zm0-18.93a3.18,3.18,0,0,1,0-6.36H72.44l-5.11,6.36ZM87.25,78,74.43,83.47c-9.35,3.47-8.93,5.43-8-3.85L69.24,63.4h0l0,0,26.56-33,18,14.6L87.27,78ZM72.31,65.89l11.86,9.59-8.42,3.6c-6.6,2.83-6.42,4.23-5.27-2.53l1.83-10.66Z"/></svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<style>
|
||||
.c {
|
||||
stroke: #d6d6d6;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.c, .d {
|
||||
fill: none;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="a" data-name="frame">
|
||||
<rect class="d" width="64" height="64"/>
|
||||
</g>
|
||||
<g id="b" data-name="Promt">
|
||||
<rect class="c" x="13.34" y="12.24" width="38.49" height="39.43" rx="2.69" ry="2.69"/>
|
||||
<line class="c" x1="23.18" y1="42.71" x2="41.1" y2="42.71"/>
|
||||
<line class="c" x1="23.18" y1="35.54" x2="41.1" y2="35.54"/>
|
||||
<line class="c" x1="23.18" y1="28.37" x2="41.1" y2="28.37"/>
|
||||
<line class="c" x1="23.18" y1="21.2" x2="41.1" y2="21.2"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 798 B |
548
index.html
|
|
@ -495,7 +495,7 @@
|
|||
font-family: Arial, Verdana;
|
||||
background-image: url(./icon/search.svg);
|
||||
background-color: #777;
|
||||
background-size: 20px;
|
||||
background-size: 30px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-repeat: no-repeat;
|
||||
|
|
@ -571,7 +571,7 @@
|
|||
|
||||
.resetButton {
|
||||
font-family: Arial, Verdana;
|
||||
background-image: url(./icon/reset_settings2.png);
|
||||
background-image: url(./icon/reset_settings.svg);
|
||||
background-color: #777;
|
||||
background-size: 30px;
|
||||
width: 30px;
|
||||
|
|
@ -588,16 +588,26 @@
|
|||
height: 30px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.svgButton {
|
||||
font-family: Arial, Verdana;
|
||||
|
||||
background-color: #777;
|
||||
background-size: 30px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.selectionAreaButton {
|
||||
background-image: url(./icon/reselect-area.svg);
|
||||
}
|
||||
.interrogateButton {
|
||||
font-family: Arial, Verdana;
|
||||
background-image: url(./icon/writing-icon.svg);
|
||||
/* background-color: transparent; */
|
||||
background-size: 25px;
|
||||
background-size: 30px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-position-x: 4px;
|
||||
}
|
||||
.svg-buttons-container *:not(:last-child) {
|
||||
margin-right: 3px;
|
||||
|
|
@ -655,7 +665,7 @@
|
|||
|
||||
.refreshButton {
|
||||
font-family: Arial, Verdana;
|
||||
background-image: url(./icon/reset_settings2.png);
|
||||
background-image: url(./icon/reset_settings.svg);
|
||||
background-color: #777;
|
||||
background-size: 30px;
|
||||
width: 30px;
|
||||
|
|
@ -699,6 +709,19 @@
|
|||
color: white;
|
||||
} */
|
||||
</style>
|
||||
<style>
|
||||
.second_panel {
|
||||
flex: 1 1 auto;
|
||||
/* overflow: scroll; */
|
||||
overflow-y: scroll;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
flex-direction: column;
|
||||
height: 100%; /* Set a fixed height for the container */
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<!-- <sp-textarea id="tool_tip" open placement="top">use this when you want to fill empty areas of the canvas</sp-textarea> -->
|
||||
|
|
@ -862,15 +885,24 @@
|
|||
</div>
|
||||
<div></div>
|
||||
<div>
|
||||
<button class="btnSquare" id="btnSetMaskViewer">
|
||||
<button
|
||||
class="btnSquare"
|
||||
id="btnSetMaskViewer"
|
||||
style="display: none"
|
||||
>
|
||||
Set Mask
|
||||
</button>
|
||||
<button class="btnSquare" id="btnSetInitImageViewer">
|
||||
<button
|
||||
class="btnSquare"
|
||||
id="btnSetInitImageViewer"
|
||||
style="display: none"
|
||||
>
|
||||
Set Init Image
|
||||
</button>
|
||||
<button
|
||||
class="btnSquare btnGenerateClass"
|
||||
id="btnGenerate"
|
||||
style="display: none"
|
||||
>
|
||||
Generate txt2img
|
||||
</button>
|
||||
|
|
@ -881,9 +913,6 @@
|
|||
>
|
||||
Interrupt
|
||||
</button>
|
||||
<button class="btnSquare" id="btnSelectionArea">
|
||||
Selection Area
|
||||
</button>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
|
|
@ -891,10 +920,6 @@
|
|||
padding-top: 3px;
|
||||
"
|
||||
>
|
||||
<!-- <button class="btnSquare acceptClass acceptAllImgBtn" style="display:none;">Accept All</button>
|
||||
<button class="btnSquare discardClass discardAllImgBtn" style="display:none;">Discard All</button>
|
||||
<button class="btnSquare acceptSelectedClass acceptSelectedImgBtn" style="display:none;">Accept Selected</button>
|
||||
<button class="btnSquare discardSelectedClass discardSelectedImgBtn" style="display:none;">Discard Selected</button> -->
|
||||
<button
|
||||
title="Keep all generated images on the canvas"
|
||||
class="btnSquare acceptClass acceptAllImgBtn"
|
||||
|
|
@ -990,308 +1015,7 @@
|
|||
</div>
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
<div class="sp-tab-page" id="sp-control_net-tab-page">
|
||||
<!-- control net tab page -->
|
||||
<sp-picker
|
||||
title="auto fill the ControlNet with smart settings, to speed up your working process."
|
||||
size="m"
|
||||
label="ControlNet Preset"
|
||||
>
|
||||
<sp-menu id="mControlNetPresetMenu" slot="options">
|
||||
<!-- <sp-menu-item> item </sp-menu-item> -->
|
||||
</sp-menu>
|
||||
</sp-picker>
|
||||
<div></div>
|
||||
<sp-label
|
||||
id="controlnetMissingError"
|
||||
style="color: #ff595e; display: none; white-space: normal"
|
||||
>
|
||||
The Controlnet Extension is missing from Automatic1111.
|
||||
Please install it to use it through the plugin.
|
||||
</sp-label>
|
||||
<div
|
||||
id="control_net_image_container"
|
||||
class="imgContainer controlNetImaageContainer"
|
||||
>
|
||||
<!-- <button class="column-item button-style" id="btnSnapAndFill">Snap And Fill</button> -->
|
||||
<!--
|
||||
<div>
|
||||
<img
|
||||
id="all_control_net_image"
|
||||
class="column-item-image"
|
||||
src="https://source.unsplash.com/random"
|
||||
width="300px"
|
||||
height="100px"
|
||||
/>
|
||||
</div> -->
|
||||
<div class="imgButton">
|
||||
<button
|
||||
class="column-item button-style btnSquare"
|
||||
id="bSetAllControlImage"
|
||||
>
|
||||
Set All CtrlNet Images
|
||||
</button>
|
||||
</div>
|
||||
<sp-checkbox id="chDisableControlNetTab"
|
||||
>Disable ControlNet Tab</sp-checkbox
|
||||
>
|
||||
<!-- <span>
|
||||
version:<span id="ControlNetVersion">0.0.0</span>
|
||||
</span> -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="controlnet_settings_template"
|
||||
data-index="0"
|
||||
style="display: none"
|
||||
>
|
||||
<div class="flexContainer">
|
||||
<sp-label slot="label " class="controlnet_unit_label_"
|
||||
>Control Net Settings Slot 0</sp-label
|
||||
>
|
||||
</div>
|
||||
<div style="display: flex">
|
||||
<div
|
||||
id="control_net_image_container_0"
|
||||
class="imgContainer controlNetImaageContainer"
|
||||
>
|
||||
<!-- <button class="column-item button-style" id="btnSnapAndFill">Snap And Fill</button> -->
|
||||
|
||||
<div>
|
||||
<img
|
||||
id="control_net_image_0"
|
||||
class="column-item-image control_net_image_"
|
||||
src="https://source.unsplash.com/random"
|
||||
width="300px"
|
||||
height="100px"
|
||||
/>
|
||||
</div>
|
||||
<div class="imgButton">
|
||||
<button
|
||||
class="column-item button-style btnSquare bSetControlImage_"
|
||||
id="bSetControlImage_0"
|
||||
>
|
||||
Set CtrlNet Img
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="control_net_mask_container_0"
|
||||
class="imgContainer controlNetImaageContainer"
|
||||
>
|
||||
<!-- <button class="column-item button-style" id="btnSnapAndFill">Snap And Fill</button> -->
|
||||
|
||||
<div>
|
||||
<img
|
||||
id="control_net_mask_0"
|
||||
class="column-item-image control_net_mask_"
|
||||
src="https://source.unsplash.com/random"
|
||||
width="300px"
|
||||
height="100px"
|
||||
/>
|
||||
</div>
|
||||
<div class="imgButton btnClass">
|
||||
<button
|
||||
class="column-item button-style btnSquare bControlMask_"
|
||||
id="bControlMask_0"
|
||||
>
|
||||
Preview Annotator
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<sp-checkbox
|
||||
id="chEnableControlNet_0"
|
||||
class="chEnableControlNet_"
|
||||
>Enable</sp-checkbox
|
||||
>
|
||||
<sp-checkbox checked id="chlowVram_0" class="chlowVram_"
|
||||
>Low VRAM</sp-checkbox
|
||||
>
|
||||
<!-- <sp-checkbox id="chGuessMode_0" class="chGuessMode_"
|
||||
>Guess Mode</sp-checkbox
|
||||
> -->
|
||||
<sp-checkbox class="chPixelPerfect_"
|
||||
>Pixel Perfect</sp-checkbox
|
||||
>
|
||||
<div>
|
||||
<sp-radio-group class="rgControlNetMode_">
|
||||
<sp-label slot="label">Control Mode:</sp-label>
|
||||
<!-- <sp-label slot="label">Select a Mode:</sp-label> -->
|
||||
<sp-radio title="" value="0" checked
|
||||
>Balanced</sp-radio
|
||||
>
|
||||
<sp-radio
|
||||
title="My Prompt is More Important"
|
||||
value="1"
|
||||
>Prompt</sp-radio
|
||||
><sp-radio
|
||||
title="ControlNet is More Important"
|
||||
value="2"
|
||||
>ControlNet</sp-radio
|
||||
>
|
||||
</sp-radio-group>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<sp-slider
|
||||
show-value="false"
|
||||
id="slControlNetWeight_0"
|
||||
class="slControlNetWeight_"
|
||||
min="0"
|
||||
max="100"
|
||||
value="50"
|
||||
title="2 will keep the composition; 0 will allow composition to change"
|
||||
>
|
||||
<sp-label slot="label">Weight:</sp-label>
|
||||
<sp-label
|
||||
slot="label"
|
||||
id="lControlNetWeight_0"
|
||||
class="lControlNetWeight_"
|
||||
>1.0</sp-label
|
||||
>
|
||||
</sp-slider>
|
||||
<sp-slider
|
||||
show-value="false"
|
||||
id="slControlNetGuidanceStrengthStart_0"
|
||||
class="slControlNetGuidanceStrengthStart_"
|
||||
min="0"
|
||||
max="100"
|
||||
value="0"
|
||||
>
|
||||
<sp-label slot="label"
|
||||
>Guidance strength start:</sp-label
|
||||
>
|
||||
<sp-label
|
||||
slot="label"
|
||||
id="lControlNetGuidanceStrengthStart_0"
|
||||
class="lControlNetGuidanceStrengthStart_"
|
||||
>0.0</sp-label
|
||||
>
|
||||
</sp-slider>
|
||||
<sp-slider
|
||||
show-value="false"
|
||||
id="slControlNetGuidanceStrengthEnd_0"
|
||||
class="slControlNetGuidanceStrengthEnd_"
|
||||
min="0"
|
||||
max="100"
|
||||
value="100"
|
||||
>
|
||||
<sp-label slot="label"
|
||||
>Guidance strength end:</sp-label
|
||||
>
|
||||
<sp-label
|
||||
slot="label"
|
||||
id="lControlNetGuidanceStrengthEnd_0"
|
||||
class="lControlNetGuidanceStrengthEnd_"
|
||||
>1.0</sp-label
|
||||
>
|
||||
</sp-slider>
|
||||
<sp-slider
|
||||
show-value="true"
|
||||
id="slControlNetProcessorRes_0"
|
||||
class="slControlNetProcessorRes_"
|
||||
style="display: none"
|
||||
min="64"
|
||||
max="2048"
|
||||
value="512"
|
||||
>
|
||||
<sp-label
|
||||
slot="label"
|
||||
class="labelControlNetProcessorRes_"
|
||||
>Annotator resolution</sp-label
|
||||
>
|
||||
<!-- <sp-label
|
||||
slot="label"
|
||||
id="lControlNetProcessorRes_0"
|
||||
class="lControlNetProcessorRes_"
|
||||
>1.0</sp-label
|
||||
> -->
|
||||
</sp-slider>
|
||||
<sp-slider
|
||||
show-value="false"
|
||||
class="slControlNetThreshold_A_"
|
||||
style="display: none"
|
||||
min="0"
|
||||
max="100"
|
||||
value="50"
|
||||
data-sd_min="64"
|
||||
data-sd_max="1024"
|
||||
>
|
||||
<sp-label
|
||||
slot="label"
|
||||
class="labelControlNetThreshold_A_"
|
||||
>Threshold A:
|
||||
</sp-label>
|
||||
<sp-label
|
||||
slot="label"
|
||||
class="lControlNetThreshold_A_"
|
||||
>0.0</sp-label
|
||||
>
|
||||
</sp-slider>
|
||||
<sp-slider
|
||||
show-value="false"
|
||||
class="slControlNetThreshold_B_"
|
||||
style="display: none"
|
||||
min="0"
|
||||
max="100"
|
||||
value="50"
|
||||
data-sd_min="64"
|
||||
data-sd_max="1024"
|
||||
>
|
||||
<sp-label
|
||||
slot="label"
|
||||
class="labelControlNetThreshold_B_"
|
||||
>Threshold B:
|
||||
</sp-label>
|
||||
<sp-label
|
||||
slot="label"
|
||||
class="lControlNetThreshold_B_"
|
||||
>0.0</sp-label
|
||||
>
|
||||
</sp-slider>
|
||||
</div>
|
||||
</div>
|
||||
<div id="menu-bar-control_net_0" style="display: flex">
|
||||
<sp-picker size="m" label="Select Module">
|
||||
<sp-menu
|
||||
id="mModulesMenuControlNet_0"
|
||||
class="mModulesMenuControlNet_"
|
||||
slot="options"
|
||||
>
|
||||
<!-- <sp-menu-item> item </sp-menu-item> -->
|
||||
</sp-menu>
|
||||
</sp-picker>
|
||||
<sp-picker size="m" label="Selection type">
|
||||
<sp-menu
|
||||
id="mModelsMenuControlNet_0"
|
||||
class="mModelsMenuControlNet_"
|
||||
slot="options"
|
||||
>
|
||||
<!-- <sp-menu-item> item </sp-menu-item> -->
|
||||
</sp-menu>
|
||||
</sp-picker>
|
||||
|
||||
<!-- <button class="btnSquare" id="btnRefreshModelsHorde">
|
||||
Refresh Models
|
||||
</button> -->
|
||||
</div>
|
||||
<div></div>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<div></div>
|
||||
|
||||
<div>
|
||||
<sp-divider
|
||||
class="line-divider"
|
||||
size="large"
|
||||
></sp-divider>
|
||||
</div>
|
||||
</div>
|
||||
<div id="controlnet_parent_container"></div>
|
||||
</div>
|
||||
<div class="sp-tab-page" id="sp-control_net-tab-page"></div>
|
||||
<div class="sp-tab-page" id="sp-history-tab-page">
|
||||
<div class="subTabOptionsContainer"></div>
|
||||
|
||||
|
|
@ -1547,7 +1271,8 @@
|
|||
>
|
||||
</sp-slider>
|
||||
</div>
|
||||
<div
|
||||
<div id="extraGenerateButtonsContainer"></div>
|
||||
<!-- <div
|
||||
style="
|
||||
display: inline-flex;
|
||||
justify-content: flex-start;
|
||||
|
|
@ -1557,6 +1282,7 @@
|
|||
<button
|
||||
class="btnSquare btnGenerateClass"
|
||||
id="btnGenerateUpscale"
|
||||
style="display: none"
|
||||
>
|
||||
Generate upscale
|
||||
</button>
|
||||
|
|
@ -1578,7 +1304,6 @@
|
|||
></button>
|
||||
<button
|
||||
class="btnSquare snapshotButton"
|
||||
id="btnSnapshotUpscale"
|
||||
style="margin-right: 3px"
|
||||
title="create a snapshot of what you see on the canvas and place on a new layer"
|
||||
></button>
|
||||
|
|
@ -1588,7 +1313,7 @@
|
|||
title="reset the ui settings to their default values"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div
|
||||
id="progressContainerUpscale"
|
||||
|
|
@ -1600,7 +1325,9 @@
|
|||
>
|
||||
</div>
|
||||
<div style="padding-bottom: 5px">
|
||||
<sp-label style="width: 60px; display: inline-block"
|
||||
<sp-label
|
||||
class="title"
|
||||
style="width: 60px; display: inline-block"
|
||||
>Upscaler 1:
|
||||
</sp-label>
|
||||
<sp-picker size="m" label="Upscaler Options">
|
||||
|
|
@ -1613,7 +1340,9 @@
|
|||
</sp-menu>
|
||||
</sp-picker>
|
||||
<div></div>
|
||||
<sp-label style="width: 60px; display: inline-block"
|
||||
<sp-label
|
||||
class="title"
|
||||
style="width: 60px; display: inline-block"
|
||||
>Upscaler 2:
|
||||
</sp-label>
|
||||
<sp-picker size="m" label="Upscaler Options">
|
||||
|
|
@ -1637,6 +1366,7 @@
|
|||
value="0"
|
||||
>
|
||||
<sp-label
|
||||
class="title"
|
||||
style="width: 110px; display: inline-block"
|
||||
slot="label"
|
||||
>Upscaler 2 visibility:
|
||||
|
|
@ -1661,6 +1391,7 @@
|
|||
value="0"
|
||||
>
|
||||
<sp-label
|
||||
class="title"
|
||||
style="width: 110px; display: inline-block"
|
||||
slot="label"
|
||||
>GFPGAN visibility:
|
||||
|
|
@ -1685,6 +1416,7 @@
|
|||
value="0"
|
||||
>
|
||||
<sp-label
|
||||
class="title"
|
||||
style="width: 110px; display: inline-block"
|
||||
slot="label"
|
||||
>CodeFormer visibility:
|
||||
|
|
@ -1709,6 +1441,7 @@
|
|||
value="0"
|
||||
>
|
||||
<sp-label
|
||||
class="title"
|
||||
style="width: 110px; display: inline-block"
|
||||
slot="label"
|
||||
>CodeFormer weight:
|
||||
|
|
@ -1853,6 +1586,7 @@
|
|||
</button>
|
||||
</div>
|
||||
<div id="settingsVAEContainer"></div>
|
||||
<div id="reactSettingsContainer"></div>
|
||||
<sp-checkbox id="chUseSharpMask">use sharp mask</sp-checkbox>
|
||||
<sp-checkbox checked id="chUseSmartObject"
|
||||
>Smart Object</sp-checkbox
|
||||
|
|
@ -1976,12 +1710,6 @@
|
|||
<!-- stable diffusion tab page -->
|
||||
|
||||
<div id="sdBtnContainer">
|
||||
<!-- <button
|
||||
class="btnSquare layerToSelection"
|
||||
id="btnLayerToSelection"
|
||||
style="margin-right: 3px"
|
||||
title="Move and reSize the highlighted layer to fit into the Selection Area "
|
||||
></button> -->
|
||||
<sp-picker
|
||||
title="use lora in your prompt"
|
||||
size="m"
|
||||
|
|
@ -2003,7 +1731,8 @@
|
|||
style="
|
||||
margin-top: 1px;
|
||||
margin-bottom: 3px;
|
||||
display: inline-block;
|
||||
/* display: inline-block; */
|
||||
display: none;
|
||||
"
|
||||
>
|
||||
Generate txt2img
|
||||
|
|
@ -2076,7 +1805,7 @@
|
|||
class="chLiveProgressImageClass"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
<!-- <div
|
||||
class="svg-buttons-container"
|
||||
style="
|
||||
display: inline-flex;
|
||||
|
|
@ -2096,13 +1825,11 @@
|
|||
></button>
|
||||
|
||||
<button
|
||||
class="btnSquare layerToSelection"
|
||||
id="btnLayerToSelection"
|
||||
class="btnSquare layerToSelection btnLayerToSelection"
|
||||
title="Move and reSize the highlighted layer to fit into the Selection Area "
|
||||
></button>
|
||||
<button
|
||||
class="btnSquare snapshotButton"
|
||||
id="btnSnapshot"
|
||||
title="create a snapshot of what you see on the canvas and place on a new layer"
|
||||
></button>
|
||||
<button
|
||||
|
|
@ -2115,7 +1842,7 @@
|
|||
id="btnInterrogate"
|
||||
title="Interrogate the selected area, convert Image to Prompt"
|
||||
></button>
|
||||
</div>
|
||||
</div> -->
|
||||
<!-- <sp-picker
|
||||
title="use lora in your prompt"
|
||||
size="s"
|
||||
|
|
@ -2133,6 +1860,7 @@
|
|||
>prompt shortcut</sp-checkbox
|
||||
> -->
|
||||
</div>
|
||||
<div id="generateButtonsContainer"></div>
|
||||
<div>
|
||||
<!-- icon only svg button -->
|
||||
<sp-action-button
|
||||
|
|
@ -2403,10 +2131,7 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
</div>
|
||||
</sp-action-button>
|
||||
</div>
|
||||
<img
|
||||
id="webp_container"
|
||||
src="https://www.keycdn.com/img/blog/convert-to-webp-the-successor-of-jpeg-lg.webp"
|
||||
/>
|
||||
|
||||
<!-- <custom-element>hello custom element</custom-element> -->
|
||||
<div class="prompts">
|
||||
<!-- <sp-label slot="label">Prompt</sp-label> -->
|
||||
|
|
@ -2462,6 +2187,7 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
>
|
||||
<!-- <sp-tooltip id="tool_tip" open placement="top">use this when you want to fill empty areas of the canvas</sp-tooltip> -->
|
||||
</sp-radio-group>
|
||||
<div id="reactModesContainer"></div>
|
||||
|
||||
<div id="image_viewer">
|
||||
<div class="imgButton">
|
||||
|
|
@ -2566,7 +2292,7 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
></sp-textfield>
|
||||
</div>
|
||||
<div
|
||||
id="batchNumberSdUiTabContainer"
|
||||
id="batchCountSdUiTabContainer"
|
||||
style="
|
||||
width: 20%;
|
||||
display: flex;
|
||||
|
|
@ -2592,7 +2318,8 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
align-items: flex-start;
|
||||
"
|
||||
>
|
||||
<sp-label>Steps:</sp-label
|
||||
<sp-label id="sdLabelSampleStep"
|
||||
>Sampling Steps</sp-label
|
||||
><sp-textfield
|
||||
style="width: 100%"
|
||||
title="the higher the steps the longer it will take to generate an image"
|
||||
|
|
@ -2662,7 +2389,9 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
value="8"
|
||||
data-old_value="512"
|
||||
>
|
||||
<sp-label slot="label">Width:</sp-label>
|
||||
<sp-label slot="label" class="title"
|
||||
>Width:</sp-label
|
||||
>
|
||||
<sp-label
|
||||
class="labelNumber"
|
||||
slot="label"
|
||||
|
|
@ -2684,7 +2413,9 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
value="8"
|
||||
data-old_value="512"
|
||||
>
|
||||
<sp-label slot="label">Height:</sp-label>
|
||||
<sp-label slot="label" class="title"
|
||||
>Height:</sp-label
|
||||
>
|
||||
<sp-label
|
||||
class="labelNumber"
|
||||
slot="label"
|
||||
|
|
@ -2724,7 +2455,9 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
max="30"
|
||||
value="7"
|
||||
>
|
||||
<sp-label slot="label">CFG Scale:</sp-label>
|
||||
<sp-label slot="label" class="title"
|
||||
>CFG Scale:</sp-label
|
||||
>
|
||||
<!-- <sp-label slot="label" id="lCfgScale">7</sp-label> -->
|
||||
</sp-slider>
|
||||
|
||||
|
|
@ -2735,7 +2468,7 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
max="100"
|
||||
value="70"
|
||||
>
|
||||
<sp-label slot="label"
|
||||
<sp-label slot="label" class="title"
|
||||
>Denoising Strength:</sp-label
|
||||
>
|
||||
<sp-label slot="label" id="lDenoisingStrength"
|
||||
|
|
@ -2752,7 +2485,9 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
value="15"
|
||||
style="display: none"
|
||||
>
|
||||
<sp-label slot="label">Image CFG Scale:</sp-label>
|
||||
<sp-label slot="label" class="title"
|
||||
>Image CFG Scale:</sp-label
|
||||
>
|
||||
<sp-label slot="label" id="lImageCfgScale"
|
||||
>1.5</sp-label
|
||||
>
|
||||
|
|
@ -2763,7 +2498,7 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
id="slMaskBlur"
|
||||
min="0"
|
||||
max="64"
|
||||
value="7"
|
||||
value="0"
|
||||
>
|
||||
<sp-label slot="label">Mask Blur:</sp-label>
|
||||
<!-- <sp-label slot="label" id="lDenoisingStrength">0.7</sp-label> -->
|
||||
|
|
@ -2773,7 +2508,7 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
id="slMaskExpansion"
|
||||
min="0"
|
||||
max="256"
|
||||
value="40"
|
||||
value="0"
|
||||
title="the larger the value the more the mask will expand, '0' means use precise masking, use in combination with the mask blur"
|
||||
>
|
||||
<sp-label slot="label">Mask Expansion:</sp-label>
|
||||
|
|
@ -2789,7 +2524,7 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
value="100"
|
||||
title="0 will keep the composition; 1 will allow composition to change"
|
||||
>
|
||||
<sp-label slot="label"
|
||||
<sp-label slot="label" class="title"
|
||||
>Inpainting conditioning mask
|
||||
strength:</sp-label
|
||||
>
|
||||
|
|
@ -2803,7 +2538,9 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
|
||||
<div id="slInpainting_fill">
|
||||
<sp-radio-group id="Inpainting_fill_group" class="">
|
||||
<sp-label slot="label">Mask Content:</sp-label>
|
||||
<sp-label class="title" slot="label"
|
||||
>Mask Content:</sp-label
|
||||
>
|
||||
<sp-radio
|
||||
class="rbMaskContent"
|
||||
checked
|
||||
|
|
@ -2836,7 +2573,9 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
<div id="HiResDiv" style="display: none">
|
||||
<div style="display: flex">
|
||||
<div>
|
||||
<sp-label>Upscaler: </sp-label>
|
||||
<sp-label class="title"
|
||||
>Upscaler:
|
||||
</sp-label>
|
||||
<sp-picker
|
||||
size="m"
|
||||
label="Upscaler Options"
|
||||
|
|
@ -2851,7 +2590,8 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
</sp-picker>
|
||||
</div>
|
||||
<div>
|
||||
<sp-label>Hi Res Steps:</sp-label
|
||||
<sp-label id="HiResStep"
|
||||
>Hi Res Steps:</sp-label
|
||||
><sp-textfield
|
||||
id="hrNumberOfSteps"
|
||||
type="number"
|
||||
|
|
@ -2872,7 +2612,7 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
max="100"
|
||||
value="50"
|
||||
>
|
||||
<sp-label slot="label"
|
||||
<sp-label slot="label" class="title"
|
||||
>Hi Res Scale:</sp-label
|
||||
>
|
||||
<sp-label
|
||||
|
|
@ -2896,7 +2636,7 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
max="100"
|
||||
value="70"
|
||||
>
|
||||
<sp-label slot="label">
|
||||
<sp-label slot="label" class="title">
|
||||
High Res Denoising Strength:</sp-label
|
||||
>
|
||||
<sp-label
|
||||
|
|
@ -2915,7 +2655,7 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
value="8"
|
||||
style="display: none"
|
||||
>
|
||||
<sp-label slot="label"
|
||||
<sp-label slot="label" class="title"
|
||||
>Hi Res Output Width:</sp-label
|
||||
>
|
||||
<sp-label
|
||||
|
|
@ -2970,7 +2710,7 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
<div>
|
||||
<!-- <sp-checkbox id="chHiResFixs">Hi Res Fix</sp-checkbox> -->
|
||||
<div style="display: flex">
|
||||
<sp-label>Seed:</sp-label
|
||||
<sp-label id="sdLabelSeed">Seed:</sp-label
|
||||
><sp-textfield
|
||||
id="tiSeed"
|
||||
placeholder="Seed"
|
||||
|
|
@ -3057,8 +2797,106 @@ l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
|
|||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<div style="margin-bottom: 3px"></div>
|
||||
<div id="scriptsContainer"></div>
|
||||
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
|
||||
<div class="reactViewerContainer"></div>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<div class="previewContainer"></div>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<div class="samContainer"></div>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<div class="oneButtonPromptContainer"></div>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<uxp-panel panelid="second_panel">
|
||||
<div id="search_second_panel" class="container second_panel">
|
||||
<div class="previewContainer"></div>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<div class="reactViewerContainer"></div>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<div id="sp-control_net-tab-page2"></div>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
<sp-divider class="line-divider" size="large"></sp-divider>
|
||||
</div>
|
||||
</uxp-panel>
|
||||
|
||||
<uxp-panel panelid="toolbar">
|
||||
<div
|
||||
id="toolbar_panel"
|
||||
class="_container"
|
||||
style="
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
/* padding-bottom: 5px; */
|
||||
flex-direction: column;
|
||||
"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
/* padding: 1px; */
|
||||
flex-direction: column;
|
||||
"
|
||||
>
|
||||
<div id="toolbarGenerateButtonsContainer"></div>
|
||||
<div id="viewerButtonContainer"></div>
|
||||
<div>
|
||||
<button
|
||||
class="btnSquare layerToSelection btnLayerToSelection"
|
||||
title="Move and reSize the highlighted layer to fit into the Selection Area "
|
||||
style="margin-right: 3px"
|
||||
></button>
|
||||
<button
|
||||
class="btnSquare snapshotButton"
|
||||
title="create a snapshot of what you see on the canvas and place on a new layer"
|
||||
style="margin-right: 3px"
|
||||
></button>
|
||||
<button
|
||||
class="btnSquare resetButton"
|
||||
id="btnResetSettings"
|
||||
title="reset the ui settings to their default values"
|
||||
style="margin-right: 3px"
|
||||
></button>
|
||||
<button
|
||||
class="btnSquare interrogateButton"
|
||||
id="btnInterrogate"
|
||||
title="Interrogate the selected area, convert Image to Prompt"
|
||||
style="margin-right: 3px"
|
||||
></button>
|
||||
<button
|
||||
class="btnSquare svgButton selectionAreaButton"
|
||||
id="btnSelectionArea"
|
||||
style="margin-right: 3px"
|
||||
title="Reselect the selection area for the current session"
|
||||
></button>
|
||||
<button
|
||||
id="scrollToPrompt"
|
||||
class="btnSquare svgButton"
|
||||
title="Quickly jump to the preview section"
|
||||
>
|
||||
P
|
||||
</button>
|
||||
<div id="scrollToControlNetUnitContainer"></div>
|
||||
<!-- <button id="scrollToViewer" class="btnSquare svgButton">
|
||||
V
|
||||
</button> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</uxp-panel>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
export * as ultimate_sd_upscaler from '../../ultimate_sd_upscaler/src/ultimate_sd_upscaler'
|
||||
export * as after_detailer_script from '../../after_detailer/src/after_detailer'
|
||||
export * as scripts from '../../ultimate_sd_upscaler/src/scripts'
|
||||
export * as main from './main'
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
|
||||
declare module 'sdapi_py_re' {
|
||||
const exports: any;
|
||||
export = exports;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
declare module 'uxp' {
|
||||
// Add type declarations for the uxp module here
|
||||
export const storage: any;
|
||||
export const versions: any;
|
||||
}
|
||||
110
manifest.json
|
|
@ -36,11 +36,12 @@
|
|||
"entrypoints": [
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "vanilla",
|
||||
"id": "main_panel",
|
||||
"label": {
|
||||
"default": "Auto-Photoshop-SD",
|
||||
"en-US": "Auto-Photoshop-SD",
|
||||
"es-ES": "Auto-Photoshop-SD"
|
||||
"es-ES": "Auto-Photoshop-SD",
|
||||
"zh-CN": "SD插件(明空汉化)"
|
||||
},
|
||||
"minimumSize": {
|
||||
"width": 400,
|
||||
|
|
@ -85,6 +86,111 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "second_panel",
|
||||
"label": {
|
||||
"default": "Second Auto-Photoshop-SD",
|
||||
"en-US": "Second Auto-Photoshop-SD",
|
||||
"es-ES": "Second Auto-Photoshop-SD",
|
||||
"zh-CN": "SD插件(明空汉化)"
|
||||
},
|
||||
"minimumSize": {
|
||||
"width": 100,
|
||||
"height": 100
|
||||
},
|
||||
"maximumSize": {
|
||||
"width": 1200,
|
||||
"height": 10000
|
||||
},
|
||||
"preferredDockedSize": {
|
||||
"width": 150,
|
||||
"height": 800
|
||||
},
|
||||
"preferredFloatingSize": {
|
||||
"width": 300,
|
||||
"height": 800
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"id": "show_alert",
|
||||
"label": {
|
||||
"default": "Show Alert",
|
||||
"en-US": "Show Alert (US)",
|
||||
"es-ES": "Show Alert (ES)"
|
||||
}
|
||||
}
|
||||
],
|
||||
"icons": [
|
||||
{
|
||||
"width": 23,
|
||||
"height": 23,
|
||||
"path": "icon/panel.png",
|
||||
"scale": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"theme": [
|
||||
"all"
|
||||
],
|
||||
"species": [
|
||||
"chrome"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "toolbar",
|
||||
"label": {
|
||||
"default": "toolbar Auto-Photoshop-SD",
|
||||
"en-US": "toolbar Auto-Photoshop-SD",
|
||||
"es-ES": "toolbar Auto-Photoshop-SD"
|
||||
},
|
||||
"minimumSize": {
|
||||
"width": 30,
|
||||
"height": 100
|
||||
},
|
||||
"maximumSize": {
|
||||
"width": 1200,
|
||||
"height": 10000
|
||||
},
|
||||
"preferredDockedSize": {
|
||||
"width": 30,
|
||||
"height": 800
|
||||
},
|
||||
"preferredFloatingSize": {
|
||||
"width": 30,
|
||||
"height": 800
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"id": "show_alert",
|
||||
"label": {
|
||||
"default": "Show Alert",
|
||||
"en-US": "Show Alert (US)",
|
||||
"es-ES": "Show Alert (ES)"
|
||||
}
|
||||
}
|
||||
],
|
||||
"icons": [
|
||||
{
|
||||
"width": 23,
|
||||
"height": 23,
|
||||
"path": "icon/panel.png",
|
||||
"scale": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"theme": [
|
||||
"all"
|
||||
],
|
||||
"species": [
|
||||
"chrome"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"icons": [
|
||||
|
|
|
|||
12
package.json
|
|
@ -5,8 +5,10 @@
|
|||
"author": "Adobe Inc",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/photoshop": "^24.5.1",
|
||||
"@types/react": "^18.2.6",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
"changedpi": "^1.0.4",
|
||||
"fastify": "^4.10.2",
|
||||
"jimp": "^0.16.2",
|
||||
"madge": "^6.0.0",
|
||||
|
|
@ -14,7 +16,8 @@
|
|||
"mobx": "^6.9.0",
|
||||
"mobx-react": "^7.6.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react-dom": "^18.2.0",
|
||||
"util": "^0.12.5"
|
||||
},
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
|
|
@ -25,6 +28,7 @@
|
|||
"@babel/plugin-proposal-object-rest-spread": "^7.20.7",
|
||||
"@babel/plugin-syntax-class-properties": "^7.12.13",
|
||||
"@babel/plugin-transform-react-jsx": "^7.21.5",
|
||||
"@svgr/webpack": "^8.0.1",
|
||||
"babel-loader": "^9.1.2",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
|
|
@ -36,13 +40,15 @@
|
|||
"terser-webpack-plugin": "^3.0.8",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^5.0.4",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.82.1",
|
||||
"webpack-cli": "^5.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"watch": "npx webpack --config webpack.config.js --watch",
|
||||
"build": "npx webpack --config webpack.config.js"
|
||||
"build": "npx webpack --config webpack.config.js",
|
||||
"format": "npx prettier -w typescripts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -53,4 +59,4 @@
|
|||
"url": "https://github.com/AbdullahAlfaraj/Auto-Photoshop-StableDiffusion-Plugin/issues"
|
||||
},
|
||||
"homepage": "https://github.com/AbdullahAlfaraj/Auto-Photoshop-StableDiffusion-Plugin#readme"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
psapi.js
|
|
@ -1,6 +1,8 @@
|
|||
const app = window.require('photoshop').app
|
||||
const batchPlay = require('photoshop').action.batchPlay
|
||||
const { executeAsModal } = require('photoshop').core
|
||||
const constants = require('photoshop').constants
|
||||
// console.log(constants.ResampleMethod.BILINEAR)
|
||||
// const export_png = require('./export_png')
|
||||
|
||||
// const { layerToSelection } = require('./helper')
|
||||
|
|
@ -643,7 +645,7 @@ async function snapshot_layerExe() {
|
|||
await snapshot_layer_new()
|
||||
},
|
||||
{
|
||||
commandName: 'Action Commands',
|
||||
commandName: `Canvas Snapshot...`,
|
||||
}
|
||||
)
|
||||
} catch (e) {
|
||||
|
|
@ -1400,7 +1402,12 @@ async function layerToSelection(selection_info) {
|
|||
scale_x_ratio = (selection_info.width / layer_info.width) * 100
|
||||
scale_y_ratio = (selection_info.height / layer_info.height) * 100
|
||||
console.log('scale_x_y_ratio:', scale_x_ratio, scale_y_ratio)
|
||||
await layer.scale(scale_x_ratio, scale_y_ratio)
|
||||
// await layer.scale(
|
||||
// scale_x_ratio,
|
||||
// scale_y_ratio,
|
||||
// constants.ResampleMethod.BILINEAR
|
||||
// )
|
||||
await layer_util.Layer.resizeImageExe(scale_x_ratio, scale_y_ratio)
|
||||
}
|
||||
|
||||
async function moveLayerExe(layerToMove, selection_info) {
|
||||
|
|
|
|||
|
|
@ -67,13 +67,14 @@ async def maskExpansionHandler(request:Request):
|
|||
# keywords = json.get('keywords','cute dogs')
|
||||
base64_mask_image = json['mask']
|
||||
mask_expansion = json['mask_expansion']
|
||||
blur = json['blur']
|
||||
#convert base64 to img
|
||||
|
||||
await img2imgapi.base64ToPng(base64_mask_image,"original_mask.png")#save a copy of the mask
|
||||
|
||||
mask_image = img2imgapi.b64_2_img(base64_mask_image)
|
||||
|
||||
expanded_mask_img = img2imgapi.maskExpansion(mask_image,mask_expansion)
|
||||
expanded_mask_img = img2imgapi.maskExpansion(mask_image,mask_expansion,blur)
|
||||
base64_expanded_mask_image = img2imgapi.img_2_b64(expanded_mask_img)
|
||||
await img2imgapi.base64ToPng(base64_expanded_mask_image,"expanded_mask.png")#save a copy of the mask
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,9 @@ const { base64ToBase64Url } = require('./utility/general')
|
|||
const { getExtensionType } = require('./utility/html_manip')
|
||||
const py_re = require('./utility/sdapi/python_replacement')
|
||||
const Enum = require('./enum')
|
||||
// const control_net = require('./utility/tab/control_net')
|
||||
const {
|
||||
mapPluginSettingsToControlNet,
|
||||
getEnableControlNet,
|
||||
getModuleDetail,
|
||||
} = require('./utility/tab/control_net')
|
||||
const { control_net } = require('./typescripts/dist/bundle')
|
||||
const { mapPluginSettingsToControlNet, getEnableControlNet, getModuleDetail } =
|
||||
control_net
|
||||
|
||||
const api = require('./utility/api')
|
||||
//javascript plugin can't read images from local directory so we send a request to local server to read the image file and send it back to plugin as image string base64
|
||||
|
|
@ -21,7 +18,7 @@ async function requestSavePng(base64_image, image_name) {
|
|||
const uniqueDocumentId = await getUniqueDocumentId()
|
||||
const folder = `${uniqueDocumentId}/init_images`
|
||||
const init_entry = await getInitImagesDir()
|
||||
saveFileInSubFolder(base64_image, folder, image_name)
|
||||
io.saveFileInSubFolder(base64_image, folder, image_name)
|
||||
console.warn('this function is deprecated')
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
|
|
@ -42,6 +39,7 @@ async function requestTxt2Img(payload) {
|
|||
}
|
||||
}
|
||||
|
||||
//Refactor: move to modes.ts
|
||||
async function requestImg2Img(payload) {
|
||||
console.log('requestImg2Img(): about to send a fetch request')
|
||||
try {
|
||||
|
|
@ -56,21 +54,22 @@ async function requestImg2Img(payload) {
|
|||
}
|
||||
}
|
||||
|
||||
//Refactor: move to progress.ts
|
||||
async function requestProgress() {
|
||||
let json = {}
|
||||
try {
|
||||
console.log('requestProgress: ')
|
||||
|
||||
const full_url = `${g_sd_url}/sdapi/v1/progress?skip_current_image=false`
|
||||
let request = await fetch(full_url)
|
||||
json = await request.json()
|
||||
console.log('progress json:', json)
|
||||
// console.log('progress json:', json)
|
||||
|
||||
return json
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
// console.log('json: ', json)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
async function requestGetModels() {
|
||||
|
|
@ -96,7 +95,7 @@ async function requestGetSamplers() {
|
|||
const full_url = `${g_sd_url}/sdapi/v1/samplers`
|
||||
let request = await fetch(full_url)
|
||||
json = await request.json()
|
||||
console.log('samplers json:', json)
|
||||
// console.log('samplers json:', json)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
|
|
@ -210,14 +209,12 @@ async function loadPromptShortcut() {
|
|||
prompt_shortcut_json = await py_re.loadPromptShortcut(
|
||||
'prompt_shortcut.json'
|
||||
)
|
||||
console.log('loadPromptShortcut:', prompt_shortcut_json)
|
||||
// console.log('loadPromptShortcut: request: ',request)
|
||||
// console.log('loadPromptShortcut:', prompt_shortcut_json)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
prompt_shortcut_json = {}
|
||||
}
|
||||
return prompt_shortcut_json
|
||||
// return json['prompt_shortcut']
|
||||
}
|
||||
|
||||
async function savePromptShortcut(prompt_shortcut) {
|
||||
|
|
@ -483,11 +480,13 @@ async function requestControlNetTxt2Img(plugin_settings) {
|
|||
app.showAlert('you need to select a valid ControlNet Module')
|
||||
throw 'you need to select a valid ControlNet Module'
|
||||
}
|
||||
|
||||
if (
|
||||
!control_net_settings['controlnet_units'][index]['model'] &&
|
||||
!getModuleDetail()[
|
||||
control_net_settings['controlnet_units'][index]['module']
|
||||
].model_free
|
||||
(!control_net_settings['controlnet_units'][index]['model'] &&
|
||||
!getModuleDetail()[
|
||||
control_net_settings['controlnet_units'][index]['module']
|
||||
].model_free) ||
|
||||
control_net_settings['controlnet_units'][index]['model'] === 'none'
|
||||
) {
|
||||
app.showAlert('you need to select a valid ControlNet Model')
|
||||
throw 'you need to select a valid ControlNet Model'
|
||||
|
|
@ -523,10 +522,7 @@ async function requestControlNetTxt2Img(plugin_settings) {
|
|||
mask_index >= numberOfAnnotations
|
||||
)
|
||||
continue
|
||||
html_manip.setControlMaskSrc(
|
||||
base64ToBase64Url(base64_mask[mask_index]),
|
||||
index
|
||||
)
|
||||
control_net.setControlDetectMapSrc(base64_mask[mask_index], index)
|
||||
g_generation_session.controlNetMask[index] = base64_mask[mask_index]
|
||||
mask_index++
|
||||
}
|
||||
|
|
@ -567,10 +563,11 @@ async function requestControlNetImg2Img(plugin_settings) {
|
|||
throw 'you need to select a valid ControlNet Module'
|
||||
}
|
||||
if (
|
||||
!control_net_settings['controlnet_units'][index]['model'] &&
|
||||
!getModuleDetail()[
|
||||
control_net_settings['controlnet_units'][index]['module']
|
||||
].model_free
|
||||
(!control_net_settings['controlnet_units'][index]['model'] &&
|
||||
!getModuleDetail()[
|
||||
control_net_settings['controlnet_units'][index]['module']
|
||||
].model_free) ||
|
||||
control_net_settings['controlnet_units'][index]['model'] === 'none'
|
||||
) {
|
||||
app.showAlert('you need to select a valid ControlNet Model')
|
||||
throw 'you need to select a valid ControlNet Model'
|
||||
|
|
@ -615,10 +612,7 @@ async function requestControlNetImg2Img(plugin_settings) {
|
|||
mask_index >= numberOfAnnotations
|
||||
)
|
||||
continue
|
||||
html_manip.setControlMaskSrc(
|
||||
base64ToBase64Url(base64_mask[mask_index]),
|
||||
index
|
||||
)
|
||||
control_net.setControlDetectMapSrc(base64_mask[mask_index], index)
|
||||
g_generation_session.controlNetMask[index] = base64_mask[mask_index]
|
||||
mask_index++
|
||||
}
|
||||
|
|
|
|||
676
selection.js
|
|
@ -1,4 +1,5 @@
|
|||
const psapi = require('./psapi')
|
||||
|
||||
const html_manip = require('./utility/html_manip')
|
||||
function finalWidthHeight(
|
||||
selectionWidth,
|
||||
|
|
@ -125,33 +126,103 @@ async function createChannelIfNotExists(channelName) {
|
|||
}
|
||||
}
|
||||
|
||||
const makeMaskChannel = () => ({
|
||||
_obj: 'set',
|
||||
_target: { _ref: 'channel', _property: 'selection' },
|
||||
to: { _ref: 'channel', _name: 'inpaint_mask' },
|
||||
const deleteChannel = (channel_name = 'mask') =>
|
||||
app.activeDocument.channels.getByName(channel_name)
|
||||
? [
|
||||
{
|
||||
_obj: 'delete',
|
||||
_target: { _ref: 'channel', _name: channel_name },
|
||||
options: {
|
||||
failOnMissingProperty: false,
|
||||
failOnMissingElement: false,
|
||||
},
|
||||
},
|
||||
]
|
||||
: []
|
||||
const makeMaskChannel = (channel_name = 'mask') => ({
|
||||
// _obj: 'set',
|
||||
// _target: { _ref: 'channel', _property: 'selection' },
|
||||
// to: { _ref: 'channel', _name: channel_name },
|
||||
|
||||
_obj: 'duplicate',
|
||||
_target: [
|
||||
{
|
||||
_ref: 'channel',
|
||||
_property: 'selection',
|
||||
},
|
||||
],
|
||||
name: channel_name,
|
||||
_isCommand: true,
|
||||
options: { failOnMissingProperty: false, failOnMissingElement: false },
|
||||
})
|
||||
async function makeMaskChannelExe() {
|
||||
async function makeMaskChannelExe(channel_name = 'mask') {
|
||||
await executeAsModal(async () => {
|
||||
// const channel = app.activeDocument.channels.getByName(channel_name)
|
||||
// channel?.remove()
|
||||
const result = await batchPlay(
|
||||
[...deleteChannel(channel_name), makeMaskChannel(channel_name)],
|
||||
// [
|
||||
// {
|
||||
// _obj: 'duplicate',
|
||||
// _target: [
|
||||
// {
|
||||
// _ref: 'channel',
|
||||
// _property: 'selection',
|
||||
// },
|
||||
// ],
|
||||
// name: channel_name,
|
||||
// _isCommand: true,
|
||||
// },
|
||||
|
||||
// {
|
||||
// _obj: 'make',
|
||||
// new: { _class: 'channel' },
|
||||
// at: { _ref: 'channel', _enum: 'channel', _value: 'mask' },
|
||||
// using: {
|
||||
// _enum: 'userMaskEnabled',
|
||||
// _value: 'revealSelection',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
{
|
||||
synchronousExecution: true,
|
||||
modalBehavior: 'execute',
|
||||
}
|
||||
)
|
||||
console.log('result: ', result)
|
||||
})
|
||||
}
|
||||
async function multiGetExe() {
|
||||
desc = {
|
||||
_obj: 'multiGet',
|
||||
_target: { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' },
|
||||
extendedReference: [['layerID', 'itemIndex', 'count']],
|
||||
options: { failOnMissingProperty: false, failOnMissingElement: false },
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await batchPlay([desc], {
|
||||
modalBehavior: 'execute',
|
||||
synchronousExecution: true,
|
||||
})
|
||||
console.log('multiGetExe result: ', result)
|
||||
// await executeAsModal(async () => {})
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
async function applyMaskChannelExe(channel_name = 'mask') {
|
||||
await executeAsModal(async () => {
|
||||
const result = await batchPlay(
|
||||
[
|
||||
// SelectionInfoDesc(),
|
||||
// makeMaskChannel(),
|
||||
|
||||
{
|
||||
_obj: 'duplicate',
|
||||
_target: [
|
||||
{
|
||||
_ref: 'channel',
|
||||
_property: 'selection',
|
||||
},
|
||||
],
|
||||
name: 'inpaint_mask_2',
|
||||
_isCommand: true,
|
||||
_obj: 'set',
|
||||
_target: { _ref: 'channel', _property: 'selection' },
|
||||
to: { _ref: 'channel', _name: channel_name },
|
||||
},
|
||||
// {
|
||||
// _obj: 'set',
|
||||
// _target: { _ref: 'channel', _property: 'selection' },
|
||||
// to: { _ref: 'channel', _name: 'inpaint_mask' },
|
||||
// },
|
||||
{
|
||||
_obj: 'make',
|
||||
new: { _class: 'channel' },
|
||||
|
|
@ -159,6 +230,7 @@ async function makeMaskChannelExe() {
|
|||
using: {
|
||||
_enum: 'userMaskEnabled',
|
||||
_value: 'revealSelection',
|
||||
_name: channel_name,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
@ -171,9 +243,151 @@ async function makeMaskChannelExe() {
|
|||
})
|
||||
}
|
||||
|
||||
async function selectionToChannel(channel_name) {
|
||||
const channelToSelection = {
|
||||
_obj: 'set',
|
||||
_target: { _ref: 'channel', _property: 'selection' },
|
||||
to: { _ref: 'channel', _name: channel_name },
|
||||
}
|
||||
let result
|
||||
try {
|
||||
await executeAsModal(async () => {
|
||||
result = await batchPlay(
|
||||
[
|
||||
...deleteChannel(channel_name),
|
||||
makeMaskChannel(channel_name),
|
||||
channelToSelection,
|
||||
],
|
||||
{ modalBehavior: 'execute', synchronousExecution: true }
|
||||
)
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
return result
|
||||
}
|
||||
async function createLayerFromMaskChannel(channel_name = 'mask') {
|
||||
await executeAsModal(async () => {
|
||||
const result = await batchPlay(
|
||||
[
|
||||
// SelectionInfoDesc(),
|
||||
// makeMaskChannel(),
|
||||
// {
|
||||
// _obj: 'set',
|
||||
// _target: { _ref: 'channel', _property: 'selection' },
|
||||
// to: { _ref: 'channel', _name: channel_name },
|
||||
// },
|
||||
// {
|
||||
// _obj: 'make',
|
||||
// new: { _class: 'channel' },
|
||||
// at: { _ref: 'channel', _enum: 'channel', _value: 'mask' },
|
||||
// using: {
|
||||
// _enum: 'userMaskEnabled',
|
||||
// _value: 'revealSelection',
|
||||
// _name: channel_name,
|
||||
// },
|
||||
// },
|
||||
{
|
||||
_obj: 'set',
|
||||
_target: { _ref: 'layer' },
|
||||
to: { _ref: 'channel', _name: channel_name },
|
||||
},
|
||||
],
|
||||
{
|
||||
synchronousExecution: true,
|
||||
modalBehavior: 'execute',
|
||||
}
|
||||
)
|
||||
console.log('result: ', result)
|
||||
})
|
||||
}
|
||||
|
||||
async function channelToSelectionExe(channel_name = 'mask') {
|
||||
const channelToSelection = {
|
||||
_obj: 'set',
|
||||
_target: { _ref: 'channel', _property: 'selection' },
|
||||
to: { _ref: 'channel', _name: channel_name },
|
||||
}
|
||||
try {
|
||||
await executeAsModal(async () => {
|
||||
const result = await batchPlay([channelToSelection], {
|
||||
modalBehavior: 'execute',
|
||||
synchronousExecution: true,
|
||||
})
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
async function inpaintLassoInitImageAndMask(channel_name = 'mask') {
|
||||
async function getImageFromCanvas() {
|
||||
const width = html_manip.getWidth()
|
||||
const height = html_manip.getHeight()
|
||||
const selectionInfo = await psapi.getSelectionInfoExe()
|
||||
const base64 = await io.IO.getSelectionFromCanvasAsBase64Interface_New(
|
||||
width,
|
||||
height,
|
||||
selectionInfo,
|
||||
true
|
||||
)
|
||||
return base64
|
||||
}
|
||||
const channelToSelection = {
|
||||
_obj: 'set',
|
||||
_target: { _ref: 'channel', _property: 'selection' },
|
||||
to: { _ref: 'channel', _name: channel_name },
|
||||
}
|
||||
// await executeAsModal(async () => {
|
||||
// const result = await batchPlay(
|
||||
// [
|
||||
// ...deleteChannel(channel_name),
|
||||
// makeMaskChannel(channel_name),
|
||||
// channelToSelection,
|
||||
// ],
|
||||
// { modalBehavior: 'execute', synchronousExecution: true }
|
||||
// )
|
||||
// const selection_info = await psapi.getSelectionInfoExe()
|
||||
// })
|
||||
await selectionToChannel('mask') //lasso selection to channel called 'mask'
|
||||
|
||||
const init_base64 = await getImageFromCanvas()
|
||||
|
||||
html_manip.setInitImageSrc(general.base64ToBase64Url(init_base64))
|
||||
let mask_base64
|
||||
await executeAsModal(async () => {
|
||||
const result = await batchPlay([channelToSelection], {
|
||||
modalBehavior: 'execute',
|
||||
synchronousExecution: true,
|
||||
})
|
||||
// const selection_info = await psapi.getSelectionInfoExe()
|
||||
mask_base64 = await fillSelectionWhiteOutsideBlack()
|
||||
})
|
||||
|
||||
//save laso selection to channel
|
||||
//get laso selection
|
||||
//make rect selection
|
||||
//base64 from selection
|
||||
//and display it in init image html element
|
||||
//get laso selection:
|
||||
//make mask layer
|
||||
//get rectangular selection
|
||||
//get base64 from selection
|
||||
//display it in mask image html element
|
||||
// let jimp_mask = await Jimp.read(Buffer.from(mask_base64, 'base64'))
|
||||
// html_manip.setInitImageMaskSrc(
|
||||
// await jimp_mask.getBase64Async(Jimp.MIME_PNG)
|
||||
// )
|
||||
html_manip.setInitImageMaskSrc(general.base64ToBase64Url(mask_base64))
|
||||
|
||||
return [init_base64, mask_base64]
|
||||
// //return
|
||||
// //init image
|
||||
//mask
|
||||
}
|
||||
|
||||
async function fillSelectionWhiteOutsideBlack() {
|
||||
// Create a new layer
|
||||
const layer_name = 'inpaint_laso_mask'
|
||||
const layer_name = 'mask'
|
||||
const getSelectionDesc = () => ({
|
||||
_obj: 'get',
|
||||
_target: [
|
||||
|
|
@ -193,6 +407,9 @@ async function fillSelectionWhiteOutsideBlack() {
|
|||
_obj: 'inverse',
|
||||
_isCommand: true,
|
||||
})
|
||||
await psapi.unselectActiveLayers()
|
||||
const mask_layer = await layer_util.createNewLayerExe('mask')
|
||||
await moveToTopLayerStackExe()
|
||||
await batchPlay(
|
||||
[
|
||||
getSelectionDesc(),
|
||||
|
|
@ -295,73 +512,17 @@ async function fillSelectionWhiteOutsideBlack() {
|
|||
)
|
||||
|
||||
//import the base64 image into the canvas as a new layer
|
||||
await io.IO.base64ToLayer(
|
||||
base64,
|
||||
'base64_to_layer_temp.png',
|
||||
rect_selection_info.left,
|
||||
rect_selection_info.top,
|
||||
rect_selection_info.width,
|
||||
rect_selection_info.height
|
||||
)
|
||||
// Fill the current selection with white
|
||||
// await batchPlay(
|
||||
// [
|
||||
// {
|
||||
// _obj: 'fill',
|
||||
// using: {
|
||||
// _enum: 'fillContents',
|
||||
// _value: 'white',
|
||||
// },
|
||||
// opacity: {
|
||||
// _unit: 'percentUnit',
|
||||
// _value: 100,
|
||||
// },
|
||||
// mode: {
|
||||
// _enum: 'blendMode',
|
||||
// _value: 'normal',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// { modalBehavior: 'execute' }
|
||||
// await io.IO.base64ToLayer(
|
||||
// base64,
|
||||
// 'base64_to_layer_temp.png',
|
||||
// rect_selection_info.left,
|
||||
// rect_selection_info.top,
|
||||
// rect_selection_info.width,
|
||||
// rect_selection_info.height
|
||||
// )
|
||||
|
||||
// // Invert the selection
|
||||
// await batchPlay(
|
||||
// [
|
||||
// {
|
||||
// _obj: 'invert',
|
||||
// _target: [
|
||||
// {
|
||||
// _ref: 'channel',
|
||||
// _property: 'selection',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// { modalBehavior: 'execute' }
|
||||
// )
|
||||
|
||||
// // Fill the inverted selection with black
|
||||
// await batchPlay(
|
||||
// [
|
||||
// {
|
||||
// _obj: 'fill',
|
||||
// using: {
|
||||
// _enum: 'fillContents',
|
||||
// _value: 'black',
|
||||
// },
|
||||
// opacity: {
|
||||
// _unit: 'percentUnit',
|
||||
// _value: 100,
|
||||
// },
|
||||
// mode: {
|
||||
// _enum: 'blendMode',
|
||||
// _value: 'normal',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// { modalBehavior: 'execute' }
|
||||
// )
|
||||
await psapi.cleanLayers([mask_layer])
|
||||
return base64
|
||||
}
|
||||
|
||||
async function inpaintLasoInitImage() {
|
||||
|
|
@ -630,6 +791,337 @@ class Selection {
|
|||
static {}
|
||||
}
|
||||
|
||||
async function moveToTopLayerStackExe() {
|
||||
const moveToTop = {
|
||||
_obj: 'move',
|
||||
_target: { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' },
|
||||
to: { _ref: 'layer', _enum: 'ordinal', _value: 'front' },
|
||||
// _options: { dialogOptions: 'dontDisplay' },
|
||||
options: { failOnMissingProperty: false, failOnMissingElement: false },
|
||||
}
|
||||
try {
|
||||
await executeAsModal(async () => {
|
||||
const layer = app.activeDocument.activeLayers[0]
|
||||
|
||||
while (layer.parent) {
|
||||
console.log(layer.parent)
|
||||
const result = await batchPlay([moveToTop], {
|
||||
modalBehavior: 'execute',
|
||||
synchronousExecution: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
async function colorRange() {
|
||||
// const select_current_layer_cmd = {
|
||||
// _obj: 'set',
|
||||
// _target: [
|
||||
// {
|
||||
// _ref: 'channel',
|
||||
// _property: 'selection',
|
||||
// },
|
||||
// ],
|
||||
// to: {
|
||||
// _ref: 'channel',
|
||||
// _enum: 'channel',
|
||||
// _value: 'transparencyEnum',
|
||||
// },
|
||||
// _isCommand: true,
|
||||
// }
|
||||
const cmd = {
|
||||
_obj: 'colorRange',
|
||||
fuzziness: 0,
|
||||
minimum: {
|
||||
_obj: 'labColor',
|
||||
luminance: 100,
|
||||
a: 0,
|
||||
b: 0,
|
||||
},
|
||||
maximum: {
|
||||
_obj: 'labColor',
|
||||
luminance: 100,
|
||||
a: 0,
|
||||
b: 0,
|
||||
},
|
||||
colorModel: 0,
|
||||
_isCommand: true,
|
||||
}
|
||||
const result = await batchPlay([cmd], {
|
||||
modalBehavior: 'execute',
|
||||
synchronousExecution: true,
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
async function colorRangeExe() {
|
||||
let result
|
||||
try {
|
||||
await executeAsModal(
|
||||
async () => {
|
||||
result = await colorRange()
|
||||
},
|
||||
{ commandName: 'Convert Black and White Layer to mask selection' }
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function base64ToLassoSelection(base64, selection_info) {
|
||||
//place the mask on the canvas
|
||||
const mask_layer = await io.IO.base64ToLayer(
|
||||
base64,
|
||||
'monochrome_mask.png',
|
||||
selection_info.left,
|
||||
selection_info.top,
|
||||
selection_info.width,
|
||||
selection_info.height
|
||||
)
|
||||
//reselect the selection area
|
||||
await psapi.reSelectMarqueeExe(selection_info)
|
||||
//reselect the layer
|
||||
await psapi.selectLayersExe([mask_layer])
|
||||
await executeAsModal(async () => {
|
||||
await layer_util.toggleActiveLayer() // undo the toggling operation, active layer will be visible
|
||||
//select the white pixels
|
||||
await colorRange()
|
||||
})
|
||||
// await colorRangeExe()
|
||||
//delete the mask layer. we only needed to select the white pixels
|
||||
await executeAsModal(async () => {
|
||||
await layer_util.toggleActiveLayer() // undo the toggling operation, active layer will be visible
|
||||
})
|
||||
await layer_util.deleteLayers([mask_layer])
|
||||
}
|
||||
|
||||
async function base64ToChannel(base64, selection_info, channel_name) {
|
||||
try {
|
||||
await executeAsModal(async (context) => {
|
||||
const history_id = await context.hostControl.suspendHistory({
|
||||
documentID: app.activeDocument.id, //TODO: change this to the session document id
|
||||
name: 'Mask Image',
|
||||
})
|
||||
|
||||
const layer = app.activeDocument.activeLayers[0]
|
||||
|
||||
if (!layer_util.Layer.doesLayerExist(layer)) {
|
||||
return null
|
||||
}
|
||||
await psapi.unSelectMarqueeExe()
|
||||
await psapi.unselectActiveLayersExe()
|
||||
await base64ToLassoSelection(base64, selection_info)
|
||||
|
||||
const expand_cmd = {
|
||||
_obj: 'expand',
|
||||
by: {
|
||||
_unit: 'pixelsUnit',
|
||||
_value: 10,
|
||||
},
|
||||
selectionModifyEffectAtCanvasBounds: true,
|
||||
_isCommand: true,
|
||||
}
|
||||
const channelToSelection = {
|
||||
_obj: 'set',
|
||||
_target: { _ref: 'channel', _property: 'selection' },
|
||||
to: { _ref: 'channel', _name: channel_name },
|
||||
}
|
||||
await executeAsModal(async () => {
|
||||
const result = await batchPlay(
|
||||
[
|
||||
expand_cmd,
|
||||
...deleteChannel(channel_name),
|
||||
makeMaskChannel(channel_name),
|
||||
channelToSelection,
|
||||
],
|
||||
{ modalBehavior: 'execute', synchronousExecution: true }
|
||||
)
|
||||
})
|
||||
await psapi.selectLayersExe([layer])
|
||||
await applyMaskChannelExe(channel_name)
|
||||
|
||||
// context = stored_context
|
||||
await context.hostControl.resumeHistory(history_id)
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function black_white_layer_to_mask(mask_id, target_layer_id, mask_name) {
|
||||
let result
|
||||
let psAction = require('photoshop').action
|
||||
|
||||
let command = [
|
||||
//
|
||||
...deleteChannel(mask_name),
|
||||
// Select layer “Layer 33”
|
||||
{
|
||||
_obj: 'select',
|
||||
_target: [{ _id: mask_id, _ref: 'layer' }],
|
||||
// layerID: [3862],
|
||||
makeVisible: false,
|
||||
},
|
||||
// Set Selection
|
||||
{
|
||||
_obj: 'set',
|
||||
_target: [{ _property: 'selection', _ref: 'channel' }],
|
||||
to: {
|
||||
_enum: 'channel',
|
||||
_ref: 'channel',
|
||||
_value: 'transparencyEnum',
|
||||
},
|
||||
},
|
||||
// Color Range
|
||||
{
|
||||
_obj: 'colorRange',
|
||||
colorModel: 0,
|
||||
fuzziness: 0,
|
||||
maximum: { _obj: 'labColor', a: 0.0, b: 0.0, luminance: 100.0 },
|
||||
minimum: { _obj: 'labColor', a: 0.0, b: 0.0, luminance: 100.0 },
|
||||
},
|
||||
// Duplicate Selection, make sure you delete the any mask that share the same name
|
||||
{
|
||||
_obj: 'duplicate',
|
||||
_target: [{ _property: 'selection', _ref: 'channel' }],
|
||||
name: mask_name,
|
||||
},
|
||||
// Select channel “Alpha 1”
|
||||
{ _obj: 'select', _target: [{ _name: mask_name, _ref: 'channel' }] },
|
||||
// Set Selection
|
||||
{
|
||||
_obj: 'set',
|
||||
_target: [{ _property: 'selection', _ref: 'channel' }],
|
||||
to: { _enum: 'ordinal', _ref: 'channel', _value: 'targetEnum' },
|
||||
},
|
||||
// Select layer “Layer 43”
|
||||
{
|
||||
_obj: 'select',
|
||||
_target: [{ _id: target_layer_id, _ref: 'layer' }],
|
||||
// layerID: [3879],
|
||||
makeVisible: false,
|
||||
},
|
||||
// Make
|
||||
{
|
||||
_obj: 'make',
|
||||
at: { _enum: 'channel', _ref: 'channel', _value: 'mask' },
|
||||
new: { _class: 'channel' },
|
||||
using: { _enum: 'userMaskEnabled', _value: 'revealSelection' },
|
||||
},
|
||||
]
|
||||
result = await psAction.batchPlay(command, {})
|
||||
}
|
||||
|
||||
async function black_white_layer_to_mask_multi_batchplay(
|
||||
mask_id,
|
||||
target_layer_id,
|
||||
mask_name
|
||||
) {
|
||||
let result
|
||||
let psAction = require('photoshop').action
|
||||
const timer = (ms) => new Promise((res) => setTimeout(res, ms))
|
||||
|
||||
let command1 = [
|
||||
{
|
||||
_obj: 'set',
|
||||
_target: [{ _property: 'selection', _ref: 'channel' }],
|
||||
to: { _enum: 'ordinal', _value: 'none' },
|
||||
},
|
||||
//
|
||||
...deleteChannel(mask_name),
|
||||
// Select layer “Layer 33”
|
||||
{
|
||||
_obj: 'select',
|
||||
_target: [{ _id: mask_id, _ref: 'layer' }],
|
||||
// layerID: [3862],
|
||||
makeVisible: false,
|
||||
},
|
||||
// Set Selection
|
||||
{
|
||||
_obj: 'set',
|
||||
_target: [{ _property: 'selection', _ref: 'channel' }],
|
||||
to: {
|
||||
_enum: 'channel',
|
||||
_ref: 'channel',
|
||||
_value: 'transparencyEnum',
|
||||
},
|
||||
},
|
||||
]
|
||||
let command2 = [
|
||||
// Color Range
|
||||
{
|
||||
_obj: 'colorRange',
|
||||
colorModel: 0,
|
||||
fuzziness: 0,
|
||||
maximum: { _obj: 'labColor', a: 0.0, b: 0.0, luminance: 100.0 },
|
||||
minimum: { _obj: 'labColor', a: 0.0, b: 0.0, luminance: 100.0 },
|
||||
},
|
||||
{
|
||||
_obj: 'expand',
|
||||
by: {
|
||||
_unit: 'pixelsUnit',
|
||||
_value: 10,
|
||||
},
|
||||
selectionModifyEffectAtCanvasBounds: true,
|
||||
_isCommand: true,
|
||||
},
|
||||
]
|
||||
let command3 = [
|
||||
// Duplicate Selection, make sure you delete the any mask that share the same name
|
||||
{
|
||||
_obj: 'duplicate',
|
||||
_target: [{ _property: 'selection', _ref: 'channel' }],
|
||||
name: mask_name,
|
||||
},
|
||||
// Select channel “Alpha 1”
|
||||
{
|
||||
_obj: 'select',
|
||||
_target: [{ _name: mask_name, _ref: 'channel' }],
|
||||
},
|
||||
// Set Selection
|
||||
{
|
||||
_obj: 'set',
|
||||
_target: [{ _property: 'selection', _ref: 'channel' }],
|
||||
to: { _enum: 'ordinal', _ref: 'channel', _value: 'targetEnum' },
|
||||
},
|
||||
// Select layer “Layer 43”
|
||||
{
|
||||
_obj: 'select',
|
||||
_target: [{ _id: target_layer_id, _ref: 'layer' }],
|
||||
// layerID: [3879],
|
||||
makeVisible: false,
|
||||
},
|
||||
// Make
|
||||
{
|
||||
_obj: 'make',
|
||||
at: { _enum: 'channel', _ref: 'channel', _value: 'mask' },
|
||||
new: { _class: 'channel' },
|
||||
using: { _enum: 'userMaskEnabled', _value: 'revealSelection' },
|
||||
},
|
||||
]
|
||||
await timer(g_timer_value)
|
||||
// result = await psAction.batchPlay(command, {})
|
||||
|
||||
await executeAsModal(async () => {
|
||||
result = await psAction.batchPlay(command1, {})
|
||||
})
|
||||
|
||||
await timer(g_timer_value)
|
||||
await executeAsModal(async () => {
|
||||
await layer_util.toggleActiveLayer() // toggle active layer will be visible, note: doesn't solve the issue in outpaint mode
|
||||
result = await psAction.batchPlay(command2, {})
|
||||
})
|
||||
|
||||
await timer(g_timer_value)
|
||||
await executeAsModal(async () => {
|
||||
await layer_util.toggleActiveLayer() // undo the toggling operation, active layer will be visible, note: doesn't solve the issue in outpaint mode
|
||||
result = await psAction.batchPlay(command3, {})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
finalWidthHeight,
|
||||
selectionToFinalWidthHeight,
|
||||
|
|
@ -640,4 +1132,18 @@ module.exports = {
|
|||
makeMaskChannel,
|
||||
makeMaskChannelExe,
|
||||
fillSelectionWhiteOutsideBlack,
|
||||
inpaintLasoInitImage,
|
||||
applyMaskChannelExe,
|
||||
createLayerFromMaskChannel,
|
||||
multiGetExe,
|
||||
inpaintLassoInitImageAndMask,
|
||||
channelToSelectionExe,
|
||||
moveToTopLayerStackExe,
|
||||
colorRangeExe,
|
||||
base64ToLassoSelection,
|
||||
base64ToChannel,
|
||||
deleteChannel,
|
||||
selectionToChannel,
|
||||
black_white_layer_to_mask,
|
||||
black_white_layer_to_mask_multi_batchplay,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
|
||||
# controlnet original + txt2img
|
||||
import requests
|
||||
import cv2
|
||||
import numpy as np
|
||||
from base64 import b64encode , b64decode
|
||||
from PIL import Image
|
||||
import io
|
||||
|
||||
def readImage(path):
|
||||
img = cv2.imread(path)
|
||||
retval, buffer = cv2.imencode('.jpg', img)
|
||||
b64img = b64encode(buffer).decode("utf-8")
|
||||
return b64img
|
||||
|
||||
def readb64(uri):
|
||||
nparr = np.fromstring(b64decode(uri), np.uint8)
|
||||
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
|
||||
return img
|
||||
|
||||
b64img = readImage("output_image.png")
|
||||
|
||||
class controlnetRequest():
|
||||
def __init__(self, prompt):
|
||||
self.url = "http://127.0.0.1:7860/controlnet/txt2img" #openpose
|
||||
self.body = {
|
||||
"prompt": prompt,
|
||||
"negative_prompt": "",
|
||||
"seed": -1,
|
||||
"subseed": -1,
|
||||
"subseed_strength": 0,
|
||||
"batch_size": 1,
|
||||
"n_iter": 1,
|
||||
"steps": 30,
|
||||
"cfg_scale": 14,
|
||||
"width": 512,
|
||||
"height": 512,
|
||||
"restore_faces": True,
|
||||
"eta": 0,
|
||||
"sampler_index": "DDIM",
|
||||
"controlnet_model": "Test_ziva",
|
||||
"controlnet_input_image": [b64img],
|
||||
"controlnet_module": 'depth',
|
||||
"ControlNet Weight": 1,
|
||||
"controlnet_model": 'control_sd15_depth [fef5e48e]',
|
||||
"controlnet_guidance": 1
|
||||
}
|
||||
|
||||
def sendRequest(self):
|
||||
# print(self.simple_txt2img)
|
||||
r = requests.post(self.url, json=self.body)
|
||||
print(r)
|
||||
return r.json()
|
||||
|
||||
js = controlnetRequest("clothed busty bird").sendRequest()
|
||||
|
||||
|
||||
for x,i in enumerate(js['images']):
|
||||
image = Image.open(io.BytesIO(b64decode(i.split(",",1)[0])))
|
||||
image.save(str(x)+'output.png')
|
||||
|
||||
|
||||
|
||||
len(js['images'])
|
||||
print(js)
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
#code copied from controlnet repo global_state.py
|
||||
|
||||
|
||||
preprocessor_filters = {
|
||||
"All": "none",
|
||||
"Canny": "canny",
|
||||
"Depth": "depth_midas",
|
||||
"Normal": "normal_bae",
|
||||
"OpenPose": "openpose_full",
|
||||
"MLSD": "mlsd",
|
||||
"Lineart": "lineart_standard (from white bg & black line)",
|
||||
"SoftEdge": "softedge_pidinet",
|
||||
"Scribble": "scribble_pidinet",
|
||||
"Seg": "seg_ofade20k",
|
||||
"Shuffle": "shuffle",
|
||||
"Tile": "tile_resample",
|
||||
"Inpaint": "inpaint_only",
|
||||
"IP2P": "none",
|
||||
"Reference": "reference_only",
|
||||
"T2IA": "none",
|
||||
}
|
||||
|
||||
cn_preprocessor_modules = ["none",
|
||||
"canny",
|
||||
"depth",
|
||||
"depth_leres",
|
||||
"depth_leres++",
|
||||
"hed",
|
||||
"hed_safe",
|
||||
"mediapipe_face",
|
||||
"mlsd",
|
||||
"normal_map",
|
||||
"openpose",
|
||||
"openpose_hand",
|
||||
"openpose_face",
|
||||
"openpose_faceonly",
|
||||
"openpose_full",
|
||||
"clip_vision",
|
||||
"color",
|
||||
"pidinet",
|
||||
"pidinet_safe",
|
||||
"pidinet_sketch",
|
||||
"pidinet_scribble",
|
||||
"scribble_xdog",
|
||||
"scribble_hed",
|
||||
"segmentation",
|
||||
"threshold",
|
||||
"depth_zoe",
|
||||
"normal_bae",
|
||||
"oneformer_coco",
|
||||
"oneformer_ade20k",
|
||||
"lineart",
|
||||
"lineart_coarse",
|
||||
"lineart_anime",
|
||||
"lineart_standard",
|
||||
"shuffle",
|
||||
"tile_resample",
|
||||
"invert",
|
||||
"lineart_anime_denoise",
|
||||
"reference_only",
|
||||
"reference_adain",
|
||||
"reference_adain+attn",
|
||||
"inpaint",
|
||||
"inpaint_only",
|
||||
"inpaint_only+lama",
|
||||
"tile_colorfix",
|
||||
"tile_colorfix+sharp",
|
||||
]
|
||||
|
||||
preprocessor_aliases = {
|
||||
"invert": "invert (from white bg & black line)",
|
||||
"lineart_standard": "lineart_standard (from white bg & black line)",
|
||||
"lineart": "lineart_realistic",
|
||||
"color": "t2ia_color_grid",
|
||||
"clip_vision": "t2ia_style_clipvision",
|
||||
"pidinet_sketch": "t2ia_sketch_pidi",
|
||||
"depth": "depth_midas",
|
||||
"normal_map": "normal_midas",
|
||||
"hed": "softedge_hed",
|
||||
"hed_safe": "softedge_hedsafe",
|
||||
"pidinet": "softedge_pidinet",
|
||||
"pidinet_safe": "softedge_pidisafe",
|
||||
"segmentation": "seg_ufade20k",
|
||||
"oneformer_coco": "seg_ofcoco",
|
||||
"oneformer_ade20k": "seg_ofade20k",
|
||||
"pidinet_scribble": "scribble_pidinet",
|
||||
"inpaint": "inpaint_global_harmonious",
|
||||
}
|
||||
|
||||
def filter_selected_helper(k,preprocessor_list,model_list):
|
||||
if 'None' not in model_list:
|
||||
model_list = ['None'] + model_list
|
||||
ui_preprocessor_keys = ['none', preprocessor_aliases['invert']]
|
||||
|
||||
|
||||
ui_preprocessor_keys += sorted([preprocessor_aliases.get(k, k)
|
||||
for k in preprocessor_list
|
||||
if preprocessor_aliases.get(k, k) not in ui_preprocessor_keys])
|
||||
|
||||
|
||||
preprocessor_list = ui_preprocessor_keys
|
||||
# print("preprocessor_list sorted: ",preprocessor_list)
|
||||
model_list = list(model_list)
|
||||
# print("list(model_list): ",model_list)
|
||||
|
||||
# print("k:",k,k.lower())
|
||||
|
||||
|
||||
default_option = preprocessor_filters[k]
|
||||
pattern = k.lower()
|
||||
# model_list = list(cn_models.keys())
|
||||
if pattern == "all":
|
||||
return [
|
||||
preprocessor_list,
|
||||
model_list,
|
||||
'none', #default option
|
||||
"None" #default model
|
||||
]
|
||||
filtered_preprocessor_list = [
|
||||
x
|
||||
for x in preprocessor_list
|
||||
if pattern in x.lower() or x.lower() == "none"
|
||||
]
|
||||
if pattern in ["canny", "lineart", "scribble", "mlsd"]:
|
||||
filtered_preprocessor_list += [
|
||||
x for x in preprocessor_list if "invert" in x.lower()
|
||||
]
|
||||
|
||||
##Debug start
|
||||
# for model in model_list:
|
||||
# print("model: ",model)
|
||||
# if pattern in model.lower():
|
||||
# print('add to filtered')
|
||||
# print("pattern:",pattern, "in model.lower():",model.lower())
|
||||
# else:
|
||||
# print("pattern:",pattern, "not in model.lower():",model.lower())
|
||||
##Debug end
|
||||
|
||||
filtered_model_list = [
|
||||
x for x in model_list if pattern in x.lower() or x.lower() == "none"
|
||||
]
|
||||
if default_option not in filtered_preprocessor_list:
|
||||
default_option = filtered_preprocessor_list[0]
|
||||
if len(filtered_model_list) == 1:
|
||||
default_model = "None"
|
||||
filtered_model_list = model_list
|
||||
else:
|
||||
default_model = filtered_model_list[1]
|
||||
for x in filtered_model_list:
|
||||
if "11" in x.split("[")[0]:
|
||||
default_model = x
|
||||
break
|
||||
|
||||
return [filtered_preprocessor_list,filtered_model_list, default_option,default_model]
|
||||
|
|
@ -32,7 +32,7 @@ def reserveBorderPixels(img,dilation_img):
|
|||
width, height = img.size
|
||||
dilation_pixels = dilation_img.load()
|
||||
all_pixels = []
|
||||
depth = 20 # five pixel depth
|
||||
depth = 1 # five pixel depth
|
||||
for x in range(width):
|
||||
for d in range(depth):
|
||||
dilation_pixels[x,d] = pixels[x, d]
|
||||
|
|
@ -44,7 +44,7 @@ def reserveBorderPixels(img,dilation_img):
|
|||
dilation_pixels[width-(d+1),y] = pixels[width-(d+1), y]
|
||||
return dilation_img
|
||||
|
||||
def maskExpansion(mask_img,mask_expansion):
|
||||
def maskExpansion(mask_img,mask_expansion,blur =10):
|
||||
#only if image exist then try to open it
|
||||
|
||||
|
||||
|
|
@ -53,8 +53,8 @@ def maskExpansion(mask_img,mask_expansion):
|
|||
# if(payload['use_sharp_mask'] == False):# use blurry mask
|
||||
iteration = mask_expansion
|
||||
dilated_img = applyDilation(mask_img,iteration)
|
||||
mask_with_border = reserveBorderPixels(mask_img,dilated_img)
|
||||
mask_with_border = mask_with_border.filter(ImageFilter.GaussianBlur(radius = 10))
|
||||
blurred_image = dilated_img.filter(ImageFilter.GaussianBlur(radius = blur))
|
||||
mask_with_border = reserveBorderPixels(mask_img,blurred_image)
|
||||
return mask_with_border
|
||||
|
||||
async def base64ToPng(base64_image,image_path):
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ except ImportError:
|
|||
|
||||
async def imageSearch(keywords="cute cats"):
|
||||
with DDGS() as ddgs:
|
||||
return [x for x in islice(ddgs.images(keywords), 30)]
|
||||
return [x for x in islice(ddgs.images(keywords,safesearch='off'), 50)]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import base64
|
|||
from PIL import Image, PngImagePlugin
|
||||
import asyncio
|
||||
import httpx
|
||||
|
||||
from typing import List
|
||||
|
||||
import os
|
||||
import time
|
||||
|
|
@ -14,6 +14,8 @@ import serverHelper
|
|||
import prompt_shortcut
|
||||
import metadata_to_json
|
||||
import search
|
||||
import global_state
|
||||
|
||||
sd_url = os.environ.get('SD_URL', 'http://127.0.0.1:7860')
|
||||
|
||||
|
||||
|
|
@ -90,8 +92,8 @@ def img_2_b64(image):
|
|||
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi import APIRouter, Request
|
||||
|
||||
from fastapi import FastAPI,APIRouter, Request,Query, Body
|
||||
|
||||
|
||||
|
||||
|
|
@ -276,7 +278,7 @@ async def savePng(request:Request):
|
|||
except:
|
||||
json = {}
|
||||
|
||||
print("json:",json)
|
||||
# print("json:",json)
|
||||
try:
|
||||
folder = './init_images'
|
||||
image_path = f"{folder}/{json['image_name']}"
|
||||
|
|
@ -323,13 +325,14 @@ async def maskExpansionHandler(request:Request):
|
|||
# keywords = json.get('keywords','cute dogs')
|
||||
base64_mask_image = json['mask']
|
||||
mask_expansion = json['mask_expansion']
|
||||
blur = json['blur']
|
||||
#convert base64 to img
|
||||
|
||||
await img2imgapi.base64ToPng(base64_mask_image,"original_mask.png")#save a copy of the mask for debugging
|
||||
|
||||
mask_image = img2imgapi.b64_2_img(base64_mask_image)
|
||||
|
||||
expanded_mask_img = img2imgapi.maskExpansion(mask_image,mask_expansion)
|
||||
expanded_mask_img = img2imgapi.maskExpansion(mask_image,mask_expansion,blur)
|
||||
base64_expanded_mask_image = img2imgapi.img_2_b64(expanded_mask_img)
|
||||
await img2imgapi.base64ToPng(base64_expanded_mask_image,"expanded_mask.png")#save a copy of the mask of the expanded_mask for debugging
|
||||
|
||||
|
|
@ -417,8 +420,8 @@ async def loadPromptShortcut(request: Request):
|
|||
json = {}
|
||||
|
||||
try:
|
||||
print("json: ",json)
|
||||
print("json['prompt_shortcut']: ",json['prompt_shortcut'])
|
||||
# print("json: ",json)
|
||||
# print("json['prompt_shortcut']: ",json['prompt_shortcut'])
|
||||
# save the prompt shortcut to the prompt_shortcut.json
|
||||
prompt_shortcut_json = json['prompt_shortcut']
|
||||
# response.body = {"prompt_shortcut":prompt_shortcut}
|
||||
|
|
@ -453,7 +456,7 @@ async def openUrl(request:Request):
|
|||
json = {}
|
||||
|
||||
url = ""
|
||||
print("json: ",json)
|
||||
# print("json: ",json)
|
||||
try:
|
||||
url = json['url']
|
||||
webbrowser.open(url) # Go to example.com
|
||||
|
|
@ -503,5 +506,37 @@ async def list_available_vae():
|
|||
return sd_vae_dict
|
||||
|
||||
|
||||
@router.post("/controlnet/filter")
|
||||
async def filter(keyword:str = Body('All',title="keyword to filter by"),
|
||||
preprocessor_list: List[str]= Body([],title="complete preprocessor list"),
|
||||
model_list: List[str] =Body([],title="complete model list"),):
|
||||
|
||||
|
||||
filtered_preprocessor_list= []
|
||||
filtered_model_list= []
|
||||
default_option= 'none'
|
||||
default_model= 'None'
|
||||
|
||||
print("preprocessor_list:",preprocessor_list)
|
||||
print("model_list:",model_list)
|
||||
try:
|
||||
[filtered_preprocessor_list,filtered_model_list,default_option,default_model] = global_state.filter_selected_helper(keyword,preprocessor_list,model_list)
|
||||
except Exception as e:
|
||||
print("Invalid Keyword: ",e)
|
||||
|
||||
return {
|
||||
"keywords": list(global_state.preprocessor_filters.keys()),
|
||||
"module_list": filtered_preprocessor_list,
|
||||
"model_list": filtered_model_list,
|
||||
"default_option":default_option,
|
||||
"default_model":default_model
|
||||
}
|
||||
|
||||
@router.get('/heartbeat')
|
||||
async def heartbeat():
|
||||
|
||||
return {'heartbeat':True}
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
"typeRoots": ["./*/types","./types", "./node_modules/@types"], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
"typeRoots": ["./*/@types","./*/types","./types", "./node_modules/@types"], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
"resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
|
|
@ -53,10 +53,10 @@
|
|||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./main/dist", /* Specify an output folder for all emitted files. */
|
||||
"outDir": "./typescripts/dist", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
|
|
@ -108,4 +108,5 @@
|
|||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"exclude": ["./jimp"],
|
||||
// "include": ["./typescripts/@types/custom.d.ts"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
declare module 'changedpi'
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
declare module '*.svg' {
|
||||
import React = require('react')
|
||||
export const ReactComponent: React.FunctionComponent<
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
declare module 'sdapi_py_re' {
|
||||
const exports: any
|
||||
export = exports
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
declare module 'uxp' {
|
||||
// Add type declarations for the uxp module here
|
||||
export const storage: any
|
||||
export const versions: any
|
||||
export const host: any
|
||||
}
|
||||
|
|
@ -2,18 +2,20 @@ import React, { ReactPropTypes } from 'react'
|
|||
import ReactDOM from 'react-dom/client'
|
||||
|
||||
// import { action, makeAutoObservable, reaction, toJS } from 'mobx'
|
||||
import { observer } from 'mobx-react'
|
||||
import { observer, useObserver } from 'mobx-react'
|
||||
|
||||
import {
|
||||
SliderType,
|
||||
SpCheckBox,
|
||||
SpMenu,
|
||||
SpSliderWithLabel,
|
||||
} from '../../ultimate_sd_upscaler/src/elements'
|
||||
|
||||
import { AStore } from '../../main/src/astore'
|
||||
} from '../util/elements'
|
||||
// import * as sdapi from '../../sdapi_py_re'
|
||||
import { api } from '../util/oldSystem'
|
||||
import { AStore } from '../main/astore'
|
||||
import { ui_config, model_list } from './config'
|
||||
import { requestGet } from '../../utility/api'
|
||||
import { requestControlNetModelList } from '../../utility/tab/control_net'
|
||||
const { requestGet } = api
|
||||
import { requestControlNetModelList } from '../controlnet/entry'
|
||||
|
||||
import './style/after_detailer.css'
|
||||
|
||||
|
|
@ -88,32 +90,28 @@ export class AfterDetailerComponent extends React.Component<{
|
|||
async componentDidUpdate(
|
||||
prevProps: ReactPropTypes,
|
||||
prevState: ReactPropTypes
|
||||
) {
|
||||
// if (store.data.refresh) {
|
||||
// if (await this.isInstalled()) {
|
||||
// await this.getInpaintModels()
|
||||
// store.updateProperty('refresh', false)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
) {}
|
||||
handleRefresh = async () => {
|
||||
if (await this.isInstalled()) {
|
||||
await this.getInpaintModels()
|
||||
// store.updateProperty('refresh', false)
|
||||
}
|
||||
}
|
||||
async isInstalled() {
|
||||
const full_url = `${g_sd_url}/sdapi/v1/scripts`
|
||||
try {
|
||||
const full_url = `${g_sd_url}/sdapi/v1/scripts`
|
||||
|
||||
const scripts = await requestGet(full_url)
|
||||
const is_installed =
|
||||
scripts?.txt2img?.includes(store.data.script_name) ||
|
||||
scripts?.img2img?.includes(store.data.script_name) ||
|
||||
false
|
||||
const scripts = await requestGet(full_url)
|
||||
const is_installed =
|
||||
scripts?.txt2img?.includes(store.data.script_name) ||
|
||||
scripts?.img2img?.includes(store.data.script_name) ||
|
||||
false
|
||||
|
||||
console.log('is_installed: ', is_installed)
|
||||
store.updateProperty('is_installed', is_installed)
|
||||
return is_installed
|
||||
console.log('is_installed: ', is_installed)
|
||||
store.updateProperty('is_installed', is_installed)
|
||||
return is_installed
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
async getInpaintModels() {
|
||||
try {
|
||||
|
|
@ -140,6 +138,7 @@ export class AfterDetailerComponent extends React.Component<{
|
|||
</sp-label>
|
||||
<button
|
||||
className="btnSquare refreshButton"
|
||||
id="btnResetSettings"
|
||||
title="Refresh the After Detailer Extension"
|
||||
onClick={this.handleRefresh}
|
||||
></button>
|
||||
|
|
@ -202,7 +201,7 @@ export class AfterDetailerComponent extends React.Component<{
|
|||
output_value={store.data['ad_conf']}
|
||||
// title={ui_config[id].label}
|
||||
label="Detection Confidence Threshold %:"
|
||||
onSliderChange={(new_value: number) => {
|
||||
onSliderInput={(new_value: number) => {
|
||||
// console.log('slider_change: ', new_value)
|
||||
store.updateProperty('ad_conf', new_value)
|
||||
}}
|
||||
|
|
@ -250,7 +249,7 @@ export class AfterDetailerComponent extends React.Component<{
|
|||
? SliderType.Integer
|
||||
: SliderType.Float
|
||||
}
|
||||
onSliderChange={(new_value: number) => {
|
||||
onSliderInput={(new_value: number) => {
|
||||
// console.log('slider_change: ', new_value)
|
||||
store.updateProperty('controlNetWeight', new_value)
|
||||
}}
|
||||
|
|
@ -264,24 +263,66 @@ const domNode = document.getElementById('alwaysOnScriptsContainer')!
|
|||
const root = ReactDOM.createRoot(domNode)
|
||||
|
||||
import { useState, ReactNode } from 'react'
|
||||
import Locale from '../locale/locale'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
|
||||
interface CollapsibleProps {
|
||||
label: string
|
||||
labelStyle?: React.CSSProperties
|
||||
containerStyle?: React.CSSProperties
|
||||
defaultIsOpen?: boolean
|
||||
checked?: boolean
|
||||
checkboxCallback?: (checked: boolean) => void
|
||||
children: ReactNode
|
||||
}
|
||||
const Collapsible = ({ label, children }: CollapsibleProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
function Collapsible({
|
||||
label,
|
||||
labelStyle,
|
||||
containerStyle,
|
||||
defaultIsOpen = false,
|
||||
checkboxCallback,
|
||||
checked,
|
||||
children,
|
||||
}: CollapsibleProps) {
|
||||
const [isOpen, setIsOpen] = useState(defaultIsOpen)
|
||||
|
||||
const handleToggle = () => {
|
||||
setIsOpen(!isOpen)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="collapsible" onClick={handleToggle}>
|
||||
<span>{label}</span>
|
||||
<span style={{ float: 'right' }} className="triangle">
|
||||
{isOpen ? '∨' : '<'}
|
||||
/*useObserver(()=>*/ <div>
|
||||
<div
|
||||
className="collapsible"
|
||||
style={containerStyle}
|
||||
onClick={handleToggle}
|
||||
>
|
||||
<span className="truncate" style={labelStyle}>
|
||||
{label}
|
||||
</span>
|
||||
|
||||
<span
|
||||
style={{ float: 'right', display: 'flex' }}
|
||||
className="triangle"
|
||||
>
|
||||
{checkboxCallback && checked !== void 0 ? (
|
||||
<input
|
||||
type="checkbox"
|
||||
className="minimal-checkbox"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
}}
|
||||
onChange={(event: any) => {
|
||||
checkboxCallback(event.target.checked)
|
||||
}}
|
||||
checked={checked}
|
||||
/>
|
||||
) : (
|
||||
void 0
|
||||
)}
|
||||
|
||||
<span>{isOpen ? '∨' : '<'}</span>
|
||||
</span>
|
||||
</div>
|
||||
{/* {isOpen && <div>{children}</div>} */}
|
||||
|
|
@ -294,38 +335,12 @@ export default Collapsible
|
|||
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<div style={{ border: '2px solid #6d6c6c', padding: '3px' }}>
|
||||
{/* <button>test</button> */}
|
||||
{/* <div
|
||||
// type="button"
|
||||
className="collapsible"
|
||||
onClick={(event: any) => {
|
||||
console.log('clicked')
|
||||
event.target.classList.toggle('collapsible-active')
|
||||
|
||||
let content = event.target.nextElementSibling
|
||||
console.log('collapsible content: ', content)
|
||||
let triangle =
|
||||
event.target.getElementsByClassName('triangle')[0]
|
||||
if (content.style.display === 'block') {
|
||||
content.style.display = 'none'
|
||||
triangle.innerText = '<'
|
||||
} else {
|
||||
content.style.display = 'block'
|
||||
triangle.innerText = '∨'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span>After Detailer</span>
|
||||
<span style={{ float: 'right' }} className="triangle">
|
||||
{'<'}
|
||||
</span>
|
||||
</div> */}
|
||||
|
||||
{/* <AfterDetailerComponent></AfterDetailerComponent> */}
|
||||
<Collapsible label={'After Detailer'}>
|
||||
<AfterDetailerComponent />
|
||||
</Collapsible>
|
||||
</div>
|
||||
<ErrorBoundary>
|
||||
<div style={{ border: '2px solid #6d6c6c', padding: '3px' }}>
|
||||
<Collapsible label={'After Detailer'}>
|
||||
<AfterDetailerComponent />
|
||||
</Collapsible>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
|
@ -9,7 +9,7 @@ export const model_list = [
|
|||
'person_yolov8n-seg.pt',
|
||||
|
||||
'person_yolov8s-seg.pt',
|
||||
'None'
|
||||
'None',
|
||||
]
|
||||
export let ui_config = {
|
||||
ad_model: {
|
||||
|
|
@ -25,4 +25,16 @@
|
|||
|
||||
.triangle {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.truncate {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.minimal-checkbox {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
import { observer } from 'mobx-react'
|
||||
import React from 'react'
|
||||
import ControlNetUnit from './ControlNetUnit'
|
||||
import { store as ControlNetStore } from './main'
|
||||
import { DefaultControlNetUnitData } from './store'
|
||||
import {
|
||||
Enum,
|
||||
controlnet_preset,
|
||||
note,
|
||||
preset,
|
||||
selection,
|
||||
} from '../util/oldSystem'
|
||||
import { SpMenuComponent } from '../util/elements'
|
||||
import Locale from '../locale/locale'
|
||||
import Collapsible from '../after_detailer/after_detailer'
|
||||
|
||||
let g_controlnet_presets: any
|
||||
declare const g_generation_session: any
|
||||
|
||||
@observer
|
||||
class ControlNetTab extends React.Component<{
|
||||
appState: typeof ControlNetStore
|
||||
}> {
|
||||
state = {
|
||||
maxControlNet: 0,
|
||||
presetMenuChildren: [],
|
||||
}
|
||||
|
||||
// private presetMenuChildren: JSX.Element[] = []
|
||||
|
||||
onPresetMenuChange(evt: any) {
|
||||
const preset_index = evt.target.selectedIndex
|
||||
const preset_name = evt.target.options[preset_index].textContent
|
||||
ControlNetStore.controlNetUnitData.forEach((dataitem, index) => {
|
||||
const presetData = g_controlnet_presets[preset_name][index] || {}
|
||||
|
||||
dataitem.enabled =
|
||||
presetData.enabled || DefaultControlNetUnitData.enabled
|
||||
dataitem.input_image =
|
||||
presetData.input_image || DefaultControlNetUnitData.input_image
|
||||
dataitem.mask = presetData.mask || DefaultControlNetUnitData.mask
|
||||
|
||||
dataitem.module =
|
||||
presetData.module || DefaultControlNetUnitData.module
|
||||
dataitem.model = presetData.model || DefaultControlNetUnitData.model
|
||||
dataitem.weight =
|
||||
presetData.weight || DefaultControlNetUnitData.weight
|
||||
dataitem.resize_mode =
|
||||
presetData.resize_mode || DefaultControlNetUnitData.resize_mode
|
||||
dataitem.lowvram =
|
||||
presetData.lowvram || DefaultControlNetUnitData.lowvram
|
||||
dataitem.processor_res =
|
||||
presetData.processor_res ||
|
||||
DefaultControlNetUnitData.processor_res
|
||||
dataitem.threshold_a =
|
||||
presetData.threshold_a || DefaultControlNetUnitData.threshold_a
|
||||
dataitem.threshold_b =
|
||||
presetData.threshold_b || DefaultControlNetUnitData.threshold_b
|
||||
dataitem.guidance_start =
|
||||
presetData.guidance_start ||
|
||||
DefaultControlNetUnitData.guidance_start
|
||||
dataitem.guidance_end =
|
||||
presetData.guidance_end ||
|
||||
DefaultControlNetUnitData.guidance_end
|
||||
dataitem.guessmode =
|
||||
presetData.guessmode || DefaultControlNetUnitData.guessmode
|
||||
|
||||
dataitem.control_mode =
|
||||
presetData.control_mode ||
|
||||
DefaultControlNetUnitData.control_mode
|
||||
dataitem.pixel_perfect =
|
||||
presetData.pixel_perfect ||
|
||||
DefaultControlNetUnitData.pixel_perfect
|
||||
})
|
||||
}
|
||||
// function to update presetMenuChildren
|
||||
updatePresetMenuChildren(newChildren: any) {
|
||||
this.setState({ presetMenuChildren: newChildren })
|
||||
}
|
||||
async updatePresetMenuEvent() {
|
||||
const custom_presets = await preset.getAllCustomPresetsSettings(
|
||||
Enum.PresetTypeEnum['ControlNetPreset']
|
||||
)
|
||||
g_controlnet_presets = {
|
||||
'Select CtrlNet Preset': {},
|
||||
...controlnet_preset.ControlNetNativePresets,
|
||||
...custom_presets,
|
||||
}
|
||||
|
||||
const presets_names = Object.keys(g_controlnet_presets)
|
||||
|
||||
const presetMenuChildren = presets_names.map((preset_name) => {
|
||||
if (preset_name == 'Select CtrlNet Preset')
|
||||
return (
|
||||
<sp-menu-item
|
||||
key={preset_name}
|
||||
className="mControlNetPresetMenuItem"
|
||||
selected
|
||||
>
|
||||
{preset_name}
|
||||
</sp-menu-item>
|
||||
)
|
||||
else
|
||||
return (
|
||||
<sp-menu-item
|
||||
key={preset_name}
|
||||
className="mControlNetPresetMenuItem"
|
||||
>
|
||||
{preset_name}
|
||||
</sp-menu-item>
|
||||
)
|
||||
})
|
||||
this.updatePresetMenuChildren(presetMenuChildren)
|
||||
}
|
||||
|
||||
async onSetAllControlImage() {
|
||||
const selectionInfo = await selection.Selection.getSelectionInfoExe()
|
||||
if (selectionInfo) {
|
||||
const base64_image =
|
||||
await g_generation_session.setControlNetImageHelper()
|
||||
|
||||
ControlNetStore.controlNetUnitData.forEach(async (data) => {
|
||||
data.input_image = base64_image
|
||||
})
|
||||
} else {
|
||||
await note.Notification.inactiveSelectionArea()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.updatePresetMenuEvent()
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<sp-picker
|
||||
title="auto fill the ControlNet with smart settings, to speed up your working process."
|
||||
size="s"
|
||||
label="ControlNet Preset"
|
||||
style={{
|
||||
width: '65%',
|
||||
}}
|
||||
>
|
||||
<SpMenuComponent
|
||||
id="mControlNetPresetMenu"
|
||||
value="Select CtrlNet Preset"
|
||||
onChange={this.onPresetMenuChange.bind(this)}
|
||||
onUpdatePresetMenuEvent={this.updatePresetMenuEvent.bind(
|
||||
this
|
||||
)}
|
||||
>
|
||||
{this.state.presetMenuChildren.map((child) => child)}
|
||||
</SpMenuComponent>
|
||||
</sp-picker>
|
||||
<div></div>
|
||||
{this.props.appState.maxControlNet == 0 && (
|
||||
<sp-label
|
||||
id="controlnetMissingError"
|
||||
style={{ color: '#ff595e', whiteSpace: 'normal' }}
|
||||
>
|
||||
{Locale(
|
||||
'The Controlnet Extension is missing from Automatic1111.\nPlease install it to use it through the plugin.'
|
||||
)}
|
||||
</sp-label>
|
||||
)}
|
||||
<div
|
||||
id="control_net_image_container"
|
||||
className="imgContainer controlNetImaageContainer"
|
||||
>
|
||||
<div className="imgButton">
|
||||
<button
|
||||
className="column-item btnSquare"
|
||||
id="bSetAllControlImage"
|
||||
onClick={this.onSetAllControlImage.bind(this)}
|
||||
>
|
||||
{Locale('Set All CtrlNet Images')}
|
||||
</button>
|
||||
</div>
|
||||
{/* <sp-checkbox id="chDisableControlNetTab"> */}
|
||||
<sp-checkbox
|
||||
checked={
|
||||
this.props.appState.disableControlNetTab
|
||||
? true
|
||||
: void 0
|
||||
}
|
||||
onClick={(
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
this.props.appState.disableControlNetTab =
|
||||
event.target.checked
|
||||
}}
|
||||
>
|
||||
{Locale('Disable ControlNet Tab')}
|
||||
</sp-checkbox>
|
||||
</div>
|
||||
<div>
|
||||
{Array(this.props.appState.maxControlNet)
|
||||
.fill(0)
|
||||
.map((v, index) => {
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[index]
|
||||
|
||||
let controlNetLabel = `${Locale(
|
||||
'ControlNet Unit'
|
||||
)} ${index}: ${
|
||||
storeData.module && storeData.module !== 'none'
|
||||
? `${storeData.module}`
|
||||
: ''
|
||||
}`
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
border: '2px solid #6d6c6c',
|
||||
padding: '3px',
|
||||
}}
|
||||
>
|
||||
<Collapsible
|
||||
defaultIsOpen={false}
|
||||
label={controlNetLabel}
|
||||
labelStyle={{ fontSize: '12px' }}
|
||||
containerStyle={{
|
||||
alignItems: 'center',
|
||||
backgroundColor: storeData.enabled
|
||||
? '#2c4639'
|
||||
: void 0,
|
||||
}}
|
||||
checkboxCallback={(checked) => {
|
||||
storeData.enabled = checked
|
||||
}}
|
||||
checked={storeData.enabled}
|
||||
>
|
||||
<div style={{ paddingTop: '10px' }}>
|
||||
<ControlNetUnit
|
||||
appState={this.props.appState}
|
||||
// key={index}
|
||||
index={index}
|
||||
/>
|
||||
</div>
|
||||
</Collapsible>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ControlNetTab
|
||||
|
|
@ -0,0 +1,853 @@
|
|||
import { observer } from 'mobx-react'
|
||||
import React from 'react'
|
||||
import {
|
||||
MoveToCanvasSvg,
|
||||
ActionButtonSVG,
|
||||
SpCheckBox,
|
||||
SpMenu,
|
||||
SpSlider,
|
||||
Thumbnail,
|
||||
PenSvg,
|
||||
PreviewSvg,
|
||||
SpSliderWithLabel,
|
||||
SliderType,
|
||||
} from '../util/elements'
|
||||
import ControlNetStore, { ControlnetMode, controlnetModes } from './store'
|
||||
import { mapRange, versionCompare } from './util'
|
||||
import {
|
||||
note,
|
||||
selection,
|
||||
html_manip,
|
||||
psapi,
|
||||
api,
|
||||
general,
|
||||
} from '../util/oldSystem'
|
||||
import Locale from '../locale/locale'
|
||||
import { requestControlNetFiltersKeywords } from './entry'
|
||||
|
||||
declare const g_generation_session: any
|
||||
declare const io: any
|
||||
declare const app: any
|
||||
declare let g_sd_url: string
|
||||
|
||||
@observer
|
||||
export default class ControlNetUnit extends React.Component<
|
||||
{ index: number; appState: typeof ControlNetStore },
|
||||
{}
|
||||
> {
|
||||
onEnableChange(event: any) {
|
||||
event.preventDefault()
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
storeData.enabled = !storeData.enabled
|
||||
}
|
||||
onLowVRamChange(event: any) {
|
||||
event.preventDefault()
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
storeData.lowvram = !storeData.lowvram
|
||||
}
|
||||
onGuessModeChange(event: any) {
|
||||
event.preventDefault()
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
storeData.guessmode = !storeData.guessmode
|
||||
}
|
||||
onPixelPerfectChange(event: any) {
|
||||
event.preventDefault()
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
console.log('onPixelPerfectChange', storeData.pixel_perfect)
|
||||
storeData.pixel_perfect = !storeData.pixel_perfect
|
||||
}
|
||||
onAutoImageChange(event: any) {
|
||||
event.preventDefault()
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
console.log('onAutoImageChange', storeData.auto_image)
|
||||
storeData.auto_image = !storeData.auto_image
|
||||
}
|
||||
onWeightMove(event: any) {
|
||||
event.preventDefault()
|
||||
if (event.target.tagName != 'SP-SLIDER') return
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
storeData.weight = +mapRange(
|
||||
event.target.value,
|
||||
0,
|
||||
200,
|
||||
0,
|
||||
2,
|
||||
0.01
|
||||
).toFixed(2)
|
||||
}
|
||||
onGuidanceStartMove(event: any) {
|
||||
event.preventDefault()
|
||||
if (event.target.tagName != 'SP-SLIDER') return
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
storeData.guidance_start = +mapRange(
|
||||
event.target.value,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
1,
|
||||
0.1
|
||||
).toFixed(1)
|
||||
}
|
||||
onGuidanceEndMove(event: any) {
|
||||
event.preventDefault()
|
||||
if (event.target.tagName != 'SP-SLIDER') return
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
storeData.guidance_end = +mapRange(
|
||||
event.target.value,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
1,
|
||||
0.1
|
||||
).toFixed(1)
|
||||
}
|
||||
async onFilterChange(
|
||||
event: any,
|
||||
{ index, item }: { index: number; item: string }
|
||||
) {
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
storeData.filter_keyword = item
|
||||
if (storeData.filter_keyword.toLowerCase() === 'none') {
|
||||
storeData.module_list = this.props.appState.supportedPreprocessors
|
||||
storeData.model_list = ['None'].concat(
|
||||
this.props.appState.supportedModels
|
||||
)
|
||||
} else {
|
||||
const filters = await requestControlNetFiltersKeywords(
|
||||
storeData.filter_keyword,
|
||||
this.props.appState.supportedPreprocessors,
|
||||
this.props.appState.supportedModels
|
||||
)
|
||||
|
||||
storeData.module_list = filters.module_list
|
||||
storeData.model_list = filters.model_list
|
||||
storeData.model = filters.default_model
|
||||
storeData.module = filters.default_option
|
||||
storeData.model = filters.default_model
|
||||
}
|
||||
}
|
||||
onPreprocsesorChange(
|
||||
event: any,
|
||||
{ index, item }: { index: number; item: string }
|
||||
) {
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
storeData.module = item
|
||||
}
|
||||
onModelChange(
|
||||
event: any,
|
||||
{ index, item }: { index: number; item: string }
|
||||
) {
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
storeData.model = item
|
||||
}
|
||||
onResolutionMove(event: any) {
|
||||
event.preventDefault()
|
||||
if (event.target.tagName != 'SP-SLIDER') return
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
let resolutionConfig =
|
||||
this.props.appState.preprocessorDetail[storeData.module] || {}
|
||||
let sliderConfig = resolutionConfig.sliders[0]
|
||||
storeData.processor_res = +(
|
||||
event.target.value * (sliderConfig.step || 1)
|
||||
)
|
||||
}
|
||||
onThresholdAMove(event: any) {
|
||||
event.preventDefault()
|
||||
if (event.target.tagName != 'SP-SLIDER') return
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
let resolutionConfig =
|
||||
this.props.appState.preprocessorDetail[storeData.module] || {}
|
||||
let sliderConfig = resolutionConfig.sliders[1]
|
||||
storeData.threshold_a = +(event.target.value * (sliderConfig.step || 1))
|
||||
}
|
||||
onThresholdBMove(event: any) {
|
||||
event.preventDefault()
|
||||
if (event.target.tagName != 'SP-SLIDER') return
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
let resolutionConfig =
|
||||
this.props.appState.preprocessorDetail[storeData.module] || {}
|
||||
let sliderConfig = resolutionConfig.sliders[2]
|
||||
storeData.threshold_b = +(event.target.value * (sliderConfig.step || 1))
|
||||
}
|
||||
async onSetImageButtonClick() {
|
||||
const selectionInfo = await selection.Selection.getSelectionInfoExe()
|
||||
if (selectionInfo) {
|
||||
const base64_image =
|
||||
await g_generation_session.setControlNetImageHelper()
|
||||
|
||||
this.props.appState.controlNetUnitData[
|
||||
this.props.index
|
||||
].input_image = base64_image
|
||||
} else {
|
||||
await note.Notification.inactiveSelectionArea()
|
||||
}
|
||||
}
|
||||
async onMaskButtonClick() {
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
if (g_generation_session.control_net_selection_info && storeData.mask) {
|
||||
const selection_info =
|
||||
g_generation_session.control_net_selection_info
|
||||
const layer = await io.IO.base64ToLayer(
|
||||
storeData.mask,
|
||||
'ControlNet Mask.png',
|
||||
selection_info.left,
|
||||
selection_info.top,
|
||||
selection_info.width,
|
||||
selection_info.height
|
||||
)
|
||||
} else {
|
||||
// await note.Notification.inactiveSelectionArea()
|
||||
app.showAlert('Mask Image is not available')
|
||||
}
|
||||
}
|
||||
|
||||
async requestControlNetDetectMap(
|
||||
controlnet_init_image: string,
|
||||
_module: string,
|
||||
processor_res: number,
|
||||
threshold_a: number,
|
||||
threshold_b: number
|
||||
) {
|
||||
try {
|
||||
const payload = {
|
||||
controlnet_module: _module,
|
||||
controlnet_input_images: [controlnet_init_image],
|
||||
controlnet_processor_res: processor_res,
|
||||
controlnet_threshold_a: threshold_a,
|
||||
controlnet_threshold_b: threshold_b,
|
||||
}
|
||||
const full_url = `${g_sd_url}/controlnet/detect`
|
||||
|
||||
const response_data = await api.requestPost(full_url, payload)
|
||||
|
||||
// update the mask preview with the new detectMap
|
||||
if (response_data['images'].length === 0) {
|
||||
app.showAlert(response_data['info'])
|
||||
}
|
||||
return response_data['images'][0]
|
||||
} catch (e) {
|
||||
console.warn('requestControlNetDetectMap(): ', _module, e)
|
||||
}
|
||||
}
|
||||
|
||||
async previewAnnotator() {
|
||||
const index = this.props.index
|
||||
try {
|
||||
const storeData = this.props.appState.controlNetUnitData[index]
|
||||
|
||||
const controlnet_init_image = storeData.input_image
|
||||
|
||||
const _module = storeData.module || 'none'
|
||||
const processor_res = storeData.processor_res
|
||||
const threshold_a = storeData.threshold_a
|
||||
const threshold_b = storeData.threshold_b
|
||||
|
||||
if (!controlnet_init_image) {
|
||||
const error = 'ControlNet initial image is empty'
|
||||
app.showAlert(error)
|
||||
throw error
|
||||
}
|
||||
if (!_module || _module === 'none') {
|
||||
const error = 'select a valid controlnet module (preprocessor)'
|
||||
app.showAlert(error)
|
||||
throw error
|
||||
}
|
||||
|
||||
const detect_map = await this.requestControlNetDetectMap(
|
||||
controlnet_init_image,
|
||||
_module,
|
||||
processor_res,
|
||||
threshold_a,
|
||||
threshold_b
|
||||
)
|
||||
|
||||
const rgb_detect_map_url =
|
||||
await io.convertBlackAndWhiteImageToRGBChannels3(detect_map)
|
||||
const rgb_detect_map = general.base64UrlToBase64(rgb_detect_map_url)
|
||||
// g_generation_session.controlNetMask[index] = rgb_detect_map
|
||||
storeData.detect_map = rgb_detect_map
|
||||
} catch (e) {
|
||||
console.warn('PreviewAnnotator click(): index: ', index, e)
|
||||
}
|
||||
}
|
||||
async setMask() {
|
||||
try {
|
||||
const selectionInfo = await psapi.getSelectionInfoExe()
|
||||
if (selectionInfo) {
|
||||
const mask_base64 = await io.getMaskFromCanvas()
|
||||
this.props.appState.controlNetUnitData[this.props.index].mask =
|
||||
mask_base64
|
||||
} else {
|
||||
// await note.Notification.inactiveSelectionArea()
|
||||
app.showAlert('No Selection is available')
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
async resetMask() {
|
||||
this.props.appState.controlNetUnitData[this.props.index].mask = ''
|
||||
}
|
||||
async toCanvas() {
|
||||
if (
|
||||
g_generation_session.control_net_preview_selection_info &&
|
||||
this.props.appState.controlNetUnitData[this.props.index].detect_map
|
||||
) {
|
||||
const selection_info =
|
||||
g_generation_session.control_net_preview_selection_info
|
||||
const layer = await io.IO.base64ToLayer(
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
.detect_map,
|
||||
'ControlNet Detection Map.png',
|
||||
selection_info.left,
|
||||
selection_info.top,
|
||||
selection_info.width,
|
||||
selection_info.height
|
||||
)
|
||||
} else {
|
||||
// await note.Notification.inactiveSelectionArea()
|
||||
app.showAlert('Detection Map is not available')
|
||||
}
|
||||
}
|
||||
async toControlNetInitImage() {
|
||||
const preview_result_base64 =
|
||||
g_generation_session.controlNetMask[this.props.index]
|
||||
g_generation_session.controlNetImage[this.props.index] =
|
||||
preview_result_base64
|
||||
g_generation_session.control_net_selection_info =
|
||||
g_generation_session.control_net_preview_selection_info
|
||||
|
||||
const rgb_detect_map_url =
|
||||
await io.convertBlackAndWhiteImageToRGBChannels3(
|
||||
preview_result_base64
|
||||
)
|
||||
|
||||
// g_generation_session.controlNetMask[index] = rgb_detect_map
|
||||
|
||||
// html_manip.setControlImageSrc(rgb_detect_map_url, this.props.index)
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
|
||||
storeData.input_image = storeData.detect_map
|
||||
}
|
||||
async previewAnnotatorFromCanvas() {
|
||||
try {
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
const _module = storeData.module || 'none'
|
||||
|
||||
const width = html_manip.getWidth()
|
||||
const height = html_manip.getHeight()
|
||||
const selectionInfo = await psapi.getSelectionInfoExe()
|
||||
g_generation_session.control_net_preview_selection_info =
|
||||
selectionInfo
|
||||
const base64 =
|
||||
await io.IO.getSelectionFromCanvasAsBase64Interface_New(
|
||||
width,
|
||||
height,
|
||||
selectionInfo,
|
||||
true
|
||||
)
|
||||
|
||||
if (!_module || _module === 'none') {
|
||||
const error = 'select a valid controlnet module (preprocessor)'
|
||||
app.showAlert(error)
|
||||
throw error
|
||||
}
|
||||
|
||||
const processor_res = storeData.processor_res
|
||||
const threshold_a = storeData.threshold_a
|
||||
const threshold_b = storeData.threshold_b
|
||||
|
||||
const detect_map = await this.requestControlNetDetectMap(
|
||||
base64,
|
||||
_module,
|
||||
processor_res,
|
||||
threshold_a,
|
||||
threshold_b
|
||||
)
|
||||
|
||||
const rgb_detect_map_url =
|
||||
await io.convertBlackAndWhiteImageToRGBChannels3(detect_map)
|
||||
g_generation_session.controlNetMask[this.props.index] = detect_map
|
||||
|
||||
storeData.detect_map = general.base64UrlToBase64(rgb_detect_map_url)
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
'PreviewAnnotator click(): index: ',
|
||||
this.props.index,
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const storeData =
|
||||
this.props.appState.controlNetUnitData[this.props.index]
|
||||
const pd =
|
||||
this.props.appState.preprocessorDetail[storeData.module] || {}
|
||||
const ppSlider = pd.sliders || []
|
||||
|
||||
return (
|
||||
<div id={`controlnet_settings_${this.props.index}`}>
|
||||
{/* <div className="flexContainer">
|
||||
<SpCheckBox
|
||||
style={{ marginRight: '10px' }}
|
||||
onChange={this.onEnableChange.bind(this)}
|
||||
checked={storeData.enabled}
|
||||
id={`chEnableControlNet_${this.props.index}`}
|
||||
value={
|
||||
this.props.appState.controlNetUnitData[
|
||||
this.props.index
|
||||
].enabled
|
||||
}
|
||||
>
|
||||
ControlNet Unit {this.props.index}{' '}
|
||||
{storeData.module && storeData.module !== 'none'
|
||||
? `(${storeData.module})`
|
||||
: void 0}
|
||||
</SpCheckBox>
|
||||
</div> */}
|
||||
<div
|
||||
style={{
|
||||
display: 'block',
|
||||
// display: storeData.enabled ? 'block' : 'none'
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div
|
||||
id={`control_net_image_container_${this.props.index}`}
|
||||
className="imgContainer controlNetImaageContainer"
|
||||
>
|
||||
<div>
|
||||
<img
|
||||
id={`control_net_image_${this.props.index}`}
|
||||
className="column-item-image"
|
||||
src={
|
||||
storeData.input_image
|
||||
? 'data:image/png;base64,' +
|
||||
storeData.input_image
|
||||
: 'https://source.unsplash.com/random'
|
||||
}
|
||||
width="300px"
|
||||
height="100px"
|
||||
/>
|
||||
</div>
|
||||
<div className="imgButton">
|
||||
<button
|
||||
className="column-item button-style btnSquare"
|
||||
id={`bSetControlImage_${this.props.index}`}
|
||||
onClick={this.onSetImageButtonClick.bind(
|
||||
this
|
||||
)}
|
||||
title="Set CtrlNet Img"
|
||||
>
|
||||
{Locale('Set CtrlImg')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id={`control_net_mask_container_${this.props.index}`}
|
||||
className="imgContainer controlNetImaageContainer"
|
||||
>
|
||||
<div>
|
||||
<Thumbnail>
|
||||
<img
|
||||
id={`control_net_mask_${this.props.index}`}
|
||||
className="column-item-image"
|
||||
src={
|
||||
storeData.detect_map
|
||||
? 'data:image/png;base64,' +
|
||||
storeData.detect_map
|
||||
: 'https://source.unsplash.com/random'
|
||||
}
|
||||
width="300px"
|
||||
height="100px"
|
||||
/>
|
||||
<ActionButtonSVG
|
||||
ComponentType={PenSvg}
|
||||
onClick={this.toControlNetInitImage.bind(
|
||||
this
|
||||
)}
|
||||
title={Locale(
|
||||
'use as controlnet input image'
|
||||
)}
|
||||
></ActionButtonSVG>
|
||||
<ActionButtonSVG
|
||||
ComponentType={MoveToCanvasSvg}
|
||||
onClick={this.toCanvas.bind(this)}
|
||||
title={Locale('Copy Image to Canvas')}
|
||||
></ActionButtonSVG>
|
||||
<ActionButtonSVG
|
||||
ComponentType={PreviewSvg}
|
||||
onClick={this.previewAnnotatorFromCanvas.bind(
|
||||
this
|
||||
)}
|
||||
title={Locale(
|
||||
'Preview Annotation From the Selected Area on Canvas'
|
||||
)}
|
||||
></ActionButtonSVG>
|
||||
</Thumbnail>
|
||||
</div>
|
||||
<div className="imgButton btnClass">
|
||||
<button
|
||||
className="column-item button-style btnSquare"
|
||||
id={`bControlMask_${this.props.index}`}
|
||||
onClick={this.previewAnnotator.bind(this)}
|
||||
title="Preview Annotator"
|
||||
>
|
||||
{Locale('Preview Annotator')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!this.props.appState.controlNetUnitData[
|
||||
this.props.index
|
||||
].model
|
||||
.toLowerCase()
|
||||
.includes('inpaint') ? (
|
||||
void 0
|
||||
) : (
|
||||
<div className="imgContainer controlNetImaageContainer">
|
||||
<div>
|
||||
<Thumbnail>
|
||||
<img
|
||||
className="column-item-image"
|
||||
src={
|
||||
storeData.mask
|
||||
? 'data:image/png;base64,' +
|
||||
storeData.mask
|
||||
: 'https://source.unsplash.com/random'
|
||||
}
|
||||
width="300px"
|
||||
height="100px"
|
||||
/>
|
||||
|
||||
<ActionButtonSVG
|
||||
ComponentType={PenSvg}
|
||||
onClick={this.setMask.bind(this)}
|
||||
title={Locale(
|
||||
'set the mask for controlnet inpaint mode'
|
||||
)}
|
||||
></ActionButtonSVG>
|
||||
<ActionButtonSVG
|
||||
ComponentType={PenSvg}
|
||||
onClick={this.resetMask.bind(this)}
|
||||
title={Locale('reset the mask')}
|
||||
></ActionButtonSVG>
|
||||
</Thumbnail>
|
||||
</div>
|
||||
<div className="imgButton btnClass">
|
||||
<button
|
||||
className="column-item button-style btnSquare"
|
||||
id={`bControlMask_${this.props.index}`}
|
||||
onClick={this.setMask.bind(this)}
|
||||
title="Preview Annotator"
|
||||
>
|
||||
{Locale('Set Mask')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<SpCheckBox
|
||||
style={{ marginRight: '10px' }}
|
||||
onChange={this.onLowVRamChange.bind(this)}
|
||||
checked={storeData.lowvram}
|
||||
id={`chlowVram_${this.props.index}`}
|
||||
>
|
||||
{Locale('Low VRAM')}
|
||||
</SpCheckBox>
|
||||
<SpCheckBox
|
||||
style={{
|
||||
display:
|
||||
this.props.appState.controlnetApiVersion > 1
|
||||
? 'none'
|
||||
: void 0,
|
||||
marginRight: '10px',
|
||||
}}
|
||||
onChange={this.onGuessModeChange.bind(this)}
|
||||
checked={storeData.guessmode}
|
||||
id={`chGuessMode_${this.props.index}`}
|
||||
>
|
||||
{Locale('Guess Mode')}
|
||||
</SpCheckBox>
|
||||
<SpCheckBox
|
||||
style={{
|
||||
display:
|
||||
this.props.appState.controlnetApiVersion > 1
|
||||
? void 0
|
||||
: 'none',
|
||||
marginRight: '10px',
|
||||
}}
|
||||
onChange={this.onPixelPerfectChange.bind(this)}
|
||||
checked={storeData.pixel_perfect}
|
||||
id={`chPixelPerfect_${this.props.index}`}
|
||||
>
|
||||
{Locale('Pixel Perfect')}
|
||||
</SpCheckBox>
|
||||
<SpCheckBox
|
||||
style={{
|
||||
marginRight: '10px',
|
||||
}}
|
||||
onChange={this.onAutoImageChange.bind(this)}
|
||||
checked={storeData.auto_image}
|
||||
// id={`chPixelPerfect_${this.props.index}`}
|
||||
title={Locale(
|
||||
'load the input image from canvas automatically'
|
||||
)}
|
||||
>
|
||||
{
|
||||
//@ts-ignore
|
||||
Locale('Auto Image')
|
||||
}
|
||||
</SpCheckBox>
|
||||
{this.props.appState.controlnetApiVersion > 1 && (
|
||||
<sp-radio-group
|
||||
style={{ display: 'flex' }}
|
||||
selected={
|
||||
this.props.appState.controlNetUnitData[
|
||||
this.props.index
|
||||
].control_mode
|
||||
}
|
||||
onClick={(event: any) => {
|
||||
this.props.appState.controlNetUnitData[
|
||||
this.props.index
|
||||
].control_mode = event.target.value
|
||||
}}
|
||||
>
|
||||
<sp-label slot="label">
|
||||
{Locale('Control Mode')}
|
||||
</sp-label>
|
||||
{controlnetModes.map(
|
||||
(mode: ControlnetMode, index: number) => {
|
||||
console.log('mode:', mode, ' index:', index)
|
||||
return (
|
||||
<sp-radio
|
||||
key={`mode-${index}`}
|
||||
checked={
|
||||
this.props.appState
|
||||
.controlNetUnitData[
|
||||
this.props.index
|
||||
].control_mode === mode
|
||||
? true
|
||||
: void 0
|
||||
}
|
||||
value={`${mode}`}
|
||||
>
|
||||
{Locale(mode)}
|
||||
</sp-radio>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</sp-radio-group>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<SpSlider
|
||||
show-value="false"
|
||||
min={0}
|
||||
max={200}
|
||||
value={storeData.weight * 100}
|
||||
onInput={this.onWeightMove.bind(this)}
|
||||
title="2 will keep the composition; 0 will allow composition to change"
|
||||
>
|
||||
<sp-label slot="label">
|
||||
{Locale('Control Weight')}
|
||||
</sp-label>
|
||||
<sp-label slot="label">
|
||||
{storeData.weight}
|
||||
</sp-label>
|
||||
</SpSlider>
|
||||
<SpSlider
|
||||
show-value="false"
|
||||
min="0"
|
||||
max="10"
|
||||
value={
|
||||
+mapRange(
|
||||
storeData.guidance_start,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
1
|
||||
).toFixed(1)
|
||||
}
|
||||
onInput={this.onGuidanceStartMove.bind(this)}
|
||||
>
|
||||
<sp-label slot="label">
|
||||
{Locale('Guidance Start (T)')}
|
||||
</sp-label>
|
||||
<sp-label
|
||||
slot="label"
|
||||
id={`lControlNetGuidanceStrengthStart_${this.props.index}`}
|
||||
>
|
||||
{storeData.guidance_start}
|
||||
</sp-label>
|
||||
</SpSlider>
|
||||
<SpSlider
|
||||
show-value="false"
|
||||
min="0"
|
||||
max="10"
|
||||
value={
|
||||
+mapRange(
|
||||
storeData.guidance_end,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
1
|
||||
).toFixed(1)
|
||||
}
|
||||
onInput={this.onGuidanceEndMove.bind(this)}
|
||||
>
|
||||
<sp-label slot="label">
|
||||
{Locale('Guidance End (T)')}
|
||||
</sp-label>
|
||||
<sp-label
|
||||
slot="label"
|
||||
id={`lControlNetGuidanceStrengthEnd_${this.props.index}`}
|
||||
>
|
||||
{storeData.guidance_end}
|
||||
</sp-label>
|
||||
</SpSlider>
|
||||
{ppSlider &&
|
||||
ppSlider[0] &&
|
||||
!storeData.pixel_perfect && (
|
||||
<SpSlider
|
||||
show-value="false"
|
||||
min={
|
||||
ppSlider[0].min /
|
||||
(ppSlider[0].step || 1)
|
||||
}
|
||||
max={
|
||||
ppSlider[0].max /
|
||||
(ppSlider[0].step || 1)
|
||||
}
|
||||
value={
|
||||
storeData.processor_res /
|
||||
(ppSlider[0].step || 1)
|
||||
}
|
||||
onInput={this.onResolutionMove.bind(
|
||||
this
|
||||
)}
|
||||
>
|
||||
<sp-label slot="label">
|
||||
{ppSlider[0].name}:
|
||||
</sp-label>
|
||||
<sp-label slot="label">
|
||||
{storeData.processor_res.toFixed(2)}
|
||||
</sp-label>
|
||||
</SpSlider>
|
||||
)}
|
||||
{ppSlider && ppSlider[1] && (
|
||||
<SpSlider
|
||||
show-value="false"
|
||||
min={
|
||||
ppSlider[1].min /
|
||||
(ppSlider[1].step || 1)
|
||||
}
|
||||
max={
|
||||
ppSlider[1].max /
|
||||
(ppSlider[1].step || 1)
|
||||
}
|
||||
value={
|
||||
storeData.threshold_a /
|
||||
(ppSlider[1].step || 1)
|
||||
}
|
||||
onInput={this.onThresholdAMove.bind(this)}
|
||||
>
|
||||
<sp-label slot="label">
|
||||
{ppSlider[1].name}:
|
||||
</sp-label>
|
||||
<sp-label slot="label">
|
||||
{storeData.threshold_a.toFixed(2)}
|
||||
</sp-label>
|
||||
</SpSlider>
|
||||
)}
|
||||
{ppSlider && ppSlider[2] && (
|
||||
<SpSlider
|
||||
show-value="false"
|
||||
min={
|
||||
ppSlider[2].min /
|
||||
(ppSlider[2].step || 1)
|
||||
}
|
||||
max={
|
||||
ppSlider[2].max /
|
||||
(ppSlider[2].step || 1)
|
||||
}
|
||||
value={
|
||||
storeData.threshold_b /
|
||||
(ppSlider[2].step || 1)
|
||||
}
|
||||
onInput={this.onThresholdBMove.bind(this)}
|
||||
>
|
||||
<sp-label slot="label">
|
||||
{ppSlider[2].name}:
|
||||
</sp-label>
|
||||
<sp-label slot="label">
|
||||
{storeData.threshold_b.toFixed(2)}
|
||||
</sp-label>
|
||||
</SpSlider>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<SpMenu
|
||||
onChange={this.onFilterChange.bind(this)}
|
||||
items={this.props.appState.filterKeywords}
|
||||
label_item={Locale('Select Filter')}
|
||||
selected_index={this.props.appState.filterKeywords.indexOf(
|
||||
storeData.filter_keyword || 'All'
|
||||
)}
|
||||
style={{ width: '50%', display: 'flex' }}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
id={`menu-bar-control_net_${this.props.index}`}
|
||||
style={{ display: 'flex' }}
|
||||
>
|
||||
<SpMenu
|
||||
onChange={this.onPreprocsesorChange.bind(this)}
|
||||
id={`mModulesMenuControlNet_${this.props.index}`}
|
||||
items={storeData.module_list || ['none']}
|
||||
label_item={Locale('Select Module')}
|
||||
selected_index={storeData.module_list?.indexOf(
|
||||
storeData.module
|
||||
)}
|
||||
style={{ width: '100%', display: 'flex' }}
|
||||
/>
|
||||
{!pd.model_free && (
|
||||
<SpMenu
|
||||
onChange={this.onModelChange.bind(this)}
|
||||
id={`mModelsMenuControlNet_${this.props.index}`}
|
||||
items={storeData.model_list || []}
|
||||
label_item={Locale('Select Module')}
|
||||
selected_index={storeData.model_list?.indexOf(
|
||||
storeData.model
|
||||
)}
|
||||
style={{ width: '100%', display: 'flex' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
import { setControlImageSrc } from '../../utility/html_manip'
|
||||
import { session_ts } from '../entry'
|
||||
import { Enum, api, python_replacement } from '../util/oldSystem'
|
||||
import { GenerationModeEnum } from '../util/ts/enum'
|
||||
import { store, versionCompare } from './main'
|
||||
|
||||
const { getExtensionUrl } = python_replacement
|
||||
declare const g_sd_config_obj: any
|
||||
declare let g_sd_url: string
|
||||
|
||||
async function requestControlNetPreprocessors() {
|
||||
const control_net_json = await api.requestGet(
|
||||
`${g_sd_url}/controlnet/module_list?alias_name=1`
|
||||
)
|
||||
|
||||
return control_net_json
|
||||
}
|
||||
async function requestControlNetModelList(): Promise<any> {
|
||||
const control_net_json = await api.requestGet(
|
||||
`${g_sd_url}/controlnet/model_list`
|
||||
)
|
||||
|
||||
const model_list = control_net_json?.model_list
|
||||
return model_list
|
||||
}
|
||||
async function requestControlNetApiVersion() {
|
||||
const json = await api.requestGet(`${g_sd_url}/controlnet/version`)
|
||||
|
||||
const version = json?.version
|
||||
|
||||
return version
|
||||
}
|
||||
async function requestControlNetMaxUnits() {
|
||||
const json = await api.requestGet(`${g_sd_url}/controlnet/settings`)
|
||||
|
||||
const control_net_max_models_num = json?.control_net_max_models_num ?? 0
|
||||
|
||||
return control_net_max_models_num
|
||||
}
|
||||
|
||||
async function requestControlNetFiltersKeywords(
|
||||
keyword = 'All',
|
||||
module_list: string[],
|
||||
model_list: string[]
|
||||
) {
|
||||
try {
|
||||
const extension_url = getExtensionUrl()
|
||||
// const full_url = `${extension_url}/controlnet/filter?keyword=${keyword}`
|
||||
|
||||
const full_url = `${extension_url}/controlnet/filter`
|
||||
|
||||
const payload = {
|
||||
keyword: keyword,
|
||||
preprocessor_list: module_list,
|
||||
model_list: model_list,
|
||||
}
|
||||
//const full_url = `${g_sd_url}/controlnet/filter?keyword=${keyword}`
|
||||
const control_net_json = await api.requestPost(full_url, payload)
|
||||
|
||||
return control_net_json
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
async function initializeControlNetTab(controlnet_max_models: number) {
|
||||
store.maxControlNet = controlnet_max_models || store.maxControlNet
|
||||
store.controlnetApiVersion = await requestControlNetApiVersion()
|
||||
|
||||
try {
|
||||
const models = await requestControlNetModelList()
|
||||
store.supportedModels = models || []
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
try {
|
||||
const pps = await requestControlNetPreprocessors()
|
||||
store.supportedPreprocessors = pps ? pps.module_list : []
|
||||
store.preprocessorDetail = pps ? pps.module_detail : {}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
try {
|
||||
//retrieve all keywords to popular the dropdown menu
|
||||
|
||||
const filters = await requestControlNetFiltersKeywords(
|
||||
'All',
|
||||
store.supportedPreprocessors,
|
||||
store.supportedModels
|
||||
)
|
||||
|
||||
store.filterKeywords = filters
|
||||
? ['none'].concat(filters.keywords)
|
||||
: ['none']
|
||||
if (filters) {
|
||||
store.controlNetUnitData.forEach((unitData) => {
|
||||
unitData.module_list = filters.module_list
|
||||
unitData.model_list = filters.model_list
|
||||
unitData.model = filters.default_model
|
||||
unitData.module = filters.default_option
|
||||
unitData.model = filters.default_model
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
function getEnableControlNet(index: number) {
|
||||
if (typeof index == 'undefined')
|
||||
return (
|
||||
store.controlNetUnitData.filter((item) => item.enabled).length > 0
|
||||
)
|
||||
else return store.controlNetUnitData[index || 0].enabled
|
||||
}
|
||||
function mapPluginSettingsToControlNet(plugin_settings: any) {
|
||||
const ps = plugin_settings // for shortness
|
||||
let controlnet_units: any[] = []
|
||||
function getControlNetInputImage(index: number) {
|
||||
try {
|
||||
const b_sync_input_image =
|
||||
store.controlNetUnitData[index].auto_image
|
||||
let input_image = store.controlNetUnitData[index].input_image
|
||||
if (
|
||||
b_sync_input_image &&
|
||||
[GenerationModeEnum.Txt2Img].includes(
|
||||
session_ts.store.data.mode
|
||||
)
|
||||
) {
|
||||
//conditions: 1) txt2img mode 2)auto image on 3)first generation of session
|
||||
|
||||
input_image = session_ts.store.data.controlnet_input_image ?? ''
|
||||
store.controlNetUnitData[index].input_image = input_image
|
||||
}
|
||||
if (
|
||||
b_sync_input_image &&
|
||||
[
|
||||
GenerationModeEnum.Img2Img,
|
||||
GenerationModeEnum.Inpaint,
|
||||
GenerationModeEnum.Outpaint,
|
||||
GenerationModeEnum.LassoInpaint,
|
||||
].includes(session_ts.store.data.mode)
|
||||
) {
|
||||
// img2img mode
|
||||
input_image = session_ts.store.data.init_image
|
||||
store.controlNetUnitData[index].input_image = input_image
|
||||
} else if (
|
||||
b_sync_input_image &&
|
||||
store.controlNetUnitData[index].enabled
|
||||
) {
|
||||
//txt2img mode
|
||||
}
|
||||
|
||||
return input_image
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
function getControlNetMask(index: number) {
|
||||
try {
|
||||
// const b_sync_input_image =
|
||||
// store.controlNetUnitData[index].auto_image
|
||||
// let mask = store.controlNetUnitData[index].mask // the user created mask
|
||||
// if (b_sync_input_image && session_ts.store.data.expanded_mask) {
|
||||
// //use the mask from inpaint and outpaint mode
|
||||
// mask = '' // this will tell controlnet to use SD mask
|
||||
// store.controlNetUnitData[index].mask = ''
|
||||
// }
|
||||
|
||||
if (
|
||||
[
|
||||
GenerationModeEnum.Txt2Img,
|
||||
GenerationModeEnum.Img2Img,
|
||||
].includes(session_ts.store.data.mode)
|
||||
) {
|
||||
//maskless mode
|
||||
} else {
|
||||
//mask related mode
|
||||
store.controlNetUnitData[index].mask = '' // use the mask from the sd mode
|
||||
}
|
||||
return store.controlNetUnitData[index].mask
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
for (let index = 0; index < store.maxControlNet; index++) {
|
||||
controlnet_units[index] = {
|
||||
enabled: getEnableControlNet(index),
|
||||
input_image: getControlNetInputImage(index),
|
||||
mask: getControlNetMask(index),
|
||||
module: store.controlNetUnitData[index].module,
|
||||
model: store.controlNetUnitData[index].model,
|
||||
weight: store.controlNetUnitData[index].weight,
|
||||
resize_mode: 'Scale to Fit (Inner Fit)',
|
||||
lowvram: store.controlNetUnitData[index].lowvram,
|
||||
processor_res: store.controlNetUnitData[index].processor_res || 512,
|
||||
threshold_a: store.controlNetUnitData[index].threshold_a,
|
||||
threshold_b: store.controlNetUnitData[index].threshold_b,
|
||||
// guidance: ,
|
||||
guidance_start: store.controlNetUnitData[index].guidance_start,
|
||||
guidance_end: store.controlNetUnitData[index].guidance_end,
|
||||
}
|
||||
if (store.controlnetApiVersion > 1) {
|
||||
//new controlnet v2
|
||||
controlnet_units[index].control_mode =
|
||||
store.controlNetUnitData[index].control_mode
|
||||
controlnet_units[index].pixel_perfect =
|
||||
store.controlNetUnitData[index].pixel_perfect
|
||||
} else {
|
||||
// old controlnet v1
|
||||
controlnet_units[index].guessmode =
|
||||
store.controlNetUnitData[index].guessmode
|
||||
}
|
||||
}
|
||||
|
||||
const controlnet_payload = {
|
||||
...ps,
|
||||
controlnet_units, //keep for backward compatibility for now
|
||||
subseed: -1,
|
||||
override_settings: {},
|
||||
override_settings_restore_afterwards: true,
|
||||
alwayson_scripts: {
|
||||
...(ps?.alwayson_scripts || {}),
|
||||
controlnet: {
|
||||
args: controlnet_units,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return controlnet_payload
|
||||
}
|
||||
function getControlNetMaxModelsNumber() {
|
||||
return store.maxControlNet
|
||||
}
|
||||
function getUnitsData() {
|
||||
return store.controlNetUnitData
|
||||
}
|
||||
function setControlDetectMapSrc(base64: string, index: number) {
|
||||
// store.controlNetUnitData[index].mask = base64
|
||||
store.controlNetUnitData[index].detect_map = base64
|
||||
}
|
||||
function setControlInputImageSrc(base64: string, index: number) {
|
||||
store.controlNetUnitData[index].input_image = base64
|
||||
}
|
||||
function isControlNetModeEnable() {
|
||||
let is_tab_enabled = !store.disableControlNetTab
|
||||
|
||||
let numOfEnabled = 0
|
||||
if (is_tab_enabled) {
|
||||
for (let index = 0; index < store.maxControlNet; index++) {
|
||||
if (getEnableControlNet(index)) {
|
||||
numOfEnabled += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
let is_mode_enabled = is_tab_enabled // could be true
|
||||
if (is_tab_enabled === false || numOfEnabled === 0) {
|
||||
is_mode_enabled = false
|
||||
}
|
||||
return is_mode_enabled
|
||||
}
|
||||
function getModuleDetail() {
|
||||
return store.preprocessorDetail
|
||||
}
|
||||
export {
|
||||
requestControlNetModelList,
|
||||
requestControlNetMaxUnits,
|
||||
requestControlNetFiltersKeywords,
|
||||
initializeControlNetTab,
|
||||
getEnableControlNet,
|
||||
mapPluginSettingsToControlNet,
|
||||
getControlNetMaxModelsNumber,
|
||||
getUnitsData,
|
||||
setControlDetectMapSrc,
|
||||
setControlInputImageSrc,
|
||||
isControlNetModeEnable,
|
||||
getModuleDetail,
|
||||
store,
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
import ReactDOM from 'react-dom/client'
|
||||
import React from 'react'
|
||||
import ControlNetTab from './ControlNetTab'
|
||||
import store from './store'
|
||||
import { versionCompare } from './util'
|
||||
import Collapsible from '../after_detailer/after_detailer'
|
||||
import Locale from '../locale/locale'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
|
||||
const elem = document.getElementById('sp-control_net-tab-page')
|
||||
const elem2 = document.getElementById('sp-control_net-tab-page2')
|
||||
|
||||
if (elem) {
|
||||
const root = ReactDOM.createRoot(elem)
|
||||
root.render(
|
||||
<ErrorBoundary>
|
||||
<ControlNetTab appState={store} />
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
||||
if (elem2) {
|
||||
const root = ReactDOM.createRoot(elem2)
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<div
|
||||
style={{
|
||||
border: '2px solid #6d6c6c',
|
||||
padding: '3px',
|
||||
}}
|
||||
>
|
||||
<Collapsible
|
||||
defaultIsOpen={true}
|
||||
label={Locale('ControlNet Tab')}
|
||||
>
|
||||
<div
|
||||
id="controlNetTabParentContainer"
|
||||
style={{ marginTop: '10px' }}
|
||||
>
|
||||
<ControlNetTab appState={store} />
|
||||
</div>
|
||||
</Collapsible>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
}
|
||||
function scrollToEnabledControlNetUnit() {}
|
||||
|
||||
const button = document.getElementById('scrollToControlNetUnitContainer')!
|
||||
const button_root = ReactDOM.createRoot(button)
|
||||
let controlnet_unit_index = 0
|
||||
button_root.render(
|
||||
<ErrorBoundary>
|
||||
<button
|
||||
className="btnSquare svgButton"
|
||||
onClick={() => {
|
||||
try {
|
||||
const units = document.querySelectorAll(
|
||||
'#controlNetTabParentContainer .collapsible'
|
||||
)
|
||||
const units_data = store.controlNetUnitData.map(
|
||||
(data, index) => ({
|
||||
enabled: data.enabled,
|
||||
index,
|
||||
})
|
||||
)
|
||||
|
||||
// Find the next enabled unit
|
||||
let counter = 0
|
||||
while (
|
||||
!units_data[controlnet_unit_index % units.length]
|
||||
.enabled &&
|
||||
counter < units.length
|
||||
) {
|
||||
controlnet_unit_index += 1
|
||||
counter += 1
|
||||
}
|
||||
|
||||
if (counter < units.length) {
|
||||
controlnet_unit_index =
|
||||
controlnet_unit_index % units.length
|
||||
units[controlnet_unit_index].scrollIntoView()
|
||||
controlnet_unit_index += 1
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}}
|
||||
title="Quickly jump to the active ControlNet Unit"
|
||||
>
|
||||
C
|
||||
</button>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
|
||||
export { store, versionCompare }
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
import { observable, reaction } from 'mobx'
|
||||
|
||||
export const DefaultControlNetUnitData = {
|
||||
enabled: false,
|
||||
input_image: '',
|
||||
mask: '',
|
||||
detect_map: '',
|
||||
module: '',
|
||||
model: '',
|
||||
weight: 1.0,
|
||||
resize_mode: 'Scale to Fit (Inner Fit)',
|
||||
lowvram: true,
|
||||
processor_res: 512,
|
||||
threshold_a: 0,
|
||||
threshold_b: 0,
|
||||
|
||||
guidance_start: 0,
|
||||
guidance_end: 1,
|
||||
guessmode: false,
|
||||
|
||||
control_mode: 'Balanced',
|
||||
pixel_perfect: true,
|
||||
auto_image: true,
|
||||
}
|
||||
|
||||
export const controlnetModes = [
|
||||
'Balanced',
|
||||
'My prompt is more important',
|
||||
'ControlNet is more important',
|
||||
] as const
|
||||
export type ControlnetMode = (typeof controlnetModes)[number]
|
||||
|
||||
export interface controlNetUnitData {
|
||||
enabled: boolean
|
||||
input_image: string
|
||||
mask: string
|
||||
detect_map: string
|
||||
module_list: string[]
|
||||
model_list: string[]
|
||||
module: string
|
||||
model: string
|
||||
filter_keyword: string
|
||||
weight: number
|
||||
resize_mode: 'Just Resize' | 'Crop and Resize' | 'Resize and Fill'
|
||||
lowvram: boolean
|
||||
processor_res: number
|
||||
threshold_a: number
|
||||
threshold_b: number
|
||||
|
||||
guidance_start: number
|
||||
guidance_end: number
|
||||
guessmode: boolean
|
||||
|
||||
control_mode: ControlnetMode
|
||||
pixel_perfect: boolean
|
||||
auto_image: boolean // sync CtrlNet image with sd input image
|
||||
}
|
||||
interface ControlNetMobxStore {
|
||||
disableControlNetTab: boolean
|
||||
maxControlNet: number
|
||||
controlnetApiVersion: number
|
||||
|
||||
supportedModels: string[]
|
||||
supportedPreprocessors: string[]
|
||||
filterKeywords: string[]
|
||||
preprocessorDetail: { [key: string]: any }
|
||||
|
||||
controlNetUnitData: controlNetUnitData[]
|
||||
}
|
||||
|
||||
var ControlNetStore = observable<ControlNetMobxStore>({
|
||||
disableControlNetTab: false,
|
||||
maxControlNet: 0,
|
||||
controlnetApiVersion: 1,
|
||||
|
||||
supportedModels: [],
|
||||
supportedPreprocessors: [],
|
||||
filterKeywords: [],
|
||||
preprocessorDetail: {},
|
||||
|
||||
controlNetUnitData: [],
|
||||
})
|
||||
|
||||
reaction(
|
||||
() => {
|
||||
return ControlNetStore.controlNetUnitData.map((data) => data.module)
|
||||
},
|
||||
(module_, index) => {
|
||||
ControlNetStore.controlNetUnitData.forEach((data, index) => {
|
||||
const pd = ControlNetStore.preprocessorDetail[module_[index]] || {}
|
||||
const pSlider = pd.sliders || []
|
||||
data.processor_res = pSlider[0]?.value || 512
|
||||
data.threshold_a = pSlider[1]?.value || 0
|
||||
data.threshold_b = pSlider[2]?.value || 0
|
||||
})
|
||||
}
|
||||
)
|
||||
reaction(
|
||||
() => ControlNetStore.maxControlNet,
|
||||
(maxControlNet) => {
|
||||
ControlNetStore.controlNetUnitData = Array(maxControlNet)
|
||||
.fill(0)
|
||||
.map((v, index) => {
|
||||
return (
|
||||
ControlNetStore.controlNetUnitData[index] ||
|
||||
DefaultControlNetUnitData
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
export default ControlNetStore
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
export function mapRange(
|
||||
x: number,
|
||||
in_min: number,
|
||||
in_max: number,
|
||||
out_min: number,
|
||||
out_max: number,
|
||||
step: number
|
||||
) {
|
||||
return (
|
||||
Math.round(
|
||||
(((x - in_min) * (out_max - out_min)) / (in_max - in_min) +
|
||||
out_min) /
|
||||
step
|
||||
) * step
|
||||
)
|
||||
}
|
||||
|
||||
export function versionCompare(to: string, from: string) {
|
||||
const vTo = to.split('.')
|
||||
const vFrom = from.split('.')
|
||||
|
||||
for (let i = 0; i < Math.max(vTo.length, vFrom.length); i++) {
|
||||
const vFromI = +(vFrom[i] || 0)
|
||||
const vToI = +(vTo[i] || 0)
|
||||
if (isNaN(vFromI) || isNaN(vToI)) {
|
||||
throw new Error(`invalid version ${vTo} or ${vFrom} `)
|
||||
}
|
||||
|
||||
if (vFromI > vToI) {
|
||||
return -1
|
||||
} else if (vFromI < vToI) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// import { configure } from 'mobx'
|
||||
// configure({
|
||||
// enforceActions: 'never', // disable mobx warning temporarily
|
||||
// })
|
||||
export * as control_net from './controlnet/entry'
|
||||
export * as after_detailer_script from './after_detailer/after_detailer'
|
||||
export * as ultimate_sd_upscaler from './ultimate_sd_upscaler/ultimate_sd_upscaler'
|
||||
export * as scripts from './ultimate_sd_upscaler/scripts'
|
||||
export * as main from './main/main'
|
||||
export * as logger from './util/logger'
|
||||
export * as image_search from './image_search/image_search'
|
||||
export * as history from './history/history'
|
||||
export * as viewer from './viewer/viewer'
|
||||
export * as session_ts from './session/session'
|
||||
export * as progress from './session/progress'
|
||||
export * as preview from './viewer/preview'
|
||||
export * as generate from './session/generate'
|
||||
export * as sd_tab_ts from './sd_tab/sd_tab'
|
||||
export * as sam from './sam/sam'
|
||||
export * as settings_tab_ts from './settings/settings'
|
||||
export * as one_button_prompt from './one_button_prompt/one_button_prompt'
|
||||
export * as enum_ts from './util/ts/enum'
|
||||
export { toJS } from 'mobx'
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { observable } from 'mobx'
|
||||
import { host } from 'uxp'
|
||||
|
||||
interface GlobalStore {
|
||||
Locale: 'zh_CN' | 'en_US'
|
||||
}
|
||||
|
||||
const initialLocale =
|
||||
localStorage.getItem('last_selected_locale') || host.uiLocale
|
||||
var globalStore = observable<GlobalStore>({
|
||||
Locale: initialLocale == 'zh_CN' ? initialLocale : 'en_US',
|
||||
})
|
||||
|
||||
export default globalStore
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import { observer } from 'mobx-react'
|
||||
import { AStore, toJS } from '../main/astore'
|
||||
import { Grid } from '../util/grid'
|
||||
import { io, settings_tab } from '../util/oldSystem'
|
||||
import { MoveToCanvasSvg, PenSvg } from '../util/elements'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
import Locale from '../locale/locale'
|
||||
|
||||
declare let g_ui_settings_object: any
|
||||
export const store = new AStore({
|
||||
images: [],
|
||||
refresh: false,
|
||||
width: 50,
|
||||
height: 50,
|
||||
scale: 1,
|
||||
metadata_jsons: [],
|
||||
})
|
||||
|
||||
async function moveHistoryImageToLayer(
|
||||
base64_image: string,
|
||||
selection_info: any
|
||||
) {
|
||||
try {
|
||||
const to_x = selection_info?.left
|
||||
const to_y = selection_info?.top
|
||||
const width = selection_info?.width
|
||||
const height = selection_info?.height
|
||||
await io.IO.base64ToLayer(
|
||||
base64_image,
|
||||
'History Image',
|
||||
to_x,
|
||||
to_y,
|
||||
width,
|
||||
height
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
function getHistoryMetadata(metadata_json: any) {
|
||||
//auto fill the ui with metadata
|
||||
// const metadata_json = JSON.parse(img.dataset.metadata_json_string)
|
||||
|
||||
console.log('metadata_json: ', metadata_json)
|
||||
// document.querySelector('#tiSeed').value = metadata_json.Seed
|
||||
|
||||
//extract auto_metadata into the preset metadata
|
||||
function convertAutoMetadataToPresset(metadata_json: any) {
|
||||
metadata_json['seed'] = metadata_json?.auto_metadata?.Seed
|
||||
}
|
||||
convertAutoMetadataToPresset(metadata_json)
|
||||
|
||||
const b_use_original_prompt = settings_tab.getUseOriginalPrompt()
|
||||
if (b_use_original_prompt) {
|
||||
metadata_json['prompt'] = metadata_json?.original_prompt
|
||||
? metadata_json['original_prompt']
|
||||
: metadata_json['prompt']
|
||||
|
||||
metadata_json['negative_prompt'] =
|
||||
metadata_json?.original_negative_prompt
|
||||
? metadata_json['original_negative_prompt']
|
||||
: metadata_json['negative_prompt']
|
||||
} else {
|
||||
metadata_json['prompt'] = metadata_json['prompt']
|
||||
|
||||
metadata_json['negative_prompt'] = metadata_json['negative_prompt']
|
||||
}
|
||||
// document.querySelector('#historySeedLabel').textContent =
|
||||
// metadata_json?.seed
|
||||
|
||||
g_ui_settings_object.autoFillInSettings(toJS(metadata_json))
|
||||
}
|
||||
|
||||
const History = observer(() => {
|
||||
return (
|
||||
<div style={{ width: '100%' }}>
|
||||
{/* {store.data.refresh} */}
|
||||
<sp-slider
|
||||
min={85}
|
||||
max={300}
|
||||
onInput={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const new_value = event.target.value
|
||||
store.updateProperty('height', new_value)
|
||||
store.updateProperty('width', new_value)
|
||||
}}
|
||||
show-value="true"
|
||||
value={100}
|
||||
>
|
||||
<sp-label slot="label">Image Size:</sp-label>
|
||||
</sp-slider>
|
||||
<Grid
|
||||
// thumbnails_data={store.data.images?.map((base64: string) =>
|
||||
// base64
|
||||
// ? 'data:image/png;base64,' + base64
|
||||
// : 'https://source.unsplash.com/random'
|
||||
// )}
|
||||
thumbnails={store.data.thumbnails?.map((base64: string) =>
|
||||
base64
|
||||
? 'data:image/png;base64,' + base64
|
||||
: 'https://source.unsplash.com/random'
|
||||
)}
|
||||
width={store.data.width}
|
||||
height={store.data.height}
|
||||
action_buttons={[
|
||||
{
|
||||
ComponentType: PenSvg,
|
||||
callback: (index: number) => {
|
||||
try {
|
||||
console.log(
|
||||
store.toJsFunc().data.metadata_jsons[index]
|
||||
)
|
||||
getHistoryMetadata(
|
||||
store.data.metadata_jsons[index]
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
},
|
||||
title: Locale('Copy Metadata to Settings'),
|
||||
},
|
||||
{
|
||||
ComponentType: MoveToCanvasSvg,
|
||||
callback: (index: number) => {
|
||||
moveHistoryImageToLayer(
|
||||
store.data.images[index],
|
||||
store.data.metadata_jsons[index][
|
||||
'selection_info'
|
||||
]
|
||||
)
|
||||
},
|
||||
title: Locale('Copy Image to Canvas'),
|
||||
},
|
||||
]}
|
||||
></Grid>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
const gridContainerNode = document.getElementById('divHistoryImagesContainer')!
|
||||
const gridRoot = ReactDOM.createRoot(gridContainerNode)
|
||||
|
||||
gridRoot.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<History></History>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import { observer } from 'mobx-react'
|
||||
import { AStore } from '../main/astore'
|
||||
import { Grid } from '../util/grid'
|
||||
import { MoveToCanvasSvg } from '../util/elements'
|
||||
import { io } from '../util/oldSystem'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
|
||||
export const store = new AStore({
|
||||
images: [],
|
||||
refresh: false,
|
||||
width: 50,
|
||||
height: 50,
|
||||
})
|
||||
|
||||
async function urlToCanvas(url: string) {
|
||||
const image_file_name = 'search_image_temp.png'
|
||||
await io.IO.urlToLayer(url, image_file_name)
|
||||
}
|
||||
|
||||
const ImageSearch = observer(() => {
|
||||
console.log('rendered')
|
||||
return (
|
||||
<div>
|
||||
<sp-slider
|
||||
min={85}
|
||||
max={300}
|
||||
onInput={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const new_value = event.target.value
|
||||
store.updateProperty('height', new_value)
|
||||
store.updateProperty('width', new_value)
|
||||
}}
|
||||
show-value="true"
|
||||
>
|
||||
<sp-label slot="label">Image Size:</sp-label>
|
||||
</sp-slider>
|
||||
<Grid
|
||||
// thumbnails_data={store.data.images}
|
||||
thumbnails={store.data.thumbnails}
|
||||
width={store.data.width}
|
||||
height={store.data.height}
|
||||
action_buttons={[
|
||||
{
|
||||
ComponentType: MoveToCanvasSvg,
|
||||
callback: (index: number) => {
|
||||
urlToCanvas(store.data.images[index])
|
||||
},
|
||||
title: 'Copy Image to Canvas',
|
||||
},
|
||||
]}
|
||||
></Grid>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
const gridContainerNode = document.getElementById(
|
||||
'divImageSearchImagesContainer'
|
||||
// 'search_second_panel'
|
||||
)!
|
||||
const gridRoot = ReactDOM.createRoot(gridContainerNode)
|
||||
|
||||
let images: string[] = []
|
||||
gridRoot.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<ImageSearch></ImageSearch>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
import { reaction } from 'mobx'
|
||||
import globalStore from '../globalstore'
|
||||
import Locale from './locale'
|
||||
|
||||
const elemSelectorForLocale = {
|
||||
// tab bar
|
||||
'#sp-stable-diffusion-ui-tab sp-label': 'Stable Diffusion',
|
||||
'#sp-viewer-tab sp-label': 'Viewer',
|
||||
'#sp-control_net-tab sp-label': 'ControlNet',
|
||||
'#sp-history-tab sp-label': 'History',
|
||||
'#sp-lexica-tab sp-label': 'Lexica',
|
||||
'#sp-image_search-tab sp-label': 'Image Search',
|
||||
'#sp-prompts-library-tab sp-label': 'Prompts library',
|
||||
'#sp-horde-tab sp-label': 'Horde',
|
||||
'#sp-extras-tab sp-label': 'Extras',
|
||||
'#sp-presets-tab sp-label': 'Presets',
|
||||
'#sp-settings-tab sp-label': 'Settings',
|
||||
|
||||
// viewer tab
|
||||
'#rgSubTab .rbSubTab': 'Viewer',
|
||||
'#rbHistoryTab': 'History',
|
||||
'#rbImageSearch': 'Image Search',
|
||||
'#rbPromptsLibrary': 'Prompts Library',
|
||||
'#rbLexica': 'Lexica',
|
||||
'#viewerSubTab .flexContainer sp-label':
|
||||
'View your generated images on the canvas',
|
||||
'#btnSetMaskViewer': 'Set Mask',
|
||||
'#btnSetInitImageViewer': 'Set Init Image',
|
||||
'#btnInterruptViewer': 'Interrupt',
|
||||
// '#btnSelectionArea': 'Selection Area',
|
||||
|
||||
// extra tab
|
||||
'#slThumbnailSize sp-label': 'Thumbnail Size',
|
||||
'#chSquareThumbnail': 'Square 1:1',
|
||||
'#btnGenerateUpscale': 'Generate upscale',
|
||||
'#btnInterruptUpscale': 'Interrupt',
|
||||
'#progressContainerUpscale sp-label': 'No work in progress',
|
||||
'#slUpscaler2Visibility .title': 'Upscaler 2 visibility',
|
||||
'#slGFPGANVisibility .title': 'GFPGAN visibility',
|
||||
'#slCodeFormerVisibility .title': 'CodeFormer visibility',
|
||||
'#slCodeFormerWeight .title': 'CodeFormer weight',
|
||||
|
||||
// sd tab
|
||||
'#pViewerProgressBar .lProgressLabel': 'Progress...',
|
||||
'#btnRefreshModels': 'Refresh',
|
||||
'#btnUpdate': 'Update',
|
||||
'#chUsePromptShortcut': 'prompt shortcut',
|
||||
'#btnInterrupt': 'Interrupt',
|
||||
'#bSetInitImage': 'Image',
|
||||
'#bSetInitImageMask': 'Mask',
|
||||
'#batchNumberSdUiTabContainer sp-label': 'Batch Size',
|
||||
'#batchCountSdUiTabContainer sp-label': 'Batch count',
|
||||
'#rbSelectionModeLabel': 'Selection Mode',
|
||||
'#selectionModeGroup [value=ratio]': 'ratio',
|
||||
'#selectionModeGroup [value=precise]': 'precise',
|
||||
'#selectionModeGroup [value=ignore]': 'ignore',
|
||||
'#slCfgScale .title': 'CFG Scale',
|
||||
'#slImageCfgScale .title': 'Image CFG Scale',
|
||||
'#slMaskBlur sp-label': 'Mask blur',
|
||||
'#slMaskExpansion sp-label': 'Mask Expansion',
|
||||
'#slInpaintingMaskWeight .title': 'Inpainting conditioning mask strength',
|
||||
'#slInpainting_fill .title': 'Masked content',
|
||||
'#slInpainting_fill [value=0]': 'fill',
|
||||
'#slInpainting_fill [value=1]': 'original',
|
||||
'#slInpainting_fill [value=2]': 'latent noise',
|
||||
'#slInpainting_fill [value=3]': 'latent nothing',
|
||||
'#chInpaintFullRes': 'Inpaint at Full Res',
|
||||
'#chRestoreFaces': 'Restore Faces',
|
||||
'#chHiResFixs': 'Highres. fix',
|
||||
'#HiResDiv .title': 'Upscaler',
|
||||
'#HiResStep': 'Hires steps',
|
||||
'#hrScaleSlider .title': 'Hires Scale',
|
||||
'#hrDenoisingStrength .title': 'High Res Denoising Strength',
|
||||
'#hrWidth': 'Hi Res Output Width',
|
||||
'#hrHeight': 'Hi Res Output Height',
|
||||
'#lNameInpaintPdding': 'Inpaint Padding',
|
||||
'#btnRandomSeed': 'Random',
|
||||
'#btnLastSeed': 'Last',
|
||||
'#sampler_group sp-label': 'Sampling method',
|
||||
'#sdLabelSeed': 'Seed',
|
||||
'#collapsible': 'Show Samplers',
|
||||
'#slHeight .title': 'Height',
|
||||
'#slWidth .title': 'Width',
|
||||
'#sdLabelSampleStep': 'Sampling Steps',
|
||||
}
|
||||
|
||||
function renderLocale(locale: string) {
|
||||
Object.keys(elemSelectorForLocale).forEach((selector) => {
|
||||
const elem = document.querySelector(selector)
|
||||
if (elem) {
|
||||
// @ts-ignore
|
||||
elem.innerHTML = Locale(elemSelectorForLocale[selector])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
reaction(() => globalStore.Locale, renderLocale)
|
||||
renderLocale(globalStore.Locale)
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import globalStore from '../globalstore'
|
||||
import type zhHans from '../../i18n/zh_CN/sd-official.json'
|
||||
import type zhHansForPSPlugin from '../../i18n/zh_CN/ps-plugin.json'
|
||||
import { lstatSync, readFileSync } from 'fs'
|
||||
|
||||
const localeFileCache: any = {}
|
||||
|
||||
function isExists(path: string): boolean {
|
||||
try {
|
||||
lstatSync(path)
|
||||
// console.log(path, 'exists')
|
||||
return true
|
||||
} catch (e) {
|
||||
// console.log(path, 'not exists')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export default function Locale(
|
||||
key: keyof typeof zhHans | keyof typeof zhHansForPSPlugin | any
|
||||
): string {
|
||||
const locale = globalStore.Locale
|
||||
|
||||
const sdOfficialJSONPath = `plugin:/i18n/${locale}/sd-official.json`
|
||||
let sdOfficialTranslate = localeFileCache[sdOfficialJSONPath]
|
||||
if (!localeFileCache[sdOfficialJSONPath] && isExists(sdOfficialJSONPath)) {
|
||||
console.log('readFile')
|
||||
sdOfficialTranslate = JSON.parse(
|
||||
readFileSync(sdOfficialJSONPath, 'utf-8')
|
||||
)
|
||||
localeFileCache[sdOfficialJSONPath] = sdOfficialTranslate
|
||||
}
|
||||
|
||||
const psPluginJSONPath = `plugin:/i18n/${locale}/ps-plugin.json`
|
||||
let psPluginTranslate = localeFileCache[psPluginJSONPath]
|
||||
if (!localeFileCache[psPluginJSONPath] && isExists(psPluginJSONPath)) {
|
||||
console.log('readFile')
|
||||
psPluginTranslate = JSON.parse(readFileSync(psPluginJSONPath, 'utf-8'))
|
||||
localeFileCache[psPluginJSONPath] = psPluginTranslate
|
||||
}
|
||||
|
||||
let res = ''
|
||||
//@ts-ignore
|
||||
if (sdOfficialTranslate && key in sdOfficialTranslate)
|
||||
res = sdOfficialTranslate[key]
|
||||
//@ts-ignore
|
||||
if (psPluginTranslate && key in psPluginTranslate)
|
||||
res = psPluginTranslate[key]
|
||||
|
||||
res = res || key
|
||||
return res
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { makeAutoObservable, reaction, toJS } from 'mobx'
|
||||
export { toJS } from 'mobx'
|
||||
// import { Provider, inject, observer } from 'mobx-react'
|
||||
export class AStore {
|
||||
data: any
|
||||
|
|
@ -20,8 +21,16 @@ export class AStore {
|
|||
updateProperty(key: keyof any, value: any) {
|
||||
;(this.data as any)[key] = value
|
||||
}
|
||||
updatePropertyArray(key: keyof any, value: any) {
|
||||
this.data[key] = this.data[key].concat(value)
|
||||
}
|
||||
updatePropertyArrayRemove(key: keyof any, valueToRemove: any) {
|
||||
this.data[key] = this.data[key].filter(
|
||||
(item: any) => item !== valueToRemove
|
||||
)
|
||||
}
|
||||
|
||||
toJsFunc() {
|
||||
return toJS(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
import React, { ReactEventHandler } from 'react'
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import { observer } from 'mobx-react'
|
||||
import { AStore } from './astore'
|
||||
import { SpMenu } from '../../ultimate_sd_upscaler/src/elements'
|
||||
import { SpMenu } from '../util/elements'
|
||||
|
||||
import { api, python_replacement } from '../util/oldSystem'
|
||||
const { getExtensionUrl } = python_replacement
|
||||
import '../locale/locale-for-old-html'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
|
||||
import { getExtensionUrl } from '../../utility/sdapi/python_replacement'
|
||||
import * as api from '../../utility/api'
|
||||
declare let g_sd_url: string
|
||||
// class SDStore extends AStore {
|
||||
// constructor(data: any) {
|
||||
|
|
@ -76,8 +79,6 @@ export class VAEComponent extends React.Component<{
|
|||
const vaeContainerNode = document.getElementById('settingsVAEContainer')!
|
||||
const vaeRoot = ReactDOM.createRoot(vaeContainerNode)
|
||||
|
||||
// let vae_model_list = ['None']
|
||||
|
||||
async function requestGetVAE() {
|
||||
const full_url = `${g_sd_url}/sdapi/v1/options`
|
||||
|
||||
|
|
@ -85,23 +86,28 @@ async function requestGetVAE() {
|
|||
return options?.sd_vae
|
||||
}
|
||||
export async function populateVAE() {
|
||||
const extension_url = getExtensionUrl()
|
||||
try {
|
||||
const extension_url = getExtensionUrl()
|
||||
|
||||
const full_url = `${extension_url}/vae/list`
|
||||
const vae_models = await api.requestGet(full_url)
|
||||
const full_url = `${extension_url}/vae/list`
|
||||
const vae_models = (await api.requestGet(full_url)) || []
|
||||
|
||||
console.log('populateVAE vae_models: ', vae_models)
|
||||
store.updateProperty('vae_model_list', vae_models)
|
||||
console.log('populateVAE vae_models: ', vae_models)
|
||||
store.updateProperty('vae_model_list', vae_models)
|
||||
|
||||
const current_vae = await requestGetVAE()
|
||||
if (current_vae && vae_models.includes(current_vae)) {
|
||||
store.updateProperty('current_vae', current_vae)
|
||||
const current_vae = await requestGetVAE()
|
||||
if (current_vae && vae_models.includes(current_vae)) {
|
||||
store.updateProperty('current_vae', current_vae)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('populateVAE():', e)
|
||||
}
|
||||
}
|
||||
// populateVAE()
|
||||
|
||||
vaeRoot.render(
|
||||
<React.StrictMode>
|
||||
<VAEComponent></VAEComponent>
|
||||
<ErrorBoundary>
|
||||
<VAEComponent></VAEComponent>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
|
@ -0,0 +1,300 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
|
||||
import Collapsible from '../after_detailer/after_detailer'
|
||||
import { observer } from 'mobx-react'
|
||||
import { AStore } from '../main/astore'
|
||||
|
||||
import { requestPost, requestGet, isScriptInstalled } from '../util/ts/api'
|
||||
import {
|
||||
ScriptInstallComponent,
|
||||
SpMenu,
|
||||
SpSliderWithLabel,
|
||||
} from '../util/elements'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
|
||||
declare let g_sd_url: string
|
||||
export const store = new AStore({
|
||||
prompts: [],
|
||||
number: 3,
|
||||
prompt_complexity: 5,
|
||||
subjects: [],
|
||||
artists: [],
|
||||
imagetypes: [],
|
||||
subject: 'all',
|
||||
artist: 'all',
|
||||
imagetype: 'all',
|
||||
script_name: 'one button prompt',
|
||||
is_installed: false,
|
||||
})
|
||||
|
||||
export async function requestRandomPrompts(
|
||||
number_of_prompts: number = 1,
|
||||
insanitylevel: number = 5,
|
||||
subject: string = 'all',
|
||||
artist: string = 'all',
|
||||
imagetype: string = 'all'
|
||||
) {
|
||||
const payload = {
|
||||
numberofprompts: number_of_prompts,
|
||||
insanitylevel: insanitylevel,
|
||||
forcesubject: subject,
|
||||
artists: artist,
|
||||
imagetype: imagetype,
|
||||
onlyartists: false,
|
||||
antivalues: '',
|
||||
prefixprompt: '',
|
||||
suffixprompt: '',
|
||||
promptcompounderlevel: '1',
|
||||
seperator: 'comma',
|
||||
givensubject: '',
|
||||
smartsubject: true,
|
||||
giventypeofimage: '',
|
||||
imagemodechance: 20,
|
||||
}
|
||||
try {
|
||||
const full_url = `${g_sd_url}/one_button_prompt/prompt/random`
|
||||
|
||||
const randomPrompts = (await requestPost(full_url, payload))?.prompts
|
||||
return randomPrompts
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
function autoResize(textarea: any) {
|
||||
// const textarea = event.target
|
||||
let measure = document.getElementById('measure')!
|
||||
if (!measure) {
|
||||
measure = document.createElement('div')
|
||||
measure.setAttribute('id', 'measure')
|
||||
measure.style.visibility = 'hidden'
|
||||
measure.style.whiteSpace = 'pre-wrap'
|
||||
measure.style.position = 'absolute'
|
||||
measure.style.fontSize = '14px'
|
||||
// measure.style.paddingBottom = '10px'
|
||||
// measure.style.paddingTop = '10px'
|
||||
// measure.style.lineHeight = '14px'
|
||||
|
||||
document.body.appendChild(measure)
|
||||
}
|
||||
measure.style.width = textarea.offsetWidth + 'px'
|
||||
// getComputedStyle(textarea).width
|
||||
|
||||
measure.textContent = textarea.value
|
||||
try {
|
||||
clearTimeout(g_style_timeout)
|
||||
g_style_timeout = setTimeout(() => {
|
||||
let height = measure.offsetHeight
|
||||
//height between [60,450]
|
||||
height = Math.max(60, height)
|
||||
height = Math.min(450, height)
|
||||
textarea.style.height = height + 'px'
|
||||
}, 300)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
let g_timeout: any
|
||||
let g_style_timeout: any
|
||||
function handleInput(event: any) {
|
||||
try {
|
||||
// clearTimeout(g_timeout)
|
||||
// g_timeout = setTimeout(() => autoResize(event.target), 1000)
|
||||
autoResize(event.target)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
export async function requestConfig() {
|
||||
try {
|
||||
const full_url = `${g_sd_url}/one_button_prompt/config`
|
||||
|
||||
const ui_config = await requestGet(full_url)
|
||||
|
||||
if (ui_config) {
|
||||
store.data.subjects = ui_config?.subjects ?? []
|
||||
store.data.artists = ui_config?.artists ?? []
|
||||
store.data.imagetypes = ui_config?.imagetypes ?? []
|
||||
}
|
||||
|
||||
return ui_config
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class OneButtonPrompt extends React.Component {
|
||||
async initScript() {
|
||||
const is_installed = await isScriptInstalled(store.data.script_name)
|
||||
await store.updateProperty('is_installed', is_installed)
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
await requestConfig()
|
||||
await this.initScript()
|
||||
}
|
||||
|
||||
renderContainer() {
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<SpSliderWithLabel
|
||||
show-value={false}
|
||||
steps={1}
|
||||
out_min={1}
|
||||
out_max={10}
|
||||
output_value={store.data.prompt_complexity}
|
||||
title={`Higher levels increases complexity and randomness of generated
|
||||
prompt`}
|
||||
label={`Prompt Complexity`}
|
||||
onSliderInput={(output_value: number) => {
|
||||
store.data.prompt_complexity = output_value
|
||||
}}
|
||||
/>
|
||||
<sp-label>Subject:</sp-label>
|
||||
<SpMenu
|
||||
title="subjects"
|
||||
items={store.data.subjects}
|
||||
label_item="Select a Subject"
|
||||
selected_index={store.data.subjects.indexOf(
|
||||
store.data.subject
|
||||
)}
|
||||
onChange={(id: any, value: any) => {
|
||||
// console.log('onChange value: ', value)
|
||||
store.updateProperty('subject', value.item)
|
||||
}}
|
||||
></SpMenu>
|
||||
<sp-label>Artist:</sp-label>
|
||||
<SpMenu
|
||||
title="artists"
|
||||
items={store.data.artists}
|
||||
label_item="Select an Artist"
|
||||
selected_index={store.data.artists.indexOf(
|
||||
store.data.artist
|
||||
)}
|
||||
onChange={(id: any, value: any) => {
|
||||
// console.log('onChange value: ', value)
|
||||
store.updateProperty('artist', value.item)
|
||||
}}
|
||||
></SpMenu>
|
||||
<sp-label>Image Type:</sp-label>
|
||||
<SpMenu
|
||||
title="image types"
|
||||
items={store.data.imagetypes}
|
||||
label_item="Select an Image Type"
|
||||
selected_index={store.data.imagetypes.indexOf(
|
||||
store.data.imagetype
|
||||
)}
|
||||
onChange={(id: any, value: any) => {
|
||||
// console.log('onChange value: ', value)
|
||||
store.updateProperty('imagetype', value.item)
|
||||
}}
|
||||
></SpMenu>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
marginTop: '5px',
|
||||
}}
|
||||
>
|
||||
<button
|
||||
style={{ float: 'right' }}
|
||||
className="btnSquare"
|
||||
onClick={async () => {
|
||||
const prompt_complexity =
|
||||
store.data.prompt_complexity ?? 5
|
||||
const subject = store.data.subject ?? 'all'
|
||||
const artist = store.data.artist ?? 'all'
|
||||
const imagetype = store.data.imagetype ?? 'all'
|
||||
store.data.prompts = await requestRandomPrompts(
|
||||
3,
|
||||
prompt_complexity,
|
||||
subject,
|
||||
artist,
|
||||
imagetype
|
||||
)
|
||||
}}
|
||||
>
|
||||
Random Prompts
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{store.data.prompts.map((prompt: string, index: number) => {
|
||||
return (
|
||||
<div
|
||||
key={`prompt-area-${index}`}
|
||||
style={{
|
||||
border: '2px solid #6d6c6c',
|
||||
padding: '3px',
|
||||
}}
|
||||
>
|
||||
<button
|
||||
className="btnSquare"
|
||||
style={{ textAlign: 'right' }}
|
||||
onClick={() => {
|
||||
//@ts-ignore
|
||||
document.querySelector('#taPrompt').value =
|
||||
prompt
|
||||
}}
|
||||
>
|
||||
use
|
||||
</button>
|
||||
<sp-textarea
|
||||
onInput={(event: any) => {
|
||||
handleInput(event)
|
||||
store.data.prompts[index] =
|
||||
event.target.value
|
||||
}}
|
||||
placeholder={`random prompt ${index}`}
|
||||
value={prompt}
|
||||
></sp-textarea>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{store.data.is_installed ? (
|
||||
<div style={{ padding: '4px' }}>
|
||||
{this.renderContainer()}
|
||||
</div>
|
||||
) : (
|
||||
<ScriptInstallComponent
|
||||
onRefreshHandler={async (event: any) => {
|
||||
console.log(`Refresh ${store.data.script_name}`)
|
||||
await this.initScript()
|
||||
}}
|
||||
></ScriptInstallComponent>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const containers = document.querySelectorAll('.oneButtonPromptContainer')!
|
||||
|
||||
containers.forEach((container) => {
|
||||
const root = ReactDOM.createRoot(container)
|
||||
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<div style={{ border: '2px solid #6d6c6c', padding: '3px' }}>
|
||||
<Collapsible
|
||||
defaultIsOpen={false}
|
||||
label={'One Button Prompt'}
|
||||
>
|
||||
<OneButtonPrompt />
|
||||
</Collapsible>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
})
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import Collapsible from '../after_detailer/after_detailer'
|
||||
import { observer } from 'mobx-react'
|
||||
import { isScriptInstalled } from '../util/ts/api'
|
||||
import { api, general, io, psapi, selection } from '../util/oldSystem'
|
||||
import { Grid } from '../util/grid'
|
||||
import { AStore } from '../main/astore'
|
||||
import {
|
||||
MoveToCanvasSvg,
|
||||
PenSvg,
|
||||
ScriptInstallComponent,
|
||||
} from '../util/elements'
|
||||
import {
|
||||
applyMaskFromBlackAndWhiteImage,
|
||||
selectionFromBlackAndWhiteImage,
|
||||
} from '../util/ts/selection'
|
||||
import { app } from 'photoshop'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
import Locale from '../locale/locale'
|
||||
import { settings_tab_ts } from '../entry'
|
||||
declare let g_sd_url: string
|
||||
|
||||
export async function getSamMap(base64: string, prompt: string) {
|
||||
// const full_url = `${g_sd_url}/sam/dino-predict`
|
||||
// const payload = {
|
||||
// dino_model_name: 'GroundingDINO_SwinT_OGC (694MB)',
|
||||
// input_image: base64,
|
||||
// text_prompt: 'the dog',
|
||||
// box_threshold: 0.3,
|
||||
// }
|
||||
const full_url = `${g_sd_url}/sam/sam-predict`
|
||||
|
||||
const payload = {
|
||||
sam_model_name: 'sam_vit_h_4b8939.pth',
|
||||
input_image: base64,
|
||||
sam_positive_points: [],
|
||||
sam_negative_points: [],
|
||||
dino_enabled: true,
|
||||
dino_model_name: 'GroundingDINO_SwinT_OGC (694MB)',
|
||||
dino_text_prompt: prompt,
|
||||
dino_box_threshold: 0.3,
|
||||
dino_preview_checkbox: false,
|
||||
dino_preview_boxes_selection: [0],
|
||||
}
|
||||
const result = await api.requestPost(full_url, payload)
|
||||
return result
|
||||
}
|
||||
export const store = new AStore({
|
||||
thumbnails: [],
|
||||
selection_info_list: [],
|
||||
prompt: '',
|
||||
width: 85,
|
||||
height: 85,
|
||||
is_installed: false,
|
||||
script_name: 'segment anything',
|
||||
})
|
||||
|
||||
@observer
|
||||
export class Sam extends React.Component<{
|
||||
// store: AStore
|
||||
}> {
|
||||
async initScript() {
|
||||
const is_installed = await isScriptInstalled(store.data.script_name)
|
||||
await store.updateProperty('is_installed', is_installed)
|
||||
}
|
||||
async componentDidMount(): Promise<void> {
|
||||
await this.initScript()
|
||||
}
|
||||
|
||||
renderScript() {
|
||||
return (
|
||||
<div>
|
||||
<sp-textarea
|
||||
placeholder="Segment Anything Prompt"
|
||||
value={store.data.prompt}
|
||||
onInput={(event: any) => {
|
||||
store.data.prompt = event.target.value
|
||||
}}
|
||||
></sp-textarea>
|
||||
<button
|
||||
className="btnSquare"
|
||||
onClick={async () => {
|
||||
const selection_info = await psapi.getSelectionInfoExe()
|
||||
const base64 = await io.getImageFromCanvas()
|
||||
const result = await getSamMap(
|
||||
base64,
|
||||
store.data.prompt
|
||||
)
|
||||
const masks = result?.masks ?? []
|
||||
const masks_urls = []
|
||||
for (const mask of masks) {
|
||||
const url =
|
||||
await io.convertBlackAndWhiteImageToRGBChannels3(
|
||||
mask
|
||||
)
|
||||
masks_urls.push(url)
|
||||
}
|
||||
|
||||
store.updateProperty('thumbnails', masks_urls)
|
||||
store.updateProperty(
|
||||
'selection_info_list',
|
||||
Array(masks_urls.length).fill(selection_info)
|
||||
)
|
||||
}}
|
||||
>
|
||||
Generate Mask
|
||||
</button>
|
||||
<Grid
|
||||
// thumbnails_data={store.data.images?.map((base64: string) =>
|
||||
// base64
|
||||
// ? 'data:image/png;base64,' + base64
|
||||
// : 'https://source.unsplash.com/random'
|
||||
// )}
|
||||
thumbnails={store.data.thumbnails}
|
||||
width={store.data.width}
|
||||
height={store.data.height}
|
||||
action_buttons={[
|
||||
{
|
||||
ComponentType: PenSvg,
|
||||
callback: async (index: number) => {
|
||||
try {
|
||||
await psapi.unSelectMarqueeExe()
|
||||
const base64 = general.base64UrlToBase64(
|
||||
store.data.thumbnails[index]
|
||||
)
|
||||
|
||||
await selectionFromBlackAndWhiteImage(
|
||||
base64,
|
||||
store.data.selection_info_list[index],
|
||||
settings_tab_ts.store.data
|
||||
.b_borders_or_corners
|
||||
)
|
||||
|
||||
// try {
|
||||
// const base64 =
|
||||
// general.base64UrlToBase64(
|
||||
// store.data.thumbnails[index]
|
||||
// )
|
||||
// await selection.base64ToLassoSelection(
|
||||
// base64,
|
||||
// store.data.selection_info_list[
|
||||
// index
|
||||
// ]
|
||||
// )
|
||||
// } catch (e) {
|
||||
// console.warn(e)
|
||||
// }
|
||||
//@ts-ignore
|
||||
await eventHandler() // this will trigger the recalculation of the width and height sliders
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
},
|
||||
title: Locale('Select Masked Area'),
|
||||
},
|
||||
{
|
||||
ComponentType: MoveToCanvasSvg,
|
||||
callback: async (index: number) => {
|
||||
try {
|
||||
const to_x =
|
||||
store.data.selection_info_list[index]
|
||||
?.left
|
||||
const to_y =
|
||||
store.data.selection_info_list[index]
|
||||
?.top
|
||||
const width =
|
||||
store.data.selection_info_list[index]
|
||||
?.width
|
||||
const height =
|
||||
store.data.selection_info_list[index]
|
||||
?.height
|
||||
|
||||
await io.IO.base64ToLayer(
|
||||
general.base64UrlToBase64(
|
||||
store.data.thumbnails[index]
|
||||
),
|
||||
'segment_anything_mask.png',
|
||||
to_x,
|
||||
to_y,
|
||||
width,
|
||||
height
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
},
|
||||
title: Locale('Copy Image to Canvas'),
|
||||
},
|
||||
]}
|
||||
></Grid>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{store.data.is_installed ? (
|
||||
this.renderScript()
|
||||
) : (
|
||||
<ScriptInstallComponent
|
||||
onRefreshHandler={async (event: any) => {
|
||||
console.log(`Refresh ${store.data.script_name}`)
|
||||
await this.initScript()
|
||||
}}
|
||||
></ScriptInstallComponent>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
const containers = document.querySelectorAll('.samContainer')
|
||||
|
||||
containers.forEach((container) => {
|
||||
const root = ReactDOM.createRoot(container)
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<div style={{ border: '2px solid #6d6c6c', padding: '3px' }}>
|
||||
<Collapsible
|
||||
defaultIsOpen={false}
|
||||
label={'Segment Anything'}
|
||||
>
|
||||
<Sam></Sam>
|
||||
</Collapsible>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
})
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
|
||||
import { observer } from 'mobx-react'
|
||||
|
||||
import { AStore } from '../main/astore'
|
||||
import { GenerationModeEnum } from '../util/ts/enum'
|
||||
import { reaction } from 'mobx'
|
||||
import { SpCheckBox } from '../util/elements'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
|
||||
export const store = new AStore({
|
||||
is_lasso_mode: false,
|
||||
mode: GenerationModeEnum.Txt2Img,
|
||||
} as { is_lasso_mode: boolean; mode: GenerationModeEnum })
|
||||
reaction(
|
||||
() =>
|
||||
[store.data.is_lasso_mode, store.data.mode] as [
|
||||
boolean,
|
||||
GenerationModeEnum
|
||||
],
|
||||
([is_lasso_mode, mode]: [boolean, GenerationModeEnum]) => {
|
||||
if (is_lasso_mode && mode === GenerationModeEnum.Inpaint) {
|
||||
store.data.mode = GenerationModeEnum.LassoInpaint
|
||||
} else if (!is_lasso_mode && mode === GenerationModeEnum.LassoInpaint) {
|
||||
store.data.mode = GenerationModeEnum.Inpaint
|
||||
}
|
||||
// if (is_lasso_mode && mode === GenerationModeEnum.Outpaint) {
|
||||
// store.data.mode = GenerationModeEnum.LassoOutpaint
|
||||
// } else if (
|
||||
// !is_lasso_mode &&
|
||||
// mode === GenerationModeEnum.LassoOutpaint
|
||||
// ) {
|
||||
// store.data.mode = GenerationModeEnum.Outpaint
|
||||
// }
|
||||
console.log('store.data.is_lasso_mode:', store.data.is_lasso_mode)
|
||||
console.log('store.data.mode:', store.data.mode)
|
||||
}
|
||||
)
|
||||
|
||||
const handleLassoModeChange = (event: any) => {
|
||||
store.updateProperty('is_lasso_mode', event.target.checked)
|
||||
}
|
||||
const Modes = observer(() => {
|
||||
const renderLassoModeElement = () => {
|
||||
if (
|
||||
[
|
||||
GenerationModeEnum.Inpaint,
|
||||
// GenerationModeEnum.Outpaint,
|
||||
GenerationModeEnum.LassoInpaint,
|
||||
// GenerationModeEnum.LassoOutpaint,
|
||||
].includes(store.data.mode)
|
||||
) {
|
||||
return (
|
||||
<SpCheckBox
|
||||
// style={{ marginRight: '10px' }}
|
||||
onChange={handleLassoModeChange}
|
||||
checked={store.data.is_lasso_mode}
|
||||
// id={`chEnableControlNet_${this.props.index}`}
|
||||
value={store.data.is_lasso_mode}
|
||||
>
|
||||
Lasso Mode
|
||||
</SpCheckBox>
|
||||
|
||||
// <sp-checkbox checked={store.data.is_lasso_mode ? true : void 0}>
|
||||
// lasso mode
|
||||
// </sp-checkbox>
|
||||
)
|
||||
}
|
||||
}
|
||||
return <div>{renderLassoModeElement()}</div>
|
||||
})
|
||||
|
||||
const container = document.getElementById('reactModesContainer')!
|
||||
const root = ReactDOM.createRoot(container)
|
||||
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<Modes />
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
|
@ -0,0 +1,367 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
|
||||
import { observer } from 'mobx-react'
|
||||
|
||||
import { sd_tab_ts, session_ts, viewer } from '../entry'
|
||||
import './style/generate.css'
|
||||
import { io, note, psapi, selection } from '../util/oldSystem'
|
||||
import { GenerationModeEnum } from '../util/ts/enum'
|
||||
import { initializeBackground } from '../util/ts/document'
|
||||
import Locale from '../locale/locale'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
declare let g_automatic_status: any
|
||||
declare let g_current_batch_index: number
|
||||
//example: take 'oI' in 'LassoInpaint' and replace it with 'o I' thus creating 'Lasso Inpaint'
|
||||
const modeDisplayNames = Object.fromEntries(
|
||||
Object.entries(GenerationModeEnum).map(([key, value]) => [
|
||||
value,
|
||||
key.replace(/([a-z])([A-Z])/g, '$1 $2'),
|
||||
])
|
||||
)
|
||||
|
||||
const GenerateButtons = observer(() => {
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
id="btnNewGenerate"
|
||||
className="btnSquare generateButtonMargin generateColor"
|
||||
onClick={handleGenerateBatch}
|
||||
style={{
|
||||
display: session_ts.store.data.can_generate
|
||||
? void 0
|
||||
: 'none',
|
||||
}}
|
||||
>
|
||||
Generate {modeDisplayNames[sd_tab_ts.store.data.mode]}
|
||||
</button>
|
||||
{session_ts.store.data.can_generate ? (
|
||||
<button
|
||||
onClick={handleGenerateMoreBatch}
|
||||
disabled={
|
||||
session_ts.store.data.can_generate_more ? void 0 : true
|
||||
}
|
||||
id="btnNewGenerateMore"
|
||||
className={
|
||||
'btnSquare generateButtonMargin generateMoreColor' +
|
||||
(session_ts.store.data.can_generate_more
|
||||
? ''
|
||||
: 'disableBtn')
|
||||
}
|
||||
style={{
|
||||
display: session_ts.store.data.can_generate_more
|
||||
? 'inline-block'
|
||||
: 'none',
|
||||
}}
|
||||
>
|
||||
Generate more
|
||||
</button>
|
||||
) : (
|
||||
void 0
|
||||
)}
|
||||
{!session_ts.store.data.can_generate ? (
|
||||
<button
|
||||
onClick={handleInterrupt}
|
||||
id="btnNewInterrupt"
|
||||
className="btnSquare generateButtonMargin"
|
||||
>
|
||||
Interrupt
|
||||
</button>
|
||||
) : (
|
||||
void 0
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
const ToolbarGenerateButtons = observer(() => {
|
||||
const button_style: any = {
|
||||
width: '30px',
|
||||
height: '30px',
|
||||
marginBottom: '3px',
|
||||
}
|
||||
const generate_display = session_ts.store.data.can_generate
|
||||
? 'inline-flex'
|
||||
: 'none'
|
||||
const generate_more_display =
|
||||
session_ts.store.data.can_generate &&
|
||||
session_ts.store.data.can_generate_more
|
||||
? 'inline-flex'
|
||||
: 'none'
|
||||
const interrupt_display = session_ts.store.data.can_generate
|
||||
? 'none'
|
||||
: 'inline-flex'
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
title={Locale('Generate')}
|
||||
className="btnSquare generateColor"
|
||||
onClick={handleGenerate}
|
||||
style={{ ...button_style, display: generate_display }}
|
||||
>
|
||||
G
|
||||
</button>
|
||||
<button
|
||||
title={Locale('Generate More')}
|
||||
onClick={handleGenerateMore}
|
||||
className={'btnSquare generateMoreColor'}
|
||||
style={{
|
||||
...button_style,
|
||||
display: generate_more_display,
|
||||
}}
|
||||
>
|
||||
M
|
||||
</button>
|
||||
<button
|
||||
title={Locale('Interrupt')}
|
||||
onClick={handleInterrupt}
|
||||
className="btnSquare"
|
||||
style={{
|
||||
...button_style,
|
||||
display: interrupt_display,
|
||||
}}
|
||||
>
|
||||
I
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
const canStartSession = async () => {
|
||||
// check for automatic1111 connection: fail if false
|
||||
// check for automatic1111 api: fail if false
|
||||
// check for having a background layer: create if false
|
||||
// check for artboard: fail if true
|
||||
// check for selection: fail if false
|
||||
let can_start_session = false
|
||||
try {
|
||||
const selection_info = await psapi.getSelectionInfoExe()
|
||||
|
||||
if (selection_info) {
|
||||
session_ts.Session.endSession()
|
||||
|
||||
can_start_session = true
|
||||
} else {
|
||||
can_start_session = await note.Notification.inactiveSelectionArea(
|
||||
session_ts.store.data.is_active,
|
||||
'Reuse Selection'
|
||||
)
|
||||
if (can_start_session) {
|
||||
//end current session and start a new one
|
||||
session_ts.Session.endSession()
|
||||
await psapi.reSelectMarqueeExe(
|
||||
session_ts.store.data.selectionInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
//@ts-ignore
|
||||
g_automatic_status = await checkAutoStatus()
|
||||
//@ts-ignore
|
||||
await displayNotification(g_automatic_status)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
|
||||
return can_start_session
|
||||
}
|
||||
|
||||
const resetBatch = () => {
|
||||
g_current_batch_index = -1
|
||||
session_ts.store.data.is_interrupted = false
|
||||
}
|
||||
// declare let g_sd_mode: any
|
||||
const handleGenerate = async () => {
|
||||
//save user input for later
|
||||
//1) save selection as channel
|
||||
await selection.selectionToChannel('mask')
|
||||
|
||||
await initializeBackground() //fix background if there is a need
|
||||
console.log('mode: ', sd_tab_ts.store.data.mode)
|
||||
try {
|
||||
if (!(await canStartSession())) {
|
||||
return void 0
|
||||
}
|
||||
var { output_images, response_json } =
|
||||
await session_ts.Session.generate(sd_tab_ts.store.data.mode)
|
||||
|
||||
if (session_ts.store.data.is_interrupted) {
|
||||
return void 0
|
||||
}
|
||||
|
||||
const thumbnail_list = []
|
||||
for (const base64 of output_images) {
|
||||
const thumbnail = await io.createThumbnail(base64, 300)
|
||||
thumbnail_list.push(thumbnail)
|
||||
}
|
||||
|
||||
viewer.store.updateProperty('thumbnails', thumbnail_list)
|
||||
viewer.store.updateProperty('images', output_images)
|
||||
if (
|
||||
[
|
||||
GenerationModeEnum.Inpaint,
|
||||
GenerationModeEnum.LassoInpaint,
|
||||
GenerationModeEnum.Outpaint,
|
||||
].includes(session_ts.store.data.mode)
|
||||
) {
|
||||
viewer.mask_store.updateProperty(
|
||||
'output_images_masks',
|
||||
Array(output_images.length).fill(
|
||||
session_ts.store.data.expanded_mask
|
||||
)
|
||||
)
|
||||
}
|
||||
console.log(
|
||||
'session_ts.store.toJsFunc(): ',
|
||||
session_ts.store.toJsFunc()
|
||||
)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.warn('output_images: ', output_images)
|
||||
console.warn('response_json: ', response_json)
|
||||
}
|
||||
}
|
||||
|
||||
const handleGenerateMore = async () => {
|
||||
try {
|
||||
var { output_images, response_json } =
|
||||
await session_ts.Session.generateMore()
|
||||
|
||||
if (session_ts.store.data.is_interrupted) {
|
||||
return void 0
|
||||
}
|
||||
|
||||
const thumbnail_list = []
|
||||
for (const base64 of output_images) {
|
||||
const thumbnail = await io.createThumbnail(base64, 300)
|
||||
thumbnail_list.push(thumbnail)
|
||||
}
|
||||
viewer.store.data.thumbnails = [
|
||||
...viewer.store.data.thumbnails,
|
||||
...thumbnail_list,
|
||||
]
|
||||
|
||||
viewer.store.data.images = [
|
||||
...viewer.store.data.images,
|
||||
...output_images,
|
||||
]
|
||||
|
||||
if (
|
||||
[
|
||||
GenerationModeEnum.Inpaint,
|
||||
GenerationModeEnum.LassoInpaint,
|
||||
GenerationModeEnum.Outpaint,
|
||||
].includes(session_ts.store.data.mode)
|
||||
) {
|
||||
viewer.mask_store.updatePropertyArray(
|
||||
'output_images_masks',
|
||||
Array(output_images.length).fill(
|
||||
session_ts.store.data.expanded_mask
|
||||
)
|
||||
)
|
||||
}
|
||||
// viewer.store.updateProperty('images', output_images)
|
||||
// console.log(
|
||||
// 'session_ts.store.toJsFunc(): ',
|
||||
// session_ts.store.toJsFunc()
|
||||
// )
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.warn('output_images: ', output_images)
|
||||
console.warn('response_json: ', response_json)
|
||||
}
|
||||
}
|
||||
|
||||
const handleGenerateBatch = async () => {
|
||||
try {
|
||||
const numberOfBatchCount: number = parseInt(
|
||||
//@ts-ignore
|
||||
document.querySelector('#tiNumberOfBatchCount').value
|
||||
)
|
||||
|
||||
await handleGenerate() //first generation is always use handleGenerate
|
||||
for (
|
||||
let i = 1;
|
||||
i < numberOfBatchCount && !session_ts.store.data.is_interrupted;
|
||||
i++
|
||||
) {
|
||||
// if (g_batch_count_interrupt_status === true) {
|
||||
// break
|
||||
// }
|
||||
// g_current_batch_index = i
|
||||
await handleGenerateMore()
|
||||
}
|
||||
resetBatch()
|
||||
// g_batch_count_interrupt_status = false // reset for next generation
|
||||
// g_current_batch_index = 0 // reset curent_batch_number
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
const handleGenerateMoreBatch = async () => {
|
||||
try {
|
||||
const numberOfBatchCount: number = parseInt(
|
||||
//@ts-ignore
|
||||
document.querySelector('#tiNumberOfBatchCount').value
|
||||
)
|
||||
|
||||
// await handleGenerateMore() //first generation is always use handleGenerate
|
||||
for (
|
||||
let i = 0;
|
||||
i < numberOfBatchCount && !session_ts.store.data.is_interrupted;
|
||||
i++
|
||||
) {
|
||||
// if (g_batch_count_interrupt_status === true) {
|
||||
// break
|
||||
// }
|
||||
// g_current_batch_index = i
|
||||
await handleGenerateMore()
|
||||
}
|
||||
|
||||
// g_batch_count_interrupt_status = false // reset for next generation
|
||||
// g_current_batch_index = 0 // reset curent_batch_number
|
||||
resetBatch()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
const handleInterrupt = async () => {
|
||||
try {
|
||||
// debugger
|
||||
await session_ts.Session.interrupt()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
const container = document.getElementById('generateButtonsContainer')!
|
||||
const root = ReactDOM.createRoot(container)
|
||||
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<GenerateButtons></GenerateButtons>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
||||
const extraContainer = document.getElementById('extraGenerateButtonsContainer')!
|
||||
const extraRoot = ReactDOM.createRoot(extraContainer)
|
||||
|
||||
extraRoot.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<GenerateButtons></GenerateButtons>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
||||
const toolBarButtonsContainer = document.getElementById(
|
||||
'toolbarGenerateButtonsContainer'
|
||||
)!
|
||||
const toolBarButtonsContainerRoot = ReactDOM.createRoot(toolBarButtonsContainer)
|
||||
toolBarButtonsContainerRoot.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<ToolbarGenerateButtons></ToolbarGenerateButtons>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
|
@ -0,0 +1,597 @@
|
|||
import { control_net, scripts, session_ts } from '../entry'
|
||||
|
||||
import {
|
||||
html_manip,
|
||||
io,
|
||||
layer_util,
|
||||
psapi,
|
||||
python_replacement,
|
||||
selection,
|
||||
session,
|
||||
} from '../util/oldSystem'
|
||||
|
||||
import { core } from 'photoshop'
|
||||
import {
|
||||
getEnableControlNet,
|
||||
getModuleDetail,
|
||||
mapPluginSettingsToControlNet,
|
||||
} from '../controlnet/entry'
|
||||
const executeAsModal = core.executeAsModal
|
||||
|
||||
declare let g_inpaint_mask_layer: any
|
||||
declare let g_sd_url: any
|
||||
declare let g_controlnet_max_models: any
|
||||
declare let g_generation_session: any
|
||||
declare let g_last_seed: any
|
||||
interface SessionData {
|
||||
init_image?: string
|
||||
mask?: string
|
||||
selectionInfo?: any
|
||||
}
|
||||
|
||||
async function saveOutputImagesToDrive(images_info: any, settings: any) {
|
||||
const base64OutputImages = [] //delete all previouse images, Note move this to session end ()
|
||||
let index = 0
|
||||
for (const image_info of images_info) {
|
||||
const path = image_info['path']
|
||||
const base64_image = image_info['base64']
|
||||
base64OutputImages[index] = base64_image
|
||||
const [document_name, image_name] = path.split('/')
|
||||
await io.saveFileInSubFolder(base64_image, document_name, image_name) //save the output image
|
||||
const json_file_name = `${image_name.split('.')[0]}.json`
|
||||
settings['auto_metadata'] = image_info?.auto_metadata
|
||||
|
||||
await io.saveJsonFileInSubFolder(
|
||||
settings,
|
||||
document_name,
|
||||
json_file_name
|
||||
) //save the settings
|
||||
index += 1
|
||||
}
|
||||
g_last_seed =
|
||||
images_info?.length > 0 ? images_info[0]?.auto_metadata?.Seed : '-1'
|
||||
return base64OutputImages
|
||||
}
|
||||
class Mode {
|
||||
constructor() {}
|
||||
|
||||
async initializeSession(): Promise<SessionData> {
|
||||
return {}
|
||||
}
|
||||
static async generate(settings: any): Promise<{
|
||||
output_images: any
|
||||
response_json: any
|
||||
}> {
|
||||
return { output_images: [], response_json: null }
|
||||
}
|
||||
//return settings that would be used by the restApi
|
||||
static async getSettings(session_data: any) {
|
||||
const ui_settings = await session.getSettings(session_data)
|
||||
|
||||
return ui_settings
|
||||
}
|
||||
//take the output from restapi and formate it to a standard formate the plugin ui understand
|
||||
static async processOutput(images_info: any, settings: any): Promise<any> {
|
||||
const base64OutputImages = await saveOutputImagesToDrive(
|
||||
images_info,
|
||||
settings
|
||||
)
|
||||
return base64OutputImages
|
||||
}
|
||||
static async interrupt() {
|
||||
return await this.requestInterrupt()
|
||||
}
|
||||
|
||||
static async requestInterrupt() {
|
||||
const full_url = `${g_sd_url}/sdapi/v1/interrupt`
|
||||
try {
|
||||
console.log('requestInterrupt: ')
|
||||
let request = await fetch(full_url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
// console.log('interrupt request:', request)
|
||||
let json = await request.json()
|
||||
return json
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Txt2ImgMode extends Mode {
|
||||
// constructor() {
|
||||
// }
|
||||
|
||||
static async initializeSession(): Promise<SessionData> {
|
||||
const selectionInfo = await psapi.getSelectionInfoExe()
|
||||
|
||||
const init_image = ''
|
||||
const mask = ''
|
||||
return { selectionInfo, init_image, mask }
|
||||
}
|
||||
|
||||
//return settings that would be used by the restApi
|
||||
// static async getSettings() {
|
||||
// const ui_settings = await session.getSettings()
|
||||
|
||||
// return ui_settings
|
||||
// }
|
||||
|
||||
//@ts-ignore
|
||||
static async requestTxt2Img(payload) {
|
||||
try {
|
||||
console.log('requestTxt2Img(): about to send a fetch request')
|
||||
|
||||
let json = await python_replacement.txt2ImgRequest(payload)
|
||||
console.log('requestTxt2Img json:', json)
|
||||
|
||||
return json
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
//REFACTOR: reuse the same code for (requestControlNetTxt2Img,requestControlNetImg2Img)
|
||||
static async requestControlNetTxt2Img(plugin_settings: any) {
|
||||
console.log('requestControlNetTxt2Img: ')
|
||||
|
||||
const full_url = `${g_sd_url}/sdapi/v1/txt2img`
|
||||
|
||||
const control_net_settings =
|
||||
mapPluginSettingsToControlNet(plugin_settings)
|
||||
let control_networks = []
|
||||
// let active_control_networks = 0
|
||||
for (let index = 0; index < g_controlnet_max_models; index++) {
|
||||
if (!getEnableControlNet(index)) {
|
||||
control_networks[index] = false
|
||||
continue
|
||||
}
|
||||
control_networks[index] = true
|
||||
|
||||
if (
|
||||
!control_net_settings['controlnet_units'][index]['input_image']
|
||||
) {
|
||||
//@ts-ignore
|
||||
app.showAlert('you need to add a valid ControlNet input image')
|
||||
throw 'you need to add a valid ControlNet input image'
|
||||
}
|
||||
|
||||
if (!control_net_settings['controlnet_units'][index]['module']) {
|
||||
//@ts-ignore
|
||||
app.showAlert('you need to select a valid ControlNet Module')
|
||||
throw 'you need to select a valid ControlNet Module'
|
||||
}
|
||||
|
||||
if (
|
||||
(!control_net_settings['controlnet_units'][index]['model'] &&
|
||||
!getModuleDetail()[
|
||||
control_net_settings['controlnet_units'][index][
|
||||
'module'
|
||||
]
|
||||
].model_free) ||
|
||||
control_net_settings['controlnet_units'][index][
|
||||
'model'
|
||||
].toLowerCase() === 'none'
|
||||
) {
|
||||
//@ts-ignore
|
||||
app.showAlert('you need to select a valid ControlNet Model')
|
||||
throw 'you need to select a valid ControlNet Model'
|
||||
}
|
||||
}
|
||||
|
||||
let request = await fetch(full_url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(control_net_settings),
|
||||
})
|
||||
|
||||
let json = await request.json()
|
||||
console.log('json:', json)
|
||||
|
||||
//update the mask in controlNet tab
|
||||
const numOfImages = json['images'].length
|
||||
let numberOfAnnotations =
|
||||
numOfImages - session_ts.store.data.ui_settings.batch_size
|
||||
if (numberOfAnnotations < 0) numberOfAnnotations = 0
|
||||
|
||||
const base64_mask = json['images'].slice(
|
||||
numOfImages - numberOfAnnotations
|
||||
)
|
||||
|
||||
let mask_index = 0
|
||||
|
||||
for (let index = 0; index < control_networks.length; index++) {
|
||||
if (
|
||||
control_networks[index] == false ||
|
||||
mask_index >= numberOfAnnotations
|
||||
)
|
||||
continue
|
||||
control_net.setControlDetectMapSrc(base64_mask[mask_index], index)
|
||||
g_generation_session.controlNetMask[index] = base64_mask[mask_index]
|
||||
mask_index++
|
||||
}
|
||||
// g_generation_session.controlNetMask = base64_mask
|
||||
|
||||
const standard_response =
|
||||
await python_replacement.convertToStandardResponse(
|
||||
control_net_settings,
|
||||
json['images'].slice(0, numOfImages - numberOfAnnotations),
|
||||
plugin_settings['uniqueDocumentId']
|
||||
)
|
||||
console.log('standard_response:', standard_response)
|
||||
|
||||
return standard_response
|
||||
}
|
||||
//REFACTOR: move to generation.js
|
||||
static async generate(
|
||||
settings: any
|
||||
): Promise<{ output_images: any; response_json: any }> {
|
||||
let response_json
|
||||
let output_images
|
||||
try {
|
||||
// const b_enable_control_net = control_net.getEnableControlNet()
|
||||
const b_enable_control_net = control_net.isControlNetModeEnable()
|
||||
|
||||
if (b_enable_control_net) {
|
||||
//use control net
|
||||
if (session_ts.store.data.generation_number === 1) {
|
||||
session_ts.store.data.controlnet_input_image =
|
||||
await io.getImg2ImgInitImage()
|
||||
}
|
||||
// console.log(
|
||||
// 'session_ts.store.data.controlnet_input_image: ',
|
||||
// session_ts.store.data.controlnet_input_image
|
||||
// )
|
||||
|
||||
response_json = await this.requestControlNetTxt2Img(settings)
|
||||
} else {
|
||||
response_json = await this.requestTxt2Img(settings)
|
||||
}
|
||||
|
||||
output_images = await this.processOutput(
|
||||
response_json.images_info,
|
||||
settings
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
console.warn('output_images: ', output_images)
|
||||
console.warn('response_json: ', response_json)
|
||||
}
|
||||
return { output_images, response_json }
|
||||
}
|
||||
//take the output from restapi and formate it to a standard formate the plugin ui understand
|
||||
static async processOutput(images_info: any, settings: any): Promise<any> {
|
||||
const base64OutputImages = await saveOutputImagesToDrive(
|
||||
images_info,
|
||||
settings
|
||||
)
|
||||
return base64OutputImages
|
||||
}
|
||||
}
|
||||
|
||||
export class Img2ImgMode extends Mode {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
//REFACTOR: reuse the same code for (requestControlNetTxt2Img,requestControlNetImg2Img)
|
||||
static async requestControlNetImg2Img(plugin_settings: any) {
|
||||
const full_url = `${g_sd_url}/sdapi/v1/img2img`
|
||||
const control_net_settings =
|
||||
mapPluginSettingsToControlNet(plugin_settings)
|
||||
|
||||
// let control_networks = 0
|
||||
let control_networks = []
|
||||
for (let index = 0; index < g_controlnet_max_models; index++) {
|
||||
if (!getEnableControlNet(index)) {
|
||||
control_networks[index] = false
|
||||
continue
|
||||
}
|
||||
control_networks[index] = true
|
||||
if (
|
||||
!control_net_settings['controlnet_units'][index]['input_image']
|
||||
) {
|
||||
//@ts-ignore
|
||||
app.showAlert('you need to add a valid ControlNet input image')
|
||||
throw 'you need to add a valid ControlNet input image'
|
||||
}
|
||||
|
||||
if (!control_net_settings['controlnet_units'][index]['module']) {
|
||||
//@ts-ignore
|
||||
app.showAlert('you need to select a valid ControlNet Module')
|
||||
throw 'you need to select a valid ControlNet Module'
|
||||
}
|
||||
if (
|
||||
(!control_net_settings['controlnet_units'][index]['model'] &&
|
||||
!getModuleDetail()[
|
||||
control_net_settings['controlnet_units'][index][
|
||||
'module'
|
||||
]
|
||||
].model_free) ||
|
||||
control_net_settings['controlnet_units'][index][
|
||||
'model'
|
||||
].toLowerCase() === 'none'
|
||||
) {
|
||||
//@ts-ignore
|
||||
app.showAlert('you need to select a valid ControlNet Model')
|
||||
throw 'you need to select a valid ControlNet Model'
|
||||
}
|
||||
}
|
||||
|
||||
let request = await fetch(full_url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(control_net_settings),
|
||||
// body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
let json = await request.json()
|
||||
console.log('json:', json)
|
||||
|
||||
//update the mask in controlNet tab
|
||||
const numOfImages = json['images'].length
|
||||
let numberOfAnnotations =
|
||||
numOfImages - session_ts.store.data.ui_settings.batch_size
|
||||
if (numberOfAnnotations < 0) numberOfAnnotations = 0
|
||||
|
||||
// To fix a bug: when Ultimate SD Upscale is active and running, the detection maps won’t be retrieved.
|
||||
// So set its value to 0 to avoid the result images being loaded in the annotation map interface.
|
||||
if (
|
||||
scripts.script_store.isInstalled() &&
|
||||
scripts.script_store.is_active &&
|
||||
scripts.script_store.selected_script_name !== 'None' &&
|
||||
scripts.script_store.is_selected_script_available
|
||||
) {
|
||||
numberOfAnnotations = 0
|
||||
}
|
||||
const base64_mask = json['images'].slice(
|
||||
numOfImages - numberOfAnnotations
|
||||
)
|
||||
|
||||
let mask_index = 0
|
||||
for (let index = 0; index < control_networks.length; index++) {
|
||||
if (
|
||||
control_networks[index] == false ||
|
||||
mask_index >= numberOfAnnotations
|
||||
)
|
||||
continue
|
||||
control_net.setControlDetectMapSrc(base64_mask[mask_index], index)
|
||||
g_generation_session.controlNetMask[index] = base64_mask[mask_index]
|
||||
mask_index++
|
||||
}
|
||||
|
||||
const standard_response =
|
||||
await python_replacement.convertToStandardResponse(
|
||||
control_net_settings,
|
||||
json['images'].slice(0, numOfImages - numberOfAnnotations),
|
||||
plugin_settings['uniqueDocumentId']
|
||||
)
|
||||
console.log('standard_response:', standard_response)
|
||||
|
||||
return standard_response
|
||||
}
|
||||
|
||||
static async requestImg2Img(payload: any) {
|
||||
console.log('requestImg2Img(): about to send a fetch request')
|
||||
try {
|
||||
let json = await python_replacement.img2ImgRequest(
|
||||
g_sd_url,
|
||||
payload
|
||||
)
|
||||
console.log('requestImg2Img json:')
|
||||
console.dir(json)
|
||||
|
||||
return json
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
static async initializeSession(): Promise<SessionData> {
|
||||
const selectionInfo = await psapi.getSelectionInfoExe()
|
||||
const init_image = await io.getImg2ImgInitImage()
|
||||
const mask = ''
|
||||
return { selectionInfo, init_image, mask }
|
||||
}
|
||||
static async generate(
|
||||
settings: any
|
||||
): Promise<{ output_images: any; response_json: any }> {
|
||||
let response_json
|
||||
let output_images
|
||||
try {
|
||||
//checks on index 0 as if not enabled ignores the rest
|
||||
const b_enable_control_net = control_net.isControlNetModeEnable()
|
||||
|
||||
if (b_enable_control_net) {
|
||||
//use control net
|
||||
response_json = await this.requestControlNetImg2Img(settings)
|
||||
} else {
|
||||
response_json = await this.requestImg2Img(settings)
|
||||
}
|
||||
output_images = await this.processOutput(
|
||||
response_json.images_info,
|
||||
settings
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
console.warn('output_images: ', output_images)
|
||||
console.warn('response_json: ', response_json)
|
||||
}
|
||||
|
||||
return { output_images, response_json }
|
||||
}
|
||||
}
|
||||
|
||||
export class InpaintMode extends Img2ImgMode {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
static async initializeSession() {
|
||||
const selectionInfo = await psapi.getSelectionInfoExe()
|
||||
let init_image
|
||||
let mask
|
||||
|
||||
try {
|
||||
await executeAsModal(
|
||||
async () => {
|
||||
if (layer_util.Layer.doesLayerExist(g_inpaint_mask_layer)) {
|
||||
g_inpaint_mask_layer.opacity = 100
|
||||
}
|
||||
},
|
||||
{ commandName: 'Set Inpaint Layer Opacity to 100%' }
|
||||
)
|
||||
|
||||
const obj = await io.getInpaintInitImageAndMask()
|
||||
init_image = obj.init_image
|
||||
mask = obj.mask
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
|
||||
return { selectionInfo, init_image, mask }
|
||||
}
|
||||
}
|
||||
|
||||
export class LassoInpaintMode extends Img2ImgMode {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
static async initializeSession() {
|
||||
await selection.channelToSelectionExe('mask')
|
||||
|
||||
try {
|
||||
await executeAsModal(
|
||||
async () => {
|
||||
if (layer_util.Layer.doesLayerExist(g_inpaint_mask_layer)) {
|
||||
g_inpaint_mask_layer.opacity = 100
|
||||
}
|
||||
},
|
||||
{ commandName: 'Set Inpaint Layer Opacity to 100%' }
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
const [init_image, mask] = await selection.inpaintLassoInitImageAndMask(
|
||||
'mask'
|
||||
)
|
||||
|
||||
const selectionInfo = await psapi.getSelectionInfoExe()
|
||||
return { selectionInfo, init_image, mask }
|
||||
}
|
||||
}
|
||||
|
||||
export class OutpaintMode extends Img2ImgMode {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
static async initializeSession() {
|
||||
const selectionInfo = await psapi.getSelectionInfoExe()
|
||||
let init_image
|
||||
let mask
|
||||
|
||||
try {
|
||||
const obj = await io.getOutpaintInitImageAndMask()
|
||||
init_image = obj.init_image
|
||||
mask = obj.mask
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
|
||||
return { selectionInfo, init_image, mask }
|
||||
}
|
||||
}
|
||||
export class UpscaleMode extends Img2ImgMode {
|
||||
static async requestExtraSingleImage(payload: any) {
|
||||
try {
|
||||
let json = await python_replacement.extraSingleImageRequest(
|
||||
g_sd_url,
|
||||
payload
|
||||
)
|
||||
|
||||
return json
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
static async getSettings(session_data: any) {
|
||||
//REFACTOR: move to generation_settings.js
|
||||
|
||||
let payload: any = {}
|
||||
try {
|
||||
const upscaling_resize = html_manip.getUpscaleSize()
|
||||
const gfpgan_visibility = html_manip.getGFPGANVisibility()
|
||||
const codeformer_visibility = html_manip.getCodeFormerVisibility()
|
||||
const codeformer_weight = html_manip.getCodeFormerWeight()
|
||||
// const selection_info = await psapi.getSelectionInfoExe()
|
||||
const selection_info = session_data.selectionInfo
|
||||
const width = selection_info.width * upscaling_resize
|
||||
const height = selection_info.height * upscaling_resize
|
||||
//resize_mode = 0 means "resize to upscaling_resize"
|
||||
//resize_mode = 1 means "resize to width and height"
|
||||
payload['resize_mode'] = 0
|
||||
payload['show_extras_results'] = 0
|
||||
payload['gfpgan_visibility'] = gfpgan_visibility
|
||||
payload['codeformer_visibility'] = codeformer_visibility
|
||||
payload['codeformer_weight'] = codeformer_weight
|
||||
payload['upscaling_resize'] = upscaling_resize
|
||||
payload['upscaling_resize_w'] = width
|
||||
payload['upscaling_resize_h'] = height
|
||||
payload['upscaling_crop'] = true
|
||||
const upscaler1 =
|
||||
//@ts-ignore
|
||||
document.querySelector('#hrModelsMenuUpscale1').value
|
||||
|
||||
payload['upscaler_1'] = upscaler1 === undefined ? 'None' : upscaler1
|
||||
const upscaler2 =
|
||||
//@ts-ignore
|
||||
document.querySelector('#hrModelsMenuUpscale2').value
|
||||
payload['upscaler_2'] = upscaler2 === undefined ? 'None' : upscaler2
|
||||
const extras_upscaler_2_visibility =
|
||||
html_manip.getUpscaler2Visibility()
|
||||
payload['extras_upscaler_2_visibility'] =
|
||||
extras_upscaler_2_visibility
|
||||
payload['upscale_first'] = false
|
||||
|
||||
payload['image'] = session_data.init_image
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
return payload
|
||||
}
|
||||
static async generate(settings: any): Promise<{
|
||||
output_images: any
|
||||
response_json: any
|
||||
}> {
|
||||
let response_json
|
||||
let output_images
|
||||
try {
|
||||
response_json = await this.requestExtraSingleImage(settings)
|
||||
output_images = await this.processOutput(
|
||||
response_json.images_info,
|
||||
settings
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
console.warn('output_images: ', output_images)
|
||||
console.warn('response_json: ', response_json)
|
||||
}
|
||||
return { output_images, response_json }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
import { reaction } from 'mobx'
|
||||
import { AStore } from '../main/astore'
|
||||
import { io, layer_util } from '../util/oldSystem'
|
||||
import Locale from '../locale/locale'
|
||||
import { session_ts } from '../entry'
|
||||
import { app, core } from 'photoshop'
|
||||
|
||||
const executeAsModal = core.executeAsModal
|
||||
|
||||
export const store = new AStore({
|
||||
progress_layer: null,
|
||||
timer_id: null,
|
||||
progress_value: 0,
|
||||
progress_image: '',
|
||||
progress_image_height: 0,
|
||||
progress_label: Locale('Progress..'),
|
||||
can_update: true,
|
||||
can_update_progress_layer: true,
|
||||
})
|
||||
declare let g_sd_url: string
|
||||
|
||||
async function updateProgressImage(progress_base64: string) {
|
||||
try {
|
||||
store.data.can_update_progress_layer = false
|
||||
await executeAsModal(
|
||||
async (context: any) => {
|
||||
const history_id = await context.hostControl.suspendHistory({
|
||||
documentID: app.activeDocument.id, //TODO: change this to the session document id
|
||||
name: 'Progress Image',
|
||||
})
|
||||
await Progress.deleteProgressLayer() // delete the old progress layer
|
||||
|
||||
//update the progress image
|
||||
const selection_info = await session_ts.store.data.selectionInfo
|
||||
|
||||
const b_exsit = layer_util.Layer.doesLayerExist(
|
||||
store.data.progress_layer
|
||||
)
|
||||
if (!b_exsit && progress_base64) {
|
||||
const layer = await io.IO.base64ToLayer(
|
||||
progress_base64,
|
||||
'temp_progress_image.png',
|
||||
selection_info.left,
|
||||
selection_info.top,
|
||||
selection_info.width,
|
||||
selection_info.height
|
||||
)
|
||||
store.data.progress_layer = layer // sotre the new progress layer// TODO: make sure you delete the progress layer when the geneeration request end
|
||||
}
|
||||
await context.hostControl.resumeHistory(history_id)
|
||||
},
|
||||
{ commandName: 'update progress layer' }
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
} finally {
|
||||
store.data.can_update_progress_layer = true
|
||||
if (!store.data.can_update) {
|
||||
//delete the last progress layer
|
||||
await Progress.deleteProgressLayer() // delete the old progress layer
|
||||
}
|
||||
}
|
||||
}
|
||||
reaction(
|
||||
() => {
|
||||
return store.data.progress_image
|
||||
},
|
||||
async (progress_image) => {
|
||||
if (store.data.progress_image_height === 0) {
|
||||
const { width, height } = await io.getImageSize(progress_image)
|
||||
store.data.progress_image_height = height
|
||||
}
|
||||
const b_update_progress_layer: Boolean = (
|
||||
document.querySelector('.chLiveProgressImageClass') as any
|
||||
).checked
|
||||
if (
|
||||
b_update_progress_layer &&
|
||||
parseInt(session_ts.store.data.ui_settings?.batch_size) === 1 &&
|
||||
store.data.can_update_progress_layer &&
|
||||
store.data.can_update // progress is still active
|
||||
) {
|
||||
await updateProgressImage(progress_image)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export async function requestProgress() {
|
||||
try {
|
||||
console.log('requestProgress: ')
|
||||
|
||||
const full_url = `${g_sd_url}/sdapi/v1/progress?skip_current_image=false`
|
||||
let request = await fetch(full_url)
|
||||
const json = await request.json()
|
||||
// console.log('progress json:', json)
|
||||
|
||||
return json
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
// console.log('json: ', json)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export class Progress {
|
||||
static timer_id: any = null
|
||||
static async deleteProgressImage() {
|
||||
// preview.store.updateProperty('image', null)
|
||||
|
||||
await this.deleteProgressLayer()
|
||||
}
|
||||
|
||||
static async deleteProgressLayer() {
|
||||
try {
|
||||
await layer_util.deleteLayers([store.data.progress_layer]) // delete the old progress layer
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
static startTimer(callback: any, interval: number = 1500) {
|
||||
store.data.can_update = true
|
||||
//clear the old timer if it exist
|
||||
try {
|
||||
store.data.progress_value = 0
|
||||
store.data.progress_image = ''
|
||||
store.data.progress_image_height = 0
|
||||
store.data.progress_label = ''
|
||||
this.timer_id = clearInterval(this.timer_id)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
|
||||
this.timer_id = setInterval(callback, interval)
|
||||
}
|
||||
|
||||
static async endTimer(callback: any) {
|
||||
try {
|
||||
this.timer_id = clearInterval(this.timer_id)
|
||||
store.data.can_update = false
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
try {
|
||||
if (callback?.constructor.name === 'AsyncFunction') {
|
||||
await callback() // may cause an issue if this an async
|
||||
} else {
|
||||
callback() // may cause an issue if this an async
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ProgressAutomatic extends Progress {}
|
||||
|
||||
export class ProgressHordeNative {}
|
||||
|
|
@ -0,0 +1,457 @@
|
|||
import { app } from 'photoshop'
|
||||
import { control_net, preview, viewer, progress } from '../entry'
|
||||
import Locale from '../locale/locale'
|
||||
import { AStore } from '../main/astore'
|
||||
import {
|
||||
html_manip,
|
||||
io,
|
||||
psapi,
|
||||
python_replacement,
|
||||
sdapi,
|
||||
} from '../util/oldSystem'
|
||||
import { GenerationModeEnum } from '../util/ts/enum'
|
||||
import {
|
||||
Img2ImgMode,
|
||||
InpaintMode,
|
||||
LassoInpaintMode,
|
||||
OutpaintMode,
|
||||
Txt2ImgMode,
|
||||
UpscaleMode,
|
||||
} from './modes'
|
||||
import { Progress } from './progress'
|
||||
import { reaction } from 'mobx'
|
||||
|
||||
declare let g_inpaint_mask_layer: any
|
||||
declare const g_image_not_found_url: string
|
||||
declare let g_current_batch_index: number
|
||||
export const store = new AStore({
|
||||
// activeBase64InitImage: '',
|
||||
// activeBase64Mask: '',
|
||||
init_image: '',
|
||||
active_mask: '', // this is the mask that is been used in the current generation
|
||||
mask: '', // the user inputted mask, also can be the mask generated by photoshop dependant on the generation mode
|
||||
expanded_mask: '', // mask after expanded
|
||||
monoMask: '', //monochrome mask, no gradation
|
||||
preprocessed_mask: '', //
|
||||
sd_mask: '', // mask send to sd as payload[mask]
|
||||
mode: '',
|
||||
ui_settings: {},
|
||||
selectionInfo: {}, //the session selection info
|
||||
current_selection_info: {}, // any new selection, could be undefined too
|
||||
can_generate: true, // is generation currently in progress
|
||||
can_generate_more: false, //
|
||||
is_active: false, // is session active
|
||||
is_interrupted: false, // did we interrupt the generation
|
||||
generation_number: 0, // generation number per session, 0 mean first generation
|
||||
controlnet_input_image: '', // the controlnet the image that will controlnet load
|
||||
|
||||
//plugin related state:
|
||||
auto_photoshop_sd_extension_status: true,
|
||||
})
|
||||
|
||||
reaction(
|
||||
() => {
|
||||
return [store.data.init_image, store.data.mask] as [string, string]
|
||||
},
|
||||
([init_image, mask]: [string, string]) => {
|
||||
html_manip.setInitImageSrc(
|
||||
init_image
|
||||
? 'data:image/png;base64,' + init_image
|
||||
: g_image_not_found_url
|
||||
)
|
||||
html_manip.setInitImageMaskSrc(
|
||||
mask ? 'data:image/png;base64,' + mask : g_image_not_found_url
|
||||
)
|
||||
}
|
||||
)
|
||||
reaction(
|
||||
() => {
|
||||
return store.data.auto_photoshop_sd_extension_status
|
||||
},
|
||||
(auto_photoshop_sd_extension_status: boolean) => {
|
||||
if (auto_photoshop_sd_extension_status) {
|
||||
} else {
|
||||
app.showAlert(
|
||||
'Please install the Auto-Photoshop-SD Extension from Automatic1111 Extensions tab '
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
function hasSelectionChanged(new_selection: any, old_selection: any) {
|
||||
try {
|
||||
if (
|
||||
new_selection.left === old_selection.left &&
|
||||
new_selection.bottom === old_selection.bottom &&
|
||||
new_selection.right === old_selection.right &&
|
||||
new_selection.top === old_selection.top
|
||||
) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} catch (e) {
|
||||
//if any properties is missing
|
||||
|
||||
// console.warn(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
reaction(
|
||||
() => {
|
||||
return store.data.current_selection_info
|
||||
},
|
||||
(new_selection_info) => {
|
||||
console.log(
|
||||
'store.data.current_selection_info: reaction is triggered ',
|
||||
store.data.current_selection_info
|
||||
)
|
||||
if (hasSelectionChanged(new_selection_info, store.data.selectionInfo)) {
|
||||
store.data.can_generate_more = false
|
||||
} else {
|
||||
if (store.data.is_active) store.data.can_generate_more = true
|
||||
}
|
||||
}
|
||||
)
|
||||
reaction(
|
||||
() => {
|
||||
return store.data.is_active
|
||||
},
|
||||
(is_active) => {
|
||||
console.log(
|
||||
'store.data.is_active: reaction is triggered ',
|
||||
store.data.is_active
|
||||
)
|
||||
|
||||
if (is_active) {
|
||||
store.data.can_generate_more = true
|
||||
} else {
|
||||
store.data.can_generate_more = false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
interface ModeToClassMap {
|
||||
[key: string]:
|
||||
| typeof Txt2ImgMode
|
||||
| typeof Img2ImgMode
|
||||
| typeof InpaintMode
|
||||
| typeof LassoInpaintMode
|
||||
| typeof OutpaintMode
|
||||
| typeof UpscaleMode
|
||||
}
|
||||
|
||||
const modeToClassMap: ModeToClassMap = {
|
||||
[GenerationModeEnum.Txt2Img]: Txt2ImgMode,
|
||||
[GenerationModeEnum.Img2Img]: Img2ImgMode,
|
||||
[GenerationModeEnum.Inpaint]: InpaintMode,
|
||||
[GenerationModeEnum.LassoInpaint]: LassoInpaintMode,
|
||||
[GenerationModeEnum.Outpaint]: OutpaintMode,
|
||||
[GenerationModeEnum.Upscale]: UpscaleMode,
|
||||
}
|
||||
|
||||
export async function getExpandedMask(
|
||||
mask: string,
|
||||
expansion_value: number,
|
||||
blur: number
|
||||
) {
|
||||
let expanded_mask = mask
|
||||
try {
|
||||
let use_sharp_mask = false
|
||||
|
||||
if (
|
||||
use_sharp_mask === false &&
|
||||
mask &&
|
||||
expansion_value >= 0 &&
|
||||
blur >= 0
|
||||
) {
|
||||
//only if mask is available and sharp_mask is off
|
||||
// use blurry and expanded mask
|
||||
const iterations = expansion_value
|
||||
expanded_mask = await python_replacement.maskExpansionRequest(
|
||||
mask,
|
||||
iterations,
|
||||
blur
|
||||
)
|
||||
}
|
||||
|
||||
// return expanded_mask
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
} finally {
|
||||
return expanded_mask
|
||||
}
|
||||
}
|
||||
|
||||
export class Session {
|
||||
constructor() {}
|
||||
static async initializeSession(mode: GenerationModeEnum): Promise<any> {
|
||||
try {
|
||||
store.data.mode = mode
|
||||
|
||||
if (modeToClassMap.hasOwnProperty(store.data.mode)) {
|
||||
const { selectionInfo, init_image, mask } =
|
||||
await modeToClassMap[store.data.mode].initializeSession()
|
||||
store.data.selectionInfo = selectionInfo
|
||||
if (init_image) {
|
||||
// const opaque_init_image = await io.fixTransparentEdges(
|
||||
// init_image
|
||||
// )
|
||||
store.data.init_image = init_image
|
||||
// store.data.init_image = opaque_init_image
|
||||
|
||||
await viewer.updateViewerStoreImageAndThumbnail(
|
||||
viewer.init_store,
|
||||
[store.data.init_image]
|
||||
)
|
||||
}
|
||||
|
||||
if (mask) {
|
||||
// store.data.mask = mask
|
||||
const mask_monochrome =
|
||||
await io.convertGrayscaleToMonochrome(mask)
|
||||
store.data.monoMask = mask_monochrome
|
||||
store.data.mask = mask
|
||||
|
||||
const expansion_value: number = parseInt(
|
||||
//@ts-ignore
|
||||
document.getElementById('slMaskExpansion').value
|
||||
)
|
||||
const mask_blur = html_manip.getMaskBlur()
|
||||
store.data.expanded_mask = await getExpandedMask(
|
||||
mask,
|
||||
expansion_value,
|
||||
mask_blur
|
||||
)
|
||||
store.data.preprocessed_mask = mask
|
||||
|
||||
await viewer.updateViewerStoreImageAndThumbnail(
|
||||
viewer.mask_store,
|
||||
|
||||
[
|
||||
store.data.preprocessed_mask,
|
||||
store.data.monoMask,
|
||||
store.data.expanded_mask,
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
selectionInfo,
|
||||
init_image,
|
||||
mask: store.data.preprocessed_mask,
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
static async getSettings(session_data: any) {
|
||||
const ui_settings = await modeToClassMap[store.data.mode].getSettings(
|
||||
session_data
|
||||
)
|
||||
|
||||
store.data.ui_settings = ui_settings
|
||||
return ui_settings
|
||||
}
|
||||
static processOutput() {}
|
||||
static validate() {
|
||||
if (store.data.is_active) {
|
||||
//@ts-ignore
|
||||
app.showAlert('You forgot to select images!')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
static async initializeGeneration() {
|
||||
store.data.is_interrupted = false
|
||||
store.data.can_generate = false
|
||||
store.data.generation_number += 1
|
||||
g_current_batch_index += 1
|
||||
}
|
||||
static async generate(mode: GenerationModeEnum): Promise<{
|
||||
output_images: any
|
||||
response_json: any
|
||||
}> {
|
||||
if (!store.data.can_generate) {
|
||||
// return null
|
||||
throw Error(
|
||||
'A Generation is progress, wait for to finish be fore you generate again'
|
||||
)
|
||||
}
|
||||
if (store.data.is_active) {
|
||||
//you can only use the generate button once per session
|
||||
//@ts-ignore
|
||||
app.showAlert(
|
||||
'You must end the current session before starting a new one'
|
||||
)
|
||||
throw Error(
|
||||
'Session is still Active. Need to end the Session before starting a new Session'
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
this.initializeGeneration()
|
||||
|
||||
store.data.is_active = true
|
||||
this.getProgress()
|
||||
|
||||
const { selectionInfo, init_image, mask } =
|
||||
await this.initializeSession(mode)
|
||||
const ui_settings = await this.getSettings({
|
||||
selectionInfo,
|
||||
init_image,
|
||||
mask,
|
||||
})
|
||||
|
||||
//this should be part of initialization method or gettingSettings()
|
||||
//calculate the expanded mask from mask
|
||||
if (
|
||||
[
|
||||
GenerationModeEnum.Inpaint,
|
||||
GenerationModeEnum.LassoInpaint,
|
||||
GenerationModeEnum.Outpaint,
|
||||
].includes(mode)
|
||||
) {
|
||||
const expansion_value: number = parseInt(
|
||||
//@ts-ignore
|
||||
document.getElementById('slMaskExpansion').value
|
||||
)
|
||||
const mask_blur = html_manip.getMaskBlur()
|
||||
store.data.expanded_mask = await getExpandedMask(
|
||||
mask,
|
||||
expansion_value,
|
||||
mask_blur
|
||||
)
|
||||
|
||||
ui_settings['mask'] = store.data.expanded_mask
|
||||
}
|
||||
var { output_images, response_json } = await modeToClassMap[
|
||||
mode
|
||||
].generate(ui_settings)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
} finally {
|
||||
store.data.can_generate = true
|
||||
await this.endProgress()
|
||||
}
|
||||
|
||||
return { output_images, response_json }
|
||||
}
|
||||
|
||||
static async generateMore(): Promise<{
|
||||
output_images: any
|
||||
response_json: any
|
||||
}> {
|
||||
if (!store.data.can_generate) {
|
||||
throw Error(
|
||||
'A Generation is progress, wait for to finish be fore you generate again'
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
this.initializeGeneration()
|
||||
store.data.can_generate = false
|
||||
this.getProgress()
|
||||
const session_data = {
|
||||
init_image: store.data.init_image,
|
||||
mask: store.data.preprocessed_mask,
|
||||
selectionInfo: store.data.selectionInfo,
|
||||
}
|
||||
const ui_settings = await this.getSettings(session_data)
|
||||
if (
|
||||
[
|
||||
GenerationModeEnum.Inpaint,
|
||||
GenerationModeEnum.LassoInpaint,
|
||||
GenerationModeEnum.Outpaint,
|
||||
].includes(store.data.mode)
|
||||
) {
|
||||
const expansion_value: number = parseInt(
|
||||
//@ts-ignore
|
||||
document.getElementById('slMaskExpansion').value
|
||||
)
|
||||
const mask_blur = html_manip.getMaskBlur()
|
||||
store.data.expanded_mask = await getExpandedMask(
|
||||
session_data.mask,
|
||||
expansion_value,
|
||||
mask_blur
|
||||
)
|
||||
|
||||
ui_settings['mask'] = store.data.expanded_mask
|
||||
}
|
||||
var { output_images, response_json } = await modeToClassMap[
|
||||
store.data.mode
|
||||
].generate(ui_settings)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
} finally {
|
||||
store.data.can_generate = true
|
||||
await this.endProgress()
|
||||
}
|
||||
return { output_images, response_json }
|
||||
}
|
||||
static async interrupt(): Promise<any> {
|
||||
try {
|
||||
await modeToClassMap[store.data.mode].interrupt()
|
||||
store.data.is_interrupted = true
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
} finally {
|
||||
//no need to reset progress since generate and generateMore will always finish executing after interrupt
|
||||
// store.data.can_generate = true
|
||||
// this.endProgress()
|
||||
}
|
||||
}
|
||||
static async getProgress() {
|
||||
// Progress.startSudoProgress()
|
||||
progress.Progress.startTimer(async () => {
|
||||
try {
|
||||
let json = await progress.requestProgress()
|
||||
const can_update = progress.store.data.can_update
|
||||
if (!can_update) {
|
||||
return null
|
||||
}
|
||||
if (json?.progress) {
|
||||
progress.store.updateProperty(
|
||||
'progress_value',
|
||||
json?.progress * 100
|
||||
)
|
||||
}
|
||||
|
||||
if (json?.current_image) {
|
||||
progress.store.updateProperty(
|
||||
'progress_image',
|
||||
json?.current_image
|
||||
)
|
||||
}
|
||||
|
||||
progress.store.data.progress_label = Locale('Progress...')
|
||||
|
||||
// console.log('progress object json: ', json)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}, 2000)
|
||||
}
|
||||
static async endProgress() {
|
||||
await progress.Progress.endTimer(async () => {
|
||||
progress.store.data.progress_value = 0
|
||||
progress.store.data.progress_image = ''
|
||||
progress.store.data.progress_image_height = 0
|
||||
await progress.Progress.deleteProgressLayer()
|
||||
})
|
||||
}
|
||||
static endSession() {
|
||||
viewer.resetViewer() //may cause circular dependency
|
||||
store.data.is_active = false //
|
||||
store.data.init_image = ''
|
||||
store.data.mask = ''
|
||||
store.data.expanded_mask = ''
|
||||
store.data.preprocessed_mask = ''
|
||||
store.data.generation_number = 0
|
||||
store.data.controlnet_input_image = ''
|
||||
g_current_batch_index = -1 // first generation will add +1 to get => 0
|
||||
}
|
||||
|
||||
static async getOutput() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
.generateButtonMargin {
|
||||
margin-top: 1px;
|
||||
margin-bottom: 3px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.generateColor {
|
||||
|
||||
background-color: #ff595e;
|
||||
|
||||
}
|
||||
|
||||
.generateMoreColor {
|
||||
|
||||
background-color: #6db579;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import { observer } from 'mobx-react'
|
||||
import { AStore } from '../main/astore'
|
||||
|
||||
import { SpCheckBox, SpMenu } from '../util/elements'
|
||||
import Locale from '../locale/locale'
|
||||
import globalStore from '../globalstore'
|
||||
import { io } from '../util/oldSystem'
|
||||
import { reaction } from 'mobx'
|
||||
import { storage } from 'uxp'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
import { MaskModeEnum } from '../util/ts/enum'
|
||||
// import { Jimp } from '../util/oldSystem'
|
||||
declare const Jimp: any // make sure you import jimp before importing settings.tsx
|
||||
|
||||
type InterpolationMethod = {
|
||||
[key: string]: {
|
||||
photoshop: string
|
||||
jimp: string
|
||||
}
|
||||
}
|
||||
|
||||
const interpolationMethods: InterpolationMethod = {
|
||||
nearestNeighbor: {
|
||||
photoshop: 'nearestNeighbor',
|
||||
jimp: Jimp.RESIZE_NEAREST_NEIGHBOR,
|
||||
},
|
||||
bicubic: {
|
||||
photoshop: 'bicubicAutomatic',
|
||||
jimp: Jimp.RESIZE_BICUBIC,
|
||||
},
|
||||
bilinear: {
|
||||
photoshop: 'bilinear',
|
||||
jimp: Jimp.RESIZE_BILINEAR,
|
||||
},
|
||||
}
|
||||
|
||||
export const store = new AStore({
|
||||
scale_interpolation_method: interpolationMethods.bilinear,
|
||||
should_log_to_file:
|
||||
JSON.parse(storage.localStorage.getItem('should_log_to_file')) || false,
|
||||
delete_log_file_timer_id: null,
|
||||
b_borders_or_corners: MaskModeEnum.Borders,
|
||||
})
|
||||
|
||||
function onShouldLogToFileChange(event: any) {
|
||||
try {
|
||||
const should_log_to_file: boolean = event.target.checked
|
||||
store.data.should_log_to_file = should_log_to_file
|
||||
storage.localStorage.setItem('should_log_to_file', should_log_to_file)
|
||||
if (should_log_to_file && !store.data.delete_log_file_timer_id) {
|
||||
store.data.delete_log_file_timer_id = setDeleteLogTimer()
|
||||
} else {
|
||||
//don't log and clear delete file timer
|
||||
try {
|
||||
store.data.delete_log_file_timer_id = clearInterval(
|
||||
store.data.delete_log_file_timer_id
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
setLogMethod(should_log_to_file)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
function setDeleteLogTimer() {
|
||||
const timer_id = setInterval(async () => {
|
||||
await io.deleteFileIfLargerThan('log.txt', 200)
|
||||
}, 2 * 60 * 1000)
|
||||
console.log('setDeleteLogTimer() timer_id :', timer_id)
|
||||
return timer_id
|
||||
}
|
||||
|
||||
@observer
|
||||
export class Settings extends React.Component<{}> {
|
||||
componentDidMount(): void {}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{ width: '100%' }}>
|
||||
<SpMenu
|
||||
title="select an interploation method for resizing images"
|
||||
items={Object.keys(interpolationMethods)}
|
||||
label_item="Select Interpolation Method"
|
||||
selected_index={Object.keys(interpolationMethods).findIndex(
|
||||
(key) => {
|
||||
return (
|
||||
interpolationMethods[key].photoshop ===
|
||||
store.data.scale_interpolation_method
|
||||
.photoshop &&
|
||||
interpolationMethods[key].jimp ===
|
||||
store.data.scale_interpolation_method.jimp
|
||||
)
|
||||
}
|
||||
)}
|
||||
onChange={(id: any, value: any) => {
|
||||
store.updateProperty(
|
||||
'scale_interpolation_method',
|
||||
interpolationMethods[value.item]
|
||||
)
|
||||
}}
|
||||
></SpMenu>
|
||||
<sp-label>select language</sp-label>
|
||||
<SpMenu
|
||||
title="select language"
|
||||
items={['en_US', 'zh_CN']}
|
||||
label_item="select language"
|
||||
selected_index={['en_US', 'zh_CN'].indexOf(
|
||||
globalStore.Locale
|
||||
)}
|
||||
onChange={(id: any, value: any) => {
|
||||
globalStore.Locale = value.item
|
||||
localStorage.setItem('last_selected_locale', value.item)
|
||||
console.log(
|
||||
localStorage.getItem('last_selected_locale')
|
||||
)
|
||||
}}
|
||||
></SpMenu>
|
||||
<SpCheckBox
|
||||
style={{
|
||||
marginRight: '10px',
|
||||
}}
|
||||
onChange={onShouldLogToFileChange}
|
||||
checked={store.data.should_log_to_file}
|
||||
>
|
||||
{
|
||||
//@ts-ignore
|
||||
Locale('Log Errors To File')
|
||||
}
|
||||
</SpCheckBox>
|
||||
|
||||
<sp-radio-group
|
||||
style={{ display: 'flex' }}
|
||||
selected={store.data.b_borders_or_corners}
|
||||
onClick={(event: any) => {
|
||||
store.data.b_borders_or_corners = event.target.value
|
||||
}}
|
||||
>
|
||||
<sp-label slot="label">
|
||||
{Locale('Mask Layer Mode:')}
|
||||
</sp-label>
|
||||
{[
|
||||
// {
|
||||
// label: 'fully transparent',
|
||||
// value: MaskModeEnum.Transparent,
|
||||
// },
|
||||
{ label: 'keep borders', value: MaskModeEnum.Borders },
|
||||
{ label: 'keep corners', value: MaskModeEnum.Corners },
|
||||
].map((mode: any, index: number) => {
|
||||
console.log('mode:', mode.label, ' index:', index)
|
||||
return (
|
||||
<sp-radio
|
||||
key={`mode-${index}`}
|
||||
checked={
|
||||
store.data.b_borders_or_corners ===
|
||||
mode.value
|
||||
? true
|
||||
: void 0
|
||||
}
|
||||
value={mode.value}
|
||||
>
|
||||
{Locale(mode.label)}
|
||||
</sp-radio>
|
||||
)
|
||||
})}
|
||||
</sp-radio-group>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
const containerNode = document.getElementById('reactSettingsContainer')!
|
||||
const root = ReactDOM.createRoot(containerNode)
|
||||
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<Settings></Settings>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
|
@ -4,9 +4,10 @@ import ReactDOM from 'react-dom/client'
|
|||
import { makeAutoObservable, toJS } from 'mobx'
|
||||
import { observer } from 'mobx-react'
|
||||
|
||||
import { SpMenu } from './elements'
|
||||
import { SpMenu } from '../util/elements'
|
||||
import * as ultimate_sd_upscale_script from './ultimate_sd_upscaler'
|
||||
import { ScriptMode } from './ultimate_sd_upscaler'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
export function toJsFunc(store: any) {
|
||||
return toJS(store)
|
||||
}
|
||||
|
|
@ -164,10 +165,10 @@ const root = ReactDOM.createRoot(domNode)
|
|||
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<div style={{ border: '2px solid #6d6c6c', padding: '3px' }}>
|
||||
<ScriptComponent></ScriptComponent>
|
||||
</div>
|
||||
|
||||
{/* <SliderValuesDisplay /> */}
|
||||
<ErrorBoundary>
|
||||
<div style={{ border: '2px solid #6d6c6c', padding: '3px' }}>
|
||||
<ScriptComponent></ScriptComponent>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
|
@ -4,13 +4,12 @@ import ReactDOM from 'react-dom/client'
|
|||
import { action, makeAutoObservable, reaction, toJS } from 'mobx'
|
||||
import { Provider, inject, observer } from 'mobx-react'
|
||||
|
||||
import { SliderType, SpMenu, SpSliderWithLabel } from './elements'
|
||||
|
||||
import * as sdapi from '../../sdapi_py_re'
|
||||
import { SliderType, SpMenu, SpSliderWithLabel } from '../util/elements'
|
||||
|
||||
import { ui_config } from './config'
|
||||
import { requestGet } from '../../utility/api'
|
||||
import { api } from '../util/oldSystem'
|
||||
|
||||
const { requestGet } = api
|
||||
declare let g_sd_url: string
|
||||
|
||||
export let script_name: string = 'ultimate sd upscale'
|
||||
|
|
@ -68,6 +67,23 @@ export const script_args_ordered = [
|
|||
'custom_scale',
|
||||
]
|
||||
|
||||
//for some reason oldSystem.sdapi is empty {}, could be caused by a circular dependency
|
||||
//so I've copy pasted requestGetUpscalers() to the ultimate sd upscaler module, as a temporary solution
|
||||
async function requestGetUpscalers() {
|
||||
console.log('requestGetUpscalers: ')
|
||||
let json = []
|
||||
const full_url = `${g_sd_url}/sdapi/v1/upscalers`
|
||||
try {
|
||||
let request = await fetch(full_url)
|
||||
json = await request.json()
|
||||
console.log('upscalers json:')
|
||||
console.dir(json)
|
||||
} catch (e) {
|
||||
console.warn(`issues requesting from ${full_url}`, e)
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
class UltimateSDUpscalerStore {
|
||||
data: UltimateSDUpscalerData
|
||||
|
||||
|
|
@ -143,12 +159,16 @@ export class UltimateSDUpscalerForm extends React.Component<{
|
|||
}
|
||||
|
||||
async getUpscalers() {
|
||||
const sd_upscalers_json = await sdapi.requestGetUpscalers()
|
||||
const sd_upscalers = sd_upscalers_json.map(
|
||||
(upscaler: any) => upscaler.name
|
||||
)
|
||||
this.setState({ sd_upscalers: sd_upscalers })
|
||||
return sd_upscalers
|
||||
try {
|
||||
const sd_upscalers_json = await requestGetUpscalers()
|
||||
const sd_upscalers = sd_upscalers_json.map(
|
||||
(upscaler: any) => upscaler.name
|
||||
)
|
||||
this.setState({ sd_upscalers: sd_upscalers })
|
||||
return sd_upscalers
|
||||
} catch (e) {
|
||||
console.warn('ultimate sd upscaler getUpscalers(): ', e)
|
||||
}
|
||||
}
|
||||
|
||||
handleRefresh = async () => {
|
||||
|
|
@ -203,7 +223,7 @@ export class UltimateSDUpscalerForm extends React.Component<{
|
|||
output_value={this.props.store.data[id]}
|
||||
title={ui_config[id].label}
|
||||
label={ui_config[id].label}
|
||||
onSliderChange={this.handleSliderChange}
|
||||
onSliderInput={this.handleSliderChange}
|
||||
/>
|
||||
))
|
||||
const seamfix_ids = [
|
||||
|
|
@ -223,7 +243,7 @@ export class UltimateSDUpscalerForm extends React.Component<{
|
|||
output_value={this.props.store.data[id]}
|
||||
title={ui_config[id].label}
|
||||
label={ui_config[id].label}
|
||||
onSliderChange={this.handleSliderChange}
|
||||
onSliderInput={this.handleSliderChange}
|
||||
slider_type={
|
||||
Number.isInteger(ui_config[id].step)
|
||||
? SliderType.Integer
|
||||
|
|
@ -255,7 +275,7 @@ export class UltimateSDUpscalerForm extends React.Component<{
|
|||
id={'custom_scale'}
|
||||
out_min={ui_config.custom_scale.minimum}
|
||||
out_max={ui_config.custom_scale.maximum}
|
||||
onSliderChange={this.handleSliderChange}
|
||||
onSliderInput={this.handleSliderChange}
|
||||
steps={0.01}
|
||||
slider_type={SliderType.Float}
|
||||
/>
|
||||
|
|
@ -1,7 +1,13 @@
|
|||
import React, { ReactEventHandler, useState } from 'react'
|
||||
import React, { CSSProperties, ComponentType } from 'react'
|
||||
// import ReactDOM from 'react-dom'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import Locale from '../locale/locale'
|
||||
import { observer } from 'mobx-react'
|
||||
// import { versions } from 'uxp'
|
||||
export { ReactComponent as MoveToCanvasSvg } from '../../icon/move_to_canvas.svg'
|
||||
export { ReactComponent as PenSvg } from '../../icon/pen.svg'
|
||||
export { ReactComponent as PreviewSvg } from '../../icon/preview.svg'
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
|
|
@ -16,6 +22,9 @@ declare global {
|
|||
'sp-divider': any
|
||||
'sp-detail': any
|
||||
'sp-textarea': any
|
||||
'sp-textfield': any
|
||||
'sp-action-button': any
|
||||
'sp-progressbar': any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,8 +43,10 @@ export enum SliderType {
|
|||
Integer = 'integer',
|
||||
Float = 'float',
|
||||
}
|
||||
@observer
|
||||
export class SpSliderWithLabel extends React.Component<{
|
||||
onSliderChange?: any
|
||||
onSliderInput?: any
|
||||
id?: string
|
||||
'show-value'?: boolean
|
||||
steps?: number
|
||||
|
|
@ -115,17 +126,19 @@ export class SpSliderWithLabel extends React.Component<{
|
|||
this.setState({ output_value: to_value })
|
||||
}
|
||||
|
||||
onSliderValueInputHandler(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const newValue: string = event.target.value
|
||||
|
||||
let output_value = this.stepToOutputValue(parseInt(newValue))
|
||||
this.setState({ output_value: output_value })
|
||||
if (this.props.onSliderInput && this.props.id) {
|
||||
this.props.onSliderInput(this.props.id, output_value)
|
||||
} else if (this.props.onSliderInput) {
|
||||
this.props.onSliderInput(output_value)
|
||||
}
|
||||
}
|
||||
onSliderValueChangeHandler(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const newValue: string = event.target.value
|
||||
console.log('onSliderValueChangeHandler value: ', newValue)
|
||||
this.setState({ output_value: newValue })
|
||||
|
||||
console.log({
|
||||
in_min: this.in_min,
|
||||
in_max: this.in_max,
|
||||
out_min: this.out_min,
|
||||
out_max: this.out_max,
|
||||
})
|
||||
|
||||
let output_value = this.stepToOutputValue(parseInt(newValue))
|
||||
this.setState({ output_value: output_value })
|
||||
|
|
@ -146,17 +159,24 @@ export class SpSliderWithLabel extends React.Component<{
|
|||
// <div>{versions.plugin}</div>
|
||||
// </div>
|
||||
<div>
|
||||
<sp-slider
|
||||
<SpSlider
|
||||
show-value="false"
|
||||
// id="slControlNetWeight_0"
|
||||
class="slControlNetWeight_"
|
||||
min={this.in_min}
|
||||
max={this.in_max}
|
||||
value={this.state.slider_value}
|
||||
title="2 will keep the composition; 0 will allow composition to change"
|
||||
onInput={this.onSliderValueChangeHandler.bind(this)}
|
||||
title={this.props?.title ?? ''}
|
||||
onInput={
|
||||
this.props.onSliderInput
|
||||
? this.onSliderValueInputHandler.bind(this)
|
||||
: void 0
|
||||
}
|
||||
onChange={this.onSliderValueChangeHandler.bind(this)}
|
||||
>
|
||||
<sp-label slot="label">{this.props.label}:</sp-label>
|
||||
<sp-label slot="label">
|
||||
{Locale(this.props.label as any)}:
|
||||
</sp-label>
|
||||
<sp-label
|
||||
slot="label"
|
||||
// id="lControlNetWeight_0"
|
||||
|
|
@ -164,7 +184,7 @@ export class SpSliderWithLabel extends React.Component<{
|
|||
>
|
||||
{this.state.output_value}
|
||||
</sp-label>
|
||||
</sp-slider>
|
||||
</SpSlider>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -174,7 +194,7 @@ export class SpMenu extends React.Component<{
|
|||
id?: string
|
||||
|
||||
title?: string
|
||||
style?: string
|
||||
style?: CSSProperties
|
||||
items?: string[]
|
||||
disabled?: boolean[]
|
||||
label_item?: string
|
||||
|
|
@ -212,11 +232,11 @@ export class SpMenu extends React.Component<{
|
|||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div style={this.props.style}>
|
||||
<sp-picker
|
||||
title={this.props.title}
|
||||
size="m"
|
||||
style={{ width: '199px', marginRight: '5px' }}
|
||||
// style={{ width: '199px', marginRight: '5px' }}
|
||||
>
|
||||
<sp-menu id={this.props.id} slot="options">
|
||||
{this.props.label_item && (
|
||||
|
|
@ -258,3 +278,214 @@ export class SpMenu extends React.Component<{
|
|||
)
|
||||
}
|
||||
}
|
||||
class PhotoshopElem extends React.Component<{ [key: string]: any }, {}> {
|
||||
protected elem: Element | null = null
|
||||
|
||||
protected curEvents: { [key: string]: (evt: Event) => any } = {}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.updateEventListener()
|
||||
}
|
||||
|
||||
// componentDidUpdate(): void {
|
||||
// this.updateEventListener()
|
||||
// }
|
||||
|
||||
updateEventListener() {
|
||||
if (!this.elem) throw new Error('elem is not rendered with ref')
|
||||
|
||||
const [, newEvent] = this.splitProps(this.props)
|
||||
|
||||
Object.keys(this.curEvents).forEach((evkey) => {
|
||||
if (this.curEvents[evkey] != newEvent[evkey]) {
|
||||
this.elem?.removeEventListener(evkey, this.curEvents[evkey])
|
||||
}
|
||||
})
|
||||
Object.keys(newEvent).forEach((evkey) => {
|
||||
this.elem?.addEventListener(evkey, newEvent[evkey])
|
||||
})
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
Object.keys(this.curEvents).forEach((evkey) => {
|
||||
this.elem?.removeEventListener(evkey, this.curEvents[evkey])
|
||||
})
|
||||
}
|
||||
splitProps(props: any): [any, any] {
|
||||
const attr: any = {}
|
||||
const event: any = {}
|
||||
Object.keys(props).forEach((propKey: string) => {
|
||||
if (propKey.startsWith('on')) {
|
||||
const key = propKey[2].toLocaleLowerCase() + propKey.slice(3)
|
||||
event[key] = props[propKey]
|
||||
} else {
|
||||
attr[propKey] = props[propKey]
|
||||
}
|
||||
})
|
||||
return [attr, event]
|
||||
}
|
||||
}
|
||||
export class SpPicker extends PhotoshopElem {
|
||||
render() {
|
||||
const [attr] = this.splitProps(this.props)
|
||||
return (
|
||||
<sp-picker
|
||||
ref={(elem: Element) => (this.elem = elem)}
|
||||
{...attr}
|
||||
></sp-picker>
|
||||
)
|
||||
}
|
||||
}
|
||||
export class SpMenuComponent extends PhotoshopElem {
|
||||
render() {
|
||||
const [attr] = this.splitProps(this.props)
|
||||
return (
|
||||
<sp-menu
|
||||
ref={(elem: Element) => (this.elem = elem)}
|
||||
{...attr}
|
||||
></sp-menu>
|
||||
)
|
||||
}
|
||||
}
|
||||
export class SpMenuItem extends PhotoshopElem {
|
||||
render() {
|
||||
const [attr] = this.splitProps(this.props)
|
||||
return (
|
||||
<sp-menu-item
|
||||
ref={(elem: Element) => (this.elem = elem)}
|
||||
{...attr}
|
||||
></sp-menu-item>
|
||||
)
|
||||
}
|
||||
}
|
||||
export class SpLabel extends PhotoshopElem {
|
||||
render() {
|
||||
const [attr] = this.splitProps(this.props)
|
||||
return (
|
||||
<sp-label
|
||||
ref={(elem: Element) => (this.elem = elem)}
|
||||
{...attr}
|
||||
></sp-label>
|
||||
)
|
||||
}
|
||||
}
|
||||
export class SpCheckBox extends PhotoshopElem {
|
||||
render() {
|
||||
const [attr] = this.splitProps(this.props)
|
||||
if (!attr['checked']) delete attr['checked']
|
||||
return (
|
||||
<sp-checkbox
|
||||
ref={(elem: Element) => (this.elem = elem)}
|
||||
{...attr}
|
||||
></sp-checkbox>
|
||||
)
|
||||
}
|
||||
}
|
||||
export class SpSlider extends PhotoshopElem {
|
||||
render() {
|
||||
const [attr] = this.splitProps(this.props)
|
||||
return (
|
||||
<sp-slider
|
||||
ref={(elem: Element) => (this.elem = elem)}
|
||||
{...attr}
|
||||
></sp-slider>
|
||||
)
|
||||
}
|
||||
}
|
||||
export class SpRadioGroup extends PhotoshopElem {
|
||||
render() {
|
||||
const [attr] = this.splitProps(this.props)
|
||||
return (
|
||||
<sp-radio-group
|
||||
ref={(elem: Element) => (this.elem = elem)}
|
||||
{...attr}
|
||||
></sp-radio-group>
|
||||
)
|
||||
}
|
||||
}
|
||||
export class SpRadio extends PhotoshopElem {
|
||||
render() {
|
||||
const [attr] = this.splitProps(this.props)
|
||||
return (
|
||||
<sp-radio
|
||||
ref={(elem: Element) => (this.elem = elem)}
|
||||
{...attr}
|
||||
></sp-radio>
|
||||
)
|
||||
}
|
||||
}
|
||||
export class SpDivider extends PhotoshopElem {
|
||||
render() {
|
||||
const [attr] = this.splitProps(this.props)
|
||||
return (
|
||||
<sp-divider
|
||||
ref={(elem: Element) => (this.elem = elem)}
|
||||
{...attr}
|
||||
></sp-divider>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class Thumbnail extends React.Component<{
|
||||
style?: any
|
||||
children: React.ReactNode
|
||||
}> {
|
||||
render() {
|
||||
return (
|
||||
<div style={this.props?.style} className="viewer-image-container">
|
||||
{this.props.children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class ActionButtonSVG extends React.Component<{
|
||||
onClick?: any
|
||||
ComponentType: ComponentType
|
||||
title?: string
|
||||
}> {
|
||||
render() {
|
||||
if (!this.props.ComponentType) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<sp-action-button
|
||||
onClick={this.props?.onClick}
|
||||
style={{
|
||||
padding: 0,
|
||||
maxWidth: '32px',
|
||||
maxHeight: '32px' /* display: none; */,
|
||||
}}
|
||||
class="thumbnail-image-button"
|
||||
title={this.props.title ?? void 0}
|
||||
>
|
||||
<div slot="icon" style={{ fill: 'currentColor' }}>
|
||||
{<this.props.ComponentType />}
|
||||
</div>
|
||||
</sp-action-button>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
interface ScriptInstallComponentProps {
|
||||
onRefreshHandler: any
|
||||
}
|
||||
export const ScriptInstallComponent = observer(
|
||||
({ onRefreshHandler }: ScriptInstallComponentProps) => {
|
||||
return (
|
||||
<div>
|
||||
<sp-label class="missing-error">
|
||||
Script is not available; Make sure to install it from
|
||||
Automatic1111 webui
|
||||
</sp-label>
|
||||
<button
|
||||
className="btnSquare refreshButton"
|
||||
id="btnResetSettings"
|
||||
title="Refresh the After Detailer Extension"
|
||||
onClick={onRefreshHandler}
|
||||
></button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import React, { Component, ErrorInfo, ReactNode } from 'react'
|
||||
|
||||
interface Props {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean
|
||||
}
|
||||
|
||||
export class ErrorBoundary extends Component<Props, State> {
|
||||
public state: State = {
|
||||
hasError: false,
|
||||
}
|
||||
|
||||
public static getDerivedStateFromError(_: Error): State {
|
||||
return { hasError: true }
|
||||
}
|
||||
|
||||
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
console.error('Uncaught error:', error, errorInfo)
|
||||
}
|
||||
|
||||
public render() {
|
||||
if (this.state.hasError) {
|
||||
return <h1>Sorry.. there was an error</h1>
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
import React from 'react'
|
||||
import { ActionButtonSVG, Thumbnail } from './elements'
|
||||
|
||||
export class Grid extends React.Component<{
|
||||
// thumbnails_data?: any[]
|
||||
thumbnails?: string[]
|
||||
width?: number
|
||||
height?: number
|
||||
action_buttons?: any[]
|
||||
children?: React.ReactNode
|
||||
callback?: any
|
||||
clicked_index?: number
|
||||
permanent_indices?: number[]
|
||||
thumbnails_styles?: []
|
||||
}> {
|
||||
static defaultProps = {
|
||||
width: 100,
|
||||
height: 100,
|
||||
thumbnails: [],
|
||||
thumbnails_styles: [],
|
||||
}
|
||||
|
||||
render() {
|
||||
const img_style = {
|
||||
width: `${this.props.width}px`,
|
||||
// height: `${this.props.height}px`,
|
||||
height: 'auto',
|
||||
}
|
||||
return (
|
||||
<div className="viewer-container">
|
||||
{this.props?.thumbnails?.map((thumbnail, index: number) => {
|
||||
// const thumbnail = this.props?.thumbnails
|
||||
// thumbnail
|
||||
// ? this.props?.thumbnails[index]
|
||||
// : 'https://source.unsplash.com/random'
|
||||
const thumbnail_class = this.props?.thumbnails_styles
|
||||
? this.props?.thumbnails_styles[index]
|
||||
: ''
|
||||
return (
|
||||
<Thumbnail style={img_style} key={`thumbnail-${index}`}>
|
||||
<img
|
||||
style={img_style}
|
||||
onClick={async (event: any) => {
|
||||
try {
|
||||
console.log('image clicked')
|
||||
if (this.props.callback) {
|
||||
if (
|
||||
this.props.callback.constructor
|
||||
.name === 'AsyncFunction'
|
||||
) {
|
||||
await this.props?.callback(
|
||||
index,
|
||||
event
|
||||
)
|
||||
} else {
|
||||
this.props?.callback(
|
||||
index,
|
||||
event
|
||||
)
|
||||
}
|
||||
this.props?.callback(index, event) //todo: is this a typo why do we call callback twice?
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
'error was thrown while calling a callback method'
|
||||
)
|
||||
console.warn(e)
|
||||
}
|
||||
}}
|
||||
src={
|
||||
thumbnail ??
|
||||
'https://source.unsplash.com/random'
|
||||
}
|
||||
className={`viewer-image-container ${thumbnail_class}`}
|
||||
/>
|
||||
|
||||
{this.props?.action_buttons?.map((button, i) => {
|
||||
return (
|
||||
i < 4 && (
|
||||
<ActionButtonSVG
|
||||
key={`action-button-${i}`}
|
||||
ComponentType={button.ComponentType}
|
||||
onClick={() => {
|
||||
button.callback(index)
|
||||
}}
|
||||
title={button?.title}
|
||||
></ActionButtonSVG>
|
||||
)
|
||||
)
|
||||
})}
|
||||
</Thumbnail>
|
||||
)
|
||||
})}
|
||||
|
||||
{/* <div className="viewer-image-container">
|
||||
{this.props?.children}
|
||||
</div> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { format } from 'util'
|
||||
export function formateLog(data: any, ...optional_param: any[]) {
|
||||
const formattedOutput = format(data, ...optional_param)
|
||||
return formattedOutput
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import type Jimp from 'jimp'
|
||||
|
||||
//@ts-ignore
|
||||
const req = window['require']
|
||||
|
||||
// because we use window['require'], so the base path of this require function is the root path of plugin.
|
||||
const selection = req('./selection')
|
||||
const note = req('./utility/notification')
|
||||
const controlnet_preset = req('./utility/presets/controlnet_preset')
|
||||
const preset = req('./utility/presets/preset')
|
||||
const Enum = req('./enum')
|
||||
const api = req('./utility/api')
|
||||
const python_replacement = req('./utility/sdapi/python_replacement')
|
||||
const sdapi = req('./sdapi_py_re')
|
||||
const html_manip = req('./utility/html_manip')
|
||||
const psapi = req('./psapi')
|
||||
const general = req('./utility/general')
|
||||
const io = req('./utility/io')
|
||||
const settings_tab = req('./utility/tab/settings')
|
||||
const layer_util = req('./utility/layer')
|
||||
const session = req('./utility/session')
|
||||
|
||||
interface _Jimp extends Jimp {}
|
||||
const _Jimp: typeof Jimp = (window as any)['Jimp']
|
||||
|
||||
export {
|
||||
selection,
|
||||
note,
|
||||
controlnet_preset,
|
||||
preset,
|
||||
Enum,
|
||||
api,
|
||||
python_replacement,
|
||||
sdapi,
|
||||
html_manip,
|
||||
psapi,
|
||||
general,
|
||||
io,
|
||||
settings_tab,
|
||||
layer_util,
|
||||
session,
|
||||
_Jimp as Jimp,
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
declare let g_sd_url: string
|
||||
export async function requestGet(url: string) {
|
||||
let json = null
|
||||
|
||||
const full_url = url
|
||||
try {
|
||||
let request = await fetch(full_url)
|
||||
if (request.status === 404) {
|
||||
return null
|
||||
}
|
||||
|
||||
json = await request.json()
|
||||
|
||||
// console.log('json: ', json)
|
||||
} catch (e) {
|
||||
console.warn(`issues requesting from ${full_url}`, e)
|
||||
}
|
||||
return json
|
||||
}
|
||||
export async function requestPost(url: string, payload: any) {
|
||||
let json = null
|
||||
|
||||
const full_url = url
|
||||
try {
|
||||
let request = await fetch(full_url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
if (request.status === 404) {
|
||||
return null
|
||||
}
|
||||
|
||||
json = await request.json()
|
||||
|
||||
// console.log('json: ', json)
|
||||
} catch (e) {
|
||||
console.warn(`issues requesting from ${full_url}`, e)
|
||||
}
|
||||
return json
|
||||
}
|
||||
export async function requestFormDataPost(url: string, payload: any) {
|
||||
try {
|
||||
var myHeaders = new Headers()
|
||||
myHeaders.append('Cookie', 'PHPSESSID=n70fa2vmvm6tfmktf4jmstmd1i')
|
||||
|
||||
var formdata = new FormData()
|
||||
|
||||
for (const [key, value] of Object.entries(payload)) {
|
||||
//@ts-ignore
|
||||
formdata.append(key, value)
|
||||
}
|
||||
// formdata.append(
|
||||
// 'source',
|
||||
// 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII='
|
||||
// )
|
||||
// formdata.append('key', '6d207e02198a847aa98d0a2a901485a5')
|
||||
|
||||
var requestOptions = {
|
||||
method: 'POST',
|
||||
headers: myHeaders,
|
||||
body: formdata,
|
||||
redirect: 'follow',
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
const response = await fetch(url, requestOptions)
|
||||
const result_json = response.json()
|
||||
return result_json
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
export async function isScriptInstalled(script_name: string): Promise<boolean> {
|
||||
let is_installed = false
|
||||
try {
|
||||
const full_url = `${g_sd_url}/sdapi/v1/scripts`
|
||||
const scripts = await requestGet(full_url)
|
||||
is_installed =
|
||||
scripts?.txt2img?.includes(script_name) ||
|
||||
scripts?.img2img?.includes(script_name)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
console.log('is_installed: ', is_installed)
|
||||
return is_installed
|
||||
}
|
||||
|
|
@ -0,0 +1,313 @@
|
|||
import { app, core, action } from 'photoshop'
|
||||
import { Jimp, layer_util, psapi } from '../oldSystem'
|
||||
import { storage } from 'uxp'
|
||||
import { Layer } from 'photoshop/dom/Layer'
|
||||
import { changeDpiDataUrl } from 'changedpi'
|
||||
|
||||
const executeAsModal = core.executeAsModal
|
||||
const batchPlay = action.batchPlay
|
||||
|
||||
enum DocumentTypeEnum {
|
||||
NoBackground = 'no_background',
|
||||
ImageBackground = 'image_background',
|
||||
SolidBackground = 'solid_background',
|
||||
ArtBoard = 'artboard',
|
||||
}
|
||||
|
||||
async function isCorrectBackground() {
|
||||
const historylist = app.activeDocument.historyStates.filter(
|
||||
(h) => h.name === 'Correct Background'
|
||||
)
|
||||
console.log('historylist:', historylist)
|
||||
const is_correct_background = historylist.length > 0 ? true : false
|
||||
return is_correct_background
|
||||
}
|
||||
|
||||
async function getColor(X: any, Y: any) {
|
||||
// const background_layer_id = await app.activeDocument.backgroundLayer.id
|
||||
|
||||
try {
|
||||
const result = await batchPlay(
|
||||
[
|
||||
{
|
||||
_obj: 'colorSampler',
|
||||
_target: {
|
||||
_ref: 'document',
|
||||
_enum: 'ordinal',
|
||||
_value: 'targetEnum',
|
||||
},
|
||||
samplePoint: {
|
||||
horizontal: X,
|
||||
vertical: Y,
|
||||
},
|
||||
},
|
||||
],
|
||||
{}
|
||||
)
|
||||
|
||||
const red = result[0].colorSampler.red
|
||||
const green = result[0].colorSampler.grain
|
||||
const blue = result[0].colorSampler.blue
|
||||
|
||||
return [red, green, blue]
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
//REFACTOR: move to document.js
|
||||
async function findDocumentType() {
|
||||
//check if the background layer exsit
|
||||
//if it doesn't return false
|
||||
//if it does:
|
||||
//duplicate the background layer and place it on the top of the document.
|
||||
//sampler 10 random pixles
|
||||
//and check if all the pixels has the same values.
|
||||
//if it doesn't duplicate the background layer and place it above the background layer.
|
||||
// make a white background layer.
|
||||
//return true
|
||||
|
||||
let document_type
|
||||
const background_layer = await app.activeDocument.backgroundLayer
|
||||
const has_background_layer = app.activeDocument.backgroundLayer
|
||||
? true
|
||||
: false
|
||||
const artboards = Array.from(await app.activeDocument.artboards)
|
||||
if (artboards.length > 0) {
|
||||
document_type = DocumentTypeEnum['ArtBoard']
|
||||
// } else if (layer_util.Layer.doesLayerExist(background_layer)) {
|
||||
} else if (has_background_layer) {
|
||||
//assume it's solid white background if correctHistory > 1 || layers.length > 5
|
||||
const b_correct_background = await isCorrectBackground() // check the history for correct operation
|
||||
if (b_correct_background) {
|
||||
document_type = DocumentTypeEnum['SolidBackground']
|
||||
} else {
|
||||
//else
|
||||
|
||||
//background layer does exist
|
||||
//check if it's solid color background or an image background
|
||||
//sampler 10 random pixels
|
||||
let width = app.activeDocument.width
|
||||
let height = app.activeDocument.height
|
||||
let old_rgb: any
|
||||
let same_color = true
|
||||
|
||||
await executeAsModal(
|
||||
async () => {
|
||||
if (app.activeDocument.layers.length > 1) {
|
||||
await layer_util.toggleBackgroundLayerExe() // hide all layers except the background layer
|
||||
}
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
let x = Math.floor(Math.random() * width)
|
||||
let y = Math.floor(Math.random() * height)
|
||||
|
||||
const rgb = (await getColor(x, y))!
|
||||
if (old_rgb) {
|
||||
if (
|
||||
Math.round(old_rgb[0]) === Math.round(rgb[0]) &&
|
||||
Math.round(old_rgb[1]) === Math.round(rgb[1]) &&
|
||||
Math.round(old_rgb[2]) === Math.round(rgb[2])
|
||||
) {
|
||||
} else {
|
||||
same_color = false //it's an image background
|
||||
break
|
||||
}
|
||||
}
|
||||
old_rgb = rgb
|
||||
}
|
||||
if (app.activeDocument.layers.length > 1) {
|
||||
await layer_util.toggleBackgroundLayerExe() // undo the toggle operation; display all layers
|
||||
}
|
||||
},
|
||||
{
|
||||
commandName: 'Checking Document Type...',
|
||||
}
|
||||
)
|
||||
|
||||
document_type = same_color
|
||||
? DocumentTypeEnum['SolidBackground']
|
||||
: DocumentTypeEnum['ImageBackground']
|
||||
}
|
||||
} else {
|
||||
//create the background layer since it doesn't exsit
|
||||
document_type = DocumentTypeEnum['NoBackground']
|
||||
}
|
||||
|
||||
return document_type
|
||||
}
|
||||
|
||||
async function correctDocumentType(documentType: any) {
|
||||
if (documentType === DocumentTypeEnum['SolidBackground']) {
|
||||
//do nothing
|
||||
} else if (documentType === DocumentTypeEnum['ImageBackground']) {
|
||||
//duplicate the layer
|
||||
await executeAsModal(
|
||||
async () => {
|
||||
const image_layer: any =
|
||||
await app.activeDocument.backgroundLayer!.duplicate() //
|
||||
image_layer.name = 'Image'
|
||||
await app.activeDocument.backgroundLayer!.delete()
|
||||
await layer_util.createBackgroundLayer(255, 255, 255)
|
||||
},
|
||||
{
|
||||
commandName: 'Correct Background',
|
||||
}
|
||||
)
|
||||
} else if (documentType === DocumentTypeEnum['ArtBoard']) {
|
||||
//duplicate the layer
|
||||
await app.showAlert(
|
||||
"the plugin doesn't work with artboards, create normal document with no artboard to use the plugin"
|
||||
)
|
||||
throw "the plugin doesn't work with artboards, create normal document with no artboard to use the plugin"
|
||||
} else if (documentType === DocumentTypeEnum['NoBackground']) {
|
||||
await layer_util.createBackgroundLayer(255, 255, 255)
|
||||
}
|
||||
}
|
||||
|
||||
export async function initializeBackground() {
|
||||
await executeAsModal(
|
||||
async (context) => {
|
||||
const document_type = await findDocumentType()
|
||||
|
||||
const history_id = await context.hostControl.suspendHistory({
|
||||
documentID: app.activeDocument.id, //TODO: change this to the session document id
|
||||
name: 'Correct Background',
|
||||
})
|
||||
//store selection
|
||||
//store active layer
|
||||
const selectionInfo = await psapi.getSelectionInfoExe()
|
||||
await psapi.unSelectMarqueeExe()
|
||||
const active_layers = app.activeDocument.activeLayers
|
||||
|
||||
//1)check if the documnet has a background layer
|
||||
|
||||
await correctDocumentType(document_type)
|
||||
|
||||
//retore selection
|
||||
//restore active layer
|
||||
await psapi.reSelectMarqueeExe(selectionInfo)
|
||||
await psapi.selectLayersExe(active_layers)
|
||||
await context.hostControl.resumeHistory(history_id)
|
||||
},
|
||||
{
|
||||
commandName: 'Initialize Background',
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* transfer a base64image to a layer.
|
||||
* the image will located at the top-left corner of canvas.
|
||||
* @param b64Image
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
export async function base64ToFileAndGetLayer(
|
||||
b64Image: string,
|
||||
options: {
|
||||
image_name?: string
|
||||
} = {}
|
||||
): Promise<{ layer: Layer; width: number; height: number }> {
|
||||
const imageName = options.image_name || 'output_image.png'
|
||||
|
||||
b64Image = changeDpiDataUrl(
|
||||
'data:image/png;base64,' + b64Image,
|
||||
app.activeDocument.resolution
|
||||
)
|
||||
const img = Buffer.from(b64Image.split(',')[1], 'base64')
|
||||
const jimp_image = await Jimp.read(img)
|
||||
|
||||
const folder = await storage.localFileSystem.getTemporaryFolder()
|
||||
const file = await folder.createFile(imageName + '.png', {
|
||||
overwrite: true,
|
||||
})
|
||||
|
||||
await file.write(img.buffer, { format: storage.formats.binary })
|
||||
|
||||
const token = await storage.localFileSystem.createSessionToken(file) // batchPlay requires a token on _path
|
||||
|
||||
const selection_info = await psapi.getSelectionInfoExe()
|
||||
|
||||
let imported_layer
|
||||
|
||||
await executeAsModal(
|
||||
() =>
|
||||
batchPlay(
|
||||
[
|
||||
{
|
||||
_obj: 'set',
|
||||
_target: [
|
||||
{
|
||||
_property: 'selection',
|
||||
_ref: 'channel',
|
||||
},
|
||||
],
|
||||
to: {
|
||||
_obj: 'rectangle',
|
||||
bottom: {
|
||||
_unit: 'pixelsUnit',
|
||||
_value: jimp_image.bitmap.height,
|
||||
},
|
||||
left: {
|
||||
_unit: 'pixelsUnit',
|
||||
_value: 0.0,
|
||||
},
|
||||
right: {
|
||||
_unit: 'pixelsUnit',
|
||||
_value: jimp_image.bitmap.width,
|
||||
},
|
||||
top: {
|
||||
_unit: 'pixelsUnit',
|
||||
_value: 0.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
{}
|
||||
),
|
||||
{
|
||||
commandName: 'select import area',
|
||||
}
|
||||
)
|
||||
await executeAsModal(
|
||||
async () => {
|
||||
const result = await batchPlay(
|
||||
[
|
||||
{
|
||||
_obj: 'placeEvent',
|
||||
// ID: 6,
|
||||
null: {
|
||||
_path: token,
|
||||
_kind: 'local',
|
||||
},
|
||||
freeTransformCenterState: {
|
||||
_enum: 'quadCenterState',
|
||||
_value: 'QCSAverage',
|
||||
},
|
||||
_isCommand: true,
|
||||
_options: {
|
||||
dialogOptions: 'dontDisplay',
|
||||
},
|
||||
},
|
||||
],
|
||||
{}
|
||||
)
|
||||
console.log('placeEmbedd batchPlay result: ', result)
|
||||
|
||||
imported_layer = await app.activeDocument.activeLayers[0]
|
||||
},
|
||||
{
|
||||
commandName: 'import base64',
|
||||
}
|
||||
)
|
||||
|
||||
await psapi.reSelectMarqueeExe(selection_info)
|
||||
if (!imported_layer) {
|
||||
throw new Error('base64ToFileAndGetLayer failed: layer is empty')
|
||||
}
|
||||
return {
|
||||
layer: imported_layer,
|
||||
height: jimp_image.bitmap.height,
|
||||
width: jimp_image.bitmap.width,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
export enum GenerationModeEnum {
|
||||
Txt2Img = 'txt2img',
|
||||
Img2Img = 'img2img',
|
||||
Inpaint = 'inpaint',
|
||||
Outpaint = 'outpaint',
|
||||
Upscale = 'upscale',
|
||||
LassoInpaint = 'lasso_inpaint',
|
||||
LassoOutpaint = 'lasso_outpaint',
|
||||
}
|
||||
|
||||
export enum MaskModeEnum {
|
||||
Transparent = 'transparent',
|
||||
Borders = 'border',
|
||||
Corners = 'corner',
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
import { app, core, action } from 'photoshop'
|
||||
import { Jimp, io, psapi } from '../oldSystem'
|
||||
import { base64ToFileAndGetLayer } from './document'
|
||||
import { transformCurrentLayerTo } from './layer'
|
||||
import { Layer } from 'photoshop/dom/Layer'
|
||||
const executeAsModal = core.executeAsModal
|
||||
|
||||
export async function moveImageToLayer_old(
|
||||
base64_image: string,
|
||||
selection_info: any,
|
||||
layer_name: string = 'output_image.png'
|
||||
) {
|
||||
let layer
|
||||
try {
|
||||
const to_x = selection_info?.left
|
||||
const to_y = selection_info?.top
|
||||
const width = selection_info?.width
|
||||
const height = selection_info?.height
|
||||
layer = await io.IO.base64ToLayer(
|
||||
base64_image,
|
||||
layer_name,
|
||||
to_x,
|
||||
to_y,
|
||||
width,
|
||||
height
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
layer = null
|
||||
}
|
||||
return layer
|
||||
}
|
||||
export async function moveImageToLayer(
|
||||
base64_image: string,
|
||||
selection_info: any,
|
||||
layer_name: string = 'output_image.png'
|
||||
): Promise<Layer> {
|
||||
if (!base64_image) throw new Error('moveImageToLayer: image is empty')
|
||||
let layer: Layer | null
|
||||
try {
|
||||
const to_x = selection_info?.left
|
||||
const to_y = selection_info?.top
|
||||
const width = selection_info?.width
|
||||
const height = selection_info?.height
|
||||
|
||||
const res = await base64ToFileAndGetLayer(base64_image, {
|
||||
image_name: layer_name,
|
||||
})
|
||||
layer = res.layer
|
||||
|
||||
await psapi.setVisibleExe(layer, true)
|
||||
await transformCurrentLayerTo(
|
||||
{
|
||||
left: to_x,
|
||||
top: to_y,
|
||||
width,
|
||||
height,
|
||||
},
|
||||
{
|
||||
width: res.width,
|
||||
height: res.height,
|
||||
left: 0,
|
||||
top: 0,
|
||||
}
|
||||
)
|
||||
await psapi.setVisibleExe(layer, true)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
layer = null
|
||||
}
|
||||
if (!layer) {
|
||||
throw new Error('moveImageToLayer failed: layer is empty')
|
||||
}
|
||||
return layer
|
||||
}
|
||||
|
||||
export async function convertGrayscaleToWhiteAndTransparent(
|
||||
base64: string
|
||||
): Promise<{
|
||||
base64: string
|
||||
width: number
|
||||
height: number
|
||||
}> {
|
||||
function grayToWhiteAndTransparent(
|
||||
this: Jimp,
|
||||
x: number,
|
||||
y: number,
|
||||
idx: number
|
||||
) {
|
||||
let color
|
||||
if (
|
||||
this.bitmap.data[idx] !== 0 &&
|
||||
this.bitmap.data[idx + 1] !== 0 &&
|
||||
this.bitmap.data[idx + 2] !== 0
|
||||
) {
|
||||
color = 0xffffffff
|
||||
} else {
|
||||
color = 0x00000000
|
||||
}
|
||||
this.setPixelColor(color, x, y)
|
||||
}
|
||||
try {
|
||||
const jimp_image = await Jimp.read(Buffer.from(base64, 'base64'))
|
||||
|
||||
const jimp_mask = await jimp_image.scan(
|
||||
0,
|
||||
0,
|
||||
jimp_image.bitmap.width,
|
||||
jimp_image.bitmap.height,
|
||||
grayToWhiteAndTransparent
|
||||
)
|
||||
|
||||
const base64_monochrome_mask = await getBase64FromJimp(jimp_mask)
|
||||
|
||||
return {
|
||||
base64: base64_monochrome_mask,
|
||||
height: jimp_image.bitmap.height,
|
||||
width: jimp_image.bitmap.width,
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async function getBase64FromJimp(jimp_image: Jimp) {
|
||||
const dataURL = await jimp_image.getBase64Async(Jimp.MIME_PNG)
|
||||
const base64 = dataURL.replace(/^data:image\/png;base64,/, '')
|
||||
return base64
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
import { app, core, action } from 'photoshop'
|
||||
import { layer_util, psapi } from '../oldSystem'
|
||||
import { settings_tab_ts } from '../../entry'
|
||||
const executeAsModal = core.executeAsModal
|
||||
const { batchPlay } = action
|
||||
|
||||
export interface RectArea {
|
||||
top: number
|
||||
left: number
|
||||
height: number
|
||||
width: number
|
||||
}
|
||||
|
||||
async function transformBatchPlay(
|
||||
centerX: number,
|
||||
centerY: number,
|
||||
scaleRatioX: number,
|
||||
scaleRatioY: number,
|
||||
translateX: number,
|
||||
translateY: number
|
||||
) {
|
||||
const setInterpolationMethodDesc = {
|
||||
_obj: 'set',
|
||||
_target: [
|
||||
{
|
||||
_ref: 'property',
|
||||
_property: 'generalPreferences',
|
||||
},
|
||||
{
|
||||
_ref: 'application',
|
||||
_enum: 'ordinal',
|
||||
_value: 'targetEnum',
|
||||
},
|
||||
],
|
||||
to: {
|
||||
_obj: 'generalPreferences',
|
||||
interpolationMethod: {
|
||||
_enum: 'interpolationType',
|
||||
// _value: 'bilinear',
|
||||
_value: settings_tab_ts.store.data.scale_interpolation_method
|
||||
.photoshop,
|
||||
},
|
||||
},
|
||||
_isCommand: true,
|
||||
}
|
||||
|
||||
let imageSizeDescriptor = {
|
||||
_obj: 'transform',
|
||||
_target: [
|
||||
{
|
||||
_ref: 'layer',
|
||||
_enum: 'ordinal',
|
||||
_value: 'targetEnum',
|
||||
},
|
||||
],
|
||||
freeTransformCenterState: {
|
||||
_enum: 'quadCenterState',
|
||||
_value: 'QCSIndependent',
|
||||
},
|
||||
position: {
|
||||
_obj: 'paint',
|
||||
horizontal: { _unit: 'pixelsUnit', _value: centerX },
|
||||
vertical: { _unit: 'pixelsUnit', _value: centerY },
|
||||
},
|
||||
offset: {
|
||||
_obj: 'offset',
|
||||
horizontal: {
|
||||
_unit: 'pixelsUnit',
|
||||
_value: translateX,
|
||||
},
|
||||
vertical: {
|
||||
_unit: 'pixelsUnit',
|
||||
_value: translateY,
|
||||
},
|
||||
},
|
||||
width: {
|
||||
_unit: 'percentUnit',
|
||||
_value: scaleRatioX,
|
||||
},
|
||||
height: {
|
||||
_unit: 'percentUnit',
|
||||
_value: scaleRatioY,
|
||||
},
|
||||
linked: true,
|
||||
interfaceIconFrameDimmed: {
|
||||
_enum: 'interpolationType',
|
||||
// _value: 'bilinear',
|
||||
_value: settings_tab_ts.store.data.scale_interpolation_method
|
||||
.photoshop,
|
||||
},
|
||||
_isCommand: true,
|
||||
}
|
||||
return batchPlay([setInterpolationMethodDesc, imageSizeDescriptor], {
|
||||
synchronousExecution: true,
|
||||
modalBehavior: 'execute',
|
||||
})
|
||||
}
|
||||
|
||||
export async function transformCurrentLayerTo(
|
||||
toRect: RectArea,
|
||||
fromRect: RectArea
|
||||
) {
|
||||
const selection_info = await psapi.getSelectionInfoExe()
|
||||
await psapi.unSelectMarqueeExe()
|
||||
|
||||
const scale_x_ratio = (toRect.width / fromRect.width) * 100
|
||||
const scale_y_ratio = (toRect.height / fromRect.height) * 100
|
||||
|
||||
const top_dist = toRect.top - fromRect.top
|
||||
const left_dist = toRect.left - fromRect.left
|
||||
console.log(
|
||||
'transformCurrentLayer',
|
||||
top_dist,
|
||||
left_dist,
|
||||
scale_x_ratio,
|
||||
scale_y_ratio
|
||||
)
|
||||
|
||||
await executeAsModal(
|
||||
() =>
|
||||
transformBatchPlay(
|
||||
fromRect.left,
|
||||
fromRect.top,
|
||||
scale_x_ratio,
|
||||
scale_y_ratio,
|
||||
left_dist,
|
||||
top_dist
|
||||
),
|
||||
{ commandName: 'transform' }
|
||||
)
|
||||
|
||||
await psapi.reSelectMarqueeExe(selection_info)
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
import { moveImageToLayer, moveImageToLayer_old } from './io'
|
||||
import { io, layer_util } from '../oldSystem'
|
||||
import { session_ts } from '../../entry'
|
||||
|
||||
import { action, core } from 'photoshop'
|
||||
import { MaskModeEnum } from './enum'
|
||||
const executeAsModal = core.executeAsModal
|
||||
const batchPlay = action.batchPlay
|
||||
|
||||
export async function applyMaskFromBlackAndWhiteImage(
|
||||
black_and_white_base64: string,
|
||||
layer_id: any,
|
||||
selectionInfo: any,
|
||||
b_borders_or_corners: MaskModeEnum = MaskModeEnum.Transparent
|
||||
) {
|
||||
let mask_layer
|
||||
try {
|
||||
const transparent_mask_base64 =
|
||||
await io.convertBlackToTransparentKeepBorders(
|
||||
black_and_white_base64,
|
||||
b_borders_or_corners
|
||||
)
|
||||
mask_layer = await moveImageToLayer_old(
|
||||
transparent_mask_base64,
|
||||
selectionInfo
|
||||
)
|
||||
|
||||
let cmd = [
|
||||
{
|
||||
_obj: 'select',
|
||||
_target: [{ _id: mask_layer.id, _ref: 'layer' }],
|
||||
makeVisible: false,
|
||||
},
|
||||
{
|
||||
_obj: 'set',
|
||||
_target: [
|
||||
{
|
||||
_ref: 'channel',
|
||||
_property: 'selection',
|
||||
},
|
||||
],
|
||||
to: {
|
||||
_ref: 'channel',
|
||||
_enum: 'channel',
|
||||
_value: 'transparencyEnum',
|
||||
},
|
||||
_isCommand: true,
|
||||
},
|
||||
{
|
||||
_obj: 'expand',
|
||||
by: {
|
||||
_unit: 'pixelsUnit',
|
||||
_value: 10,
|
||||
},
|
||||
selectionModifyEffectAtCanvasBounds: true,
|
||||
_isCommand: true,
|
||||
},
|
||||
{
|
||||
_obj: 'select',
|
||||
_target: [{ _id: layer_id, _ref: 'layer' }],
|
||||
|
||||
makeVisible: false,
|
||||
},
|
||||
{
|
||||
_obj: 'make',
|
||||
new: {
|
||||
_class: 'channel',
|
||||
},
|
||||
at: {
|
||||
_ref: 'channel',
|
||||
_enum: 'channel',
|
||||
_value: 'mask',
|
||||
},
|
||||
using: {
|
||||
_enum: 'userMaskEnabled',
|
||||
_value: 'revealSelection',
|
||||
},
|
||||
_isCommand: true,
|
||||
},
|
||||
]
|
||||
//@ts-ignore
|
||||
// await timer(g_timer_value)
|
||||
await executeAsModal(
|
||||
async () => {
|
||||
const result = await batchPlay(cmd, {
|
||||
synchronousExecution: true,
|
||||
modalBehavior: 'execute',
|
||||
})
|
||||
},
|
||||
{
|
||||
commandName: 'select opaque pixels',
|
||||
}
|
||||
)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
await layer_util.deleteLayers([mask_layer])
|
||||
}
|
||||
}
|
||||
|
||||
export async function selectionFromBlackAndWhiteImage(
|
||||
black_and_white_base64: string,
|
||||
selectionInfo: any,
|
||||
b_borders_or_corners: MaskModeEnum = MaskModeEnum.Transparent
|
||||
) {
|
||||
let mask_layer
|
||||
try {
|
||||
const transparent_mask_base64 =
|
||||
await io.convertBlackToTransparentKeepBorders(
|
||||
black_and_white_base64,
|
||||
b_borders_or_corners
|
||||
)
|
||||
mask_layer = await moveImageToLayer_old(
|
||||
transparent_mask_base64,
|
||||
selectionInfo
|
||||
)
|
||||
|
||||
let cmd = [
|
||||
{
|
||||
_obj: 'select',
|
||||
_target: [{ _id: mask_layer.id, _ref: 'layer' }],
|
||||
makeVisible: false,
|
||||
},
|
||||
{
|
||||
_obj: 'set',
|
||||
_target: [
|
||||
{
|
||||
_ref: 'channel',
|
||||
_property: 'selection',
|
||||
},
|
||||
],
|
||||
to: {
|
||||
_ref: 'channel',
|
||||
_enum: 'channel',
|
||||
_value: 'transparencyEnum',
|
||||
},
|
||||
_isCommand: true,
|
||||
},
|
||||
]
|
||||
//@ts-ignore
|
||||
// await timer(g_timer_value)
|
||||
await executeAsModal(
|
||||
async () => {
|
||||
const result = await batchPlay(cmd, {
|
||||
synchronousExecution: true,
|
||||
modalBehavior: 'execute',
|
||||
})
|
||||
},
|
||||
{
|
||||
commandName: 'select opaque pixels',
|
||||
}
|
||||
)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
await layer_util.deleteLayers([mask_layer])
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
|
||||
import Collapsible from '../after_detailer/after_detailer'
|
||||
import { observer } from 'mobx-react'
|
||||
import { AStore } from '../main/astore'
|
||||
import { progress } from '../entry'
|
||||
import './style/preview.css'
|
||||
import { reaction } from 'mobx'
|
||||
import Locale from '../locale/locale'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
export const store = new AStore({
|
||||
// image: '',
|
||||
// progress_value: 0,
|
||||
})
|
||||
// update all progress bar when progress store progress_value update
|
||||
reaction(
|
||||
() => {
|
||||
return progress.store.data.progress_value
|
||||
},
|
||||
(value: number) => {
|
||||
document.querySelectorAll('.pProgressBars').forEach((progress: any) => {
|
||||
progress.value = value?.toFixed(2)
|
||||
})
|
||||
}
|
||||
)
|
||||
const Previewer = observer(() => {
|
||||
const renderImage = () => {
|
||||
let preview_img_html
|
||||
if (progress.store.data.progress_image) {
|
||||
preview_img_html = (
|
||||
<img
|
||||
style={{ maxWidth: '100%' }}
|
||||
src={
|
||||
'data:image/png;base64,' +
|
||||
progress.store.data.progress_image
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="progressImageContainer"
|
||||
style={{
|
||||
minHeight: progress.store.data.progress_image_height,
|
||||
}}
|
||||
>
|
||||
<sp-progressbar
|
||||
class="pProgressBars preview_progress_bar"
|
||||
max="100"
|
||||
value={`${progress.store.data.progress_value}`}
|
||||
></sp-progressbar>
|
||||
{progress.store.data.progress_image ? preview_img_html : void 0}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return <div style={{ padding: '4px' }}>{renderImage()}</div>
|
||||
})
|
||||
|
||||
const containers = document.querySelectorAll('.previewContainer')
|
||||
|
||||
const PreviewerContainer = observer(() => {
|
||||
return (
|
||||
<div style={{ border: '2px solid #6d6c6c', padding: '3px' }}>
|
||||
<Collapsible
|
||||
defaultIsOpen={true}
|
||||
label={
|
||||
Locale('Preview') +
|
||||
' ' +
|
||||
(progress.store.data.progress_value
|
||||
? `: ${
|
||||
progress.store.data.progress_label
|
||||
} ${progress.store.data.progress_value?.toFixed(2)}%`
|
||||
: '')
|
||||
}
|
||||
>
|
||||
<Previewer></Previewer>
|
||||
</Collapsible>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
containers.forEach((container) => {
|
||||
const root = ReactDOM.createRoot(container)
|
||||
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<PreviewerContainer />
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
})
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.preview_progress_bar {
|
||||
border-left: 2px solid #3e3e3e;
|
||||
border-right: 2px solid #3e3e3e;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -0,0 +1,703 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
// import ReactDOM from 'react-dom'
|
||||
import { observer } from 'mobx-react'
|
||||
import { AStore } from '../main/astore'
|
||||
import { Grid } from '../util/grid'
|
||||
import {
|
||||
MoveToCanvasSvg,
|
||||
SpCheckBox,
|
||||
SpSlider,
|
||||
SpSliderWithLabel,
|
||||
} from '../util/elements'
|
||||
import {
|
||||
convertGrayscaleToWhiteAndTransparent,
|
||||
moveImageToLayer,
|
||||
moveImageToLayer_old,
|
||||
} from '../util/ts/io'
|
||||
import { io, layer_util, psapi, selection } from '../util/oldSystem'
|
||||
import Collapsible from '../after_detailer/after_detailer'
|
||||
import { progress, session_ts, settings_tab_ts } from '../entry'
|
||||
import { reaction } from 'mobx'
|
||||
import { GenerationModeEnum, MaskModeEnum } from '../util/ts/enum'
|
||||
import { base64ToLassoSelection } from '../../selection'
|
||||
import { action, app, core } from 'photoshop'
|
||||
import Locale from '../locale/locale'
|
||||
import { applyMaskFromBlackAndWhiteImage } from '../util/ts/selection'
|
||||
import { ErrorBoundary } from '../util/errorBoundary'
|
||||
|
||||
const executeAsModal = core.executeAsModal
|
||||
const batchPlay = action.batchPlay
|
||||
declare let g_generation_session: any
|
||||
|
||||
enum ClickTypeEnum {
|
||||
Click = 'click',
|
||||
ShiftClick = 'shift_click',
|
||||
AltClick = 'alt_click',
|
||||
SecondClick = 'second_click', //when we click a thumbnail that is active/ has orange border
|
||||
}
|
||||
|
||||
enum OutputImageStateEnum {
|
||||
Add = 'add',
|
||||
remove = 'remove',
|
||||
}
|
||||
enum ClassNameEnum {
|
||||
Green = 'viewerImgSelected',
|
||||
Orange = 'viewerImgActive',
|
||||
None = '',
|
||||
}
|
||||
function findClickType(event: any) {
|
||||
let click_type: ClickTypeEnum = ClickTypeEnum.Click
|
||||
|
||||
if (event.shiftKey) {
|
||||
click_type = ClickTypeEnum.ShiftClick
|
||||
} else if (event.altKey) {
|
||||
click_type = ClickTypeEnum.AltClick
|
||||
}
|
||||
return click_type
|
||||
}
|
||||
|
||||
export const store = new AStore({
|
||||
images: [],
|
||||
thumbnails: [],
|
||||
metadata: [], // metadata for each image
|
||||
width: 50,
|
||||
height: 50,
|
||||
|
||||
prev_layer: null,
|
||||
clicked_index: null,
|
||||
|
||||
permanent_indices: [],
|
||||
|
||||
prev_index: -1,
|
||||
output_image_obj_list: [],
|
||||
is_stored: [],
|
||||
layers: [],
|
||||
class_name: [],
|
||||
can_click: true,
|
||||
auto_mask: true,
|
||||
})
|
||||
|
||||
const timer = (ms: any) => new Promise((res) => setTimeout(res, ms))
|
||||
//when a generation is done, add the last generated image from the viewer to tha canvas
|
||||
reaction(
|
||||
() => {
|
||||
return store.data.images
|
||||
},
|
||||
async (images: string[]) => {
|
||||
try {
|
||||
if (images.length > 0) {
|
||||
let attempts: number = 5
|
||||
store.data.is_stored = Array(images.length).fill(false)
|
||||
while (attempts > 0) {
|
||||
if (!progress.store.data.can_update) {
|
||||
await timer(2000)
|
||||
await handleOutputImageThumbnailClick(images.length - 1)
|
||||
break
|
||||
}
|
||||
|
||||
attempts -= 1
|
||||
|
||||
console.log('waiting 1000:')
|
||||
console.log(
|
||||
'progress.store.data.can_update:',
|
||||
progress.store.data.can_update
|
||||
)
|
||||
await timer(2000)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
)
|
||||
export const init_store = new AStore({
|
||||
images: [],
|
||||
thumbnails: [],
|
||||
|
||||
width: 50,
|
||||
height: 50,
|
||||
|
||||
prev_layer: null,
|
||||
clicked_index: null,
|
||||
|
||||
permanent_indices: [],
|
||||
|
||||
prev_index: -1,
|
||||
output_image_obj_list: [],
|
||||
is_stored: [],
|
||||
layers: [],
|
||||
class_name: [],
|
||||
can_click: true,
|
||||
})
|
||||
export const mask_store = new AStore({
|
||||
images: [],
|
||||
thumbnails: [],
|
||||
output_images_masks: [],
|
||||
|
||||
width: 50,
|
||||
height: 50,
|
||||
|
||||
prev_layer: null,
|
||||
clicked_index: null,
|
||||
|
||||
permanent_indices: [],
|
||||
|
||||
prev_index: -1,
|
||||
output_image_obj_list: [],
|
||||
is_stored: [],
|
||||
layers: [],
|
||||
class_name: [],
|
||||
can_click: true,
|
||||
})
|
||||
|
||||
export async function updateViewerStoreImageAndThumbnail(
|
||||
store: AStore,
|
||||
images: string[]
|
||||
) {
|
||||
try {
|
||||
if (typeof images === 'undefined' || !images) {
|
||||
return null
|
||||
}
|
||||
store.data.images = images
|
||||
const thumbnail_list = []
|
||||
for (const base64 of images) {
|
||||
const thumbnail = await io.createThumbnail(base64, 300)
|
||||
thumbnail_list.push(thumbnail)
|
||||
}
|
||||
|
||||
store.data.thumbnails = thumbnail_list
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
console.warn('images: ', images)
|
||||
}
|
||||
}
|
||||
|
||||
const add_new = async (base64: string, mask?: string) => {
|
||||
//change the color of thumbnail border
|
||||
//add image to the canvas
|
||||
await psapi.unselectActiveLayersExe()
|
||||
const layer = await moveImageToLayer_old(
|
||||
base64,
|
||||
session_ts.store.data.selectionInfo
|
||||
)
|
||||
|
||||
// create channel if the generated mode support masking
|
||||
if (
|
||||
[
|
||||
GenerationModeEnum.Inpaint,
|
||||
GenerationModeEnum.LassoInpaint,
|
||||
GenerationModeEnum.Outpaint,
|
||||
].includes(session_ts.store.data.mode) &&
|
||||
store.data.auto_mask &&
|
||||
mask
|
||||
) {
|
||||
const channel_mask_monochrome =
|
||||
await convertGrayscaleToWhiteAndTransparent(
|
||||
// session_ts.store.data.expanded_mask
|
||||
mask
|
||||
)
|
||||
if (
|
||||
settings_tab_ts.store.data.b_borders_or_corners ===
|
||||
MaskModeEnum.Transparent
|
||||
) {
|
||||
//will use colorRange() which may or may not break
|
||||
const mask_layer = await moveImageToLayer(
|
||||
channel_mask_monochrome.base64,
|
||||
session_ts.store.data.selectionInfo
|
||||
)
|
||||
|
||||
if (!mask_layer) {
|
||||
throw new Error('mask_layer is empty')
|
||||
}
|
||||
|
||||
await selection.black_white_layer_to_mask_multi_batchplay(
|
||||
mask_layer.id,
|
||||
layer.id,
|
||||
'mask'
|
||||
)
|
||||
await layer_util.deleteLayers([mask_layer])
|
||||
} else {
|
||||
// if MaskModeEnum.Borders or MaskModeEnum.Corners
|
||||
// another option that doesn't use colorRange()
|
||||
await applyMaskFromBlackAndWhiteImage(
|
||||
channel_mask_monochrome.base64,
|
||||
layer.id,
|
||||
session_ts.store.data.selectionInfo,
|
||||
settings_tab_ts.store.data.b_borders_or_corners
|
||||
)
|
||||
}
|
||||
}
|
||||
return layer
|
||||
}
|
||||
const add = async (base64: string, mask?: string) => {
|
||||
try {
|
||||
//change the color of thumbnail border
|
||||
//add image to the canvas
|
||||
const layer = await moveImageToLayer_old(
|
||||
base64,
|
||||
session_ts.store.data.selectionInfo
|
||||
)
|
||||
|
||||
// create channel if the generated mode support masking
|
||||
if (
|
||||
[
|
||||
GenerationModeEnum.Inpaint,
|
||||
GenerationModeEnum.LassoInpaint,
|
||||
GenerationModeEnum.Outpaint,
|
||||
].includes(session_ts.store.data.mode) &&
|
||||
store.data.auto_mask
|
||||
) {
|
||||
// const base64_monochrome_mask = await io.convertGrayscaleToMonochrome(
|
||||
// session_ts.store.data.selected_mask
|
||||
// )
|
||||
const timer = (ms: any) => new Promise((res) => setTimeout(res, ms))
|
||||
|
||||
const mask_monochrome = await io.convertGrayscaleToMonochrome(
|
||||
// session_ts.store.data.expanded_mask
|
||||
mask
|
||||
)
|
||||
const channel_mask = mask_monochrome
|
||||
const selectionInfo = session_ts.store.data.selectionInfo
|
||||
// await selection.base64ToChannel(channel_mask, selectionInfo, 'mask')
|
||||
|
||||
await applyMaskFromBlackAndWhiteImage(
|
||||
channel_mask,
|
||||
layer.id,
|
||||
selectionInfo,
|
||||
settings_tab_ts.store.data.b_borders_or_corners
|
||||
)
|
||||
}
|
||||
|
||||
return layer
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
const addWithHistory = async (base64: string, mask?: string) => {
|
||||
let layer
|
||||
await executeAsModal(
|
||||
async (context: any) => {
|
||||
let history_id
|
||||
try {
|
||||
history_id = await context.hostControl.suspendHistory({
|
||||
documentID: app.activeDocument.id,
|
||||
name: 'Add Image to Canvas',
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
|
||||
layer = await add(base64, mask)
|
||||
|
||||
try {
|
||||
await context.hostControl.resumeHistory(history_id)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
},
|
||||
{
|
||||
commandName: 'Add Image to Canvas',
|
||||
}
|
||||
)
|
||||
return layer
|
||||
}
|
||||
|
||||
const remove = async (layer: any) => {
|
||||
await layer_util.deleteLayers([layer]) // delete previous layer
|
||||
}
|
||||
|
||||
export const resetViewer = () => {
|
||||
store.updateProperty('images', [])
|
||||
store.data.thumbnails = []
|
||||
store.data.prev_index = -1
|
||||
store.data.is_stored = []
|
||||
store.data.layers = []
|
||||
store.data.class_name = []
|
||||
store.data.can_click = true
|
||||
|
||||
mask_store.data.images = []
|
||||
mask_store.data.thumbnails = []
|
||||
init_store.data.images = []
|
||||
init_store.data.thumbnails = []
|
||||
}
|
||||
|
||||
const addAll = async () => {
|
||||
let i = 0
|
||||
for (let i = 0; i < store.data.images.length; i++) {
|
||||
if (
|
||||
store.data.is_stored[i] ||
|
||||
layer_util.Layer.doesLayerExist(store.data.layers?.[i])
|
||||
) {
|
||||
continue
|
||||
}
|
||||
await addWithHistory(
|
||||
store.data.images[i],
|
||||
mask_store.data?.output_images_masks?.[i] ?? void 0
|
||||
)
|
||||
}
|
||||
|
||||
session_ts.Session.endSession()
|
||||
}
|
||||
const discardAll = async () => {
|
||||
for (let i = 0; i < store.data.images.length; i++) {
|
||||
await remove(store.data.layers[i])
|
||||
}
|
||||
|
||||
session_ts.Session.endSession()
|
||||
}
|
||||
const onlySelected = () => {
|
||||
session_ts.Session.endSession()
|
||||
}
|
||||
export const handleOutputImageThumbnailClick = async (
|
||||
index: number,
|
||||
event?: any
|
||||
) => {
|
||||
try {
|
||||
if (!store.data.can_click) return null
|
||||
|
||||
store.data.can_click = false
|
||||
const prev_index = store.data.prev_index
|
||||
const image = store.data.images[index] || ''
|
||||
const is_stored = store.data.is_stored[index] || false
|
||||
const is_prev_stored = store.data.is_stored[prev_index] || false
|
||||
const prev_layer = store.data.layers[prev_index] || null
|
||||
const prev_image = store.data.images[prev_index] || ''
|
||||
|
||||
console.log('prev_index:', prev_index)
|
||||
console.log('is_stored:', is_stored)
|
||||
console.log('is_prev_stored:', is_prev_stored)
|
||||
console.log('prev_layer:', prev_layer)
|
||||
|
||||
// store.updateProperty('clicked_index', index)
|
||||
|
||||
let click_type: ClickTypeEnum = event
|
||||
? findClickType(event)
|
||||
: ClickTypeEnum.Click
|
||||
if (
|
||||
index === store.data.prev_index &&
|
||||
click_type === ClickTypeEnum.Click
|
||||
) {
|
||||
click_type = ClickTypeEnum.SecondClick
|
||||
//toggle functionality
|
||||
}
|
||||
|
||||
console.log('click_type:', click_type)
|
||||
if (click_type === ClickTypeEnum.Click) {
|
||||
//1) modify layer stacks
|
||||
|
||||
const layer = await addWithHistory(
|
||||
image,
|
||||
mask_store.data?.output_images_masks?.[index] ?? void 0
|
||||
)
|
||||
await remove(store.data.layers[index])
|
||||
console.log('layer:', layer)
|
||||
store.data.layers[index] = layer
|
||||
|
||||
if (is_prev_stored) {
|
||||
} else {
|
||||
await remove(prev_layer)
|
||||
}
|
||||
//2)change style
|
||||
store.data.class_name[prev_index] = is_prev_stored
|
||||
? ClassNameEnum.Green
|
||||
: ClassNameEnum.None
|
||||
|
||||
store.data.class_name[index] = is_stored
|
||||
? ClassNameEnum.Green
|
||||
: ClassNameEnum.Orange
|
||||
|
||||
//3)modify index
|
||||
store.data.prev_index = index
|
||||
} else if (click_type === ClickTypeEnum.ShiftClick) {
|
||||
//1) modify layer stacks
|
||||
if (prev_index === index) {
|
||||
store.data.class_name[index] = ClassNameEnum.Green
|
||||
} else {
|
||||
if (is_prev_stored) {
|
||||
} else {
|
||||
// await remove(prev_layer)
|
||||
}
|
||||
|
||||
const layer = await addWithHistory(
|
||||
image,
|
||||
mask_store.data?.output_images_masks?.[index] ?? void 0
|
||||
)
|
||||
await remove(store.data.layers[index])
|
||||
store.data.layers[index] = layer
|
||||
//2)change style
|
||||
store.data.class_name[prev_index] = ClassNameEnum.Green
|
||||
store.data.class_name[index] = ClassNameEnum.Green
|
||||
|
||||
//3)store index
|
||||
store.data.is_stored[prev_index] = true
|
||||
store.data.is_stored[index] = true
|
||||
store.data.prev_index = index
|
||||
}
|
||||
} else if (click_type === ClickTypeEnum.AltClick) {
|
||||
//1) modify layer stacks
|
||||
if (is_prev_stored) {
|
||||
} else {
|
||||
await remove(prev_layer)
|
||||
}
|
||||
await remove(store.data.layers[index])
|
||||
//2)change style
|
||||
store.data.class_name[prev_index] = is_prev_stored
|
||||
? ClassNameEnum.Green
|
||||
: ClassNameEnum.None
|
||||
store.data.class_name[index] = ClassNameEnum.None
|
||||
|
||||
//3)store index
|
||||
store.data.prev_index = -1
|
||||
store.data.is_stored[index] = false
|
||||
} else if (click_type === ClickTypeEnum.SecondClick) {
|
||||
//1) modify layer stacks
|
||||
if (is_prev_stored) {
|
||||
} else {
|
||||
await remove(prev_layer)
|
||||
}
|
||||
|
||||
//2)change style
|
||||
store.data.class_name[prev_index] = is_prev_stored
|
||||
? ClassNameEnum.Green
|
||||
: ClassNameEnum.None
|
||||
|
||||
//3)store index
|
||||
store.data.prev_index = -1
|
||||
}
|
||||
store.data.class_name = [...store.data.class_name]
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
store.data.can_click = true
|
||||
}
|
||||
const Viewer = observer(() => {
|
||||
// console.log('rendered', store.toJsFunc())
|
||||
const display_button: Boolean =
|
||||
session_ts.store.data.is_active && session_ts.store.data.can_generate
|
||||
const button_style = {
|
||||
display: display_button ? 'block' : 'none',
|
||||
marginRight: '3px',
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<SpSliderWithLabel
|
||||
out_min={50}
|
||||
out_max={300}
|
||||
in_min={1}
|
||||
in_max={10}
|
||||
// min={85}
|
||||
// max={300}
|
||||
|
||||
onSliderChange={(new_value: number) =>
|
||||
// event: React.ChangeEvent<HTMLInputElement>
|
||||
{
|
||||
try {
|
||||
console.log('change event triggered!')
|
||||
// const new_value = event.target.value
|
||||
// const base_width = 100
|
||||
// const scale_ratio = new_value / base_width
|
||||
|
||||
// store.updateProperty('height', scale_ratio)
|
||||
store.updateProperty('width', new_value)
|
||||
init_store.updateProperty('width', new_value)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
show-value={false}
|
||||
steps={1}
|
||||
output_value={store.data.width}
|
||||
label="Thumbnail Size"
|
||||
></SpSliderWithLabel>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-evenly',
|
||||
paddingTop: '3px',
|
||||
}}
|
||||
>
|
||||
<button
|
||||
title={Locale('Keep all generated images on the canvas')}
|
||||
className="btnSquare acceptClass acceptAllImgBtn"
|
||||
style={button_style}
|
||||
onClick={addAll}
|
||||
></button>
|
||||
<button
|
||||
title={Locale(
|
||||
'Delete all generated images from the canvas'
|
||||
)}
|
||||
className="btnSquare discardClass discardAllImgBtn"
|
||||
style={button_style}
|
||||
onClick={discardAll}
|
||||
></button>
|
||||
<button
|
||||
title={Locale('Keep only the highlighted images')}
|
||||
className="btnSquare acceptSelectedClass acceptSelectedImgBtn"
|
||||
style={button_style}
|
||||
onClick={onlySelected}
|
||||
></button>
|
||||
</div>
|
||||
<div>
|
||||
<SpCheckBox
|
||||
style={{
|
||||
display: [
|
||||
GenerationModeEnum.Inpaint,
|
||||
GenerationModeEnum.LassoInpaint,
|
||||
GenerationModeEnum.Outpaint,
|
||||
].includes(session_ts.store.data.mode)
|
||||
? void 0
|
||||
: 'none',
|
||||
marginRight: '10px',
|
||||
}}
|
||||
onChange={(event: any) => {
|
||||
store.data.auto_mask = event.target.checked
|
||||
}}
|
||||
checked={store.data.auto_mask}
|
||||
>
|
||||
{
|
||||
//@ts-ignore
|
||||
Locale('Apply Auto Masking')
|
||||
}
|
||||
</SpCheckBox>
|
||||
</div>
|
||||
<div style={{ border: '2px solid #6d6c6c', padding: '3px' }}>
|
||||
<Grid
|
||||
// images={init_store.data.images}
|
||||
thumbnails={init_store.data.thumbnails}
|
||||
thumbnails_styles={init_store.data.class_name}
|
||||
callback={(index: number, event: any) => {
|
||||
console.log(index)
|
||||
}}
|
||||
width={init_store.data.width}
|
||||
height={init_store.data.height}
|
||||
// clicked_index={init_store.data.clicked_index}
|
||||
// permanent_indices={init_store.data.permanent_indices}
|
||||
></Grid>
|
||||
</div>
|
||||
<div style={{ border: '2px solid #6d6c6c', padding: '3px' }}>
|
||||
<Grid
|
||||
// images={mask_store.data.images}
|
||||
thumbnails={mask_store.data.thumbnails}
|
||||
thumbnails_styles={mask_store.data.class_name}
|
||||
callback={(index: number, event: any) => {
|
||||
console.log(index)
|
||||
}}
|
||||
width={mask_store.data.width}
|
||||
height={mask_store.data.height}
|
||||
// clicked_index={init_store.data.clicked_index}
|
||||
// permanent_indices={init_store.data.permanent_indices}
|
||||
action_buttons={[
|
||||
{
|
||||
ComponentType: MoveToCanvasSvg,
|
||||
callback: async (index: number) => {
|
||||
await moveImageToLayer_old(
|
||||
mask_store.data.images[index],
|
||||
session_ts.store.data.selectionInfo,
|
||||
'mask'
|
||||
)
|
||||
},
|
||||
},
|
||||
]}
|
||||
></Grid>
|
||||
</div>
|
||||
<div style={{ border: '2px solid #6d6c6c', padding: '3px' }}>
|
||||
<Grid
|
||||
// images={store.data.images}
|
||||
thumbnails={store.data.thumbnails}
|
||||
thumbnails_styles={store.data.class_name}
|
||||
callback={handleOutputImageThumbnailClick}
|
||||
width={store.data.width}
|
||||
height={store.data.height}
|
||||
clicked_index={store.data.clicked_index}
|
||||
permanent_indices={store.data.permanent_indices}
|
||||
// action_buttons={[
|
||||
// {
|
||||
// ComponentType: MoveToCanvasSvg,
|
||||
// callback: (index: number) => {
|
||||
// console.log(
|
||||
// 'viewer callback:',
|
||||
// store.data.images[index],
|
||||
// g_generation_session.selectionInfo
|
||||
// )
|
||||
// moveImageToLayer(
|
||||
// store.data.images[index],
|
||||
// g_generation_session.selectionInfo
|
||||
// )
|
||||
// },
|
||||
// },
|
||||
// ]}
|
||||
></Grid>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
const ToolbarViewerButtons = observer(() => {
|
||||
const display_button: Boolean =
|
||||
session_ts.store.data.is_active && session_ts.store.data.can_generate
|
||||
const button_style = {
|
||||
display: display_button ? 'block' : 'none',
|
||||
|
||||
marginRight: '3px',
|
||||
marginBottom: '3px',
|
||||
}
|
||||
return (
|
||||
<div
|
||||
// style={{
|
||||
// display: 'flex',
|
||||
// justifyContent: 'space-evenly',
|
||||
// paddingTop: '3px',
|
||||
// }}
|
||||
>
|
||||
<button
|
||||
title={Locale('Keep all generated images on the canvas')}
|
||||
className="btnSquare acceptClass acceptAllImgBtn"
|
||||
style={button_style}
|
||||
onClick={addAll}
|
||||
></button>
|
||||
<button
|
||||
title={Locale('Delete all generated images from the canvas')}
|
||||
className="btnSquare discardClass discardAllImgBtn"
|
||||
style={button_style}
|
||||
onClick={discardAll}
|
||||
></button>
|
||||
<button
|
||||
title={Locale('Keep only the highlighted images')}
|
||||
className="btnSquare acceptSelectedClass acceptSelectedImgBtn"
|
||||
style={button_style}
|
||||
onClick={onlySelected}
|
||||
></button>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
// const node = document.getElementById('reactViewerContainer')!
|
||||
const containers = document.querySelectorAll('.reactViewerContainer')
|
||||
|
||||
containers.forEach((container) => {
|
||||
const root = ReactDOM.createRoot(container)
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<div style={{ border: '2px solid #6d6c6c', padding: '3px' }}>
|
||||
<Collapsible defaultIsOpen={true} label={Locale('Viewer')}>
|
||||
<Viewer></Viewer>
|
||||
</Collapsible>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
})
|
||||
|
||||
const button_container = document.getElementById('viewerButtonContainer')!
|
||||
const root = ReactDOM.createRoot(button_container)
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<ToolbarViewerButtons />
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
//deprecated file don't use
|
||||
|
||||
console.warn('api.js is deprecated, use typescript/util/ts/api.ts')
|
||||
|
||||
async function requestGet(url) {
|
||||
let json = null
|
||||
|
||||
|
|
@ -10,7 +14,7 @@ async function requestGet(url) {
|
|||
|
||||
json = await request.json()
|
||||
|
||||
console.log('json: ', json)
|
||||
// console.log('json: ', json)
|
||||
} catch (e) {
|
||||
console.warn(`issues requesting from ${full_url}`, e)
|
||||
}
|
||||
|
|
@ -36,7 +40,7 @@ async function requestPost(url, payload) {
|
|||
|
||||
json = await request.json()
|
||||
|
||||
console.log('json: ', json)
|
||||
// console.log('json: ', json)
|
||||
} catch (e) {
|
||||
console.warn(`issues requesting from ${full_url}`, e)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -552,15 +552,6 @@ function setControlImageSrc(image_src, element_index = 0) {
|
|||
// )
|
||||
control_net_image_element.src = image_src
|
||||
}
|
||||
function setControlMaskSrc(image_src, element_index = 0) {
|
||||
const control_net_image_element = document.querySelector(
|
||||
`#controlnet_settings_${element_index} .control_net_mask_`
|
||||
)
|
||||
// const control_net_image_element = document.getElementById(
|
||||
// 'control_net_mask' + '_' + element_index
|
||||
// )
|
||||
control_net_image_element.src = image_src
|
||||
}
|
||||
|
||||
function setProgressImageSrc(image_src) {
|
||||
// const progress_image_element = document.getElementById('progressImage')
|
||||
|
|
@ -1153,7 +1144,6 @@ module.exports = {
|
|||
setLinkWidthHeightState,
|
||||
isSquareThumbnail,
|
||||
setControlImageSrc,
|
||||
setControlMaskSrc,
|
||||
|
||||
setHordeApiKey,
|
||||
populateMenu,
|
||||
|
|
|
|||
598
utility/io.js
|
|
@ -2,7 +2,6 @@ const psapi = require('../psapi')
|
|||
|
||||
const layer_util = require('../utility/layer')
|
||||
const general = require('./general')
|
||||
const Jimp = require('../jimp/browser/lib/jimp.min')
|
||||
|
||||
const { executeAsModal } = require('photoshop').core
|
||||
const batchPlay = require('photoshop').action.batchPlay
|
||||
|
|
@ -216,6 +215,7 @@ class IO {
|
|||
new_doc.width,
|
||||
new_doc.height
|
||||
) //
|
||||
|
||||
await layer_util.Layer.moveTo(new_layer, 0, 0) //move to the top left corner
|
||||
//
|
||||
await IOHelper.saveAsWebpExe(doc_entry) //save current document as .webp file, save it into doc_entry folder
|
||||
|
|
@ -312,15 +312,37 @@ class IO {
|
|||
) {
|
||||
let layer
|
||||
if (format === 'png') {
|
||||
layer = await IOBase64ToLayer.base64PngToLayer(
|
||||
base64_png,
|
||||
image_name
|
||||
)
|
||||
try {
|
||||
await executeAsModal(async (context) => {
|
||||
// let history_id
|
||||
// try {
|
||||
// history_id = await context.hostControl.suspendHistory({
|
||||
// documentID: app.activeDocument.id,
|
||||
// name: 'Place Image',
|
||||
// })
|
||||
// } catch (e) {
|
||||
// console.warn(e)
|
||||
// }
|
||||
|
||||
psapi.setVisibleExe(layer, true)
|
||||
await layer_util.Layer.scaleTo(layer, width, height) //
|
||||
await layer_util.Layer.moveTo(layer, to_x, to_y) //move to the top left corner
|
||||
psapi.setVisibleExe(layer, true)
|
||||
layer = await IOBase64ToLayer.base64PngToLayer(
|
||||
base64_png,
|
||||
image_name
|
||||
)
|
||||
|
||||
await psapi.setVisibleExe(layer, true)
|
||||
await layer_util.Layer.scaleTo(layer, width, height) //
|
||||
await layer_util.Layer.moveTo(layer, to_x, to_y) //move to the top left corner
|
||||
await psapi.setVisibleExe(layer, true)
|
||||
|
||||
// try {
|
||||
// await context.hostControl.resumeHistory(history_id)
|
||||
// } catch (e) {
|
||||
// console.warn(e)
|
||||
// }
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
return layer
|
||||
}
|
||||
|
|
@ -565,13 +587,24 @@ class IOHelper {
|
|||
const crop_h = selectionInfo.height
|
||||
const base64_url_result = await Jimp.read(arrayBuffer)
|
||||
.then(async (img) => {
|
||||
let cropped_img = await img.crop(crop_x, crop_y, crop_w, crop_h)
|
||||
let cropped_img = await img.crop(
|
||||
crop_x,
|
||||
crop_y,
|
||||
crop_w,
|
||||
crop_h
|
||||
// crop_w - 1,
|
||||
// crop_h - 1
|
||||
)
|
||||
|
||||
let resized_img
|
||||
if (b_resize) {
|
||||
resized_img = await cropped_img.resize(
|
||||
resize_width,
|
||||
resize_height
|
||||
resize_height,
|
||||
// Jimp.RESIZE_BILINEAR
|
||||
// Jimp.RESIZE_NEAREST_NEIGHBOR
|
||||
settings_tab_ts.store.data.scale_interpolation_method
|
||||
.jimp
|
||||
)
|
||||
} else {
|
||||
resized_img = cropped_img
|
||||
|
|
@ -728,7 +761,7 @@ class IOLog {
|
|||
append: true,
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
_warn(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -821,6 +854,530 @@ class IOJson {
|
|||
}
|
||||
}
|
||||
|
||||
async function createThumbnail(base64Image, width = 100) {
|
||||
const image = await Jimp.read(Buffer.from(base64Image, 'base64'))
|
||||
image.resize(
|
||||
width,
|
||||
Jimp.AUTO,
|
||||
settings_tab_ts.store.data.scale_interpolation_method.jimp
|
||||
)
|
||||
const thumbnail = await image.getBase64Async(Jimp.MIME_PNG)
|
||||
return thumbnail
|
||||
}
|
||||
|
||||
async function getImageFromCanvas() {
|
||||
const width = html_manip.getWidth()
|
||||
const height = html_manip.getHeight()
|
||||
const selectionInfo = await psapi.getSelectionInfoExe()
|
||||
const base64 = await io.IO.getSelectionFromCanvasAsBase64Interface_New(
|
||||
width,
|
||||
height,
|
||||
selectionInfo,
|
||||
true
|
||||
)
|
||||
return base64
|
||||
}
|
||||
async function getBase64FromJimp(jimp_image) {
|
||||
const dataURL = await jimp_image.getBase64Async(Jimp.MIME_PNG)
|
||||
const base64 = dataURL.replace(/^data:image\/png;base64,/, '')
|
||||
return base64
|
||||
}
|
||||
|
||||
function transparentToMask(x, y, idx) {
|
||||
const alpha = this.bitmap.data[idx + 3]
|
||||
let color
|
||||
if (alpha === 0) {
|
||||
color = 0xffffffff
|
||||
} else if (alpha === 255) {
|
||||
color = 0x000000ff
|
||||
} else {
|
||||
color = Jimp.rgbaToInt(alpha, alpha, alpha, 255)
|
||||
}
|
||||
this.setPixelColor(color, x, y)
|
||||
}
|
||||
function inpaintTransparentToMask(x, y, idx) {
|
||||
const alpha = this.bitmap.data[idx + 3]
|
||||
let color
|
||||
// if (alpha === 0) {
|
||||
// color = 0x000000ff
|
||||
// } else if (alpha === 255) {
|
||||
// color = 0xffffffff
|
||||
// } else {
|
||||
// color = Jimp.rgbaToInt(alpha, alpha, alpha, 255)
|
||||
// }
|
||||
|
||||
if (alpha === 0) {
|
||||
color = 0x000000ff
|
||||
} else {
|
||||
color = 0xffffffff
|
||||
}
|
||||
this.setPixelColor(color, x, y)
|
||||
}
|
||||
function transparentToWhiteBackground(x, y, idx) {
|
||||
const alpha = this.bitmap.data[idx + 3]
|
||||
let color
|
||||
if (alpha === 0) {
|
||||
color = 0xffffffff
|
||||
} else {
|
||||
color = Jimp.rgbaToInt(
|
||||
this.bitmap.data[idx],
|
||||
this.bitmap.data[idx + 1],
|
||||
this.bitmap.data[idx + 2],
|
||||
255
|
||||
) // remove transparency but keep the color, This is bad. used as workaround Auto1111 not able to handle alpha channels
|
||||
}
|
||||
this.setPixelColor(color, x, y)
|
||||
}
|
||||
async function getMask() {
|
||||
try {
|
||||
let b = app.activeDocument.backgroundLayer
|
||||
await executeAsModal(() => (b.visible = false))
|
||||
const base64 = await getImageFromCanvas()
|
||||
await executeAsModal(() => (b.visible = true))
|
||||
const jimp_image = await Jimp.read(Buffer.from(base64, 'base64'))
|
||||
|
||||
const jimp_mask = await jimp_image.scan(
|
||||
0,
|
||||
0,
|
||||
jimp_image.bitmap.width,
|
||||
jimp_image.bitmap.height,
|
||||
transparentToMask
|
||||
)
|
||||
html_manip.setInitImageSrc(
|
||||
await jimp_mask.getBase64Async(Jimp.MIME_PNG)
|
||||
)
|
||||
const mask = await getBase64FromJimp(jimp_mask)
|
||||
return mask
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function getImg2ImgInitImage() {
|
||||
//the init image will has transparent pixel in it
|
||||
//the mask will be a grayscale image/white and black
|
||||
try {
|
||||
let b = app.activeDocument.backgroundLayer
|
||||
await executeAsModal(() => (b.visible = false))
|
||||
const base64 = await getImageFromCanvas()
|
||||
await executeAsModal(() => (b.visible = true))
|
||||
const init_image = base64
|
||||
|
||||
html_manip.setInitImageSrc(general.base64ToBase64Url(init_image)) // convert jimp_image to img.src data
|
||||
|
||||
// console.log('mask: ', mask)
|
||||
return init_image
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
async function getOutpaintInitImageAndMask() {
|
||||
//the init image will has transparent pixel in it
|
||||
//the mask will be a grayscale image/white and black
|
||||
try {
|
||||
let b = app.activeDocument.backgroundLayer
|
||||
await executeAsModal(() => (b.visible = false))
|
||||
const base64 = await getImageFromCanvas()
|
||||
await executeAsModal(() => (b.visible = true))
|
||||
const init_image = base64
|
||||
let jimp_init = await Jimp.read(Buffer.from(base64, 'base64'))
|
||||
|
||||
let jimp_mask = await jimp_init
|
||||
.clone()
|
||||
.scan(
|
||||
0,
|
||||
0,
|
||||
jimp_init.bitmap.width,
|
||||
jimp_init.bitmap.height,
|
||||
transparentToMask
|
||||
)
|
||||
// jimp_init = await jimp_init.scan(
|
||||
// 0,
|
||||
// 0,
|
||||
// jimp_init.bitmap.width,
|
||||
// jimp_init.bitmap.height,
|
||||
// transparentToWhiteBackground
|
||||
// // transparentToMask
|
||||
// )
|
||||
html_manip.setInitImageMaskSrc(
|
||||
await jimp_mask.getBase64Async(Jimp.MIME_PNG)
|
||||
) // convert jimp_image to img.src data
|
||||
html_manip.setInitImageSrc(
|
||||
await jimp_init.getBase64Async(Jimp.MIME_PNG)
|
||||
) // convert jimp_image to img.src data
|
||||
|
||||
const mask = await getBase64FromJimp(jimp_mask)
|
||||
// console.log('mask: ', mask)
|
||||
return {
|
||||
init_image,
|
||||
mask,
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
//generate black and white mask image from
|
||||
async function getMaskFromCanvas() {
|
||||
try {
|
||||
await executeAsModal(async () => await layer_util.toggleActiveLayer()) //only white mark layer should be visible
|
||||
let mask_base64 = await getImageFromCanvas()
|
||||
await executeAsModal(async () => {
|
||||
await layer_util.toggleActiveLayer() // undo the toggling operation, active layer will be visible
|
||||
app.activeDocument.activeLayers[0].visible = false //hide the white mark
|
||||
})
|
||||
let jimp_mask = await Jimp.read(Buffer.from(mask_base64, 'base64')) //make jimp object
|
||||
jimp_mask = await jimp_mask.scan(
|
||||
0,
|
||||
0,
|
||||
jimp_mask.bitmap.width,
|
||||
jimp_mask.bitmap.height,
|
||||
inpaintTransparentToMask
|
||||
) //convert transparent image to black and white image
|
||||
mask_base64 = await getBase64FromJimp(jimp_mask)
|
||||
return mask_base64
|
||||
} catch (e) {
|
||||
warn(e)
|
||||
}
|
||||
}
|
||||
async function getInpaintInitImageAndMask() {
|
||||
try {
|
||||
await executeAsModal(async () => await layer_util.toggleActiveLayer()) //only white mark layer should be visible
|
||||
const mask_base64 = await getImageFromCanvas()
|
||||
await executeAsModal(async () => {
|
||||
await layer_util.toggleActiveLayer() // undo the toggling operation, active layer will be visible
|
||||
app.activeDocument.activeLayers[0].visible = false //hide the white mark
|
||||
})
|
||||
const init_base64 = await getImageFromCanvas()
|
||||
|
||||
let jimp_mask = await Jimp.read(Buffer.from(mask_base64, 'base64')) //make jimp object
|
||||
let jimp_init = await Jimp.read(Buffer.from(init_base64, 'base64')) //make jimp object, jimp_init will have transparent pixels, should we convert to white??
|
||||
|
||||
jimp_mask = await jimp_mask.scan(
|
||||
0,
|
||||
0,
|
||||
jimp_mask.bitmap.width,
|
||||
jimp_mask.bitmap.height,
|
||||
inpaintTransparentToMask
|
||||
) //convert transparent image to black and white image
|
||||
|
||||
html_manip.setInitImageMaskSrc(
|
||||
await jimp_mask.getBase64Async(Jimp.MIME_PNG)
|
||||
)
|
||||
html_manip.setInitImageSrc(
|
||||
await jimp_init.getBase64Async(Jimp.MIME_PNG)
|
||||
)
|
||||
|
||||
const mask = await getBase64FromJimp(jimp_mask)
|
||||
const init_image = await getBase64FromJimp(jimp_init)
|
||||
return { init_image, mask }
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function saveFileInSubFolder(b64Image, sub_folder_name, file_name) {
|
||||
// const b64Image =
|
||||
// 'iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAADMElEQVR4nOzVwQnAIBQFQYXff81RUkQCOyDj1YOPnbXWPmeTRef+/3O/OyBjzh3CD95BfqICMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMO0TAAD//2Anhf4QtqobAAAAAElFTkSuQmCC'
|
||||
|
||||
const img = _base64ToArrayBuffer(b64Image)
|
||||
|
||||
// const img_name = 'temp_output_image.png'
|
||||
const img_name = file_name
|
||||
const folder = await storage.localFileSystem.getDataFolder()
|
||||
const documentFolderName = sub_folder_name
|
||||
let documentFolder
|
||||
try {
|
||||
documentFolder = await folder.getEntry(documentFolderName)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
//create document folder
|
||||
documentFolder = await folder.createFolder(documentFolderName)
|
||||
}
|
||||
|
||||
console.log('documentFolder.nativePath: ', documentFolder.nativePath)
|
||||
const file = await documentFolder.createFile(img_name, { overwrite: true })
|
||||
|
||||
await file.write(img, { format: storage.formats.binary })
|
||||
|
||||
const token = await storage.localFileSystem.createSessionToken(file) // batchPlay requires a token on _path
|
||||
}
|
||||
//REFACTOR: move to document.js
|
||||
async function saveJsonFileInSubFolder(json, sub_folder_name, file_name) {
|
||||
// const b64Image =
|
||||
// 'iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAADMElEQVR4nOzVwQnAIBQFQYXff81RUkQCOyDj1YOPnbXWPmeTRef+/3O/OyBjzh3CD95BfqICMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMO0TAAD//2Anhf4QtqobAAAAAElFTkSuQmCC'
|
||||
|
||||
// const img_name = 'temp_output_image.png'
|
||||
|
||||
const json_file_name = file_name
|
||||
|
||||
const folder = await storage.localFileSystem.getDataFolder()
|
||||
const documentFolderName = sub_folder_name
|
||||
let documentFolder
|
||||
try {
|
||||
documentFolder = await folder.getEntry(documentFolderName)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
//create document folder
|
||||
documentFolder = await folder.createFolder(documentFolderName)
|
||||
}
|
||||
|
||||
console.log('documentFolder.nativePath: ', documentFolder.nativePath)
|
||||
const file = await documentFolder.createFile(json_file_name, {
|
||||
type: storage.types.file,
|
||||
overwrite: true,
|
||||
})
|
||||
|
||||
const JSONInPrettyFormat = JSON.stringify(json, undefined, 4)
|
||||
await file.write(JSONInPrettyFormat, {
|
||||
format: storage.formats.utf8,
|
||||
append: false,
|
||||
})
|
||||
|
||||
const token = await storage.localFileSystem.createSessionToken(file) // batchPlay requires a token on _path
|
||||
}
|
||||
async function fixTransparentEdges(base64) {
|
||||
function transparentToOpaque(x, y, idx) {
|
||||
const alpha = this.bitmap.data[idx + 3]
|
||||
if (alpha > 0 && alpha < 255) {
|
||||
this.bitmap.data[idx + 3] = 0 //make semi transparent pixels completely transparent
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let jimp_img = await Jimp.read(Buffer.from(base64, 'base64'))
|
||||
|
||||
jimp_img = await jimp_img.scan(
|
||||
0,
|
||||
0,
|
||||
jimp_img.bitmap.width,
|
||||
jimp_img.bitmap.height,
|
||||
transparentToOpaque
|
||||
)
|
||||
const opaque_base64 = await getBase64FromJimp(jimp_img)
|
||||
return opaque_base64
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function maskFromInitImage(base64) {
|
||||
function setTransparentToBlack(x, y, idx) {
|
||||
let alpha = this.bitmap.data[idx + 3]
|
||||
if (alpha !== 0) {
|
||||
this.bitmap.data[idx] = 0
|
||||
this.bitmap.data[idx + 1] = 0
|
||||
this.bitmap.data[idx + 2] = 0
|
||||
this.bitmap.data[idx + 3] = 255
|
||||
} else {
|
||||
//alpha === 0
|
||||
|
||||
this.bitmap.data[idx] = 255
|
||||
this.bitmap.data[idx + 1] = 255
|
||||
this.bitmap.data[idx + 2] = 255
|
||||
this.bitmap.data[idx + 3] = 255
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let jimp_img = await Jimp.read(Buffer.from(base64, 'base64'))
|
||||
|
||||
jimp_img = await jimp_img.scan(
|
||||
0,
|
||||
0,
|
||||
jimp_img.bitmap.width,
|
||||
jimp_img.bitmap.height,
|
||||
setTransparentToBlack
|
||||
)
|
||||
const mask_base64 = await getBase64FromJimp(jimp_img)
|
||||
return mask_base64
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
async function fixMaskEdges(base64) {
|
||||
function grayScaleToBlack(x, y, idx) {
|
||||
if (
|
||||
this.bitmap.data[idx] !== 255 ||
|
||||
this.bitmap.data[idx + 1] !== 255 ||
|
||||
this.bitmap.data[idx + 2] !== 255
|
||||
) {
|
||||
this.bitmap.data[idx] = 0
|
||||
this.bitmap.data[idx + 1] = 0
|
||||
this.bitmap.data[idx + 2] = 0
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let jimp_img = await Jimp.read(Buffer.from(base64, 'base64'))
|
||||
|
||||
jimp_img = await jimp_img.scan(
|
||||
0,
|
||||
0,
|
||||
jimp_img.bitmap.width,
|
||||
jimp_img.bitmap.height,
|
||||
grayScaleToBlack
|
||||
)
|
||||
const opaque_base64 = await getBase64FromJimp(jimp_img)
|
||||
return opaque_base64
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function getUniqueDocumentId() {
|
||||
try {
|
||||
let uniqueDocumentId = await psapi.readUniqueDocumentIdExe()
|
||||
|
||||
console.log(
|
||||
'getUniqueDocumentId(): uniqueDocumentId: ',
|
||||
uniqueDocumentId
|
||||
)
|
||||
|
||||
// Regular expression to check if string is a valid UUID
|
||||
const regexExp =
|
||||
/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/gi
|
||||
|
||||
// String with valid UUID separated by dash
|
||||
// const str = 'a24a6ea4-ce75-4665-a070-57453082c256'
|
||||
|
||||
const isValidId = regexExp.test(uniqueDocumentId) // true
|
||||
console.log('isValidId: ', isValidId)
|
||||
if (isValidId == false) {
|
||||
let uuid = self.crypto.randomUUID()
|
||||
console.log(uuid) // for example "36b8f84d-df4e-4d49-b662-bcde71a8764f"
|
||||
await psapi.saveUniqueDocumentIdExe(uuid)
|
||||
uniqueDocumentId = uuid
|
||||
}
|
||||
return uniqueDocumentId
|
||||
} catch (e) {
|
||||
console.warn('warning Document Id may not be valid', e)
|
||||
}
|
||||
}
|
||||
async function getImageSize(base64) {
|
||||
const image = await Jimp.read(Buffer.from(base64, 'base64'))
|
||||
const width = image.bitmap.width
|
||||
const height = image.bitmap.height
|
||||
return { width, height }
|
||||
}
|
||||
async function convertGrayscaleToMonochrome(base64) {
|
||||
function grayToMonoPixel(x, y, idx) {
|
||||
// convert any grayscale value to white, resulting in black and white image
|
||||
|
||||
// if (this.bitmap.data[idx] > 0) {
|
||||
// this.bitmap.data[idx] = 255
|
||||
// }
|
||||
// if (this.bitmap.data[idx + 1] > 0) {
|
||||
// this.bitmap.data[idx + 1] = 255
|
||||
// }
|
||||
// if (this.bitmap.data[idx + 2] > 0) {
|
||||
// this.bitmap.data[idx + 2] = 255
|
||||
// }
|
||||
let color
|
||||
if (
|
||||
this.bitmap.data[idx] !== 0 &&
|
||||
this.bitmap.data[idx + 1] !== 0 &&
|
||||
this.bitmap.data[idx + 2] !== 0
|
||||
) {
|
||||
color = 0xffffffff
|
||||
} else {
|
||||
color = 0x000000ff
|
||||
}
|
||||
this.setPixelColor(color, x, y)
|
||||
}
|
||||
try {
|
||||
const jimp_image = await Jimp.read(Buffer.from(base64, 'base64'))
|
||||
|
||||
const jimp_mask = await jimp_image.scan(
|
||||
0,
|
||||
0,
|
||||
jimp_image.bitmap.width,
|
||||
jimp_image.bitmap.height,
|
||||
grayToMonoPixel
|
||||
)
|
||||
const base64_monochrome_mask = await getBase64FromJimp(jimp_mask)
|
||||
return base64_monochrome_mask
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function convertBlackToTransparentKeepBorders(
|
||||
base64,
|
||||
b_borders_or_corners = enum_ts.MaskModeEnum.Transparent // false for borders, true for corners
|
||||
) {
|
||||
try {
|
||||
let jimp_mask = await Jimp.read(Buffer.from(base64, 'base64'))
|
||||
|
||||
const width = jimp_mask.bitmap.width
|
||||
const height = jimp_mask.bitmap.height
|
||||
jimp_mask = await jimp_mask.scan(
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
function (x, y, idx) {
|
||||
if (b_borders_or_corners === enum_ts.MaskModeEnum.Borders) {
|
||||
// keep borders
|
||||
if (
|
||||
x === 0 ||
|
||||
y === 0 ||
|
||||
x === width - 1 ||
|
||||
y === height - 1
|
||||
)
|
||||
return
|
||||
} else if (
|
||||
b_borders_or_corners === enum_ts.MaskModeEnum.Corners
|
||||
) {
|
||||
// keep corners
|
||||
if (
|
||||
(x === 0 && y === 0) ||
|
||||
(x === 0 && y === height - 1) ||
|
||||
(x === width - 1 && y === 0) ||
|
||||
(x === width - 1 && y === height - 1)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const red = this.bitmap.data[idx + 0]
|
||||
const green = this.bitmap.data[idx + 1]
|
||||
const blue = this.bitmap.data[idx + 2]
|
||||
if (red === 0 && green === 0 && blue === 0) {
|
||||
this.bitmap.data[idx + 3] = 0
|
||||
}
|
||||
}
|
||||
)
|
||||
const base64_mask = await getBase64FromJimp(jimp_mask)
|
||||
return base64_mask
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteFileIfLargerThan(file_name, size_mb = 200) {
|
||||
// const file = await fs.getEntry('path/to/file.txt')
|
||||
try {
|
||||
const plugin_folder = await fs.getDataFolder()
|
||||
try {
|
||||
var file = await plugin_folder.getEntry(file_name)
|
||||
} catch (e) {
|
||||
_warn(e)
|
||||
}
|
||||
if (file) {
|
||||
const contents = await file.read({ format: storage.formats.binary })
|
||||
// storage.formats.utf8
|
||||
const fileSizeInBytes = contents.byteLength
|
||||
const fileSizeInMegabytes = fileSizeInBytes / (1024 * 1024)
|
||||
|
||||
if (fileSizeInMegabytes > size_mb) {
|
||||
await fs.removeEntry(file)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// console.warn(e)
|
||||
_warn(e)
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
IO,
|
||||
snapShotLayerExe,
|
||||
|
|
@ -832,4 +1389,21 @@ module.exports = {
|
|||
convertBlackAndWhiteImageToRGBChannels2,
|
||||
convertBlackAndWhiteImageToRGBChannels3,
|
||||
isBlackAndWhiteImage,
|
||||
createThumbnail,
|
||||
getMask,
|
||||
getOutpaintInitImageAndMask,
|
||||
getInpaintInitImageAndMask,
|
||||
getImg2ImgInitImage,
|
||||
saveFileInSubFolder,
|
||||
saveJsonFileInSubFolder,
|
||||
fixTransparentEdges,
|
||||
fixMaskEdges,
|
||||
maskFromInitImage,
|
||||
getImageFromCanvas,
|
||||
getUniqueDocumentId,
|
||||
getImageSize,
|
||||
convertGrayscaleToMonochrome,
|
||||
deleteFileIfLargerThan,
|
||||
getMaskFromCanvas,
|
||||
convertBlackToTransparentKeepBorders,
|
||||
}
|
||||
|
|
|
|||
142
utility/layer.js
|
|
@ -11,6 +11,9 @@ const {
|
|||
} = require('../psapi')
|
||||
|
||||
const psapi = require('../psapi')
|
||||
// const Jimp = require('../jimp/browser/lib/jimp.min')
|
||||
// const { settings_tab_ts } = require('../typescripts/dist/bundle')
|
||||
const constants = require('photoshop').constants
|
||||
|
||||
async function createNewLayerExe(layerName, opacity = 100) {
|
||||
await executeAsModal(async () => {
|
||||
|
|
@ -157,13 +160,128 @@ class Layer {
|
|||
const scale_x_ratio = (new_width / layer_info.width) * 100
|
||||
const scale_y_ratio = (new_height / layer_info.height) * 100
|
||||
console.log('scale_x_y_ratio:', scale_x_ratio, scale_y_ratio)
|
||||
await layer.scale(scale_x_ratio, scale_y_ratio)
|
||||
// await layer.scale(
|
||||
// scale_x_ratio,
|
||||
// scale_y_ratio,
|
||||
// constants.ResampleMethod.BILINEAR
|
||||
// )
|
||||
await layer_util.Layer.resizeImageExe(
|
||||
scale_x_ratio,
|
||||
scale_y_ratio
|
||||
)
|
||||
await psapi.reSelectMarqueeExe(selection_info)
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
static async resizeImage(percent_width, percent_height) {
|
||||
// let imageSizeDescriptor = {
|
||||
// _obj: 'imageSize',
|
||||
// scaleStyles: true,
|
||||
// constrainProportions: true,
|
||||
// interfaceIconFrameDimmed: {
|
||||
// _enum: 'interpolationType',
|
||||
// // _value: 'automaticInterpolation',
|
||||
// _value: 'bilinear',
|
||||
// },
|
||||
// _options: {
|
||||
// dialogOptions: 'dontDisplay',
|
||||
// },
|
||||
// }
|
||||
|
||||
// if (width !== undefined && width !== null) {
|
||||
// imageSizeDescriptor.width = {
|
||||
// _unit: 'pixelsUnit',
|
||||
// _value: width,
|
||||
// }
|
||||
// }
|
||||
// if (height !== undefined && height !== null) {
|
||||
// imageSizeDescriptor.height = {
|
||||
// _unit: 'pixelsUnit',
|
||||
// _value: height,
|
||||
// }
|
||||
// }
|
||||
const setInterpolationMethodDesc = {
|
||||
_obj: 'set',
|
||||
_target: [
|
||||
{
|
||||
_ref: 'property',
|
||||
_property: 'generalPreferences',
|
||||
},
|
||||
{
|
||||
_ref: 'application',
|
||||
_enum: 'ordinal',
|
||||
_value: 'targetEnum',
|
||||
},
|
||||
],
|
||||
to: {
|
||||
_obj: 'generalPreferences',
|
||||
interpolationMethod: {
|
||||
_enum: 'interpolationType',
|
||||
// _value: 'bilinear',
|
||||
_value: settings_tab_ts.store.data
|
||||
.scale_interpolation_method.photoshop,
|
||||
},
|
||||
},
|
||||
_isCommand: true,
|
||||
}
|
||||
let imageSizeDescriptor = {
|
||||
_obj: 'transform',
|
||||
_target: [
|
||||
{
|
||||
_ref: 'layer',
|
||||
_enum: 'ordinal',
|
||||
_value: 'targetEnum',
|
||||
},
|
||||
],
|
||||
freeTransformCenterState: {
|
||||
_enum: 'quadCenterState',
|
||||
_value: 'QCSAverage',
|
||||
},
|
||||
// offset: {
|
||||
// _obj: 'offset',
|
||||
// horizontal: {
|
||||
// _unit: 'pixelsUnit',
|
||||
// _value: 24.4388124547429,
|
||||
// },
|
||||
// vertical: {
|
||||
// _unit: 'pixelsUnit',
|
||||
// _value: -24.4388124547429,
|
||||
// },
|
||||
// },
|
||||
width: {
|
||||
_unit: 'percentUnit',
|
||||
_value: percent_width,
|
||||
},
|
||||
height: {
|
||||
_unit: 'percentUnit',
|
||||
_value: percent_height,
|
||||
},
|
||||
linked: true,
|
||||
interfaceIconFrameDimmed: {
|
||||
_enum: 'interpolationType',
|
||||
// _value: 'bilinear',
|
||||
_value: settings_tab_ts.store.data.scale_interpolation_method
|
||||
.photoshop,
|
||||
},
|
||||
_isCommand: true,
|
||||
}
|
||||
|
||||
return batchPlay([setInterpolationMethodDesc, imageSizeDescriptor], {
|
||||
synchronousExecution: true,
|
||||
modalBehavior: 'execute',
|
||||
})
|
||||
}
|
||||
static async resizeImageExe(percent_width, percent_height) {
|
||||
try {
|
||||
await executeAsModal(async () => {
|
||||
await this.resizeImage(percent_width, percent_height)
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}
|
||||
|
||||
static async moveTo(layer, to_x, to_y) {
|
||||
try {
|
||||
|
|
@ -277,6 +395,27 @@ const createSolidLayerDesc = (r, g, b) => ({
|
|||
},
|
||||
})
|
||||
|
||||
async function toggleActiveLayer() {
|
||||
const result = await batchPlay(
|
||||
[
|
||||
{
|
||||
_obj: 'show',
|
||||
null: [
|
||||
{
|
||||
_ref: 'layer',
|
||||
_enum: 'ordinal',
|
||||
_value: 'targetEnum',
|
||||
},
|
||||
],
|
||||
toggleOptionsPalette: true,
|
||||
_options: {
|
||||
dialogOptions: 'dontDisplay',
|
||||
},
|
||||
},
|
||||
],
|
||||
{}
|
||||
)
|
||||
}
|
||||
const toggleBackgroundLayerDesc = () => ({
|
||||
_obj: 'show',
|
||||
null: [
|
||||
|
|
@ -354,4 +493,5 @@ module.exports = {
|
|||
createSolidLayerDesc,
|
||||
makeBackgroundLayerDesc,
|
||||
toggleBackgroundLayerExe,
|
||||
toggleActiveLayer,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,10 +71,13 @@ class Notification {
|
|||
}
|
||||
return false
|
||||
}
|
||||
static async inactiveSelectionArea(is_active_session) {
|
||||
static async inactiveSelectionArea(
|
||||
is_active_session,
|
||||
button_label = 'Continue Session'
|
||||
) {
|
||||
let buttons = ['Cancel', 'Rectangular Marquee']
|
||||
if (is_active_session) {
|
||||
buttons.push('Continue Session')
|
||||
buttons.push(button_label)
|
||||
}
|
||||
const r1 = await dialog_box.prompt(
|
||||
'Please Select a Rectangular Area',
|
||||
|
|
@ -89,7 +92,7 @@ class Notification {
|
|||
console.log('Rectangular Marquee')
|
||||
psapi.selectMarqueeRectangularToolExe()
|
||||
return false // should this be false?! what does true and false means in this context?! Yes: it should be false since boolean value represent wither we have an active selection area or not
|
||||
} else if (r1 === 'Continue Session') {
|
||||
} else if (r1 === button_label) {
|
||||
await activateSessionSelectionArea()
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ const html_manip = require('../html_manip')
|
|||
const Enum = require('../../enum')
|
||||
const event = require('../event')
|
||||
|
||||
// const control_net = require('../../utility/tab/control_net')
|
||||
let settings = {
|
||||
model: null,
|
||||
prompt_shortcut: null,
|
||||
|
|
@ -161,10 +160,9 @@ function getPresetSettings(preset_type) {
|
|||
if (preset_type === Enum.PresetTypeEnum['SDPreset']) {
|
||||
preset_settings = g_ui_settings_object.getSettings()
|
||||
} else if (preset_type === Enum.PresetTypeEnum['ControlNetPreset']) {
|
||||
const { ControlNetUnit } = require('../../utility/tab/control_net') // only import ControlNetUnit to avoid circular dependency
|
||||
// preset_settings = control_net.ControlNetUnit.getUnits()
|
||||
|
||||
preset_settings = ControlNetUnit.getUnits()
|
||||
const { getUnitsData } =
|
||||
require('../../typescripts/dist/bundle').control_net // only import ControlNetUnit to avoid circular dependency
|
||||
preset_settings = getUnitsData()
|
||||
}
|
||||
return preset_settings
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ class hordeGenerator {
|
|||
const base64_image = _arrayBufferToBase64(image_buffer) //convert the buffer to base64
|
||||
//send the base64 to the server to save the file in the desired directory
|
||||
// await sdapi.requestSavePng(base64_image, image_name)
|
||||
await saveFileInSubFolder(base64_image, document_name, image_name)
|
||||
await io.saveFileInSubFolder(base64_image, document_name, image_name)
|
||||
return base64_image
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +156,7 @@ class hordeGenerator {
|
|||
const base64_image = _arrayBufferToBase64(image_buffer) //convert the buffer to base64
|
||||
//send the base64 to the server to save the file in the desired directory
|
||||
// await sdapi.requestSavePng(base64_image, image_name)
|
||||
await saveFileInSubFolder(base64_image, document_name, image_name)
|
||||
await io.saveFileInSubFolder(base64_image, document_name, image_name)
|
||||
return base64_image
|
||||
}
|
||||
|
||||
|
|
@ -249,7 +249,7 @@ class hordeGenerator {
|
|||
|
||||
g_generation_session.base64OutputImages[path] =
|
||||
image_info['base64']
|
||||
await saveJsonFileInSubFolder(
|
||||
await io.saveJsonFileInSubFolder(
|
||||
this.plugin_settings,
|
||||
document_name,
|
||||
json_file_name
|
||||
|
|
|
|||
|
|
@ -17,23 +17,15 @@ function replaceShortcut(text, prompt_shortcut_json) {
|
|||
return content
|
||||
})
|
||||
|
||||
// original_substrings = list(map(lambda s: '{'+s+'}',raw_keywords))
|
||||
|
||||
// print("strip_keywords: ", strip_keywords)
|
||||
// print("original_substrings: ",original_substrings)
|
||||
// # print ("text:",text)
|
||||
|
||||
let i = 0
|
||||
for (const word of strip_keywords) {
|
||||
// # word = word.strip()
|
||||
// print("word: ",word)
|
||||
if (word.length > 0 && prompt_shortcut_json.hasOwnProperty(word)) {
|
||||
const prompt = prompt_shortcut_json[word]
|
||||
console.log('prompt: ', prompt)
|
||||
// console.log('prompt: ', prompt)
|
||||
text = text.replace(original_keywords[i], prompt)
|
||||
}
|
||||
}
|
||||
console.log('final text: ', text)
|
||||
// console.log('final text: ', text)
|
||||
return text
|
||||
}
|
||||
module.exports = {
|
||||
|
|
|
|||