Add accumulator preview reordering
This commit is contained in:
+16
@@ -343,6 +343,7 @@ try:
|
||||
LOOP_NODE_DISPLAY_NAME_MAPPINGS,
|
||||
accumulator_delete_entries,
|
||||
accumulator_list_entries,
|
||||
accumulator_move_entry,
|
||||
)
|
||||
from .prompt_builder import (
|
||||
build_camera_config_json,
|
||||
@@ -418,6 +419,7 @@ except ImportError:
|
||||
LOOP_NODE_DISPLAY_NAME_MAPPINGS,
|
||||
accumulator_delete_entries,
|
||||
accumulator_list_entries,
|
||||
accumulator_move_entry,
|
||||
)
|
||||
from prompt_builder import (
|
||||
build_camera_config_json,
|
||||
@@ -524,6 +526,20 @@ if PromptServer is not None and web is not None:
|
||||
except Exception as exc:
|
||||
return web.json_response({"error": str(exc)}, status=400)
|
||||
|
||||
@PromptServer.instance.routes.post("/sxcp/accumulator/move")
|
||||
async def sxcp_accumulator_move(request):
|
||||
try:
|
||||
payload = await request.json()
|
||||
result = accumulator_move_entry(
|
||||
store_key=str(payload.get("store_key") or ""),
|
||||
entry_id=str(payload.get("entry_id") or ""),
|
||||
index=int(payload.get("index") or 0),
|
||||
direction=str(payload.get("direction") or "up"),
|
||||
)
|
||||
return web.json_response(result)
|
||||
except Exception as exc:
|
||||
return web.json_response({"error": str(exc)}, status=400)
|
||||
|
||||
|
||||
class SxCPPromptBuilder:
|
||||
@classmethod
|
||||
|
||||
@@ -283,6 +283,57 @@ def accumulator_delete_entries(
|
||||
return result
|
||||
|
||||
|
||||
def accumulator_move_entry(
|
||||
store_key: str,
|
||||
entry_id: str = "",
|
||||
index: int = 0,
|
||||
direction: str = "up",
|
||||
) -> dict[str, Any]:
|
||||
key = str(store_key or "").strip()
|
||||
if not key:
|
||||
raise ValueError("store_key is required for accumulator preview actions")
|
||||
store = _ACCUMULATOR_STORES.setdefault(key, [])
|
||||
if not store:
|
||||
result = accumulator_list_entries(key)
|
||||
result["moved"] = False
|
||||
return result
|
||||
zero_index = -1
|
||||
entry_id = str(entry_id or "").strip()
|
||||
if entry_id:
|
||||
for current_index, entry in enumerate(store):
|
||||
if str(entry.get("id") or "") == entry_id:
|
||||
zero_index = current_index
|
||||
break
|
||||
elif int(index) > 0:
|
||||
candidate = int(index) - 1
|
||||
if candidate < len(store):
|
||||
zero_index = candidate
|
||||
else:
|
||||
raise ValueError("entry_id or 1-based index is required")
|
||||
if zero_index < 0:
|
||||
result = accumulator_list_entries(key)
|
||||
result["moved"] = False
|
||||
return result
|
||||
direction = str(direction or "up").strip().lower()
|
||||
if direction == "top":
|
||||
target_index = 0
|
||||
elif direction == "bottom":
|
||||
target_index = len(store) - 1
|
||||
elif direction == "down":
|
||||
target_index = min(len(store) - 1, zero_index + 1)
|
||||
else:
|
||||
target_index = max(0, zero_index - 1)
|
||||
moved = target_index != zero_index
|
||||
if moved:
|
||||
entry = store.pop(zero_index)
|
||||
store.insert(target_index, entry)
|
||||
result = accumulator_list_entries(key)
|
||||
result["moved"] = moved
|
||||
result["from_index"] = zero_index + 1
|
||||
result["to_index"] = target_index + 1
|
||||
return result
|
||||
|
||||
|
||||
def _require_image_saving() -> None:
|
||||
if folder_paths is None or np is None or Image is None:
|
||||
raise RuntimeError("Image preview/save helpers require ComfyUI image dependencies.")
|
||||
|
||||
@@ -154,6 +154,31 @@ async function deleteSelected(node) {
|
||||
}
|
||||
}
|
||||
|
||||
async function moveSelected(node, direction) {
|
||||
const key = storeKey(node);
|
||||
if (!key) {
|
||||
alert("Set the same explicit store_key on the Accumulator and Accumulator Preview first.");
|
||||
return;
|
||||
}
|
||||
const entry = selectedEntry(node);
|
||||
if (!entry) {
|
||||
alert("No accumulator entry selected.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data = await postJson("/sxcp/accumulator/move", {
|
||||
store_key: key,
|
||||
entry_id: entry.id || "",
|
||||
index: entry.id ? 0 : entry.index,
|
||||
direction,
|
||||
});
|
||||
setEntries(node, data.entries || [], `${data.status || ""}; moved=${data.moved ? "yes" : "no"}; rerun preview to refresh images`);
|
||||
} catch (err) {
|
||||
console.error(`[${EXTENSION}] move failed`, err);
|
||||
alert(`Move failed: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function clearStore(node) {
|
||||
const key = storeKey(node);
|
||||
if (!key) {
|
||||
@@ -183,6 +208,18 @@ function setupNode(node) {
|
||||
node._sxcpAccumulatorStatusWidget = node.addWidget("text", "accumulator_status", "no accumulator data", () => {});
|
||||
node._sxcpAccumulatorStatusWidget.serialize = false;
|
||||
}
|
||||
if (!node._sxcpMoveTopButton) {
|
||||
node._sxcpMoveTopButton = node.addWidget("button", "Move Selected Top", null, () => moveSelected(node, "top"));
|
||||
}
|
||||
if (!node._sxcpMoveUpButton) {
|
||||
node._sxcpMoveUpButton = node.addWidget("button", "Move Selected Up", null, () => moveSelected(node, "up"));
|
||||
}
|
||||
if (!node._sxcpMoveDownButton) {
|
||||
node._sxcpMoveDownButton = node.addWidget("button", "Move Selected Down", null, () => moveSelected(node, "down"));
|
||||
}
|
||||
if (!node._sxcpMoveBottomButton) {
|
||||
node._sxcpMoveBottomButton = node.addWidget("button", "Move Selected Bottom", null, () => moveSelected(node, "bottom"));
|
||||
}
|
||||
if (!node._sxcpDeleteSelectedButton) {
|
||||
node._sxcpDeleteSelectedButton = node.addWidget("button", "Delete Selected Entry", null, () => deleteSelected(node));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user