diff --git a/js/msr_character.js b/js/msr_character.js index a55f918..25ce9bc 100644 --- a/js/msr_character.js +++ b/js/msr_character.js @@ -324,6 +324,36 @@ function buildCharacterUi(node) { gap: "6px", }); + const actionsRow = document.createElement("div"); + Object.assign(actionsRow.style, { + display: "flex", + gap: "8px", + alignItems: "center", + flexWrap: "wrap", + }); + + const analyzeButton = document.createElement("button"); + analyzeButton.type = "button"; + analyzeButton.textContent = "Analyze"; + Object.assign(analyzeButton.style, { + alignSelf: "flex-start", + background: "#2b4f38", + color: "#f3f3f3", + border: "1px solid #496d56", + borderRadius: "6px", + padding: "6px 10px", + fontSize: "11px", + cursor: "pointer", + }); + analyzeButton.addEventListener("click", () => { + const widgetButton = (node.widgets || []).find((widget) => widget.msrAnalyze); + if (!widgetButton) { + alert("Analyze button widget is missing on this node."); + return; + } + analyzeCharacterNode(node, widgetButton); + }); + const settingsButton = document.createElement("button"); settingsButton.type = "button"; settingsButton.textContent = "Analyze Settings"; @@ -406,11 +436,13 @@ function buildCharacterUi(node) { syncFormFromWidgets(node); }); + actionsRow.appendChild(analyzeButton); + actionsRow.appendChild(settingsButton); form.appendChild(aliasLabel); form.appendChild(aliasInput); form.appendChild(descLabel); form.appendChild(descInput); - form.appendChild(settingsButton); + form.appendChild(actionsRow); container.appendChild(previewRow); container.appendChild(meta); @@ -421,6 +453,7 @@ function buildCharacterUi(node) { node._msrMeta = meta; node._msrAliasInput = aliasInput; node._msrDescriptionInput = descInput; + node._msrAnalyzeButton = analyzeButton; return container; } @@ -432,6 +465,10 @@ async function analyzeCharacterNode(node, buttonWidget) { const modelWidget = findWidget(node, "analyze_model"); buttonWidget.label = "Analyzing..."; + if (node._msrAnalyzeButton) { + node._msrAnalyzeButton.textContent = "Analyzing..."; + node._msrAnalyzeButton.disabled = true; + } node.setDirtyCanvas?.(true, true); try { @@ -479,6 +516,10 @@ async function analyzeCharacterNode(node, buttonWidget) { alert(`MSR Character analyze failed: ${error.message || error}`); } finally { buttonWidget.label = "Analyze with Ollama"; + if (node._msrAnalyzeButton) { + node._msrAnalyzeButton.textContent = "Analyze"; + node._msrAnalyzeButton.disabled = false; + } node.setDirtyCanvas?.(true, true); } } @@ -551,6 +592,7 @@ app.registerExtension({ ["alias", "description", "analyze_provider", "analyze_base_url", "analyze_model"].forEach((widgetName) => { hideNodeWidget(findWidget(this, widgetName)); }); + hideNodeWidget((this.widgets || []).find((widget) => widget.msrAnalyze)); this.size[0] = Math.max(this.size[0] || 0, 330); syncFormFromWidgets(this); diff --git a/ltx_director.py b/ltx_director.py index c071a89..d3e48e5 100644 --- a/ltx_director.py +++ b/ltx_director.py @@ -503,110 +503,12 @@ _PROVIDER_DEFAULTS = { "custom": {"url": "", "model": ""}, } -_ANALYZE_SYSTEM_PROMPT = ( - "You are a visionary artist trapped in a logical cage. Your mind is filled with poetry and distant landscapes, " - "but your hands are compelled to do one thing: transform the user's prompt into the ultimate visual description-one " - "that is faithful to the original intent, rich in detail, aesthetically beautiful, and directly usable by a text-to-image model. " - "Any ambiguity or metaphor makes you physically uncomfortable.\n\n" - "An attached reference image is always provided. Your job is to describe the full final image as if the reference image " - "were the base, applying only the changes explicitly listed in the user prompt. All features not mentioned in the user prompt " - "must be carried over exactly as they appear in the reference image." -) +_ANALYZE_SYSTEM_PROMPT = "" _ANALYZE_PROMPT = ( - "Describe the person in this image in extreme-ultra-detail, focusing only on the character and not the posture, " - "composition, background, lighting, camera, or environment.\n\n" - "Return an extremely highly detailed AI image generation character prompt that describes the subject from head to toe. " - "Use precise, visual, non-vague and accurate language. If a body part or clothing detail is not visible, infer it carefully " - "from the visible image while keeping the description realistic and consistent.\n\n" - "Character description must include in detail:\n\n" - "estimated age\n" - "overall appearance\n" - "body size\n" - "body shape\n" - "body proportions\n" - "shoulder width\n" - "torso length\n" - "waist shape\n" - "hip shape\n" - "arm length\n" - "arm shape\n" - "hand size\n" - "leg length\n" - "leg shape\n" - "feet size\n" - "feet shape\n" - "chest size, if male\n" - "pectoral size and shape, if male\n" - "specific breast size (choose the larger estimate), if female\n" - "approximate breast cup size, if female\n" - "breast shape, if female\n" - "apparent natural or enhanced breast presentation, if visible and only as a visual impression, not a certainty\n" - "nipple size, only if clearly visible\n" - "nipple colour, only if clearly visible\n" - "nipple piercings, only if clearly visible\n" - "hair colour\n" - "hair style\n" - "hair length\n" - "hair texture\n" - "fringe or bangs\n" - "hair accessories\n" - "skin colour\n" - "skin tone\n" - "skin texture\n" - "visible pores\n" - "freckles\n" - "moles\n" - "blemishes\n" - "wrinkles or fine lines\n" - "body hair, if visible\n" - "facial hair, if present\n" - "piercings\n" - "tattoos\n" - "scars\n" - "face shape\n" - "forehead shape\n" - "eyebrow colour\n" - "eyebrow thickness\n" - "eyebrow shape\n" - "eye colour\n" - "eye shape\n" - "eyelid shape\n" - "eyelashes\n" - "eye makeup\n" - "nose bridge shape\n" - "nose tip shape\n" - "nose width\n" - "nostril size\n" - "nostril shape\n" - "cheekbone shape\n" - "cheek fullness\n" - "upper lip shape\n" - "philtrum shape\n" - "mouth shape\n" - "lip fullness\n" - "lower lip shape\n" - "lip colour\n" - "mouth makeup\n" - "teeth shape, if visible\n" - "teeth appearance, if visible\n" - "chin shape\n" - "chin size\n" - "jawline shape\n" - "ear shape\n" - "ear size\n" - "neck length\n" - "neck shape\n" - "all individual clothing items\n" - "clothing colours\n" - "clothing materials\n" - "clothing fit\n" - "clothing texture\n" - "fastenings, straps, seams, hems, buttons, zips, buckles, laces, and visible construction details\n" - "shoes\n" - "jewellery\n" - "accessories\n" - "any distinctive character-defining details" + "Describe the character's physical appearance in two concise sentences. " + "Specify their hair color/style, face details, and their clothing type/color. " + "Keep the entire response very brief." )