Region widget
This commit is contained in:
@@ -14,18 +14,20 @@ function isBoundingBox(param: any): param is BoundingBox {
|
|||||||
|
|
||||||
export interface ComfyMultiRegionProperties extends ComfyWidgetProperties {
|
export interface ComfyMultiRegionProperties extends ComfyWidgetProperties {
|
||||||
regionCount: number,
|
regionCount: number,
|
||||||
totalWidth: number,
|
canvasWidth: number,
|
||||||
totalHeight: number,
|
canvasHeight: number,
|
||||||
inputType: "size" | "image"
|
inputType: "size" | "image"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_BBOX: BoundingBox = [0.4, 0.4, 0.2, 0.2];
|
||||||
|
|
||||||
export default class ComfyMultiRegionNode extends ComfyWidgetNode<BoundingBox[]> {
|
export default class ComfyMultiRegionNode extends ComfyWidgetNode<BoundingBox[]> {
|
||||||
override properties: ComfyMultiRegionProperties = {
|
override properties: ComfyMultiRegionProperties = {
|
||||||
tags: [],
|
tags: [],
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
regionCount: 1,
|
regionCount: 1,
|
||||||
totalWidth: 512,
|
canvasWidth: 512,
|
||||||
totalHeight: 512,
|
canvasHeight: 512,
|
||||||
inputType: "size"
|
inputType: "size"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,12 +51,13 @@ export default class ComfyMultiRegionNode extends ComfyWidgetNode<BoundingBox[]>
|
|||||||
}
|
}
|
||||||
|
|
||||||
override svelteComponentType = MultiRegionWidget;
|
override svelteComponentType = MultiRegionWidget;
|
||||||
override defaultValue: BoundingBox[] = [[0.4, 0.4, 0.8, 0.2]];
|
override defaultValue: BoundingBox[] = [[...DEFAULT_BBOX]];
|
||||||
override outputSlotName = null;
|
override outputSlotName = null;
|
||||||
override storeActionName = "store";
|
override storeActionName = "store";
|
||||||
override changedEventName = "changed";
|
override changedEventName = "changed";
|
||||||
|
|
||||||
sizeChanged: Writable<boolean> = writable(true);
|
sizeChanged: Writable<boolean> = writable(true);
|
||||||
|
regionsChanged: Writable<boolean> = writable(true);
|
||||||
|
|
||||||
override onPropertyChanged(property: any, value: any) {
|
override onPropertyChanged(property: any, value: any) {
|
||||||
if (property === "regionCount") {
|
if (property === "regionCount") {
|
||||||
@@ -66,16 +69,18 @@ export default class ComfyMultiRegionNode extends ComfyWidgetNode<BoundingBox[]>
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(name?: string) {
|
constructor(name?: string) {
|
||||||
super(name, [[0.4, 0.4, 0.8, 0.2]])
|
super(name, [[...DEFAULT_BBOX]])
|
||||||
}
|
}
|
||||||
|
|
||||||
override onExecute() {
|
override onExecute() {
|
||||||
let width = this.getInputData(1)
|
let width = this.getInputData(1) || 0
|
||||||
let height = this.getInputData(2)
|
let height = this.getInputData(2) || 0
|
||||||
|
|
||||||
if (width != null && height != null && width != this.properties.width && height != this.properties.height) {
|
if (width != this.properties.canvasWidth || height != this.properties.canvasHeight) {
|
||||||
this.properties.width = width;
|
console.warn("SIZCHANGE", width, height, this.properties.canvasWidth, this.properties.canvasHeight)
|
||||||
this.properties.height = height;
|
this.properties.canvasWidth = width;
|
||||||
|
this.properties.canvasHeight = height;
|
||||||
|
this.sizeChanged.set(true);
|
||||||
this.updateSize();
|
this.updateSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,10 +91,10 @@ export default class ComfyMultiRegionNode extends ComfyWidgetNode<BoundingBox[]>
|
|||||||
if (bbox != null) {
|
if (bbox != null) {
|
||||||
const xOutput = this.outputs[index + 1]
|
const xOutput = this.outputs[index + 1]
|
||||||
if (xOutput != null) {
|
if (xOutput != null) {
|
||||||
this.setOutputData(index + 1, bbox[0] * this.properties.width)
|
this.setOutputData(index + 1, bbox[0] * this.properties.canvasWidth)
|
||||||
this.setOutputData(index + 2, bbox[1] * this.properties.height)
|
this.setOutputData(index + 2, bbox[1] * this.properties.canvasHeight)
|
||||||
this.setOutputData(index + 3, bbox[2] * this.properties.width)
|
this.setOutputData(index + 3, bbox[2] * this.properties.canvasWidth)
|
||||||
this.setOutputData(index + 4, bbox[3] * this.properties.height)
|
this.setOutputData(index + 4, bbox[3] * this.properties.canvasHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,12 +116,15 @@ export default class ComfyMultiRegionNode extends ComfyWidgetNode<BoundingBox[]>
|
|||||||
this.addOutput(`h${index + 1}`, "number")
|
this.addOutput(`h${index + 1}`, "number")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.regionsChanged.set(true);
|
||||||
|
this.notifyPropsChanged();
|
||||||
|
|
||||||
this.setValue(this.getValue())
|
this.setValue(this.getValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateSize(value?: BoundingBox[]): BoundingBox[] {
|
private updateSize(value?: BoundingBox[]): BoundingBox[] {
|
||||||
this.properties.width = Math.max(this.properties.width, 1);
|
this.properties.canvasWidth = Math.max(this.properties.canvasWidth, 0);
|
||||||
this.properties.height = Math.max(this.properties.height, 1);
|
this.properties.canvasHeight = Math.max(this.properties.canvasHeight, 0);
|
||||||
|
|
||||||
value ||= this.getValue();
|
value ||= this.getValue();
|
||||||
|
|
||||||
@@ -128,6 +136,7 @@ export default class ComfyMultiRegionNode extends ComfyWidgetNode<BoundingBox[]>
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.sizeChanged.set(true);
|
this.sizeChanged.set(true);
|
||||||
|
this.notifyPropsChanged();
|
||||||
|
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
@@ -136,13 +145,19 @@ export default class ComfyMultiRegionNode extends ComfyWidgetNode<BoundingBox[]>
|
|||||||
if (param == null || this.properties.regionCount <= 0)
|
if (param == null || this.properties.regionCount <= 0)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
let val = []
|
||||||
|
|
||||||
if (isBoundingBox(param))
|
if (isBoundingBox(param))
|
||||||
return this.updateSize([param])
|
val = this.updateSize([param])
|
||||||
|
|
||||||
if (Array.isArray(param) && param.every(isBoundingBox))
|
if (Array.isArray(param) && param.every(isBoundingBox))
|
||||||
return this.updateSize(param.splice(0, this.properties.regionCount))
|
val = this.updateSize(param.splice(0, this.properties.regionCount))
|
||||||
|
|
||||||
return null;
|
// Fill the array with missing regions
|
||||||
|
for (let index = val.length; index < this.properties.regionCount; index++)
|
||||||
|
val.push([...DEFAULT_BBOX])
|
||||||
|
|
||||||
|
return val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,28 +8,30 @@
|
|||||||
import type { ComfyMultiRegionNode } from "$lib/nodes/widgets";
|
import type { ComfyMultiRegionNode } from "$lib/nodes/widgets";
|
||||||
import type { BoundingBox } from "$lib/nodes/widgets/ComfyMultiRegionNode";
|
import type { BoundingBox } from "$lib/nodes/widgets/ComfyMultiRegionNode";
|
||||||
import type { WidgetLayout } from "$lib/stores/layoutStates";
|
import type { WidgetLayout } from "$lib/stores/layoutStates";
|
||||||
import { Block } from "@gradio/atoms";
|
import { Block, BlockLabel } from "@gradio/atoms";
|
||||||
|
import { Chart as SquareIcon } from "@gradio/icons";
|
||||||
import { writable, type Writable } from "svelte/store";
|
import { writable, type Writable } from "svelte/store";
|
||||||
import { generateBlankCanvas, loadImage } from "./utils";
|
import { generateBlankCanvas, loadImage } from "./utils";
|
||||||
|
import { clamp } from "$lib/utils";
|
||||||
|
|
||||||
// ref: https://html-color.codes/
|
// ref: https://html-color.codes/
|
||||||
const COLOR_MAP: [string, string][] = [
|
const COLOR_MAP: [string, string][] = [
|
||||||
['#ff0000', '2px solid rgba(255, 0, 0, 0.3)'], // red
|
['#ff0000', 'rgba(255, 0, 0, 0.3)'], // red
|
||||||
['#ff9900', '2px solid rgba(255, 153, 0, 0.3)'], // orange
|
['#ff9900', 'rgba(255, 153, 0, 0.3)'], // orange
|
||||||
['#ffff00', '2px solid rgba(255, 255, 0, 0.3)'], // yellow
|
['#ffff00', 'rgba(255, 255, 0, 0.3)'], // yellow
|
||||||
['#33cc33', '2px solid rgba(51, 204, 51, 0.3)'], // green
|
['#33cc33', 'rgba(51, 204, 51, 0.3)'], // green
|
||||||
['#33cccc', '2px solid rgba(51, 204, 204, 0.3)'], // indigo
|
['#33cccc', 'rgba(51, 204, 204, 0.3)'], // indigo
|
||||||
['#0066ff', '2px solid rgba(0, 102, 255, 0.3)'], // blue
|
['#0066ff', 'rgba(0, 102, 255, 0.3)'], // blue
|
||||||
['#6600ff', '2px solid rgba(102, 0, 255, 0.3)'], // purple
|
['#6600ff', 'rgba(102, 0, 255, 0.3)'], // purple
|
||||||
['#cc00cc', '2px solid rgba(204, 0, 204, 0.3)'], // dark pink
|
['#cc00cc', 'rgba(204, 0, 204, 0.3)'], // dark pink
|
||||||
['#ff6666', '2px solid rgba(255, 102, 102, 0.3)'], // light red
|
['#ff6666', 'rgba(255, 102, 102, 0.3)'], // light red
|
||||||
['#ffcc66', '2px solid rgba(255, 204, 102, 0.3)'], // light orange
|
['#ffcc66', 'rgba(255, 204, 102, 0.3)'], // light orange
|
||||||
['#99cc00', '2px solid rgba(153, 204, 0, 0.3)'], // lime green
|
['#99cc00', 'rgba(153, 204, 0, 0.3)'], // lime green
|
||||||
['#00cc99', '2px solid rgba(0, 204, 153, 0.3)'], // teal
|
['#00cc99', 'rgba(0, 204, 153, 0.3)'], // teal
|
||||||
['#0099cc', '2px solid rgba(0, 153, 204, 0.3)'], // steel blue
|
['#0099cc', 'rgba(0, 153, 204, 0.3)'], // steel blue
|
||||||
['#9933cc', '2px solid rgba(153, 51, 204, 0.3)'], // lavender
|
['#9933cc', 'rgba(153, 51, 204, 0.3)'], // lavender
|
||||||
['#ff3399', '2px solid rgba(255, 51, 153, 0.3)'], // hot pink
|
['#ff3399', 'rgba(255, 51, 153, 0.3)'], // hot pink
|
||||||
['#996633', '2px solid rgba(153, 102, 51, 0.3)'], // brown
|
['#996633', 'rgba(153, 102, 51, 0.3)'], // brown
|
||||||
];
|
];
|
||||||
|
|
||||||
export let widget: WidgetLayout | null = null;
|
export let widget: WidgetLayout | null = null;
|
||||||
@@ -41,17 +43,29 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
let node: ComfyMultiRegionNode | null = null;
|
let node: ComfyMultiRegionNode | null = null;
|
||||||
let nodeValue: Writable<BoundingBox[]> = writable([]);
|
let nodeValue: Writable<BoundingBox[]> = writable([]);
|
||||||
let sizeChanged: Writable<boolean> = writable(false);
|
let sizeChanged: Writable<boolean> = writable(false);
|
||||||
|
let regionsChanged: Writable<boolean> = writable(false);
|
||||||
|
let propsChanged: Writable<number> = writable(0);
|
||||||
|
let selectedIndex: number = 0;
|
||||||
|
|
||||||
$: widget && setNodeValue(widget);
|
$: widget && setNodeValue(widget);
|
||||||
|
|
||||||
function setNodeValue(widget: WidgetLayout) {
|
function setNodeValue(widget: WidgetLayout) {
|
||||||
|
console.error("SETNODEVALUE")
|
||||||
if (widget) {
|
if (widget) {
|
||||||
node = widget.node as ComfyMultiRegionNode
|
node = widget.node as ComfyMultiRegionNode
|
||||||
nodeValue = node.value;
|
nodeValue = node.value;
|
||||||
|
propsChanged = node.propsChanged;
|
||||||
sizeChanged = node.sizeChanged;
|
sizeChanged = node.sizeChanged;
|
||||||
|
regionsChanged = node.regionsChanged;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let showWidget: boolean = false;
|
||||||
|
|
||||||
|
if (sizeChanged && $sizeChanged) {
|
||||||
|
console.error("Z@");
|
||||||
|
}
|
||||||
|
|
||||||
type DisplayBoundingBox = {
|
type DisplayBoundingBox = {
|
||||||
xPx: number,
|
xPx: number,
|
||||||
yPx: number,
|
yPx: number,
|
||||||
@@ -62,8 +76,7 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
borderColor: string
|
borderColor: string
|
||||||
}
|
}
|
||||||
|
|
||||||
let displayBoxes = []
|
let displayBoxes = [];
|
||||||
let changed = true;
|
|
||||||
|
|
||||||
async function recreateDisplayBoxes(_node?: ComfyMultiRegionNode, bboxes?: BoundingBox[]): Promise<DisplayBoundingBox[]> {
|
async function recreateDisplayBoxes(_node?: ComfyMultiRegionNode, bboxes?: BoundingBox[]): Promise<DisplayBoundingBox[]> {
|
||||||
_node ||= node;
|
_node ||= node;
|
||||||
@@ -72,7 +85,8 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
console.debug("[MultiRegionWidget] Recreate!", bboxes, imageElem, _node)
|
console.debug("[MultiRegionWidget] Recreate!", bboxes, imageElem, _node)
|
||||||
|
|
||||||
if (_node != null && imageElem != null && imageContainer != null) {
|
if (_node != null && imageElem != null && imageContainer != null) {
|
||||||
await updateImage(_node.properties.totalWidth, _node.properties.totalHeight);
|
selectedIndex = clamp(selectedIndex, 0, bboxes.length - 1);
|
||||||
|
await updateImage(_node.properties.canvasWidth, _node.properties.canvasHeight);
|
||||||
return bboxes.map((b, i) => displayBoundingBox(b, i, imageElem))
|
return bboxes.map((b, i) => displayBoundingBox(b, i, imageElem))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -81,21 +95,29 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
}
|
}
|
||||||
|
|
||||||
$: if (node != null && $sizeChanged) {
|
$: if (node != null && $sizeChanged) {
|
||||||
updateImage(node.properties.totalWidth, node.properties.totalHeight)
|
console.warn("SIZCHANGEd")
|
||||||
.then(() => $sizeChanged = false)
|
updateImage(node.properties.canvasWidth, node.properties.canvasHeight)
|
||||||
|
.then(() => {
|
||||||
|
return recreateDisplayBoxes()
|
||||||
|
})
|
||||||
|
.then(dbs => {
|
||||||
|
displayBoxes = dbs;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
displayBoxes = await recreateDisplayBoxes(node, $nodeValue);
|
displayBoxes = await recreateDisplayBoxes(node, $nodeValue);
|
||||||
})
|
})
|
||||||
|
|
||||||
$: if (changed) {
|
$: if ($regionsChanged) {
|
||||||
changed = false;
|
$regionsChanged = false;
|
||||||
recreateDisplayBoxes(node, $nodeValue).then(dbs => displayBoxes = dbs);
|
recreateDisplayBoxes(node, $nodeValue).then(dbs => displayBoxes = dbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateImage(width: number, height: number) {
|
async function updateImage(width: number, height: number) {
|
||||||
const blank = generateBlankCanvas(width, height);
|
showWidget = width > 0 && height > 0;
|
||||||
|
console.error("SHOW", showWidget, width, height)
|
||||||
|
const blank = generateBlankCanvas(width, height, "transparent");
|
||||||
const url = blank.toDataURL();
|
const url = blank.toDataURL();
|
||||||
const newImg = await loadImage(url);
|
const newImg = await loadImage(url);
|
||||||
newImg.classList.add("regions-image");
|
newImg.classList.add("regions-image");
|
||||||
@@ -103,6 +125,7 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
imageContainer.replaceChildren(newImg)
|
imageContainer.replaceChildren(newImg)
|
||||||
}
|
}
|
||||||
imageElem = newImg;
|
imageElem = newImg;
|
||||||
|
imageElem.style.border = `${BORDER_SIZE_PX}px solid var(--border-color-primary)`;
|
||||||
$sizeChanged = false;
|
$sizeChanged = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,14 +142,14 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
// client: image widget display size
|
// client: image widget display size
|
||||||
// natural: content image real size
|
// natural: content image real size
|
||||||
const vpScale = Math.min(imageElem.clientWidth / imageElem.naturalWidth, imageElem.clientHeight / imageElem.naturalHeight);
|
const vpScale = Math.min(imageElem.clientWidth / imageElem.naturalWidth, imageElem.clientHeight / imageElem.naturalHeight);
|
||||||
const imageElemCenterX = imageElem.clientWidth / 2;
|
const imageElemCenterX = (imageElem.clientWidth) / 2;
|
||||||
const imageElemCenterY = imageElem.clientHeight / 2;
|
const imageElemCenterY = (imageElem.clientHeight) / 2;
|
||||||
const scaledX = imageElem.naturalWidth * vpScale;
|
const scaledX = imageElem.naturalWidth * vpScale;
|
||||||
const scaledY = imageElem.naturalHeight * vpScale;
|
const scaledY = imageElem.naturalHeight * vpScale;
|
||||||
const viewRectLeft = imageElemCenterX - scaledX / 2;
|
const viewRectLeft = imageElemCenterX - scaledX / 2 + BORDER_SIZE_PX;
|
||||||
const viewRectRight = imageElemCenterX + scaledX / 2;
|
const viewRectRight = imageElemCenterX + scaledX / 2 + BORDER_SIZE_PX;
|
||||||
const viewRectTop = imageElemCenterY - scaledY / 2;
|
const viewRectTop = imageElemCenterY - scaledY / 2 + BORDER_SIZE_PX;
|
||||||
const viewRectDown = imageElemCenterY + scaledY / 2;
|
const viewRectDown = imageElemCenterY + scaledY / 2 + BORDER_SIZE_PX;
|
||||||
|
|
||||||
const xDiv = viewRectLeft + scaledX * x;
|
const xDiv = viewRectLeft + scaledX * x;
|
||||||
const yDiv = viewRectTop + scaledY * y;
|
const yDiv = viewRectTop + scaledY * y;
|
||||||
@@ -143,7 +166,7 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
const maxH = maxSize / scaledY;
|
const maxH = maxSize / scaledY;
|
||||||
const warnLargeSize = w > maxW || h > maxH
|
const warnLargeSize = w > maxW || h > maxH
|
||||||
|
|
||||||
const [bgColor, borderColor] = COLOR_MAP[index % COLOR_MAP.length]
|
const [borderColor, bgColor] = COLOR_MAP[index % COLOR_MAP.length]
|
||||||
|
|
||||||
out ||= {} as DisplayBoundingBox
|
out ||= {} as DisplayBoundingBox
|
||||||
out.xPx= xDiv;
|
out.xPx= xDiv;
|
||||||
@@ -159,6 +182,7 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
|
|
||||||
const RESIZE_BORDER = 5;
|
const RESIZE_BORDER = 5;
|
||||||
const MOVE_BORDER = 5;
|
const MOVE_BORDER = 5;
|
||||||
|
const BORDER_SIZE_PX = 3;
|
||||||
|
|
||||||
function updateCursorStyle(e: MouseEvent) {
|
function updateCursorStyle(e: MouseEvent) {
|
||||||
// This function changes the cursor style when hovering over the bounding box
|
// This function changes the cursor style when hovering over the bounding box
|
||||||
@@ -194,6 +218,8 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
if (!imageElem || !bbox)
|
if (!imageElem || !bbox)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
selectedIndex = index;
|
||||||
|
|
||||||
// Check if the click is inside the bounding box
|
// Check if the click is inside the bounding box
|
||||||
const div = e.target as HTMLDivElement;
|
const div = e.target as HTMLDivElement;
|
||||||
const boxRect = div.getBoundingClientRect();
|
const boxRect = div.getBoundingClientRect();
|
||||||
@@ -344,7 +370,7 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
const onMouseUp = () => {
|
const onMouseUp = () => {
|
||||||
document.removeEventListener('mousemove', onMouseMove);
|
document.removeEventListener('mousemove', onMouseMove);
|
||||||
document.removeEventListener('mouseup', onMouseUp);
|
document.removeEventListener('mouseup', onMouseUp);
|
||||||
changed = true;
|
$regionsChanged = true;
|
||||||
$nodeValue = $nodeValue;
|
$nodeValue = $nodeValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,21 +386,36 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
|
|
||||||
<svelte:window on:resize={onResize}/>
|
<svelte:window on:resize={onResize}/>
|
||||||
|
|
||||||
<Block>
|
{#key $propsChanged}
|
||||||
|
<Block>
|
||||||
|
{#if widget?.attrs.title}
|
||||||
|
{@const label = widget.attrs.title}
|
||||||
|
<BlockLabel
|
||||||
|
label={label}
|
||||||
|
show_label={label != ""}
|
||||||
|
Icon={SquareIcon}
|
||||||
|
float={label != ""}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{#if showWidget}
|
||||||
<div class="regions-container">
|
<div class="regions-container">
|
||||||
<div bind:this={imageContainer} class="regions-image-container">
|
<div bind:this={imageContainer} class="regions-image-container">
|
||||||
<img bind:this={imageElem} class="regions-image"/>
|
<img bind:this={imageElem} class="regions-image"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="regions">
|
<div class="regions">
|
||||||
{#each displayBoxes as dBox, i}
|
{#each displayBoxes as dBox, i}
|
||||||
|
{@const selected = selectedIndex === i}
|
||||||
<div class="region"
|
<div class="region"
|
||||||
style:left="{dBox.xPx}px"
|
style:left="{dBox.xPx}px"
|
||||||
style:top="{dBox.yPx}px"
|
style:top="{dBox.yPx}px"
|
||||||
style:width="{dBox.widthPx}px"
|
style:width="{dBox.widthPx}px"
|
||||||
style:height="{dBox.heightPx}px"
|
style:height="{dBox.heightPx}px"
|
||||||
style:background={dBox.bgColor}
|
style:background={dBox.bgColor}
|
||||||
style:border={dBox.borderColor}
|
style:border-style={selected ? "solid" : "dotted"}
|
||||||
|
style:border-color={dBox.borderColor}
|
||||||
style:display="block"
|
style:display="block"
|
||||||
|
style:opacity={selected ? "100%" : "40%"}
|
||||||
|
style:z-index={selected ? "var(--layer-3)" : "var(--layer-2)"}
|
||||||
on:mousemove={updateCursorStyle}
|
on:mousemove={updateCursorStyle}
|
||||||
on:mousedown={(e) => onBoxMouseDown(e, i)}
|
on:mousedown={(e) => onBoxMouseDown(e, i)}
|
||||||
>
|
>
|
||||||
@@ -386,12 +427,24 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Block>
|
{:else}
|
||||||
|
<div class="regions-empty">
|
||||||
|
<span>(Empty canvas)</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</Block>
|
||||||
|
{/key}
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.regions-container {
|
.regions-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
.regions-image-container {
|
||||||
|
img {
|
||||||
|
border: 3px solid var(--input-border-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.regions {
|
.regions {
|
||||||
@@ -403,10 +456,10 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
|
|
||||||
.region {
|
.region {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: 2px solid blue;
|
|
||||||
background: teal;
|
|
||||||
z-index: var(--layer-3);
|
z-index: var(--layer-3);
|
||||||
cursor: move;
|
cursor: move;
|
||||||
|
border-style: dotted;
|
||||||
|
border-width: 2px;
|
||||||
|
|
||||||
.tip {
|
.tip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -421,4 +474,19 @@ const COLOR_MAP: [string, string][] = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.regions-empty {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
height: 10rem;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: bolder;
|
||||||
|
color: var(--comfy-accent-soft);
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user