feat: ✨ implement ipaq ideas for the I/O sidebar
This commit is contained in:
committed by
Mel Massadian
parent
c7248344cc
commit
940a781f29
+9
-4
@@ -110,11 +110,15 @@ def ACTIONS_getUserVideos(
|
|||||||
|
|
||||||
def ACTIONS_getUserImages(
|
def ACTIONS_getUserImages(
|
||||||
mode: Literal["input", "output"],
|
mode: Literal["input", "output"],
|
||||||
|
target_width: int | str | None = None,
|
||||||
count=1000,
|
count=1000,
|
||||||
offset=0,
|
offset=0,
|
||||||
sort: str | None = None,
|
sort: str | None = None,
|
||||||
include_subfolders: bool = False,
|
include_subfolders: bool = False,
|
||||||
subfolder=None,
|
subfolder: str | None = None,
|
||||||
|
# IIRC I copied this from Comfy base
|
||||||
|
# just keeping it until I properly checked implications
|
||||||
|
salt_urls=False,
|
||||||
):
|
):
|
||||||
# enabled = "MTB_EXPOSE" in os.environ
|
# enabled = "MTB_EXPOSE" in os.environ
|
||||||
# if not enabled:
|
# if not enabled:
|
||||||
@@ -122,11 +126,12 @@ def ACTIONS_getUserImages(
|
|||||||
|
|
||||||
imgs = {}
|
imgs = {}
|
||||||
count = count or 1000
|
count = count or 1000
|
||||||
|
target_width = int(target_width) if target_width else None
|
||||||
|
|
||||||
input_dir = Path(folder_paths.get_input_directory())
|
input_dir = Path(folder_paths.get_input_directory())
|
||||||
output_dir = Path(folder_paths.get_output_directory())
|
output_dir = Path(folder_paths.get_output_directory())
|
||||||
|
|
||||||
entry_dir = input_dir if mode == "input" else output_dir
|
entry_dir: Path = input_dir if mode == "input" else output_dir
|
||||||
if subfolder:
|
if subfolder:
|
||||||
entry_dir = entry_dir / subfolder
|
entry_dir = entry_dir / subfolder
|
||||||
|
|
||||||
@@ -155,9 +160,9 @@ def ACTIONS_getUserImages(
|
|||||||
|
|
||||||
imgs = {
|
imgs = {
|
||||||
img.name: (
|
img.name: (
|
||||||
f"/mtb/view?filename={img.name}&width=512&type={mode}&subfolder={subfolder or ''}"
|
f"/mtb/view?filename={img.name}{f'&width={target_width}' if target_width and target_width > 0 else ''}&type={mode}&subfolder={subfolder or ''}"
|
||||||
f"{img.parent.relative_to(entry_dir) if include_subfolders else ''}"
|
f"{img.parent.relative_to(entry_dir) if include_subfolders else ''}"
|
||||||
f"&preview=&rand={secrets.randbelow(424242)}"
|
f"&preview={f'&rand={secrets.randbelow(424242)}' if salt_urls else ''}"
|
||||||
)
|
)
|
||||||
for i, img in enumerate(entries)
|
for i, img in enumerate(entries)
|
||||||
if offset <= i < offset + count
|
if offset <= i < offset + count
|
||||||
|
|||||||
+118
-42
@@ -3,6 +3,7 @@
|
|||||||
import { app } from '../../scripts/app.js'
|
import { app } from '../../scripts/app.js'
|
||||||
import { api } from '../../scripts/api.js'
|
import { api } from '../../scripts/api.js'
|
||||||
|
|
||||||
|
import * as mtb_ui from './mtb_ui.js'
|
||||||
import * as shared from './comfy_shared.js'
|
import * as shared from './comfy_shared.js'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -15,7 +16,13 @@ import {
|
|||||||
} from './mtb_ui.js'
|
} from './mtb_ui.js'
|
||||||
|
|
||||||
const offset = 0
|
const offset = 0
|
||||||
|
|
||||||
|
// These are "global" variables mostly meant to sync user settings.
|
||||||
let currentWidth = 200
|
let currentWidth = 200
|
||||||
|
let saltUrls =
|
||||||
|
app.extensionManager.setting.get('mtb.io-sidebar.salt_urls') || false
|
||||||
|
let targetWidth =
|
||||||
|
app.extensionManager.setting.get('mtb.io-sidebar.img-size') || 512
|
||||||
let currentMode = 'input'
|
let currentMode = 'input'
|
||||||
let subfolder = ''
|
let subfolder = ''
|
||||||
let currentSort = 'None'
|
let currentSort = 'None'
|
||||||
@@ -46,15 +53,19 @@ const updateImage = (node, image) => {
|
|||||||
* @param {ResultItem} resultItem
|
* @param {ResultItem} resultItem
|
||||||
* @returns {string} - The request URL.
|
* @returns {string} - The request URL.
|
||||||
*/
|
*/
|
||||||
const resultItemToQuery = (resultItem) =>
|
const resultItemToQuery = (resultItem) => {
|
||||||
[
|
const res = [
|
||||||
`/mtb/view?filename=${resultItem.filename}`,
|
`/mtb/view?filename=${resultItem.filename}`,
|
||||||
`width=512`,
|
|
||||||
`type=${resultItem.type}`,
|
`type=${resultItem.type}`,
|
||||||
`subfolder=${resultItem.subfolder}`,
|
`subfolder=${resultItem.subfolder}`,
|
||||||
`preview=`,
|
'preview=',
|
||||||
].join('&')
|
]
|
||||||
|
if (targetWidth > 0) {
|
||||||
|
res.splice(1, 0, `width=${targetWidth}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.join('&')
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Retrieves the unique prompt ID from a history task item.
|
* Retrieves the unique prompt ID from a history task item.
|
||||||
* @param {HistoryTaskItem} historyTaskItem
|
* @param {HistoryTaskItem} historyTaskItem
|
||||||
@@ -80,7 +91,7 @@ const getNewOutputUrls = (mostRecentTask) => {
|
|||||||
const imageOutputs = Object.values(nodeOutputs.images)
|
const imageOutputs = Object.values(nodeOutputs.images)
|
||||||
imageOutputs.forEach(
|
imageOutputs.forEach(
|
||||||
(resultItem) =>
|
(resultItem) =>
|
||||||
(urls[resultItem.filename] = resultItemToQuery(resultItem))
|
(urls[resultItem.filename] = resultItemToQuery(resultItem)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Can process `animated` and `audio` outputs here.
|
// Can process `animated` and `audio` outputs here.
|
||||||
@@ -209,7 +220,7 @@ const getUrls = async (subfolder) => {
|
|||||||
if (currentMode === 'video') {
|
if (currentMode === 'video') {
|
||||||
const output = await shared.runAction(
|
const output = await shared.runAction(
|
||||||
'getUserVideos',
|
'getUserVideos',
|
||||||
256,
|
targetWidth,
|
||||||
count,
|
count,
|
||||||
offset,
|
offset,
|
||||||
currentSort,
|
currentSort,
|
||||||
@@ -219,11 +230,13 @@ const getUrls = async (subfolder) => {
|
|||||||
const output = await shared.runAction(
|
const output = await shared.runAction(
|
||||||
'getUserImages',
|
'getUserImages',
|
||||||
currentMode,
|
currentMode,
|
||||||
|
targetWidth,
|
||||||
count,
|
count,
|
||||||
offset,
|
offset,
|
||||||
currentSort,
|
currentSort,
|
||||||
false,
|
false,
|
||||||
subfolder,
|
subfolder,
|
||||||
|
saltUrls,
|
||||||
)
|
)
|
||||||
return output || {}
|
return output || {}
|
||||||
}
|
}
|
||||||
@@ -236,55 +249,110 @@ if (window?.__COMFYUI_FRONTEND_VERSION__) {
|
|||||||
|
|
||||||
const sidebar_extension = {
|
const sidebar_extension = {
|
||||||
name: 'mtb.io-sidebar',
|
name: 'mtb.io-sidebar',
|
||||||
// init: async () => {
|
settings: [
|
||||||
// try {
|
{
|
||||||
// const res = await api.fetchApi('/mtb/server-info')
|
|
||||||
// const msg = await res.json()
|
|
||||||
// exposed = msg.exposed
|
|
||||||
// } catch (e) {
|
|
||||||
// console.error('Error:', e)
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
init: () => {
|
|
||||||
let handle
|
|
||||||
const version = window?.__COMFYUI_FRONTEND_VERSION__
|
|
||||||
console.log(`%c ${version}`, 'background: orange; color: white;')
|
|
||||||
|
|
||||||
ensureMTBStyles()
|
|
||||||
|
|
||||||
app.ui.settings.addSetting({
|
|
||||||
id: 'mtb.io-sidebar.count',
|
id: 'mtb.io-sidebar.count',
|
||||||
category: ['mtb', 'Input & Output Sidebar', 'count'],
|
category: ['mtb', 'Input & Output Sidebar', 'count'],
|
||||||
|
|
||||||
name: 'Number of images to fetch',
|
name: 'Number of images to fetch',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
defaultValue: 1000,
|
defaultValue: 1000,
|
||||||
|
|
||||||
tooltip:
|
tooltip:
|
||||||
"This setting affects the input/output sidebar to determine how many images to fetch per pagination (pagination is not yet supported so for now it's the static total)",
|
"This setting affects the input/output sidebar to determine how many images to fetch per pagination (pagination is not yet supported so for now it's the static total)",
|
||||||
attrs: {
|
},
|
||||||
style: {
|
{
|
||||||
// fontFamily: 'monospace',
|
id: 'mtb.io-sidebar.salt_urls',
|
||||||
},
|
category: ['mtb', 'Input & Output Sidebar', 'salt_urls'],
|
||||||
|
name: 'Salt URLs',
|
||||||
|
type: 'boolean',
|
||||||
|
defaultValue: false,
|
||||||
|
onChange: (n, o) => {
|
||||||
|
saltUrls = n
|
||||||
},
|
},
|
||||||
})
|
tooltip:
|
||||||
|
'Adds a random query parameter to every urls to always invalidate caching.',
|
||||||
app.ui.settings.addSetting({
|
},
|
||||||
|
{
|
||||||
id: 'mtb.io-sidebar.img-size',
|
id: 'mtb.io-sidebar.img-size',
|
||||||
category: ['mtb', 'Input & Output Sidebar', 'img-size'],
|
category: ['mtb', 'Input & Output Sidebar', 'img-size'],
|
||||||
|
|
||||||
name: 'Resize width of shown images',
|
name: 'Resize width of shown images',
|
||||||
type: 'number',
|
|
||||||
defaultValue: 512,
|
defaultValue: 512,
|
||||||
|
type: (name, setter, value, attrs) => {
|
||||||
|
targetWidth = value
|
||||||
|
const container = mtb_ui.makeElement('div', {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '8px',
|
||||||
|
})
|
||||||
|
|
||||||
tooltip: "It's recommended to keep it at 512px. Setting a high enough value will show images of less width in their native resolution.",
|
console.log({ name, setter, value, attrs })
|
||||||
attrs: {
|
|
||||||
style: {
|
const baseId = name.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase()
|
||||||
// fontFamily: 'monospace',
|
const checkboxId = `${baseId}-checkbox`
|
||||||
},
|
const numberInputId = `${baseId}-number`
|
||||||
|
|
||||||
|
const isCheckedInitially = value !== -1
|
||||||
|
|
||||||
|
// TODO: better way to get defaultValue?
|
||||||
|
const defaultValue = 512
|
||||||
|
const initialNumberValue = isCheckedInitially ? value : defaultValue
|
||||||
|
|
||||||
|
console.log('recreate')
|
||||||
|
const checkbox = mtb_ui.makeElement(
|
||||||
|
// harder to match styles (.p-toggleswitch-input)
|
||||||
|
// since it uses a div synced to the input...
|
||||||
|
'input',
|
||||||
|
{},
|
||||||
|
container,
|
||||||
|
)
|
||||||
|
checkbox.type = 'checkbox'
|
||||||
|
checkbox.id = checkboxId
|
||||||
|
checkbox.checked = isCheckedInitially
|
||||||
|
|
||||||
|
const numberInput = mtb_ui.makeElement(
|
||||||
|
'input.p-inputtext',
|
||||||
|
{},
|
||||||
|
container,
|
||||||
|
)
|
||||||
|
numberInput.type = 'number'
|
||||||
|
numberInput.id = numberInputId
|
||||||
|
numberInput.value = initialNumberValue
|
||||||
|
numberInput.disabled = !isCheckedInitially
|
||||||
|
numberInput.min = 128
|
||||||
|
|
||||||
|
checkbox.addEventListener('change', () => {
|
||||||
|
let valToSet = -1
|
||||||
|
if (checkbox.checked) {
|
||||||
|
numberInput.disabled = false
|
||||||
|
|
||||||
|
valToSet = Number.parseInt(numberInput.value, 10)
|
||||||
|
if (Number.isNaN(valToSet) || valToSet < numberInput.min) {
|
||||||
|
valToSet = defaultValue
|
||||||
|
numberInput.value = valToSet
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
numberInput.disabled = true
|
||||||
|
}
|
||||||
|
setter(valToSet)
|
||||||
|
})
|
||||||
|
|
||||||
|
numberInput.addEventListener('input', () => {
|
||||||
|
if (checkbox.checked) {
|
||||||
|
const numValue = Number.parseInt(numberInput.value, 10)
|
||||||
|
if (!Number.isNaN(numValue) && numberInput.value !== '') {
|
||||||
|
setter(numValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return container
|
||||||
},
|
},
|
||||||
})
|
|
||||||
app.ui.settings.addSetting({
|
tooltip:
|
||||||
|
"If browsing large folders it's recommended to use this to avoid overflow/crash of the webpage. Image will get resized to this target width on the server before being sent to the client.",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
id: 'mtb.io-sidebar.sort',
|
id: 'mtb.io-sidebar.sort',
|
||||||
category: ['mtb', 'Input & Output Sidebar', 'sort'],
|
category: ['mtb', 'Input & Output Sidebar', 'sort'],
|
||||||
name: 'Default sort mode',
|
name: 'Default sort mode',
|
||||||
@@ -304,7 +372,15 @@ if (window?.__COMFYUI_FRONTEND_VERSION__) {
|
|||||||
'Name',
|
'Name',
|
||||||
'Name-Reverse',
|
'Name-Reverse',
|
||||||
],
|
],
|
||||||
})
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
init: () => {
|
||||||
|
let handle
|
||||||
|
const version = window?.__COMFYUI_FRONTEND_VERSION__
|
||||||
|
console.log(`%c ${version}`, 'background: orange; color: white;')
|
||||||
|
|
||||||
|
ensureMTBStyles()
|
||||||
|
|
||||||
app.extensionManager.registerSidebarTab({
|
app.extensionManager.registerSidebarTab({
|
||||||
id: 'mtb-inputs-outputs',
|
id: 'mtb-inputs-outputs',
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
// NOTE: this will be the LT part of mtb API system
|
|
||||||
// I need to properly publish the source and fix a few things before
|
|
||||||
|
|
||||||
// import { app } from '../../scripts/app.js'
|
|
||||||
// // import { api } from '../../scripts/api.js'
|
|
||||||
//
|
|
||||||
// import * as shared from './comfy_shared.js'
|
|
||||||
// import { createOutliner } from './dist/mtb_inspector.js'
|
|
||||||
//
|
|
||||||
// if (window?.__COMFYUI_FRONTEND_VERSION__) {
|
|
||||||
// const version = window?.__COMFYUI_FRONTEND_VERSION__
|
|
||||||
// console.log(`%c ${version}`, 'background: orange; color: white;')
|
|
||||||
//
|
|
||||||
// const panel = app.extensionManager.registerSidebarTab({
|
|
||||||
// id: 'mtb-nodes',
|
|
||||||
// icon: 'pi pi-bolt',
|
|
||||||
// title: 'MTB',
|
|
||||||
// tooltip: 'MTB: API outliner',
|
|
||||||
// type: 'custom',
|
|
||||||
// // this is run everytime the tab's diplay is toggled on.
|
|
||||||
// render: (el) => {
|
|
||||||
// const outliner = createOutliner(el)
|
|
||||||
// const inputs = shared.getAPIInputs()
|
|
||||||
// console.log('INPUTS', inputs)
|
|
||||||
// outliner.$$set({ inputs })
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
Reference in New Issue
Block a user