Comfy Preview Update Bug
This commit is contained in:
+1
-1
@@ -14,7 +14,7 @@ develop-eggs/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
# lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
const BASE_URL = 'nexus';
|
||||
|
||||
async function fetchWithHandling(url, options = {}) {
|
||||
try {
|
||||
// console.info("Calling: ", url)
|
||||
const response = await fetch(url, options);
|
||||
const data = await response.json();
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Something went wrong');
|
||||
}
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(`Fetch error: ${error.message}`);
|
||||
return { error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUserSpecificWorkflows(userId) {
|
||||
const url = `${BASE_URL}/workflows`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ user_id: userId }),
|
||||
};
|
||||
return await fetchWithHandling(url, options);
|
||||
}
|
||||
|
||||
export async function getUserSpecificWorkflow(userId, workflowId) {
|
||||
const url = `${BASE_URL}/workflows/${workflowId}`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ user_id: userId }),
|
||||
};
|
||||
return await fetchWithHandling(url, options);
|
||||
}
|
||||
|
||||
export async function getUserSpecificWorkflowsMeta(userId) {
|
||||
const url = `${BASE_URL}/workflows/meta`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ user_id: userId }),
|
||||
};
|
||||
return await fetchWithHandling(url, options);
|
||||
}
|
||||
|
||||
export async function postWorkflow(userId, workflowData) {
|
||||
const url = `${BASE_URL}/workflow`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ user_id: userId, ...workflowData }),
|
||||
};
|
||||
return await fetchWithHandling(url, options);
|
||||
}
|
||||
|
||||
export async function postBackupWorkflow(userId, workflowData) {
|
||||
const url = `${BASE_URL}/workflow/backup`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ user_id: userId, ...workflowData }),
|
||||
};
|
||||
return await fetchWithHandling(url, options);
|
||||
}
|
||||
|
||||
export async function getLatestWorkflow() {
|
||||
const url = `${BASE_URL}/workflow`;
|
||||
return await fetchWithHandling(url);
|
||||
}
|
||||
|
||||
export async function getPermissionById(userId) {
|
||||
const url = `${BASE_URL}/permission/${userId}`;
|
||||
return await fetchWithHandling(url);
|
||||
}
|
||||
|
||||
export async function postPermissionById(userId, adminId, data, token) {
|
||||
const url = `${BASE_URL}/permission/${userId}`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': token,
|
||||
},
|
||||
body: JSON.stringify({ admin_id: adminId, data }),
|
||||
};
|
||||
return await fetchWithHandling(url, options);
|
||||
}
|
||||
|
||||
export async function nexusLogin(uuid, account, password) {
|
||||
const url = `${BASE_URL}/login`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ uuid, account, password }),
|
||||
};
|
||||
return await fetchWithHandling(url, options);
|
||||
}
|
||||
|
||||
export async function nexusVerify(token) {
|
||||
const url = `${BASE_URL}/verify`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ token }),
|
||||
};
|
||||
return await fetchWithHandling(url, options);
|
||||
}
|
||||
|
||||
export async function getNameById(userId) {
|
||||
const url = `${BASE_URL}/name/${userId}`;
|
||||
return await fetchWithHandling(url);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
import { DynamicElement } from "./nexus.elements.js"
|
||||
import { StyleInjector } from "./nexus.styles.js";
|
||||
|
||||
let chatMain = new DynamicElement({
|
||||
id: 'nexus-chat-main',
|
||||
tag: 'div',
|
||||
visible: true,
|
||||
styles: {
|
||||
width: '512px',
|
||||
minHeight: '170px',
|
||||
maxHeight: '170px',
|
||||
overflow: 'hidden',
|
||||
padding: '16px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid rgba(94,94,94,0.5)'
|
||||
},
|
||||
hoverStyles: {
|
||||
overflowY: 'scroll'
|
||||
},
|
||||
afterChildrenAppend: function (childrens, me) {
|
||||
if (childrens.length > 50) {
|
||||
childrens[0].removeElement()
|
||||
childrens.shift()
|
||||
}
|
||||
me.el.scrollTop = me.el.scrollHeight
|
||||
}
|
||||
})
|
||||
|
||||
let inputMain = new DynamicElement({
|
||||
id: 'nexus-input-main',
|
||||
tag: 'div',
|
||||
visible: true,
|
||||
styles: {
|
||||
width: '100%',
|
||||
height: '32px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
childrens: [
|
||||
new DynamicElement({
|
||||
id: 'nexus-input-label',
|
||||
tag: 'span',
|
||||
visible: false,
|
||||
styles: {
|
||||
fontFamily: 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
||||
display: 'block',
|
||||
fontSize: '14px',
|
||||
lineHeight: '14px',
|
||||
fontWeight: 600,
|
||||
color: 'white',
|
||||
padding: '0',
|
||||
margin: '0',
|
||||
textShadow: '1pt 1pt #000',
|
||||
},
|
||||
childrens: ['Say:']
|
||||
}),
|
||||
new DynamicElement({
|
||||
id: 'nexus-input',
|
||||
tag: 'input',
|
||||
visible: false,
|
||||
styles: {
|
||||
fontFamily: 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
||||
fontSize: '14px',
|
||||
lineHeight: '14px',
|
||||
fontWeight: 600,
|
||||
width: '100%',
|
||||
color: 'white',
|
||||
padding: '0',
|
||||
margin: '0',
|
||||
border: 'none',
|
||||
background: "transparent",
|
||||
flexGrow: 1,
|
||||
outline: 'none',
|
||||
textIndent: '4pt',
|
||||
caretColor: 'transparent',
|
||||
textShadow: '1pt 1pt #000',
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
let chatWindow = new DynamicElement({
|
||||
id: 'nexus-chat-window',
|
||||
tag: 'div',
|
||||
styles: {
|
||||
position: 'absolute',
|
||||
left: "16px",
|
||||
top: '100px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
zIndex: 1000
|
||||
},
|
||||
childrens: [
|
||||
new DynamicElement({
|
||||
id: "nexus-total-players",
|
||||
tag: "div",
|
||||
visible: true,
|
||||
styles: {
|
||||
fontFamily: 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
||||
fontSize: '14px',
|
||||
lineHeight: '14px',
|
||||
fontWeight: 600,
|
||||
left: "16px",
|
||||
top: '80px',
|
||||
color: "#a5a5a5",
|
||||
width: "100%",
|
||||
paddingBottom: "12px",
|
||||
textAlign: "right"
|
||||
},
|
||||
childrens: ['Users Online: ', new DynamicElement({ id: "nexus-total-players-count", visible: true, tag: "span", childrens:['0'] })]
|
||||
}),
|
||||
chatMain,
|
||||
inputMain,
|
||||
new StyleInjector({
|
||||
styles: {
|
||||
'#nexus-chat-main::-webkit-scrollbar': {
|
||||
'display': 'none',
|
||||
'scrollbar-width': 'none',
|
||||
'-ms-overflow-style': 'none',
|
||||
},
|
||||
"#nexus-chat-window" : {
|
||||
// for pointer events
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
let addChatWindowMessage = function (subject, message, subject_color, message_color, merge) {
|
||||
let msg = ""
|
||||
if (merge) {
|
||||
msg = `<span style="color: ${subject_color ? subject_color : "#fff"};">${subject}</span> <span style="color: ${message_color ? message_color : "#fff"};">${message}</span>`
|
||||
}
|
||||
else {
|
||||
msg = `<span style="color: ${subject_color ? subject_color : "#fff"};">${subject}</span>: <span style="color: ${message_color ? message_color : "#fff"};">${message}</span>`
|
||||
}
|
||||
|
||||
let chatElement = new DynamicElement({
|
||||
tag: 'span',
|
||||
visible: true,
|
||||
styles: {
|
||||
fontFamily: 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
||||
display: 'block',
|
||||
fontSize: '14px',
|
||||
lineHeight: '14px',
|
||||
fontWeight: 600,
|
||||
width: '100%',
|
||||
color: 'white',
|
||||
padding: '0',
|
||||
margin: '0',
|
||||
textShadow: '1pt 1pt #000',
|
||||
marginBottom: '1pt',
|
||||
lineBreak: 'anywhere'
|
||||
},
|
||||
childrens: [msg],
|
||||
})
|
||||
chatMain.addChild(chatElement)
|
||||
}
|
||||
|
||||
export { chatMain, inputMain, chatWindow, addChatWindowMessage }
|
||||
@@ -0,0 +1,737 @@
|
||||
import { nexusLogin, nexusVerify, getPermissionById, postPermissionById, getUserSpecificWorkflowsMeta, getUserSpecificWorkflow } from "./nexus.api.js"
|
||||
import { addChatWindowMessage, inputMain, chatWindow } from "./nexus.chat.js"
|
||||
import { UserPanel } from "./nexus.panel.js"
|
||||
import { WorkflowPanel } from "./nexus.workflow.panel.js"
|
||||
|
||||
export class NexusCommands {
|
||||
|
||||
constructor(app, api, nexusSocket, color, mouseManger, workflowManager, promptControl) {
|
||||
|
||||
this.color = color
|
||||
|
||||
this.colors = {
|
||||
"join": "#ff6e4d",
|
||||
"info": "#fba510",
|
||||
"personal": "#fba510",
|
||||
"name": "#00fa00",
|
||||
"none": "#ffffff"
|
||||
}
|
||||
|
||||
this.inputMain = inputMain
|
||||
this.nexusSocket = nexusSocket
|
||||
|
||||
this.app = app
|
||||
this.api = api
|
||||
|
||||
this.workflowManager = workflowManager
|
||||
this.promptControl = promptControl
|
||||
this.mouseManger = mouseManger
|
||||
|
||||
this.nexusSocket.onConnected.push(this.handleConnect.bind(this))
|
||||
this.nexusSocket.onMessageReceive.push(this.handleMessageReceive.bind(this))
|
||||
|
||||
this.inputMain.addEventListener('message', this.handleMessage.bind(this))
|
||||
|
||||
document.addEventListener('keydown', this.handleChatWindow.bind(this));
|
||||
|
||||
this.disableComfyThings = this.disableComfyThings.bind(this);
|
||||
this.enableComfyThings = this.enableComfyThings.bind(this);
|
||||
this.disableGraphSelectionWithExtremePrejudice = this.disableGraphSelectionWithExtremePrejudice.bind(this);
|
||||
|
||||
this.comfyThings = [
|
||||
'#extraOptions',
|
||||
'#queue-button',
|
||||
'#comfy-save-button',
|
||||
'#comfy-file-input',
|
||||
'#comfy-clear-button',
|
||||
'#comfy-load-default-button',
|
||||
'.queue-tab-button.side-bar-button',
|
||||
'.queue-tab-button.side-bar-button',
|
||||
'#comfy-refresh-button',
|
||||
'#queue-front-button',
|
||||
'#comfy-view-queue-button',
|
||||
'#comfy-view-history-button'
|
||||
];
|
||||
|
||||
chatWindow.childrens[3].softUpdate('#nexus-chat-window', 'pointer-events', 'none')
|
||||
chatWindow.show()
|
||||
|
||||
setInterval((chatWindow) => {
|
||||
chatWindow.childrens[0].childrens[1].el.textContent = Object.keys(this.nexusSocket.userManager.users).length
|
||||
}, 1000, chatWindow);
|
||||
|
||||
this.up = new UserPanel()
|
||||
this.up.onCommandReceived = this.onPanelCommandReceived.bind(this)
|
||||
|
||||
this.wp = new WorkflowPanel()
|
||||
this.wp.onCommandReceived = this.onWFPanelCommandReceived.bind(this)
|
||||
|
||||
this.spectating = null
|
||||
this.lastkey = null
|
||||
|
||||
}
|
||||
|
||||
timeAgo(timeString) {
|
||||
const now = new Date(); // current time
|
||||
const time = new Date(timeString); // input time
|
||||
|
||||
const diff = Math.floor((now - time) / 1000); // difference in seconds
|
||||
|
||||
const minutes = Math.floor(diff / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
|
||||
if (minutes < 1) return "just now";
|
||||
if (minutes === 1) return "1 minute ago";
|
||||
if (minutes < 60) return `${minutes} minutes ago`;
|
||||
if (hours === 1) return "1 hour ago";
|
||||
if (hours < 24) return `${hours} hours ago`;
|
||||
if (days === 1) return "1 day ago";
|
||||
|
||||
return `${days} days ago`;
|
||||
}
|
||||
|
||||
async verifyLogin() {
|
||||
|
||||
this.disableComfyThings()
|
||||
|
||||
let token = localStorage.getItem('nexus-socket-token');
|
||||
if (token) {
|
||||
let user = await nexusVerify(token)
|
||||
if (user && this.nexusSocket.uuid == user.user_id) {
|
||||
this.enableComfyThings()
|
||||
this.promptControl.handleControl(true)
|
||||
this.promptControl.handleEditor(true)
|
||||
this.workflowManager.handleWorkflowEvents(true)
|
||||
this.nexusSocket.admin = true
|
||||
addChatWindowMessage("", "You are now an admin!", this.color.info, this.colors.personal, true)
|
||||
this.workflowManager.saveManager(60)
|
||||
}
|
||||
else {
|
||||
this.promptControl.handleControl(false)
|
||||
this.promptControl.handleEditor(false)
|
||||
this.workflowManager.handleWorkflowEvents(false)
|
||||
this.workflowManager.removeSaveManager()
|
||||
addChatWindowMessage("", "You are a viewer!", this.color.info, this.colors.info, true)
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
let permissions = await getPermissionById(this.nexusSocket.uuid)
|
||||
|
||||
if (permissions) {
|
||||
|
||||
let editor = permissions.editor ? permissions.editor : false
|
||||
let queue = permissions.queue ? permissions.queue : false
|
||||
|
||||
this.workflowManager.handleWorkflowEvents(editor)
|
||||
|
||||
this.promptControl.handleEditor(editor)
|
||||
this.promptControl.handleControl(queue)
|
||||
|
||||
this.nexusSocket.queue = queue
|
||||
this.nexusSocket.editor = editor
|
||||
|
||||
if (editor) {
|
||||
this.workflowManager.saveManager(60)
|
||||
}
|
||||
else {
|
||||
this.workflowManager.removeSaveManager()
|
||||
}
|
||||
|
||||
addChatWindowMessage("", `Permission: Workflow Editor ${editor ? "Yes" : "No"} | Queue Prompt ${queue ? "Yes" : "No"} `, this.color.info, this.colors.personal, true)
|
||||
|
||||
if (queue) {
|
||||
addChatWindowMessage("", `You can now queue prompt press CTRL+Enter`, this.color.info, this.colors.personal, true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.workflowManager.loadLatestWorkflow()
|
||||
}
|
||||
|
||||
handleConnect() {
|
||||
|
||||
let name = this.nexusSocket.userManager.get(this.nexusSocket.uuid, "name")
|
||||
this.nexusSocket.sendMessage('chat-join', { name }, false)
|
||||
this.verifyLogin()
|
||||
|
||||
}
|
||||
|
||||
findChildrenAfterHr(parentClass) {
|
||||
const parentDiv = document.querySelector(`.${parentClass}`);
|
||||
const childElementsAfterHr = [];
|
||||
if (parentDiv) {
|
||||
const hr = parentDiv.querySelector('hr');
|
||||
if (hr) {
|
||||
let sibling = hr.nextElementSibling;
|
||||
while (sibling) {
|
||||
childElementsAfterHr.push(sibling);
|
||||
sibling = sibling.nextElementSibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
return childElementsAfterHr;
|
||||
}
|
||||
|
||||
disableGraphSelectionWithExtremePrejudice() {
|
||||
if (!this.nexusSocket.admin && !this.nexusSocket.editor) {
|
||||
if (this.app.graph && this.app.graph.list_of_graphcanvas && this.app.graph.list_of_graphcanvas.length > 0) {
|
||||
this.app.graph.list_of_graphcanvas[0].deselectAllNodes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disableComfyThings() {
|
||||
|
||||
this.comfyThings.forEach((selector) => {
|
||||
const elements = document.querySelectorAll(selector);
|
||||
elements.forEach((element) => {
|
||||
element.disabled = true;
|
||||
element.style.pointerEvents = 'none';
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.comfy-settings-btn').forEach(button => {
|
||||
button.disabled = true;
|
||||
button.style.pointerEvents = 'none';
|
||||
});
|
||||
|
||||
const children = this.findChildrenAfterHr('comfy-menu');
|
||||
|
||||
children.forEach(element => {
|
||||
if (element.tagName === 'BUTTON' || element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA') {
|
||||
element.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelector(`.comfy-menu`).style.display = 'none';
|
||||
document.querySelector(`.comfy-menu`).style.pointerEvents = 'none';
|
||||
|
||||
|
||||
clearInterval(this.disableComfyThingsTimer)
|
||||
|
||||
this.disableComfyThingsTimer = setInterval(() => {
|
||||
|
||||
|
||||
this.comfyThings.forEach((selector) => {
|
||||
const elements = document.querySelectorAll(selector);
|
||||
elements.forEach((element) => {
|
||||
element.disabled = true;
|
||||
element.style.pointerEvents = 'none';
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.comfy-settings-btn').forEach(button => {
|
||||
button.disabled = true;
|
||||
button.style.pointerEvents = 'none';
|
||||
});
|
||||
|
||||
const children = this.findChildrenAfterHr('comfy-menu');
|
||||
|
||||
children.forEach(element => {
|
||||
if (element.tagName === 'BUTTON' || element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA') {
|
||||
element.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelector(`.comfy-menu`).style.display = 'none';
|
||||
document.querySelector(`.comfy-menu`).style.pointerEvents = 'none';
|
||||
|
||||
this.promptControl.handleEditor(this.nexusSocket.editor ? this.nexusSocket.editor : false)
|
||||
this.promptControl.handleControl(this.nexusSocket.queue ? this.nexusSocket.queue : false)
|
||||
|
||||
}, 500);
|
||||
|
||||
this.disableGraphSelectionWithExtremePrejudiceTimer = setInterval(this.disableGraphSelectionWithExtremePrejudice, 50)
|
||||
|
||||
}
|
||||
|
||||
enableComfyThings() {
|
||||
|
||||
clearInterval(this.disableComfyThingsTimer)
|
||||
clearInterval(this.disableGraphSelectionWithExtremePrejudiceTimer)
|
||||
|
||||
this.comfyThings.forEach((selector) => {
|
||||
const elements = document.querySelectorAll(selector);
|
||||
elements.forEach((element) => {
|
||||
element.disabled = false;
|
||||
element.style.pointerEvents = 'all';
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.comfy-settings-btn').forEach(button => {
|
||||
button.disabled = false;
|
||||
button.style.pointerEvents = 'all';
|
||||
});
|
||||
|
||||
const children = this.findChildrenAfterHr('comfy-menu');
|
||||
|
||||
children.forEach(element => {
|
||||
if (element.tagName === 'BUTTON' || element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA') {
|
||||
element.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelector(`.comfy-menu`).style.display = 'flex';
|
||||
document.querySelector(`.comfy-menu`).style.pointerEvents = 'all';
|
||||
|
||||
}
|
||||
|
||||
|
||||
handleChatWindow(event) {
|
||||
|
||||
let key = event.key
|
||||
let label = inputMain.childrens[0]
|
||||
let input = inputMain.childrens[1]
|
||||
|
||||
let active = {
|
||||
tag: document.activeElement.tagName,
|
||||
el: document.activeElement,
|
||||
chat: document.activeElement == input.el
|
||||
}
|
||||
|
||||
function activateChat() {
|
||||
let allowedTags = ['INPUT', 'TEXTAREA']
|
||||
if (!allowedTags.includes(active.tag) && input.visible == false) {
|
||||
event.preventDefault();
|
||||
label.show()
|
||||
input.show()
|
||||
input.el.focus()
|
||||
input.el.value = ""
|
||||
input.focusTimer = setInterval((parent, input) => {
|
||||
if (document.activeElement != input && parent.visible == true)
|
||||
input.focus()
|
||||
}, 500, input, input.el);
|
||||
chatWindow.childrens[3].softUpdate('#nexus-chat-window', 'pointer-events', 'all')
|
||||
}
|
||||
}
|
||||
|
||||
function deactivateChat() {
|
||||
if (!active.chat) return;
|
||||
label.hide()
|
||||
input.el.blur();
|
||||
input.el.value = ""
|
||||
input.hide()
|
||||
clearTimeout(input.focusTimer)
|
||||
chatWindow.childrens[3].softUpdate('#nexus-chat-window', 'pointer-events', 'none')
|
||||
}
|
||||
|
||||
function sendMessage() {
|
||||
if (!active.chat) return;
|
||||
let message = input.el.value
|
||||
inputMain.dispatchEvent(new CustomEvent('message', { detail: { message } }))
|
||||
deactivateChat()
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case 't':
|
||||
case 'T':
|
||||
activateChat()
|
||||
this.togglePlayerTab(true)
|
||||
this.toggleWFTab(true)
|
||||
break;
|
||||
case 'Escape':
|
||||
deactivateChat()
|
||||
break;
|
||||
case 'Enter':
|
||||
sendMessage()
|
||||
break;
|
||||
case 'p':
|
||||
case 'P':
|
||||
if (event.altKey && event.shiftKey) {
|
||||
if (!active.chat)
|
||||
this.togglePlayerTab()
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
case 'O':
|
||||
if (event.altKey && event.shiftKey) {
|
||||
if (!active.chat)
|
||||
this.toggleWFTab()
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return active
|
||||
}
|
||||
|
||||
toggleWFTab(forceHide, forceReload) {
|
||||
if (this.nexusSocket.admin || this.nexusSocket.editor) {
|
||||
|
||||
if (forceHide) {
|
||||
this.wp.active = 0
|
||||
this.wp.panel.hide()
|
||||
} else if (forceReload) {
|
||||
this.prepareWFPanel()
|
||||
}
|
||||
else {
|
||||
if (this.wp.active == 0) {
|
||||
this.wp.active = 1
|
||||
this.wp.panel.show()
|
||||
this.prepareWFPanel()
|
||||
} else if (this.wp.active == 1) {
|
||||
this.wp.active = 0
|
||||
this.wp.panel.hide()
|
||||
}
|
||||
}
|
||||
|
||||
if (this.wp.active == 1) {
|
||||
this.workflowManager.removeSaveManager()
|
||||
}
|
||||
else {
|
||||
this.workflowManager.saveManager(this.workflowManager.saveTime || 60)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
togglePlayerTab(forceHide, forceReload) {
|
||||
|
||||
if (forceHide) {
|
||||
this.up.active = 0
|
||||
this.up.panel.hide()
|
||||
} else if (forceReload) {
|
||||
this.preparePanel(this.nexusSocket.admin)
|
||||
} else {
|
||||
if (this.up.active == 0) {
|
||||
this.up.active = 1
|
||||
this.up.panel.show()
|
||||
this.preparePanel(this.nexusSocket.admin)
|
||||
} else if (this.up.active == 1) {
|
||||
this.up.active = 0
|
||||
this.up.panel.hide()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async onWFPanelCommandReceived(kind, label, data, active) {
|
||||
|
||||
switch (kind) {
|
||||
case 'button':
|
||||
if (label == "load") {
|
||||
let wfid = data // workflowid is data
|
||||
let wf = await getUserSpecificWorkflow(this.nexusSocket.uuid, wfid)
|
||||
if (wf) {
|
||||
wf.clear = true
|
||||
let message = {
|
||||
name: "workflow",
|
||||
from: this.nexusSocket.uuid,
|
||||
data: { workflow: wf }
|
||||
}
|
||||
this.nexusSocket.onMessageReceive.forEach(f => { f(message); });
|
||||
if (this.nexusSocket.admin) {
|
||||
this.nexusSocket.sendMessage('workflow', { workflow: wf }, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async onPanelCommandReceived(kind, label, data, active) {
|
||||
|
||||
switch (kind) {
|
||||
case 'button':
|
||||
if (label == "spectate") {
|
||||
this.nexusSocket.sendMessage("spectate", { on: active, receiver: data }, false)
|
||||
if (active == 1) {
|
||||
this.spectating = data // userid is data
|
||||
}
|
||||
else {
|
||||
this.spectating = null
|
||||
}
|
||||
} else if (label == "editor") {
|
||||
if (this.nexusSocket.admin) {
|
||||
let token = localStorage.getItem('nexus-socket-token');
|
||||
await postPermissionById(data, this.nexusSocket.uuid, { editor: active ? true : false }, token) // userid is data
|
||||
this.nexusSocket.sendMessage("permission", { receiver: data }, false)
|
||||
}
|
||||
} else if (label == "queue") {
|
||||
if (this.nexusSocket.admin) {
|
||||
let token = localStorage.getItem('nexus-socket-token');
|
||||
await postPermissionById(data, this.nexusSocket.uuid, { queue: active ? true : false }, token) // userid is data
|
||||
this.nexusSocket.sendMessage("permission", { receiver: data }, false)
|
||||
}
|
||||
}
|
||||
else if (label == "mouse") {
|
||||
this.nexusSocket.sendMessage("mouse_view_toggle", { on: active, receiver: data }, false)
|
||||
this.nexusSocket.userManager.set(data, 'hide_mouse', active)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async prepareWFPanel() {
|
||||
|
||||
const columns = ['Name', 'Created', 'Action'];
|
||||
|
||||
this.wp.addColumnsToPanel(columns);
|
||||
|
||||
let meta = await getUserSpecificWorkflowsMeta(this.nexusSocket.uuid)
|
||||
|
||||
const rows = [];
|
||||
const callbacks = [];
|
||||
const values = [];
|
||||
const data = [];
|
||||
|
||||
for (let id = 0; id < meta.length; id++) {
|
||||
|
||||
const file = meta[id];
|
||||
|
||||
let fn = file.file_name + ""
|
||||
|
||||
let name = (fn.includes('lt') ? 'Long Term ' : 'Short Term ')
|
||||
// let time = file.last_saved.split(':')
|
||||
// time = time.join("|")
|
||||
|
||||
// console.log(time)
|
||||
let time = this.timeAgo(file.last_saved)
|
||||
|
||||
rows.push([name, time, 'button:load'])
|
||||
callbacks.push([null, null, this.wp.onclick])
|
||||
values.push([null, null, '0'])
|
||||
data.push([null, null, file.file_name])
|
||||
|
||||
}
|
||||
|
||||
this.wp.addRowsToPanel(rows, callbacks, values, data);
|
||||
}
|
||||
|
||||
async preparePanel(admin) {
|
||||
|
||||
const columns = ['Name', 'Actions', 'Online'];
|
||||
this.up.addColumnsToPanel(columns);
|
||||
|
||||
const users = this.nexusSocket.userManager.users;
|
||||
const usersIds = Object.keys(users);
|
||||
|
||||
const rows = [];
|
||||
const callbacks = [];
|
||||
const values = [];
|
||||
const data = [];
|
||||
|
||||
for (const id of usersIds) {
|
||||
|
||||
const user = users[id];
|
||||
const name = id === this.nexusSocket.uuid ? `${user.name} (You)` : user.name;
|
||||
const self = id === this.nexusSocket.uuid;
|
||||
|
||||
const status = user.status === "online" || self ? '🟢' : '🔴';
|
||||
const hide_mouse = user.hide_mouse ? 0 : 1;
|
||||
const permissions = await getPermissionById(id);
|
||||
const spectating = id == this.spectating ? 1 : 0
|
||||
|
||||
callbacks.push([null, self ? 'none' : this.up.onclick, null]);
|
||||
|
||||
let powers = 'button:spectate,button:editor,button:queue,button:mouse'
|
||||
|
||||
if (!admin) {
|
||||
powers = 'button:spectate,button:mouse'
|
||||
}
|
||||
|
||||
rows.push([name, self ? 'button:none' : powers, status]);
|
||||
|
||||
let row_values = `${spectating},${permissions.editor ? 1 : 0},${permissions.queue ? 1 : 0},${hide_mouse}`
|
||||
if (!admin) {
|
||||
row_values = `${spectating},${hide_mouse}`
|
||||
}
|
||||
|
||||
values.push([null, row_values, null]);
|
||||
|
||||
let row_data = `${id},${id},${id},${id}`
|
||||
if (!admin) {
|
||||
row_data = `${id},${id}`
|
||||
}
|
||||
|
||||
data.push([null, row_data, null]);
|
||||
|
||||
}
|
||||
|
||||
this.up.addRowsToPanel(rows, callbacks, values, data);
|
||||
}
|
||||
|
||||
handleMessageReceive(message) {
|
||||
|
||||
let name = message.name;
|
||||
let from = message.from;
|
||||
let data = message.data
|
||||
|
||||
// console.log(name, data, from)
|
||||
|
||||
if (name == "chat") {
|
||||
|
||||
let user_name = this.nexusSocket.userManager.get(from, "name") || "User";
|
||||
let mouse = this.nexusSocket.userManager.get(from, "mouse") || "#0f0";
|
||||
let message = data.message
|
||||
addChatWindowMessage(user_name, message, mouse[3], this.colors.none)
|
||||
|
||||
} else if (name == "nick") {
|
||||
|
||||
let old_name = data.old_name
|
||||
let new_name = data.new_name
|
||||
|
||||
addChatWindowMessage(old_name + " is now known as ", new_name, this.colors.personal, this.colors.personal, true)
|
||||
this.nexusSocket.userManager.set(from, "name", new_name)
|
||||
|
||||
} else if (name == "chat-join") {
|
||||
|
||||
let name = data.name
|
||||
addChatWindowMessage(name + " joined the server", "", this.colors.personal, this.colors.personal, true)
|
||||
this.nexusSocket.userManager.set(from, "name", name)
|
||||
|
||||
name = this.nexusSocket.userManager.get(this.nexusSocket.uuid, "name")
|
||||
this.nexusSocket.sendMessage('name', { name }, false)
|
||||
|
||||
if (this.up.active) {
|
||||
this.togglePlayerTab(false, true)
|
||||
}
|
||||
|
||||
} else if (name == "name") {
|
||||
|
||||
let name = data.name
|
||||
this.nexusSocket.userManager.set(from, "name", name)
|
||||
//console.log("Received name:", name)
|
||||
|
||||
if (this.up.active) {
|
||||
this.togglePlayerTab(false, true)
|
||||
}
|
||||
|
||||
} else if (name == 'afk' || name == 'online') {
|
||||
|
||||
this.nexusSocket.userManager.set(from, "status", name)
|
||||
|
||||
if (this.up.active) {
|
||||
this.togglePlayerTab(false, true)
|
||||
}
|
||||
|
||||
} else if (name == 'permission') {
|
||||
|
||||
|
||||
// window.location.reload()
|
||||
|
||||
this.verifyLogin()
|
||||
|
||||
if (this.up.active) {
|
||||
this.togglePlayerTab(false, true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async handleMessage(evt) {
|
||||
|
||||
let message = evt.detail.message
|
||||
message = this.cleanMessage(message)
|
||||
|
||||
if (message) {
|
||||
|
||||
let startsWithCommand = this.startsWithCommand(message)
|
||||
|
||||
if (startsWithCommand) {
|
||||
let parts = this.extractCommandAndValue(message)
|
||||
let cmds = { 'nick': true, 'login': true, 'logout': true }
|
||||
let cmd = parts.command
|
||||
let values = parts.values
|
||||
|
||||
if (cmds[cmd]) {
|
||||
switch (cmd) {
|
||||
case 'nick':
|
||||
let new_name = this.cleanMessage(values[0], 16)
|
||||
let old_name = this.nexusSocket.userManager.get(this.nexusSocket.uuid, "name") || "User"
|
||||
if (!new_name) {
|
||||
addChatWindowMessage("", "Name is invalid", this.colors.info, this.colors.info, true)
|
||||
}
|
||||
else {
|
||||
localStorage.setItem('nexus-socket-name', new_name);
|
||||
this.nexusSocket.sendMessage('nick', { old_name, new_name })
|
||||
}
|
||||
break;
|
||||
case 'login':
|
||||
|
||||
let account = this.cleanMessage(values[0], 16)
|
||||
let password = this.cleanMessage(values[1], 16)
|
||||
let uuid = this.nexusSocket.uuid
|
||||
let token = await nexusLogin(uuid, account, password)
|
||||
if (token) {
|
||||
localStorage.setItem('nexus-socket-token', token.token);
|
||||
// addChatWindowMessage("", "You are now an admin!", this.color.info, this.colors.info, true)
|
||||
this.verifyLogin()
|
||||
// window.location.reload()
|
||||
}
|
||||
else {
|
||||
addChatWindowMessage("", "Admin account/password invalid!", this.color.info, this.colors.info, true)
|
||||
}
|
||||
break;
|
||||
case 'logout':
|
||||
let usertoken = localStorage.getItem('nexus-socket-token')
|
||||
if (this.nexusSocket.admin) {
|
||||
await postPermissionById(this.nexusSocket.uuid, this.nexusSocket.uuid, { admin: false, queue: false, editor: false }, usertoken)
|
||||
localStorage.removeItem('nexus-socket-token');
|
||||
window.location.reload()
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
addChatWindowMessage("You", message, this.color, this.colors.none)
|
||||
this.nexusSocket.sendMessage('chat', { message }, false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cleanMessage(message, length = 512) {
|
||||
message = message.substr(0, length);
|
||||
if (message.length === 0) {
|
||||
return null;
|
||||
} else {
|
||||
let msg = "";
|
||||
const nonAsciiRe = /[^\x00-\x7F]/;
|
||||
const emojiRe = /[\u{1F600}-\u{1F64F}]|[\u{1F300}-\u{1F5FF}]|[\u{1F680}-\u{1F6FF}]|[\u{1F700}-\u{1F77F}]|[\u{1F780}-\u{1F7FF}]|[\u{1F800}-\u{1F8FF}]|[\u{1F900}-\u{1F9FF}]|[\u{1FA00}-\u{1FA6F}]|[\u{1FA70}-\u{1FAFF}]/u;
|
||||
const htmlTagRe = /<\/?[^>]+(>|$)/g;
|
||||
message = message.replace(htmlTagRe, '');
|
||||
for (let char of message) {
|
||||
if (!(nonAsciiRe.test(char) && !emojiRe.test(char))) {
|
||||
msg += char;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
startsWithCommand(str) {
|
||||
const commandPattern = /^\/\w+(\s+\S+)?/;
|
||||
return commandPattern.test(str);
|
||||
}
|
||||
|
||||
extractCommandAndValue(str) {
|
||||
const commandPattern = /^\/(\w+)\s*(.*)/;
|
||||
const match = str.match(commandPattern);
|
||||
if (match) {
|
||||
const command = match[1];
|
||||
const values = match[2].trim() ? match[2].split(/\s+/) : [];
|
||||
return {
|
||||
command,
|
||||
values
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
export class NexusPromptControl extends EventTarget {
|
||||
|
||||
constructor(api, app, nexusSocket) {
|
||||
|
||||
super()
|
||||
|
||||
this.app = app
|
||||
this.api = api
|
||||
this.runningNodeId = null
|
||||
this.nexusSocket = nexusSocket
|
||||
this.comfy_events = ['b_preview', 'progress', 'executing', 'executed', 'execution_start', 'execution_error', 'execution_cached'];
|
||||
|
||||
let oqp = api.queuePrompt;
|
||||
this.apiWrapper = {
|
||||
oqp: oqp,
|
||||
enableApi: false,
|
||||
api: api,
|
||||
qp: async function (number, data) {
|
||||
if (!this.enableApi) {
|
||||
return {
|
||||
"node_errors": {}
|
||||
};
|
||||
}
|
||||
return this.oqp.call(this.api, number, { output: data.output, workflow: data.workflow });
|
||||
}
|
||||
};
|
||||
|
||||
this.api.queuePrompt = this.apiWrapper.qp.bind(this.apiWrapper);
|
||||
this.nexusSocket.onMessageReceive.push(this.handleMessageReceive.bind(this))
|
||||
|
||||
this.addEventListeners();
|
||||
this.comfy_events.forEach((event) => {
|
||||
this.api.addEventListener(event, this.handleComfyEvents.bind(this));
|
||||
});
|
||||
|
||||
this.handleProgressDraw()
|
||||
this.handleControl(false)
|
||||
this.handleEditor(false)
|
||||
|
||||
}
|
||||
|
||||
handleProgressDraw() {
|
||||
|
||||
const origDrawNodeShape = LGraphCanvas.prototype.drawNodeShape;
|
||||
const self = this.app;
|
||||
const self2 = this;
|
||||
|
||||
LGraphCanvas.prototype.drawNodeShape = function (node, ctx, size, fgcolor, bgcolor, selected, mouse_over) {
|
||||
|
||||
const res = origDrawNodeShape.apply(this, arguments);
|
||||
const nodeErrors = self.lastNodeErrors?.[node.id];
|
||||
|
||||
let color = null;
|
||||
let lineWidth = 1;
|
||||
|
||||
if (node.id === +self2.runningNodeId) {
|
||||
color = "#0f0";
|
||||
} else if (self.dragOverNode && node.id === self.dragOverNode.id) {
|
||||
color = "dodgerblue";
|
||||
}
|
||||
else if (nodeErrors?.errors) {
|
||||
color = "red";
|
||||
lineWidth = 2;
|
||||
}
|
||||
else if (self.lastExecutionError && self.lastExecutionError.node_id === node.id) {
|
||||
color = "#f0f";
|
||||
lineWidth = 2;
|
||||
}
|
||||
|
||||
if (color) {
|
||||
const shape = node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE;
|
||||
ctx.lineWidth = lineWidth;
|
||||
ctx.globalAlpha = 0.8;
|
||||
ctx.beginPath();
|
||||
if (shape == LiteGraph.BOX_SHAPE)
|
||||
ctx.rect(-6, -6 - LiteGraph.NODE_TITLE_HEIGHT, 12 + size[0] + 1, 12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT);
|
||||
else if (shape == LiteGraph.ROUND_SHAPE || (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed))
|
||||
ctx.roundRect(
|
||||
-6,
|
||||
-6 - LiteGraph.NODE_TITLE_HEIGHT,
|
||||
12 + size[0] + 1,
|
||||
12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT,
|
||||
this.round_radius * 2
|
||||
);
|
||||
else if (shape == LiteGraph.CARD_SHAPE)
|
||||
ctx.roundRect(
|
||||
-6,
|
||||
-6 - LiteGraph.NODE_TITLE_HEIGHT,
|
||||
12 + size[0] + 1,
|
||||
12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT,
|
||||
[this.round_radius * 2, this.round_radius * 2, 2, 2]
|
||||
);
|
||||
else if (shape == LiteGraph.CIRCLE_SHAPE)
|
||||
ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5 + 6, 0, Math.PI * 2);
|
||||
ctx.strokeStyle = color;
|
||||
ctx.stroke();
|
||||
ctx.strokeStyle = fgcolor;
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
|
||||
if (self.progress && node.id === +self2.runningNodeId) {
|
||||
ctx.fillStyle = "green";
|
||||
ctx.fillRect(0, 0, size[0] * (self.progress.value / self.progress.max), 6);
|
||||
ctx.fillStyle = bgcolor;
|
||||
}
|
||||
|
||||
// Highlight inputs that failed validation
|
||||
if (nodeErrors) {
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = "red";
|
||||
for (const error of nodeErrors.errors) {
|
||||
if (error.extra_info && error.extra_info.input_name) {
|
||||
const inputIndex = node.findInputSlot(error.extra_info.input_name)
|
||||
if (inputIndex !== -1) {
|
||||
let pos = node.getConnectionPos(true, inputIndex);
|
||||
ctx.beginPath();
|
||||
ctx.arc(pos[0] - node.pos[0], pos[1] - node.pos[1], 12, 0, 2 * Math.PI, false)
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
handleMessageReceive(message) {
|
||||
try {
|
||||
this.dispatchEvent(new CustomEvent(message.name, { detail: message }));
|
||||
} catch (e) {
|
||||
console.error('Error handling message:', e);
|
||||
}
|
||||
}
|
||||
|
||||
handleComfyEvents(evt) {
|
||||
if (evt.type === "b_preview") {
|
||||
this.blobToBase64(evt.detail).then((result) => {
|
||||
this.nexusSocket.sendMessage(evt.type, { detail: result }, false)
|
||||
});
|
||||
} else {
|
||||
this.nexusSocket.sendMessage(evt.type, { detail: evt.detail }, false)
|
||||
}
|
||||
}
|
||||
|
||||
addEventListeners() {
|
||||
|
||||
this.addEventListener("progress", ({ detail }) => {
|
||||
detail = detail.data.detail
|
||||
this.app.progress = detail;
|
||||
this.app.graph.setDirtyCanvas(true, false);
|
||||
});
|
||||
|
||||
this.addEventListener("executing", ({ detail }) => {
|
||||
detail = detail.data.detail
|
||||
this.app.progress = null;
|
||||
this.runningNodeId = detail;
|
||||
this.app.graph.setDirtyCanvas(true, false);
|
||||
delete this.app.nodePreviewImages[this.runningNodeId];
|
||||
});
|
||||
|
||||
this.addEventListener("executed", ({ detail }) => {
|
||||
detail = detail.data.detail
|
||||
const output = this.app.nodeOutputs[detail.node];
|
||||
if (detail.merge && output) {
|
||||
for (const k in detail.output ?? {}) {
|
||||
const v = output[k];
|
||||
if (v instanceof Array) {
|
||||
output[k] = v.concat(detail.output[k]);
|
||||
} else {
|
||||
output[k] = detail.output[k];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.app.nodeOutputs[detail.node] = detail.output;
|
||||
}
|
||||
const node = this.app.graph.getNodeById(detail.node);
|
||||
if (node && node.onExecuted) {
|
||||
node.onExecuted(detail.output);
|
||||
}
|
||||
});
|
||||
|
||||
this.addEventListener("execution_start", ({ detail }) => {
|
||||
detail = detail.data.detail
|
||||
this.runningNodeId = null;
|
||||
this.app.lastExecutionError = null;
|
||||
this.app.graph._nodes.forEach((node) => {
|
||||
if (node.onExecutionStart) {
|
||||
node.onExecutionStart();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.addEventListener("execution_error", ({ detail }) => {
|
||||
detail = detail.data.detail
|
||||
this.app.lastExecutionError = detail;
|
||||
const formattedError = this.formatExecutionError(detail);
|
||||
this.app.ui.dialog.show(formattedError);
|
||||
this.app.canvas.draw(true, true);
|
||||
});
|
||||
|
||||
this.addEventListener("b_preview", ({ detail }) => {
|
||||
detail = detail.data.detail
|
||||
const id = this.runningNodeId;
|
||||
if (id == null) return;
|
||||
const blob = this.base64ToBlob(detail);
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
this.app.nodePreviewImages[id] = [blobUrl];
|
||||
});
|
||||
}
|
||||
|
||||
blobToBase64(blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
resolve(reader.result);
|
||||
};
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
}
|
||||
|
||||
base64ToBlob(dataUrl) {
|
||||
const [header, base64] = dataUrl.split(',');
|
||||
const mime = header.match(/:(.*?);/)[1];
|
||||
const byteCharacters = atob(base64);
|
||||
const byteNumbers = new Array(byteCharacters.length);
|
||||
for (let i = 0; i < byteCharacters.length; i++) {
|
||||
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
||||
}
|
||||
const byteArray = new Uint8Array(byteNumbers);
|
||||
return new Blob([byteArray], { type: mime });
|
||||
}
|
||||
|
||||
formatExecutionError(error) {
|
||||
if (error == null) {
|
||||
return "(unknown error)";
|
||||
}
|
||||
|
||||
const traceback = error.traceback.join("");
|
||||
const nodeId = error.node_id;
|
||||
const nodeType = error.node_type;
|
||||
|
||||
return `Error occurred when executing ${nodeType}:\n\n${error.exception_message}\n\n${traceback}`;
|
||||
}
|
||||
|
||||
handleEditor(oN) {
|
||||
|
||||
this.app.canvas.allow_dragnodes = oN;
|
||||
this.app.canvas.allow_interaction = oN;
|
||||
this.app.canvas.allow_reconnect_links = oN;
|
||||
this.app.canvas.allow_searchbox = oN;
|
||||
|
||||
}
|
||||
|
||||
handleControl(oN) {
|
||||
this.apiWrapper.enableApi = oN;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
import { StyleInjector } from "./nexus.styles.js";
|
||||
|
||||
export class DynamicElement extends EventTarget {
|
||||
id = '';
|
||||
tag = '';
|
||||
styles = {};
|
||||
hoverStyles = {};
|
||||
focusStyles = {};
|
||||
activeStyles = {};
|
||||
attrs = {};
|
||||
childrens = [];
|
||||
events = {};
|
||||
appendTo = document.body;
|
||||
visible = false;
|
||||
afterChildrenAppend = null;
|
||||
afterCreated = null;
|
||||
defaultDisplay = 'initial';
|
||||
el = null;
|
||||
|
||||
constructor({
|
||||
id,
|
||||
tag,
|
||||
styles = {},
|
||||
hoverStyles = {},
|
||||
focusStyles = {},
|
||||
activeStyles = {},
|
||||
attrs = {},
|
||||
childrens = [],
|
||||
events = {},
|
||||
appendTo = document.body,
|
||||
afterChildrenAppend = null,
|
||||
afterCreated = null,
|
||||
visible = false
|
||||
} = {}) {
|
||||
|
||||
super()
|
||||
|
||||
this.id = id;
|
||||
this.tag = tag;
|
||||
this.styles = styles;
|
||||
this.hoverStyles = hoverStyles;
|
||||
this.focusStyles = focusStyles;
|
||||
this.activeStyles = activeStyles;
|
||||
this.attrs = attrs;
|
||||
this.childrens = childrens;
|
||||
this.events = events;
|
||||
this.appendTo = appendTo;
|
||||
this.afterChildrenAppend = afterChildrenAppend;
|
||||
this.afterCreated = afterCreated;
|
||||
this.visible = visible
|
||||
|
||||
this.createElement();
|
||||
}
|
||||
|
||||
createElement() {
|
||||
if (!this.tag) {
|
||||
throw new Error('Tag is required to create an element.');
|
||||
}
|
||||
|
||||
this.el = document.createElement(this.tag);
|
||||
if (this.id) this.el.id = this.id;
|
||||
this.appendTo.appendChild(this.el);
|
||||
|
||||
this.addStyles();
|
||||
this.addAttrs();
|
||||
this.addChildrens();
|
||||
this.addEvents();
|
||||
this.addStateStyles();
|
||||
|
||||
if (!this.visible) {
|
||||
this.hide()
|
||||
}
|
||||
|
||||
if (this.afterCreated) {
|
||||
this.afterCreated(this)
|
||||
}
|
||||
}
|
||||
|
||||
removeElement() {
|
||||
if (!this.el) return;
|
||||
this.removeAttrs();
|
||||
this.removeEvents();
|
||||
this.removeStyles();
|
||||
this.removeChildrens();
|
||||
this.el.remove();
|
||||
this.el = null;
|
||||
}
|
||||
|
||||
updateStyles(styles = {}) {
|
||||
this.styles = { ...this.styles, ...styles };
|
||||
this.addStyles();
|
||||
}
|
||||
|
||||
addStyles() {
|
||||
if (!this.el) return;
|
||||
Object.entries(this.styles).forEach(([style, value]) => {
|
||||
if (style === 'display') {
|
||||
this.defaultDisplay = value;
|
||||
}
|
||||
this.el.style[style] = value;
|
||||
});
|
||||
}
|
||||
|
||||
removeStyles() {
|
||||
if (!this.el) return;
|
||||
Object.keys(this.styles).forEach(style => {
|
||||
this.el.style[style] = '';
|
||||
});
|
||||
this.styles = {};
|
||||
}
|
||||
|
||||
updateAttrs(attrs = {}) {
|
||||
this.attrs = { ...this.attrs, ...attrs };
|
||||
this.addAttrs();
|
||||
}
|
||||
|
||||
addAttrs() {
|
||||
if (!this.el) return;
|
||||
Object.entries(this.attrs).forEach(([attr, value]) => {
|
||||
this.el.setAttribute(attr, value);
|
||||
});
|
||||
}
|
||||
|
||||
removeAttrs() {
|
||||
if (!this.el) return;
|
||||
Object.keys(this.attrs).forEach(attr => {
|
||||
this.el.removeAttribute(attr);
|
||||
});
|
||||
this.attrs = {};
|
||||
}
|
||||
|
||||
addChildrens() {
|
||||
if (!this.el) return;
|
||||
this.childrens.forEach(child => {
|
||||
if (typeof child === 'string') {
|
||||
this.el.insertAdjacentHTML('beforeend', child);
|
||||
} else if (child instanceof Node) {
|
||||
this.el.appendChild(child);
|
||||
} else if (child instanceof StyleInjector) {
|
||||
this.el.appendChild(child.el);
|
||||
} else if (child instanceof DynamicElement) {
|
||||
this.el.appendChild(child.el);
|
||||
}
|
||||
});
|
||||
if (this.afterChildrenAppend) {
|
||||
requestAnimationFrame(() => this.afterChildrenAppend(this.childrens, this));
|
||||
}
|
||||
}
|
||||
|
||||
addChild(child) {
|
||||
if (!this.el) return;
|
||||
if (typeof child === 'string') {
|
||||
this.el.insertAdjacentHTML('beforeend', child);
|
||||
} else if (child instanceof Node) {
|
||||
this.el.appendChild(child);
|
||||
} else if (child instanceof DynamicElement) {
|
||||
this.el.appendChild(child.el);
|
||||
}
|
||||
if(!this.childrens.includes(child)) {
|
||||
this.childrens.push(child)
|
||||
}
|
||||
if (this.afterChildrenAppend) {
|
||||
this.afterChildrenAppend(this.childrens, this);
|
||||
}
|
||||
}
|
||||
|
||||
removeChildrens() {
|
||||
if (!this.el) return;
|
||||
this.el.innerHTML = '';
|
||||
this.childrens = [];
|
||||
}
|
||||
|
||||
addEvents() {
|
||||
if (!this.el) return;
|
||||
Object.entries(this.events).forEach(([event, handler]) => {
|
||||
this.el.addEventListener(event, (e) => { handler(e, this.parent) });
|
||||
});
|
||||
}
|
||||
|
||||
removeEvents() {
|
||||
if (!this.el) return;
|
||||
Object.entries(this.events).forEach(([event, handler]) => {
|
||||
this.el.removeEventListener(event, handler);
|
||||
});
|
||||
this.events = {};
|
||||
}
|
||||
|
||||
show() {
|
||||
if (!this.el) return;
|
||||
this.el.style.display = this.defaultDisplay;
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (!this.el) return;
|
||||
this.el.style.display = 'none';
|
||||
this.visible = false;
|
||||
}
|
||||
|
||||
addStateStyles() {
|
||||
if (!this.el) return;
|
||||
|
||||
this.el.addEventListener('mouseenter', () => this.applyStyles(this.hoverStyles));
|
||||
this.el.addEventListener('mouseleave', () => this.applyStyles(this.styles));
|
||||
this.el.addEventListener('focus', () => this.applyStyles(this.focusStyles), true);
|
||||
this.el.addEventListener('blur', () => this.applyStyles(this.styles), true);
|
||||
this.el.addEventListener('mousedown', () => this.applyStyles(this.activeStyles));
|
||||
this.el.addEventListener('mouseup', () => this.applyStyles(this.styles));
|
||||
}
|
||||
|
||||
applyStyles(stateStyles) {
|
||||
if (!this.visible) return
|
||||
Object.entries(stateStyles).forEach(([style, value]) => {
|
||||
this.el.style[style] = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,693 @@
|
||||
LiteGraph.use_uuids = true
|
||||
LiteGraph.uuidv4 = function() { return Date.now() }
|
||||
|
||||
LGraph.prototype.getGroupByUUID = function (uuid) {
|
||||
let groups = this._groups
|
||||
let group = null
|
||||
for (let index = 0; index < groups.length; index++) {
|
||||
let tgroup = groups[index];
|
||||
if (tgroup.uuid && tgroup.uuid == uuid) {
|
||||
group = tgroup
|
||||
break
|
||||
}
|
||||
}
|
||||
return group
|
||||
}
|
||||
|
||||
LGraph.prototype.getGroupIndexByUUID = function (uuid) {
|
||||
let groups = this._groups
|
||||
let at = -1
|
||||
for (let index = 0; index < groups.length; index++) {
|
||||
let tgroup = groups[index];
|
||||
if (tgroup.uuid && tgroup.uuid == uuid) {
|
||||
at = index
|
||||
break
|
||||
}
|
||||
}
|
||||
return at
|
||||
}
|
||||
|
||||
|
||||
LGraph.prototype.add = function (node, skip_compute_order) {
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
//groups
|
||||
if (node.constructor === LGraphGroup) {
|
||||
this._groups.push(node);
|
||||
this.setDirtyCanvas(true);
|
||||
this.change();
|
||||
if(!node.uuid) {
|
||||
node.uuid = LiteGraph.uuidv4()
|
||||
}
|
||||
node.graph = this;
|
||||
this._version++;
|
||||
if (this.onGroupAdded)
|
||||
this.onGroupAdded(node)
|
||||
return;
|
||||
}
|
||||
|
||||
//nodes
|
||||
if (node.id != -1 && this._nodes_by_id[node.id] != null) {
|
||||
console.warn(
|
||||
"Nexus LiteGraph: there is already a node with this ID, will reuse it"
|
||||
);
|
||||
if (LiteGraph.use_uuids) {
|
||||
node.id = LiteGraph.uuidv4()
|
||||
}
|
||||
else {
|
||||
node.id = ++this.last_node_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) {
|
||||
throw "LiteGraph: max number of nodes in a graph reached";
|
||||
}
|
||||
|
||||
//give him an id
|
||||
if (LiteGraph.use_uuids) {
|
||||
if (node.id == null || node.id == -1)
|
||||
node.id = LiteGraph.uuidv4();
|
||||
|
||||
let uuid = localStorage.getItem('nexus-socket-uuid');
|
||||
if (uuid) {
|
||||
node.properties.owner = uuid
|
||||
}
|
||||
this.last_node_id = node.id;
|
||||
}
|
||||
else {
|
||||
if (node.id == null || node.id == -1) {
|
||||
node.id = ++this.last_node_id;
|
||||
} else if (this.last_node_id < node.id) {
|
||||
this.last_node_id = node.id;
|
||||
}
|
||||
}
|
||||
|
||||
node.graph = this;
|
||||
this._version++;
|
||||
|
||||
this._nodes.push(node);
|
||||
this._nodes_by_id[node.id] = node;
|
||||
|
||||
if (node.onAdded) {
|
||||
node.onAdded(this);
|
||||
}
|
||||
|
||||
if (this.config.align_to_grid) {
|
||||
node.alignToGrid();
|
||||
}
|
||||
|
||||
if (!skip_compute_order) {
|
||||
this.updateExecutionOrder();
|
||||
}
|
||||
|
||||
if (this.onNodeAdded) {
|
||||
this.onNodeAdded(node);
|
||||
}
|
||||
|
||||
this.setDirtyCanvas(true);
|
||||
this.change();
|
||||
|
||||
return node; //to chain actions
|
||||
};
|
||||
|
||||
LGraph.prototype.addNexus = function (node, skip_compute_order) {
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
//groups
|
||||
if (node.constructor === LGraphGroup) {
|
||||
this._groups.push(node);
|
||||
this.setDirtyCanvas(true);
|
||||
this.change();
|
||||
// node.id = LiteGraph.uuidv4();
|
||||
node.graph = this;
|
||||
this._version++;
|
||||
return;
|
||||
}
|
||||
|
||||
// //nodes
|
||||
// if (node.id != -1 && this._nodes_by_id[node.id] != null) {
|
||||
// console.warn(
|
||||
// "Nexus LiteGraph: there is already a node with this ID, will reuse it"
|
||||
// );
|
||||
// if (LiteGraph.use_uuids) {
|
||||
// node.id = LiteGraph.uuidv4();
|
||||
// }
|
||||
// else {
|
||||
// node.id = ++this.last_node_id;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) {
|
||||
throw "LiteGraph: max number of nodes in a graph reached";
|
||||
}
|
||||
|
||||
//give him an id
|
||||
// if (LiteGraph.use_uuids) {
|
||||
// if (node.id == null || node.id == -1)
|
||||
// let uuid = localStorage.getItem('nexus-socket-uuid');
|
||||
// if (uuid) {
|
||||
// node.properties.owner = uuid
|
||||
// }
|
||||
// node.id = LiteGraph.uuidv4();
|
||||
// }
|
||||
// else {
|
||||
// if (node.id == null || node.id == -1) {
|
||||
// node.id = ++this.last_node_id;
|
||||
// } else if (this.last_node_id < node.id) {
|
||||
this.last_node_id = node.id;
|
||||
// }
|
||||
// }
|
||||
|
||||
node.graph = this;
|
||||
this._version++;
|
||||
|
||||
this._nodes.push(node);
|
||||
this._nodes_by_id[node.id] = node;
|
||||
|
||||
if (this.config.align_to_grid) {
|
||||
node.alignToGrid();
|
||||
}
|
||||
|
||||
if (!skip_compute_order) {
|
||||
this.updateExecutionOrder();
|
||||
}
|
||||
|
||||
if (this.onNodeAddedNexus) {
|
||||
this.onNodeAddedNexus(node);
|
||||
}
|
||||
|
||||
this.setDirtyCanvas(true);
|
||||
this.change();
|
||||
|
||||
return node; //to chain actions
|
||||
};
|
||||
|
||||
LGraph.prototype.remove = function (node) {
|
||||
if (node.constructor === LiteGraph.LGraphGroup) {
|
||||
var index = this._groups.indexOf(node);
|
||||
if (index != -1) {
|
||||
this._groups.splice(index, 1);
|
||||
}
|
||||
node.graph = null;
|
||||
this._version++;
|
||||
this.setDirtyCanvas(true, true);
|
||||
this.change();
|
||||
if (this.onGroupRemoved)
|
||||
this.onGroupRemoved(node)
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._nodes_by_id[node.id] == null) {
|
||||
return;
|
||||
} //not found
|
||||
|
||||
if (node.ignore_remove) {
|
||||
return;
|
||||
} //cannot be removed
|
||||
|
||||
this.beforeChange(); //sure? - almost sure is wrong
|
||||
|
||||
//disconnect inputs
|
||||
if (node.inputs) {
|
||||
for (var i = 0; i < node.inputs.length; i++) {
|
||||
var slot = node.inputs[i];
|
||||
if (slot.link != null) {
|
||||
node.disconnectInput(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//disconnect outputs
|
||||
if (node.outputs) {
|
||||
for (var i = 0; i < node.outputs.length; i++) {
|
||||
var slot = node.outputs[i];
|
||||
if (slot.links != null && slot.links.length) {
|
||||
node.disconnectOutput(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//node.id = -1; //why?
|
||||
|
||||
//callback
|
||||
if (node.onRemoved) {
|
||||
node.onRemoved();
|
||||
}
|
||||
|
||||
node.graph = null;
|
||||
this._version++;
|
||||
|
||||
//remove from canvas render
|
||||
if (this.list_of_graphcanvas) {
|
||||
for (var i = 0; i < this.list_of_graphcanvas.length; ++i) {
|
||||
var canvas = this.list_of_graphcanvas[i];
|
||||
if (canvas.selected_nodes[node.id]) {
|
||||
delete canvas.selected_nodes[node.id];
|
||||
}
|
||||
if (canvas.node_dragged == node) {
|
||||
canvas.node_dragged = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//remove from containers
|
||||
var pos = this._nodes.indexOf(node);
|
||||
if (pos != -1) {
|
||||
this._nodes.splice(pos, 1);
|
||||
}
|
||||
delete this._nodes_by_id[node.id];
|
||||
|
||||
if (this.onNodeRemoved) {
|
||||
this.onNodeRemoved(node);
|
||||
}
|
||||
|
||||
//close panels
|
||||
this.sendActionToCanvas("checkPanels");
|
||||
|
||||
this.setDirtyCanvas(true, true);
|
||||
this.afterChange(); //sure? - almost sure is wrong
|
||||
this.change();
|
||||
|
||||
this.updateExecutionOrder();
|
||||
};
|
||||
|
||||
LGraph.prototype.removeNexus = function (node) {
|
||||
|
||||
if (node.constructor === LiteGraph.LGraphGroup) {
|
||||
var index = this._groups.indexOf(node);
|
||||
if (index != -1) {
|
||||
this._groups.splice(index, 1);
|
||||
}
|
||||
node.graph = null;
|
||||
this._version++;
|
||||
this.setDirtyCanvas(true, true);
|
||||
this.change();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._nodes_by_id[node.id] == null) {
|
||||
return;
|
||||
} //not found
|
||||
|
||||
if (node.ignore_remove) {
|
||||
return;
|
||||
} //cannot be removed
|
||||
|
||||
this.beforeChange(); //sure? - almost sure is wrong
|
||||
|
||||
//disconnect inputs
|
||||
if (node.inputs) {
|
||||
for (var i = 0; i < node.inputs.length; i++) {
|
||||
var slot = node.inputs[i];
|
||||
if (slot.link != null) {
|
||||
node.disconnectInput(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//disconnect outputs
|
||||
if (node.outputs) {
|
||||
for (var i = 0; i < node.outputs.length; i++) {
|
||||
var slot = node.outputs[i];
|
||||
if (slot.links != null && slot.links.length) {
|
||||
node.disconnectOutput(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//node.id = -1; //why?
|
||||
|
||||
//callback
|
||||
if (node.onRemoved) {
|
||||
node.onRemoved();
|
||||
}
|
||||
|
||||
node.graph = null;
|
||||
this._version++;
|
||||
|
||||
//remove from canvas render
|
||||
if (this.list_of_graphcanvas) {
|
||||
for (var i = 0; i < this.list_of_graphcanvas.length; ++i) {
|
||||
var canvas = this.list_of_graphcanvas[i];
|
||||
if (canvas.selected_nodes[node.id]) {
|
||||
delete canvas.selected_nodes[node.id];
|
||||
}
|
||||
if (canvas.node_dragged == node) {
|
||||
canvas.node_dragged = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//remove from containers
|
||||
var pos = this._nodes.indexOf(node);
|
||||
if (pos != -1) {
|
||||
this._nodes.splice(pos, 1);
|
||||
}
|
||||
delete this._nodes_by_id[node.id];
|
||||
|
||||
//close panels
|
||||
this.sendActionToCanvas("checkPanels");
|
||||
|
||||
this.setDirtyCanvas(true, true);
|
||||
this.afterChange(); //sure? - almost sure is wrong
|
||||
this.change();
|
||||
|
||||
this.updateExecutionOrder();
|
||||
};
|
||||
|
||||
LGraphNode.prototype.configureNexus = function (info) {
|
||||
if (this.graph) {
|
||||
this.graph._version++;
|
||||
}
|
||||
for (var j in info) {
|
||||
if (j == "properties") {
|
||||
//i don't want to clone properties, I want to reuse the old container
|
||||
for (var k in info.properties) {
|
||||
this.properties[k] = info.properties[k];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (info[j] == null) {
|
||||
continue;
|
||||
} else if (typeof info[j] == "object") {
|
||||
//object
|
||||
if (this[j] && this[j].configure) {
|
||||
this[j].configure(info[j]);
|
||||
} else {
|
||||
this[j] = LiteGraph.cloneObject(info[j], this[j]);
|
||||
}
|
||||
} //value
|
||||
else {
|
||||
this[j] = info[j];
|
||||
}
|
||||
}
|
||||
|
||||
if (!info.title) {
|
||||
this.title = this.constructor.title;
|
||||
}
|
||||
|
||||
if (this.inputs) {
|
||||
for (var i = 0; i < this.inputs.length; ++i) {
|
||||
var input = this.inputs[i];
|
||||
if (this.onInputAdded)
|
||||
this.onInputAdded(input);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.outputs) {
|
||||
for (var i = 0; i < this.outputs.length; ++i) {
|
||||
var output = this.outputs[i];
|
||||
if (!output.links) {
|
||||
continue;
|
||||
}
|
||||
if (this.onOutputAdded)
|
||||
this.onOutputAdded(output);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.widgets) {
|
||||
for (var i = 0; i < this.widgets.length; ++i) {
|
||||
var w = this.widgets[i];
|
||||
if (!w)
|
||||
continue;
|
||||
if (w.options && w.options.property && (this.properties[w.options.property] != undefined))
|
||||
w.value = JSON.parse(JSON.stringify(this.properties[w.options.property]));
|
||||
}
|
||||
if (info.widgets_values) {
|
||||
for (var i = 0; i < info.widgets_values.length; ++i) {
|
||||
if (this.widgets[i]) {
|
||||
this.widgets[i].value = info.widgets_values[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.onConfigure) {
|
||||
this.onConfigure(info);
|
||||
}
|
||||
};
|
||||
|
||||
LGraphCanvas.prototype.selectNodesNexus = function (nodes, add_to_current_selection) {
|
||||
if (!add_to_current_selection) {
|
||||
this.deselectAllNodes();
|
||||
}
|
||||
|
||||
nodes = nodes || this.graph._nodes;
|
||||
if (typeof nodes == "string") nodes = [nodes];
|
||||
for (var i in nodes) {
|
||||
var node = nodes[i];
|
||||
if (node.is_selected) {
|
||||
this.deselectNode(node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!node.is_selected && node.onSelected) {
|
||||
node.onSelected();
|
||||
}
|
||||
node.is_selected = true;
|
||||
this.selected_nodes[node.id] = node;
|
||||
|
||||
if (node.inputs) {
|
||||
for (var j = 0; j < node.inputs.length; ++j) {
|
||||
this.highlighted_links[node.inputs[j].link] = true;
|
||||
}
|
||||
}
|
||||
if (node.outputs) {
|
||||
for (var j = 0; j < node.outputs.length; ++j) {
|
||||
var out = node.outputs[j];
|
||||
if (out.links) {
|
||||
for (var k = 0; k < out.links.length; ++k) {
|
||||
this.highlighted_links[out.links[k]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setDirty(true);
|
||||
};
|
||||
|
||||
LGraphCanvas.prototype.deselectNodeNexus = function(node) {
|
||||
|
||||
if (!node.is_selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.onDeselected) {
|
||||
node.onDeselected();
|
||||
}
|
||||
|
||||
node.is_selected = false;
|
||||
|
||||
//remove highlighted
|
||||
if (node.inputs) {
|
||||
for (var i = 0; i < node.inputs.length; ++i) {
|
||||
delete this.highlighted_links[node.inputs[i].link];
|
||||
}
|
||||
}
|
||||
if (node.outputs) {
|
||||
for (var i = 0; i < node.outputs.length; ++i) {
|
||||
var out = node.outputs[i];
|
||||
if (out.links) {
|
||||
for (var j = 0; j < out.links.length; ++j) {
|
||||
delete this.highlighted_links[out.links[j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LGraphGroup.prototype.move = function (deltax, deltay, ignore_nodes) {
|
||||
this._pos[0] += deltax;
|
||||
this._pos[1] += deltay;
|
||||
if (ignore_nodes) {
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < this._nodes.length; ++i) {
|
||||
var node = this._nodes[i];
|
||||
node.pos[0] += deltax;
|
||||
node.pos[1] += deltay;
|
||||
if (this.onGroupNodeMoved)
|
||||
this.onGroupNodeMoved(node)
|
||||
}
|
||||
if (this.onGroupMoved)
|
||||
this.onGroupMoved(this)
|
||||
};
|
||||
|
||||
LGraphGroup.prototype.configure = function (o) {
|
||||
this.title = o.title;
|
||||
this.owner = o.owner;
|
||||
this.uuid = o.uuid;
|
||||
this._bounding.set(o.bounding);
|
||||
this.color = o.color;
|
||||
if (o.font_size) {
|
||||
this.font_size = o.font_size;
|
||||
}
|
||||
};
|
||||
|
||||
LGraphGroup.prototype.serialize = function () {
|
||||
var b = this._bounding;
|
||||
return {
|
||||
title: this.title,
|
||||
bounding: [
|
||||
Math.round(b[0]),
|
||||
Math.round(b[1]),
|
||||
Math.round(b[2]),
|
||||
Math.round(b[3])
|
||||
],
|
||||
uuid: this.uuid,
|
||||
color: this.color,
|
||||
font_size: this.font_size
|
||||
};
|
||||
};
|
||||
|
||||
LGraph.prototype.addNode = function (nodeData) {
|
||||
let node = LiteGraph.createNode(nodeData.type, nodeData.title);
|
||||
if (!node) {
|
||||
console.warn("Node type not found: " + nodeData.type);
|
||||
return false;
|
||||
}
|
||||
node.id = nodeData.id;
|
||||
node.configureNexus(nodeData);
|
||||
this.addNexus(node);
|
||||
return true;
|
||||
};
|
||||
|
||||
LGraph.prototype.removeNode = function (data) {
|
||||
let node = this.getNodeById(data.id);
|
||||
if (!node) {
|
||||
console.warn("Node ID not found: " + data);
|
||||
return false;
|
||||
}
|
||||
this.removeNexus(node);
|
||||
return true;
|
||||
};
|
||||
|
||||
LGraph.prototype.addLink = function (linkData) {
|
||||
let link = new LiteGraph.LLink();
|
||||
link.configure(linkData);
|
||||
this.links[link.id] = link;
|
||||
return;
|
||||
};
|
||||
|
||||
LGraph.prototype.addGroup = function (data) {
|
||||
var group = new LiteGraph.LGraphGroup();
|
||||
group.configure(data);
|
||||
this._groups.push(group);
|
||||
this.setDirtyCanvas(true);
|
||||
this.change();
|
||||
group.graph = this;
|
||||
this._version++;
|
||||
if (this.onGroupAdded)
|
||||
this.onGroupAdded(group)
|
||||
return;
|
||||
};
|
||||
|
||||
LGraph.prototype.removeGroup = function (group) {
|
||||
let existingGroupIndex = this.getGroupIndexByUUID(group.uuid);
|
||||
if (existingGroupIndex != -1) {
|
||||
this._groups.splice(existingGroupIndex, 1);
|
||||
}
|
||||
group.graph = null;
|
||||
this._version++;
|
||||
this.setDirtyCanvas(true, true);
|
||||
this.change();
|
||||
return;
|
||||
};
|
||||
|
||||
LGraph.prototype.updateNode = function (nodeData, type = "all") {
|
||||
let existingNode = this.getNodeById(nodeData.id);
|
||||
if (existingNode) {
|
||||
if (type == "all") {
|
||||
existingNode.configureNexus(nodeData);
|
||||
} else if (type == "widgets") {
|
||||
if (existingNode.widgets) {
|
||||
for (var i = 0; i < existingNode.widgets.length; ++i) {
|
||||
var w = existingNode.widgets[i];
|
||||
if (!w)
|
||||
continue;
|
||||
if (w.options && w.options.property && (existingNode.properties[w.options.property] != undefined))
|
||||
w.value = JSON.parse(JSON.stringify(existingNode.properties[w.options.property]));
|
||||
}
|
||||
if (nodeData.widgets_values) {
|
||||
for (var i = 0; i < nodeData.widgets_values.length; ++i) {
|
||||
if (existingNode.widgets[i]) {
|
||||
existingNode.widgets[i].value = nodeData.widgets_values[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.addNode(nodeData);
|
||||
}
|
||||
};
|
||||
|
||||
LGraph.prototype.updateLink = function (linkData) {
|
||||
|
||||
if (linkData) {
|
||||
|
||||
let id
|
||||
|
||||
if (linkData instanceof Object) {
|
||||
id = linkData.id
|
||||
} else {
|
||||
id = linkData[0]
|
||||
}
|
||||
|
||||
let existingLink = this.links[id];
|
||||
if (existingLink) {
|
||||
existingLink.configure(linkData);
|
||||
} else {
|
||||
this.addLink(linkData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LGraph.prototype.updateLinkManual = function (change, linkData, node, graph) {
|
||||
if (change == "add") {
|
||||
this.updateLink(linkData)
|
||||
} else if (change == "remove") {
|
||||
let existingLink = this.links[linkData[0]];
|
||||
if (existingLink) {
|
||||
this.removeLink(linkData[0])
|
||||
delete this.links[linkData[0]]
|
||||
}
|
||||
}
|
||||
if (node) {
|
||||
this.updateNode(node)
|
||||
}
|
||||
graph.updateExecutionOrder();
|
||||
graph._version++;
|
||||
graph.setDirtyCanvas(true, true);
|
||||
};
|
||||
|
||||
LGraph.prototype.updateGroup = function (groupData) {
|
||||
let existingGroup = this.getGroupByUUID(groupData.uuid);
|
||||
if (existingGroup) {
|
||||
existingGroup.configure(groupData);
|
||||
} else {
|
||||
this.addGroup(groupData);
|
||||
}
|
||||
};
|
||||
|
||||
LGraph.prototype.updateNodes = function (newNodes) {
|
||||
newNodes.forEach(nodeData => this.updateNode(nodeData));
|
||||
};
|
||||
|
||||
LGraph.prototype.updateLinks = function (newLinks) {
|
||||
newLinks.forEach(linkData => this.updateLink(linkData));
|
||||
};
|
||||
|
||||
LGraph.prototype.updateGroups = function (newGroups) {
|
||||
newGroups.forEach(groupData => this.updateGroup(groupData));
|
||||
};
|
||||
@@ -0,0 +1,190 @@
|
||||
export class NexusMouseManager {
|
||||
|
||||
hexColors = ["#1abc9c","#2ecc71","#3498db","#9b59b6","#f1c40f","#e67e22","#e74c3c","#ecf0f1"]
|
||||
|
||||
createMouseSvg(color) {
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 96 96" shape-rendering="geometricPrecision" text-rendering="geometricPrecision"
|
||||
width="96" height="96">
|
||||
<path d="M22.311034,9.174631v81.070373L48,71.061684l35.530843-8.173761L22.311034,9.174631Z"
|
||||
transform="translate(0-1.709817)" stroke="${color}" fill="${color}" stroke-width="7" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</svg>`
|
||||
}
|
||||
|
||||
svgStringToImage(svgString) {
|
||||
const blob = new Blob([svgString], { type: 'image/svg+xml' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const image = new Image();
|
||||
image.src = url;
|
||||
return image;
|
||||
}
|
||||
|
||||
hex2rgb(hex) {
|
||||
const r = parseInt(hex.slice(1, 3), 16);
|
||||
const g = parseInt(hex.slice(3, 5), 16);
|
||||
const b = parseInt(hex.slice(5, 7), 16);
|
||||
return { r, g, b };
|
||||
}
|
||||
|
||||
getRandomHexColor() {
|
||||
return this.hexColors[Math.floor(Math.random() * this.hexColors.length)];
|
||||
}
|
||||
|
||||
constructor(nexusSocket, app, fps) {
|
||||
|
||||
this.color = this.getRandomHexColor()
|
||||
|
||||
this.app = app
|
||||
this.fps = fps
|
||||
|
||||
this.canSendMouse = false
|
||||
|
||||
this.nexusSocket = nexusSocket
|
||||
this.nexusSocket.onConnected.push(this.handleConnect.bind(this))
|
||||
this.nexusSocket.onMessageReceive.push(this.handleMessageReceive.bind(this))
|
||||
|
||||
document.addEventListener("visibilitychange", this.handleVisiblityChange.bind(this))
|
||||
document.addEventListener('mousemove', this.handleSendMouseControl.bind(this));
|
||||
this.app.graph.list_of_graphcanvas[0].onDrawForeground = this.handlePointerDraw.bind(this)
|
||||
|
||||
setInterval(this.handleSendMouse.bind(this), 1000 / this.fps)
|
||||
|
||||
}
|
||||
|
||||
throttle(func, limit) {
|
||||
let lastFunc;
|
||||
let lastRan;
|
||||
return function (...args) {
|
||||
const context = this;
|
||||
if (!lastRan) {
|
||||
func.apply(context, args);
|
||||
lastRan = Date.now();
|
||||
} else {
|
||||
clearTimeout(lastFunc);
|
||||
lastFunc = setTimeout(function () {
|
||||
if ((Date.now() - lastRan) >= limit) {
|
||||
func.apply(context, args);
|
||||
lastRan = Date.now();
|
||||
}
|
||||
}, limit - (Date.now() - lastRan));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
handleCanvasDraw() {
|
||||
if (this.app.graph && this.app.graph.list_of_graphcanvas && this.app.graph.list_of_graphcanvas.length > 0) {
|
||||
this.app.graph.list_of_graphcanvas[0].draw(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
handleConnect() {
|
||||
this.drawTimer = setInterval(this.handleCanvasDraw.bind(this), 1000 / this.fps)
|
||||
}
|
||||
|
||||
handleMessageReceive(message) {
|
||||
|
||||
let name = message.name;
|
||||
let from = message.from;
|
||||
let data = message.data
|
||||
|
||||
if (name == "mouse") {
|
||||
let mouse = this.nexusSocket.userManager.get(from, "mouse");
|
||||
let pointer = this.nexusSocket.userManager.get(from, "pointer");
|
||||
if (!pointer) {
|
||||
this.nexusSocket.userManager.set(from, "pointer", this.svgStringToImage(this.createMouseSvg(data.mouse[3])));
|
||||
} else if (pointer && mouse && mouse[3] != data.mouse[3]) {
|
||||
this.nexusSocket.userManager.set(from, "pointer", this.svgStringToImage(this.createMouseSvg(data.mouse[3])));
|
||||
}
|
||||
this.nexusSocket.userManager.set(from, "mouse", data.mouse)
|
||||
} else if (name == "hide_mouse") {
|
||||
this.nexusSocket.userManager.set(from, "mouse", null)
|
||||
} else if (name== "mouse_view_toggle") {
|
||||
this.nexusSocket.userManager.set(from, "mouse_view", data.on)
|
||||
if(data.on == 0){
|
||||
this.nexusSocket.sendMessage("hide_mouse", { receiver: from }, false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
handleVisiblityChange() {
|
||||
if (document.visibilityState === "hidden") {
|
||||
clearInterval(this.drawTimer)
|
||||
this.nexusSocket.sendMessage("hide_mouse", {}, false)
|
||||
this.nexusSocket.sendMessage("afk", {}, false)
|
||||
} else {
|
||||
this.handleConnect()
|
||||
this.nexusSocket.sendMessage("online", {}, false)
|
||||
}
|
||||
}
|
||||
|
||||
handleSendMouse = function () {
|
||||
if (this.app.graph && this.app.graph.list_of_graphcanvas && this.app.graph.list_of_graphcanvas.length > 0) {
|
||||
this.mouse = this.app.graph.list_of_graphcanvas[0].canvas_mouse
|
||||
this.scale = this.app.graph.list_of_graphcanvas[0].ds.scale
|
||||
}
|
||||
this.mouse[3] = this.color
|
||||
this.mouse[4] = this.scale / 10
|
||||
|
||||
if (this.canSendMouse) {
|
||||
let users = Object.keys(this.nexusSocket.userManager.users)
|
||||
for (let index = 0; index < users.length; index++) {
|
||||
let user_id = users[index]
|
||||
let view = this.nexusSocket.userManager.get(user_id, "mouse_view");
|
||||
if(view != null) {
|
||||
if(view == 1) {
|
||||
this.nexusSocket.sendMessage('mouse', { mouse: this.mouse, receiver: user_id }, false)
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.nexusSocket.sendMessage('mouse', { mouse: this.mouse, receiver: user_id }, false)
|
||||
}
|
||||
}
|
||||
this.canSendMouse = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
handleSendMouseControl() {
|
||||
this.canSendMouse = true
|
||||
}
|
||||
|
||||
handlePointerDraw = function (ctx) {
|
||||
Object.keys(this.nexusSocket.userManager.users).forEach(id => {
|
||||
if (id != this.nexusSocket.uuid) {
|
||||
|
||||
let mouse = this.nexusSocket.userManager.get(id, "mouse");
|
||||
let name = this.nexusSocket.userManager.get(id, "name") || "User";
|
||||
let pointerImage = this.nexusSocket.userManager.get(id, "pointer");
|
||||
let hidden = this.nexusSocket.userManager.get(id, "mouse_hidden");
|
||||
|
||||
if(hidden == 0) {
|
||||
mouse = null
|
||||
}
|
||||
|
||||
if (mouse && mouse.length > 0) {
|
||||
|
||||
let mouseX = mouse[0] - 4;
|
||||
let mouseY = mouse[1] - 4;
|
||||
let scale = 1 - mouse[4];
|
||||
|
||||
ctx.drawImage(pointerImage, mouseX, mouseY, 18, 18);
|
||||
ctx.font = '14px Arial';
|
||||
ctx.fillStyle = mouse[3];
|
||||
ctx.fillText(name, mouseX + 22, mouseY + 16);
|
||||
|
||||
let color = this.hex2rgb(mouse[3])
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(mouseX, mouseY, scale * 600, 0, 2 * Math.PI, false);
|
||||
ctx.strokeStyle = `rgba(${color.r},${color.g},${color.b},0.1)`;
|
||||
ctx.lineWidth = 2;
|
||||
ctx.stroke();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
import { DynamicElement } from "./nexus.elements.js"
|
||||
import { StyleInjector } from "./nexus.styles.js";
|
||||
|
||||
export class UserPanel {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.active = 0
|
||||
|
||||
this.panel = new DynamicElement({
|
||||
id: 'nexus-player-panel',
|
||||
tag: 'div',
|
||||
visible: false,
|
||||
styles: {
|
||||
"width": "100%",
|
||||
"height": "100vh",
|
||||
"display": "flex",
|
||||
"align-items": "center",
|
||||
"justify-content": "center",
|
||||
"box-sizing": "border-box",
|
||||
"position": "absolute",
|
||||
"left": "0",
|
||||
"top": "0",
|
||||
"z-index": "10000"
|
||||
},
|
||||
childrens: [
|
||||
new DynamicElement({
|
||||
id: 'nexus-player-panel-table',
|
||||
tag: 'div',
|
||||
visible: true,
|
||||
styles: {
|
||||
"width": "832px",
|
||||
"display": "flex",
|
||||
"flex-direction": "column",
|
||||
"border-radius": "6px",
|
||||
"box-sizing": "border-box"
|
||||
},
|
||||
childrens: [
|
||||
new DynamicElement({
|
||||
id: 'nexus-player-panel-table-body',
|
||||
tag: 'div',
|
||||
visible: true,
|
||||
styles: {
|
||||
"width": "100%",
|
||||
"display": "flex",
|
||||
"flex-direction": "column",
|
||||
"align-items": "center",
|
||||
"justify-content": "flex-start",
|
||||
"color": "#a3a3a3",
|
||||
"font-family": "Calibri",
|
||||
"font-weight": "600",
|
||||
"gap": "12px",
|
||||
"padding-bottom": "12px",
|
||||
"box-sizing": "border-box",
|
||||
"background": "rgba(54,54,54,0.8)",
|
||||
"border-radius": "6px",
|
||||
"box-shadow": "4px 8px 8px rgba(0,0,0,0.5)",
|
||||
"max-height": "540px",
|
||||
"overflowY": "auto"
|
||||
}
|
||||
}),
|
||||
new StyleInjector({
|
||||
styles: {
|
||||
"#nexus-player-panel .table-row": {
|
||||
"width": "100%",
|
||||
"display": "grid",
|
||||
"align-items": "center",
|
||||
"grid-template-columns": "20% 60% 20%",
|
||||
"justify-content": "space-between",
|
||||
},
|
||||
"#nexus-player-panel .table-row:first-child": {
|
||||
"width": "100%",
|
||||
"color": "#a3a3a3",
|
||||
"padding": "16px 0",
|
||||
"background": "rgb(0,0,0,0.6)",
|
||||
"border-top-left-radius": "6px",
|
||||
"border-top-right-radius": "6px",
|
||||
"position": "sticky",
|
||||
"top": "0",
|
||||
},
|
||||
"#nexus-player-panel .table-row .table-row-child": {
|
||||
"display": "flex",
|
||||
"justify-content": "flex-start",
|
||||
"gap": "12px",
|
||||
"padding-left": "12px",
|
||||
"width": "100%"
|
||||
},
|
||||
"#nexus-player-panel .table-row-child button": {
|
||||
"border": "none",
|
||||
"padding": "6px 12px",
|
||||
"border-radius": "3px",
|
||||
"cursor": "pointer",
|
||||
"background": "rgba(94,94,94)",
|
||||
"color": "#a1a1a1",
|
||||
},
|
||||
"#nexus-player-panel .table-row-child button:active": {
|
||||
"opacity": "0.8"
|
||||
},
|
||||
"#nexus-player-panel .table-row-child button[active='1'][label='spectate']": {
|
||||
"background": "#218c74",
|
||||
"color": "#fafafa"
|
||||
},
|
||||
"#nexus-player-panel .table-row-child button[active='1'][label='editor']": {
|
||||
"background": "#474787",
|
||||
"color": "#fafafa"
|
||||
},
|
||||
"#nexus-player-panel .table-row-child button[active='1'][label='mouse']": {
|
||||
"background": "#cd6133",
|
||||
"color": "#fafafa"
|
||||
},
|
||||
"#nexus-player-panel .table-row-child button[active='1'][label='queue']": {
|
||||
"background": "#6F1E51",
|
||||
"color": "#fafafa"
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
],
|
||||
})
|
||||
this.onCommandReceived = null
|
||||
this.buttons = []
|
||||
this.columns = []
|
||||
}
|
||||
|
||||
addColumnsToPanel(columns) {
|
||||
|
||||
this.columns = columns
|
||||
|
||||
}
|
||||
|
||||
addRowsToPanel(rows, callbacks, values, data) {
|
||||
|
||||
let tbody = this.panel.childrens[0].childrens[0];
|
||||
tbody.removeChildrens();
|
||||
this.buttons = []
|
||||
|
||||
const tr = document.createElement('div');
|
||||
tr.className = "table-row";
|
||||
|
||||
this.columns.forEach(heading => {
|
||||
const th = document.createElement('span');
|
||||
th.className = "table-row-child"
|
||||
th.textContent = heading;
|
||||
tr.appendChild(th)
|
||||
});
|
||||
|
||||
tbody.addChild(tr)
|
||||
|
||||
rows.forEach((row, i) => {
|
||||
const tr = document.createElement('div');
|
||||
tr.className = "table-row";
|
||||
|
||||
row.forEach((cell, j) => {
|
||||
|
||||
let td = document.createElement('span');
|
||||
td.className = "table-row-child"
|
||||
|
||||
let hascomma = cell.indexOf(",") > -1
|
||||
let hascolon = cell.indexOf(":") > -1
|
||||
|
||||
let content = []
|
||||
|
||||
if (hascomma) {
|
||||
content = cell.split(',')
|
||||
content.forEach((elm, k) => {
|
||||
let elmcontent = elm.split(':')
|
||||
this.addChildToParent(elmcontent, td, callbacks, values, data, i, j, k)
|
||||
})
|
||||
}
|
||||
else if (hascolon) {
|
||||
content = cell.split(':')
|
||||
this.addChildToParent(content, td, callbacks, values, data, i, j, -1)
|
||||
} else {
|
||||
td.textContent = cell
|
||||
}
|
||||
tr.appendChild(td);
|
||||
|
||||
});
|
||||
tbody.addChild(tr)
|
||||
});
|
||||
}
|
||||
|
||||
addChildToParent(content, td, callbacks, values, data, i, j, k) {
|
||||
//console.log(content, td, callbacks, values, data, i, j, k)
|
||||
switch (content[0]) {
|
||||
case 'button':
|
||||
let btn = document.createElement('button');
|
||||
if (content[1] == 'none') {
|
||||
btn.className = "none"
|
||||
}
|
||||
if (!this.buttons.includes(btn)) {
|
||||
this.buttons.push(btn)
|
||||
}
|
||||
btn.textContent = content[1]
|
||||
if (callbacks[i] && callbacks[i][j] && values[i][j] && data[i][j]) {
|
||||
let inner_values = values[i][j].split(',')
|
||||
let inner_data = data[i][j].split(',')
|
||||
if (k != -1) {
|
||||
btn.setAttribute('data', inner_data[k]);
|
||||
btn.setAttribute('active', inner_values[k]);
|
||||
} else {
|
||||
btn.setAttribute('data', data[i][j]);
|
||||
btn.setAttribute('active', values[i][j]);
|
||||
}
|
||||
btn.setAttribute('kind', content[0]);
|
||||
btn.setAttribute('label', content[1]);
|
||||
if (content[1] != 'none') {
|
||||
btn.onclick = callbacks[i][j].bind(this);
|
||||
}
|
||||
}
|
||||
td.appendChild(btn)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
disableButtons(label) {
|
||||
this.buttons.forEach(btn => {
|
||||
let btn_kind = btn.getAttribute('kind')
|
||||
let btn_data = btn.getAttribute('data')
|
||||
let btn_active = parseInt(btn.getAttribute('active'))
|
||||
let btn_label = btn.getAttribute('label')
|
||||
if (btn_label == label) {
|
||||
if (btn_active == 1) {
|
||||
btn.setAttribute('active', 0)
|
||||
//console.log(btn_kind, btn_label, btn_data, 0)
|
||||
if (this.onCommandReceived) {
|
||||
this.onCommandReceived(btn_kind, btn_label, btn_data, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onclick(e) {
|
||||
|
||||
let kind = e.target.getAttribute('kind')
|
||||
let data = e.target.getAttribute('data')
|
||||
let label = e.target.getAttribute('label')
|
||||
let active = parseInt(e.target.getAttribute('active'))
|
||||
if (kind == "button") {
|
||||
if (label == "spectate") {
|
||||
this.disableButtons(label)
|
||||
}
|
||||
if (active == 0) {
|
||||
e.target.setAttribute('active', 1)
|
||||
} else if (active == 1) {
|
||||
e.target.setAttribute('active', 0)
|
||||
}
|
||||
}
|
||||
active = parseInt(e.target.getAttribute('active'))
|
||||
//console.log(kind, label, data, active)
|
||||
if (this.onCommandReceived) {
|
||||
this.onCommandReceived(kind, label, data, active)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import { UserManager } from "./nexus.user.manager.js";
|
||||
|
||||
export class NexusSocket extends EventTarget {
|
||||
|
||||
getName() {
|
||||
let name = localStorage.getItem('nexus-socket-name');
|
||||
if (!name) {
|
||||
localStorage.setItem('nexus-socket-name', "User");
|
||||
name = "User"
|
||||
}
|
||||
this.userManager.set(this.uuid, "name", name)
|
||||
return name
|
||||
}
|
||||
|
||||
getUUID() {
|
||||
let uuid = localStorage.getItem('nexus-socket-uuid');
|
||||
if (!uuid) {
|
||||
uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c =>
|
||||
(Math.random() * 16 | 0).toString(16)
|
||||
);
|
||||
localStorage.setItem('nexus-socket-uuid', uuid);
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this.ws = null;
|
||||
this.userManager = new UserManager();
|
||||
this.uuid = this.getUUID();
|
||||
this.admin = false
|
||||
this.wsUrl = `ws${window.location.protocol === "https:" ? "s" : ""}://${location.host}/nexus?id=${this.uuid}`;
|
||||
|
||||
this.onConnected = [this.onConnectedHandler.bind(this)];
|
||||
this.onDisconnected = [];
|
||||
this.onMessageReceive = [this.onMessageReceiveHandler.bind(this)];
|
||||
this.onMessageSend = [];
|
||||
this.onError = [];
|
||||
this.onClose = [];
|
||||
|
||||
this.getName()
|
||||
|
||||
}
|
||||
|
||||
onConnectedHandler() {
|
||||
this.userManager.create(this.uuid);
|
||||
this.sendMessage('join', { name: this.getName() }, false);
|
||||
this.sendMessage("online", {}, false)
|
||||
}
|
||||
|
||||
onMessageReceiveHandler(message) {
|
||||
let name = message.name;
|
||||
let from = message.from;
|
||||
switch (name) {
|
||||
case "join":
|
||||
this.userManager.create(from);
|
||||
this.sendMessage('hi', {}, false);
|
||||
break;
|
||||
case "hi":
|
||||
this.userManager.create(from);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
connect() {
|
||||
try {
|
||||
this.ws = new WebSocket(this.wsUrl);
|
||||
this.ws.onopen = () => {
|
||||
if (this.onConnected) {
|
||||
this.onConnected.forEach(f => { f(); });
|
||||
}
|
||||
};
|
||||
this.ws.onmessage = (msg) => {
|
||||
try {
|
||||
const message = JSON.parse(msg.data);
|
||||
if (this.onMessageReceive) {
|
||||
this.onMessageReceive.forEach(f => { f(message); });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error handling message:', e);
|
||||
}
|
||||
};
|
||||
this.ws.onerror = (error) => {
|
||||
if (this.onError) {
|
||||
this.onError.forEach(f => { f(error); });
|
||||
}
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
this.ws.onclose = () => {
|
||||
if (this.onDisconnected) {
|
||||
this.onDisconnected.forEach(f => { f(); });
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.connect();
|
||||
}, 5000);
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('Error connecting to WebSocket:', e);
|
||||
}
|
||||
}
|
||||
|
||||
closeWebSocket() {
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
this.ws.close();
|
||||
} else {
|
||||
console.log('WebSocket is not open.');
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage(event_name, event_data, all = true) {
|
||||
try {
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
let data = {}
|
||||
if(event_data.receiver) {
|
||||
data.receiver = event_data.receiver
|
||||
delete event_data.receiver
|
||||
} else if (event_data.exclude) {
|
||||
data.exclude = event_data.exclude
|
||||
delete event_data.exclude
|
||||
}
|
||||
data.from = this.uuid
|
||||
data.name = event_name
|
||||
data.data = event_data
|
||||
data.all = all
|
||||
this.ws.send(JSON.stringify(data));
|
||||
if (this.onMessageSend) {
|
||||
this.onMessageSend.forEach(f => { f(message); });
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Error sending ${event_name} message:`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
export class StyleInjector {
|
||||
|
||||
constructor({ styles }) {
|
||||
this.el = null
|
||||
this.styles = styles;
|
||||
this.create()
|
||||
}
|
||||
|
||||
create() {
|
||||
this.el = document.createElement('style');
|
||||
let cssString = "";
|
||||
for (const selector in this.styles) {
|
||||
cssString += `${selector} { `;
|
||||
for (const property in this.styles[selector]) {
|
||||
cssString += `${property}: ${this.styles[selector][property]}; `;
|
||||
}
|
||||
cssString += `} `;
|
||||
}
|
||||
this.el.textContent = cssString;
|
||||
}
|
||||
|
||||
softUpdate(selector, property, value) {
|
||||
let cssString = "";
|
||||
for (const key in this.styles) {
|
||||
cssString += `${key} { `;
|
||||
for (const property in this.styles[key]) {
|
||||
cssString += `${property}: ${this.styles[key][property]}; `;
|
||||
}
|
||||
if(key == selector) {
|
||||
cssString += `${property}: ${value}; `;
|
||||
}
|
||||
cssString += `} `;
|
||||
}
|
||||
this.el.textContent = cssString;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
export class UserManager {
|
||||
constructor() {
|
||||
this.users = {}
|
||||
}
|
||||
|
||||
create(id) {
|
||||
if (!this.users[id]) {
|
||||
this.users[id] = {}
|
||||
}
|
||||
}
|
||||
|
||||
set(id, key, data) {
|
||||
this.create(id)
|
||||
this.users[id][key] = data
|
||||
}
|
||||
|
||||
get(id, key) {
|
||||
this.create(id)
|
||||
return this.users[id][key]
|
||||
}
|
||||
|
||||
remove(id) {
|
||||
if(this.users[id])
|
||||
delete this.users[id];
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
import "./nexus.litegraph.js"
|
||||
import { postWorkflow, postBackupWorkflow, getLatestWorkflow } from "./nexus.api.js";
|
||||
|
||||
export class NexusWorkflowManager {
|
||||
|
||||
constructor(app, api, nexusSocket) {
|
||||
|
||||
this.app = app;
|
||||
this.api = api;
|
||||
|
||||
this.nexusSocket = nexusSocket;
|
||||
this.nexusSocket.onMessageReceive.push(this.handleMessageReceive.bind(this))
|
||||
|
||||
this.init()
|
||||
|
||||
this.spectators = []
|
||||
this.saveTime = 0
|
||||
setInterval(this.handleSpectators.bind(this), 1000 / 24) // 24 fps
|
||||
|
||||
}
|
||||
|
||||
async init() {
|
||||
let workflow = (await this.app.graphToPrompt()).workflow;
|
||||
await postBackupWorkflow(this.nexusSocket.uuid, workflow)
|
||||
this.app.graph.clear()
|
||||
}
|
||||
|
||||
removeSaveManager() {
|
||||
if (this.saveTimer) {
|
||||
clearInterval(this.saveTimer)
|
||||
}
|
||||
// document.removeEventListener("visibilitychange", this.handleSaveWorkflowManager.bind(this))
|
||||
}
|
||||
|
||||
async saveManager(time) {
|
||||
if (this.nexusSocket.admin) {
|
||||
this.nexusSocket.sendMessage('workflow:timer', { time: time - 2 }, false)
|
||||
}
|
||||
this.removeSaveManager()
|
||||
this.saveTime = time
|
||||
this.saveTimer = setInterval(this.sendWorkFlow.bind(this), time * 1000)
|
||||
console.log("Auto Backup Timer Setup:",time)
|
||||
// document.addEventListener("visibilitychange", this.handleSaveWorkflowManager.bind(this))
|
||||
}
|
||||
|
||||
async sendWorkFlow() {
|
||||
let workflow = (await this.app.graphToPrompt()).workflow;
|
||||
await postWorkflow(this.nexusSocket.uuid, workflow)
|
||||
}
|
||||
|
||||
async loadLatestWorkflow() {
|
||||
let workflow = await getLatestWorkflow()
|
||||
let links = workflow.links || [];
|
||||
let nodes = workflow.nodes || [];
|
||||
let groups = workflow.groups || [];
|
||||
this.app.graph.updateNodes(nodes);
|
||||
this.app.graph.updateLinks(links);
|
||||
this.app.graph.updateGroups(groups);
|
||||
this.app.graph.updateExecutionOrder();
|
||||
this.app.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
|
||||
async handleSaveWorkflowManager() {
|
||||
this.sendWorkFlow()
|
||||
// if (document.visibilityState === "hidden") {
|
||||
// if (this.saveTimer) {
|
||||
// clearInterval(this.saveTimer)
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// this.saveTimer = setInterval(this.sendWorkFlow.bind(this), this.saveTime * 1000)
|
||||
// }
|
||||
}
|
||||
|
||||
async handleSpectators() {
|
||||
if (this.spectators.length > 0) {
|
||||
let workflow = (await this.app.graphToPrompt()).workflow;
|
||||
let wfe = workflow.extra
|
||||
for (let index = 0; index < this.spectators.length; index++) {
|
||||
const id = this.spectators[index];
|
||||
this.nexusSocket.sendMessage('workflow:spectate', { workflow: wfe, receiver: id }, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async handleMessageReceive(message) {
|
||||
|
||||
let from = message.from;
|
||||
let data = message.data
|
||||
let name = message.name;
|
||||
|
||||
switch (name) {
|
||||
case "spectate":
|
||||
if (data.on == 0) {
|
||||
if (this.spectators.includes(from)) {
|
||||
this.spectators.splice(this.spectators.indexOf(from), 1)
|
||||
}
|
||||
} else if (data.on == 1) {
|
||||
if (!this.spectators.includes(from)) {
|
||||
this.spectators.push(from)
|
||||
}
|
||||
}
|
||||
case "join":
|
||||
// this.sendWorkFlow()
|
||||
this.saveManager(this.saveTime || 60)
|
||||
break;
|
||||
case "workflow:spectate":
|
||||
let wfe = data.workflow
|
||||
if (wfe?.ds) {
|
||||
this.app.canvas.ds.offset = wfe.ds.offset;
|
||||
this.app.canvas.ds.scale = wfe.ds.scale;
|
||||
}
|
||||
break;
|
||||
case "workflow:timer":
|
||||
let time = data.time
|
||||
this.saveManager(time)
|
||||
break;
|
||||
case "workflow:update":
|
||||
this.handleWorkflowUpdate(from, data)
|
||||
break;
|
||||
case "workflow":
|
||||
let workflow = data.workflow
|
||||
if (workflow.clear) {
|
||||
await this.app.graph.clear()
|
||||
}
|
||||
let links = workflow.links || [];
|
||||
let nodes = workflow.nodes || [];
|
||||
let groups = workflow.groups || [];
|
||||
this.app.graph.updateNodes(nodes);
|
||||
this.app.graph.updateLinks(links);
|
||||
this.app.graph.updateGroups(groups);
|
||||
this.app.graph.updateExecutionOrder();
|
||||
this.app.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
handleWorkflowEvents(oN) {
|
||||
if (oN) {
|
||||
this.app.graph.onNodeAdded = this.onNodeAdded.bind(this);
|
||||
this.app.graph.onNodeAddedNexus = this.onNodeAddedNexus.bind(this);
|
||||
this.app.graph.onNodeRemoved = this.onNodeRemoved.bind(this);
|
||||
this.app.canvas.onSelectionChange = this.onSelectionChange.bind(this);
|
||||
this.app.canvas.onNodeDeselected = this.onNodeDeselected.bind(this);
|
||||
this.app.canvas.onNodeMoved = this.onNodeMoved.bind(this);
|
||||
this.app.graph.onGroupAdded = this.onGroupAdded.bind(this);
|
||||
this.app.graph.onGroupRemoved = this.onGroupRemoved.bind(this);
|
||||
this.app.graph._groups.forEach(group => {
|
||||
group.onGroupMoved = this.onGroupMoved.bind(this, group);
|
||||
group.onGroupNodeMoved = this.onNodeMoved.bind(this);
|
||||
});
|
||||
this.app.graph._nodes.forEach(node => {
|
||||
node.onWidgetChanged = this.onWidgetChanged.bind(this, node);
|
||||
node.onConnectionsChange = this.onNodeConnectionChange.bind(this, node);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.app.graph.onNodeAddedNexus = null;
|
||||
this.app.graph.onNodeAdded = null;
|
||||
this.app.graph.onNodeRemoved = null;
|
||||
this.app.canvas.onSelectionChange = null;
|
||||
this.app.canvas.onNodeDeselected = null;
|
||||
this.app.canvas.onNodeMoved = null;
|
||||
this.app.graph.onGroupAdded = null;
|
||||
this.app.graph.onGroupRemoved = null;
|
||||
this.app.graph._groups.forEach(group => {
|
||||
group.onGroupMoved = null;
|
||||
group.onGroupNodeMoved = null;
|
||||
});
|
||||
this.app.graph._nodes.forEach(node => {
|
||||
node.onWidgetChanged = null;
|
||||
node.onConnectionsChange = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleWorkflowUpdate(from, detail) {
|
||||
|
||||
let graph = this.app.graph;
|
||||
let canvas = this.app.canvas;
|
||||
let update = detail.update;
|
||||
let data = detail.data;
|
||||
|
||||
let existingNode;
|
||||
|
||||
switch (update) {
|
||||
case "node-added":
|
||||
graph.updateNode(data);
|
||||
break;
|
||||
case "node-removed":
|
||||
graph.removeNode(data);
|
||||
break;
|
||||
case "node-moved":
|
||||
graph.updateNode(data);
|
||||
break;
|
||||
case "node-widget-changed":
|
||||
graph.updateNode(data);
|
||||
break;
|
||||
case "node-selection":
|
||||
let nodes = []
|
||||
data.forEach(node => {
|
||||
existingNode = graph.getNodeById(node.id);
|
||||
if (existingNode) {
|
||||
nodes.push(existingNode)
|
||||
}
|
||||
})
|
||||
canvas.selectNodesNexus(nodes, true)
|
||||
break;
|
||||
case "node-deselection":
|
||||
existingNode = graph.getNodeById(data.id);
|
||||
if (existingNode)
|
||||
canvas.deselectNodeNexus(existingNode)
|
||||
break;
|
||||
case "group-added":
|
||||
graph.updateGroup(data);
|
||||
break;
|
||||
case "group-moved":
|
||||
graph.updateGroup(data);
|
||||
break;
|
||||
case "group-removed":
|
||||
graph.removeGroup(data);
|
||||
break;
|
||||
case "node-connection":
|
||||
graph.updateLinkManual(detail.change, detail.link, detail.node, graph);
|
||||
break;
|
||||
default:
|
||||
//console.log(update);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onNodeAddedNexus(node) {
|
||||
node.onWidgetChanged = this.onWidgetChanged.bind(this, node);
|
||||
node.onConnectionsChange = this.onNodeConnectionChange.bind(this, node);
|
||||
}
|
||||
|
||||
onNodeAdded(node) {
|
||||
let data = node.serialize();
|
||||
node.onWidgetChanged = this.onWidgetChanged.bind(this, node);
|
||||
node.onConnectionsChange = this.onNodeConnectionChange.bind(this, node);
|
||||
this.nexusSocket.sendMessage('workflow:update', { update: "node-added", data }, false);
|
||||
}
|
||||
|
||||
onWidgetChanged(node) {
|
||||
let data = node.serialize();
|
||||
this.nexusSocket.sendMessage('workflow:update', { update: "node-widget-changed", data }, false);
|
||||
}
|
||||
|
||||
onNodeRemoved(node) {
|
||||
let data = node.serialize();
|
||||
this.nexusSocket.sendMessage('workflow:update', { update: "node-removed", data }, false);
|
||||
}
|
||||
|
||||
async onNodeConnectionChange(node, a, b, c, d, e) {
|
||||
let change = c ? "add" : "remove";
|
||||
let link = d;
|
||||
this.graphData = (await this.app.graphToPrompt()).workflow;
|
||||
let data = this.graphData.links;
|
||||
//console.log(data)
|
||||
node = this.app.graph.getNodeById(node.id);
|
||||
node = node ? node.serialize() : null;
|
||||
this.nexusSocket.sendMessage('workflow:update', { node, update: "node-connection", link, change, data }, false);
|
||||
}
|
||||
|
||||
onSelectionChange(nodes) {
|
||||
let data = []
|
||||
Object.keys(nodes).forEach(node_id => {
|
||||
data.push(nodes[node_id].serialize())
|
||||
})
|
||||
this.nexusSocket.sendMessage('workflow:update', { update: "node-selection", data }, false);
|
||||
}
|
||||
|
||||
onNodeDeselected(node) {
|
||||
let data = node.serialize()
|
||||
this.nexusSocket.sendMessage('workflow:update', { update: "node-deselection", data }, false);
|
||||
}
|
||||
|
||||
onNodeMoved(node) {
|
||||
let data = node.serialize();
|
||||
this.nexusSocket.sendMessage('workflow:update', { update: "node-moved", data }, false);
|
||||
}
|
||||
|
||||
onGroupAdded(group) {
|
||||
let data = group.serialize();
|
||||
let uuid = localStorage.getItem('nexus-socket-uuid');
|
||||
if (uuid) {
|
||||
data.owner = uuid
|
||||
group.configure(data);
|
||||
}
|
||||
group.onGroupMoved = this.onGroupMoved.bind(this, group);
|
||||
group.onGroupNodeMoved = this.onNodeMoved.bind(this);
|
||||
this.nexusSocket.sendMessage('workflow:update', { update: "group-added", data }, false);
|
||||
}
|
||||
|
||||
onGroupRemoved(group) {
|
||||
let data = group.serialize();
|
||||
this.nexusSocket.sendMessage('workflow:update', { update: "group-removed", data }, false);
|
||||
}
|
||||
|
||||
onGroupMoved(group) {
|
||||
let data = group.serialize();
|
||||
this.nexusSocket.sendMessage('workflow:update', { update: "group-moved", data }, false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
import { DynamicElement } from "./nexus.elements.js"
|
||||
import { StyleInjector } from "./nexus.styles.js";
|
||||
|
||||
export class WorkflowPanel {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.active = 0
|
||||
|
||||
this.panel = new DynamicElement({
|
||||
id: 'nexus-workflow-panel',
|
||||
tag: 'div',
|
||||
visible: false,
|
||||
styles: {
|
||||
"width": "100%",
|
||||
"height": "100vh",
|
||||
"display": "flex",
|
||||
"align-items": "center",
|
||||
"justify-content": "center",
|
||||
"box-sizing": "border-box",
|
||||
"position": "absolute",
|
||||
"left": "0",
|
||||
"top": "0",
|
||||
"z-index": "10000"
|
||||
},
|
||||
childrens: [
|
||||
new DynamicElement({
|
||||
id: 'nexus-workflow-panel-table',
|
||||
tag: 'div',
|
||||
visible: true,
|
||||
styles: {
|
||||
"width": "512px",
|
||||
"display": "flex",
|
||||
"flex-direction": "column",
|
||||
"border-radius": "6px",
|
||||
"box-sizing": "border-box"
|
||||
},
|
||||
childrens: [
|
||||
new DynamicElement({
|
||||
id: 'nexus-workflow-panel-table-body',
|
||||
tag: 'div',
|
||||
visible: true,
|
||||
styles: {
|
||||
"width": "100%",
|
||||
"display": "flex",
|
||||
"flex-direction": "column",
|
||||
"align-items": "center",
|
||||
"justify-content": "flex-start",
|
||||
"color": "#a3a3a3",
|
||||
"font-family": "Calibri",
|
||||
"font-weight": "600",
|
||||
"gap": "12px",
|
||||
"padding-bottom": "12px",
|
||||
"box-sizing": "border-box",
|
||||
"background": "rgba(54,54,54,0.8)",
|
||||
"border-radius": "6px",
|
||||
"box-shadow": "4px 8px 8px rgba(0,0,0,0.5)",
|
||||
"max-height": "540px",
|
||||
"overflowY": "auto"
|
||||
}
|
||||
}),
|
||||
new StyleInjector({
|
||||
styles: {
|
||||
"#nexus-workflow-panel .table-row": {
|
||||
"width": "100%",
|
||||
"display": "flex",
|
||||
"align-items": "center",
|
||||
"justify-content": "center",
|
||||
},
|
||||
"#nexus-workflow-panel .table-row:first-child": {
|
||||
"width": "100%",
|
||||
"color": "#a3a3a3",
|
||||
"padding": "16px 0",
|
||||
"background": "rgb(0,0,0,0.6)",
|
||||
"border-top-left-radius": "6px",
|
||||
"border-top-right-radius": "6px",
|
||||
"position": "sticky",
|
||||
"top": "0",
|
||||
},
|
||||
"#nexus-workflow-panel .table-row .table-row-child": {
|
||||
"display": "flex",
|
||||
"justify-content": "flex-start",
|
||||
"gap": "12px",
|
||||
"padding-left": "12px",
|
||||
"width": "100%"
|
||||
},
|
||||
"#nexus-workflow-panel .table-row-child button": {
|
||||
"border": "none",
|
||||
"padding": "6px 12px",
|
||||
"border-radius": "3px",
|
||||
"cursor": "pointer",
|
||||
"background": "rgba(94,94,94)",
|
||||
"color": "#a1a1a1",
|
||||
"width": "100%"
|
||||
},
|
||||
"#nexus-workflow-panel .table-row-child:first-child": {
|
||||
"width":"50%"
|
||||
},
|
||||
"#nexus-workflow-panel .table-row-child:last-child": {
|
||||
"padding-right":"12px"
|
||||
},
|
||||
"#nexus-workflow-panel .table-row-child button:active": {
|
||||
"opacity": "0.8"
|
||||
},
|
||||
"#nexus-workflow-panel .table-row-child button[label='load']:active": {
|
||||
"background": "#218c74",
|
||||
"color": "#fafafa"
|
||||
},
|
||||
"#nexus-workflow-panel .table-row-child button[label='loadforall']:active": {
|
||||
"background": "#474787",
|
||||
"color": "#fafafa"
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
],
|
||||
})
|
||||
this.onCommandReceived = null
|
||||
this.buttons = []
|
||||
this.columns = []
|
||||
}
|
||||
|
||||
addColumnsToPanel(columns) {
|
||||
|
||||
this.columns = columns
|
||||
|
||||
}
|
||||
|
||||
addRowsToPanel(rows, callbacks, values, data) {
|
||||
|
||||
let tbody = this.panel.childrens[0].childrens[0];
|
||||
tbody.removeChildrens();
|
||||
this.buttons = []
|
||||
|
||||
const tr = document.createElement('div');
|
||||
tr.className = "table-row";
|
||||
|
||||
this.columns.forEach(heading => {
|
||||
const th = document.createElement('span');
|
||||
th.className = "table-row-child"
|
||||
th.textContent = heading;
|
||||
tr.appendChild(th)
|
||||
});
|
||||
|
||||
tbody.addChild(tr)
|
||||
|
||||
rows.forEach((row, i) => {
|
||||
const tr = document.createElement('div');
|
||||
tr.className = "table-row";
|
||||
|
||||
row.forEach((cell, j) => {
|
||||
|
||||
let td = document.createElement('span');
|
||||
td.className = "table-row-child"
|
||||
|
||||
let hascomma = cell.indexOf(",") > -1
|
||||
let hascolon = cell.indexOf(":") > -1
|
||||
|
||||
let content = []
|
||||
|
||||
if (hascomma) {
|
||||
content = cell.split(',')
|
||||
content.forEach((elm, k) => {
|
||||
let elmcontent = elm.split(':')
|
||||
this.addChildToParent(elmcontent, td, callbacks, values, data, i, j, k)
|
||||
})
|
||||
}
|
||||
else if (hascolon) {
|
||||
content = cell.split(':')
|
||||
this.addChildToParent(content, td, callbacks, values, data, i, j, -1)
|
||||
} else {
|
||||
cell = cell.split("|")
|
||||
cell = cell.join(":")
|
||||
td.textContent = cell
|
||||
}
|
||||
tr.appendChild(td);
|
||||
|
||||
});
|
||||
tbody.addChild(tr)
|
||||
});
|
||||
}
|
||||
|
||||
addChildToParent(content, td, callbacks, values, data, i, j, k) {
|
||||
switch (content[0]) {
|
||||
case 'button':
|
||||
let btn = document.createElement('button');
|
||||
if (content[1] == 'none') {
|
||||
btn.className = "none"
|
||||
}
|
||||
if (!this.buttons.includes(btn)) {
|
||||
this.buttons.push(btn)
|
||||
}
|
||||
btn.textContent = content[1]
|
||||
if (callbacks[i] && callbacks[i][j] && values[i][j] && data[i][j]) {
|
||||
let inner_values = values[i][j].split(',')
|
||||
let inner_data = data[i][j].split(',')
|
||||
if (k != -1) {
|
||||
btn.setAttribute('data', inner_data[k]);
|
||||
btn.setAttribute('active', inner_values[k]);
|
||||
} else {
|
||||
btn.setAttribute('data', data[i][j]);
|
||||
btn.setAttribute('active', values[i][j]);
|
||||
}
|
||||
btn.setAttribute('kind', content[0]);
|
||||
btn.setAttribute('label', content[1]);
|
||||
if (content[1] != 'none') {
|
||||
btn.onclick = callbacks[i][j].bind(this);
|
||||
}
|
||||
}
|
||||
td.appendChild(btn)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onclick(e) {
|
||||
|
||||
let kind = e.target.getAttribute('kind')
|
||||
let data = e.target.getAttribute('data')
|
||||
let label = e.target.getAttribute('label')
|
||||
let active = parseInt(e.target.getAttribute('active'))
|
||||
if (kind == "button") {
|
||||
if (active == 0) {
|
||||
e.target.setAttribute('active', 1)
|
||||
} else if (active == 1) {
|
||||
e.target.setAttribute('active', 0)
|
||||
}
|
||||
}
|
||||
active = parseInt(e.target.getAttribute('active'))
|
||||
//console.log(kind, label, data, active)
|
||||
if (this.onCommandReceived) {
|
||||
this.onCommandReceived(kind, label, data, active)
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
-9
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user