feat(node): Current node panel with one list of saved parameters
The Node Assistant becomes "Current node" (当前节点): one page instead of the "actions" / "parameter sets" tabs, model actions on top, Parameters below. - node_parameter_sets.js: the node type's saved values from the material library and from recipes' parameter sets as one list; the same values saved in both are one entry naming both sources. For the selected node, exactly what an entry would change; seeds (and their after-run control), model files and choices this computer lacks are kept. - ui_node_parameters.js: the list, "apply these N" (one undo in the receipt) and "save these values". - api/node_material.py: POST /anomalous/save_node_material keeps one node's values as a node_parameter_selection material, deduplicated by type and values; the library lists it under parameters. - Removed: ui_node_presets.js with its hover popovers, the beta notice and their styles and strings. The panel and help texts use the new name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
86d165b891
commit
a173e4e561
@@ -0,0 +1,65 @@
|
||||
"""Saving one canvas node's current values as a parameter material (the current-node panel's "save")."""
|
||||
|
||||
import asyncio
|
||||
import hashlib
|
||||
import json
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
from . import materials
|
||||
from .material_schema import MAX_MATERIAL_NAME_LENGTH, _material_summary, _normalise_material_tags
|
||||
from .notebooks import MAX_NOTEBOOK_BYTES
|
||||
|
||||
|
||||
def _node_material(payload):
|
||||
node = payload.get("node")
|
||||
if not isinstance(node, dict):
|
||||
raise ValueError("Invalid node")
|
||||
node_type = node.get("type")
|
||||
if not isinstance(node_type, str) or not node_type.strip() or len(node_type) > 200:
|
||||
raise ValueError("Invalid node type")
|
||||
values = node.get("widgets_values")
|
||||
if not isinstance(values, list) or not values:
|
||||
raise ValueError("Invalid node values")
|
||||
encoded = json.dumps([node_type, values], ensure_ascii=False, allow_nan=False, sort_keys=True)
|
||||
if len(encoded.encode("utf-8")) > MAX_NOTEBOOK_BYTES:
|
||||
raise ValueError("Node values are too large")
|
||||
title = node.get("title") if isinstance(node.get("title"), str) else ""
|
||||
title = title.strip()[:200] or node_type
|
||||
name = payload.get("name") or title
|
||||
if not isinstance(name, str) or not (name := name.strip()) or len(name) > MAX_MATERIAL_NAME_LENGTH:
|
||||
raise ValueError("Invalid material name")
|
||||
return {
|
||||
"schema_version": materials.MATERIAL_SCHEMA_VERSION,
|
||||
"id": uuid.uuid4().hex,
|
||||
"kind": "node_parameter_selection",
|
||||
"name": name,
|
||||
"tags": _normalise_material_tags(payload.get("tags", [])),
|
||||
"timestamp": int(time.time() * 1000),
|
||||
"source": {
|
||||
"type": "canvas_node",
|
||||
"parameter_name": title,
|
||||
"parameter_signature": hashlib.sha256(encoded.encode("utf-8")).hexdigest(),
|
||||
},
|
||||
"workflow": {"nodes": [{"id": 1, "type": node_type, "title": title, "widgets_values": values}]},
|
||||
"selection": {"scope": "nodes", "node_ids": [1]},
|
||||
"capabilities": ["apply_node_parameters"],
|
||||
}
|
||||
|
||||
|
||||
async def api_save_node_material(request):
|
||||
try:
|
||||
material = _node_material(await request.json())
|
||||
except (AttributeError, TypeError, ValueError):
|
||||
return web.json_response({"status": "error", "message": "Invalid node material"}, status=400)
|
||||
filename = f"material_{int(time.time())}_{material['id']}.json"
|
||||
try:
|
||||
duplicate = await asyncio.to_thread(
|
||||
materials._persist_parameter_material, materials.get_materials_dir(), filename, material, False)
|
||||
except (OSError, ValueError):
|
||||
return web.json_response({"status": "error", "message": "Could not save node material"}, status=500)
|
||||
if duplicate:
|
||||
return web.json_response({"status": "duplicate", "filename": duplicate["filename"], "name": duplicate["name"]}, status=409)
|
||||
return web.json_response({"status": "success", "filename": filename, "material": _material_summary(filename, material)})
|
||||
Reference in New Issue
Block a user