Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0846013378 | ||
|
|
c141ba405f | ||
|
|
0320f13a9f | ||
|
|
8adc34be4d | ||
|
|
cb6810d3c1 | ||
|
|
d384f64abf | ||
|
|
ef7035f8ee | ||
|
|
bfcadde5c3 | ||
|
|
496ff41782 | ||
|
|
46f0be5484 | ||
|
|
f3db0131c1 | ||
|
|
83a8d47f51 | ||
|
|
fee0222910 | ||
|
|
1ed7b5511f | ||
|
|
b8f7c31537 | ||
|
|
164791c257 | ||
|
|
8f5e599928 | ||
|
|
7a7aaeb84d | ||
|
|
e2136ab2fc | ||
|
|
c75cb21946 | ||
|
|
bf95218c91 |
@@ -3,28 +3,34 @@
|
||||
> 适配了最新版 comfyui 的 py3.11 ,torch 2.3.1+cu121
|
||||
> [Mixlab nodes discord](https://discord.gg/cXs9vZSqeK)
|
||||
|
||||
商务合作请联系 389570357@qq.com
|
||||
For business cooperation, please contact email 389570357@qq.com
|
||||
|
||||
##### `最新`:
|
||||
|
||||
- 增加p5.js作为输入节点
|
||||
[workflow](./workflow/p5workflow.json)
|
||||
- 增加 MiniCPM-V 2.6 int4
|
||||
|
||||
- App模式增加batch prompt,批量提示词,可以把动态提示词批量组成后运行
|
||||
This is the int4 quantized version of MiniCPM-V 2.6.
|
||||
Running with int4 version would use lower GPU memory (about 7GB).
|
||||
|
||||
- 移动端适配、修改 app 模式的 Mask 编辑器
|
||||
|
||||
- 增加 p5.js 作为输入节点
|
||||
[workflow](./workflow/p5workflow.json)
|
||||
[workflow2](./workflow/p5-video-workflow.json)
|
||||
|
||||
- App 模式增加 batch prompt,批量提示词,可以把动态提示词批量组成后运行
|
||||
|
||||

|
||||
|
||||
- 增加 API Key Input 节点,用于管理LLM的Key,同时优化LLM相关节点,为后续agent模式做准备
|
||||
- 增加 API Key Input 节点,用于管理 LLM 的 Key,同时优化 LLM 相关节点,为后续 agent 模式做准备
|
||||
|
||||
- 增加 SiliconflowLLM,可以使用由Siliconflow提供的免费LLM
|
||||
|
||||
- 增加 Edit Mask,方便在生成的时候手动绘制 mask [workflow](./workflow/edit-mask-workflow.json)
|
||||
|
||||
- LaMaInpainting 调整为手动安装
|
||||
- 增加 SiliconflowLLM,可以使用由 Siliconflow 提供的免费 LLM
|
||||
|
||||
<!-- - ChatGPT 节点支持 Local LLM(llama.cpp),Phi3、llama3 都可以直接一个节点运行了。模型下载后,放置到 `models/llamafile/` -->
|
||||
|
||||
<!-- - 右键菜单支持 text-to-text,方便对 prompt 词补全 -->
|
||||
<!--
|
||||
<!--
|
||||
强烈推荐:
|
||||
[Phi-3-mini-4k-instruct-function-calling-GGUF](https://huggingface.co/nold/Phi-3-mini-4k-instruct-function-calling-GGUF)
|
||||
|
||||
@@ -35,7 +41,6 @@
|
||||

|
||||
 -->
|
||||
|
||||
|
||||
#### `相关插件推荐`
|
||||
|
||||
[comfyui-liveportrait](https://github.com/shadowcz007/comfyui-liveportrait)
|
||||
@@ -59,8 +64,8 @@
|
||||
- 发布为 app 的 workflow,可以在右键里再次编辑了
|
||||
- web app 可以设置分类,在 comfyui 右键菜单可以编辑更新 web app
|
||||
- 支持动态提示
|
||||
- 支持把输出显示到comfyui背景(TouchDesigner 风格)
|
||||
- 如果转为web app打开是空白的,注意检查下插件目录的名字需要是:comfyui-mixlab-nodes(如果是zip包下载会多了个-main的后缀,需要去掉)
|
||||
- 支持把输出显示到 comfyui 背景(TouchDesigner 风格)
|
||||
- 如果转为 web app 打开是空白的,注意检查下插件目录的名字需要是:comfyui-mixlab-nodes(如果是 zip 包下载会多了个-main 的后缀,需要去掉)
|
||||
|
||||

|
||||
|
||||
@@ -189,7 +194,6 @@ pip install llama-cpp-python \
|
||||
|
||||
> The composite images node overlays a foreground image onto a background image at specified positions and scales, with optional blending modes and masking capabilities. position : 'overall',"center_center","left_bottom","center_bottom","right_bottom","left_top","center_top","right_top"
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
@@ -223,9 +227,16 @@ pip install llama-cpp-python \
|
||||
|
||||
#### TextImage
|
||||
|
||||
> [下载字体](https://drxie.github.io/OSFCC/)放到 ```custom_nodes/comfyui-mixlab-nodes/assets/fonts```
|
||||
> [下载字体](https://drxie.github.io/OSFCC/)放到 `custom_nodes/comfyui-mixlab-nodes/assets/fonts`
|
||||
|
||||
#### MiniCPM-VQA Simple
|
||||
|
||||
This is the int4 quantized version of MiniCPM-V 2.6.
|
||||
Running with int4 version would use lower GPU memory (about 7GB).
|
||||
|
||||
[模型](https://huggingface.co/openbmb/MiniCPM-V-2_6-int4)
|
||||
|
||||

|
||||
|
||||
### Style
|
||||
|
||||
@@ -249,6 +260,8 @@ pip install llama-cpp-python \
|
||||
|
||||
### Other Nodes
|
||||
|
||||
- 增加 Edit Mask,方便在生成的时候手动绘制 mask [workflow](./workflow/edit-mask-workflow.json)
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -266,11 +279,11 @@ Add edges to an image.
|
||||
|
||||
> LaMaInpainting(需要手动安装)
|
||||
|
||||
* simple-lama-inpainting 里的pillow造成冲突,暂时从依赖里移除,如果有安装 simple-lama-inpainting ,节点会自动添加,没有,则不会自动添加。
|
||||
- simple-lama-inpainting 里的 pillow 造成冲突,暂时从依赖里移除,如果有安装 simple-lama-inpainting ,节点会自动添加,没有,则不会自动添加。
|
||||
|
||||
from [simple-lama-inpainting](https://github.com/enesmsahin/simple-lama-inpainting)
|
||||
|
||||
* [问题汇总](https://github.com/shadowcz007/comfyui-mixlab-nodes/issues/294)
|
||||
- [问题汇总](https://github.com/shadowcz007/comfyui-mixlab-nodes/issues/294)
|
||||
|
||||
> rembgNode
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ _URL_=None
|
||||
# except:
|
||||
# print("##nodes.ChatGPT ImportError")
|
||||
|
||||
from .nodes.ChatGPT import openai_client
|
||||
|
||||
from .nodes.RembgNode import get_rembg_models,U2NET_HOME,run_briarmbg,run_rembg
|
||||
|
||||
@@ -584,17 +585,101 @@ async def mixlab_hander(request):
|
||||
print(e)
|
||||
return web.json_response(data)
|
||||
|
||||
# llm的api key,使用硅基流动
|
||||
@routes.post('/mixlab/llm_api_key')
|
||||
async def mixlab_llm_api_key_handler(request):
|
||||
data = await request.json()
|
||||
api_key = data.get('key')
|
||||
|
||||
app_folder = os.path.join(current_path, "app")
|
||||
key_file_path = os.path.join(app_folder, "llm_api_key.txt")
|
||||
|
||||
if api_key:
|
||||
if not os.path.exists(app_folder):
|
||||
os.makedirs(app_folder)
|
||||
try:
|
||||
with open(key_file_path, 'w') as f:
|
||||
f.write(api_key)
|
||||
return web.json_response({'message': 'API key saved successfully'})
|
||||
except Exception as e:
|
||||
return web.json_response({'error': str(e)}, status=500)
|
||||
else:
|
||||
if os.path.exists(key_file_path):
|
||||
try:
|
||||
with open(key_file_path, 'r') as f:
|
||||
saved_api_key = f.read().strip()
|
||||
return web.json_response({'key': saved_api_key})
|
||||
except Exception as e:
|
||||
return web.json_response({'error': str(e)}, status=500)
|
||||
else:
|
||||
return web.json_response({'error': 'No API key provided and no key found in local storage'}, status=400)
|
||||
|
||||
|
||||
@routes.post('/chat/completions')
|
||||
async def chat_completions(request):
|
||||
data = await request.json()
|
||||
messages = data.get('messages')
|
||||
key=data.get('key')
|
||||
if not messages:
|
||||
return web.json_response({"error": "No messages provided"}, status=400)
|
||||
|
||||
async def generate():
|
||||
try:
|
||||
client=openai_client(key,"https://api.siliconflow.cn/v1")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="01-ai/Yi-1.5-9B-Chat-16K",
|
||||
messages=messages,
|
||||
stream=True
|
||||
)
|
||||
|
||||
for chunk in response:
|
||||
if hasattr(chunk.choices[0].delta, 'content'):
|
||||
content = chunk.choices[0].delta.content
|
||||
if content is not None:
|
||||
yield content.encode('utf-8') + b"\r\n"
|
||||
|
||||
except Exception as e:
|
||||
yield f"Error: {str(e)}".encode('utf-8') + b"\r\n"
|
||||
|
||||
return web.Response(body=generate(), content_type='text/event-stream')
|
||||
|
||||
|
||||
@routes.get('/mixlab/app')
|
||||
async def mixlab_app_handler(request):
|
||||
html_file = os.path.join(current_path, "web/index.html")
|
||||
html_file = os.path.join(current_path, "webApp/index.html")
|
||||
if os.path.exists(html_file):
|
||||
with open(html_file, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
html_data = f.read()
|
||||
return web.Response(text=html_data, content_type='text/html')
|
||||
else:
|
||||
return web.Response(text="HTML file not found", status=404)
|
||||
|
||||
# web app模式独立
|
||||
@routes.get('/mixlab/app/{filename:.*}')
|
||||
async def static_file_handler(request):
|
||||
filename = request.match_info['filename']
|
||||
file_path = os.path.join(current_path, "webApp", filename)
|
||||
print(file_path)
|
||||
|
||||
if os.path.exists(file_path) and os.path.isfile(file_path):
|
||||
if filename.endswith('.js'):
|
||||
content_type = 'application/javascript'
|
||||
elif filename.endswith('.css'):
|
||||
content_type = 'text/css'
|
||||
elif filename.endswith('.html'):
|
||||
content_type = 'text/html'
|
||||
elif filename.endswith('.svg'):
|
||||
content_type = 'image/svg+xml'
|
||||
else:
|
||||
content_type = 'application/octet-stream'
|
||||
|
||||
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
file_data = f.read()
|
||||
return web.Response(text=file_data, content_type=content_type)
|
||||
else:
|
||||
return web.Response(text="File not found", status=404)
|
||||
|
||||
|
||||
@routes.post('/mixlab/workflow')
|
||||
async def mixlab_workflow_hander(request):
|
||||
@@ -795,7 +880,10 @@ async def mixlab_post_prompt(request):
|
||||
# 种子
|
||||
seed=json_data['seed'] if "seed" in json_data else {}
|
||||
|
||||
apps=get_my_workflow_for_app(json_data['filename'],json_data['category'],False)
|
||||
try:
|
||||
apps=json_data['apps']
|
||||
except:
|
||||
apps=get_my_workflow_for_app(json_data['filename'],json_data['category'],False)
|
||||
|
||||
prompt=json_data['prompt'] if 'prompt' in json_data else None
|
||||
|
||||
@@ -1309,5 +1397,16 @@ try:
|
||||
except Exception as e:
|
||||
logging.info('TripoSR.available False' )
|
||||
|
||||
from .nodes.MiniCPMNode import MiniCPM_VQA_Simple
|
||||
try:
|
||||
|
||||
logging.info('MiniCPMNode.available')
|
||||
# logging.info( folder_paths.get_temp_directory())
|
||||
NODE_CLASS_MAPPINGS['MiniCPM_VQA_Simple']=MiniCPM_VQA_Simple
|
||||
NODE_DISPLAY_NAME_MAPPINGS["MiniCPM_VQA_Simple"]= "MiniCPM VQA Simple"
|
||||
|
||||
except Exception as e:
|
||||
logging.info('MiniCPMNode.available False' )
|
||||
|
||||
|
||||
logging.info('\033[93m -------------- \033[0m')
|
||||
|
||||
|
After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 2.2 MiB |
@@ -0,0 +1,127 @@
|
||||
# Referenced some code:https://github.com/IuvenisSapiens/ComfyUI_MiniCPM-V-2_6-int4
|
||||
|
||||
import os
|
||||
import torch
|
||||
import folder_paths
|
||||
from transformers import AutoTokenizer, AutoModel
|
||||
from torchvision.transforms.v2 import ToPILImage
|
||||
from decord import VideoReader, cpu # pip install decord
|
||||
from PIL import Image
|
||||
|
||||
def get_model_path(n=""):
|
||||
try:
|
||||
return folder_paths.get_folder_paths(n)[0]
|
||||
except:
|
||||
return os.path.join(folder_paths.models_dir, n)
|
||||
|
||||
|
||||
class MiniCPM_VQA_Simple:
|
||||
def __init__(self):
|
||||
self.model_checkpoint = None
|
||||
self.tokenizer = None
|
||||
self.model = None
|
||||
self.device = (
|
||||
torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
|
||||
)
|
||||
self.bf16_support = (
|
||||
torch.cuda.is_available()
|
||||
and torch.cuda.get_device_capability(self.device)[0] >= 8
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"images": ("IMAGE",),
|
||||
"text": ("STRING", {"default": "", "multiline": True}),
|
||||
"seed": ("INT", {"default": -1}), # add seed parameter, default is -1
|
||||
"temperature": (
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0.7,
|
||||
},
|
||||
),
|
||||
"keep_model_loaded": ("BOOLEAN", {"default": False}),
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
FUNCTION = "inference"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
def inference(
|
||||
self,
|
||||
images,
|
||||
text,
|
||||
seed, # add seed parameter, default is -1
|
||||
temperature,
|
||||
keep_model_loaded,
|
||||
):
|
||||
if seed != -1:
|
||||
torch.manual_seed(seed)
|
||||
model_id = "openbmb/MiniCPM-V-2_6-int4"
|
||||
|
||||
self.model_checkpoint = os.path.join( get_model_path("prompt_generator"), os.path.basename(model_id))
|
||||
|
||||
if not os.path.exists(self.model_checkpoint):
|
||||
from huggingface_hub import snapshot_download
|
||||
|
||||
snapshot_download(
|
||||
repo_id=model_id,
|
||||
local_dir=self.model_checkpoint,
|
||||
local_dir_use_symlinks=False,
|
||||
endpoint='https://hf-mirror.com'
|
||||
)
|
||||
|
||||
if self.tokenizer is None:
|
||||
self.tokenizer = AutoTokenizer.from_pretrained(
|
||||
self.model_checkpoint,
|
||||
trust_remote_code=True,
|
||||
low_cpu_mem_usage=True,
|
||||
)
|
||||
|
||||
if self.model is None:
|
||||
self.model = AutoModel.from_pretrained(
|
||||
self.model_checkpoint,
|
||||
trust_remote_code=True,
|
||||
low_cpu_mem_usage=True,
|
||||
attn_implementation="sdpa",
|
||||
torch_dtype=torch.bfloat16 if self.bf16_support else torch.float16,
|
||||
)
|
||||
|
||||
with torch.no_grad():
|
||||
images = images.permute([0, 3, 1, 2])
|
||||
images = [ToPILImage()(img).convert("RGB") for img in images]
|
||||
msgs = [{"role": "user", "content": images + [text]}]
|
||||
|
||||
params = {"use_image_id": False, }
|
||||
|
||||
# offload model to CPU
|
||||
# self.model = self.model.to(torch.device("cpu"))
|
||||
# self.model.eval()
|
||||
|
||||
result = self.model.chat(
|
||||
image=None,
|
||||
msgs=msgs,
|
||||
tokenizer=self.tokenizer,
|
||||
sampling=True,
|
||||
# top_k=top_k,
|
||||
# top_p=top_p,
|
||||
temperature=temperature,
|
||||
# repetition_penalty=repetition_penalty,
|
||||
# max_new_tokens=max_new_tokens,
|
||||
**params,
|
||||
)
|
||||
# offload model to GPU
|
||||
# self.model = self.model.to(torch.device("cpu"))
|
||||
# self.model.eval()
|
||||
if not keep_model_loaded:
|
||||
del self.tokenizer # release tokenizer memory
|
||||
del self.model # release model memory
|
||||
self.tokenizer = None # set tokenizer to None
|
||||
self.model = None # set model to None
|
||||
torch.cuda.empty_cache() # release GPU memory
|
||||
torch.cuda.ipc_collect()
|
||||
|
||||
return (result,)
|
||||
@@ -18,7 +18,13 @@ import json
|
||||
# req = request.Request("http://127.0.0.1:8188/prompt", data=data)
|
||||
# request.urlopen(req)
|
||||
|
||||
embeddings_path=os.path.join(folder_paths.models_dir, "embeddings")
|
||||
def get_model_path(n=""):
|
||||
try:
|
||||
return folder_paths.get_folder_paths(n)[0]
|
||||
except:
|
||||
return os.path.join(folder_paths.models_dir, n)
|
||||
|
||||
embeddings_path=get_model_path("embeddings")
|
||||
|
||||
def get_files_with_extension(directory, extension):
|
||||
|
||||
|
||||
@@ -280,7 +280,7 @@ class ChinesePrompt:
|
||||
},
|
||||
|
||||
"optional":{
|
||||
"seed":("INT", {"default": 100, "min": 100, "max": 1000000}),
|
||||
"seed":("INT", {"default": 100, "min": 100, "max": 0xffffffffffffffff}),
|
||||
|
||||
},
|
||||
|
||||
@@ -331,13 +331,15 @@ class ChinesePrompt:
|
||||
|
||||
for t in texts:
|
||||
if t:
|
||||
# translated_text = translated_word = translate(zh_en_tokenizer,zh_en_model,str(t))
|
||||
parser = Lark(grammar, start="start", parser="lalr", transformer=ChinesePromptTranslate())
|
||||
# print('t',t)
|
||||
result = parser.parse(t).children
|
||||
# print('en_result',result)
|
||||
# en_text=translate(zh_en_tokenizer,zh_en_model,text_without_syntax)
|
||||
en_texts.append(result[0])
|
||||
try:
|
||||
result = parser.parse(t).children
|
||||
en_texts.append(result[0])
|
||||
except:
|
||||
print(f"Error parsing '{t}'")
|
||||
t = translate(str(t))
|
||||
en_texts.append(t)
|
||||
|
||||
|
||||
zh_en_model.to('cpu')
|
||||
print("test en_text",en_texts)
|
||||
@@ -384,7 +386,7 @@ class PromptGenerate:
|
||||
|
||||
"optional":{
|
||||
"multiple": (["off","on"],),
|
||||
"seed":("INT", {"default": 100, "min": 100, "max": 1000000}),
|
||||
"seed":("INT", {"default": 100, "min": 100, "max": 0xffffffffffffffff}),
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-mixlab-nodes"
|
||||
description = "3D, ScreenShareNode & FloatingVideoNode, SpeechRecognition & SpeechSynthesis, GPT, LoadImagesFromLocal, Layers, Other Nodes, ..."
|
||||
version = "0.36.0"
|
||||
version = "0.39.0"
|
||||
license = "MIT"
|
||||
dependencies = ["numpy", "pyOpenSSL", "watchdog", "opencv-python-headless", "matplotlib", "openai", "simple-lama-inpainting", "clip-interrogator==0.6.0", "transformers>=4.36.0", "lark-parser", "imageio-ffmpeg", "rembg[gpu]", "omegaconf==2.3.0", "Pillow>=9.5.0", "einops==0.7.0", "trimesh>=4.0.5", "huggingface-hub", "scikit-image"]
|
||||
|
||||
|
||||
@@ -18,4 +18,8 @@ huggingface-hub
|
||||
scikit-image
|
||||
torchaudio
|
||||
soundfile>=0.12.1
|
||||
json-repair
|
||||
json-repair
|
||||
|
||||
decord
|
||||
bitsandbytes
|
||||
accelerate
|
||||
@@ -2,6 +2,8 @@ import { app } from '../../../scripts/app.js'
|
||||
import { api } from '../../../scripts/api.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
|
||||
import { loadExternalScript } from './common.js'
|
||||
|
||||
const getLocalData = key => {
|
||||
let data = {}
|
||||
try {
|
||||
@@ -99,7 +101,10 @@ function get_position_style (ctx, widget_width, y, node_height) {
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
left:
|
||||
document.querySelector('.comfy-menu').style.display === 'none'
|
||||
? `60px`
|
||||
: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
@@ -266,6 +271,11 @@ app.registerExtension({
|
||||
if (nodeType.comfyClass == '3DImage') {
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = async function () {
|
||||
await loadExternalScript(
|
||||
'/mixlab/app/lib/model-viewer.min.js',
|
||||
'module'
|
||||
)
|
||||
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
|
||||
const uploadWidget = this.widgets.filter(w => w.name == 'upload')[0]
|
||||
|
||||
@@ -4,28 +4,11 @@ import { api } from '../../../scripts/api.js'
|
||||
|
||||
import { td_bg } from './td_background.js'
|
||||
// console.log('td_bg', td_bg)
|
||||
import { getUrl, base64Df, get_position_style, getObjectInfo } from './common.js'
|
||||
|
||||
//本机安装的插件节点全集
|
||||
window._nodesAll = null
|
||||
|
||||
//获取当前系统的插件,节点清单
|
||||
function getObjectInfo () {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let url = getUrl()
|
||||
|
||||
try {
|
||||
const response = await fetch(`${url}/object_info`)
|
||||
const data = await response.json()
|
||||
resolve(data)
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const base64Df =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAALZJREFUKFOFkLERwjAQBPdbgBkInECGaMLUQDsE0AkRVRAYWqAByxldPPOWHwnw4OBGye1p50UDSoA+W2ABLPN7i+C5dyC6R/uiAUXRQCs0bXoNIu4QPQzAxDKxHoALOrZcqtiyR/T6CXw7+3IGHhkYcy6BOR2izwT8LptG8rbMiCRAUb+CQ6WzQVb0SNOi5Z2/nX35DRyb/ENazhpWKoGwrpD6nICp5c2qogc4of+c7QcrhgF4Aa/aoAFHiL+RAAAAAElFTkSuQmCC'
|
||||
|
||||
const parseImageToBase64 = url => {
|
||||
return new Promise((res, rej) => {
|
||||
fetch(url)
|
||||
@@ -45,39 +28,6 @@ const parseImageToBase64 = url => {
|
||||
})
|
||||
}
|
||||
|
||||
function get_position_style (ctx, widget_width, y, node_height) {
|
||||
const MARGIN = 12 // the margin around the html element
|
||||
|
||||
/* Create a transform that deals with all the scrolling and zooming */
|
||||
const elRect = ctx.canvas.getBoundingClientRect()
|
||||
const transform = new DOMMatrix()
|
||||
.scaleSelf(
|
||||
elRect.width / ctx.canvas.width,
|
||||
elRect.height / ctx.canvas.height
|
||||
)
|
||||
.multiplySelf(ctx.getTransform())
|
||||
.translateSelf(MARGIN, MARGIN + y)
|
||||
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
maxWidth: `${widget_width - MARGIN * 2}px`,
|
||||
// maxHeight: `${node_height - MARGIN * 2}px`, // we're assuming we have the whole height of the node
|
||||
width: `${widget_width - MARGIN * 2}px`,
|
||||
// height: `${node_height * 0.3 - MARGIN * 2}px`,
|
||||
// background: '#EEEEEE',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
// alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
zIndex: 9999999
|
||||
}
|
||||
}
|
||||
|
||||
async function drawImageToCanvas (imageUrl, sFactor = 320) {
|
||||
var canvas = document.createElement('canvas')
|
||||
var ctx = canvas.getContext('2d')
|
||||
@@ -257,9 +207,9 @@ async function extractInputAndOutputData (
|
||||
node.type === 'KSampler' ||
|
||||
node.type == 'SamplerCustom' ||
|
||||
node.type === 'ChinesePrompt_Mix' ||
|
||||
node.type === 'Seed_'||
|
||||
node.type==='SiliconflowLLM'||
|
||||
node.type==='ChatGPTOpenAI'
|
||||
node.type === 'Seed_' ||
|
||||
node.type === 'SiliconflowLLM' ||
|
||||
node.type === 'ChatGPTOpenAI'
|
||||
) {
|
||||
// seed 的类型收集
|
||||
try {
|
||||
@@ -279,13 +229,6 @@ async function extractInputAndOutputData (
|
||||
return { input, output, seed, seedTitle }
|
||||
}
|
||||
|
||||
function getUrl () {
|
||||
let api_host = `${window.location.hostname}:${window.location.port}`
|
||||
let api_base = ''
|
||||
let url = `${window.location.protocol}//${api_host}${api_base}`
|
||||
return url
|
||||
}
|
||||
|
||||
const getLocalData = key => {
|
||||
let data = {}
|
||||
try {
|
||||
@@ -473,15 +416,15 @@ app.registerExtension({
|
||||
type: 'div',
|
||||
name: 'AppInfoRun',
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(
|
||||
this.div.style,
|
||||
{...get_position_style(
|
||||
Object.assign(this.div.style, {
|
||||
...get_position_style(
|
||||
ctx,
|
||||
widget_width,
|
||||
node.size[1] - widget_height,
|
||||
node.size[1]
|
||||
),zIndex:1}
|
||||
)
|
||||
),
|
||||
zIndex: 1
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,7 +654,6 @@ app.registerExtension({
|
||||
this.serialize_widgets = true //需要保存参数
|
||||
|
||||
window._mixlab_app_json = null
|
||||
|
||||
}
|
||||
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
|
||||
@@ -19,7 +19,10 @@ function get_position_style (ctx, widget_width, y, node_height) {
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
left:
|
||||
document.querySelector('.comfy-menu').style.display === 'none'
|
||||
? `60px`
|
||||
: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getUrl } from './common.js'
|
||||
|
||||
async function* completion (url, messages, controller) {
|
||||
let data = {
|
||||
model: 'gpt-3.5-turbo-16k',
|
||||
@@ -91,16 +93,81 @@ async function* completion (url, messages, controller) {
|
||||
return content
|
||||
// return (await response.json()).content
|
||||
}
|
||||
|
||||
export async function completion_ (url, messages, controller, callback) {
|
||||
let request = await completion(url, messages, controller)
|
||||
export async function completion_ (apiKey, url, messages, controller, callback) {
|
||||
let request = await chatCompletion(apiKey, url, messages, controller)
|
||||
for await (const chunk of request) {
|
||||
let content = chunk.data.choices[0].delta.content || ''
|
||||
if (chunk.data.choices[0].role == 'assistant') {
|
||||
//开始
|
||||
content = ''
|
||||
}
|
||||
|
||||
if (callback) callback(content)
|
||||
if (callback) callback(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
export async function* chatCompletion (apiKey, url, messages, controller) {
|
||||
url = `${getUrl()}/chat/completions`
|
||||
|
||||
const requestBody = {
|
||||
model: '01-ai/Yi-1.5-9B-Chat-16K',
|
||||
messages: messages,
|
||||
stream: true,
|
||||
key: apiKey
|
||||
}
|
||||
|
||||
let response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${apiKey}`
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
mode: 'cors', // This is to ensure the request is made with CORS
|
||||
signal: controller.signal
|
||||
})
|
||||
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
let content = ''
|
||||
let leftover = '' // Buffer for partially read lines
|
||||
|
||||
try {
|
||||
let cont = true
|
||||
while (cont) {
|
||||
let result = await reader.read()
|
||||
if (result.done) {
|
||||
break
|
||||
}
|
||||
|
||||
// Add any leftover data to the current chunk of data
|
||||
const text = leftover + decoder.decode(result.value)
|
||||
|
||||
// Check if the last character is a line break
|
||||
const endsWithLineBreak = text.endsWith('\r\n')
|
||||
|
||||
// Split the text into lines
|
||||
let lines = text.split('\r\n')
|
||||
|
||||
// If the text doesn't end with a line break, then the last line is incomplete
|
||||
// Store it in leftover to be added to the next chunk of data
|
||||
if (!endsWithLineBreak) {
|
||||
leftover = lines.pop()
|
||||
} else {
|
||||
leftover = '' // Reset leftover if we have a line break at the end
|
||||
}
|
||||
|
||||
for (const line of lines) {
|
||||
if (line) {
|
||||
content += line
|
||||
yield line // Yield the trimmed line
|
||||
} else {
|
||||
cont = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('chat error: ', e)
|
||||
throw e
|
||||
} finally {
|
||||
controller.abort()
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { app } from '../../../scripts/app.js'
|
||||
const repoOwner = 'shadowcz007' // 替换为仓库的所有者
|
||||
const repoName = 'comfyui-mixlab-nodes' // 替换为仓库的名称
|
||||
|
||||
const version = 'v0.36.0'
|
||||
const version = 'v0.39.0'
|
||||
|
||||
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
|
||||
.then(response => response.json())
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
export const base64Df =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAALZJREFUKFOFkLERwjAQBPdbgBkInECGaMLUQDsE0AkRVRAYWqAByxldPPOWHwnw4OBGye1p50UDSoA+W2ABLPN7i+C5dyC6R/uiAUXRQCs0bXoNIu4QPQzAxDKxHoALOrZcqtiyR/T6CXw7+3IGHhkYcy6BOR2izwT8LptG8rbMiCRAUb+CQ6WzQVb0SNOi5Z2/nX35DRyb/ENazhpWKoGwrpD6nICp5c2qogc4of+c7QcrhgF4Aa/aoAFHiL+RAAAAAElFTkSuQmCC'
|
||||
|
||||
export function getUrl () {
|
||||
let api_host = `${window.location.hostname}:${window.location.port}`
|
||||
let api_base = ''
|
||||
let url = `${window.location.protocol}//${api_host}${api_base}`
|
||||
return url
|
||||
}
|
||||
|
||||
// 更新或者获取key
|
||||
export const updateLLMAPIKey = async key => {
|
||||
try {
|
||||
const res = await fetch(`${getUrl()}/mixlab/llm_api_key`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
key: key || null
|
||||
})
|
||||
})
|
||||
|
||||
const data = await res.json()
|
||||
|
||||
if (!res.ok) {
|
||||
console.error('Error:', data.error)
|
||||
return
|
||||
}
|
||||
|
||||
if (key) {
|
||||
console.log('API key saved successfully:', data.message)
|
||||
return key
|
||||
} else {
|
||||
console.log('Retrieved API key:', data.key)
|
||||
return data.key
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Request failed:', error)
|
||||
}
|
||||
}
|
||||
|
||||
//获取当前系统的插件,节点清单
|
||||
export function getObjectInfo () {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let url = getUrl()
|
||||
|
||||
try {
|
||||
const response = await fetch(`${url}/object_info`)
|
||||
const data = await response.json()
|
||||
resolve(data)
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function get_position_style (
|
||||
ctx,
|
||||
widget_width,
|
||||
y,
|
||||
node_height,
|
||||
left = 44
|
||||
) {
|
||||
const MARGIN = 0 // the margin around the html element
|
||||
|
||||
/* Create a transform that deals with all the scrolling and zooming */
|
||||
const elRect = ctx.canvas.getBoundingClientRect()
|
||||
|
||||
const scaleX = elRect.width / ctx.canvas.width
|
||||
const scaleY = elRect.height / ctx.canvas.height
|
||||
|
||||
const transform = new DOMMatrix()
|
||||
.scaleSelf(scaleX, scaleY)
|
||||
.multiplySelf(ctx.getTransform())
|
||||
.translateSelf(MARGIN, MARGIN + y)
|
||||
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left:
|
||||
document.querySelector('.comfy-menu').style.display === 'none'
|
||||
? `${left}px`
|
||||
: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
maxWidth: `${widget_width - MARGIN * 2}px`,
|
||||
// maxHeight: `${node_height - MARGIN * 2}px`, // we're assuming we have the whole height of the node
|
||||
width: `${widget_width - MARGIN * 2}px`,
|
||||
height: `${node_height * 0.3 - MARGIN * 2}px`,
|
||||
// background: '#EEEEEE',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
// alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
zIndex: 99
|
||||
}
|
||||
}
|
||||
|
||||
export function loadExternalScript (url, type) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const existingScript = document.querySelector(`script[src="${url}"]`)
|
||||
if (existingScript) {
|
||||
existingScript.onload = () => {
|
||||
resolve()
|
||||
}
|
||||
existingScript.onerror = reject
|
||||
return
|
||||
}
|
||||
|
||||
const script = document.createElement('script')
|
||||
script.src = url
|
||||
if (type) script.type = type // Add this line to load the script as an ES module
|
||||
script.onload = () => {
|
||||
resolve()
|
||||
}
|
||||
script.onerror = reject
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
}
|
||||
|
||||
export async function getQueue () {
|
||||
try {
|
||||
const res = await fetch(`${getUrl()}/queue`)
|
||||
const data = await res.json()
|
||||
// console.log(data.queue_running,data.queue_pending)
|
||||
return {
|
||||
// Running action uses a different endpoint for cancelling
|
||||
Running: data.queue_running.length,
|
||||
Pending: data.queue_pending.length
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return { Running: 0, Pending: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
export async function interrupt () {
|
||||
const resp = await fetch(`${getUrl()}/interrupt`, {
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
|
||||
export async function sleep (t = 200) {
|
||||
return new Promise((res, rej) => {
|
||||
setTimeout(() => {
|
||||
res(true)
|
||||
}, t)
|
||||
})
|
||||
}
|
||||
|
||||
export function createImage (url) {
|
||||
let im = new Image()
|
||||
return new Promise((res, rej) => {
|
||||
im.onload = () => res(im)
|
||||
im.src = url
|
||||
})
|
||||
}
|
||||
|
||||
export const getLocalData = key => {
|
||||
let data = {}
|
||||
try {
|
||||
data = JSON.parse(localStorage.getItem(key)) || {}
|
||||
} catch (error) {
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
export const saveLocalData = (key, id, val) => {
|
||||
let data = getLocalData(key)
|
||||
data[id] = val
|
||||
localStorage.setItem(key, JSON.stringify(data))
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import { api } from '../../../scripts/api.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
import { applyTextReplacements } from '../../../scripts/utils.js'
|
||||
|
||||
import { loadExternalScript, get_position_style } from './common.js'
|
||||
|
||||
function loadImageToCanvas (base64Image) {
|
||||
var img = new Image()
|
||||
var canvas = document.createElement('canvas')
|
||||
@@ -88,37 +90,40 @@ function getContentTypeFromBase64 (base64Data) {
|
||||
// const blob = base64ToBlob(base64Data, contentType);
|
||||
// console.log(blob);
|
||||
|
||||
function get_position_style (ctx, widget_width, y, node_height) {
|
||||
const MARGIN = 4 // the margin around the html element
|
||||
// function get_position_style (ctx, widget_width, y, node_height) {
|
||||
// const MARGIN = 4 // the margin around the html element
|
||||
|
||||
/* Create a transform that deals with all the scrolling and zooming */
|
||||
const elRect = ctx.canvas.getBoundingClientRect()
|
||||
const transform = new DOMMatrix()
|
||||
.scaleSelf(
|
||||
elRect.width / ctx.canvas.width,
|
||||
elRect.height / ctx.canvas.height
|
||||
)
|
||||
.multiplySelf(ctx.getTransform())
|
||||
.translateSelf(MARGIN, MARGIN + y)
|
||||
// /* Create a transform that deals with all the scrolling and zooming */
|
||||
// const elRect = ctx.canvas.getBoundingClientRect()
|
||||
// const transform = new DOMMatrix()
|
||||
// .scaleSelf(
|
||||
// elRect.width / ctx.canvas.width,
|
||||
// elRect.height / ctx.canvas.height
|
||||
// )
|
||||
// .multiplySelf(ctx.getTransform())
|
||||
// .translateSelf(MARGIN, MARGIN + y)
|
||||
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
maxWidth: `${widget_width - MARGIN * 2}px`,
|
||||
// maxHeight: `${node_height - MARGIN * 2}px`, // we're assuming we have the whole height of the node
|
||||
width: `${widget_width - MARGIN * 2}px`,
|
||||
// height: `${node_height * 0.3 - MARGIN * 2}px`,
|
||||
// background: '#EEEEEE',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
// alignItems: 'center',
|
||||
justifyContent: 'space-around'
|
||||
}
|
||||
}
|
||||
// return {
|
||||
// transformOrigin: '0 0',
|
||||
// transform: transform,
|
||||
// left:
|
||||
// document.querySelector('.comfy-menu').style.display === 'none'
|
||||
// ? `60px`
|
||||
// : `0`,
|
||||
// top: `0`,
|
||||
// cursor: 'pointer',
|
||||
// position: 'absolute',
|
||||
// maxWidth: `${widget_width - MARGIN * 2}px`,
|
||||
// // maxHeight: `${node_height - MARGIN * 2}px`, // we're assuming we have the whole height of the node
|
||||
// width: `${widget_width - MARGIN * 2}px`,
|
||||
// // height: `${node_height * 0.3 - MARGIN * 2}px`,
|
||||
// // background: '#EEEEEE',
|
||||
// display: 'flex',
|
||||
// flexDirection: 'column',
|
||||
// // alignItems: 'center',
|
||||
// justifyContent: 'space-around'
|
||||
// }
|
||||
// }
|
||||
|
||||
const getLocalData = key => {
|
||||
let data = {}
|
||||
@@ -344,7 +349,7 @@ app.registerExtension({
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(
|
||||
this.div.style,
|
||||
get_position_style(ctx, widget_width, 44, node.size[1])
|
||||
get_position_style(ctx, widget_width, 44, node.size[1], 36)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -530,7 +535,7 @@ app.registerExtension({
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(
|
||||
this.div.style,
|
||||
get_position_style(ctx, widget_width, y, node.size[1])
|
||||
get_position_style(ctx, widget_width, y, node.size[1], 36)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -681,7 +686,7 @@ const addBase64ToWidgetForLoadImagesToBatch = (
|
||||
imagesWidget,
|
||||
imagesDiv
|
||||
) => {
|
||||
if(!imagesWidget.value.base64) imagesWidget.value.base64=[]
|
||||
if (!imagesWidget.value.base64) imagesWidget.value.base64 = []
|
||||
imagesWidget.value.base64.push(base64)
|
||||
let im = createInputImageForBatch(base64, imagesWidget)
|
||||
imagesDiv.appendChild(im)
|
||||
@@ -730,7 +735,7 @@ app.registerExtension({
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(
|
||||
this.div.style,
|
||||
get_position_style(ctx, widget_width, 44, node.size[1])
|
||||
get_position_style(ctx, widget_width, 44, node.size[1], 44)
|
||||
)
|
||||
},
|
||||
serialize: false
|
||||
@@ -882,9 +887,11 @@ app.registerExtension({
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.output.ComparingTwoFrames_',
|
||||
init () {
|
||||
loadExternalScript('/mixlab/app/lib/juxtapose.min.js')
|
||||
|
||||
$el('link', {
|
||||
rel: 'stylesheet',
|
||||
href: '/extensions/comfyui-mixlab-nodes/lib/juxtapose.css',
|
||||
href: '/mixlab/app/lib/juxtapose.css',
|
||||
parent: document.head
|
||||
})
|
||||
|
||||
@@ -911,10 +918,10 @@ app.registerExtension({
|
||||
type: 'div',
|
||||
name: 'preview',
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(
|
||||
this.div.style,
|
||||
get_position_style(ctx, 400, 44, node.size[1])
|
||||
)
|
||||
let s = get_position_style(ctx, widget_width, 44, node.size[1], 36)
|
||||
delete s.height
|
||||
|
||||
Object.assign(this.div.style, s)
|
||||
},
|
||||
serialize: false
|
||||
}
|
||||
|
||||
@@ -81,7 +81,10 @@ function get_position_style (ctx, widget_width, y, node_height) {
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
left:
|
||||
document.querySelector('.comfy-menu').style.display === 'none'
|
||||
? `60px`
|
||||
: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
|
||||
@@ -3,31 +3,17 @@ import { app } from '../../../scripts/app.js'
|
||||
import { ComfyWidgets } from '../../../scripts/widgets.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
|
||||
let api_host = `${window.location.hostname}:${window.location.port}`
|
||||
let api_base = ''
|
||||
let url = `${window.location.protocol}//${api_host}${api_base}`
|
||||
import {
|
||||
getQueue,
|
||||
interrupt,
|
||||
get_position_style,
|
||||
base64Df,
|
||||
getUrl,
|
||||
createImage,
|
||||
sleep
|
||||
} from './common.js'
|
||||
|
||||
async function getQueue () {
|
||||
try {
|
||||
const res = await fetch(`${url}/queue`)
|
||||
const data = await res.json()
|
||||
// console.log(data.queue_running,data.queue_pending)
|
||||
return {
|
||||
// Running action uses a different endpoint for cancelling
|
||||
Running: data.queue_running.length,
|
||||
Pending: data.queue_pending.length
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return { Running: 0, Pending: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
async function interrupt () {
|
||||
const resp = await fetch(`${url}/interrupt`, {
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
// let url = getUrl()
|
||||
|
||||
async function clipboardWriteImage (win, url) {
|
||||
const canvas = document.createElement('canvas')
|
||||
@@ -208,22 +194,6 @@ async function shareScreen (
|
||||
}
|
||||
}
|
||||
|
||||
async function sleep (t = 200) {
|
||||
return new Promise((res, rej) => {
|
||||
setTimeout(() => {
|
||||
res(true)
|
||||
}, t)
|
||||
})
|
||||
}
|
||||
|
||||
function createImage (url) {
|
||||
let im = new Image()
|
||||
return new Promise((res, rej) => {
|
||||
im.onload = () => res(im)
|
||||
im.src = url
|
||||
})
|
||||
}
|
||||
|
||||
async function compareImages (threshold, previousImage, currentImage) {
|
||||
// 将 base64 转换为 Image 对象
|
||||
var previousImg = await createImage(previousImage)
|
||||
@@ -458,44 +428,6 @@ async function requestCamera () {
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
A method that returns the required style for the html
|
||||
*/
|
||||
function get_position_style (ctx, widget_width, y, node_height, top) {
|
||||
const MARGIN = 4 // the margin around the html element
|
||||
|
||||
/* Create a transform that deals with all the scrolling and zooming */
|
||||
const elRect = ctx.canvas.getBoundingClientRect()
|
||||
const transform = new DOMMatrix()
|
||||
.scaleSelf(
|
||||
elRect.width / ctx.canvas.width,
|
||||
elRect.height / ctx.canvas.height
|
||||
)
|
||||
.multiplySelf(ctx.getTransform())
|
||||
.translateSelf(MARGIN, MARGIN + y)
|
||||
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
top: `${top}px`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
maxWidth: `${widget_width - MARGIN * 2}px`,
|
||||
// maxHeight: `${node_height - MARGIN * 2}px`, // we're assuming we have the whole height of the node
|
||||
width: `${widget_width - MARGIN * 2}px`,
|
||||
// height: `${node_height - MARGIN * 2}px`,
|
||||
// background: '#EEEEEE',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
// alignItems: 'center',
|
||||
justifyContent: 'space-around'
|
||||
}
|
||||
}
|
||||
|
||||
const base64Df =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAALZJREFUKFOFkLERwjAQBPdbgBkInECGaMLUQDsE0AkRVRAYWqAByxldPPOWHwnw4OBGye1p50UDSoA+W2ABLPN7i+C5dyC6R/uiAUXRQCs0bXoNIu4QPQzAxDKxHoALOrZcqtiyR/T6CXw7+3IGHhkYcy6BOR2izwT8LptG8rbMiCRAUb+CQ6WzQVb0SNOi5Z2/nX35DRyb/ENazhpWKoGwrpD6nICp5c2qogc4of+c7QcrhgF4Aa/aoAFHiL+RAAAAAElFTkSuQmCC'
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.image.ScreenShareNode',
|
||||
async getCustomWidgets (app) {
|
||||
@@ -593,17 +525,12 @@ app.registerExtension({
|
||||
type: 'HTML', // whatever
|
||||
name: 'sreen_share', // whatever
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
// console.log('ScreenSHare', y, widget_height)
|
||||
// console.log('ScreenSHare', node)
|
||||
Object.assign(
|
||||
this.card.style,
|
||||
get_position_style(
|
||||
ctx,
|
||||
widget_width,
|
||||
widget_height * 5,
|
||||
node.size[1],
|
||||
40
|
||||
)
|
||||
get_position_style(ctx, widget_width, y, node.size[1], 40)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1043,12 +970,13 @@ async function setArea (src) {
|
||||
div.innerHTML = `
|
||||
<div id='ml_overlay' style='position: absolute;top:0;background: #251f1fc4;
|
||||
height: 100vh;
|
||||
z-index:999999;
|
||||
z-index:99999999999999;
|
||||
width: 100%;'>
|
||||
<img id='ml_video' style='position: absolute;
|
||||
height: ${displayHeight}px;user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
outline: 2px solid #eaeaea;
|
||||
left: 0;
|
||||
box-shadow: 8px 9px 17px #575757;' />
|
||||
<div id='ml_selection' style='position: absolute;
|
||||
border: 2px dashed red;
|
||||
|
||||
@@ -2,6 +2,8 @@ import { app } from '../../../scripts/app.js'
|
||||
import { api } from '../../../scripts/api.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
|
||||
import { get_position_style } from './common.js'
|
||||
|
||||
function base64ToBlobFromURL (base64URL, contentType) {
|
||||
return fetch(base64URL).then(response => response.blob())
|
||||
}
|
||||
@@ -37,38 +39,6 @@ async function uploadBase64ToFile (base64) {
|
||||
return url
|
||||
}
|
||||
|
||||
function get_position_style (ctx, widget_width, y, node_height) {
|
||||
const MARGIN = 4 // the margin around the html element
|
||||
|
||||
/* Create a transform that deals with all the scrolling and zooming */
|
||||
const elRect = ctx.canvas.getBoundingClientRect()
|
||||
const transform = new DOMMatrix()
|
||||
.scaleSelf(
|
||||
elRect.width / ctx.canvas.width,
|
||||
elRect.height / ctx.canvas.height
|
||||
)
|
||||
.multiplySelf(ctx.getTransform())
|
||||
.translateSelf(MARGIN, MARGIN + y)
|
||||
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
maxWidth: `${widget_width - MARGIN * 2}px`,
|
||||
// maxHeight: `${node_height - MARGIN * 2}px`, // we're assuming we have the whole height of the node
|
||||
width: `${widget_width - MARGIN * 2}px`,
|
||||
// height: `${node_height * 0.3 - MARGIN * 2}px`,
|
||||
// background: '#EEEEEE',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
// alignItems: 'center',
|
||||
justifyContent: 'space-around'
|
||||
}
|
||||
}
|
||||
|
||||
const p5InputNode = {
|
||||
name: 'Mixlab.Comfy.P5Input',
|
||||
async getCustomWidgets (app) {
|
||||
@@ -106,7 +76,13 @@ const p5InputNode = {
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(
|
||||
this.div.style,
|
||||
get_position_style(ctx, widget_width - 24, 44, node.size[1])
|
||||
get_position_style(
|
||||
ctx,
|
||||
widget_width - 24,
|
||||
44,
|
||||
node.size[1] * 2.8,
|
||||
44
|
||||
)
|
||||
)
|
||||
},
|
||||
serialize: false
|
||||
@@ -179,7 +155,7 @@ const p5InputNode = {
|
||||
|
||||
let nodeId = node.id
|
||||
//延迟才能获得this.id
|
||||
widget.div.innerHTML = `<iframe src="extensions/comfyui-mixlab-nodes/p5_export/p5.html?id=${nodeId}"
|
||||
widget.div.innerHTML = `<iframe src="mixlab/app/p5_export/p5.html?id=${nodeId}"
|
||||
style="border:0;width:100%;height:100%;"
|
||||
></iframe>`
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { api } from '../../../scripts/api.js'
|
||||
import { ComfyWidgets } from '../../../scripts/widgets.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
|
||||
import PhotoSwipeLightbox from '/extensions/comfyui-mixlab-nodes/lib/photoswipe-lightbox.esm.min.js'
|
||||
import PhotoSwipeLightbox from '/mixlab/app/lib/photoswipe-lightbox.esm.min.js'
|
||||
function loadCSS (url) {
|
||||
var link = document.createElement('link')
|
||||
link.rel = 'stylesheet'
|
||||
@@ -40,14 +40,14 @@ function loadCSS (url) {
|
||||
// Append the style element to the document head
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
loadCSS('/extensions/comfyui-mixlab-nodes/lib/photoswipe.min.css')
|
||||
loadCSS('/mixlab/app/lib/photoswipe.min.css')
|
||||
|
||||
function initLightBox () {
|
||||
const lightbox = new PhotoSwipeLightbox({
|
||||
gallery: '.prompt_image_output',
|
||||
children: 'a',
|
||||
pswpModule: () =>
|
||||
import('/extensions/comfyui-mixlab-nodes/lib/photoswipe.esm.min.js')
|
||||
import('/mixlab/app/lib/photoswipe.esm.min.js')
|
||||
})
|
||||
|
||||
lightbox.on('uiRegister', function () {
|
||||
@@ -100,7 +100,10 @@ function get_position_style (ctx, widget_width, y, node_height) {
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
left:
|
||||
document.querySelector('.comfy-menu').style.display === 'none'
|
||||
? `60px`
|
||||
: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
@@ -178,7 +181,7 @@ app.registerExtension({
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = async function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
|
||||
|
||||
const mutable_prompt = this.widgets.filter(
|
||||
w => w.name == 'mutable_prompt'
|
||||
)[0]
|
||||
@@ -190,7 +193,12 @@ app.registerExtension({
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(
|
||||
this.div.style,
|
||||
get_position_style(ctx, widget_width, y, node.size[1])
|
||||
get_position_style(
|
||||
ctx,
|
||||
widget_width,
|
||||
y + widget_height + 24,
|
||||
node.size[1]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -207,7 +215,7 @@ app.registerExtension({
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid; height: 30px;min-width: 122px;
|
||||
border-style: solid;height: 30px;min-width: 122px;
|
||||
`
|
||||
|
||||
// const btn=document.createElement('button');
|
||||
@@ -266,7 +274,6 @@ app.registerExtension({
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'RandomPrompt') {
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -408,7 +415,7 @@ const _createResult = async (node, widget, message) => {
|
||||
const width = node.size[0] * 0.5 - 12
|
||||
|
||||
let height_add = 0
|
||||
|
||||
|
||||
for (let index = 0; index < message._images.length; index++) {
|
||||
const imgs = message._images[index]
|
||||
|
||||
@@ -559,7 +566,7 @@ app.registerExtension({
|
||||
|
||||
let cards = widget.div.querySelectorAll('.card')
|
||||
if (cards.length == 0) node.size = [280, 120]
|
||||
if(widget.value) _createResult(node, widget, widget.value)
|
||||
if (widget.value) _createResult(node, widget, widget.value)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -19,7 +19,10 @@ function get_position_style (ctx, widget_width, y, node_height) {
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
left:
|
||||
document.querySelector('.comfy-menu').style.display === 'none'
|
||||
? `60px`
|
||||
: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
|
||||
@@ -21,7 +21,10 @@ function get_position_style (ctx, widget_width, y, node_height) {
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
left:
|
||||
document.querySelector('.comfy-menu').style.display === 'none'
|
||||
? `60px`
|
||||
: `0`,
|
||||
top: '0',
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
|
||||
@@ -11,6 +11,10 @@ import { smart_init, addSmartMenu } from './smart_connect.js'
|
||||
|
||||
import { completion_ } from './chat.js'
|
||||
|
||||
import { getLocalData, saveLocalData, updateLLMAPIKey } from './common.js'
|
||||
|
||||
const BIZYAIR_SERVER_ADDRESS = 'https://api.siliconflow.cn'
|
||||
|
||||
function showTextByLanguage (key, json) {
|
||||
// 获取浏览器语言
|
||||
var language = navigator.language
|
||||
@@ -28,33 +32,48 @@ function showTextByLanguage (key, json) {
|
||||
//系统prompt
|
||||
// const systemPrompt = `You are a prompt creator, your task is to create prompts for the user input request, the prompts are image descriptions that include keywords for (an adjective, type of image, framing/composition, subject, subject appearance/action, environment, lighting situation, details of the shoot/illustration, visuals aesthetics and artists), brake keywords by comas, provide high quality, non-verboose, coherent, brief, concise, and not superfluous prompts, the subject from the input request must be included verbatim on the prompt,the prompt is english`
|
||||
|
||||
let tool ={
|
||||
"name": "create_prompt",
|
||||
"description": "Create a prompt with a given subject, content, and style based on user input for image descriptions.",
|
||||
"parameter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"subject": {
|
||||
"type": "string",
|
||||
"description": "The subject of the prompt, included verbatim from the input request.",
|
||||
"required": true
|
||||
let tool = {
|
||||
name: 'create_prompt',
|
||||
description:
|
||||
'Create a prompt with a given subject, content, and style based on user input for image descriptions.',
|
||||
parameter: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
subject: {
|
||||
type: 'string',
|
||||
description:
|
||||
'The subject of the prompt, included verbatim from the input request.',
|
||||
required: true
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "The content of the prompt, primarily focusing on the scene and objects, including keywords for adjective, type of image, framing/composition, subject appearance/action, and environment.",
|
||||
"required": true
|
||||
content: {
|
||||
type: 'string',
|
||||
description:
|
||||
'The content of the prompt, primarily focusing on the scene and objects, including keywords for adjective, type of image, framing/composition, subject appearance/action, and environment.',
|
||||
required: true
|
||||
},
|
||||
"style": {
|
||||
"type": "string",
|
||||
"description": "The style of the prompt, including lighting situation, details of the shoot/illustration, visual aesthetics, and artists. Ensure it is high quality, non-verbose, coherent, brief, concise, and not superfluous.",
|
||||
"required": true
|
||||
style: {
|
||||
type: 'string',
|
||||
description:
|
||||
'The style of the prompt, including lighting situation, details of the shoot/illustration, visual aesthetics, and artists. Ensure it is high quality, non-verbose, coherent, brief, concise, and not superfluous.',
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const systemPrompt=`You are a helpful assistant with access to the following functions. Use them if required - ${JSON.stringify(tool,null,2)}`
|
||||
const systemPrompt = `
|
||||
Prompt:
|
||||
|
||||
Describe a scene with a specific theme in fluent and highly detailed English, focusing on the content and style. The description should be within 100 words.
|
||||
|
||||
Theme: [Insert Theme Here]
|
||||
|
||||
Example:
|
||||
|
||||
Theme: Sunset
|
||||
|
||||
The sun sets in a blaze of orange and pink, casting a warm glow over a tranquil lake. Silhouetted trees line the shore, their reflections shimmering in the water. A lone figure sits at the end of a wooden pier, feet dangling above the mirrored surface, lost in thought. The scene exudes peacefulness and quiet beauty.
|
||||
`
|
||||
|
||||
if (!localStorage.getItem('_mixlab_system_prompt')) {
|
||||
localStorage.setItem('_mixlab_system_prompt', systemPrompt)
|
||||
@@ -100,7 +119,7 @@ async function start_llama (model = 'Phi-3-mini-4k-instruct-Q5_K_S.gguf') {
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
if (data.llama_cpp_error||!data.port) {
|
||||
if (data.llama_cpp_error || !data.port) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -145,6 +164,31 @@ function resizeImage (base64Image) {
|
||||
})
|
||||
}
|
||||
|
||||
const createMixlabBtn = () => {
|
||||
const appsButton = document.createElement('button')
|
||||
appsButton.id = 'mixlab_chatbot_by_llamacpp'
|
||||
appsButton.className = 'comfyui-button'
|
||||
appsButton.textContent = '♾️Mixlab'
|
||||
|
||||
// appsButton.onclick = () =>
|
||||
appsButton.onclick = async () => {
|
||||
let llm_key = await updateLLMAPIKey()
|
||||
// if (window._mixlab_llamacpp&&window._mixlab_llamacpp.model&&window._mixlab_llamacpp.model.length>0) {
|
||||
// //显示运行的模型
|
||||
// createModelsModal([
|
||||
// window._mixlab_llamacpp.url,
|
||||
// window._mixlab_llamacpp.model
|
||||
// ])
|
||||
// } else {
|
||||
// // let ms = await get_llamafile_models()
|
||||
// // ms = ms.filter(m => !m.match('-mmproj-'))
|
||||
// // if (ms.length > 0) createModelsModal(ms)
|
||||
// }
|
||||
createModelsModal([], llm_key)
|
||||
}
|
||||
return appsButton
|
||||
}
|
||||
|
||||
// 菜单入口
|
||||
async function createMenu () {
|
||||
const menu = document.querySelector('.comfy-menu')
|
||||
@@ -156,29 +200,16 @@ async function createMenu () {
|
||||
`
|
||||
menu.append(separator)
|
||||
|
||||
if (!menu.querySelector('#mixlab_chatbot_by_llamacpp')) {
|
||||
const appsButton = document.createElement('button')
|
||||
appsButton.id = 'mixlab_chatbot_by_llamacpp'
|
||||
appsButton.textContent = '♾️Mixlab'
|
||||
|
||||
// appsButton.onclick = () =>
|
||||
appsButton.onclick = async () => {
|
||||
// if (window._mixlab_llamacpp&&window._mixlab_llamacpp.model&&window._mixlab_llamacpp.model.length>0) {
|
||||
// //显示运行的模型
|
||||
// createModelsModal([
|
||||
// window._mixlab_llamacpp.url,
|
||||
// window._mixlab_llamacpp.model
|
||||
// ])
|
||||
// } else {
|
||||
// // let ms = await get_llamafile_models()
|
||||
// // ms = ms.filter(m => !m.match('-mmproj-'))
|
||||
// // if (ms.length > 0) createModelsModal(ms)
|
||||
// }
|
||||
createModelsModal([
|
||||
|
||||
])
|
||||
if (
|
||||
menu.style.display === 'none' &&
|
||||
document.querySelector('.comfyui-menu-push')
|
||||
) {
|
||||
//新版ui
|
||||
document.querySelector('.comfyui-menu-push').append(createMixlabBtn())
|
||||
} else {
|
||||
if (!menu.querySelector('#mixlab_chatbot_by_llamacpp')) {
|
||||
menu.append(createMixlabBtn())
|
||||
}
|
||||
menu.append(appsButton)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +222,16 @@ function loadExternalScript (url) {
|
||||
return
|
||||
}
|
||||
|
||||
const existingScript = document.querySelector(`script[src="${url}"]`)
|
||||
if (existingScript) {
|
||||
existingScript.onload = () => {
|
||||
isScriptLoaded[url] = true
|
||||
resolve()
|
||||
}
|
||||
existingScript.onerror = reject
|
||||
return
|
||||
}
|
||||
|
||||
const script = document.createElement('script')
|
||||
script.src = url
|
||||
script.onload = () => {
|
||||
@@ -233,9 +274,7 @@ function createChart (chartDom, nodes) {
|
||||
}
|
||||
|
||||
async function createNodesCharts () {
|
||||
await loadExternalScript(
|
||||
'/extensions/comfyui-mixlab-nodes/lib/echarts.min.js'
|
||||
)
|
||||
await loadExternalScript('/mixlab/app/lib/echarts.min.js')
|
||||
const templates = await loadTemplate()
|
||||
var nodes = {}
|
||||
Array.from(templates, t => {
|
||||
@@ -666,7 +705,10 @@ function get_position_style (ctx, widget_width, y, node_height) {
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
left:
|
||||
document.querySelector('.comfy-menu').style.display === 'none'
|
||||
? `60px`
|
||||
: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
@@ -803,21 +845,67 @@ async function fetchReadmeContent (url) {
|
||||
|
||||
async function startLLM (model) {
|
||||
let res = await start_llama(model)
|
||||
window._mixlab_llamacpp = res||{ model:[] }
|
||||
window._mixlab_llamacpp = res || { model: [] }
|
||||
|
||||
localStorage.setItem('_mixlab_llama_select', res?.model||'')
|
||||
localStorage.setItem('_mixlab_llama_select', res?.model || '')
|
||||
|
||||
if (document.body.querySelector('#mixlab_chatbot_by_llamacpp')&&window._mixlab_llamacpp?.url) {
|
||||
if (
|
||||
document.body.querySelector('#mixlab_chatbot_by_llamacpp') &&
|
||||
window._mixlab_llamacpp?.url
|
||||
) {
|
||||
document.body
|
||||
.querySelector('#mixlab_chatbot_by_llamacpp')
|
||||
.setAttribute('title', window._mixlab_llamacpp.url)
|
||||
}
|
||||
if (document.body.querySelector('#llm_status_btn')&&window._mixlab_llamacpp) {
|
||||
document.body.querySelector('#llm_status_btn').innerText = window._mixlab_llamacpp.model
|
||||
if (
|
||||
document.body.querySelector('#llm_status_btn') &&
|
||||
window._mixlab_llamacpp
|
||||
) {
|
||||
document.body.querySelector('#llm_status_btn').innerText =
|
||||
window._mixlab_llamacpp.model
|
||||
}
|
||||
}
|
||||
|
||||
function createModelsModal (models) {
|
||||
function createInputOfLabel (labelText, key, id) {
|
||||
const label = document.createElement('p')
|
||||
label.innerText = labelText
|
||||
|
||||
const input = document.createElement('input')
|
||||
input.type = 'text'
|
||||
input.style = `color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
height: 26px;
|
||||
padding: 4px 10px;
|
||||
width: 150px;
|
||||
margin-left: 12px;`
|
||||
|
||||
input.value =
|
||||
getLocalData(key)['-'] || Object.values(getLocalData(key))[0] || 'by Mixlab'
|
||||
|
||||
input.addEventListener('change', e => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
|
||||
saveLocalData(key, '-', input.value)
|
||||
})
|
||||
|
||||
const div = document.createElement('div')
|
||||
div.style = `display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: baseline;padding: 0 18px;`
|
||||
|
||||
div.addEventListener('click', e => {
|
||||
e.stopPropagation()
|
||||
})
|
||||
|
||||
div.appendChild(label)
|
||||
div.appendChild(input)
|
||||
return div
|
||||
}
|
||||
|
||||
function createModelsModal (models, llmKey) {
|
||||
var div =
|
||||
document.querySelector('#model-modal') || document.createElement('div')
|
||||
div.id = 'model-modal'
|
||||
@@ -883,8 +971,6 @@ function createModelsModal (models) {
|
||||
user-select: none;
|
||||
`
|
||||
|
||||
// headTitleElement.href = 'https://github.com/shadowcz007/comfyui-mixlab-nodes'
|
||||
// headTitleElement.target = '_blank'
|
||||
const linkIcon = document.createElement('small')
|
||||
linkIcon.textContent = showTextByLanguage('Auto Open', {
|
||||
'Auto Open': '自动开启'
|
||||
@@ -896,7 +982,7 @@ function createModelsModal (models) {
|
||||
Status: 'OFF'
|
||||
})
|
||||
statusIcon.id = 'llm_status_btn'
|
||||
statusIcon.style=`padding: 4px;
|
||||
statusIcon.style = `padding: 4px;
|
||||
background-color: rgb(102, 255, 108);
|
||||
color: black;
|
||||
font-size: 12px;
|
||||
@@ -912,37 +998,25 @@ function createModelsModal (models) {
|
||||
// startLLM()
|
||||
})
|
||||
|
||||
const n_gpu = document.createElement('input')
|
||||
n_gpu.type = 'number'
|
||||
n_gpu.setAttribute('min', -1)
|
||||
n_gpu.setAttribute('max', 9999)
|
||||
|
||||
n_gpu.style = `color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
height: 26px;
|
||||
padding: 4px 10px;
|
||||
width: 48px;
|
||||
margin-left: 12px;`
|
||||
if (localStorage.getItem('_mixlab_llama_n_gpu')) {
|
||||
n_gpu.value = parseInt(localStorage.getItem('_mixlab_llama_n_gpu'))
|
||||
} else {
|
||||
n_gpu.value = -1
|
||||
localStorage.setItem('_mixlab_llama_n_gpu', -1)
|
||||
}
|
||||
|
||||
const n_gpu_p = document.createElement('p')
|
||||
n_gpu_p.innerText = 'n_gpu_layers'
|
||||
|
||||
const batchPageBtn = document.createElement('div')
|
||||
batchPageBtn.style = `display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 12px;`
|
||||
batchPageBtn.innerHTML=`<a href="${get_url()}/mixlab/app" target="_blank" style="color: var(--input-text);
|
||||
batchPageBtn.innerHTML = `<a href="${get_url()}/mixlab/app" target="_blank" style="color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);">App</a>`
|
||||
|
||||
|
||||
const siliconflowHelp = document.createElement('a')
|
||||
siliconflowHelp.textContent = showTextByLanguage('Siliconflow', {
|
||||
Siliconflow: '硅基流动'
|
||||
})
|
||||
siliconflowHelp.style = `color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);margin-top:14px`
|
||||
siliconflowHelp.href = 'https://cloud.siliconflow.cn/s/mixlabs'
|
||||
siliconflowHelp.target = '_blank'
|
||||
|
||||
|
||||
const title = document.createElement('p')
|
||||
title.innerText = 'Mixlab Nodes'
|
||||
title.style = `font-size: 18px;
|
||||
@@ -956,12 +1030,10 @@ function createModelsModal (models) {
|
||||
font-size: 12px;
|
||||
flex-direction: column; `
|
||||
left_d.appendChild(title)
|
||||
// title.appendChild(statusIcon)
|
||||
// left_d.appendChild(linkIcon)
|
||||
left_d.appendChild(batchPageBtn)
|
||||
headTitleElement.appendChild(left_d)
|
||||
left_d.appendChild(siliconflowHelp)
|
||||
|
||||
// headTitleElement.appendChild(n_gpu_div)
|
||||
headTitleElement.appendChild(left_d)
|
||||
|
||||
//重启
|
||||
const reStart = document.createElement('small')
|
||||
@@ -969,7 +1041,7 @@ function createModelsModal (models) {
|
||||
restart: '重启'
|
||||
})
|
||||
|
||||
reStart.style=`padding: 8px;
|
||||
reStart.style = `padding: 8px;
|
||||
font-size: 16px;
|
||||
outline: 1px solid;
|
||||
padding-top: 4px;
|
||||
@@ -1002,23 +1074,23 @@ function createModelsModal (models) {
|
||||
})
|
||||
})
|
||||
|
||||
n_gpu.addEventListener('click', e => {
|
||||
e.stopPropagation()
|
||||
localStorage.setItem('_mixlab_llama_n_gpu', n_gpu.value)
|
||||
})
|
||||
|
||||
modal.appendChild(headTitleElement)
|
||||
|
||||
// Create modal content area
|
||||
var modalContent = document.createElement('div')
|
||||
modalContent.classList.add('modal-content')
|
||||
|
||||
let llmKeyDiv = createInputOfLabel('LLM Key', '_mixlab_llm_api_key', '-')
|
||||
|
||||
saveLocalData('_mixlab_llm_api_url', '-', BIZYAIR_SERVER_ADDRESS)
|
||||
let llmAPIDiv = createInputOfLabel('LLM API', '_mixlab_llm_api_url', '-')
|
||||
|
||||
modalContent.appendChild(llmKeyDiv)
|
||||
modalContent.appendChild(llmAPIDiv)
|
||||
|
||||
var inputForSystemPrompt = document.createElement('textarea')
|
||||
inputForSystemPrompt.className = 'comfy-multiline-input'
|
||||
inputForSystemPrompt.style = ` height: 260px;
|
||||
width: 480px;
|
||||
font-size: 16px;
|
||||
padding: 18px;`
|
||||
inputForSystemPrompt.style = `height: 260px;width: 480px;font-size: 16px;padding: 18px;`
|
||||
inputForSystemPrompt.value = localStorage.getItem('_mixlab_system_prompt')
|
||||
|
||||
inputForSystemPrompt.addEventListener('change', e => {
|
||||
@@ -1030,25 +1102,8 @@ function createModelsModal (models) {
|
||||
e.stopPropagation()
|
||||
})
|
||||
|
||||
// modalContent.appendChild(inputForSystemPrompt)
|
||||
modalContent.appendChild(inputForSystemPrompt)
|
||||
|
||||
if (!window._mixlab_llamacpp||(window._mixlab_llamacpp?.model?.length==0)) {
|
||||
for (const m of models) {
|
||||
let d = document.createElement('div')
|
||||
d.innerText = `${showTextByLanguage('Run', {
|
||||
Run: '运行'
|
||||
})} ${m}`
|
||||
d.className = `mix_tag`
|
||||
|
||||
d.addEventListener('click', async e => {
|
||||
e.stopPropagation()
|
||||
div.remove()
|
||||
// startLLM(m)
|
||||
})
|
||||
|
||||
// modalContent.appendChild(d)
|
||||
}
|
||||
}
|
||||
modal.appendChild(modalContent)
|
||||
|
||||
const helpInfo = document.createElement('a')
|
||||
@@ -1416,7 +1471,7 @@ app.registerExtension({
|
||||
.querySelector('#mixlab_chatbot_by_llamacpp')
|
||||
.setAttribute('title', res.url)
|
||||
})
|
||||
}else{
|
||||
} else {
|
||||
// startLLM('')
|
||||
}
|
||||
|
||||
@@ -1443,19 +1498,17 @@ app.registerExtension({
|
||||
LGraphCanvas.prototype.fixTheNode = function (node) {
|
||||
let new_node = LiteGraph.createNode(node.comfyClass)
|
||||
console.log(node)
|
||||
if(new_node){
|
||||
if (new_node) {
|
||||
new_node.pos = [node.pos[0], node.pos[1]]
|
||||
app.canvas.graph.add(new_node, false)
|
||||
copyNodeValues(node, new_node)
|
||||
app.canvas.graph.remove(node)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
smart_init()
|
||||
|
||||
LGraphCanvas.prototype.text2text = async function (node) {
|
||||
// console.log(node)
|
||||
let widget = node.widgets.filter(
|
||||
w => w.name === 'text' && typeof w.value == 'string'
|
||||
)[0]
|
||||
@@ -1467,10 +1520,14 @@ app.registerExtension({
|
||||
let userInput = widget.value
|
||||
widget.value = widget.value.trim()
|
||||
widget.value += '\n'
|
||||
let jsonStr="";
|
||||
let jsonStr = ''
|
||||
try {
|
||||
await completion_(
|
||||
window._mixlab_llamacpp.url + '/v1/chat/completions',
|
||||
getLocalData('_mixlab_llm_api_key')['-'] ||
|
||||
Object.values(getLocalData('_mixlab_llm_api_key'))[0],
|
||||
getLocalData('_mixlab_llm_api_url')['-'] ||
|
||||
Object.values(getLocalData('_mixlab_llm_api_url'))[0],
|
||||
|
||||
[
|
||||
{
|
||||
role: 'system',
|
||||
@@ -1480,55 +1537,15 @@ app.registerExtension({
|
||||
],
|
||||
controller,
|
||||
t => {
|
||||
// console.log(t)
|
||||
// console.log(t.endsWith('\r'))
|
||||
widget.value += t
|
||||
jsonStr+=t
|
||||
jsonStr += t
|
||||
}
|
||||
)
|
||||
} catch (error) {
|
||||
//是否要自动加载模型
|
||||
if (localStorage.getItem('_mixlab_auto_llama_open')) {
|
||||
let model = localStorage.getItem('_mixlab_llama_select')
|
||||
start_llama(model).then(async res => {
|
||||
window._mixlab_llamacpp = res
|
||||
document.body
|
||||
.querySelector('#mixlab_chatbot_by_llamacpp')
|
||||
.setAttribute('title', res.url)
|
||||
|
||||
await completion_(
|
||||
window._mixlab_llamacpp.url + '/v1/chat/completions',
|
||||
[
|
||||
{
|
||||
role: 'system',
|
||||
content: localStorage.getItem('_mixlab_system_prompt')
|
||||
},
|
||||
{ role: 'user', content: userInput }
|
||||
],
|
||||
controller,
|
||||
t => {
|
||||
// console.log(t)
|
||||
widget.value += t
|
||||
jsonStr+=t
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
let json=null;
|
||||
|
||||
try {
|
||||
json=JSON.parse(jsonStr.trim())
|
||||
} catch (error) {
|
||||
json=JSON.parse(jsonStr.trim()+"}")
|
||||
}
|
||||
|
||||
if(json){
|
||||
widget.value = [json.subject,json.content,json.style].join('\n')
|
||||
}else{
|
||||
widget.value = widget.value.trim()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1812,12 +1829,17 @@ app.registerExtension({
|
||||
inp => inp.name == 'text' && inp.type == 'STRING'
|
||||
)
|
||||
|
||||
const llm_api_key =
|
||||
getLocalData('_mixlab_llm_api_key')['-'] ||
|
||||
Object.values(getLocalData('_mixlab_llm_api_key'))[0],
|
||||
llm_api_url =
|
||||
getLocalData('_mixlab_llm_api_url')['-'] ||
|
||||
Object.values(getLocalData('_mixlab_llm_api_url'))[0]
|
||||
|
||||
if (
|
||||
text_input &&
|
||||
text_input.length == 0 &&
|
||||
text_widget &&
|
||||
text_widget.length == 1 &&
|
||||
window._mixlab_llamacpp &&
|
||||
llm_api_key &&llm_api_url&&
|
||||
node.type != 'ShowTextForGPT'
|
||||
) {
|
||||
opts.push({
|
||||
@@ -1828,19 +1850,19 @@ app.registerExtension({
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
node.imgs &&
|
||||
node.imgs.length > 0 &&
|
||||
window._mixlab_llamacpp &&
|
||||
window._mixlab_llamacpp.chat_format === 'llava-1-5'
|
||||
) {
|
||||
opts.push({
|
||||
content: 'Image-to-Text ♾️Mixlab', // with a name
|
||||
callback: () => {
|
||||
LGraphCanvas.prototype.image2text(node)
|
||||
} // and the callback
|
||||
})
|
||||
}
|
||||
// if (
|
||||
// node.imgs &&
|
||||
// node.imgs.length > 0 &&
|
||||
// window._mixlab_llamacpp &&
|
||||
// window._mixlab_llamacpp.chat_format === 'llava-1-5'
|
||||
// ) {
|
||||
// opts.push({
|
||||
// content: 'Image-to-Text ♾️Mixlab', // with a name
|
||||
// callback: () => {
|
||||
// LGraphCanvas.prototype.image2text(node)
|
||||
// } // and the callback
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
return [...opts, null, ...options] // and return the options
|
||||
@@ -1901,127 +1923,127 @@ app.registerExtension({
|
||||
// Add canvas menu options
|
||||
const orig = LGraphCanvas.prototype.getCanvasMenuOptions
|
||||
|
||||
const apps = await get_my_app()
|
||||
if (!apps) return
|
||||
// const apps = await get_my_app()
|
||||
// if (!apps) return
|
||||
|
||||
console.log('apps', apps)
|
||||
// console.log('apps', apps)
|
||||
|
||||
let apps_map = { 0: [] }
|
||||
// let apps_map = { 0: [] }
|
||||
|
||||
for (const app of apps) {
|
||||
if (app.category) {
|
||||
if (!apps_map[app.category]) apps_map[app.category] = []
|
||||
apps_map[app.category].push(app)
|
||||
} else {
|
||||
apps_map['0'].push(app)
|
||||
}
|
||||
}
|
||||
// for (const app of apps) {
|
||||
// if (app.category) {
|
||||
// if (!apps_map[app.category]) apps_map[app.category] = []
|
||||
// apps_map[app.category].push(app)
|
||||
// } else {
|
||||
// apps_map['0'].push(app)
|
||||
// }
|
||||
// }
|
||||
|
||||
let apps_opts = []
|
||||
for (const category in apps_map) {
|
||||
// console.log('category', typeof category)
|
||||
if (category === '0') {
|
||||
apps_opts.push(
|
||||
...Array.from(apps_map[category], a => {
|
||||
// console.log('#1级',a)
|
||||
return {
|
||||
content: `${a.name}_${a.version}`,
|
||||
has_submenu: false,
|
||||
callback: async () => {
|
||||
try {
|
||||
let ddd = await get_my_app(a.filename)
|
||||
if (!ddd) return
|
||||
let item = ddd[0]
|
||||
if (item) {
|
||||
if (item.author) {
|
||||
// 有作者信息
|
||||
if (item.author.avatar)
|
||||
localStorage.setItem(
|
||||
'_mixlab_author_avatar',
|
||||
item.author.avatar
|
||||
)
|
||||
if (item.author.name)
|
||||
localStorage.setItem(
|
||||
'_mixlab_author_name',
|
||||
item.author.name
|
||||
)
|
||||
// for (const category in apps_map) {
|
||||
// // console.log('category', typeof category)
|
||||
// if (category === '0') {
|
||||
// apps_opts.push(
|
||||
// ...Array.from(apps_map[category], a => {
|
||||
// // console.log('#1级',a)
|
||||
// return {
|
||||
// content: `${a.name}_${a.version}`,
|
||||
// has_submenu: false,
|
||||
// callback: async () => {
|
||||
// try {
|
||||
// let ddd = await get_my_app(a.filename)
|
||||
// if (!ddd) return
|
||||
// let item = ddd[0]
|
||||
// if (item) {
|
||||
// if (item.author) {
|
||||
// // 有作者信息
|
||||
// if (item.author.avatar)
|
||||
// localStorage.setItem(
|
||||
// '_mixlab_author_avatar',
|
||||
// item.author.avatar
|
||||
// )
|
||||
// if (item.author.name)
|
||||
// localStorage.setItem(
|
||||
// '_mixlab_author_name',
|
||||
// item.author.name
|
||||
// )
|
||||
|
||||
if (item.author.link)
|
||||
localStorage.setItem(
|
||||
'_mixlab_author_link',
|
||||
item.author.link
|
||||
)
|
||||
}
|
||||
// if (item.author.link)
|
||||
// localStorage.setItem(
|
||||
// '_mixlab_author_link',
|
||||
// item.author.link
|
||||
// )
|
||||
// }
|
||||
|
||||
// console.log(item.data)
|
||||
app.loadGraphData(item.data)
|
||||
setTimeout(() => {
|
||||
const node = app.graph._nodes_in_order[0]
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(0.5)
|
||||
}, 1000)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
} else {
|
||||
// 二级
|
||||
apps_opts.push({
|
||||
content: '🚀 ' + category,
|
||||
has_submenu: true,
|
||||
disabled: false,
|
||||
submenu: {
|
||||
options: Array.from(apps_map[category], a => {
|
||||
// console.log('#二级',a)
|
||||
return {
|
||||
content: `${a.name}_${a.version}`,
|
||||
callback: async () => {
|
||||
try {
|
||||
let ddd = await get_my_app(a.filename, a.category)
|
||||
// // console.log(item.data)
|
||||
// app.loadGraphData(item.data)
|
||||
// setTimeout(() => {
|
||||
// const node = app.graph._nodes_in_order[0]
|
||||
// if (!node) return
|
||||
// app.canvas.centerOnNode(node)
|
||||
// app.canvas.setZoom(0.5)
|
||||
// }, 1000)
|
||||
// }
|
||||
// } catch (error) {}
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// )
|
||||
// } else {
|
||||
// // 二级
|
||||
// apps_opts.push({
|
||||
// content: '🚀 ' + category,
|
||||
// has_submenu: true,
|
||||
// disabled: false,
|
||||
// submenu: {
|
||||
// options: Array.from(apps_map[category], a => {
|
||||
// // console.log('#二级',a)
|
||||
// return {
|
||||
// content: `${a.name}_${a.version}`,
|
||||
// callback: async () => {
|
||||
// try {
|
||||
// let ddd = await get_my_app(a.filename, a.category)
|
||||
|
||||
if (!ddd) return
|
||||
let item = ddd[0]
|
||||
if (item) {
|
||||
console.log(item)
|
||||
if (item.author) {
|
||||
// 有作者信息
|
||||
if (item.author.avatar)
|
||||
localStorage.setItem(
|
||||
'_mixlab_author_avatar',
|
||||
item.author.avatar
|
||||
)
|
||||
if (item.author.name)
|
||||
localStorage.setItem(
|
||||
'_mixlab_author_name',
|
||||
item.author.name
|
||||
)
|
||||
if (item.author.link)
|
||||
localStorage.setItem(
|
||||
'_mixlab_author_link',
|
||||
item.author.link
|
||||
)
|
||||
}
|
||||
// if (!ddd) return
|
||||
// let item = ddd[0]
|
||||
// if (item) {
|
||||
// console.log(item)
|
||||
// if (item.author) {
|
||||
// // 有作者信息
|
||||
// if (item.author.avatar)
|
||||
// localStorage.setItem(
|
||||
// '_mixlab_author_avatar',
|
||||
// item.author.avatar
|
||||
// )
|
||||
// if (item.author.name)
|
||||
// localStorage.setItem(
|
||||
// '_mixlab_author_name',
|
||||
// item.author.name
|
||||
// )
|
||||
// if (item.author.link)
|
||||
// localStorage.setItem(
|
||||
// '_mixlab_author_link',
|
||||
// item.author.link
|
||||
// )
|
||||
// }
|
||||
|
||||
// console.log(item.data)
|
||||
app.loadGraphData(item.data)
|
||||
setTimeout(() => {
|
||||
const node = app.graph._nodes_in_order[0]
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(0.5)
|
||||
}, 1000)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// // console.log(item.data)
|
||||
// app.loadGraphData(item.data)
|
||||
// setTimeout(() => {
|
||||
// const node = app.graph._nodes_in_order[0]
|
||||
// if (!node) return
|
||||
// app.canvas.centerOnNode(node)
|
||||
// app.canvas.setZoom(0.5)
|
||||
// }, 1000)
|
||||
// }
|
||||
// } catch (error) {}
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// console.log('apps',apps_map, apps_opts,apps)
|
||||
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
|
||||
|
||||
@@ -1,46 +1,13 @@
|
||||
import { app } from '../../../scripts/app.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
import {
|
||||
loadExternalScript,
|
||||
updateLLMAPIKey,
|
||||
get_position_style,
|
||||
getLocalData
|
||||
} from './common.js'
|
||||
|
||||
const getLocalData = key => {
|
||||
let data = {}
|
||||
try {
|
||||
data = JSON.parse(localStorage.getItem(key)) || {}
|
||||
} catch (error) {
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
}
|
||||
function get_position_style (ctx, widget_width, y, node_height) {
|
||||
const MARGIN = 4 // the margin around the html element
|
||||
|
||||
/* Create a transform that deals with all the scrolling and zooming */
|
||||
const elRect = ctx.canvas.getBoundingClientRect()
|
||||
const transform = new DOMMatrix()
|
||||
.scaleSelf(
|
||||
elRect.width / ctx.canvas.width,
|
||||
elRect.height / ctx.canvas.height
|
||||
)
|
||||
.multiplySelf(ctx.getTransform())
|
||||
.translateSelf(MARGIN, MARGIN + y)
|
||||
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
maxWidth: `${widget_width - MARGIN * 2}px`,
|
||||
// maxHeight: `${node_height - MARGIN * 2}px`, // we're assuming we have the whole height of the node
|
||||
width: `${widget_width - MARGIN * 2}px`,
|
||||
// height: `${node_height * 0.3 - MARGIN * 2}px`,
|
||||
// background: '#EEEEEE',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
// alignItems: 'center',
|
||||
justifyContent: 'space-around'
|
||||
}
|
||||
}
|
||||
loadExternalScript('/mixlab/app/lib/pickr.min.js')
|
||||
|
||||
function hexToRGBA (hexColor) {
|
||||
var hex = hexColor.replace('#', '')
|
||||
@@ -62,7 +29,7 @@ app.registerExtension({
|
||||
init () {
|
||||
$el('link', {
|
||||
rel: 'stylesheet',
|
||||
href: '/extensions/comfyui-mixlab-nodes/lib/classic.min.css',
|
||||
href: '/mixlab/app/lib/classic.min.css',
|
||||
parent: document.head
|
||||
})
|
||||
|
||||
@@ -122,7 +89,7 @@ app.registerExtension({
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
|
||||
console.log('Color nodeData', this.div)
|
||||
// console.log('Color nodeData', this.div)
|
||||
|
||||
const widget = {
|
||||
type: 'div',
|
||||
@@ -363,7 +330,7 @@ app.registerExtension({
|
||||
return [128, 32] // a method to compute the current size of the widget
|
||||
},
|
||||
async serializeValue (nodeId, widgetIndex) {
|
||||
let data = getLocalData('_mixlab_api_key')
|
||||
let data = getLocalData('_mixlab_llm_api_key')
|
||||
return data[node.id] || 'by Mixlab'
|
||||
}
|
||||
}
|
||||
@@ -380,6 +347,7 @@ app.registerExtension({
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
|
||||
const rowHeight = this.rowHeight
|
||||
const widget = {
|
||||
type: 'div',
|
||||
name: 'input_key',
|
||||
@@ -414,11 +382,11 @@ app.registerExtension({
|
||||
// ip.value = placeholder
|
||||
|
||||
ip.style = `margin-left:8px;
|
||||
outline: none;
|
||||
border: none;
|
||||
padding:12px;
|
||||
width: 100%;
|
||||
`
|
||||
outline: none;
|
||||
border: none;
|
||||
padding:12px;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
div.appendChild(ip)
|
||||
|
||||
@@ -426,12 +394,13 @@ app.registerExtension({
|
||||
let data = getLocalData(key)
|
||||
data[this.id] = ip.value.trim()
|
||||
localStorage.setItem(key, JSON.stringify(data))
|
||||
updateLLMAPIKey(data[this.id])
|
||||
})
|
||||
|
||||
return div
|
||||
}
|
||||
|
||||
let inputKey = inputDiv('_mixlab_api_key', 'Key')
|
||||
let inputKey = inputDiv('_mixlab_llm_api_key', 'Key')
|
||||
|
||||
widget.div.appendChild(inputKey)
|
||||
|
||||
@@ -444,6 +413,12 @@ app.registerExtension({
|
||||
return onRemoved?.()
|
||||
}
|
||||
|
||||
// const processMouseWheel=app.canvas.processMouseWheel
|
||||
// app.canvas.processMouseWheel=()=>{
|
||||
// console.log(app.canvas.ds.scale)
|
||||
// return processMouseWheel?.()
|
||||
// }
|
||||
|
||||
this.serialize_widgets = true //需要保存参数
|
||||
}
|
||||
}
|
||||
@@ -452,11 +427,13 @@ app.registerExtension({
|
||||
if (node.type === 'KeyInput') {
|
||||
let widget = node.widgets.filter(w => w.div)[0]
|
||||
|
||||
let apiKey = getLocalData('_mixlab_api_key')
|
||||
let apiKey = getLocalData('_mixlab_llm_api_key')
|
||||
|
||||
let id = node.id
|
||||
if (widget.div.querySelector('.Key'))
|
||||
widget.div.querySelector('.Key').value = apiKey[id] || 'by Mixlab'
|
||||
|
||||
if (apiKey[id]) updateLLMAPIKey(apiKey[id])
|
||||
}
|
||||
},
|
||||
nodeCreated (node, app) {
|
||||
@@ -466,12 +443,14 @@ app.registerExtension({
|
||||
if (node.type === 'KeyInput') {
|
||||
let widget = node.widgets.filter(w => w.div)[0]
|
||||
|
||||
let apiKey = getLocalData('_mixlab_api_key')
|
||||
let apiKey = getLocalData('_mixlab_llm_api_key')
|
||||
|
||||
let id = node.id
|
||||
|
||||
if (widget.div.querySelector('.Key'))
|
||||
widget.div.querySelector('.Key').value = apiKey[id] || 'by Mixlab'
|
||||
|
||||
if (apiKey[id]) updateLLMAPIKey(apiKey[id])
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,10 @@ function get_position_style (ctx, widget_width, y, node_height) {
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
left:
|
||||
document.querySelector('.comfy-menu').style.display === 'none'
|
||||
? `60px`
|
||||
: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
|
||||
@@ -40,7 +40,7 @@ class Visualizer {
|
||||
scrolling: 'no',
|
||||
overflow: 'hidden'
|
||||
})
|
||||
this.iframe.src = '/extensions/comfyui-mixlab-nodes/' + visualSrc + '.html'
|
||||
this.iframe.src = '/mixlab/app/' + visualSrc + '.html'
|
||||
console.log('#Visualizer', container, this.iframe)
|
||||
container.appendChild(this.iframe)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,10 @@ function get_position_style (ctx, widget_width, y, node_height) {
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
left:
|
||||
document.querySelector('.comfy-menu').style.display === 'none'
|
||||
? `60px`
|
||||
: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
@@ -125,7 +128,7 @@ app.registerExtension({
|
||||
window._mixlab_file_path_watcher = json.event_type
|
||||
// widget.card.innerText = window._mixlab_file_path_watcher || ''
|
||||
//运行
|
||||
// document.querySelector('#queue-button').click()
|
||||
if (app) app.queuePrompt()
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
||||
pica
|
||||
https://github.com/nodeca/pica
|
||||
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Block below copied from Protovis: http://mbostock.github.com/protovis/
|
||||
* Copyright 2010 Stanford Visualization Group
|
||||
* Licensed under the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
* @license
|
||||
*/
|
||||
|
||||
/*!
|
||||
* jQuery JavaScript Library v3.7.1
|
||||
* https://jquery.com/
|
||||
*
|
||||
* Copyright OpenJS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://jquery.org/license
|
||||
*
|
||||
* Date: 2023-08-28T13:37Z
|
||||
*/
|
||||
|
||||
/*!
|
||||
* quantize.js Copyright 2008 Nick Rabinowitz.
|
||||
* Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
* @license
|
||||
*/
|
||||
|
||||
/*! alertifyjs - v1.13.1 - Mohammad Younes <Mohammad@alertifyjs.com> (http://alertifyjs.com) */
|
||||
|
||||
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
||||
|
||||
/**
|
||||
* hermite-resize - Canvas image resize/resample using Hermite filter with JavaScript.
|
||||
* @version v2.2.10
|
||||
* @link https://github.com/viliusle/miniPaint
|
||||
* @license MIT
|
||||
*/
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"bundle.js","sources":["webpack://miniPaint/bundle.js"],"mappings":";AAAA","sourceRoot":""}
|
||||
|
Before Width: | Height: | Size: 4.5 KiB |
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 298.73 298.73" style="enable-background:new 0 0 298.73 298.73;" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill:#010002;" d="M264.959,9.35H33.787C15.153,9.35,0,24.498,0,43.154v212.461c0,18.634,15.153,33.766,33.787,33.766
|
||||
h231.171c18.634,0,33.771-15.132,33.771-33.766V43.154C298.73,24.498,283.593,9.35,264.959,9.35z M193.174,59.623
|
||||
c18.02,0,32.634,14.615,32.634,32.634s-14.615,32.634-32.634,32.634c-18.025,0-32.634-14.615-32.634-32.634
|
||||
S175.149,59.623,193.174,59.623z M254.363,258.149H149.362H49.039c-9.013,0-13.027-6.521-8.964-14.566l56.006-110.93
|
||||
c4.058-8.044,11.792-8.762,17.269-1.605l56.316,73.596c5.477,7.158,15.05,7.767,21.386,1.354l13.777-13.951
|
||||
c6.331-6.413,15.659-5.619,20.826,1.762l35.675,50.959C266.487,252.16,263.376,258.149,254.363,258.149z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 30.051 30.051" style="enable-background:new 0 0 30.051 30.051;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M19.982,14.438l-6.24-4.536c-0.229-0.166-0.533-0.191-0.784-0.062c-0.253,0.128-0.411,0.388-0.411,0.669v9.069
|
||||
c0,0.284,0.158,0.543,0.411,0.671c0.107,0.054,0.224,0.081,0.342,0.081c0.154,0,0.31-0.049,0.442-0.146l6.24-4.532
|
||||
c0.197-0.145,0.312-0.369,0.312-0.607C20.295,14.803,20.177,14.58,19.982,14.438z"/>
|
||||
<path d="M15.026,0.002C6.726,0.002,0,6.728,0,15.028c0,8.297,6.726,15.021,15.026,15.021c8.298,0,15.025-6.725,15.025-15.021
|
||||
C30.052,6.728,23.324,0.002,15.026,0.002z M15.026,27.542c-6.912,0-12.516-5.601-12.516-12.514c0-6.91,5.604-12.518,12.516-12.518
|
||||
c6.911,0,12.514,5.607,12.514,12.518C27.541,21.941,21.937,27.542,15.026,27.542z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,5 +0,0 @@
|
||||
<svg width="124" height="150" viewBox="0 0 124 150" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="55" y="14" width="14" height="63"/>
|
||||
<rect x="116" width="14" height="61" transform="rotate(90 116 0)"/>
|
||||
<path d="M62 150L8.30643 75L115.694 75L62 150Z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 258 B |
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, 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 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M264.574,4.675C262.697,1.761,259.467,0,256,0c-3.467,0-6.697,1.761-8.574,4.675
|
||||
c-6.532,10.14-159.966,249.362-159.966,338.784C87.459,436.393,163.066,512,256,512s168.541-75.607,168.541-168.541
|
||||
C424.541,254.037,271.106,14.815,264.574,4.675z M256,491.602c-81.686,0-148.142-66.456-148.142-148.143
|
||||
c0-34.037,26.926-101.269,77.865-194.427C213.83,97.626,242.219,51.324,256,29.29c13.77,22.016,42.123,68.259,70.223,119.64
|
||||
c50.976,93.212,77.92,160.478,77.92,194.529C404.142,425.146,337.686,491.602,256,491.602z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M375.907,332.939c-5.633,0-10.199,4.566-10.199,10.199c0,43.197-25.482,82.521-64.919,100.181
|
||||
c-5.141,2.301-7.442,8.335-5.14,13.476c1.695,3.788,5.416,6.034,9.314,6.034c1.393,0,2.809-0.287,4.163-0.893
|
||||
c46.764-20.941,76.981-67.572,76.981-118.797C386.106,337.505,381.54,332.939,375.907,332.939z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M281.818,460.702c-0.729-5.586-5.85-9.519-11.435-8.791c-4.736,0.619-9.574,0.933-14.383,0.933
|
||||
c-5.633,0-10.199,4.566-10.199,10.199c0,5.633,4.566,10.199,10.199,10.199c5.69,0,11.419-0.372,17.028-1.106
|
||||
C278.613,471.407,282.548,466.287,281.818,460.702z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.21 13c2.106 0 3.412-1.087 3.412-2.823 0-1.306-.984-2.283-2.324-2.386v-.055a2.176 2.176 0 0 0 1.852-2.14c0-1.51-1.162-2.46-3.014-2.46H3.843V13H8.21zM5.908 4.674h1.696c.963 0 1.517.451 1.517 1.244 0 .834-.629 1.32-1.73 1.32H5.908V4.673zm0 6.788V8.598h1.73c1.217 0 1.88.492 1.88 1.415 0 .943-.643 1.449-1.832 1.449H5.907z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 491 B |
@@ -1 +0,0 @@
|
||||
<svg height="443pt" viewBox="0 0 443.06138 443" width="443pt" xmlns="http://www.w3.org/2000/svg"><path d="m431.328125 25.894531-14.136719-14.136719c-8.070312-8.078124-19.210937-12.320312-30.613281-11.6601558-11.402344.6601558-21.976563 6.1640628-29.058594 15.1249998l-117.023437 164.839844c-4.089844 5.761719-10.511719 9.425781-17.554688 10.019531-7.039062.59375-13.984375-1.945312-18.980468-6.941406l-34.234376-34.207031c-9.480468-9.109375-24.46875-9.109375-33.949218 0l-11.296875 11.304687 158.398437 158.398438 11.304688-11.304688c4.527344-4.488281 7.070312-10.597656 7.070312-16.96875 0-6.375-2.542968-12.484375-7.070312-16.972656l-34.222656-34.234375c-4.996094-4.996094-7.535157-11.941406-6.941407-18.980469.59375-7.042969 4.257813-13.464843 10.019531-17.554687l165-117.183594c8.890626-7.109375 14.332032-17.671875 14.960938-29.035156.628906-11.367188-3.617188-22.464844-11.671875-30.507813zm-24 43.808594c-9.375 9.371094-24.570313 9.371094-33.945313 0-9.371093-9.375-9.371093-24.574219 0-33.945313 9.496094-9.0625 24.441407-9.0625 33.9375 0 9.375 9.371094 9.378907 24.570313.007813 33.945313zm0 0"/><path d="m390.351562 44.734375c-3.234374 0-6.152343 1.949219-7.390624 4.9375-1.234376 2.988281-.550782 6.429687 1.734374 8.71875 3.160157 3.03125 8.148438 3.03125 11.304688 0 2.289062-2.289063 2.972656-5.730469 1.734375-8.71875s-4.15625-4.9375-7.390625-4.9375zm0 0"/><path d="m135.792969 420.429688 22.65625 22.65625 113.085937-113.167969-158.398437-158.402344-113.136719 113.121094 22.65625 22.65625 84.847656-84.855469c2.007813-2.078125 4.984375-2.914062 7.78125-2.183594 2.796875.734375 4.980469 2.917969 5.710938 5.714844.734375 2.796875-.101563 5.773438-2.179688 7.78125l-84.847656 84.855469 22.632812 22.625 50.902344-50.90625c3.140625-3.03125 8.128906-2.988281 11.214844.097656s3.128906 8.078125.097656 11.214844l-50.90625 50.921875 22.617188 22.621094 62.214844-62.222657c3.125-3.125 8.191406-3.128906 11.316406-.003906 3.128906 3.125 3.128906 8.191406.003906 11.316406l-62.222656 62.222657 22.625 22.625 73.535156-73.535157c3.140625-3.03125 8.128906-2.988281 11.214844.097657 3.085937 3.085937 3.128906 8.074218.097656 11.214843zm0 0"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 402.56 402.56" style="enable-background:new 0 0 402.56 402.56;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<polygon points="38.613,234.88 38.4,274.56 96.64,274.56 116.693,265.173 9.6,372.48 39.68,402.56 147.2,295.253 137.813,316.587
|
||||
137.813,373.76 177.493,374.187 177.493,234.88 "/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<polygon points="306.987,128.213 285.653,137.6 392.96,30.08 362.88,0 255.573,107.307 264.96,87.04 264.96,28.8 225.28,29.013
|
||||
225.28,167.893 364.587,167.893 364.16,128.213 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 778 B |
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 540.721 540.721" style="enable-background:new 0 0 540.721 540.721;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M521.858,465.271H18.862c-7.545,0-12.575,5.03-12.575,12.575v50.3c0,7.545,5.03,12.575,12.575,12.575h502.996
|
||||
c7.545,0,12.575-5.03,12.575-12.575v-50.3C534.433,470.301,529.403,465.271,521.858,465.271z"/>
|
||||
<path d="M227.606,98.084c5.03-5.03,20.12-12.575,30.18-12.575c7.545,0,12.575-5.03,12.575-12.575c0-7.545-5.03-10.06-12.575-10.06
|
||||
l0,0c-17.605,0-37.725,7.545-47.785,17.605c-12.575,10.06-22.635,27.665-22.635,47.785c0,7.545,5.03,10.06,12.575,10.06l0,0
|
||||
c7.545,0,12.575-2.515,12.575-10.06C212.516,118.204,220.061,103.114,227.606,98.084z"/>
|
||||
<path d="M18.862,440.121h502.996c5.03,0,7.545-2.515,10.06-5.03c2.515-2.515,2.515-7.545,0-12.575l-47.785-100.599
|
||||
c0-5.03-5.03-7.545-10.06-7.545H333.235v-77.964c25.15-20.12,60.359-60.36,60.359-110.659C393.594,55.33,338.265,0,270.36,0
|
||||
S147.126,57.845,147.126,125.749c-2.515,47.785,35.21,88.024,60.36,110.659v77.964H66.647c-5.03,0-10.06,2.515-10.06,7.545
|
||||
L8.802,422.517c-2.515,5.03-2.515,7.545,0,12.575C11.317,440.121,13.832,440.121,18.862,440.121z M169.761,125.749
|
||||
c0-55.33,45.27-100.599,98.084-100.599c52.815,0,98.084,45.27,98.084,100.599c0,37.725-27.665,75.449-55.33,93.054
|
||||
c-2.515,2.515-5.03,5.03-5.03,10.06v123.234c0,2.515-12.575,10.06-22.635,10.06h-37.725c-7.545,0-15.09-7.545-15.09-10.06V228.863
|
||||
c0-5.03-2.515-7.545-5.03-10.06C207.486,206.228,169.761,168.504,169.761,125.749z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="956.815px" height="956.815px" viewBox="0 0 956.815 956.815" style="enable-background:new 0 0 956.815 956.815;"
|
||||
xml:space="preserve">
|
||||
<g>
|
||||
<path d="M137.621,162.622H20c-11.046,0-20,8.954-20,20v72.919c0,11.046,8.954,20,20,20h117.622L137.621,162.622L137.621,162.622z"
|
||||
/>
|
||||
<path d="M774.193,956.815c11.046,0,20-8.954,20-20V819.193H681.274v117.621c0,11.046,8.954,20,20,20L774.193,956.815
|
||||
L774.193,956.815z"/>
|
||||
<path d="M794.193,656.275V182.622c0-11.046-8.954-20-20-20H300.54v112.919h380.734v380.734H794.193z"/>
|
||||
<path d="M936.814,681.275H794.193H681.274H275.54V275.541V162.622V20c0-11.046-8.954-20-20-20h-72.918c-11.046,0-20,8.954-20,20
|
||||
v142.622v112.919v498.653c0,11.046,8.954,20,20,20h498.653h112.918h142.622c11.045,0,20-8.954,20-20v-72.918
|
||||
C956.814,690.229,947.86,681.275,936.814,681.275z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,6 +0,0 @@
|
||||
<svg height="365pt" viewBox="0 0 365.71733 365" width="365pt" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#f44336">
|
||||
<path d="m356.339844 296.347656-286.613282-286.613281c-12.5-12.5-32.765624-12.5-45.246093 0l-15.105469 15.082031c-12.5 12.503906-12.5 32.769532 0 45.25l286.613281 286.613282c12.503907 12.5 32.769531 12.5 45.25 0l15.082031-15.082032c12.523438-12.480468 12.523438-32.75.019532-45.25zm0 0"/>
|
||||
<path d="m295.988281 9.734375-286.613281 286.613281c-12.5 12.5-12.5 32.769532 0 45.25l15.082031 15.082032c12.503907 12.5 32.769531 12.5 45.25 0l286.632813-286.59375c12.503906-12.5 12.503906-32.765626 0-45.246094l-15.082032-15.082032c-12.5-12.523437-32.765624-12.523437-45.269531-.023437zm0 0"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 719 B |
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<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 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
|
||||
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
|
||||
<g><g transform="translate(0.000000,511.000000) scale(0.100000,-0.100000)"><path d="M2646.5,3789.8c-1040.2-119.8-2106-953.5-2445-1917.3l-91.8-260.1L102,355.5c-5.1-1081-2.5-1279.9,33.1-1415c155.5-609.4,578.8-986.7,1282.4-1139.7c300.9-66.3,670.5-96.9,1534.8-127.5c400.3-12.7,831.2-43.3,961.2-63.7c464-76.5,836.3-229.5,1448.1-599.2c698.6-420.7,1063.2-550.7,1682.7-599.2c234.6-17.9,645,20.4,866.9,81.6c494.6,132.6,1007.1,476.8,1341.1,902.6c318.7,405.4,484.4,749.6,589,1223.8c53.5,255,56.1,313.6,58.6,1527.2c0,1155-5.1,1274.8-45.9,1415c-186.1,596.6-609.4,917.9-1374.2,1045.3c-135.1,22.9-550.7,51-963.7,63.7c-1560.3,53.5-1828,112.2-2957.5,662.9c-685.8,334-928,415.6-1328.3,458.9C2944.8,3820.4,2919.3,3820.4,2646.5,3789.8z M3378.2,3165.2c255-51,461.5-135.1,1014.7-413c543.1-270.3,821-382.4,1213.6-487c311-84.2,798-155.5,1042.8-155.5c142.8,0,158.1-5.1,234.6-91.8c112.2-124.9,214.2-163.2,420.7-163.2c193.8,0,359.5,61.2,423.2,153c40.8,56.1,48.5,58.6,328.9,43.3c894.9-51,1249.3-328.9,1246.7-971.4c0-117.3-7.6-247.3-17.8-288.1c-30.6-147.9-94.3-331.4-168.3-494.6l-76.5-168.3l-158.1-12.7c-379.9-30.6-563.4-270.3-372.2-487l73.9-86.7l-81.6-76.5c-211.6-198.9-527.8-359.5-843.9-428.3c-160.6-35.7-272.8-43.3-560.9-30.6c-555.8,20.4-777.6,99.4-1494,525.2C5111.9-174.8,4857-52.4,4482.2,70c-410.5,137.7-685.8,175.9-1402.3,206.5c-1287.5,51-1596,86.7-1891.8,219.3c-191.2,84.1-379.9,288.1-443.6,471.7c-66.3,193.8-58.6,471.7,17.9,696l61.2,175.9l155.5,5.1c226.9,5.1,402.8,119.8,433.4,280.5c20.4,107.1,0,168.3-84.1,249.9l-66.3,66.3l61.2,66.3c249.9,267.7,808.2,571.1,1200.8,657.8C2745.9,3213.6,3143.6,3213.6,3378.2,3165.2z M3391-391.5c221.8-112.2,397.7-351.8,362-492.1c-51-211.6-418.1-183.6-657.8,51C2972.8-712.8,2921.8-610.8,2932-496c5.1,79.1,22.9,102,96.9,137.7C3130.9-307.4,3240.5-317.6,3391-391.5z M1328.3-414.5c140.2-86.7,221.8-209.1,221.8-323.8c0-216.7-392.6-153-573.7,91.8C792.9-394.1,1040.2-230.9,1328.3-414.5z M5550.4-1273.7c135.1-84.1,244.8-257.5,221.8-349.3c-23-89.2-145.3-132.6-267.7-96.9c-137.7,43.4-293.2,163.2-341.7,267.7c-56.1,117.3-53.5,160.6,10.2,224.4C5244.5-1156.4,5387.3-1174.2,5550.4-1273.7z M2682.2-1339.9c293.2-165.7,341.6-476.8,73.9-476.8c-196.3,0-466.6,232-466.6,397.8C2289.5-1278.7,2503.7-1238,2682.2-1339.9z M6659.5-2446.5c209-107.1,372.2-308.5,372.2-461.5c0-68.8-79-150.4-163.2-173.4c-168.3-40.8-517.6,158.1-617,351.8C6111.3-2459.2,6353.6-2290.9,6659.5-2446.5z"/><path d="M3013.6,2639.9c-135.1-45.9-221.8-130-221.8-219.3c0-160.6,181-270.3,453.8-270.3c418.1-2.5,599.2,288.1,280.5,453.8C3408.8,2665.4,3138.5,2683.3,3013.6,2639.9z"/><path d="M2213,1490.1c-168.3-48.5-295.8-132.6-334-226.9c-28-68.8-25.5-89.2,17.9-168.3c188.7-351.9,1106.5-288.1,1106.5,79c0,48.5-22.9,114.7-51,150.4C2837.7,1472.2,2450.2,1556.4,2213,1490.1z"/><path d="M5693.2,1492.6c-119.8-40.8-216.7-145.3-216.7-232c0-267.7,594-372.2,846.5-147.9c201.4,181-17.9,410.5-387.5,407.9C5843.6,1520.7,5736.6,1507.9,5693.2,1492.6z"/><path d="M8051.6,1278.5c-124.9-53.5-178.5-109.6-178.5-188.7c0-137.7,165.7-229.5,415.6-229.5c175.9,0,341.7,73.9,377.3,168.3C8737.4,1227.5,8321.8,1395.8,8051.6,1278.5z"/><path d="M4321.5,1013.3c-147.9-28-234.6-102-234.6-204c0-124.9,96.9-186.1,313.6-198.9c204-12.7,346.7,35.7,392.6,132.6C4869.7,916.4,4609.6,1064.3,4321.5,1013.3z"/><path d="M6662.1,225.5c-196.3-48.4-344.2-181-344.2-311c0-323.8,775.1-453.8,1070.8-175.9C7671.7,3.7,7161.8,345.3,6662.1,225.5z"/></g></g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.7 KiB |
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="548.157px" height="548.157px" viewBox="0 0 548.157 548.157" style="enable-background:new 0 0 548.157 548.157;"
|
||||
xml:space="preserve">
|
||||
<g>
|
||||
<path d="M545.027,112.765c-3.046-6.471-7.57-11.657-13.565-15.555c-5.996-3.9-12.614-5.852-19.846-5.852H292.351
|
||||
c-11.04,0-20.175,4.184-27.408,12.56L9.13,396.279c-4.758,5.328-7.661,11.56-8.708,18.698c-1.049,7.139-0.144,13.941,2.712,20.417
|
||||
c3.044,6.468,7.564,11.652,13.561,15.553c5.997,3.898,12.612,5.853,19.845,5.853h219.268c11.042,0,20.177-4.179,27.41-12.56
|
||||
l255.813-292.363c4.75-5.33,7.655-11.561,8.699-18.699C548.788,126.039,547.877,119.238,545.027,112.765z M255.811,420.254H36.54
|
||||
l95.93-109.632h219.27L255.811,420.254z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 122 B |
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M494.412,245.154L371.59,122.507l93.792-93.934L437.07,0.304L343.28,94.237l-56.613-56.532L37.714,286.659
|
||||
l207.754,207.459c11.699,11.699,27.069,17.549,42.436,17.549c15.367,0,30.735-5.849,42.436-17.549l164.082-164.082
|
||||
c11.389-11.388,17.632-26.53,17.578-42.635C511.946,271.446,505.704,256.446,494.412,245.154z M466.131,301.745L302.048,465.828
|
||||
c-7.799,7.8-20.489,7.8-28.3-0.01L94.314,286.638L286.687,94.266l28.324,28.283L211.272,226.445l28.312,28.269L343.322,150.82
|
||||
l122.811,122.636c3.761,3.761,5.842,8.761,5.859,14.079C472.009,292.902,469.928,297.948,466.131,301.745z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M95.137,398.966c-10.179-15.198-20.245-27.482-20.669-27.997l-15.455-18.808l-15.455,18.808
|
||||
c-0.424,0.516-10.49,12.799-20.669,27.997C2.372,429.596,0,444.293,0,452.684c0,32.54,26.474,59.012,59.012,59.012
|
||||
c32.539,0,59.012-26.473,59.012-59.012C118.025,444.293,115.652,429.596,95.137,398.966z M59.012,471.688
|
||||
c-10.479,0-19.004-8.525-19.005-18.956c0.004-0.08,0.526-8.271,16.291-31.757c0.907-1.35,1.813-2.678,2.714-3.972
|
||||
c0.899,1.294,1.806,2.622,2.714,3.972c15.491,23.079,16.265,31.388,16.29,31.745C77.996,463.182,69.48,471.688,59.012,471.688z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 265 B |
|
Before Width: | Height: | Size: 84 B |
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-type-italic" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M7.991 11.674L9.53 4.455c.123-.595.246-.71 1.347-.807l.11-.52H7.211l-.11.52c1.06.096 1.128.212 1.005.807L6.57 11.674c-.123.595-.246.71-1.346.806l-.11.52h3.774l.11-.52c-1.06-.095-1.129-.211-1.006-.806z"/></svg>
|
||||
|
Before Width: | Height: | Size: 393 B |
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, 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 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g id="XMLID_1_">
|
||||
<path id="XMLID_12_" d="M128.4,96.2L64.2,32H32.6v31.6l64.2,64.2L128.4,96.2z M160,0.4h31.6v64.2H160V0.4z M287.5,160.4h64.2V192
|
||||
h-64.2V160.4z M320,64.6V33h-31.6l-64.2,64.2l31.6,31.6L320,64.6z M0,160.4h64.2V192H0V160.4z M160,287.9h31.6v64.2H160V287.9z
|
||||
M31.6,287.9v31.6h31.6l64.2-64.2l-31.6-31.6L31.6,287.9z M504.2,441.4L187,123.2c-9.3-9.3-24.2-9.3-33.5,0L123.7,153
|
||||
c-9.3,9.3-9.3,24.2,0,33.5l318.2,318.2c9.3,9.3,24.2,9.3,33.5,0l29.8-29.8C514.4,465.5,514.4,450.7,504.2,441.4z M240,272
|
||||
l-95.8-95.8l31.6-31.6l95.8,95.8L240,272z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 913 B |
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="548.176px" height="548.176px" viewBox="0 0 548.176 548.176" style="enable-background:new 0 0 548.176 548.176;"
|
||||
xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M534.75,68.238c-8.945-8.945-19.694-13.417-32.261-13.417H45.681c-12.562,0-23.313,4.471-32.264,13.417
|
||||
C4.471,77.185,0,87.936,0,100.499v347.173c0,12.566,4.471,23.318,13.417,32.264c8.951,8.946,19.702,13.419,32.264,13.419h456.815
|
||||
c12.56,0,23.312-4.473,32.258-13.419c8.945-8.945,13.422-19.697,13.422-32.264V100.499
|
||||
C548.176,87.936,543.699,77.185,534.75,68.238z M511.623,447.672c0,2.478-0.899,4.613-2.707,6.427
|
||||
c-1.81,1.8-3.952,2.703-6.427,2.703H45.681c-2.473,0-4.615-0.903-6.423-2.703c-1.807-1.813-2.712-3.949-2.712-6.427V100.495
|
||||
c0-2.474,0.902-4.611,2.712-6.423c1.809-1.803,3.951-2.708,6.423-2.708h456.815c2.471,0,4.613,0.905,6.42,2.708
|
||||
c1.801,1.812,2.707,3.949,2.707,6.423V447.672L511.623,447.672z"/>
|
||||
<path d="M127.91,237.541c15.229,0,28.171-5.327,38.831-15.987c10.657-10.66,15.987-23.601,15.987-38.826
|
||||
c0-15.23-5.333-28.171-15.987-38.832c-10.66-10.656-23.603-15.986-38.831-15.986c-15.227,0-28.168,5.33-38.828,15.986
|
||||
c-10.656,10.66-15.986,23.601-15.986,38.832c0,15.225,5.327,28.169,15.986,38.826C99.742,232.211,112.683,237.541,127.91,237.541z
|
||||
"/>
|
||||
<polygon points="210.134,319.765 164.452,274.088 73.092,365.447 73.092,420.267 475.085,420.267 475.085,292.36 356.315,173.587
|
||||
"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="124px" height="124px" viewBox="0 0 124 124" style="enable-background:new 0 0 124 124;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M112,6H12C5.4,6,0,11.4,0,18s5.4,12,12,12h100c6.6,0,12-5.4,12-12S118.6,6,112,6z"/>
|
||||
<path d="M112,50H12C5.4,50,0,55.4,0,62c0,6.6,5.4,12,12,12h100c6.6,0,12-5.4,12-12C124,55.4,118.6,50,112,50z"/>
|
||||
<path d="M112,94H12c-6.6,0-12,5.4-12,12s5.4,12,12,12h100c6.6,0,12-5.4,12-12S118.6,94,112,94z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 797 B |
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<polygon points="51.2,353.28 0,512 158.72,460.8 "/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
|
||||
<rect x="89.73" y="169.097" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -95.8575 260.3719)" width="353.277" height="153.599"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M504.32,79.36L432.64,7.68c-10.24-10.24-25.6-10.24-35.84,0l-23.04,23.04l107.52,107.52l23.04-23.04
|
||||
C514.56,104.96,514.56,89.6,504.32,79.36z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 767 B |
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 296.135 296.135" style="enable-background:new 0 0 296.135 296.135;" xml:space="preserve">
|
||||
<path d="M284.5,11.635C276.997,4.132,267.021,0,256.411,0s-20.586,4.132-28.089,11.635l-64.681,64.68l-6.658-6.658
|
||||
c-2.777-2.777-6.2-4.512-9.786-5.206c-0.598-0.116-1.2-0.202-1.804-0.26s-1.211-0.087-1.817-0.087s-1.213,0.029-1.817,0.087
|
||||
s-1.206,0.145-1.804,0.26c-3.585,0.694-7.009,2.43-9.786,5.206v0c-1.388,1.388-2.516,2.938-3.384,4.59
|
||||
c-0.289,0.55-0.55,1.112-0.781,1.683c-0.694,1.712-1.128,3.505-1.302,5.317c-0.058,0.604-0.087,1.211-0.087,1.817
|
||||
c0,1.213,0.116,2.426,0.347,3.621c0.347,1.793,0.954,3.545,1.822,5.196c0.868,1.651,1.996,3.201,3.384,4.59l4.319,4.319
|
||||
L21.468,213.811c-1.434,1.434-2.563,3.143-3.316,5.025l-16.19,40.387c-3.326,8.298-2.338,17.648,2.644,25.013
|
||||
c5.04,7.451,13.356,11.899,22.244,11.899c3.432,0,6.817-0.659,10.063-1.961L77.3,277.984c1.882-0.754,3.592-1.883,5.025-3.316
|
||||
l113.021-113.021l4.318,4.318c0.463,0.463,0.944,0.897,1.44,1.302c0.993,0.81,2.049,1.504,3.15,2.083
|
||||
c2.752,1.446,5.785,2.169,8.818,2.169l0,0c0.029,0,0.058-0.004,0.087-0.004c1.791-0.008,3.58-0.264,5.312-0.777
|
||||
c2.345-0.694,4.583-1.851,6.569-3.471c0.497-0.405,0.977-0.839,1.44-1.302v0c2.314-2.314,3.905-5.077,4.772-8.009
|
||||
c0.694-2.345,0.926-4.798,0.694-7.216c-0.116-1.209-0.347-2.408-0.694-3.581s-0.81-2.318-1.388-3.419
|
||||
c-0.868-1.651-1.996-3.201-3.384-4.59l-6.658-6.658l64.68-64.68C299.988,52.326,299.988,27.124,284.5,11.635z M63.285,251.282
|
||||
l-30.764,12.331l12.332-30.763l110.848-110.848l18.432,18.432L63.285,251.282z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M403.678,272c0,40.005-15.615,77.651-43.989,106.011C331.33,406.385,293.683,422,253.678,422
|
||||
c-40.005,0-77.651-15.615-106.011-43.989c-28.374-28.359-43.989-66.006-43.989-106.011s15.615-77.651,43.989-106.011
|
||||
C176.027,137.615,213.673,122,253.678,122c25.298,0,49.849,6.343,71.88,18.472L267.023,212h231.299L440.49,0l-57.393,70.13
|
||||
C344.323,45.14,299.865,32,253.678,32c-64.116,0-124.395,24.961-169.702,70.298C38.639,147.605,13.678,207.884,13.678,272
|
||||
s24.961,124.395,70.298,169.702C129.284,487.039,189.562,512,253.678,512s124.395-24.961,169.702-70.298
|
||||
c45.337-45.308,70.298-105.586,70.298-169.702v-15h-90V272z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1008 B |
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, 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 262.474 262.474" style="enable-background:new 0 0 262.474 262.474;" xml:space="preserve">
|
||||
<g id="XMLID_351_">
|
||||
<path id="XMLID_352_" d="M217.327,210.541l-38.815-71.507l31.813-17.271c4.652-2.525,7.628-7.315,7.833-12.604
|
||||
c0.204-5.289-2.395-10.294-6.837-13.17L66.756,2.408c-4.609-2.984-10.481-3.211-15.308-0.591c-4.826,2.62-7.835,7.667-7.844,13.159
|
||||
l-0.277,172.206c-0.008,5.293,2.773,10.198,7.32,12.909c4.545,2.709,10.185,2.824,14.836,0.298l31.811-17.268l38.816,71.506
|
||||
c2.718,5.007,7.873,7.847,13.196,7.847c2.417,0,4.869-0.586,7.143-1.82l54.849-29.774
|
||||
C218.58,226.928,221.279,217.822,217.327,210.541z M155.321,227.132l-38.816-71.507c-1.898-3.496-5.107-6.095-8.922-7.225
|
||||
c-1.395-0.414-2.831-0.618-4.261-0.618c-2.478,0-4.94,0.614-7.157,1.817l-22.797,12.376L73.56,42.55l100.255,64.898l-22.799,12.377
|
||||
c-7.281,3.952-9.979,13.058-6.027,20.338l38.815,71.507L155.321,227.132z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,69 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 511.997 511.997" style="enable-background:new 0 0 511.997 511.997;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<rect x="137.809" y="471.577" width="89.817" height="39.919"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="283.463" y="471.577" width="89.817" height="39.919"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M60.963,471.581c-11.579,0-21-9.421-21-21.001v-19.959H0.044v19.959c0,33.592,27.327,60.92,60.919,60.92h19.959v-39.919
|
||||
H60.963z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M471.083,430.621v19.959c0,11.58-9.42,21.001-21,21.001h-19.959V511.5h19.959c33.592,0,60.919-27.328,60.919-60.92
|
||||
v-19.959H471.083z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="137.809" y="0.497" width="89.817" height="39.919"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="283.463" y="0.497" width="89.817" height="39.919"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M60.963,0.497c-33.592,0-60.92,27.328-60.92,60.92v19.959h39.919h0.001V61.417c0-11.58,9.42-21.001,21-21.001h19.959
|
||||
V0.497H60.963z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M450.083,0.497h-19.959v39.919h19.959c11.579,0,21,9.421,21,21.001v19.959h39.919V61.417
|
||||
C511.002,27.825,483.675,0.497,450.083,0.497z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect y="288.909" width="39.919" height="89.817"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect y="143.246" width="39.919" height="89.817"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="472.078" y="288.909" width="39.919" height="89.817"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="472.078" y="143.246" width="39.919" height="89.817"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,6 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1 2 31 27" x="0px" y="0px">
|
||||
<title>shapes</title>
|
||||
<g>
|
||||
<path d="M30.87,16.33l-7.5-13a1,1,0,0,0-1.73,0l-5,8.69a8.15,8.15,0,0,0-1.61,0V7a1,1,0,0,0-1-1H2A1,1,0,0,0,1,7V19a1,1,0,0,0,1,1H8a8,8,0,1,0,15.69-2.17H30a1,1,0,0,0,.87-1.5ZM3,18V8H13v4.58l0,0A8,8,0,0,0,8.28,18H3Zm13,8a6,6,0,0,1-6-6,4.62,4.62,0,0,1,.07-.86,6,6,0,0,1,4.22-4.89A5.92,5.92,0,0,1,16,14a5.29,5.29,0,0,1,1,.09A6,6,0,0,1,16,26Zm6.83-10.17h0a8,8,0,0,0-4.12-3.34l0,0L22.5,5.84l5.77,10Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 514 B |
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M508.867,190.394l-111-143.568C394.996,43.113,390.52,41,386,41H126c-4.498,0-8.98,2.092-11.867,5.825l-111,143.568
|
||||
c-4.509,5.832-4.112,14.076,0.937,19.447l241,256.432c2.655,2.824,6.568,4.728,10.93,4.728c4.3,0,8.221-1.845,10.93-4.728
|
||||
l241-256.432C512.979,204.469,513.376,196.226,508.867,190.394z M241,418.137L34.694,198.62l93.584-121.043L241,172.957V418.137z
|
||||
M256,146.351L166.949,71h178.102L256,146.351z M271,418.137v-245.18l112.722-95.38l93.585,121.043L271,418.137z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 867 B |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<svg width="1em" height="1em" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.527 13.164c-2.153 0-3.589-1.107-3.705-2.81h1.23c.144 1.06 1.129 1.703 2.544 1.703 1.34 0 2.31-.705 2.31-1.675 0-.827-.547-1.374-1.914-1.675L8.046 8.5h3.45c.468.437.675.994.675 1.697 0 1.826-1.436 2.967-3.644 2.967zM6.602 6.5H5.167a2.776 2.776 0 0 1-.099-.76c0-1.627 1.436-2.768 3.48-2.768 1.969 0 3.39 1.175 3.445 2.85h-1.23c-.11-1.08-.964-1.743-2.25-1.743-1.23 0-2.18.602-2.18 1.607 0 .31.083.581.27.814z"/><path fill-rule="evenodd" d="M15 8.5H1v-1h14v1z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 608 B |
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="977.7px" height="977.7px" viewBox="0 0 977.7 977.7" style="enable-background:new 0 0 977.7 977.7;" xml:space="preserve"
|
||||
>
|
||||
<g>
|
||||
<path d="M770.7,930.6v-35.301c0-23.398-18-42.898-41.3-44.799c-17.9-1.5-35.8-3.1-53.7-5c-34.5-3.6-72.5-7.4-72.5-50.301L603,131.7
|
||||
c136-2,210.5,76.7,250,193.2c6.3,18.7,23.8,31.3,43.5,31.3h36.2c24.9,0,45-20.1,45-45V47.1c0-24.9-20.1-45-45-45H488.9h-0.2H45
|
||||
c-24.9,0-45,20.1-45,45v264.1c0,24.9,20.1,45,45,45h36.2c19.7,0,37.2-12.6,43.5-31.3c39.4-116.5,114-195.2,250-193.2l-0.3,663.5
|
||||
c0,42.9-38,46.701-72.5,50.301c-17.9,1.9-35.8,3.5-53.7,5c-23.3,1.9-41.3,21.4-41.3,44.799V930.6c0,24.9,20.1,45,45,45h236.8h0.3
|
||||
h236.7C750.5,975.6,770.7,955.401,770.7,930.6z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-type-underline" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M5.313 3.136h-1.23V9.54c0 2.105 1.47 3.623 3.917 3.623s3.917-1.518 3.917-3.623V3.136h-1.23v6.323c0 1.49-.978 2.57-2.687 2.57-1.709 0-2.687-1.08-2.687-2.57V3.136z"/><path fill-rule="evenodd" d="M12.5 15h-9v-1h9v1z"/></svg>
|
||||
|
Before Width: | Height: | Size: 408 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M18.885 3.515c-4.617-4.618-12.056-4.676-16.756-.195l-2.129-2.258v7.938h7.484l-2.066-2.191c2.82-2.706 7.297-2.676 10.073.1 4.341 4.341 1.737 12.291-5.491 12.291v4.8c3.708 0 6.614-1.244 8.885-3.515 4.686-4.686 4.686-12.284 0-16.97z"/></svg>
|
||||
|
Before Width: | Height: | Size: 330 B |
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 461.312 461.312" style="enable-background:new 0 0 461.312 461.312;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M230.656,156.416c-40.96,0-74.24,33.28-74.24,74.24s33.28,74.24,74.24,74.24s74.24-33.28,74.24-74.24
|
||||
S271.616,156.416,230.656,156.416z M225.024,208.64c-9.216,0-16.896,7.68-16.896,16.896h-24.576
|
||||
c0.512-23.04,18.944-41.472,41.472-41.472V208.64z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M455.936,215.296c-25.088-31.232-114.688-133.12-225.28-133.12S30.464,184.064,5.376,215.296
|
||||
c-7.168,8.704-7.168,21.504,0,30.72c25.088,31.232,114.688,133.12,225.28,133.12s200.192-101.888,225.28-133.12
|
||||
C463.104,237.312,463.104,224.512,455.936,215.296z M230.656,338.176c-59.392,0-107.52-48.128-107.52-107.52
|
||||
s48.128-107.52,107.52-107.52s107.52,48.128,107.52,107.52S290.048,338.176,230.656,338.176z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.1 MiB |
|
Before Width: | Height: | Size: 198 KiB |
@@ -1,139 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr" lang="en-US">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="IE=edge" />
|
||||
<title>miniPaint - image editor</title>
|
||||
<meta name="description"
|
||||
content="miniPaint is free online image editor using HTML5. Edit, adjust your images, add effects online in your browser, without installing anything..." />
|
||||
<meta name="keywords"
|
||||
content="photo, image, picture, transparent, layers, free, edit, html5, canvas, javascript, online, photoshop, gimp, effects, sharpen, blur, magic eraser tool, clone tool, rotate, resize, photoshop online, online tools, tilt shift, sprites, keypoints" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0" />
|
||||
<link rel="icon" sizes="192x192" href="images/favicon.png">
|
||||
<!-- <link rel="manifest" href="dist/manifest.json"> -->
|
||||
<!-- Google -->
|
||||
<meta itemprop="name" content="miniPaint" />
|
||||
<meta itemprop="description"
|
||||
content="miniPaint is free online image editor using HTML5. Edit, adjust your images, add effects online in your browser, without installing anything..." />
|
||||
<meta itemprop="image" content="https://viliusle.github.io/miniPaint/images/preview.jpg" />
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="miniPaint" />
|
||||
<meta name="twitter:description"
|
||||
content="miniPaint is free online image editor using HTML5. Edit, adjust your images, add effects online in your browser, without installing anything..." />
|
||||
<meta name="twitter:image" content="https://viliusle.github.io/miniPaint/images/preview.jpg" />
|
||||
<meta name="twitter:image:alt"
|
||||
content="miniPaint is free online image editor using HTML5. Edit, adjust your images, add effects online in your browser, without installing anything..." />
|
||||
<!-- Facebook, Pinterest -->
|
||||
<meta property="og:title" content="miniPaint" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:url" content="https://viliusle.github.io/miniPaint/" />
|
||||
<meta property="og:image" content="https://viliusle.github.io/miniPaint/images/preview.jpg" />
|
||||
<meta property="og:description"
|
||||
content="miniPaint is free online image editor using HTML5. Edit, adjust your images, add effects online in your browser, without installing anything..." />
|
||||
<meta property="og:site_name" content="miniPaint" />
|
||||
|
||||
<script src="dist/bundle.ejs"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
|
||||
<nav aria-label="Main Menu" class="main_menu" id="main_menu"></nav>
|
||||
|
||||
<div class="submenu">
|
||||
<!-- <a class="logo" href="#">miniPaint</a> -->
|
||||
<div class="block attributes" id="action_attributes"></div>
|
||||
|
||||
<select id="automask_models_mixlab"></select>
|
||||
|
||||
<button id="automask_image_mixlab" type="button" style="width: 98px;
|
||||
height: 36px;margin-right: 18px;
|
||||
color: white;">
|
||||
RemoveBg
|
||||
</button>
|
||||
|
||||
<button id="cancel_image_mixlab" type="button" style="width: 98px;
|
||||
height: 36px;
|
||||
color: white;">
|
||||
Cancel
|
||||
</button>
|
||||
<button id="save_image_mixlab" type="button" style="width: 98px;
|
||||
height: 36px;
|
||||
color: white;">
|
||||
Save
|
||||
</button>
|
||||
|
||||
<a
|
||||
target="_blank" href="https://discord.gg/xbP2GZF6gn"
|
||||
style="width: 98px;
|
||||
color: white;
|
||||
text-decoration: none;"> Help/帮助 </a>
|
||||
|
||||
<button class="undo_button" id="undo_button" type="button">
|
||||
<span class="sr_only">Undo</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="sidebar_left" id="tools_container"></div>
|
||||
|
||||
|
||||
<div class="middle_area" id="middle_area">
|
||||
|
||||
<canvas class="ruler_left" id="ruler_left"></canvas>
|
||||
<canvas class="ruler_top" id="ruler_top"></canvas>
|
||||
|
||||
<div class="main_wrapper" id="main_wrapper">
|
||||
<div class="canvas_wrapper" id="canvas_wrapper">
|
||||
<div id="mouse"></div>
|
||||
<div class="transparent-grid" id="canvas_minipaint_background"></div>
|
||||
<canvas id="canvas_minipaint">
|
||||
<div class="trn error">
|
||||
Your browser does not support canvas or JavaScript is not enabled.
|
||||
</div>
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar_right">
|
||||
<div class="preview block" style="display: none;">
|
||||
<h2 class="trn toggle" data-target="toggle_preview">Preview</h2>
|
||||
<div id="toggle_preview"></div>
|
||||
</div>
|
||||
|
||||
<div class="colors block">
|
||||
<h2 class="trn toggle" data-target="toggle_colors">Colors</h2>
|
||||
<div class="content" id="toggle_colors"></div>
|
||||
</div>
|
||||
|
||||
<div class="block" id="info_base" style="display: none;">
|
||||
<h2 class="trn toggle toggle-full" data-target="toggle_info">Information</h2>
|
||||
<div class="content" id="toggle_info"></div>
|
||||
</div>
|
||||
|
||||
<div class="details block" id="details_base">
|
||||
<h2 class="trn toggle toggle-full" data-target="toggle_details">Layer details</h2>
|
||||
<div class="content details-content" id="toggle_details"></div>
|
||||
</div>
|
||||
|
||||
<div class="layers block">
|
||||
<h2 class="trn">Layers</h2>
|
||||
<div class="content" id="layers_base"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mobile_menu">
|
||||
<button class="left_mobile_menu" id="left_mobile_menu_button" type="button">
|
||||
<span class="sr_only">Toggle Menu</span>
|
||||
</button>
|
||||
<button class="right_mobile_menu" id="mobile_menu_button" type="button">
|
||||
<span class="sr_only">Toggle Menu</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="hidden" id="tmp"></div>
|
||||
<div id="popups"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,70 +0,0 @@
|
||||
function setup () {
|
||||
createCanvas(400, 400, WEBGL)
|
||||
angleMode(DEGREES)
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (frameCount === 1) {
|
||||
capturer.start()
|
||||
}
|
||||
|
||||
background(30)
|
||||
noStroke()
|
||||
translate(0, 0, sin(frameCount) * 400 - 800)
|
||||
|
||||
rotateX(frameCount)
|
||||
rotateY(frameCount)
|
||||
rotateZ(frameCount)
|
||||
|
||||
var w = 20
|
||||
|
||||
randomSeed(1)
|
||||
for (var x = -width / 2; x <= width / 2; x += w) {
|
||||
for (var y = -width / 2; y <= width / 2; y += w) {
|
||||
for (var z = -width / 2; z <= width / 2; z += w) {
|
||||
var r = random(255)
|
||||
var g = random(255)
|
||||
var b = random(255)
|
||||
|
||||
fill(r, g, b)
|
||||
|
||||
push()
|
||||
translate(x, y, z)
|
||||
box(w)
|
||||
pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
// console.log(frameRate());
|
||||
|
||||
if (frameCount < 60) {
|
||||
capturer.capture(canvas)
|
||||
} else if (frameCount === 60) {
|
||||
capturer.save(function (blob) {
|
||||
// console.log(blob)
|
||||
|
||||
// 示例用法
|
||||
// const blob = new Blob([/* 数据 */], { type: 'video/webm' });
|
||||
blobToBase64(blob).then(base64String => {
|
||||
console.log(base64String);
|
||||
|
||||
const video = document.createElement('video');
|
||||
video.controls = true; // 显示视频控件(播放、暂停等)
|
||||
video.src = base64String; // 设置视频的 src 属性为 Base64 数据 URL
|
||||
video.width = 640; // 设置视频宽度
|
||||
video.height = 360; // 设置视频高度
|
||||
|
||||
// 将 video 元素添加到页面中
|
||||
document.body.appendChild(video)
|
||||
|
||||
// 自动播放视频
|
||||
video.play();
|
||||
|
||||
}).catch(error => {
|
||||
console.error('转换失败:', error);
|
||||
});
|
||||
|
||||
})
|
||||
capturer.stop()
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
<script type="module">
|
||||
|
||||
import { api } from "../../../scripts/api.js";
|
||||
import Command from '/extensions/comfyui-mixlab-nodes/javascript/command.js'
|
||||
import { api } from "/mixlab/app/javascript/api.js";
|
||||
import Command from '/mixlab/app/javascript/command.js'
|
||||
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,482 @@
|
||||
class ComfyApi extends EventTarget {
|
||||
#registered = new Set();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.api_host = location.host;
|
||||
this.api_base = location.pathname.split('/').slice(0, -1).join('/');
|
||||
this.initialClientId = sessionStorage.getItem("clientId");
|
||||
}
|
||||
|
||||
apiURL(route) {
|
||||
return this.api_base + route;
|
||||
}
|
||||
|
||||
fetchApi(route, options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
if (!options.headers) {
|
||||
options.headers = {};
|
||||
}
|
||||
options.headers["Comfy-User"] = this.user;
|
||||
return fetch(this.apiURL(route), options);
|
||||
}
|
||||
|
||||
addEventListener(type, callback, options) {
|
||||
super.addEventListener(type, callback, options);
|
||||
this.#registered.add(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll status for colab and other things that don't support websockets.
|
||||
*/
|
||||
#pollQueue() {
|
||||
setInterval(async () => {
|
||||
try {
|
||||
const resp = await this.fetchApi("/prompt");
|
||||
const status = await resp.json();
|
||||
this.dispatchEvent(new CustomEvent("status", { detail: status }));
|
||||
} catch (error) {
|
||||
this.dispatchEvent(new CustomEvent("status", { detail: null }));
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and connects a WebSocket for realtime updates
|
||||
* @param {boolean} isReconnect If the socket is connection is a reconnect attempt
|
||||
*/
|
||||
#createSocket(isReconnect) {
|
||||
if (this.socket) {
|
||||
return;
|
||||
}
|
||||
|
||||
let opened = false;
|
||||
let existingSession = window.name;
|
||||
if (existingSession) {
|
||||
existingSession = "?clientId=" + existingSession;
|
||||
}
|
||||
this.socket = new WebSocket(
|
||||
`ws${window.location.protocol === "https:" ? "s" : ""}://${this.api_host}${this.api_base}/ws${existingSession}`
|
||||
);
|
||||
this.socket.binaryType = "arraybuffer";
|
||||
|
||||
this.socket.addEventListener("open", () => {
|
||||
opened = true;
|
||||
if (isReconnect) {
|
||||
this.dispatchEvent(new CustomEvent("reconnected"));
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.addEventListener("error", () => {
|
||||
if (this.socket) this.socket.close();
|
||||
if (!isReconnect && !opened) {
|
||||
this.#pollQueue();
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.addEventListener("close", () => {
|
||||
setTimeout(() => {
|
||||
this.socket = null;
|
||||
this.#createSocket(true);
|
||||
}, 300);
|
||||
if (opened) {
|
||||
this.dispatchEvent(new CustomEvent("status", { detail: null }));
|
||||
this.dispatchEvent(new CustomEvent("reconnecting"));
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.addEventListener("message", (event) => {
|
||||
try {
|
||||
if (event.data instanceof ArrayBuffer) {
|
||||
const view = new DataView(event.data);
|
||||
const eventType = view.getUint32(0);
|
||||
const buffer = event.data.slice(4);
|
||||
switch (eventType) {
|
||||
case 1:
|
||||
const view2 = new DataView(event.data);
|
||||
const imageType = view2.getUint32(0)
|
||||
let imageMime
|
||||
switch (imageType) {
|
||||
case 1:
|
||||
default:
|
||||
imageMime = "image/jpeg";
|
||||
break;
|
||||
case 2:
|
||||
imageMime = "image/png"
|
||||
}
|
||||
const imageBlob = new Blob([buffer.slice(4)], { type: imageMime });
|
||||
this.dispatchEvent(new CustomEvent("b_preview", { detail: imageBlob }));
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown binary websocket message of type ${eventType}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const msg = JSON.parse(event.data);
|
||||
switch (msg.type) {
|
||||
case "status":
|
||||
if (msg.data.sid) {
|
||||
this.clientId = msg.data.sid;
|
||||
window.name = this.clientId; // use window name so it isnt reused when duplicating tabs
|
||||
sessionStorage.setItem("clientId", this.clientId); // store in session storage so duplicate tab can load correct workflow
|
||||
}
|
||||
this.dispatchEvent(new CustomEvent("status", { detail: msg.data.status }));
|
||||
break;
|
||||
case "progress":
|
||||
this.dispatchEvent(new CustomEvent("progress", { detail: msg.data }));
|
||||
break;
|
||||
case "executing":
|
||||
this.dispatchEvent(new CustomEvent("executing", { detail: msg.data.node }));
|
||||
break;
|
||||
case "executed":
|
||||
this.dispatchEvent(new CustomEvent("executed", { detail: msg.data }));
|
||||
break;
|
||||
case "execution_start":
|
||||
this.dispatchEvent(new CustomEvent("execution_start", { detail: msg.data }));
|
||||
break;
|
||||
case "execution_success":
|
||||
this.dispatchEvent(new CustomEvent("execution_success", { detail: msg.data }));
|
||||
break;
|
||||
case "execution_error":
|
||||
this.dispatchEvent(new CustomEvent("execution_error", { detail: msg.data }));
|
||||
break;
|
||||
case "execution_cached":
|
||||
this.dispatchEvent(new CustomEvent("execution_cached", { detail: msg.data }));
|
||||
break;
|
||||
default:
|
||||
if (this.#registered.has(msg.type)) {
|
||||
this.dispatchEvent(new CustomEvent(msg.type, { detail: msg.data }));
|
||||
} else {
|
||||
throw new Error(`Unknown message type ${msg.type}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Unhandled message:", event.data, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises sockets and realtime updates
|
||||
*/
|
||||
init() {
|
||||
this.#createSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of extension urls
|
||||
* @returns An array of script urls to import
|
||||
*/
|
||||
async getExtensions() {
|
||||
const resp = await this.fetchApi("/extensions", { cache: "no-store" });
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of embedding names
|
||||
* @returns An array of script urls to import
|
||||
*/
|
||||
async getEmbeddings() {
|
||||
const resp = await this.fetchApi("/embeddings", { cache: "no-store" });
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads node object definitions for the graph
|
||||
* @returns The node definitions
|
||||
*/
|
||||
async getNodeDefs() {
|
||||
const resp = await this.fetchApi("/object_info", { cache: "no-store" });
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} number The index at which to queue the prompt, passing -1 will insert the prompt at the front of the queue
|
||||
* @param {object} prompt The prompt data to queue
|
||||
*/
|
||||
async queuePrompt(number, { output, workflow }) {
|
||||
const body = {
|
||||
client_id: this.clientId,
|
||||
prompt: output,
|
||||
extra_data: { extra_pnginfo: { workflow } },
|
||||
};
|
||||
|
||||
if (number === -1) {
|
||||
body.front = true;
|
||||
} else if (number != 0) {
|
||||
body.number = number;
|
||||
}
|
||||
|
||||
const res = await this.fetchApi("/prompt", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw {
|
||||
response: await res.json(),
|
||||
};
|
||||
}
|
||||
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a list of items (queue or history)
|
||||
* @param {string} type The type of items to load, queue or history
|
||||
* @returns The items of the specified type grouped by their status
|
||||
*/
|
||||
async getItems(type) {
|
||||
if (type === "queue") {
|
||||
return this.getQueue();
|
||||
}
|
||||
return this.getHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current state of the queue
|
||||
* @returns The currently running and queued items
|
||||
*/
|
||||
async getQueue() {
|
||||
try {
|
||||
const res = await this.fetchApi("/queue");
|
||||
const data = await res.json();
|
||||
return {
|
||||
// Running action uses a different endpoint for cancelling
|
||||
Running: data.queue_running.map((prompt) => ({
|
||||
prompt,
|
||||
remove: { name: "Cancel", cb: () => api.interrupt() },
|
||||
})),
|
||||
Pending: data.queue_pending.map((prompt) => ({ prompt })),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return { Running: [], Pending: [] };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the prompt execution history
|
||||
* @returns Prompt history including node outputs
|
||||
*/
|
||||
async getHistory(max_items=200) {
|
||||
try {
|
||||
const res = await this.fetchApi(`/history?max_items=${max_items}`);
|
||||
return { History: Object.values(await res.json()) };
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return { History: [] };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets system & device stats
|
||||
* @returns System stats such as python version, OS, per device info
|
||||
*/
|
||||
async getSystemStats() {
|
||||
const res = await this.fetchApi("/system_stats");
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a POST request to the API
|
||||
* @param {*} type The endpoint to post to
|
||||
* @param {*} body Optional POST data
|
||||
*/
|
||||
async #postItem(type, body) {
|
||||
try {
|
||||
await this.fetchApi("/" + type, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an item from the specified list
|
||||
* @param {string} type The type of item to delete, queue or history
|
||||
* @param {number} id The id of the item to delete
|
||||
*/
|
||||
async deleteItem(type, id) {
|
||||
await this.#postItem(type, { delete: [id] });
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the specified list
|
||||
* @param {string} type The type of list to clear, queue or history
|
||||
*/
|
||||
async clearItems(type) {
|
||||
await this.#postItem(type, { clear: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupts the execution of the running prompt
|
||||
*/
|
||||
async interrupt() {
|
||||
await this.#postItem("interrupt", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets user configuration data and where data should be stored
|
||||
* @returns { Promise<{ storage: "server" | "browser", users?: Promise<string, unknown>, migrated?: boolean }> }
|
||||
*/
|
||||
async getUserConfig() {
|
||||
return (await this.fetchApi("/users")).json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new user
|
||||
* @param { string } username
|
||||
* @returns The fetch response
|
||||
*/
|
||||
createUser(username) {
|
||||
return this.fetchApi("/users", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ username }),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all setting values for the current user
|
||||
* @returns { Promise<string, unknown> } A dictionary of id -> value
|
||||
*/
|
||||
async getSettings() {
|
||||
return (await this.fetchApi("/settings")).json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a setting for the current user
|
||||
* @param { string } id The id of the setting to fetch
|
||||
* @returns { Promise<unknown> } The setting value
|
||||
*/
|
||||
async getSetting(id) {
|
||||
return (await this.fetchApi(`/settings/${encodeURIComponent(id)}`)).json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a dictionary of settings for the current user
|
||||
* @param { Record<string, unknown> } settings Dictionary of setting id -> value to save
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
async storeSettings(settings) {
|
||||
return this.fetchApi(`/settings`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(settings)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a setting for the current user
|
||||
* @param { string } id The id of the setting to update
|
||||
* @param { unknown } value The value of the setting
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
async storeSetting(id, value) {
|
||||
return this.fetchApi(`/settings/${encodeURIComponent(id)}`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(value)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a user data file for the current user
|
||||
* @param { string } file The name of the userdata file to load
|
||||
* @param { RequestInit } [options]
|
||||
* @returns { Promise<Response> } The fetch response object
|
||||
*/
|
||||
async getUserData(file, options) {
|
||||
return this.fetchApi(`/userdata/${encodeURIComponent(file)}`, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a user data file for the current user
|
||||
* @param { string } file The name of the userdata file to save
|
||||
* @param { unknown } data The data to save to the file
|
||||
* @param { RequestInit & { overwrite?: boolean, stringify?: boolean, throwOnError?: boolean } } [options]
|
||||
* @returns { Promise<Response> }
|
||||
*/
|
||||
async storeUserData(file, data, options = { overwrite: true, stringify: true, throwOnError: true }) {
|
||||
const resp = await this.fetchApi(`/userdata/${encodeURIComponent(file)}?overwrite=${options?.overwrite}`, {
|
||||
method: "POST",
|
||||
body: options?.stringify ? JSON.stringify(data) : data,
|
||||
...options,
|
||||
});
|
||||
if (resp.status !== 200 && options?.throwOnError !== false) {
|
||||
throw new Error(`Error storing user data file '${file}': ${resp.status} ${(await resp).statusText}`);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a user data file for the current user
|
||||
* @param { string } file The name of the userdata file to delete
|
||||
*/
|
||||
async deleteUserData(file) {
|
||||
const resp = await this.fetchApi(`/userdata/${encodeURIComponent(file)}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
if (resp.status !== 204) {
|
||||
throw new Error(`Error removing user data file '${file}': ${resp.status} ${(resp).statusText}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a user data file for the current user
|
||||
* @param { string } source The userdata file to move
|
||||
* @param { string } dest The destination for the file
|
||||
*/
|
||||
async moveUserData(source, dest, options = { overwrite: false }) {
|
||||
const resp = await this.fetchApi(`/userdata/${encodeURIComponent(source)}/move/${encodeURIComponent(dest)}?overwrite=${options?.overwrite}`, {
|
||||
method: "POST",
|
||||
});
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* Lists user data files for the current user
|
||||
* @param { string } dir The directory in which to list files
|
||||
* @param { boolean } [recurse] If the listing should be recursive
|
||||
* @param { true } [split] If the paths should be split based on the os path separator
|
||||
* @returns { Promise<string[][]>> } The list of split file paths in the format [fullPath, ...splitPath]
|
||||
*/
|
||||
/**
|
||||
* @overload
|
||||
* Lists user data files for the current user
|
||||
* @param { string } dir The directory in which to list files
|
||||
* @param { boolean } [recurse] If the listing should be recursive
|
||||
* @param { false | undefined } [split] If the paths should be split based on the os path separator
|
||||
* @returns { Promise<string[]>> } The list of files
|
||||
*/
|
||||
async listUserData(dir, recurse, split) {
|
||||
const resp = await this.fetchApi(
|
||||
`/userdata?${new URLSearchParams({
|
||||
recurse,
|
||||
dir,
|
||||
split,
|
||||
})}`
|
||||
);
|
||||
if (resp.status === 404) return [];
|
||||
if (resp.status !== 200) {
|
||||
throw new Error(`Error getting user data list '${dir}': ${resp.status} ${resp.statusText}`);
|
||||
}
|
||||
return resp.json();
|
||||
}
|
||||
}
|
||||
|
||||
export const api = new ComfyApi();
|
||||
@@ -142,7 +142,14 @@ async function getPromptResult (category) {
|
||||
}
|
||||
|
||||
// 新的运行工作流的接口
|
||||
function queuePromptNew (filename, category, seed, input, client_id,apps=null) {
|
||||
function queuePromptNew (
|
||||
filename,
|
||||
category,
|
||||
seed,
|
||||
input,
|
||||
client_id,
|
||||
apps = null
|
||||
) {
|
||||
let url = get_url()
|
||||
// var filename = "Text-to-Image_1.json", category = "";
|
||||
|
||||
@@ -325,8 +332,9 @@ function convertImageToBlackBasedOnAlpha (image) {
|
||||
const alpha = pixels[i + 3]
|
||||
if (alpha !== 0) {
|
||||
// Set non-transparent pixels to black
|
||||
// 蒙版是黑色?
|
||||
pixels[i] = 0 // Red
|
||||
pixels[i + 1] = 0 // Green
|
||||
pixels[i + 1] = 255 // Green
|
||||
pixels[i + 2] = 0 // Blue
|
||||
}
|
||||
}
|
||||