Extract MSR characters into reusable nodes

This commit is contained in:
OpenClaw Agent
2026-07-10 09:23:01 +00:00
parent 119692160a
commit e68357f384
5 changed files with 512 additions and 92 deletions

View File

@@ -788,13 +788,8 @@ function parseInitial(jsonStr) {
reference_mode: "OFF",
analyzeProvider: "ollama",
analyzeBaseUrl: "",
analyzeModel: "",
characters: [
{ images: [], description: "" },
{ images: [], description: "" },
{ images: [], description: "" }
]
};
analyzeModel: ""
};
try {
if (jsonStr) {
const p = JSON.parse(jsonStr);
@@ -819,16 +814,7 @@ function parseInitial(jsonStr) {
if (p.reference_mode !== undefined) parsed.reference_mode = p.reference_mode;
if (p.analyzeProvider !== undefined) parsed.analyzeProvider = p.analyzeProvider;
if (p.analyzeBaseUrl !== undefined) parsed.analyzeBaseUrl = p.analyzeBaseUrl;
if (p.analyzeModel !== undefined) parsed.analyzeModel = p.analyzeModel;
if (Array.isArray(p.characters)) {
parsed.characters = p.characters.map(c => ({
images: Array.isArray(c.images) ? c.images : [],
description: c.description || ""
}));
while (parsed.characters.length < 3) {
parsed.characters.push({ images: [], description: "" });
}
}
if (p.analyzeModel !== undefined) parsed.analyzeModel = p.analyzeModel;
if (Array.isArray(p.segments)) {
parsed.segments = p.segments.map(stripImageSegmentTransient);
}
@@ -4055,10 +4041,7 @@ class TimelineEditor {
this.wrapper.appendChild(propContainer);
this.wrapper.appendChild(this.globalPropContainer);
// --- Character reference slots (3 @char panels) at the bottom of the editor ---
this.createCharacterSlots(this.wrapper);
// --- @char autocomplete on both prompt fields ---
// --- @char autocomplete on both prompt fields ---
if (this.globalPromptInput) this.setupAutocomplete(this.globalPromptInput);
if (this.promptInput) this.setupAutocomplete(this.promptInput);
@@ -9336,11 +9319,14 @@ class TimelineEditor {
if (!this._autocompleteMenus) this._autocompleteMenus = [];
this._autocompleteMenus.push(menu);
const suggestions = [
{ tag: "@char1", label: "Character 1" },
{ tag: "@char2", label: "Character 2" },
{ tag: "@char3", label: "Character 3" }
];
const suggestions = [
{ tag: "@char1", label: "Character 1" },
{ tag: "@char2", label: "Character 2" },
{ tag: "@char3", label: "Character 3" },
{ tag: "@char4", label: "Character 4" },
{ tag: "@char5", label: "Character 5" },
{ tag: "@char6", label: "Character 6" }
];
let activeIndex = 0;
let showMenu = false;
@@ -9554,15 +9540,11 @@ class TimelineEditor {
fileSize: this.timeline.retakeVideo.fileSize,
} : null,
normalStartFrame: this.timeline.normalStartFrame,
normalDurationFrames: this.timeline.normalDurationFrames,
normalDurationFrames: this.timeline.normalDurationFrames,
reference_mode: this.timeline.reference_mode || "OFF",
analyzeProvider: this.timeline.analyzeProvider || "ollama",
analyzeBaseUrl: this.timeline.analyzeBaseUrl || "",
analyzeModel: this.timeline.analyzeModel || "",
characters: (this.timeline.characters || []).map(c => ({
images: (c.images || []).map(img => img.b64 ? { b64: img.b64, name: img.name } : { name: img.name }),
description: c.description || ""
})),
analyzeModel: this.timeline.analyzeModel || "",
segments: sortedSegments.map(stripImageSegmentTransient),
motionSegments: (this.timeline.motionSegments || []).map(stripMotionSegmentTransient),
audioSegments: (this.timeline.audioSegments || []).map(stripAudioSegmentTransient)
@@ -12225,12 +12207,9 @@ app.registerExtension({
const canvasH = self._timelineEditor ? self._timelineEditor.canvasHeight : CANVAS_HEIGHT;
const propH = self._timelineEditor ? (self._timelineEditor.propHeight || 90) : 90;
const globalPropH = self._timelineEditor ? (self._timelineEditor.globalPropHeight || 60) : 60;
// Reserve room for the @char reference panel at the bottom so the node doesn't
// collapse and crop it whenever ComfyUI recomputes the node height.
const charPanelH = self._timelineEditor ? (self._timelineEditor.charPanelHeight || 150) : 150;
const nodeWidth = self.size?.[0] || width || 1375;
return [Math.max(10, nodeWidth - 30), canvasH + propH + globalPropH + charPanelH + 160];
};
const nodeWidth = self.size?.[0] || width || 1375;
return [Math.max(10, nodeWidth - 30), canvasH + propH + globalPropH + 160];
};
setTimeout(() => {
try {