From b786d9bd3f1ffaa97004e19b31eca005c2b36a23 Mon Sep 17 00:00:00 2001 From: James Knox <93682381+jamesk9526@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:45:46 -0500 Subject: [PATCH] update To V2 --- NoxPromptNode.py | 2133 +--------- __init__.py | 24 + noxprompter/__init__.py | 31 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1047 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 1074 bytes .../__pycache__/common.cpython-312.pyc | Bin 0 -> 16797 bytes .../__pycache__/common.cpython-313.pyc | Bin 0 -> 15536 bytes .../__pycache__/constants.cpython-312.pyc | Bin 0 -> 245745 bytes .../__pycache__/constants.cpython-313.pyc | Bin 0 -> 224423 bytes noxprompter/common.py | 325 ++ noxprompter/constants.py | 3644 +++++++++++++++++ noxprompter/nodes/__init__.py | 75 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2012 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 1783 bytes ...nox_prompt_action_director.cpython-312.pyc | Bin 0 -> 9695 bytes ...nox_prompt_action_director.cpython-313.pyc | Bin 0 -> 9981 bytes ...nox_prompt_advanced_camera.cpython-312.pyc | Bin 0 -> 13564 bytes .../nox_prompt_analyzer.cpython-312.pyc | Bin 0 -> 6008 bytes .../nox_prompt_analyzer.cpython-313.pyc | Bin 0 -> 6021 bytes .../nox_prompt_builder.cpython-312.pyc | Bin 0 -> 19924 bytes .../nox_prompt_builder.cpython-313.pyc | Bin 0 -> 20527 bytes .../nox_prompt_camz.cpython-312.pyc | Bin 0 -> 9649 bytes .../nox_prompt_camz.cpython-313.pyc | Bin 0 -> 9958 bytes ...x_prompt_character_creator.cpython-312.pyc | Bin 0 -> 11574 bytes ...x_prompt_character_creator.cpython-313.pyc | Bin 0 -> 11823 bytes .../nox_prompt_combiner.cpython-312.pyc | Bin 0 -> 4318 bytes .../nox_prompt_combiner.cpython-313.pyc | Bin 0 -> 4443 bytes .../nox_prompt_enhancer.cpython-312.pyc | Bin 0 -> 6612 bytes .../nox_prompt_enhancer.cpython-313.pyc | Bin 0 -> 6730 bytes .../nox_prompt_human_designer.cpython-312.pyc | Bin 0 -> 14921 bytes .../nox_prompt_human_designer.cpython-313.pyc | Bin 0 -> 11254 bytes ...nox_prompt_lighting_master.cpython-312.pyc | Bin 0 -> 10968 bytes ...nox_prompt_lighting_master.cpython-313.pyc | Bin 0 -> 11191 bytes ...ox_prompt_narrative_weaver.cpython-312.pyc | Bin 0 -> 7263 bytes ...ox_prompt_narrative_weaver.cpython-313.pyc | Bin 0 -> 7329 bytes ...rompt_nsfw_action_director.cpython-312.pyc | Bin 0 -> 11479 bytes ...rompt_nsfw_action_director.cpython-313.pyc | Bin 0 -> 11868 bytes .../nox_prompt_nsfw_aio.cpython-312.pyc | Bin 0 -> 5164 bytes .../nox_prompt_nsfw_designer.cpython-312.pyc | Bin 0 -> 16135 bytes .../nox_prompt_nsfw_designer.cpython-313.pyc | Bin 0 -> 16461 bytes .../nox_prompt_palette_mixer.cpython-312.pyc | Bin 0 -> 6141 bytes .../nox_prompt_palette_mixer.cpython-313.pyc | Bin 0 -> 6342 bytes ...x_prompt_pipeline_combiner.cpython-312.pyc | Bin 0 -> 17105 bytes ...x_prompt_pipeline_combiner.cpython-313.pyc | Bin 0 -> 17208 bytes ...ox_prompt_prompt_organizer.cpython-312.pyc | Bin 0 -> 3938 bytes .../nox_prompt_usage_guide.cpython-312.pyc | Bin 0 -> 6103 bytes ...x_prompt_wardrobe_designer.cpython-312.pyc | Bin 0 -> 10708 bytes ...x_prompt_wardrobe_designer.cpython-313.pyc | Bin 0 -> 10838 bytes .../nodes/nox_prompt_action_director.py | 193 + .../nodes/nox_prompt_advanced_camera.py | 278 ++ noxprompter/nodes/nox_prompt_analyzer.py | 130 + noxprompter/nodes/nox_prompt_builder.py | 444 ++ noxprompter/nodes/nox_prompt_camz.py | 219 + .../nodes/nox_prompt_character_creator.py | 238 ++ noxprompter/nodes/nox_prompt_combiner.py | 119 + noxprompter/nodes/nox_prompt_enhancer.py | 190 + .../nodes/nox_prompt_human_designer.py | 299 ++ .../nodes/nox_prompt_lighting_master.py | 226 + .../nodes/nox_prompt_narrative_weaver.py | 132 + .../nodes/nox_prompt_nsfw_action_director.py | 230 ++ noxprompter/nodes/nox_prompt_nsfw_aio.py | 116 + noxprompter/nodes/nox_prompt_nsfw_designer.py | 457 +++ noxprompter/nodes/nox_prompt_palette_mixer.py | 117 + .../nodes/nox_prompt_pipeline_combiner.py | 392 ++ .../nodes/nox_prompt_prompt_organizer.py | 129 + noxprompter/nodes/nox_prompt_usage_guide.py | 126 + .../nodes/nox_prompt_wardrobe_designer.py | 217 + .../prompt_builder/unit_builder_profile.json | 35 + 68 files changed, 8456 insertions(+), 2063 deletions(-) create mode 100644 noxprompter/__init__.py create mode 100644 noxprompter/__pycache__/__init__.cpython-312.pyc create mode 100644 noxprompter/__pycache__/__init__.cpython-313.pyc create mode 100644 noxprompter/__pycache__/common.cpython-312.pyc create mode 100644 noxprompter/__pycache__/common.cpython-313.pyc create mode 100644 noxprompter/__pycache__/constants.cpython-312.pyc create mode 100644 noxprompter/__pycache__/constants.cpython-313.pyc create mode 100644 noxprompter/common.py create mode 100644 noxprompter/constants.py create mode 100644 noxprompter/nodes/__init__.py create mode 100644 noxprompter/nodes/__pycache__/__init__.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/__init__.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_action_director.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_action_director.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_advanced_camera.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_analyzer.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_analyzer.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_builder.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_builder.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_camz.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_camz.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_character_creator.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_character_creator.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_combiner.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_combiner.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_enhancer.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_enhancer.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_human_designer.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_human_designer.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_lighting_master.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_lighting_master.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_narrative_weaver.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_narrative_weaver.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_nsfw_action_director.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_nsfw_action_director.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_nsfw_aio.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_nsfw_designer.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_nsfw_designer.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_palette_mixer.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_palette_mixer.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_pipeline_combiner.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_pipeline_combiner.cpython-313.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_prompt_organizer.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_usage_guide.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_wardrobe_designer.cpython-312.pyc create mode 100644 noxprompter/nodes/__pycache__/nox_prompt_wardrobe_designer.cpython-313.pyc create mode 100644 noxprompter/nodes/nox_prompt_action_director.py create mode 100644 noxprompter/nodes/nox_prompt_advanced_camera.py create mode 100644 noxprompter/nodes/nox_prompt_analyzer.py create mode 100644 noxprompter/nodes/nox_prompt_builder.py create mode 100644 noxprompter/nodes/nox_prompt_camz.py create mode 100644 noxprompter/nodes/nox_prompt_character_creator.py create mode 100644 noxprompter/nodes/nox_prompt_combiner.py create mode 100644 noxprompter/nodes/nox_prompt_enhancer.py create mode 100644 noxprompter/nodes/nox_prompt_human_designer.py create mode 100644 noxprompter/nodes/nox_prompt_lighting_master.py create mode 100644 noxprompter/nodes/nox_prompt_narrative_weaver.py create mode 100644 noxprompter/nodes/nox_prompt_nsfw_action_director.py create mode 100644 noxprompter/nodes/nox_prompt_nsfw_aio.py create mode 100644 noxprompter/nodes/nox_prompt_nsfw_designer.py create mode 100644 noxprompter/nodes/nox_prompt_palette_mixer.py create mode 100644 noxprompter/nodes/nox_prompt_pipeline_combiner.py create mode 100644 noxprompter/nodes/nox_prompt_prompt_organizer.py create mode 100644 noxprompter/nodes/nox_prompt_usage_guide.py create mode 100644 noxprompter/nodes/nox_prompt_wardrobe_designer.py create mode 100644 noxprompter/presets/prompt_builder/unit_builder_profile.json diff --git a/NoxPromptNode.py b/NoxPromptNode.py index d7248ca..0aef982 100644 --- a/NoxPromptNode.py +++ b/NoxPromptNode.py @@ -1,2074 +1,81 @@ -import random -import re +"""Legacy aggregator exposing Nox Prompter nodes and shared utilities.""" + +from __future__ import annotations + +from noxprompter import common as _common +from noxprompter import constants as _constants +from noxprompter import nodes as _nodes -FORMULA_CONFIGS = { - "Basic Formula": { - "description": "Subject + Scene + Motion — perfect for ideation and quick inspiration.", - "structure": ["subject", "scene", "motion"], - }, - "Advanced Formula": { - "description": "Subject (desc) + Scene (desc) + Motion (desc) + Aesthetic controls + Stylization.", - "structure": ["subject", "scene", "motion", "aesthetic", "stylization"], - }, -} -LIGHT_SOURCE_OPTIONS = { - "None": {"prompt": "", "summary": "No dedicated light source emphasis."}, - "Daylight": {"prompt": "daylight, sunlit ambience, warm highlights, natural light", "summary": "Sunlit scene with warm, natural illumination."}, - "Artificial Light": {"prompt": "artificial lighting, tungsten glow, controlled practicals", "summary": "Moody artificial lighting with cinematic practical sources."}, - "Moonlight": {"prompt": "moonlight, cool lunar glow, silver rim lighting", "summary": "Cool moonlit ambience with gentle highlights."}, - "Practical Light": {"prompt": "practical lighting, lamps, diegetic light sources", "summary": "Scene lit by visible practical fixtures."}, - "Firelight": {"prompt": "firelight, ember glow, flickering warm highlights", "summary": "Flickering firelight with warm dancing shadows."}, - "Fluorescent Light": {"prompt": "fluorescent lighting, clinical glow, cool overhead wash", "summary": "Flat fluorescent illumination with slight green tint."}, - "Overcast Light": {"prompt": "overcast sky, diffuse natural light, soft ambient shadows", "summary": "Soft overcast diffusion with muted contrast."}, - "Mixed Light": {"prompt": "mixed lighting sources, color contrast, layered temperatures", "summary": "Mixed lighting with contrasting color temperatures."}, - "Sunlight": {"prompt": "direct sunlight, high contrast shadows, golden highlights", "summary": "Direct sun with crisp highlights and defined shadows."}, - "Lantern Glow": {"prompt": "lantern-lit ambience, swaying warm pools, intimate highlights", "summary": "Lantern pools of light creating intimate pockets of warmth."}, - "Bioluminescence": {"prompt": "bioluminescent glow, organic shimmer, underwater luminosity", "summary": "Organic bioluminescence casting ethereal aquatic light."}, -} +PresetManager = _common.PresetManager +PresetMixin = _common.PresetMixin +PromptFragmentFilter = _common.PromptFragmentFilter +_format_notes = _common._format_notes # noqa: SLF001 +_resolve_action_option = _common._resolve_action_option # noqa: SLF001 +_resolve_option = _common._resolve_option # noqa: SLF001 +_resolve_with_custom = _common._resolve_with_custom # noqa: SLF001 +_split_tokens = _common._split_tokens # noqa: SLF001 -LIGHT_QUALITY_OPTIONS = { - "None": {"prompt": "", "summary": "No additional light quality emphasis."}, - "Soft Light": {"prompt": "soft light, diffused shadows, gentle transition", "summary": "Soft, diffused lighting with subtle shadow falloff."}, - "Hard Light": {"prompt": "hard light, sharp shadows, chiseled contrast", "summary": "Hard-edged lighting with defined contrast."}, - "Top Light": {"prompt": "top lighting, overhead light source, dramatic downward shadows", "summary": "Top-down lighting with sculpted shadows."}, - "Side Light": {"prompt": "side lighting, directional key light, lateral highlights", "summary": "Side lighting with strong modeling."}, - "Backlight": {"prompt": "backlighting, halo rim, silhouette highlights", "summary": "Backlighting that separates subject from background."}, - "Bottom Light": {"prompt": "uplighting, underlight, theatrical glow", "summary": "Bottom lighting creating dramatic, eerie ambience."}, - "Rim Light": {"prompt": "rim lighting, edge highlight, subject outline glow", "summary": "Highlighted subject edges for separation."}, - "Silhouette": {"prompt": "silhouette lighting, subject in shadow, strong backlight", "summary": "High-contrast silhouette composition."}, - "Low Contrast": {"prompt": "low contrast lighting, gentle tonal range, subtle shadows", "summary": "Low-contrast lighting for a soft palette."}, - "High Contrast": {"prompt": "high contrast lighting, stark shadows, deep blacks", "summary": "High-contrast lighting with dramatic tonality."}, - "Dappled Light": {"prompt": "dappled light, patterned highlights, dancing shade", "summary": "Patterned illumination filtered through foliage or lattice."}, - "Volumetric Rays": {"prompt": "volumetric god rays, particulate beams, dramatic shafts", "summary": "Visible shafts of light carving through atmosphere."}, -} +NoxPromptUsageGuide = _nodes.NoxPromptUsageGuide +NoxPromptOrganizer = _nodes.NoxPromptOrganizer +NoxPromptEnhancer = _nodes.NoxPromptEnhancer +NoxPromptBuilder = _nodes.NoxPromptBuilder +NoxPromptPaletteMixer = _nodes.NoxPromptPaletteMixer +NoxPromptNarrativeWeaver = _nodes.NoxPromptNarrativeWeaver +NoxPromptCharacterCreator = _nodes.NoxPromptCharacterCreator +NoxPromptHumanDesigner = _nodes.NoxPromptHumanDesigner +NoxPromptWardrobeDesigner = _nodes.NoxPromptWardrobeDesigner +NoxPromptActionDirector = _nodes.NoxPromptActionDirector +NoxPromptNSFWActionDirector = _nodes.NoxPromptNSFWActionDirector +NoxPromptNSFWAIO = _nodes.NoxPromptNSFWAIO +NoxPromptLightingMaster = _nodes.NoxPromptLightingMaster +NoxPromptNSFWDesigner = _nodes.NoxPromptNSFWDesigner +NoxPromptCamz = _nodes.NoxPromptCamz +NoxPromptCombiner = _nodes.NoxPromptCombiner +NoxPromptPipelineCombiner = _nodes.NoxPromptPipelineCombiner +NoxPromptAnalyzer = _nodes.NoxPromptAnalyzer + +NODE_CLASSES = tuple(_nodes.NODE_CLASSES) +get_node_classes = _nodes.get_node_classes -TIME_OF_DAY_OPTIONS = { - "None": {"prompt": "", "summary": "No specific time-of-day emphasis."}, - "Daytime": {"prompt": "daytime setting, bright ambience", "summary": "Bright daytime environment."}, - "Nighttime": {"prompt": "nighttime scene, deep shadows, artificial accents", "summary": "Nighttime backdrop with dramatic contrast."}, - "Dusk": {"prompt": "dusk, golden hour, lingering warmth", "summary": "Golden hour glow with soft warmth."}, - "Sunset": {"prompt": "sunset sky, saturated horizon, long shadows", "summary": "Sunset palette with vivid horizon."}, - "Dawn": {"prompt": "pre-dawn hues, gentle blue hour, quiet ambience", "summary": "Pre-dawn tones with soft gradients."}, - "Sunrise": {"prompt": "sunrise light, fresh warmth, emerging glow", "summary": "Sunrise optimism with warm highlights."}, - "Midnight": {"prompt": "midnight sky, star-pricked darkness, tranquil hush", "summary": "Deep midnight calm with subtle celestial accents."}, - "Blue Hour": {"prompt": "blue hour tones, twilight glow, cobalt horizon", "summary": "Cool twilight wash bridging night and day."}, -} +_CONSTANT_NAMES = [name for name in dir(_constants) if name.isupper()] +for _const_name in _CONSTANT_NAMES: + globals()[_const_name] = getattr(_constants, _const_name) -SHOT_SIZE_OPTIONS = { - "None": {"prompt": "", "summary": "No shot size emphasis."}, - "Close-up": {"prompt": "close-up shot, intimate framing, facial detail", "summary": "Close-up framing for emotional focus."}, - "Medium Close-up": {"prompt": "medium close-up composition, chest-up framing", "summary": "Medium close-up showcasing shoulders and expression."}, - "Medium Shot": {"prompt": "medium shot, balanced subject framing", "summary": "Medium shot capturing subject and context."}, - "Medium Long Shot": {"prompt": "medium long shot, knees-up framing", "summary": "Medium long shot blending subject and space."}, - "Long Shot": {"prompt": "long shot, full body, environmental scale", "summary": "Long shot emphasizing subject within environment."}, - "Full Shot": {"prompt": "full shot, entire figure, spatial context", "summary": "Full body framing with contextual surroundings."}, - "Wide Angle": {"prompt": "wide angle framing, expansive field of view", "summary": "Wide angle perspective with environmental breadth."}, - "Extreme Close-up": {"prompt": "extreme close-up, micro expression, intimate texture", "summary": "Intense framing that magnifies subtle detail."}, - "Bird's Eye Wide": {"prompt": "bird's-eye master shot, sweeping overhead, geographic context", "summary": "High vantage shot revealing spatial relationships."}, -} - - -COMPOSITION_OPTIONS = { - "None": {"prompt": "", "summary": "No composition emphasis."}, - "Centered": {"prompt": "centered composition, balanced subject placement", "summary": "Subject centered for symmetry."}, - "Balanced": {"prompt": "balanced framing, visual equilibrium", "summary": "Balanced composition with even weight."}, - "Right-weighted": {"prompt": "right-weighted composition, dynamic off-center balance", "summary": "Subject anchored to the right for motion."}, - "Left-weighted": {"prompt": "left-weighted composition, directional focus", "summary": "Subject anchored to the left for leading motion."}, - "Symmetrical": {"prompt": "symmetrical framing, mirrored balance", "summary": "Symmetrical layout with mirrored elements."}, - "Short-siding": {"prompt": "short-siding composition, open space behind subject", "summary": "Short-siding for tension and anticipation."}, - "Leading Lines": {"prompt": "leading lines composition, guiding geometry, directional flow", "summary": "Structural lines that guide the eye to the subject."}, - "Rule of Thirds": {"prompt": "rule of thirds framing, dynamic balance, offset subject", "summary": "Offset placement using thirds for energetic balance."}, -} - - -LENS_FOCAL_OPTIONS = { - "None": {"prompt": "", "summary": "No focal length emphasis."}, - "Medium": {"prompt": "medium focal length, natural perspective", "summary": "Medium focal length for balanced view."}, - "Wide Angle": {"prompt": "wide angle lens, expanded perspective, mild distortion", "summary": "Wide angle depth with sweeping environment."}, - "Long": {"prompt": "long lens, compressed background, shallow depth", "summary": "Long lens compression with tight focus."}, - "Telephoto": {"prompt": "telephoto lens, extreme compression, isolated subject", "summary": "Telephoto compression isolating subject."}, - "Fisheye": {"prompt": "fisheye lens, dramatic curvature, immersive distortion", "summary": "Fisheye distortion for stylized wrap."}, - "Ultra Wide": {"prompt": "ultra wide focal length, expansive sweep, dramatic scale", "summary": "Ultra wide view exaggerating space and scope."}, - "Macro": {"prompt": "macro lens, magnified detail, shallow depth of field", "summary": "Macro magnification revealing intricate textures."}, -} - - -LENS_ANGLE_OPTIONS = { - "None": {"prompt": "", "summary": "No camera angle emphasis."}, - "Over-the-shoulder": {"prompt": "over-the-shoulder view, subjective framing", "summary": "Over-the-shoulder perspective for dialogue."}, - "High Angle": {"prompt": "high angle shot, elevated vantage", "summary": "High angle perspective for vulnerability."}, - "Low Angle": {"prompt": "low angle shot, heroic scale", "summary": "Low angle for power and grandeur."}, - "Tilted Angle": {"prompt": "dutch angle, tilted horizon, dynamic tension", "summary": "Tilted angle introducing unease."}, - "Aerial Shot": {"prompt": "aerial perspective, sweeping overhead view", "summary": "Aerial viewpoint capturing scale."}, - "Top-down View": {"prompt": "top-down shot, orthographic view, planar composition", "summary": "Top-down vantage for graphic patterns."}, - "Ground Level": {"prompt": "ground-level angle, horizon skimming, towering subject", "summary": "Camera placed at ground height for towering emphasis."}, - "Point-of-View": {"prompt": "first-person POV angle, subjective framing, immersive gaze", "summary": "Immersive first-person angle echoing the character's view."}, -} - - -LENS_TYPE_OPTIONS = { - "None": {"prompt": "", "summary": "No shot type emphasis."}, - "Single Shot": {"prompt": "single shot focus, solo subject", "summary": "Single shot isolating the hero."}, - "Two Shot": {"prompt": "two shot framing, dual subjects", "summary": "Two shot balancing interactions."}, - "Three Shot": {"prompt": "three shot composition, trio dynamic", "summary": "Three shot capturing group interplay."}, - "Group Shot": {"prompt": "group shot, ensemble framing", "summary": "Group shot highlighting ensemble relationships."}, - "Establishing Shot": {"prompt": "establishing shot, wide context, scene-setting", "summary": "Establishing shot grounding the location."}, - "Insert Shot": {"prompt": "insert shot detail, object emphasis, storytelling prop", "summary": "Tight detail shot spotlighting a crucial element."}, - "Crowd Panorama": {"prompt": "crowd panorama, sweeping ensemble, layered depth", "summary": "Expansive ensemble shot capturing scale and energy."}, -} - - -COLOR_TONE_OPTIONS = { - "None": {"prompt": "", "summary": "No color tone emphasis."}, - "Warm Tone": {"prompt": "warm color palette, golden hues, inviting saturation", "summary": "Warm palette with golden highlights."}, - "Cool Tone": {"prompt": "cool color palette, blue undertones, serene mood", "summary": "Cool palette with calming tones."}, - "High Saturation": {"prompt": "high saturation, vivid colors, bold intensity", "summary": "Highly saturated color treatment."}, - "Low Saturation": {"prompt": "low saturation, muted palette, cinematic restraint", "summary": "Muted palette with cinematic restraint."}, - "Pastel Tone": {"prompt": "pastel color palette, powdery hues, gentle saturation", "summary": "Soft pastel wash for dreamy, delicate moods."}, - "Neo Noir": {"prompt": "neo-noir palette, electric highlights, deep shadows", "summary": "High-contrast neon noir treatment with moody blacks."}, -} - - -MOTION_TYPE_OPTIONS = { - "None": {"prompt": "", "summary": "No specific motion descriptor."}, - "Running": {"prompt": "dynamic running motion, explosive sprint, powerful stride", "summary": "High-energy running sequence."}, - "Skateboarding": {"prompt": "skateboarding trick, ollie, urban street energy", "summary": "Skateboarding stunt with street flair."}, - "Soccer": {"prompt": "soccer play, agile footwork, goal attempt", "summary": "Soccer action with athletic control."}, - "Tennis": {"prompt": "tennis rally, racket swing, athletic lunge", "summary": "Tennis motion with decisive swing."}, - "Ping Pong": {"prompt": "table tennis volley, rapid reflexes", "summary": "Ping pong rally with rapid exchanges."}, - "Skiing": {"prompt": "downhill skiing, carving turns, powder spray", "summary": "Skiing descent with dynamic carving."}, - "Basketball": {"prompt": "basketball drive, crossover dribble, slam dunk", "summary": "Basketball highlight moment."}, - "Football": {"prompt": "football play, sprint downfield, defensive pressure", "summary": "Football play with explosive impact."}, - "Bowl Dance": {"prompt": "bowl dance motion, rhythmic choreography", "summary": "Stylized bowl dance movement."}, - "Cartwheel": {"prompt": "cartwheel, acrobatic rotation, graceful motion", "summary": "Cartwheel showcasing athletic grace."}, - "Hovering": {"prompt": "hovering suspension, weightless balance, gentle drift", "summary": "Weightless hover capturing poised suspension."}, - "Freefall": {"prompt": "freefall descent, gravity rush, wind-torn limbs", "summary": "High-velocity drop frozen mid freefall."}, -} - - -EMOTION_OPTIONS = { - "None": {"prompt": "", "summary": "No explicit emotional cue."}, - "Anger": {"prompt": "fiery anger, clenched expression, intense gaze", "summary": "Emotion of anger and determination."}, - "Fear": {"prompt": "subtle fear, wide eyes, tense posture", "summary": "Emotion of fear and uncertainty."}, - "Joy": {"prompt": "radiant joy, bright smile, delighted energy", "summary": "Joyful optimism."}, - "Sadness": {"prompt": "somber sadness, reflective emotion", "summary": "Quiet sadness and introspection."}, - "Surprise": {"prompt": "surprised reaction, widened expression", "summary": "Emotion of surprise and wonder."}, - "Determination": {"prompt": "steely determination, set jaw, unwavering focus", "summary": "Focused resolve radiating grit and purpose."}, - "Serenity": {"prompt": "calm serenity, relaxed features, centered presence", "summary": "Peaceful composure with gentle poise."}, -} - - -CAMERA_BASIC_OPTIONS = { - "None": {"prompt": "", "summary": "No basic camera move."}, - "Push-in": {"prompt": "camera push-in, forward dolly, tightening focus", "summary": "Push-in move for increasing intensity."}, - "Pull-out": {"prompt": "camera pull-out, backward dolly, reveal context", "summary": "Pull-out move to reveal context."}, - "Pan Right": {"prompt": "pan right, lateral camera sweep", "summary": "Pan right following subject motion."}, - "Pan Left": {"prompt": "pan left, lateral follow", "summary": "Pan left tracking movement."}, - "Tilt Up": {"prompt": "tilt up, vertical reveal", "summary": "Tilt up uncovering grandeur."}, - "Crane Sweep": {"prompt": "crane sweep, rising arc, cinematic reveal", "summary": "Elevated crane arc that lifts to reveal scope."}, - "Whip Pan": {"prompt": "whip pan, rapid pivot, motion blur streaks", "summary": "Snap pan creating dynamic motion streaks."}, -} - - -CAMERA_ADVANCED_OPTIONS = { - "None": {"prompt": "", "summary": "No advanced camera move."}, - "Handheld": {"prompt": "handheld camera, organic sway, documentary feel", "summary": "Handheld movement for visceral texture."}, - "Compound": {"prompt": "compound move, layered dolly and pan", "summary": "Compound move blending multiple axes."}, - "Following": {"prompt": "dynamic follow shot, tracking movement", "summary": "Following shot tracking the subject."}, - "Orbit": {"prompt": "orbiting camera, circular move, wraparound view", "summary": "Orbiting move for immersive coverage."}, - "Steadicam Glide": {"prompt": "steadicam glide, fluid tracking, stabilized motion", "summary": "Stabilized glide preserving fluid, anchored movement."}, - "Cable Cam Chase": {"prompt": "cable cam chase, high-speed lateral track, aerial pursuit", "summary": "High-speed cable run carving dynamic parallax."}, -} - - -VISUAL_STYLE_OPTIONS = { - "None": {"prompt": "", "summary": "No specific visual style."}, - "Felt Style": {"prompt": "felt texture style, handcrafted softness", "summary": "Felt-inspired tactile aesthetic."}, - "3D Cartoon": {"prompt": "3D cartoon aesthetic, stylized characters", "summary": "3D cartoon look with bold forms."}, - "Pixel Art": {"prompt": "pixel art style, retro resolution", "summary": "Pixel art aesthetic with retro charm."}, - "Puppet Animation": {"prompt": "puppet animation style, tangible materials", "summary": "Puppet animation with stop-motion feel."}, - "3D Game": {"prompt": "3D game visuals, real-time rendering", "summary": "3D game engine aesthetic."}, - "Claymation": {"prompt": "claymation style, sculpted forms", "summary": "Claymation stop-motion texture."}, - "Anime": {"prompt": "anime style, crisp linework, expressive shading", "summary": "Anime-inspired illustrative style."}, - "Watercolor": {"prompt": "watercolor wash, fluid pigment, paper texture", "summary": "Watercolor painting feel."}, - "B&W Animation": {"prompt": "black and white animation, noir contrast", "summary": "Monochrome animation aesthetic."}, - "Oil Painting": {"prompt": "oil painting style, rich brushwork", "summary": "Oil painting depth and texture."}, - "Ink Sketch": {"prompt": "ink sketch style, bold linework, crosshatch shading", "summary": "Expressive inked line art with graphic contrast."}, - "Voxel Art": {"prompt": "voxel art style, blocky volumetric pixels, retro 3D", "summary": "Voxel-based 3D aesthetic with chunky forms."}, -} - - -SPECIAL_EFFECT_OPTIONS = { - "None": {"prompt": "", "summary": "No special effect emphasis."}, - "Tilt-shift": {"prompt": "tilt-shift effect, miniature depth of field", "summary": "Tilt-shift miniature illusion."}, - "Time-lapse": {"prompt": "time-lapse effect, accelerated motion", "summary": "Time-lapse acceleration for dynamic pacing."}, - "Light Trails": {"prompt": "long-exposure light trails, kinetic streaks, luminous ribbons", "summary": "Dynamic light streaks painting motion paths."}, - "Chromatic Bloom": {"prompt": "chromatic bloom effect, spectral haze, prismatic fringe", "summary": "Prismatic bloom that radiates color halos."}, -} - - -PALETTE_PROFILES = { - "Moody Nightfall": { - "description": "Storm-lashed nocturnal palette with electrified highlights and handheld energy.", - "overrides": { - "light_source": ["Moonlight", "Practical Light"], - "light_quality": ["Soft Light"], - "time_of_day": ["Nighttime"], - "color_tone": ["Cool Tone"], - "camera_advanced": ["Handheld"], - }, - "intense_overrides": { - "motion_type": ["Running"], - "lens_angle": ["Low Angle"], - }, - "effects": ["Time-lapse"], - "keywords": [ - "storm-lashed", - "suspended rain droplets", - "electric skyline", - "brooding shadows", - ], - "intense_keywords": [ - "staccato lightning bursts", - "ground-level spray", - ], - "effect_keywords": ["temporal smearing"], - }, - "Action Spotlight": { - "description": "High-contrast spotlighting that carves out kinetic hero beats on stage-like sets.", - "overrides": { - "light_source": ["Artificial Light"], - "light_quality": ["Hard Light"], - "time_of_day": ["Daytime"], - "shot_size": ["Medium Close-up"], - "composition": ["Right-weighted"], - "camera_basic": ["Push-in"], - }, - "intense_overrides": { - "motion_type": ["Basketball"], - "camera_advanced": ["Orbit"], - }, - "effects": ["Tilt-shift"], - "keywords": [ - "razor-edged highlights", - "charged air", - "spotlit sweat sheen", - ], - "intense_keywords": ["dynamic crowd blur"], - "effect_keywords": ["miniature stage illusion"], - }, - "Dreamscape Glow": { - "description": "Soft dawn-drenched dreamscape with painterly diffusion and weightless pacing.", - "overrides": { - "light_source": ["Daylight"], - "light_quality": ["Soft Light"], - "time_of_day": ["Dawn"], - "color_tone": ["Warm Tone"], - "visual_style": ["Watercolor"], - "lens_focal": ["Wide Angle"], - }, - "intense_overrides": { - "composition": ["Symmetrical"], - "special_effect": ["Tilt-shift"], - }, - "effects": ["Tilt-shift"], - "keywords": [ - "ethereal mist", - "periwinkle glow", - "feathered highlights", - ], - "intense_keywords": ["floating pollen shimmer"], - "effect_keywords": ["miniature haze"], - }, - "Urban Neon Rush": { - "description": "High-voltage city palette pulsing with reflected neon and kinetic energy.", - "overrides": { - "light_source": ["Artificial Light", "Mixed Light"], - "light_quality": ["High Contrast"], - "time_of_day": ["Nighttime"], - "color_tone": ["High Saturation"], - "camera_basic": ["Whip Pan"], - }, - "intense_overrides": { - "motion_type": ["Running"], - "camera_advanced": ["Cable Cam Chase"], - }, - "effects": ["Light Trails"], - "keywords": [ - "neon signage", - "slick pavement", - "electric reflections", - ], - "intense_keywords": [ - "crowd rush blur", - "pulsebeat traffic", - ], - "effect_keywords": ["holographic streaks"], - }, - "Aurora Reverie": { - "description": "Celestial aurora palette awash with drifting color curtains and tranquil wonder.", - "overrides": { - "light_source": ["Moonlight"], - "light_quality": ["Volumetric Rays"], - "time_of_day": ["Midnight"], - "color_tone": ["Cool Tone"], - "visual_style": ["Watercolor"], - }, - "intense_overrides": { - "composition": ["Leading Lines"], - "lens_angle": ["Ground Level"], - }, - "effects": ["Chromatic Bloom"], - "keywords": [ - "dancing aurora", - "glacial horizon", - "whispered starlight", - ], - "intense_keywords": [ - "skyfire curtains", - "crystalline hush", - ], - "effect_keywords": ["spectral mist"], - }, -} - - -NARRATIVE_ARCHETYPES = { - "Resolute Protector": { - "subject": "Resolute protector clad in weathered armor", - "keywords": ["steadfast", "guardian", "battle-tempered"], - }, - "Wandering Visionary": { - "subject": "Wandering visionary draped in layered fabrics", - "keywords": ["imaginative", "inventive", "wide-eyed"], - }, - "Arcane Duelist": { - "subject": "Arcane duelist wreathed in swirling glyphs", - "keywords": ["charged sigils", "arc-light", "precision"], - }, - "Rebel Outrider": { - "subject": "Rebel outrider astride a dust-scarred hoverbike", - "keywords": ["maverick", "reckless", "ferocious"], - }, - "Archivist Seer": { - "subject": "Archivist seer cocooned in floating tomes and light glyphs", - "keywords": ["omniscient", "patient", "enigmatic"], - }, -} - - -NARRATIVE_TONES = { - "Hopeful Resurgence": { - "descriptor": "radiating renewed hope", - "ambience": "glints of light pierce the gloom", - "hook": "A surge of hope cuts through the collapse.", - "keywords": ["amber embers", "resilient resolve"], - }, - "Brooding Tension": { - "descriptor": "coiled with restrained fury", - "ambience": "echoing thunder gathers along the horizon", - "hook": "Tension thickens as the storm converges.", - "keywords": ["pressure building", "charged silence"], - }, - "Whimsical Discovery": { - "descriptor": "buoyed by playful curiosity", - "ambience": "glowing motes dance in the periphery", - "hook": "Delight sparks as a hidden wonder unfurls.", - "keywords": ["gossamer spark", "playful wonder"], - }, - "Rising Triumph": { - "descriptor": "charged with impending victory", - "ambience": "anthemic echoes swell across the horizon", - "hook": "Momentum crests as destiny crowds the frame.", - "keywords": ["surging anthem", "victory pulse"], - }, - "Haunting Echo": { - "descriptor": "haunted by distant memories", - "ambience": "soft dirges reverberate through vacant halls", - "hook": "Silence lingers long after the last note falls.", - "keywords": ["ghostlight", "murmured lament"], - }, -} - - -NARRATIVE_ENVIRONMENTS = { - "Rain-Soaked Citadel": { - "description": "Rain-soaked citadel ramparts shimmer under guttering torchlight", - "details": "rain threads off crenellations, puddles mirror the battle-scarred stone", - "keywords": ["slick masonry", "torchlit haze"], - }, - "Skybound Orchard": { - "description": "Skybound orchard terraces float amid drifting cloudbanks", - "details": "luminous fruit sway on wind-swept branches", - "keywords": ["weightless terraces", "cloud-sifted light"], - }, - "Desert Mirage Bazaar": { - "description": "Sun-baked bazaar shimmers inside a wavering desert mirage", - "details": "textile canopies ripple above sandstone stalls", - "keywords": ["heat shimmer", "spice-laden air"], - }, - "Submerged Temple": { - "description": "Sunken temple halls glimmer beneath rippling water canopy", - "details": "shoals of fish weave through mossy columns", - "keywords": ["refracted light", "ancient murals"], - }, - "Skyline Rooftops": { - "description": "Skyline rooftops knit a maze above neon avenues", - "details": "fluttering tarps snap between antenna arrays", - "keywords": ["city haze", "wind-sliced banners"], - }, -} - - -NARRATIVE_SETPIECES = { - "Climactic Duel": { - "motion": "locks blades with an adversary as sparks cascade in slow arcs", - "hook": "Every strike echoes like a bell tolling fate.", - "keywords": ["arc of sparks", "ringing steel"], - }, - "Heist Dash": { - "motion": "vaults through market stalls clutching luminous artifacts", - "hook": "The chase snaps into sharp focus as alarms flare.", - "keywords": ["darting silhouettes", "flashing sirens"], - }, - "Revelation Stillness": { - "motion": "stands suspended as constellations form sigils around them", - "hook": "Silence blooms before the cosmos whispers the truth.", - "keywords": ["starlit hush", "sigil glow"], - }, - "Rescue Descent": { - "motion": "rappels through fractured skylights to lift allies from danger", - "hook": "The drop becomes a lifeline threading hope through chaos.", - "keywords": ["tensioned cable", "shattered glass"], - }, - "Diplomatic Standoff": { - "motion": "circles the negotiation table as tempers simmer just below the surface", - "hook": "Every measured word teeters between alliance and collapse.", - "keywords": ["clenched fists", "shifting glances"], - }, -} - - -TEMPO_PROFILES = { - "slow burn": { - "motion_prefix": "With deliberate poise,", - "motion_suffix": "drawing out each telling beat", - "hook_suffix": "The moment lingers, heavy with intent.", - "subject_accent": "poised in measured control", - "keywords": ["elongated cadence", "measured breaths"], - }, - "measured build": { - "motion_prefix": "In a building rhythm,", - "motion_suffix": "stacking momentum with purpose", - "hook_suffix": "Anticipation climbs like a drumline swell.", - "subject_accent": "centered and ready", - "keywords": ["tight rhythm", "rising tempo"], - }, - "surging": { - "motion_prefix": "With a sudden surge,", - "motion_suffix": "as the surrounding world blurs", - "hook_suffix": "The scene ignites in a flash of motion.", - "subject_accent": "caught mid-action", - "keywords": ["accelerated blur", "kinetic rush"], - }, - "frantic": { - "motion_prefix": "In a frantic burst,", - "motion_suffix": "leaving streaks of energy hanging in the air", - "hook_suffix": "Heartbeat percussion rattles every frame.", - "subject_accent": "fractured by adrenaline", - "keywords": ["ragged tempo", "stuttered focus"], - }, - "syncopated pulse": { - "motion_prefix": "On a syncopated pulse,", - "motion_suffix": "as tension snaps between staggered beats", - "hook_suffix": "Rhythmic jolts keep the narrative out of step just enough to thrill.", - "subject_accent": "moving on off-beat pivots", - "keywords": ["stammered rhythm", "counter tempo"], - }, - "glacial stillness": { - "motion_prefix": "Within glacial stillness,", - "motion_suffix": "each breath crystallizes before the next", - "hook_suffix": "Time stretches thin enough to hear the quiet turn.", - "subject_accent": "enveloped in frozen composure", - "keywords": ["suspended hush", "slow tide"], - }, -} - - -NSFW_POSE_PROFILES = { - "None": { - "prompt": "", - "notes": "No specific pose emphasis.", - }, - "Suggestive Pinup": { - "prompt": "suggestive pinup pose, playful hip tilt, confident eye contact", - "notes": "Keep the focus on charisma and silhouette rather than explicit detail.", - }, - "Reclined Elegance": { - "prompt": "reclined pose on plush surface, soft arm drape, languid posture", - "notes": "Use graceful lines and relaxed body language for a calm mood.", - }, - "Backlit Silhouette": { - "prompt": "backlit silhouette pose, curves defined through rim light, raised arms framing the body", - "notes": "Let light shape the figure while facial expression carries the emotion.", - }, - "Arching Stretch": { - "prompt": "arching stretch pose, elongated spine, fingertips brushing hair", - "notes": "Emphasize athletic grace and tension without revealing more than intended.", - }, - "Window Lean": { - "prompt": "window sill lean, soft shoulder drop, contemplative outward gaze", - "notes": "Use architecture and reflections to frame a quiet, alluring moment.", - }, - "S-Curve Stand": { - "prompt": "standing s-curve pose, hip popped, graceful arm framing", - "notes": "Classic glamour stance that accentuates silhouette without explicit focus.", - }, - "Floorline Spotlight": { - "prompt": "floorline pose, one leg extended, torso propped on forearms, spotlight emphasis", - "notes": "Great for stage-style shoots with dramatic uplight.", - }, - "Barstool Tease": { - "prompt": "perched on barstool, crossed legs, arched shoulders, playful lean", - "notes": "Combine confident posture with relaxed, social energy.", - }, - "Over-Shoulder Glance": { - "prompt": "over-shoulder glance, back arched, coy smile, hair tossed", - "notes": "Highlights back definition and adds flirtatious storytelling.", - }, - "XXX Stage Strut": { - "prompt": "bold stage strut, hips leading, spotlight-followed, sultry focus", - "notes": "High-energy adult stage choreography—keep movement confident and empowered.", - }, - "XXX Lounge Arch": { - "prompt": "provocative lounge arch, chest lifted, legs draped over chair edge", - "notes": "Editorial-level eroticism; frame to celebrate form without explicit exposure.", - }, - "Custom": { - "prompt": "", - "notes": "Use the custom pose prompt input to describe a bespoke stance.", - }, - "XXX Custom": { - "prompt": "", - "notes": "Use the custom pose prompt input for high-heat adult staging while staying policy compliant.", - }, -} - - -NSFW_WARDROBE_STYLES = { - "None": { - "prompt": "", - "notes": "No wardrobe styling adjustments.", - }, - "Silk Lingerie": { - "prompt": "luxurious silk lingerie set, delicate lace trim, coordinated stockings", - "notes": "Highlight texture and craftsmanship for an elevated aesthetic.", - }, - "Sheer Robe": { - "prompt": "sheer chiffon robe tied at the waist, glimpses of toned figure", - "notes": "Let fabric layers provide implied coverage with elegant motion.", - }, - "Artistic Drapery": { - "prompt": "artistic fabric drape, classical fine art styling, implied coverage", - "notes": "Reference fine art photography to keep the composition tasteful.", - }, - "Latex Couture": { - "prompt": "sleek latex couture ensemble, high-shine finish, sculpted fit", - "notes": "Balance bold materials with confident posture for empowered vibes.", - }, - "Velvet Bodysuit": { - "prompt": "plush velvet bodysuit, deep jewel tones, subtle sheen", - "notes": "Use rich lighting to bring out the texture without overpowering skin tones.", - }, - "Corseted Ensemble": { - "prompt": "structured corseted set, ribbon lacing, sculpted hourglass silhouette", - "notes": "Keep posture elongated to emphasize tailoring and refined glamour.", - }, - "Crystal Bikini": { - "prompt": "sparkling crystal bikini set, reflective rhinestone accents, gleaming straps", - "notes": "Ideal for showgirl aesthetics—watch for specular blowouts in lighting.", - }, - "Leather Harness": { - "prompt": "sleek leather harness layering, adjustable straps, metallic buckles", - "notes": "Edgy styling that pairs well with dramatic lighting and strong poses.", - }, - "Body Chain Highlight": { - "prompt": "delicate body chains draped across torso, shimmering highlights", - "notes": "Use focused light to let jewelry catch attention over skin.", - }, - "XXX Latex Catsuit": { - "prompt": "second-skin latex catsuit, high-gloss finish, contour-tracing seams", - "notes": "Channel fetish couture while keeping framing artistic and celebratory.", - }, - "XXX Micro Lingerie": { - "prompt": "ultra-minimal lingerie set, strappy cutouts, bold adult-mag styling", - "notes": "Reserve for explicit glamour contexts; ensure confident expression and agency.", - }, - "Custom": { - "prompt": "", - "notes": "Use the custom wardrobe prompt input to define bespoke styling.", - }, - "XXX Custom": { - "prompt": "", - "notes": "Use the custom wardrobe prompt input for adult-oriented wardrobe design.", - }, -} - - -NSFW_TONE_MOODS = { - "None": { - "prompt": "", - "notes": "No additional mood guidance.", - }, - "Glamorous Editorial": { - "prompt": "glamorous editorial energy, polished makeup, confident gaze", - "notes": "Channel high-fashion storytelling with poised expression.", - }, - "Sensual Noir": { - "prompt": "noir inspired mood, smoky atmosphere, mysterious expression", - "notes": "Lean into shadow play and narrative intrigue.", - }, - "Soft Intimacy": { - "prompt": "soft intimate atmosphere, gentle smile, inviting warmth", - "notes": "Keep gestures small and welcoming for a romantic tone.", - }, - "Artistic Nude": { - "prompt": "fine art nude photography approach, focus on form and composition", - "notes": "Treat the body as sculpture, emphasizing respect and artistry.", - }, - "Sultry Twilight": { - "prompt": "dusky twilight mood, amber window light, languid expression", - "notes": "Balance warmth and shadow to create a quiet, smoldering vibe.", - }, - "Playful Tease": { - "prompt": "playful teasing energy, coy glance over shoulder, bright pops of color", - "notes": "Keep body language energetic and lighthearted to avoid overt intensity.", - }, - "Opulent Burlesque": { - "prompt": "burlesque stage mood, velvet curtains, theatrical wink", - "notes": "Dial up opulence and showmanship with articulate gestures.", - }, - "Magenta Heat": { - "prompt": "electric magenta nightclub glow, pulsing beat, charged allure", - "notes": "Pair with neon or gel lighting for high-energy atmospherics.", - }, - "Sensory Overdrive": { - "prompt": "sensory overdrive mood, pounding bass, adrenaline sparkle", - "notes": "Great for festival or rave-inspired adult sets.", - }, - "XXX After Hours": { - "prompt": "after-hours adult lounge vibe, smoky haze, unabashed swagger", - "notes": "Acknowledge adult-only context while keeping tone empowered.", - }, - "XXX Seductive Drama": { - "prompt": "high-drama erotic tension, intense eye contact, charged pauses", - "notes": "Amplify emotional stakes without shifting into explicit action.", - }, - "Custom": { - "prompt": "", - "notes": "Use the custom tone prompt input to define bespoke mood cues.", - }, - "XXX Custom": { - "prompt": "", - "notes": "Use the custom tone prompt input for adult-only mood or storytelling.", - }, -} - - -NSFW_LIGHTING_SETUPS = { - "None": { - "prompt": "", - "notes": "No special lighting setup.", - }, - "Candlelit Warmth": { - "prompt": "candlelit glow, warm highlights, gentle falloff across skin", - "notes": "Use clustered light sources to emphasize depth and comfort.", - }, - "Neon Accent": { - "prompt": "neon accent lighting, complementary color contrast, reflective sheen", - "notes": "Pick two bold hues to accentuate mood without washing out tones.", - }, - "Moody Spotlight": { - "prompt": "single spotlight, sculpted shadows, dramatic contrast", - "notes": "Frame the subject with shadow for a theatrical focal point.", - }, - "Window Backlight": { - "prompt": "soft window backlight, translucent curtains, haloed edges", - "notes": "Let natural spill wrap the figure while keeping front fill minimal.", - }, - "Colored Gel Duo": { - "prompt": "split lighting with complementary gels, gradient washes across skin", - "notes": "Blend opposing hues to carve shape while preserving complexion.", - }, - "Striplight Sculpt": { - "prompt": "studio striplights skimming curves, controlled edge highlights", - "notes": "Ideal for glamour sets that crave clean separation from backdrop.", - }, - "Mirror Bounce": { - "prompt": "mirror bounce lighting, reflective panels amplifying shimmer", - "notes": "Leverage reflective surfaces for layered specular detail.", - }, - "Holographic Wash": { - "prompt": "holographic prism wash, iridescent color shifts, futuristic sheen", - "notes": "Pairs with metallic wardrobe and modern editorial staging.", - }, - "XXX Club Strobe": { - "prompt": "club strobe pulses, alternating gel colors, rapid highlights", - "notes": "Simulate adult club energy—capture motion trails responsibly.", - }, - "XXX Champagne Glow": { - "prompt": "champagne bottle glints, gold bounce fill, indulgent sparkle", - "notes": "Adds decadent adult-party flair with luxurious reflections.", - }, - "Custom": { - "prompt": "", - "notes": "Use the custom lighting prompt input to detail bespoke setups.", - }, - "XXX Custom": { - "prompt": "", - "notes": "Use the custom lighting prompt input for explicit nightlife scenarios.", - }, -} - - -NSFW_CAMERA_FRAMING = { - "None": { - "prompt": "", - "notes": "No specific framing guidance.", - }, - "Intimate Portrait": { - "prompt": "intimate portrait framing, medium close-up, shallow depth of field", - "notes": "Highlight eyes and expression to carry the story.", - }, - "Full-Length Showcase": { - "prompt": "full-length framing, elongated lines, elegant posture", - "notes": "Ensure the pose reads clearly from head to toe.", - }, - "Low Angle Admiration": { - "prompt": "low angle lens, empowering perspective, upward gaze", - "notes": "Use perspective to emphasize confidence and strength.", - }, - "Overhead Curve Highlight": { - "prompt": "overhead shot, graceful curvature emphasized, symmetrical layout", - "notes": "Mind the set dressing so shapes remain readable from above.", - }, - "Mirror Reflection": { - "prompt": "mirror reflection framing, doubled imagery, blurred foreground glass", - "notes": "Use the mirror to hint at form while keeping direct exposure restrained.", - }, - "Profile Silhouette": { - "prompt": "profile silhouette framing, edge lighting tracing contours, negative space", - "notes": "Let rim light outline the figure, preserving mystery and mood.", - }, - "Dutch Glamour": { - "prompt": "dutch angle glamour framing, diagonal energy, kinetic composition", - "notes": "Adds motion and drama while centering the subject's presence.", - }, - "Split Focus Mirror": { - "prompt": "split focus with mirrored panels, layered depth cues", - "notes": "Great for editorial concepts mixing reflection and direct gaze.", - }, - "XXX Runway POV": { - "prompt": "runway pov framing, direct eye-line, confident strut captured mid-step", - "notes": "Channels adult runway shows—keep perspective empowering.", - }, - "XXX Close Intimacy": { - "prompt": "hyper-close framing on lips and neckline, shimmering highlights", - "notes": "Use shallow focus to maintain artistry in explicit glamour shots.", - }, - "Custom": { - "prompt": "", - "notes": "Use the custom camera prompt input to define bespoke framing.", - }, - "XXX Custom": { - "prompt": "", - "notes": "Use the custom camera prompt input for adult-centric framing approaches.", - }, -} - - -NSFW_EXPLICITNESS_LEVELS = { - "None": { - "prompt": "", - "negative": "", - }, - "Soft Suggestive": { - "prompt": "tasteful suggestive tone, emphasis on attitude over exposure", - "negative": "graphic nudity, explicit anatomy, pornographic framing", - }, - "Implied Nude": { - "prompt": "implied nudity, strategic coverage, focus on silhouette and light", - "negative": "fully exposed genitals, spread legs, explicit close-ups", - }, - "Artistic Nude": { - "prompt": "fine art nude composition, respect for subject, sculptural lighting", - "negative": "pornographic framing, fetish extremes, explicit anatomical detail", - }, - "Bold Glamour": { - "prompt": "bold glamour styling, confident pose, editorial polish", - "negative": "hardcore explicit acts, fetish content, degrading scenarios", - }, - "XXX Glamour": { - "prompt": "xxx glamour focus, unapologetic skin-forward posing, adult magazine energy", - "negative": "explicit sexual acts, penetration, fetish violence, non-consensual themes", - }, - "XXX Showcase": { - "prompt": "erotic showcase, full nude confidence, provocative glimmer lighting", - "negative": "pornographic close-ups, bodily fluids, explicit intercourse depiction", - }, - "XXX Performance": { - "prompt": "adult-stage performance vibe, theatrical erotica, high-voltage seduction", - "negative": "graphic sexual acts, fetish degradation, non-consensual scenarios", - }, - "Custom": { - "prompt": "", - "negative": "", - }, - "XXX Custom": { - "prompt": "", - "negative": "", - }, -} - - -NSFW_BASELINE_NEGATIVES = [ - "child", - "teen", - "minor", - "underage", - "youth", - "loli", - "shota", - "young-looking", - "non-consensual", - "abuse", - "violence", - "blood", - "gore", - "injury", - "body horror", - "incest", - "bestiality", - "rape", - "drugged", - "sleeping partner", - "unconscious", +_BASE_EXPORTS = [ + "PresetManager", + "PresetMixin", + "PromptFragmentFilter", + "_format_notes", + "_resolve_action_option", + "_resolve_option", + "_resolve_with_custom", + "_split_tokens", + "NoxPromptUsageGuide", + "NoxPromptOrganizer", + "NoxPromptEnhancer", + "NoxPromptBuilder", + "NoxPromptPaletteMixer", + "NoxPromptNarrativeWeaver", + "NoxPromptCharacterCreator", + "NoxPromptHumanDesigner", + "NoxPromptWardrobeDesigner", + "NoxPromptActionDirector", + "NoxPromptNSFWActionDirector", + "NoxPromptNSFWAIO", + "NoxPromptLightingMaster", + "NoxPromptNSFWDesigner", + "NoxPromptCamz", + "NoxPromptCombiner", + "NoxPromptPipelineCombiner", + "NoxPromptAnalyzer", + "NODE_CLASSES", + "get_node_classes", ] +__all__ = _BASE_EXPORTS + list(_CONSTANT_NAMES) # type: ignore[assignment] -NSFW_DEFAULT_SAFETY_NOTE = ( - "For mature audiences only. Confirm all depicted subjects are consenting adults and comply with platform policies." -) - -class NoxPromptEnhancer: - """ - A custom ComfyUI node for enhancing and manipulating text prompts. - Provides various prompt enhancement features including style application, - keyword emphasis, and prompt combination. - """ - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "base_prompt": ("STRING", { - "multiline": True, - "default": "a beautiful landscape" - }), - "enhancement_mode": (["none", "artistic", "photorealistic", "cinematic", "fantasy", "sci-fi", "portrait"], { - "default": "none" - }), - "emphasis_strength": ("FLOAT", { - "default": 1.0, - "min": 0.0, - "max": 2.0, - "step": 0.1 - }), - "add_quality_tags": ("BOOLEAN", {"default": True}), - }, - "optional": { - "secondary_prompt": ("STRING", { - "multiline": True, - "default": "" - }), - "negative_prompt": ("STRING", { - "multiline": True, - "default": "" - }), - "custom_style": ("STRING", { - "multiline": True, - "default": "" - }), - } - } - - RETURN_TYPES = ("STRING", "STRING") - RETURN_NAMES = ("enhanced_prompt", "negative_prompt") - FUNCTION = "enhance_prompt" - CATEGORY = "NoxPrompter/Text" - - def enhance_prompt(self, base_prompt, enhancement_mode, emphasis_strength, add_quality_tags, - secondary_prompt="", negative_prompt="", custom_style=""): - """ - Enhance the input prompt based on the selected parameters. - """ - - # Start with the base prompt - enhanced_prompt = base_prompt.strip() - - # Apply emphasis strength if greater than 1.0 - if emphasis_strength > 1.0: - # Add parentheses for emphasis (ComfyUI/Stable Diffusion syntax) - emphasis_level = int((emphasis_strength - 1.0) * 10) - emphasis_chars = "(" * min(emphasis_level, 5) # Cap at 5 levels - closing_chars = ")" * len(emphasis_chars) - enhanced_prompt = f"{emphasis_chars}{enhanced_prompt}{closing_chars}" - - # Apply enhancement mode styles - style_additions = self._get_style_additions(enhancement_mode) - if style_additions: - enhanced_prompt = f"{enhanced_prompt}, {style_additions}" - - # Add custom style if provided - if custom_style.strip(): - enhanced_prompt = f"{enhanced_prompt}, {custom_style.strip()}" - - # Combine with secondary prompt if provided - if secondary_prompt.strip(): - enhanced_prompt = f"{enhanced_prompt}, {secondary_prompt.strip()}" - - # Add quality tags if enabled - if add_quality_tags: - quality_tags = self._get_quality_tags(enhancement_mode) - enhanced_prompt = f"{enhanced_prompt}, {quality_tags}" - - # Process negative prompt - processed_negative = self._process_negative_prompt(negative_prompt, enhancement_mode) - - # Clean up the final prompt - enhanced_prompt = self._clean_prompt(enhanced_prompt) - - return (enhanced_prompt, processed_negative) - - def _get_style_additions(self, mode): - """Get style-specific prompt additions based on enhancement mode.""" - styles = { - "artistic": "artistic, painterly, expressive brushstrokes, vibrant colors", - "photorealistic": "photorealistic, highly detailed, sharp focus, professional photography", - "cinematic": "cinematic lighting, dramatic composition, film grain, movie still", - "fantasy": "fantasy art, magical atmosphere, ethereal lighting, mystical", - "sci-fi": "sci-fi, futuristic, high-tech, neon lighting, cyberpunk aesthetic", - "portrait": "portrait photography, professional lighting, shallow depth of field, bokeh" - } - return styles.get(mode, "") - - def _get_quality_tags(self, mode): - """Get quality enhancement tags based on the mode.""" - base_quality = "high quality, detailed, masterpiece" - - mode_specific = { - "artistic": "8k resolution, trending on artstation", - "photorealistic": "8k uhd, dslr, soft lighting, high quality", - "cinematic": "4k, imax, film photography", - "fantasy": "concept art, digital painting, trending on artstation", - "sci-fi": "concept art, digital art, trending on artstation", - "portrait": "professional photography, studio lighting" - } - - specific_tags = mode_specific.get(mode, "best quality") - return f"{base_quality}, {specific_tags}" - - def _process_negative_prompt(self, negative_prompt, mode): - """Process and enhance the negative prompt based on mode.""" - base_negative = negative_prompt.strip() if negative_prompt.strip() else "" - - # Add common negative prompts based on mode - mode_negatives = { - "photorealistic": "cartoon, anime, painting, drawing, illustration", - "artistic": "photograph, photo, realistic", - "cinematic": "amateur, low quality, blurry", - "fantasy": "modern, contemporary, realistic photography", - "sci-fi": "medieval, ancient, primitive", - "portrait": "full body, landscape, multiple people" - } - - mode_specific = mode_negatives.get(mode, "") - - # Combine negatives - if base_negative and mode_specific: - return f"{base_negative}, {mode_specific}" - elif mode_specific: - return mode_specific - else: - return base_negative - - def _clean_prompt(self, prompt): - """Clean up the prompt by removing duplicate commas and extra spaces.""" - # Remove multiple consecutive commas - prompt = re.sub(r',\s*,', ',', prompt) - # Remove spaces before commas - prompt = re.sub(r'\s+,', ',', prompt) - # Remove multiple consecutive spaces - prompt = re.sub(r'\s+', ' ', prompt) - # Add space after commas if missing - prompt = re.sub(r',([^\s])', r', \1', prompt) - return prompt.strip() - - -class NoxPromptBuilder: - """Construct prompts using formulas and curated keyword palettes.""" - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "formula": (list(FORMULA_CONFIGS.keys()), {"default": "Advanced Formula"}), - "subject_focus": ("STRING", {"multiline": True, "default": "Battle-hardened warrior drenched in rain, wearing leather armor and fur"}), - "scene_setting": ("STRING", {"multiline": True, "default": "Muddy battlefield at night, lightning in the distance, flames flickering"}), - "motion_arc": ("STRING", {"multiline": True, "default": "Charges forward roaring, swings blade in a slow-motion crash zoom"}), - }, - "optional": { - "narrative_hook": ("STRING", {"multiline": True, "default": "Camera locks onto her determined eyes as thunder cracks overhead"}), - "model_emphasis": ("STRING", {"multiline": True, "default": "Cinematic aesthetic control, precise semantic adherence"}), - "light_source": (list(LIGHT_SOURCE_OPTIONS.keys()), {"default": "Daylight"}), - "light_quality": (list(LIGHT_QUALITY_OPTIONS.keys()), {"default": "Soft Light"}), - "time_of_day": (list(TIME_OF_DAY_OPTIONS.keys()), {"default": "Dusk"}), - "shot_size": (list(SHOT_SIZE_OPTIONS.keys()), {"default": "Medium Close-up"}), - "composition": (list(COMPOSITION_OPTIONS.keys()), {"default": "Balanced"}), - "lens_focal": (list(LENS_FOCAL_OPTIONS.keys()), {"default": "Medium"}), - "lens_angle": (list(LENS_ANGLE_OPTIONS.keys()), {"default": "None"}), - "lens_type": (list(LENS_TYPE_OPTIONS.keys()), {"default": "Single Shot"}), - "color_tone": (list(COLOR_TONE_OPTIONS.keys()), {"default": "Warm Tone"}), - "motion_type": (list(MOTION_TYPE_OPTIONS.keys()), {"default": "Running"}), - "emotion": (list(EMOTION_OPTIONS.keys()), {"default": "Joy"}), - "camera_basic": (list(CAMERA_BASIC_OPTIONS.keys()), {"default": "Push-in"}), - "camera_advanced": (list(CAMERA_ADVANCED_OPTIONS.keys()), {"default": "Handheld"}), - "visual_style": (list(VISUAL_STYLE_OPTIONS.keys()), {"default": "Anime"}), - "special_effect": (list(SPECIAL_EFFECT_OPTIONS.keys()), {"default": "None"}), - "keyword_style": (["auto", "inline", "compact"], {"default": "auto"}), - "randomize_missing": ("BOOLEAN", {"default": False}), - "random_seed": ("INT", {"default": 0, "min": 0, "max": 1_000_000, "step": 1}), - "palette_overrides": ("STRING", {"multiline": True, "default": ""}), - "extra_descriptors": ("STRING", {"multiline": True, "default": "Model feature emphasis: complex & dynamic motion"}), - "custom_keywords": ("STRING", {"multiline": True, "default": ""}), - "negative_prompt": ("STRING", {"multiline": True, "default": ""}), - "prompt_prefix": ("STRING", {"default": ""}), - "prompt_suffix": ("STRING", {"default": ""}), - } - } - - RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING") - RETURN_NAMES = ("prompt", "negative_prompt", "aesthetic_notes", "dynamic_notes") - FUNCTION = "build_prompt" - CATEGORY = "NoxPrompter/Builders" - - def build_prompt( - self, - formula, - subject_focus, - scene_setting, - motion_arc, - narrative_hook="", - model_emphasis="", - light_source="Daylight", - light_quality="Soft Light", - time_of_day="Dusk", - shot_size="Medium Close-up", - composition="Balanced", - lens_focal="Medium", - lens_angle="None", - lens_type="Single Shot", - color_tone="Warm Tone", - motion_type="Running", - emotion="Joy", - camera_basic="Push-in", - camera_advanced="Handheld", - visual_style="Anime", - special_effect="None", - keyword_style="auto", - randomize_missing=False, - random_seed=0, - palette_overrides="", - extra_descriptors="", - custom_keywords="", - negative_prompt="", - prompt_prefix="", - prompt_suffix="", - ): - formula_config = FORMULA_CONFIGS.get(formula, next(iter(FORMULA_CONFIGS.values()))) - overrides_map, override_custom = self._parse_palette_overrides(palette_overrides) - rng = random.Random(random_seed) if randomize_missing else None - - prompt_fragments = [] - - if prompt_prefix.strip(): - prompt_fragments.append(prompt_prefix.strip()) - - subject_clause = subject_focus.strip() - if model_emphasis.strip(): - emphasis = model_emphasis.strip() - subject_clause = f"{subject_clause}, {emphasis}" if subject_clause else emphasis - if subject_clause: - prompt_fragments.append(subject_clause) - - if scene_setting.strip(): - prompt_fragments.append(scene_setting.strip()) - - if motion_arc.strip(): - prompt_fragments.append(motion_arc.strip()) - - if narrative_hook.strip(): - prompt_fragments.append(narrative_hook.strip()) - - if extra_descriptors.strip(): - prompt_fragments.append(extra_descriptors.strip()) - - aesthetic_prompts, aesthetic_summaries = self._collect_category_details( - [ - ("light_source", light_source, LIGHT_SOURCE_OPTIONS), - ("light_quality", light_quality, LIGHT_QUALITY_OPTIONS), - ("time_of_day", time_of_day, TIME_OF_DAY_OPTIONS), - ("shot_size", shot_size, SHOT_SIZE_OPTIONS), - ("composition", composition, COMPOSITION_OPTIONS), - ("lens_focal", lens_focal, LENS_FOCAL_OPTIONS), - ("lens_angle", lens_angle, LENS_ANGLE_OPTIONS), - ("lens_type", lens_type, LENS_TYPE_OPTIONS), - ("color_tone", color_tone, COLOR_TONE_OPTIONS), - ], - overrides_map, - rng=rng, - randomize_missing=randomize_missing, - ) - - dynamic_prompts, dynamic_summaries = self._collect_category_details( - [ - ("motion_type", motion_type, MOTION_TYPE_OPTIONS), - ("emotion", emotion, EMOTION_OPTIONS), - ("camera_basic", camera_basic, CAMERA_BASIC_OPTIONS), - ("camera_advanced", camera_advanced, CAMERA_ADVANCED_OPTIONS), - ], - overrides_map, - rng=rng, - randomize_missing=randomize_missing, - ) - - style_prompts, style_summaries = self._collect_category_details( - [ - ("visual_style", visual_style, VISUAL_STYLE_OPTIONS), - ("special_effect", special_effect, SPECIAL_EFFECT_OPTIONS), - ], - overrides_map, - rng=rng, - randomize_missing=randomize_missing, - ) - - keyword_mode = (keyword_style or "auto").lower() - structure = formula_config.get("structure", []) - - if keyword_mode == "compact": - combined_keywords = aesthetic_prompts + dynamic_prompts + style_prompts - if combined_keywords: - prompt_fragments.append(f"Keywords: {', '.join(combined_keywords)}") - elif keyword_mode == "inline": - for chunk in aesthetic_prompts + dynamic_prompts + style_prompts: - prompt_fragments.append(chunk) - else: - if "aesthetic" in structure and aesthetic_prompts: - prompt_fragments.append(f"Cinematic palette: {', '.join(aesthetic_prompts)}") - elif aesthetic_prompts: - prompt_fragments.extend(aesthetic_prompts) - - if dynamic_prompts: - prompt_fragments.append(f"Motion & camera: {', '.join(dynamic_prompts)}") - - if "stylization" in structure and style_prompts: - prompt_fragments.append(f"Stylization: {', '.join(style_prompts)}") - elif style_prompts: - prompt_fragments.extend(style_prompts) - - combined_custom = self._split_keywords(custom_keywords) - if override_custom: - combined_custom.extend(override_custom) - if combined_custom: - combined_custom = list(dict.fromkeys(filter(None, (item.strip() for item in combined_custom)))) - if combined_custom: - prompt_fragments.append(", ".join(combined_custom)) - - if prompt_suffix.strip(): - prompt_fragments.append(prompt_suffix.strip()) - - prompt_text = self._assemble_prompt(prompt_fragments) - neg_text = negative_prompt.strip() - - aesthetic_notes = " | ".join(filter(None, aesthetic_summaries + style_summaries)) - dynamic_notes = " | ".join(filter(None, dynamic_summaries)) - - return (prompt_text, neg_text, aesthetic_notes, dynamic_notes) - - def _collect_category_details(self, selections, overrides, rng=None, randomize_missing=False): - prompts = [] - summaries = [] - overrides = overrides or {} - for key, label, options in selections: - tokens = self._expand_selection(label) - override_tokens = overrides.get(key, []) - if override_tokens: - tokens.extend(override_tokens) - if not tokens: - tokens.append(label) - - for token in tokens: - option = self._resolve_option(token, options, rng=rng, randomize_missing=randomize_missing) - prompt_text = option.get("prompt", "").strip() - summary_text = option.get("summary", "").strip() - if prompt_text: - prompts.append(prompt_text) - if summary_text: - summaries.append(summary_text) - - prompts = list(dict.fromkeys(filter(None, prompts))) - summaries = list(dict.fromkeys(filter(None, summaries))) - return prompts, summaries - - def _resolve_option(self, label, options, rng=None, randomize_missing=False): - normalized = (label or "").strip() - if normalized: - if normalized.lower() == "none": - normalized = "" - else: - lower_map = {name.lower(): name for name in options.keys()} - lookup = lower_map.get(normalized.lower()) - if lookup: - return options.get(lookup, options.get("None", {"prompt": "", "summary": ""})) - if randomize_missing and rng: - candidates = [name for name in options if name.lower() != "none" and options[name].get("prompt")] - if candidates: - choice = rng.choice(candidates) - return options[choice] - return options.get("None", {"prompt": "", "summary": ""}) - - def _expand_selection(self, value): - if not value: - return [] - tokens = [token.strip() for token in re.split(r'[|/,]', value) if token.strip()] - return tokens - - def _parse_palette_overrides(self, text): - if not text or not text.strip(): - return {}, [] - - overrides = {} - custom_chunks = [] - mapping = self._palette_alias_map() - - for line in text.splitlines(): - line = line.strip() - if not line: - continue - if ":" in line: - key_part, values_part = line.split(":", 1) - canonical = mapping.get(self._normalize_token(key_part)) - values = self._split_keywords(values_part) - if canonical and values: - overrides.setdefault(canonical, []).extend(values) - elif values: - custom_chunks.extend(values) - else: - custom_chunks.extend(self._split_keywords(line)) - - for key in list(overrides.keys()): - overrides[key] = list(dict.fromkeys(overrides[key])) - - custom_chunks = list(dict.fromkeys(custom_chunks)) - return overrides, custom_chunks - - def _palette_alias_map(self): - return { - "light source": "light_source", - "light sources": "light_source", - "lighting": "light_source", - "light quality": "light_quality", - "light qualities": "light_quality", - "time of day": "time_of_day", - "time": "time_of_day", - "shot size": "shot_size", - "composition": "composition", - "lens focal": "lens_focal", - "focal length": "lens_focal", - "lens angle": "lens_angle", - "camera angle": "lens_angle", - "lens type": "lens_type", - "shot type": "lens_type", - "color tone": "color_tone", - "color": "color_tone", - "motion type": "motion_type", - "motion": "motion_type", - "emotion": "emotion", - "camera basic": "camera_basic", - "camera move": "camera_basic", - "basic move": "camera_basic", - "camera advanced": "camera_advanced", - "advanced move": "camera_advanced", - "visual style": "visual_style", - "style": "visual_style", - "special effect": "special_effect", - "effects": "special_effect", - } - - def _normalize_token(self, token): - return token.strip().lower().replace("_", " ") - - def _split_keywords(self, text): - if not text: - return [] - separators = [",", "\n", ";"] - tokens = [text] - for sep in separators: - temp = [] - for token in tokens: - temp.extend(token.split(sep)) - tokens = temp - return [token.strip() for token in tokens if token.strip()] - - def _assemble_prompt(self, fragments): - cleaned = [] - for fragment in fragments: - segment = fragment.strip() - if not segment: - continue - if segment[-1] not in ".!?": - segment = f"{segment}." - cleaned.append(segment) - return " ".join(cleaned) - - -class NoxPromptPaletteMixer: - """Generate palette overrides and keyword clusters for the builder.""" - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "mood_profile": (list(PALETTE_PROFILES.keys()), {"default": "Moody Nightfall"}), - "intensity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.05}), - }, - "optional": { - "include_effects": ("BOOLEAN", {"default": True}), - "custom_palette": ("STRING", {"multiline": True, "default": ""}), - }, - } - - RETURN_TYPES = ("STRING", "STRING", "STRING") - RETURN_NAMES = ("palette_overrides", "custom_keywords", "notes") - FUNCTION = "mix_palette" - CATEGORY = "NoxPrompter/Companions" - - def mix_palette(self, mood_profile, intensity, include_effects=True, custom_palette=""): - profile = PALETTE_PROFILES.get(mood_profile) - if not profile: - overrides_text = custom_palette.strip() - custom_keywords = ", ".join(self._split_keywords(custom_palette)) - notes = "Custom palette applied. Profile not found." - return (overrides_text, custom_keywords, notes) - - overrides = {key: list(values) for key, values in profile.get("overrides", {}).items()} - - if intensity >= 0.6: - for key, values in profile.get("intense_overrides", {}).items(): - overrides.setdefault(key, []).extend(values) - - if include_effects and profile.get("effects"): - overrides.setdefault("special_effect", []).extend(profile["effects"]) - - override_lines = [] - for key, values in overrides.items(): - deduped = list(dict.fromkeys(value for value in values if value)) - if deduped: - override_lines.append(f"{key}: {', '.join(deduped)}") - - if custom_palette.strip(): - override_lines.append(custom_palette.strip()) - - overrides_text = "\n".join(override_lines).strip() - - keywords = list(profile.get("keywords", [])) - if intensity >= 0.6: - keywords.extend(profile.get("intense_keywords", [])) - if include_effects: - keywords.extend(profile.get("effect_keywords", [])) - if custom_palette.strip(): - keywords.extend(self._split_keywords(custom_palette)) - - keywords = list(dict.fromkeys(kw.strip() for kw in keywords if kw and kw.strip())) - custom_keywords = ", ".join(keywords) - - notes_parts = [profile.get("description", mood_profile)] - notes_parts.append(f"Intensity: {intensity:.2f}") - if include_effects and profile.get("effects"): - notes_parts.append("Special effects included: " + ", ".join(profile["effects"])) - if keywords: - notes_parts.append("Keywords curated for emphasis.") - - return (overrides_text, custom_keywords, " | ".join(notes_parts)) - - def _split_keywords(self, text): - if not text: - return [] - separators = [",", "\n", ";"] - tokens = [text] - for sep in separators: - temp = [] - for token in tokens: - temp.extend(token.split(sep)) - tokens = temp - return [token.strip() for token in tokens if token.strip()] - - -class NoxPromptNarrativeWeaver: - """Craft narrative scaffolding for the prompt builder.""" - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "hero_archetype": (list(NARRATIVE_ARCHETYPES.keys()), {"default": "Resolute Protector"}), - "story_tone": (list(NARRATIVE_TONES.keys()), {"default": "Hopeful Resurgence"}), - "environment": (list(NARRATIVE_ENVIRONMENTS.keys()), {"default": "Rain-Soaked Citadel"}), - }, - "optional": { - "set_piece": (list(NARRATIVE_SETPIECES.keys()), {"default": "Climactic Duel"}), - "tempo": (list(TEMPO_PROFILES.keys()), {"default": "surging"}), - "focus_detail": ("STRING", {"multiline": True, "default": ""}), - }, - } - - RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING") - RETURN_NAMES = ( - "subject_focus", - "scene_setting", - "motion_arc", - "narrative_hook", - "extra_descriptors", - ) - FUNCTION = "weave" - CATEGORY = "NoxPrompter/Companions" - - def weave( - self, - hero_archetype, - story_tone, - environment, - set_piece="Climactic Duel", - tempo="surging", - focus_detail="", - ): - archetype = NARRATIVE_ARCHETYPES.get(hero_archetype, {"subject": hero_archetype, "keywords": []}) - tone = NARRATIVE_TONES.get(story_tone, {"descriptor": story_tone, "ambience": "", "hook": "", "keywords": []}) - env = NARRATIVE_ENVIRONMENTS.get(environment, {"description": environment, "details": "", "keywords": []}) - setpiece = NARRATIVE_SETPIECES.get(set_piece, {"motion": set_piece, "hook": "", "keywords": []}) - tempo_cfg = TEMPO_PROFILES.get(tempo, TEMPO_PROFILES["measured build"]) - - subject_parts = [archetype.get("subject", "")] - if tempo_cfg.get("subject_accent"): - subject_parts.append(tempo_cfg["subject_accent"]) - if tone.get("descriptor"): - subject_parts.append(tone["descriptor"]) - subject_focus = ", ".join(part for part in subject_parts if part).strip(", ") - - scene_parts = [env.get("description", ""), env.get("details", ""), tone.get("ambience", "")] - scene_setting = ", ".join(part for part in scene_parts if part).strip(", ") - - motion_parts = [tempo_cfg.get("motion_prefix"), setpiece.get("motion"), tempo_cfg.get("motion_suffix")] - motion_arc = " ".join(part.strip() for part in motion_parts if part and part.strip()) - - hook_parts = [tone.get("hook"), setpiece.get("hook"), tempo_cfg.get("hook_suffix")] - narrative_hook = " ".join(part.strip() for part in hook_parts if part and part.strip()) - - descriptor_sources = [ - archetype.get("keywords", []), - tone.get("keywords", []), - env.get("keywords", []), - setpiece.get("keywords", []), - tempo_cfg.get("keywords", []), - ] - if focus_detail.strip(): - descriptor_sources.append(self._split_keywords(focus_detail)) - - descriptors = [] - for source in descriptor_sources: - descriptors.extend(source) - descriptors = list(dict.fromkeys(desc.strip() for desc in descriptors if desc and desc.strip())) - extra_descriptors = ", ".join(descriptors) - - return ( - subject_focus, - scene_setting, - motion_arc, - narrative_hook, - extra_descriptors, - ) - - def _split_keywords(self, text): - if not text: - return [] - separators = [",", "\n", ";"] - tokens = [text] - for sep in separators: - temp = [] - for token in tokens: - temp.extend(token.split(sep)) - tokens = temp - return [token.strip() for token in tokens if token.strip()] - - -class NoxPromptNSFWDesigner: - """Assemble mature-oriented prompts with responsible defaults and safeguards.""" - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "subject_focus": ( - "STRING", - { - "multiline": True, - "default": "Confident adult model with poised expression", - }, - ), - "scene_setting": ( - "STRING", - { - "multiline": True, - "default": "Softly lit studio lounge accented with plush textures", - }, - ), - "pose_profile": (list(NSFW_POSE_PROFILES.keys()), {"default": "Suggestive Pinup"}), - "wardrobe_style": ( - list(NSFW_WARDROBE_STYLES.keys()), - {"default": "Silk Lingerie"}, - ), - "tone_profile": (list(NSFW_TONE_MOODS.keys()), {"default": "Glamorous Editorial"}), - }, - "optional": { - "lighting_setup": ( - list(NSFW_LIGHTING_SETUPS.keys()), - {"default": "Candlelit Warmth"}, - ), - "camera_framing": ( - list(NSFW_CAMERA_FRAMING.keys()), - {"default": "Intimate Portrait"}, - ), - "explicitness_level": ( - list(NSFW_EXPLICITNESS_LEVELS.keys()), - {"default": "Implied Nude"}, - ), - "custom_pose_prompt": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "custom_pose_notes": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "custom_wardrobe_prompt": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "custom_wardrobe_notes": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "custom_tone_prompt": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "custom_tone_notes": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "custom_lighting_prompt": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "custom_lighting_notes": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "custom_camera_prompt": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "custom_camera_notes": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "custom_explicitness_prompt": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "custom_explicitness_negative": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "custom_explicitness_notes": ( - "STRING", - {"multiline": True, "default": ""}, - ), - "detail_accent": ( - "STRING", - { - "multiline": True, - "default": "soft blemish-free skin, natural curves, respectful staging", - }, - ), - "nsfw_tags": ("STRING", {"multiline": True, "default": "tasteful, mature, confident"}), - "negative_prompt": ("STRING", {"multiline": True, "default": ""}), - "include_negative_baseline": ("BOOLEAN", {"default": True}), - "include_safety_note": ("BOOLEAN", {"default": True}), - "custom_safety_note": ( - "STRING", - {"multiline": True, "default": ""}, - ), - }, - } - - RETURN_TYPES = ("STRING", "STRING", "STRING") - RETURN_NAMES = ("prompt", "negative_prompt", "safety_notes") - FUNCTION = "design_prompt" - CATEGORY = "NoxPrompter/NSFW" - - def design_prompt( - self, - subject_focus, - scene_setting, - pose_profile, - wardrobe_style, - tone_profile, - lighting_setup="Candlelit Warmth", - camera_framing="Intimate Portrait", - explicitness_level="Implied Nude", - detail_accent="", - nsfw_tags="", - negative_prompt="", - include_negative_baseline=True, - include_safety_note=True, - custom_pose_prompt="", - custom_pose_notes="", - custom_wardrobe_prompt="", - custom_wardrobe_notes="", - custom_tone_prompt="", - custom_tone_notes="", - custom_lighting_prompt="", - custom_lighting_notes="", - custom_camera_prompt="", - custom_camera_notes="", - custom_explicitness_prompt="", - custom_explicitness_negative="", - custom_explicitness_notes="", - custom_safety_note="", - ): - pose_cfg = self._resolve_nsfw_option( - pose_profile, - NSFW_POSE_PROFILES, - custom_prompt=custom_pose_prompt, - custom_notes=custom_pose_notes, - ) - wardrobe_cfg = self._resolve_nsfw_option( - wardrobe_style, - NSFW_WARDROBE_STYLES, - custom_prompt=custom_wardrobe_prompt, - custom_notes=custom_wardrobe_notes, - ) - tone_cfg = self._resolve_nsfw_option( - tone_profile, - NSFW_TONE_MOODS, - custom_prompt=custom_tone_prompt, - custom_notes=custom_tone_notes, - ) - lighting_cfg = self._resolve_nsfw_option( - lighting_setup, - NSFW_LIGHTING_SETUPS, - custom_prompt=custom_lighting_prompt, - custom_notes=custom_lighting_notes, - ) - camera_cfg = self._resolve_nsfw_option( - camera_framing, - NSFW_CAMERA_FRAMING, - custom_prompt=custom_camera_prompt, - custom_notes=custom_camera_notes, - ) - explicit_cfg = self._resolve_nsfw_option( - explicitness_level, - NSFW_EXPLICITNESS_LEVELS, - custom_prompt=custom_explicitness_prompt, - custom_notes=custom_explicitness_notes, - custom_negative=custom_explicitness_negative, - ) - - fragments = [] - - subject_clause_parts = [subject_focus.strip()] - if tone_cfg.get("prompt"): - subject_clause_parts.append(tone_cfg["prompt"]) - if explicit_cfg.get("prompt"): - subject_clause_parts.append(explicit_cfg["prompt"]) - subject_clause = ", ".join(part for part in subject_clause_parts if part) - if subject_clause: - fragments.append(subject_clause) - - if scene_setting.strip(): - fragments.append(scene_setting.strip()) - - descriptive_chunks = [ - pose_cfg.get("prompt", ""), - wardrobe_cfg.get("prompt", ""), - lighting_cfg.get("prompt", ""), - camera_cfg.get("prompt", ""), - ] - if detail_accent.strip(): - descriptive_chunks.append(detail_accent.strip()) - descriptive_line = ", ".join(chunk for chunk in descriptive_chunks if chunk) - if descriptive_line: - fragments.append(descriptive_line) - - tag_list = self._split_keywords(nsfw_tags) - if tag_list: - fragments.append(f"Keywords: {', '.join(tag_list)}") - - prompt_text = self._assemble_sentences(fragments) - - negative_terms = [] - if include_negative_baseline: - negative_terms.extend(NSFW_BASELINE_NEGATIVES) - explicit_negative = self._split_keywords(explicit_cfg.get("negative", "")) - negative_terms.extend(explicit_negative) - negative_terms.extend(self._split_keywords(negative_prompt)) - negative_terms = list(dict.fromkeys(term.strip() for term in negative_terms if term.strip())) - negative_text = ", ".join(negative_terms) - - safety_parts = [] - if include_safety_note: - safety_parts.append(NSFW_DEFAULT_SAFETY_NOTE) - notes_sources = [ - pose_cfg.get("notes"), - wardrobe_cfg.get("notes"), - tone_cfg.get("notes"), - lighting_cfg.get("notes"), - camera_cfg.get("notes"), - explicit_cfg.get("notes"), - ] - curated_notes = [note for note in notes_sources if note] - if curated_notes: - safety_parts.extend(list(dict.fromkeys(curated_notes))) - if custom_safety_note.strip(): - safety_parts.append(custom_safety_note.strip()) - safety_notes = " | ".join(part for part in safety_parts if part) - - return (prompt_text, negative_text, safety_notes) - - def _assemble_sentences(self, fragments): - sentences = [] - for fragment in fragments: - segment = fragment.strip() - if not segment: - continue - if segment[-1] not in ".!?": - segment = f"{segment}." - sentences.append(segment) - return " ".join(sentences) - - def _split_keywords(self, text): - if not text: - return [] - separators = [",", "\n", ";"] - tokens = [text] - for sep in separators: - expanded = [] - for token in tokens: - expanded.extend(token.split(sep)) - tokens = expanded - return [token.strip() for token in tokens if token.strip()] - - def _resolve_nsfw_option( - self, - selection, - options, - *, - custom_prompt="", - custom_notes="", - custom_negative=None, - ): - key = self._match_option_key(selection, options) - fallback_key = "None" if "None" in options else next(iter(options)) - source_key = key or fallback_key - base = dict(options.get(source_key, {})) - - prompt_parts = [part.strip() for part in [base.get("prompt", ""), custom_prompt] if part and part.strip()] - base["prompt"] = ", ".join(dict.fromkeys(prompt_parts)) if prompt_parts else "" - - notes_parts = [part.strip() for part in [base.get("notes", ""), custom_notes] if part and part.strip()] - if notes_parts: - base["notes"] = " | ".join(dict.fromkeys(notes_parts)) - elif "notes" in base: - base["notes"] = base.get("notes", "") - - if custom_negative is not None: - negative_parts = [part.strip() for part in [base.get("negative", ""), custom_negative] if part and part.strip()] - base["negative"] = ", ".join(dict.fromkeys(negative_parts)) if negative_parts else "" - - return base - - def _match_option_key(self, selection, options): - normalized = (selection or "").strip() - if not normalized: - return None - lookup = normalized.lower() - for key in options.keys(): - if key.lower() == lookup: - return key - return None - - -class NoxPromptCombiner: - """ - A node for combining multiple prompts with different blending modes. - """ - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "prompt_1": ("STRING", { - "multiline": True, - "default": "" - }), - "prompt_2": ("STRING", { - "multiline": True, - "default": "" - }), - "combination_mode": (["concatenate", "blend", "alternate", "weighted"], { - "default": "concatenate" - }), - "weight_1": ("FLOAT", { - "default": 1.0, - "min": 0.0, - "max": 2.0, - "step": 0.1 - }), - "weight_2": ("FLOAT", { - "default": 1.0, - "min": 0.0, - "max": 2.0, - "step": 0.1 - }), - }, - "optional": { - "prompt_3": ("STRING", { - "multiline": True, - "default": "" - }), - "separator": ("STRING", { - "default": ", " - }), - } - } - - RETURN_TYPES = ("STRING",) - RETURN_NAMES = ("combined_prompt",) - FUNCTION = "combine_prompts" - CATEGORY = "NoxPrompter/Text" - - def combine_prompts(self, prompt_1, prompt_2, combination_mode, weight_1, weight_2, - prompt_3="", separator=", "): - """ - Combine prompts using different combination modes. - """ - - prompts = [p.strip() for p in [prompt_1, prompt_2, prompt_3] if p.strip()] - - if not prompts: - return ("",) - - if len(prompts) == 1: - return (prompts[0],) - - if combination_mode == "concatenate": - return (separator.join(prompts),) - - elif combination_mode == "blend": - # Simple blending - interleave words - words = [] - all_words = [p.split() for p in prompts] - max_len = max(len(w) for w in all_words) - - for i in range(max_len): - for word_list in all_words: - if i < len(word_list): - words.append(word_list[i]) - - return (" ".join(words),) - - elif combination_mode == "alternate": - # Alternate between prompts sentence by sentence - sentences = [] - for i, prompt in enumerate(prompts): - prompt_sentences = [s.strip() for s in prompt.split('.') if s.strip()] - for j, sentence in enumerate(prompt_sentences): - if (i + j) % len(prompts) == 0: - sentences.append(sentence) - - return (". ".join(sentences) + ".",) - - elif combination_mode == "weighted": - # Apply weights using parentheses - weighted_prompts = [] - weights = [weight_1, weight_2, 1.0] # Default weight for prompt_3 - - for prompt, weight in zip(prompts, weights[:len(prompts)]): - if weight > 1.0: - emphasis_level = int((weight - 1.0) * 10) - emphasis_chars = "(" * min(emphasis_level, 5) - closing_chars = ")" * len(emphasis_chars) - weighted_prompts.append(f"{emphasis_chars}{prompt}{closing_chars}") - elif weight < 1.0: - # Use square brackets for de-emphasis - deemphasis_level = int((1.0 - weight) * 10) - deemphasis_chars = "[" * min(deemphasis_level, 5) - closing_chars = "]" * len(deemphasis_chars) - weighted_prompts.append(f"{deemphasis_chars}{prompt}{closing_chars}") - else: - weighted_prompts.append(prompt) - - return (separator.join(weighted_prompts),) - - return (separator.join(prompts),) - - -class NoxPromptAnalyzer: - """ - A node for analyzing prompt characteristics and providing feedback. - """ - - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "prompt": ("STRING", { - "multiline": True, - "default": "" - }), - "analysis_type": (["word_count", "complexity", "style_detection", "full_analysis"], { - "default": "full_analysis" - }), - } - } - - RETURN_TYPES = ("STRING", "INT", "FLOAT") - RETURN_NAMES = ("analysis_result", "word_count", "complexity_score") - FUNCTION = "analyze_prompt" - CATEGORY = "NoxPrompter/Analysis" - - def analyze_prompt(self, prompt, analysis_type): - """ - Analyze the prompt and return various metrics. - """ - - if not prompt.strip(): - return ("Empty prompt", 0, 0.0) - - words = prompt.split() - word_count = len(words) - - # Calculate complexity score - complexity_score = self._calculate_complexity(prompt) - - if analysis_type == "word_count": - result = f"Word count: {word_count}" - - elif analysis_type == "complexity": - result = f"Complexity score: {complexity_score:.2f}" - - elif analysis_type == "style_detection": - detected_style = self._detect_style(prompt) - result = f"Detected style: {detected_style}" - - else: # full_analysis - detected_style = self._detect_style(prompt) - sentiment = self._analyze_sentiment(prompt) - result = f"""Prompt Analysis: -Word Count: {word_count} -Complexity Score: {complexity_score:.2f} -Detected Style: {detected_style} -Sentiment: {sentiment} -Length: {'Short' if word_count < 10 else 'Medium' if word_count < 25 else 'Long'}""" - - return (result, word_count, complexity_score) - - def _calculate_complexity(self, prompt): - """Calculate a complexity score for the prompt.""" - words = prompt.split() - if not words: - return 0.0 - - # Factors that increase complexity - avg_word_length = sum(len(word) for word in words) / len(words) - unique_words = len(set(words)) - uniqueness_ratio = unique_words / len(words) - - # Look for artistic/technical terms - artistic_terms = ['masterpiece', 'detailed', 'artstation', 'concept', 'digital', 'painting'] - artistic_score = sum(1 for term in artistic_terms if term.lower() in prompt.lower()) - - complexity = (avg_word_length * 0.3 + uniqueness_ratio * 0.4 + artistic_score * 0.3) - return min(complexity, 10.0) # Cap at 10 - - def _detect_style(self, prompt): - """Detect the likely style of the prompt.""" - prompt_lower = prompt.lower() - - style_keywords = { - 'photorealistic': ['photo', 'realistic', 'photography', 'dslr', 'camera'], - 'artistic': ['painting', 'art', 'artistic', 'canvas', 'brushstroke'], - 'anime': ['anime', 'manga', 'kawaii', 'chibi'], - 'cinematic': ['cinematic', 'movie', 'film', 'dramatic'], - 'fantasy': ['fantasy', 'magical', 'mystical', 'dragon', 'wizard'], - 'sci-fi': ['sci-fi', 'futuristic', 'cyberpunk', 'robot', 'space'], - 'abstract': ['abstract', 'surreal', 'experimental'] - } - - style_scores = {} - for style, keywords in style_keywords.items(): - score = sum(1 for keyword in keywords if keyword in prompt_lower) - if score > 0: - style_scores[style] = score - - if style_scores: - # Find the style with the highest score - best_style = None - best_score = 0 - for style, score in style_scores.items(): - if score > best_score: - best_score = score - best_style = style - return best_style - return "general" - - def _analyze_sentiment(self, prompt): - """Basic sentiment analysis of the prompt.""" - positive_words = ['beautiful', 'amazing', 'stunning', 'gorgeous', 'magnificent', 'wonderful'] - negative_words = ['dark', 'scary', 'horror', 'grim', 'sinister', 'evil'] - - prompt_lower = prompt.lower() - positive_count = sum(1 for word in positive_words if word in prompt_lower) - negative_count = sum(1 for word in negative_words if word in prompt_lower) - - if positive_count > negative_count: - return "positive" - elif negative_count > positive_count: - return "negative" - else: - return "neutral" \ No newline at end of file +del _CONSTANT_NAMES diff --git a/__init__.py b/__init__.py index 00b47a3..1420c77 100644 --- a/__init__.py +++ b/__init__.py @@ -10,8 +10,16 @@ from .NoxPromptNode import ( NoxPromptBuilder, NoxPromptPaletteMixer, NoxPromptNarrativeWeaver, + NoxPromptCharacterCreator, + NoxPromptHumanDesigner, + NoxPromptWardrobeDesigner, + NoxPromptActionDirector, + NoxPromptLightingMaster, + NoxPromptNSFWActionDirector, NoxPromptNSFWDesigner, + NoxPromptCamz, NoxPromptCombiner, + NoxPromptPipelineCombiner, NoxPromptAnalyzer, ) @@ -21,8 +29,16 @@ NODE_CLASS_MAPPINGS = { "NoxPromptBuilder": NoxPromptBuilder, "NoxPromptPaletteMixer": NoxPromptPaletteMixer, "NoxPromptNarrativeWeaver": NoxPromptNarrativeWeaver, + "NoxPromptCharacterCreator": NoxPromptCharacterCreator, + "NoxPromptHumanDesigner": NoxPromptHumanDesigner, + "NoxPromptWardrobeDesigner": NoxPromptWardrobeDesigner, + "NoxPromptActionDirector": NoxPromptActionDirector, + "NoxPromptLightingMaster": NoxPromptLightingMaster, + "NoxPromptNSFWActionDirector": NoxPromptNSFWActionDirector, "NoxPromptNSFWDesigner": NoxPromptNSFWDesigner, + "NoxPromptCamz": NoxPromptCamz, "NoxPromptCombiner": NoxPromptCombiner, + "NoxPromptPipelineCombiner": NoxPromptPipelineCombiner, "NoxPromptAnalyzer": NoxPromptAnalyzer, } @@ -32,8 +48,16 @@ NODE_DISPLAY_NAME_MAPPINGS = { "NoxPromptBuilder": "Nox Prompt Builder", "NoxPromptPaletteMixer": "Nox Prompt Palette Mixer", "NoxPromptNarrativeWeaver": "Nox Prompt Narrative Weaver", + "NoxPromptCharacterCreator": "Nox Prompt Character Creator", + "NoxPromptHumanDesigner": "Nox Prompt Human Designer", + "NoxPromptWardrobeDesigner": "Nox Prompt Wardrobe Designer", + "NoxPromptActionDirector": "Nox Prompt Action Director", + "NoxPromptLightingMaster": "Nox Prompt Lighting Master", + "NoxPromptNSFWActionDirector": "Nox Prompt NSFW Action Director", "NoxPromptNSFWDesigner": "Nox Prompt NSFW Designer", + "NoxPromptCamz": "Nox Camz", "NoxPromptCombiner": "Nox Prompt Combiner", + "NoxPromptPipelineCombiner": "Nox Prompt Pipeline Combiner", "NoxPromptAnalyzer": "Nox Prompt Analyzer", } diff --git a/noxprompter/__init__.py b/noxprompter/__init__.py new file mode 100644 index 0000000..b6b926f --- /dev/null +++ b/noxprompter/__init__.py @@ -0,0 +1,31 @@ +"""Modular Nox Prompter node package.""" + +from __future__ import annotations + +from . import common as _common +from . import constants as _constants +from .nodes import NODE_CLASSES as _NODE_CLASSES +from .nodes import get_node_classes as _get_node_classes + + +NODE_CLASSES = tuple(_NODE_CLASSES) +get_node_classes = _get_node_classes + +for _helper_name in ( + "PresetManager", + "PresetMixin", + "PromptFragmentFilter", + "_format_notes", + "_resolve_action_option", + "_resolve_option", + "_resolve_with_custom", + "_split_tokens", +): + globals()[_helper_name] = getattr(_common, _helper_name) + +for _cls in NODE_CLASSES: + globals()[_cls.__name__] = _cls + +for _const_name in dir(_constants): + if _const_name.isupper(): + globals()[_const_name] = getattr(_constants, _const_name) diff --git a/noxprompter/__pycache__/__init__.cpython-312.pyc b/noxprompter/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7505a0807f6ee6a1d86dd8411d204eeec1548de GIT binary patch literal 1047 zcmZuv%}*0S6rXLk-L|xR$QBU^Uj;%#LOdEnG{~n4NJ6}gzTd^{Z0ghWn8OObZpR`hCYyhsY(i(~& zk|C*G&{D@rlC)LhBu3C&BUrWIts^l~yBn$a!LHw}KtyT>0c~Y zC_t=b5_7r#N^liks?dS zDS0;Qn5L^8GmQG7Agm*x4(SN(;)=aU22NClye4bxAfUn)E)DpJng($3BK3V507@2t z7Iq*w80qQ9BcphDa%gsTbQbLTMIx{g#YNBNobY@?W@Z@SL`>NMBw@0qYPzd#Ak9i8 zW6WOkNg&2t52}(Gyg-?6D`t0`H zeqC-OebCtb4b`9P$!ug}^2|i-y&o5MUhKCGz8rt`;I+4>-#pEN@4OjhZfuMkwCBpa z^VZ+=_w?4&D9|8hUf*7Se{bW_fpP7p(emAB`C+skcJ}SGe0jRxIkaiKNpAP=CECtn K$jq@ERQ3n@`z=lY literal 0 HcmV?d00001 diff --git a/noxprompter/__pycache__/__init__.cpython-313.pyc b/noxprompter/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de4aaa378dea1926f928cbd42dcd80697eea41ff GIT binary patch literal 1074 zcmZuvOHUL*5UzdBK6%K1f<#vE0U;6(k{ClY$Ww69F!5xY&bZqw6K19--MtFN1I7!) zgIqKwUOeK>pQ70V*a;`Yft$F0z?$8KsId<-Uwzf})z{Ts&*w9MYwP2|>fIQ?k1#k{ zi6)5DZ$tpzfdvArh!tIoEJQ`L09MS3FUA()BGJGJ#7SW^aB?xVkQSK+HV|h;t^g!I z2qZI(THz9Dk`q}g)dlk=Ms1`WvkB5R9&P4H#~_S1A&|DKh>AS`SwNjM)||zfPSW|; zuFmC4t%5&(jmq9uWf%4^P8^`hD5{vmT*m*u>!kN^D^W&Y#Xf+BPlK5M^=US`U2Ar$ zq(?F@28+C0b!|~t;_HQnf_s5tqTur~D+G3F&91Pqqp-tBL}zW^=gL+N_vKMICFGJN z?s?otlrAByY+p$Xa!U`UXXw=ZNz0nCFz%=@MMF_qa&0M@97*Z?L%}3dMcc04WQdl1xalfRd6-^HMdOq9k*Uny)@3Q`}qG!23SA zZsPOxpmp_xQpd-s(6L}crzX7cb+FswG75)REM?IeIj&9yRHeuk(r^@cj+J%W%bFc4hxwZKD<8I8=&q2O- zb9#SZsKJ-tdfQ$t-hUiJ8s*HhJ1^ef*<9Q=&g>a|-;BOJqrYbKf4?}q+xO*h?c(IN T@g}`9T1#E{l>l={457K-=Z`5P literal 0 HcmV?d00001 diff --git a/noxprompter/__pycache__/common.cpython-312.pyc b/noxprompter/__pycache__/common.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc7c57b3ec8be438d4eedf3c76912ae96207d0c5 GIT binary patch literal 16797 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
I8SutaX&SjDm_=)N8WMc_UcBb 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 FlR9c=w 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 +pYB6X(1eYB{KCn!K(1`K&C%9%eO0 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 literal 0 HcmV?d00001 diff --git a/noxprompter/__pycache__/constants.cpython-313.pyc b/noxprompter/__pycache__/constants.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23d9fdccc95a69492793c3fa2387562ce898c7c9 GIT binary patch literal 224423 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

`n7K=79nM;b7m{=-g?|J-t;`?ii zzP)M0AL3ybPZw{2huc%I6Am8VBmvVt7!-twseXgjcL6nSG8oms4}(D_QT1Dlt_9g#hhy7Va>gHV;zvHHQ#?M1<>Sfa zTd|z`hX6K@AKnGD<&hX%^er z(rVyfK6NsH@jb+isiVxfMcfo$OyS;WDdXrAYA+D!MZ}m=AEY? z*7j8=_K0dvJACG2W|DLwabBWAK8OgUPFnRaaX(BErmW+iO2VvyjHI0}Rx6b( zs1*yzJbH<7CUV(fjP&u7b&wSKD!_unyWQF@lcz0aFy35&Gmt54HhKh&5UIKMaF;{I z*&ZiQpj4%}Rx4~>M*RgQx__fD&&coYp>I;t$&s489ybnIAm(R_cRGOmK)u6)rBgP^ z+|5~BShz(pF-8wrx-4R1W7Cgld+%BMg8nP~Hw0cMIHM)Oe>wV+Umns+UWE{|f}-t( zL#Q82Qt7_>r*bVAmXy3$F#wn5Hof$id0^8dptN*@^IX|)=gPO=&L`ZBsC1T@8?bZZ z6S8qaV-#iC&aO=cqq7iov6I|kKqm}@f?jt^PfX|&N9)`(OnXVI4#IYX!I9*|~*!ZEGmQ@lA3 zdGH)AnwYM`Y2&iTNVT)E=qNrc8=x&$SVLBLYrsf;|6n(HPwkIr3`DQEzXiRnf(+QR zIgIgBqYPw;VtNWrrOXE!uN2f1Ls`rnN|a1?=?*pnasB9PGA$l&Z2|*HSLp5n`nx3A zq{M`Z(*1RL_#v7`=ad)Y@#!Zd{7drge2eHGOif@t17Yp*I%lX_eErTeZ(tde!M|U9 zp_S`5W8C=GC$5&=;H_4&zidBu$Tr``GT=thNAo~^j4E6RZY{W}$R2C#c8X)9Mk%}7 zKAvKWxzl2RizQ0*$&JMlsXJs{jhRpiQcc>sNjB}biyVa|fF(LoqClePMNbFq>>Xbe zqQwo1msCbl!3$rE9?Kg&>SzgfEoraIF#!H-#Mfaq_n%~9QAywg-)qh#>6o`IhgPd0 z$*OT!SZ}7WPAe14v-}MUkme9)+Bqs<0Ey(HC2b3QmY#MaQ47n1BUIj5**MNFdAJT< zwV$x#;$3XY5-o&WIKUABpkjLH(F)$X$Mxv%(Tr@Eb3hzDIsD#nTK zG_H=h;~ry(c@z*9Rn2lIVczrVj`y|Ym2SY|Px8ER zAub|XrXJ8!x_IJ6bRBaq<`piMk+P*S^?2mHo5sm7F}sW9T%S&Lse;0-A5^AJT4tCBgGuh#g!+UPAJPxcOhV4n-(OqXO%c-^S; zI(Mbtu#yY#%_LrQr?J=_%6_aM;~n)UA)J6ioSY;(7L@ggR1}jX$*l9xVAeB~@FyW}5A~2!J`uCp2V*yOxpZV@bi>@$Zk&I~hAED1 zWWsYt&WhSHs7Po?$>h0{*}?V4dmtQ%53=3Q{Va&B`CyjW0?DBUcJ#;zK00oNRsp}0 zPG*&sGs=1aSHO4=Q!xHPLFcI>(SEq<>YI4?^lLx|tPLH|-du}cyt)vx!*$6f`gYnx z_O4dQ*Se5<5=o@+dY3|eP$JSHG=x;A0A0$gO#3bI+CJe|ldIr_1InCZ6u{D*Oh2(?S$JYj6{7TAcr0C zune3eTnHJEo=B0zaUgmZwMx;o8S$Ks(l_-;yL^3ZGZ7ee*&4d)31rLkg~}_;P9?#4 z4wT8h#C1KjhVfJy-8-v2OU@GuNu_6Nqk`iNfYDq>sfZJ6EF?|~L}^caK6g;xBnTq! zgK6D-aCsyWH_W=y6J|tOY3EKvf^eL#7 z4-Aa1OaU`|JBN8-`8yN@fA>L_q z+I0)5d+Qvpu+|l8ziLGBYSwqKfVI}-jYV!!(S%01gVH5)AljuXU`c%QaA4pB4o>_O z)HV7-NLPJ2)OE;pC>mmqb!uc**@_(6-yM>0$fRsD#e{U}GW29Atzf+=9J9q7TeCmk8zM%>U(Dmr0yN-Pd3T`hyao8`^)PTl0?r2 zPbxhSYp4NpKY2Qw`X;~H#%2z&})0wG$g|fE+UTPHTMYIa6MMnnXxk~kq5rXTY=3d-GVO-Wjlv{N2mQh7~Mvs zHUe=GgynBXerTZbqCL(wXNS&lQv#$Iy>Mv^KmANA-#t1axdXsFVm5#W@>1JHl>wy4 zllrZjsRubrn9XQO0#a|1}mV zY2o!lx)$T^=2^KxJYmn9B=;Nc5ak&a1p<=B^R)^G&l*u}+}^vLLn^~3vyUg@fg$XY zIMHW;I;lz3&c6B!CS*Ya5A=+(Sczlr)5rD1YWF5PtD+q|3<{R!$-V&h-7>aJa_AxX zh{#W0bG}K3nOI*k$qOXsI-{vf2A-L!rt&yI-zdG{p`@2%CNV5CD1 zusF?G-$!Y&pEQ$;NVr?TI9;_@fw>f-jAS_+t7_p02&(hNDn5M{4*O@6a=i%a3)$3nS|+*L zglsDZtcEBl3){WKN@f=tm0b-EBtoW2aTA|UFol*NAgF{_RS&Dec|TG97O$Fy=s z+xxdW@EDayt}`P(tfgCsHgE)CcbbC4*#pHeLt>Z9I-C|L}1T$pqg3?sj8^bBZ**cSFfp4uQ@*)vWL@Q(ho$=l%|~ zd+o&#ObPJkm(#nu@`NhU^}bIco$b_ZwkWU9H(rr&2D*#wN@jK-L|6N)Wx_%zL)1T5 zy5IXDohXF7J=aPz8p@(>|F#{J^N$?Niv3`bp+HN=@+#(OGaVht@vq}HlNSL;iLQ2y z`PaURA5LIpXub^s5H*OJo61lHj$*+cK6|{I7?TyKn&2^B1Q9F7R4Xv|BzD=-+8{&G|;3DylE@77=Vdj9z6h$(AaGl$+ zzY*MF8?I(Gb-99&ndMt5uw!43X}iI+(Rsc2v|Em`-3%-zrc3rY|5NQIh=J6OL~9ir z<0j!1%atOc=v?Q&p#SVTw6AW+iy(DGb_okfcqgkt7zRcS2f}+*2JD{QQ2A6(_t2zM z|FnOCc+m4-vdj$Z+Cpjiy=N9w`;H{-vd_}Vr-!6545mGft(X!C|B2orrr@>fw}Qgv zzDpA?(94mqf*d5z$aOLStap2c5GsXpN0F?Ykw6SK6Cb*m7kRKpUYhvTWN6czwsCzu zQel1-@{a!AQR_il`Gz^LwSdf&E8wg^mOR0 zu5@d;DV`R2JT~#VQgc%&I#vny#NPI4)4&mETRF$0%lDDa=lz9uZ-30fZ3~v9&mdLV z$!Zf!F;=TqlsQeJG4zz+f=S%cF6`~MFE3Y|X>^$4!hcvy?@cx6ws?Ys4OK&j*q_qJ zXvgmL9R#4XH|$ggLxIUbNV><&^B-@Udp{1kX`kIl6Y26_Yo*GyINZJ)%vt~m`U&S$ z0U1Jcru_-1Vd*NN3*ykzm?2t>Wr#>&?d*sKsAOfucK7cyt2WTvrO{LS)87ni`K#ng ztKgLsdQ++7##tno-%(G(RN{pDJLZfWxli^V+{JrF-AQ}^pv^@w)r#620z9O++ukRd zpoUFsBdo!nZ*KjaXN64m=o=#;&-$LBKB+3)GrTS#3z;cq-zX~pASV05+1W$U zoQvMr;AP|`y2p$Cq{#|-1M7f&JpVmj0lN)Py#DugdFAj4!p|>ygrTgh4>Z-p{mmLp zX^twImqOstEZ>Z!k8e&rkZS>aebf(c7vvHp>xI!*d_hXO*+^BS$Mo*=K z_cpdpxIeyXpRW?B?HJLYOY+vz3)PYfz#_U=>zULN%K#9GB^S#zSx!z<_pcW8d?Al4 z%LutF?2z`y(qGt_DOb7tvc8*>a~;Zns#CIy`_l!F1_=J#ab4sEc%f8}mH|^pXR2Vx zWLF$*#Jt;|hvLQqHuT3RQ$tkvu6xm7R;yC6MtIkKOYmg2Ez#0npf75j6XkjhSVJTV z`Fpg#3TWWSzQR4Ej!+i&9|@{Xwn4B;8nD&rrUe;rrJkwtrdiVk=DE|31J_`l;px%~ zVQnUX*D?6n-^HJn>*4%Zc92_}7sEMH6=E6OZAVy@91s^{SUN}o6xmz?h-u3*w-0U& zRMwA$t!*E((}U>dK=OiTXx3DSe4=Y3R%G&uPJT_7)MxDFy-CHnF!dtp)78)D&67kXHx3u;mBg(ax<-lHwKqq?jiVBafX! z@!b`WRho&*(6-zkuwaERFD0=f;-?J3=Sr{X;$lq~?$4CvsotOmvsQbSMSXz?|qNl^M2ZR!vDp6WCKui9r87p9+U+V^kr=1nJE z$a>+o!y)a=$*0#AVgU>C)6F6G$DIl6rlQbKJxKy-fOf(LnEgWZli8Z`miU>Qfceu6 zCIHUj4(_B~A`^>IkSI+Sh{bTzW#Wg#k)sf)lIhf;=}RBYF-J$v6cN7bYsrqlG9VZI z^_gO2fOR8ff+7bGWS=kBPltRbUdfO47Zd_XC|$l^3V1OzQZoVS!7b(bFBJsILTVRt)V#i?la8^WPPMN(6tgcKqP~k4-N1 z4Q9Cg=4JBVIX~L_(nPr1(eLZ9-8=C{&7ebOOeRrg+CpslN?+FYZzJy5bw4QT(aAoMO3OF(^h=2&F8wXf>$$AspV^SB>iq&8!rV9VO z-7|mPG`WcX2Qk0UU+DQSt|2xYab9VN_xS)v42B8L3J~CSL#GosDdfW){b_EfJtzFe zqiF>D&wn9D|3f7PXm3%RAxmAOA|K1$;-l|fT*(QpFe=Q6bfoAV?9jh(ZG9u!=?@2F z2qFFl_4x%yr%zE^qJRpDvi~G6#G8;-7p#dre*VfDt(oAlHrspf$@$CL%#je~U{(0@ zG`6=EhB+?Dou5+qMH$xfW1}-kiP~A!GvM!qsYUF0ub|;m^Hs0i=GD6^(B8kQCni?u7mCFy z??h*)9-1miL(k~2+coy~A@K}8_NmY~LM$*hWwU4^WG;rTg{b0GmfI;M0`(|-Nx!g% zPLm|0d4K+^AF^8Q!}X`~zO?ayLU8>`z_aG~52Nse2^m0}W{jHy_? z20|EpOJ@g>f9)m%W-IQ2t}pgkg@5f|AqF7pNLUSMKp3Zv)J9mW4@-pSYw8gldb}*n zR}VS+mkyil3=>l4)c0>>Pk9tZEsZ9bB6@X_M~hKn-h1Ep4#PM5HI>fo2n$^HOaap~ z@B{!!uMbh@zN?mXo*h!o7s?}ERwS~fQaCa?U?Rjv(U%w()YX%4#;+0p?Hos znUS+9*~A4+Kfta#*D2brOs<2B zKB?`s9iLGf6IbWF&1dD2DcP`ttU3=}g=8CPT2M!mXtdJ{<-0?ZEyN2#W7W~_>@4inTBCbcxrUtQ}{wfp>^ z|Ci0(=d>%F9@+i{tIW)zo?sPZCTt7AaeucUyq1Y%p6y>huFvj;;zh#-Op~0~ispZ> zUzr;P81||bf5K!tv!+|ULVEr7GEV=(BkBtbD!f?8*Zz=#zK&H2uG#-Rm)0}j6khy+ z&9Oniw_X4C7Q7ij9{xmo06P`xKBiQ$wzvIlS@0@d^hH4nwl3rW<*>ctk8VKKw14w{ zk~Z`0>azBq{-)?0*;8*bdTcPE9#gvXx5w?@Ok=?5m!`$@MZxs9Z~rwv;S4hHh6cqO zz{{b~JtOtVDu05+Qp0h#a2Xs+|u*i5sg7EF&U1Dx3iS6p(r|KGhRUr>mX}rch zd8ANqW;oe@#(5b^`+!Kg4gT4yczy)qdW*b{kT_)p;3ALn=zuB@Q=Oa|$+sAkjn2yS zpZ8z7Hj0u<*glF>J?2jm6eO40-X7`}w_p%y_GV&)eLp&2F8gEdkjtFl{Km;rYxfCXNfG;YbLW=`22NeH211EDO65W>`z?NsEyR@7g~4ek|N{WnQD^ z(ceMkFa%Ml=^Z^}IzN_rOM2%_7Z>ZzI{-i8hj=$F%*ZLJOvCo&^xXRGNVxEc6A3vT z`SDHN1fr2P;O9r{&I= z(L+-nLnk!xnqCbtxAdLuCTgA8_JLRkw{PD!_DKG7;BR8)woYXdZm-ZQQ? z@!lkM*ICF%@RHneiNv2owEO^E!o@A=PkgRZwiPD_xPGYz)jQ?%Yq&cm9PQjoq=wuN zl<{Q4TjQaf-u;*_#a;W>Hd3ztgKc8##R znIp;ADBzqN=;wyX+E?||SwyC*F#jRqG<0n0-Y^%V*e%bUrD(d6_nh>yzcKnnX1QX~ zp=R#HfTNfqz-OYFN^naxO}FbhYDh0pU7Wv*iB?BmxhE!|5U=i84r}{6m zwWv67HjcE^=cNq4qFPWV-S~RB9$DU~Kj^CH$PWk0i<(0eo%=1^TaaEeaxA(>zmHAS# z*_7EGPQDLI)dz0bH5FsaKznkmE z0}C&}DDTJ5iWyAwgVYxpB9?(HMF1cPDAN}`A~7Y2#PkBxdf^i~sn*w~K~)RISGgz! z7kGX?K9@_GyWJne=I&c1bTU3OMlnZBCbRX}_4erTx5L9U7VB$94n}#8jmSbMev&M|B)t zfRq3Jb~UW5Yw?SwgNZMGeD44Lzx1=yfvP{pNW+o8Y~95v!x*sSAOFM$SSUXTzN&D1 z+WBaD%vQ!hU<-Uyk-7tlxZ(M?`kBVwBWh+cTy;l9;DuYUSJudhc5pj%Odfx7G4;<3 zyab;l>SXJ5-A7vap5=L^am(SkHWXLEdxrMHjijLU97aB>dF|mvx^uc7Ky$XAo-)#yH%WLV?-> z0WNSfI^0TsA(sgMlRc*5sey9HJJcX^iKnW6>5loluFo~q#4!QTaUwF45x8IAt*=zk zSI7WQ1?!d&lIZP+XqjnqDRD)4EEnz@dZT?_*TNH_g#zKKT~0(kU?O@Zw{w@yz>|8A zJ62Wh6>7eL^VvR4on`+9p4u}8MBvJf-hUv$OIt9dI_(pW#Kf$U0b`~XBUq6B2{F-Z zb|ijyccXks>x+R|HNmTT=#;_na|g`yrK`iZ1p+{~1*;?5);^i64iE2F;fsyt>g6nyo9!gkIQVa#pl*CD;y zQBMyhk5wdTf?_*>5v-3Io3lpn&9f`;6^vopVnvxmfiV09Wh}5H2Gg#f1-Yiq6x_q( zn(j9rmQZ&_b}N%?2)TRL`?46HGV%2CSXPW*IKH+{D=!KF(SjbDb>)W3_NTAe3(I5c zd+uj~2Tp=kv>$9+aGcL<8iT)^`y4+nr^XCO8%#Ig1$-^;gR~(h_N$2zr}s9Pv>{R!i4*8KKbzGXCLJ<$D=SaCEUU_M<#CS}my1vV!cx}LB|ro%ktQ!PD}W%T1j7REb$r?ow}7z zTk6~@?)g_Y*D0@5f#%BU=d!rvIdQNkAMV7_5d$+sn;>Wx?fbK-nBp0CU5Aa7Z^jw# zuj?@?#fB3Er9;ZKwOPUEnsW2K2RQ{VxJafmFayKxgx<7D55|~86P^NxtdN(cXC%h5 zw4vl^=P+6^PXX^qg_C^>pFmDoeppw|cS4+Rd0FE$t7 zmHUjXOt8W5M%el#uYwdstkd5>ZNxjRqktc}jNJF)6((i>$)1f*_#0sF-}6u0y`=eM z?+zt@(3Tc{YLZ@UjvOoODzPIt?eFi*(#rmm_WQ^AlRJ->rHqNWNR)^IXCBR2fBxE^ zOfOgWteJFK7LiP@p%LUqR0+-CcDY2 z5*{9ZkC&XH@R_vs*8FRJF~8O1V-t1e(e11wKr7VBlN5SDz91VD@J)8)h>ID`dRR#c zf5>&SAi&9VLfs#E8(uYvD0!M~ISQ+CHY>HWeAV8w84yEBphaqbPhQc#&zJHqS|c{E z>;W#o<@bJASg!YT_HhUqG&;$4(^{ajOl1xJf!t|t-JFlLoqn`1we6?{$Ye(6Y3&ad zA5YKjGlTXaX`9%=LuaPFo#UPr5WaDPrgbx_E`d zm%XU>vOx}C+CBFuil9oj?@3yrds&2`eN;Qkxdp$m`T8J;_F|cK>7=Ep{Ol2 zH@y9^?opTNeLa=LuDzPMCCRp~&D`!1C0pONwxAz2O(yNu()Tn|r_m5;)LV3uNh})Mpz%%2*mj>J!xakIYiGB+8{lnIDlFgPk`cIg9{5hVV z>~1^|3-xP$dJg|i`zixpUdZX6uP$9p(IQGA2_T@teyctZtM!329)LT^>}P z34SS1+5(~O*`tz)0q+5mK3g~wC6{8{iVM#`yLj}}F-2+bDe-E2Im&gSEn=M5-(=gS zb75yg=wpU0XS&n52IJ%bH8ecY0bJ%EyQ-O9v?OhCDKGQdtM)T96u<4kZ4XuHgyx?V ziXaxMrvedzW{YYJvGwvr%se5ZVTC^W)=s_H8CW>N~ML;OS#uq$%iW`n*Q!zPUhmR&arGjGsOIRj5KxLoHgUH>Mv3^lHW zUyoZ_I6`@n492Cdt;i_ZM=?%*&Yl_I#HDm+l7Ys%m`}wM&`3BGXoySSEc~vneffC{_pTj) zuVmB$f2qT_q4!D15aFsG;-$D(Muuczv(Ik_@A0F4hpy_nBFGJ*XzM!Obz6(do01d> z9hv&CZ&9z$U%MrEiaKm>T%Gh45g+vx_Gu`C_U1Rt;SYSjfS_KB!C(58s+9IBo1%m~ zRdTbewqq;wVcUB^d`}^wji76@F4KxQb8q`*omO2|1 zA8d`+jA}tv;CHnT4O7+M{fFkFa|mCQYQZd%6ZQMr@8$~^ev~C=@VW601(55H;b7kt zXV>4;jV-XsS(q$DDI>a`%s-p{SkRIhV!)hd# zKmoEw3cceF2qJ*RfvhH@e)^^^OhsriJgGa>#xin}n5=|3Zqjg$G@|SPzxWfp=K2gB z1wA>QIfYZms={|g{-DSjKoi3E^vZx>hYpW zQn-cV%X_r*i%$N~Ze7!Nl3-X9ESlP=ebp8FHmn4DcHy3N{aop#1O)9AY0;Z@6HcGsX(FVb99D|in1^WqQ5wlmxunFQoW`}-j*aNV3A!y_R7UX3|2VqF)ZMTfa#k1+La*Sz{5X+}uwb?A%T zrL`?6G3Fe_@rlNQ)bH^H_B5qw{2NjS6-3RVm~1gIlC-;B4t=U?7`?a1S?Scs#NkWU zi0taZRpVB=?nCZay-+_N^YSH$EVtiGOEAe|Bu{J+nz(qLcHZSj4qT*h3!yi<$giw2 z!a@(Jgz`tL)UGF^{89TT0IhZ``T36kN=&Qxfn8(PbKl0SeCg0wt@fwVsJ)_Kz8p?& zSj2FoH7V>6c%`B*)*gUVt?n@l#g;3RDz6yq*f0bF z_92Qc-58qwg{!gn{RrtHT;s)|WY4tk)_{&X*sElH5{JY7xt+ReR1gT`W$!tR;Ud}y za4@uxKv_NX%&A{KKN>mvkpgG(a-ScoPgXJkxt||#>3`h2|KrB`v*)ixe1Ftec>X%X zGq(K4kqdI_pPnD7H~sMmi1ztw)|UR5m;zO4M@78jwcCH0)qYleY~XgHzd6ZCj|7#Q z?F31lbb)ls#oaq-|Bn8xe${!?vO>fzutev0{T}K zHq&Mgp1&bX0lEG-fA#CyG6&CJp@%aCV*aLHm)AxF`Tr>6 zH%InGn!mE64)@!?&B!aSTWH$-cyrBi(hUyIBqZHw3jSE%37#(LoB?wcadTZnbD=wU z2U7nq_WeyK;AlBnKZd~Sf<~}reVai)_6NtsHW;%XYk3&|u}y$OoGhE=wAaDLTS+6Q zWCm5x+|aKVvhrlhRO@p%G7Pwks5J2p5hmcHt=A<4&#=Na#)8!es^ zw^I*hNWrupUs~=AdH;W{Wjvb8(gq)dcAPvlSh2^jOi4-l?X-J_aTGtkUJEBdS*!Pk zue6yCETGW)_rXWMc@f_*Q6#V4TIGJE)}dWE1(c2q)0im9<7VaJ4vScL5dYF}b@{p6 zJqhBy{DP$G2yl&P-x1Y%?LVy19k(5{JEb1iUdP{wXJFk!L%Mzgoy*d?O32-Qc2+xq zs_4z?>)GwZy?u^~AXP|Ol#ulo=u>9Z6&~|6@!8+-U3ky~)rFpa!1mY&L z@i#HAq&;W$GkXzOb(i4eANS}y#Qi6IZ2g_EY`c4ppS3rqYVj2V^PHdV~!PVM}k;&$~F&9{hB7u7>8#|->PC=iFRToGe^mDSG?4* z%<4ySsJ;HTeGXKPZ_9Dm2*>f2lJ479tZq@vw_4(}bWC)vVt0Uw&{uQ0!t$6e~_`G^1KfBWAtWXQK5t*ju6>))HZ`Gjq_7;T2{LM({u-S3Rb=tNbjf zOZ*P>hrpSWgx-KZVLwt@2E#%sD~*q3TFT$U)g;0X+||!!<@J6feD7}Y5%cPKh~WKa zRrUDMn$dtnw`p}6j@Dc2NstS}a|h}h8M7=XrID{(rtb6!wxX?|xbv6q#jzTCRRHlV z*)!kXy#mjDm>8{teIZsCUjO4zvfD#$Db6rs1zobEf~**2Ru)KPK^Uwf3X+%jes61o z@#EUr9mBs=)ui|GMk-gbHaHkaW|p_{z2(-Xd{Y$lu7csZCf%5zu6R;S;X&lS7I-c#s7VWC!~u<#F&J&YwdEmIPaW#<2_l z+H69*Yok^`Wc#UZVzKswW)#i#GHEjE=>5eqBGAXKpp_9#(%Cn`N|*4$>!Uu#hBt)G zV5M0?`Jo)UUdl>~u-PLTCcN5}pci_TQrYJL z2%bJBT9I`Yyn)p0-8(5!074uP6okVu1iouoZnr_OscTfO_z;Frw_IRi=mOCtf1j7; z5%)5?#jT+_etq*9^w`!0DIxWTUacO7TiApw-h}3pt_b{)p`MrQOZ)g5>5!}v8%Py{ zSIK2MD{i{nZ9azK?v}f;cEPWUwLIX!73s_63A{!K;pQ1;C@G^$Rf#-AvVC;R5KW?K z*?kKtIKW(`#_AgxBs$*5xdBT-%SJ<3drKs13oAeCBIcs0xL8B<6ejorlwN5|h6bF%Qo2;eo zOulf!R9b97AL|1~6@1Rb)t^)rqJ3EHN@Ya_P`q3UF)D#=;2_DuCDkF@;oMcG03eXU zNxRS}q~WuTugFyr=;S*^+^4V{GbW`v8ji1Bl&US1N?9S_%xmxCRLsh_s~Sa{no$!E z0Uu-k)a?7HT~$L;-wIt$>R4<*AcQBe2=sbczx#~EhsI)J>C?dWkI{K)(K$UBo#RW4 z@?@G&vl^`1$5+sR%d~u;>5!%1MA0Gxs2ha6Py~W-Pu%V+h2m!Q|5(azNo@tAVp)s=tVfayFg213ah>Azf7#4^!PbswV@FzAz{>ai<;J z7IAi;Y#MBjGORdO7q4wTV+sOQ5eAj58y(HpLA8RJYt>Xi^duNUyzQ@ZumiRNDN5Ue zeTt5_ur|ieIP%Da{4pg0IlP8DxAR{M%ec{fTx2t2Ypw%|@N@B5a!`$@3Zi$mHyG1- zWEb?ZwDA*pe%LLrm9?|I3rwgQXaC+Fm96)}6Sd_Zh{kH!LQ71m2EFgV?xWSbBhZU` z@q}|I(;;Z=ZZtZ}<`JyMR{>^b(lIJLN*`mk-lw1wycJJWihB2?KNTQ)rn& z?Lv7PXgmY6=-rnr#Kxge!{6On=3Hp&Wy=60LC;!hqWkUBJN(dNIbksO-d6KQPx9dbu2ZGucu;wsfrzR`B_LOr8j=@SX)HiH_YMEr=Pba*I$*LnVsn}r*flPGyL_<`fJ_lVgZA=Iy=&`Kr!p#}kl>ngYQ0g@puqCrC z^tJRFwFBeB+MpXqgM<~C)rARX!4$nGVRf!99C6S-S>dqlEiPkSkjPK>LY+y zXMOqQ0I2qa85{P`_~5|Ep9#mIMBzatUniwVu@9(VHxbbtY`vNOeMV^zNyCH<$BI4T zwaNwzl}I)MItN^fz5rMwoX+Y`q~alyvPw2{VvflVO0DBl?gh$^Kq8_zBOrRaj`O%g zBBe^Bmp-}t0z4O6;tXeY^LsFf{;O=(Y{l(J{W;8Nj<~I$Si$szl;_HZ$9E?WP>sKL zcO+1gJZNs!EzMR7BS%dS&TM~P(GF9|CdVh4z~`ygiM4ivb47#WF)RY&rK@c8 zcAQv6LK|z*%k9`5)#onxq{>V?`faJWE2=ocxoC`QDwcMcIOB=^aAPF~yBs@QIb6%ZxtPg+bva~$4d=Cjt z$aB1>KZx$Y0_O`>x7--S(>z|JkD~@R+qr8DI&z9Ryw4s(%BT*(N-%Fd0W}^Xb5Bvq z6Y{X`;Hf^s{1Xd<5wAaePGXzLKS7-8yKu0*DT}bYcYhfXPNNv~yS=h5{%sfQou0e8 z&{$Sb3a`Xn_KGW^C66e81Zw`1Y#Dml^hy0_bDvgIn7*!)EgCgXd@{|t7kbj`+s_bJ zU(dC2J2=mOdQ+dOT z%fW34xR-u9+=!EIjp+9&u+eCK3IUDWs*6Hyt8ft6eJn{@!4cJ6N4YYU#H2;~owp=E zX>AkDorDwq`wT@!k08Jl=2>NoSH{Z%pjf3|c7d6+|3~NtI~oCZ82P)nn&^FuVE;xb zHWh4#hw^s(%OfhBKIYU5#*0MLS;xjVI1rR{gy`soa;S4fi*@sjk*DFMv=59h*n5oO zBE+=RkN1KU2MLohNJ}|cMa~5IoO61?2XtSgC$szE5k{7W3S-AY?xF$F$?mY-%?C(h zP`OQe@`~*}{yCm0!pJLkB4M2!LL8_Z0dGLjYW6*|fNFIO*^mc-R~{#LOnx3S>~kjm z#zp*QcXtnc#uR=rFCu!ezO0QAABhl~&%aAqr5{?!ug(^av8?g}f#<9Pw5 zwy&RCGx)Mop44y!T(UP8Vb(BlJ;8&gQE#hs01JoI$Sl7xY`;? z1cE*P6)YP2i9Y%Ta-h=qI6I7!P-|_t1Jb3`lREhfhJu=-2moacHSq-(^z5>s4)MY5 z9im-_Hy@SaN*y+rqe%=sd;$Opr#8M>plx#QHS$2Lk@?WTC#5FXup0K3oOcG6874J8 zu>w|Ht;PzM-#i(Y;rCH5`W*<5qwH_(Q9N=^xW|Xbd_YaJOS%%05B6Mfm=dOiAklV= zw=CYnLB{Ur>GW^2n*hMjU7_CcK)Z9Ehv_R1y` z>yO)4-8<8f*;rq${+e z9_gDv6YS+KK`U&m+a|b!JV#rR={>OGC_tM$d0V8BPFDPxV?*H@v1E{C=ol`nS~;W~ zE5kH>4T@u*ouP_{PslW%F!NGAY)0lhrxuX_M#f9}z^`xFE%VDe48rXa+mv@IAb~Tx z`4~rs=qjE?__YRAN9R8FsD33ACpO(2O zrQqDdS?fG+lCdFfm=I|xwki#cWuh7xI39-s&ALsa3R|8`UuoL{Q8OS?SvdbIgf~*g zdsURMx`ut4y&`&Z^4$%n^xO)M2QyMfk1<0oHiN-=NUA1-r?PTW=K_zD(R!+salw3} z%l!ay2v>k(6*d|2);+vdawmMF*~YfBD`vIu@1^to*F#oa_p9e(ra*(B2Ogk01C>w2m)djXkAUld2C4eAzyyS2A03!y$__Qd>t(SLQ@7x zRR%&T4W8hV$3tib0AL*dPKURP(i4ePA;0J!zQdBxxXi+NGeuOSY{^4BbPyk|Ef)0S zm#p<xAfQD*unFLf0rKRgOLbTuTWt-aS(2yug=3Q zcVDI~#}*aS=7L3l60lfWEbqu6?{$=>4c%E9(^Z*&JTmycGF-T3{TuaE=eI?t`{^vg zfccI&6h>e&=)fOrEia}P8AKnnSe-GNIqulkIVwk=zH~Pbe@mD()))6NClm8ybhQ7A zn+a+)1OR%~r{h@g!W0CZzk%d!cV+spcfi5{LKx9~f?wW0uKWRfzh^E!e|5FqkVpOz z$l7Vz{@RMx#7lL)M5!W1!*d7J?wahA)%q8AF;vJ?7ztDzufu1nE$3=qx2G-Af+kSi zGfHolCe}=aSI}K>Kc`uuv3d4HG(RBj)INt}c8aDhwx(7A9s)!r7C{=ChW^aMjM~7T z^iE|f&>4o7&>N91_D+3e&%Agx5XP$MuC&copSo>En|<`RU0e4_zzQQ&DQ;`FNMvmo z4W@OtaaaW=|Lb+?E#oh;IiYk6mlv4AJ>IV$}fwEx`{D{LSpQN z5y0xjQqme%`Q~-1^Pp=Aqb5Q~EI?I)F{IC%agkUHC=6Dd=xZkxtJd7tC+PCiPO5W+$lDmQq76PQ<$gb8GQ zMKLOd(9XR%tcKNri40M#q|rrR^{~0C=nBQ~I{d3Hk)fK5@e=9fgWX#cDK;duB`W=txMuH3R!p45W!IW3#Ctb+sA zg^INM3!(%)!s~WC*#2~T>)_Eceui_3yWZD!p-lk6PF{y_<|B#s5pZPoR6iCcP5l`T z2yH!~bshFWRE?QK26!A#rA-R@(r1ublcQ?M^I(ib(=^`9E58thA=UQs7I{XY^Ak&a z3=IZoFF53$oEm;v>HBS$vEm`n{d~njMJ0p$-a_0pUj6H|azHv?SYA<^Zke zch^0clH-f>YacSG;&uFDk66$(OVqZIbz50YKwf*7D1*Ay`6{nk6gN<+Ec{d(QPXg z0-tso%~RiiSNsOOk73vXfC@30lch}#C`bV$al$q33|hfWt~1P8{aINvy-!gIC~|`* zmg)!hm0q{|_(Lk)FW-A?zu=0L+J~ebIoFo`7anObsz*@r`s&*l4zh6*aPM9Pm)&L51 znLHy#w9Qck4>7CTlMgZLEIi=~HAzy;L^_Wc8BPyrje8QHU0ZN{n;V)mx8SF;yr|os zkmmN;DnFm?O>Mzt^ilXe@@kdR)UG4+{wdTqq_Vqm9n$X%Yi$5vt)Z& zx%5TDKw=tnD8;Ge!*^$x$;8wuj82Ct7OuMdF=t;+ zAJkR?DN(|GIs%t!mbmOzxBZFOT;Vadh3315j7>ekZyvyE=$KOcsNNV$mh$*WdN1JL zPLIracC(HEpkKs1C(p|YP!OOqMaAkZ+#%^eiCgV|_GY(~eJ=(mX})5S6MOp29U}rr z+YY@FFw3{@b#Tz{ebc+}$NqER{JD)v7iR<*$u=-PO6u}v|6LxSVNwtgR zpp*%b^pHb&FkDewx(UyiM!@#t(RgH!p1~Q(g}#G6sy`KhM}*#KJ7zG@+AB77XJ~NZr~-m;*cNKsV7{LJMNvloTAUZCs6Y zb{!ffPNE{l3P~37oVP<-DMB9k#eS3M@MPv<#{h$SPdKeuGK{zpU(!<(R8*% zPb=Uy?)bNHa=1~U4ABGnk<-QTv|O13gQ1Vwa<*DoTwf^J@j*^(x8&t9k^!(hk91CE zog|;6H`2C!m5r@L-8tIW*!jQ4UfnP_&rOTi;P&Yj119LZs-vE(C+&uEG z?yopEEG@mt25v&}N_E-ti!V2m?dWKL4SYsc)7R`eaX>i07~VYLeQYFBMtZQ@41$)X zGCEf*t}1bYu^8-CFh)e@XDd`*0u6PWhtGB-?zxkJ7#?pi_p$40qP4Dx+VLrNUU8%2 z!Zo(~YHf95`R-0a_D&Oa;Mht}YR^6*9lL}cyQ$E(H^SFeQ>GG5PF{{^t%YU#4T;Vp z!FhUzBX_cX#W|k1?j+w}k_quU$vu6*6nS3Y`7gm(5DR}`+p#vBn8%@~0vrCzZbD}* z91XB)90}KtnPL(u=FHprj3$Gv7Ff ztWFrEGk5ma$*Ak0&=?4D!?=t{d0J#E|B#8q*ErG#P1xSz*by~iv6FL=`*gzJCE}N| zG4-QGZFgp7nu!^-0 zg7&4=r&M@BhKUAMy=7iDhsxNbQc&nElrebdvBHI(vbdWwQV;nZT?Y zdyPOi6g)HYHZkn{rQ2s*a=27?Hz5|gNS?bLl)@s*{Sj;c(~Xm5Qd8j9sfg|No)n}y zMCRv6Cax?vn}{Xiso~-=V3r(2#bnSv_!br|2}4~1OSiaMfwUSfV_Q+HI_6PtQJs{v z=0i;M=^8{rOzk|eSCtVl0e5~9Ma}(~&j_bVo(1zMWNE79Uj23iCEGi5d~zru#MdJO z7i6e?EFP7k#BOEcsQcCl9A^RsT+(?Kf#_Ge?qPXeaHrX)9*3)+3%Ko!uGH20;f^DXp zwqsgwB2d*IC?bWC$Ts8u&NQ{1iOX5HFq-J`XqidvLkR%*Q%sR#sZo(Z;4V)ooSMBs z)qyxU#ryd`|FHjzN-}TXj^dVEYs6w`bpYvp62FrpTnY{ZXhLeIirlhgGR~rdk})%| z`j{h65qe5d6cS~1AQf?VefS}=W6LX5eAM279b4{|NCpN|%GBAf^r+u@A_->##8$L| z{{jDr-%!^HiuaC1s56p9?HC(x`^0=r1q*CQpiU?IQ6CMhPKr|%bTU)ur%|KXed1G> zA!8VE)&i`lyy^xSa$qwFKkJ4#!UG+Zb^wmGQ=r1+X`;VyYeZ{~L#MNEAUt@rjs+_ssYUIKrj36R8@jMM zb4FA2k#b4h$7c*!5;L&`_6GrfAp-4Htkp2h-OS02c9Mv6eI^pce$hQ~WU6tJu0^0# zVDbKt4K$qv3Y>3-f&fBz18z6f7(IV2fRNn|tNr_UF8P}ghY%ZH3wu>X)y}OMvD9Ri zTMZO2_^-q-1pm#3?dfyvH4rF`NY!8ssLtQj;RRRjko2F)gN&~y&t-E4ETzm6P9I8L z`wS-xpGPD@;+P;+EQvcAiwg1|$yF=&B>vjl1!pYC;-ldiG{~U|$a6wGK8CqniNxU0 z)?r_9#4T#t7(eF$$%|pn#RGU9i1^IWFZGbi{D8AC9xG6&T7;R0&e25E}O+944QY2NlA2- zH8Vq62_VLR%pAvfaDeUrng{DLVWuE6$W%VLE@ zOp-PHWU7aVvOvM6h-(t>_L_;(oKxX$eH-;fnsqsCYQ{X*u*4_5R^e=PnbT36~#nzqcYg-LZ{}nnfb}Y zh%HzRZPVDqjOipdA^B7_vW*svrcM!w^9^r8PZ;jbj9vOV9W?L^>f0{Ns07gzUow}( zK}DFccxvS{?GK$+B7ljklt|BXl3H|!wnP3S{zf-{rAYa=7t-@F9}?x=1wi;|(eY31 z=+*xEKk)kOrgFZ$q0PXe7cVFKWlBBt6XWbNr03P0pY-y>NhctQNOFpLLK!WNoeA? z5A*_}FxJUSCQo>Xq90YvULs zs&ttR;-Up=WrF+fTz}fD1n#qtM@KEJ>7p!aRmP6|!K4{YGpR+|Y=2%&W@*KsXm>eh z7nxtxJKWO|pzKOtUPhk;N77e}8&DX$OXH$76HnS7$lS@tKru6S_dNYNuwQP|60XnP zIb@8I?~y6csvE3zU^wEGvIV>o0akuX>Bov^d&8`#IWL=xz=0%d8OwO<}kr5 zeA3ZW-?$B_PS?1Kk()+2m~EzT+{EqGSCX+L@62RP_sFpyP^74{wS+U;TY4;fWIDl+ zjIV;Fek+<_^=@Ja&@YB9>PP4?bTUs{*d+eGSUt_mYOKf76%T+*Y48q6&mEU?13q*kl4y8~5CB>DD8V`tdGrtLj-wdEV+G7rkrL+@($m=;mWK zG4}Y|%C^9+1=Z+(<)|iCq94Z-euIDQJUvqVEvHezC;SySg125D$TEQAfGi-x1KP8E z8eTSdcg|riUud!e?i|8%K20(#`U1URc4@+qsNLb}4B>ijI69MTu{jf#6i$Nqi+*pP zU0yfAxi+Xq1yl%#Ly%Dj!lvYSP(K2N#d}UsR~G8HNe=a(I`@c+?jvmvh@GNQ$>*Y7 z$L41H$w;diN~{C}2TGZQeC@BR1kgf0xbe zVnSk21edMDM|_O$P7Q~VWX*k~#xtx&$p>aC0hhUkX1h;6H2xVwi z4rps<6K2Fbp)sS6m}2vD=PoEr7D<#G9b$ugR^{fFO?NTY*jGX!)hVhwL&JSyAS39& zE-KG@dAM5$#rQ{q3dR7ic8-Nm*hD=XgM`^!x;7;Cq}6A1ruNGr#xBTUX57))VQ0Qe z>C6(L5uM~N;N;(z{^5#%+RjU8r%CnOU%FL=vhQ$S8lO@&E3mQy2P8dnwnYrc5RrXC z&tH)r!iAVrG@xjnE! zX;#aC1mrzfoW75Jflg^eQkb^x1TNiRr-y+`VRxEKbB?-(R zYSk2BlM|71E#Hv)$hk?8@p@!6>3M*dx(69sg&S`{UGUEDRPdOS^rRBHLNzhXTaAs| z&VHwlyi5Z_YGvxshrXzD&iuTmb`S^_Rsw^W7iYW|Z3V;VN5-#qMZ2l_oR;1~0C)I0 z*?*SdkMiwg=SWG-1@ijg(uLRs9q%7ZVS z(+KASGyu*g4#@h`?RM!Z(qd73s{C-QqA(WTlz=#zHCbt~)%2?dq+Y|s0rmDFEzu;; z+6N;chXwKrE6I$X2#kAXqAQB%61Dwy)85KCM}QkAt#BdtSvZ_x3THcxh5oa$dEzDu zm$8s@P0(q(T~I#q4cvm?L?{F-iLek>?+J}O2i0ddv0Ykq_zSI8!jT41RL8=I@5?Fo zCvGts8X`=wg0}4VDD|4Y84JnN^G^h#3}IB!4~_x(cfbQk>O<3I{8b-^Og;aTz5bT`?pP-FOT zSwDa`=gN~%k?Yvnh3CwHl*_jH@o!Pqr3Ax0!Clist(`_^*S9PxgUPqV^MTFirjRFW zNGw_O-trAJ7|$??n%^Kq=7_-tCnv@IQ^=&_#5wPVS`Mn3CU5I*K8Kbf7R8kIx;Ije zIM2zLlK%xiVu=M*CYq0orBP3V)*aEZ*E=XD0th8^?-{ndccBbNbkB$Z5n;nu)_@2c z;i;O{a~1>%Ht;b_pd8-2JFqP$fN-xWcJ(XG>!_az#2^7 znG*|%lh zb}u(%>#Kq(RPtS_XmsxqcFCF8`mtje|r%6P>!&M`3t71)U4 zLR^)DWs45cblIMx26m_oo_O`%9zru2k#-8omi3svaB?iw18Nz2z|VoOywAaJv;n*h z=1Ui}uyn(yFhC}#ik!2hB)nmOD-xUJ6NY*sei~Rsnn4BJPR0P>4I zjibi^R9hl|P>u1YrL6k$LeHnN`f{FRj_4=Mj40pJ}FO8$b{U|X># z5MG3m5DPHdWHFYbU7~*v5*?~4bxa=oWiw3075;axliThPC)WrQ>0hLrVnOyvZmK^* zI~YE;F%5#&J^%Lhb-e{KB4`V9gaq2+E4A-tKtL05H};p5OZ#0oEXvv7T6{Z6JP2m) zSB+0kUP(b=?CPW=0Bq|dvnnR$?BcGF5Ol|W7HE?8UfM6KQ}Ez$jNzblOOkUBNVS-Z z!Q=e8#~Gx*9Jx~^MLChwuhF!qANse-rEPFZV*b0}QDe32^)#lboxEz8SQnfLsI;!g zB^n|*9u@~OUF5X%ObYJ$UeBXo&~`x+GU$K<6)d*Joht<2&Rm^Q$L4PG4PufFB_MTG zl0I@NCqcF|j%XPK zq3Mp|VQf)5lW>X-``nD(g=Ti}c9(-ci0^A|7}HLB|Ay;gSD{T~_}jFdv_i~$_FgB& zzC&(#2I1Z`7=jt=e+{|gP7Db}w+&}o0^iPGb}9@lZd62wQCD^2Y^)I;E(1UGC#>{7 zm+e%-W3tUyEqo$krPY-#Or+v4&CsQr{&NJUPo?*ir1Cp72Z+$D%IvXX-N}ag^$&Ps z96i_Gep-LLAV{;20zNP#%agaweq#&Hn>lMS_hv4E`t=t9FUkW}l%@@sT^tyP&#cSlB(Phv*hMH$r1A z8~SALsPb3_4dz6zOb2B}y_k~t%5`VMWR#N+`d`GTN{~VDSO#CKODl07T*nL7*fqvK zJ8d)zRaOGrYGLAhT?BXa8P1yP@n7%tzY;Z=^c%*G9oQ~jwiCHga)zM#t&E8XPWFF* zO3%R*h?x8PN_^5JCdQ{WD-qAgIpH;oTbyl!M58&> z-*O^n+J8=%mI-4AV)}t4?p!A9!|U+x{C>>!=HF>ihP<=r>l^0Tsl-P=lT+0mHj$QW zCcy;L>mIV|*b9N8k{h384kn*;!(_um$WWvF)^uJrEhz*#x?zh8YEW|fPguW>!k@oZ z$A|gEc7zObP0)4op5V3e;ekm{b;wK@#Q;pGy!CeSZ?;tc(i;Udu9$^gNIykF;+WGE z?~qsOHwL-Xr+()|i&&ur!<(dm_C5zbgE{wZ?F4&%%v?wBXiWj!)kwrH`R~*ym-8 z!XO)sx*|p7$hZvH#d~R5)=pm|xJFno6Ik$>2rtb**^;i)o`fl^ht!iK+&-XUJm@@H zcoX`kCTr93O!|m!9a1-=I4}#EIJ6E*)CJ!K7mx(Oa5{jg3DVx%=+W>3u&8EcJmbQc zs_~e^x&0oGb7zCeO$36x0x|0No?P*4MdLp`EetKTi}By@Ne=X-<6q_C`pjL$?`-e( zX(LYwIi<}DQPq2qZ1m4a$%eb29pVAE7Z>rnVTfvRrdP2y9@EuFv6Kf8z34OL6DZin z(vgI7z;q?W85(6*3vq6Z*7X$YR`u0fh!^3tQ5~XS&K}8hOs{}=lCC`G4`a$QlF2KB z^&(@!BV`+;<(`nQaQNdRte1gPe4xnStOtCn0#)w@+A_D z0d0ItQWUBuvk%z>98rPtOjv(&NR6olVquzZ5z3~qdVANHwjI{zT)G!pgp?v^UtT4x zJBX64j5m(%Lz2~*PvoFbQ%T-&k~aSy`NlY^Y(XOk?@D~`rEmJRK#%%jx*0uhJYA3c z5_>6G%uoem&)P?~H_d}#2lnKDM`B9G;9nV^uBzo^tWrrjc#{P6hyh;0kOiBtyQyzSj}oO)}~#=L{Z9pXv%;R{j3H{P_g=tRGzi1v8t-(l~v7?0r_aAWC}vKO$vlb6~#u<&dj|wxtXWl z@3BV=^szrp3+mzOZnX&l6anNM0&LZ(yUnYrC=1NIz$@ zx&}slQ}U6#77O2hy~aWIC?RNx`lHNq$-Q&o_U`UGuS1+bSMH#ZioFduz~uGU9#eAn zkY3w)R3s<<5MG)7+%q;q(K2$=`9@d~Eb-9FKio!rn$q_(h$53$yBSc?(b2O%j zJU>D?(&(|$PT{YO4?WsOKFMaXQh4ihe9B|+uh!VFQ4|yf(_5DJr<0Z42coiETa3c< zU)-Ujq`e;sT%!!a)fFP?7P+qvrGs3VkGnMY45&##GpOTET>kxn~fd>akH05{E@td(VcLEY@DY zGcyj;W-4)o-*{*>@^XW(tJW3pGyTn~8!9N3)stU4!Pd^IG4lGvZA+KQd~SF3qOOR7 zQ{t%LWG~%-Y7~2e@7g=^j{QSV0w#x#we_Yp&qcO8jc1pj4&&s1@D_@qbK)(rA$a%r zRnT8Hrx#ws%yT6q@B15M{Ay*;126WC<_i>#>g5Ru8@uh?9btn2QDox8@o%UHrCil8 zdl8sN))`ItHLt+hwvz}}x)r^s+4)`1ZRRh@Cr`6v9#t?dH?J>b&#(}G#!|pBj~kNo zPW{2mr1b}j8|~=gvCBYD)vEkfa>961-TZ1OjOJi9xM^kzWg=;md-K%okfLYWd!t(~ znZiJ?9VJ{8hvR#V=3x*rm>)g1UwxPN-v2+8 z+M}1Vy~qXTU)yJWWaPhx%a;#aV8YzS`lMQh(3#BFV8>QgjAJkF$h9Fs zj>|rP692Be43$7w?9Q;eYbMBlXt@bF4tQF#MLytP5X8;Yrz|}Ft83$|tjT!$Q=GbI zaAdt2Y2;KhWFV>}jC5eK7G5>-fV)bl_1zxAP8b~5&G>5KTD=Yf0iw8*9sumURIBAT zEEQpRI!N*`H79Hogn-fjTem|ytlz9*^QlfS$CsT$dJJq3HM;eFh}rl@O_Be|JWoEi zH=&dI7j+2Q!jF5O45XQdJ}F#6U$)&R%VcCgg?n;Ta(6(zcb3;45`JEKZ|N^qB+$fI z?)2Us`C05R(+v1W4~7`?g;LqvSZ1TiZtl=`$g8fb(7|kv`HPbl0LLMt+y*V5kyld) znZ;eLuWO1CV4}9! zRPAJhqL{f@Xa13-+x#QO4k_?`Hh0=*26}5Wa|W3{v?bVi7So>i?RbkSSLC6`E?XAq z7S2X`uXGqMDs$BTrEesl*kd+{<;v|7G!{*Sdqi}Iq5CzSKpdN*(c>$y_sw}AJov_H zEx{V$STyMxVCZ*aA~=_=F9ev=iOFx;nIFJ_bVAXQM9U#Mprv#{7vMQ)2B;N!~_^5&Nfcx)n(q&umWwAJ_l!$>F_9 zwg9Vf>`xq5a?bcUCw8;mL+~;IbHBKr<@u7DeC@Iw0k$&DpK%h`GVSsi!`=EeBkpd3 zlnzn4TyG}nGWgS+Ptr_aRY}O%=>j&bwUckY4{2~F=m1IB=r*@vhp`Gryk-e*iCXJ+ z8c*ilBXUx3xcwT2g|BPM*#+VU!fPy#RT}MNml1!OiS|*W2gpLrI?lQ0k?@ayqjZl2 zY9G@po}+fj@Xpsb)_%=iLE|7hKcNe%9d=r(DPqDcY2$3QKE!@mz1}X^bbTA_2110J z@j)4bT?du);5X_sR%U?V@9$)&w=AWE-HdpW? zh6)m&P0~KPL(0bHcAsr!oQsowgJYJqavAjKfl#S{GGgXAk%P}nP`;PECeV$a;K8*0 z%O>tqs$wHGIK;&QQ`~I4TWr$jJiJ=eIyqCmEkQu3^*ngWOd)QG(Qs?w)*#cbnSQi% z2l=>1VrjX)<{uf7-MYsl6PcY?HanGaoi!^kQCqURyoD(^)j+|@21T24i?O=p3zF{g zl*hes3xoWGbn{??tn<0Y`HF0H;X+r;x+pArjhf&^gdAu0$Vsv%{Zt2l*a(|B#vIY~*`3cD^Z|b+zm2yRTrzHm zdZ~W~Ab@;F1RLSAU?Q~zJdvK73?1VA8SGXZZB80i1WxhjQTqB&ob z5VxRukN@DQKupMomJ! zYw`ec*o-CR{#GR5Gp)@k+9zaUf0FGzaZ+##Bv zh1Si2;aE^1z&YdyhJYro!6ZhS{Nup4C(pt)TVYo1PrAZN?zXp_WIwL^5!Gp6$1?<@ zH6d@$u00`uxHnX`o(QY4VROGeqTZ_m1czj@yQqP;zCZh>#Qa0zcGFkTB+}(w&_V7a zC9%6qXRlKH$V*4gd8q*_`Q?a-#$mz-V8=0O$IYExDvfy?`sDW>&C9y0L7`LrU((*? zH?J#A_p@DA*}BV@a$Q`u>6;|lA}z_bd@mQT5?!Ro=0{0gedON0G z=T;HD#$qG-YFSJc8kWZD3QKbfp8>xkU<3@?=gX>LbTU3PntfZi5*@9WIBY9zKLnBj z4{B90i|!d*cK7x^cYg$qXBi07Tnjg%_=iN|FxcKeDqK`wMf_{*paeC zgPP^UWTW>$TI$$5l$wH4_R5c5rH*?&H8f|RHc#M+7A85BvR=x%qO)}g`Nr|FcsuP( zq8%Wq(_IU=#8~gQ7IcC=|iR$HeHXY(n6h zaFdeRLhj1BTBZ>eJ|3f>j5UF5nm_G#um<9SX_Nv)q=1`t7CaryWLC`>xIobOhAI3m z2;)Kpxaawi!CW<0g7x>DRKg_c7;uQOZpm)=C$QR+c1$^@mmn5tBr(p%XKE z=Y)!1GyB$2%lBRn`7OmWBgo~ zg1bRSx>2eot+lFgF{Kzbx%gO2wK9q%bys^F7L>=_DDPyc4+=$p6Mg7_wkWPqM~$4M zF*VxA_lRq{t`swL*Ab=dSEgGC2#X?xK%2mu(qa5X0pueJ&Yha#PeH|+=~q6ys^X@x zb#U6KZ`1YG9n~F^5Zb?m39t@)ygPDZW39Z6UGN`|Hr8|o13BVoc$d~)$0$-7Q?Dm^ z;y^(;BieqVe_H;efmj=0h;wA1`l*rY^wgF!PLiq>%f)bFEoxH)dE(Cz>DNA{a$Y8} zLY4lJsy&-sat)3;*l}2W11!Nx##pp~FseYvvqLWPD4rwAS7N8uE2~Q)`0#WwqpCo2 zyL2!#56V;SoL3^^KoKb9zy}we_MIcw^4@K`5Q(v6G~1vznP5^=Wjy0&b0AhaM3#FB ze?nfsHVK7fgm4j5XEg;zMN1KAzF|J&5#TEREYE*)YAd0|v4ppb>iOb$5)6knbVMVh zqTyOA1{T>&24s*NDDO9;;oP~h1-0Zt{KbrY*2ils88hN{aeDx5yD|F@6x#LCCr!Uo zJ{z+o6NKz3%t_!(MJi#K_O?l_KyRlaIOI11&-7_vdJ^19orJ37e_fCf>0N^D*?l(@ zN!A!%CRZK%&dNPYJ5^V=c*z=aAAWznfNPA+5*RKcLii4ZFc@VC#11ESsX$7i#vd>U ze>ifNCRO}`tB3_G<8{DzT+&C~dW=;g-p)Z^S=B`T@&*(p7Nq`*zmznyCx%z7Q5LIZ zEZq5UT(ehC9h&SNxtwbKLCTCa${NqcvbGqtEV4Lo=U}o0J@9`C3qNdZU1=rt&3yQSahQ>)5$!i z^qTN?Y>s7P-9;~!9sLkP?2#8@`EiY-geH`}lJMoV!RibCU|tgF;i7Q~PD35sp+rU33r{pPDP@n_ zLxZNr#hd{(%@24j;hq8TfGnsnMbNLu&y@>v=38lNdF7;l;`-aGv`E7qR0Xy`7Tqhz z=Gb6!>z+=2RI@t)ss5toTYnL>sYBC+04jb3PP@%0uk7u~i+I0galEDMShPm-y!ed@ za1|5>oLI&i9AdPpf{dE7*f%o|He*a1FC~ke3MJoMPmQSRF2#R-uIj2?!pTb=wsKnF z3@h8}7Qxpd2sJS9(e> zs>!}cK4R{HX5n_|I(so4e4k|{cJxq=SvCnku+38_lZI@XrsEH=8UDbajnEYKiuB48 z<&I(t&euUR8w9wvYwb4`jj@iXRu{e{!hnB9nDWBW#G^zE{3Xj6hKG?+s&TucLmvV{ zOg=4V{EP7Ug`nUwA%L}J&z&KPp4FJ)^_Nf zp)SfS2@w>(laEoeMr_|M`)og{yrm2eNxkrp7+SW4uS#{*<{eHBky}1mlQDTkdPV6Z z5#b1f*Gapqm^?Ar5~e{}7lEHEcTyc*r7UrB!Wxv(xK_*~%BudH<(tUTN0=!^{-_pA z{dZ`3#$Ek^tdHhK05zfhzK;*(O#vLZ2HYl(6R~Ty+MZU(Hv~|~qx*L#dBg_zL9}fV zMl;;_JppcN=8nJdvrr4zu1CO35Hh*t(;INtZihqF%ZCz(PTTasMJmlUR;BHj8>C?^ zC^}>C@FV3|yIb|<%NUCh&$Rw4Gr3b{lScW?kq1>==(nuJ8T2=wt z6n|#ne@EML>6(tw0&@{GBvs?-$ZbAuBHhRrm`nFFJcGvQY|_SKY^u`sr1G=JU27_z zUg5UNd{bl9iCgMTDN=dY)}5-776`xx+`9&5w>f7xF>nkT`D#35cti0hQD^$1aKBt7 zSiGv{PDfP7gD}c(s9#bA<`i`$j7+z63qgqBd5C@ zNyX$5IZv@QXAt3ipUcLa{b)C~B5z>~8?4Dt90`jbPqsyy!GMwrGXQ#bn7$j+`}FKE z0JU4CIUm#j0$Q^6ce4XlDwiAz1wH7P&WDM?Q0Hbhx2TdM@=I?T;+V|c6tUU^%Poa& z5#{12z>&IJj>U+xNXZjKwXgX#8`bga4B4!wu(;o|BAk#WiD5ibhFAg&AQWG^G* zqH)QJ&13Lj|OE{#S2!+0Q5mAe`sxl`^tk=g!UtsW3)v@zpy;2)^MrL7` za`>vos9IAT8MyO%L00&sY$Z0`Rzx?7bMn0{lE>;HHCkFZzN~hbktIwTPE4^M!O*Mt zQ{gZW)05g^2`$U}d8wisD=}9G*``wff0+K*NLZ&GVLQ$dzEP}`gw-aL$`Q2xQr zzB5v1ba|jl(Q23}E6!cPz6ntC%tA2aItXi077_OlARtF!jj=&wA%*g}iBDfjZ<1vW zwX0MdWgqilhubwEEw~QiP2}G$9qUv9HvXfvGg+bPZfNid_a*3VA8Gmu9}S@^`0Gjt zoFOJz56>K%|ENVubfN1E9E`QG7yQv|ke*RHmK=k3vr8JS=?~zseM(y0y^{h$eQv#c zcn&JUMs9%3g}Y!k(pz{aE3(?6<8?v03b*rbL%g`TF&P@#q4x~cUb!}1&im@Vzou`; zTs9IZod^jN4rKqCh#;bI=o_>9aA$WhmGOzv`2K{4#qnZV4q15!b*^15_UeL{p;!=x zeN1^Th5&bqRw#bMX75qz?LWD{{zOA3TJ^8_A4WISw4=RbcrfJmAzFIywqtNLy33b!DUp~ zVTx;3X9w;XnU0)AIit+AJ^;+Z;i4cu8p>c!pA!!G*THBgJZcT7c+T;Y+b9rmF}Fu; z1PrIynUnaa3U36j5=_eXP<@j>0$0UpiMCi;Chp@4g}cKSp}tZk{2HoFLUAdbb&tAI z!eYP*;4((+g;zo9H$TB1%PW%4uF`1AMd*m<$5x4WRuyn!9JDKWr49MDSS^#yOzT?V z-)!?8wwROj(2)=I!P+X20uhF9kKtB2i7Mgd8X__@6Lh!@CJ}0j&x}7H4Tq;0VZfY5 z)aE-TtMOP67D0|OmPo4$%|_;DrYUUFl}+z~%j%?NUM+qK<_PpF@6+7MZqt7gRS_o! z=*s&e*NE2yDXIxpe`^-C{+2U=S7PswR$prVuzBS??u}4GC_&>0=m#f40#oknhmL{@ zs6B!yh9VdsMCK;2w^#y>&LhfHlQ{{G`U#;TpY-@B6wLW0!U%7M7~hz@mP9oIYl=(` z>d?v{>q?Ku5WIIAt@)}8)!Mxd(T#M^D9gChmL{i&@{pMI2M|=1Xo@1$a6e#cEU7LV% zafWCt3Y!JVF{VgmN(j0~h?08t&H}kU#PerjtBRd*S#)+VZWs}f>j&UFd%AI4r^=LE z0uS^=B?G79$&*7A@I?H}J!x1RlrEM>$GvXKGYy-XItePX6yVGUtc7myOKrTjzE)ji z1}&<*8-oP4iuf>q)09%~Jd)o^W$>YL98AEV{gSq=9J}m+)j`$r3ez7dsjeoe0OjNL zc!xG6c8aHh8#%LDhvBmCm>UoV_L*aM+os}{!D^&HNjxpqp#F!7tdm80ym{B>P^i1$g<$iV|@^UXN zI6!&IioVs$VUoDlXL!46ZIVAuBJ2I-Xba6RIqi{g=m?DDMz_b;k_&#SISWh*0Stqp zO#;=DY75Ae5(*z{dIXbSMkkb)^lSJUo4+`;foH%dklS6&*res4*rfKSoYlR?gwL^p zKv?uSO1^>pA!Fr(8Jezn8A{W0Zxyq8FSxgc*EN)<99}>v!~@>X z%RmtjJWJ>Y{Jgo2Hs{dOiCSmUxX9cCbpu_Y{YNG6JQ(i~9MnlSl8lNW%3Exz^KSWp z@-gUb0w(+tP2oIiERBpU5?a^V;@A zPiZC32?WXcVdGB@BvXv8uhK_VT6|C;$R>hBN2*KSc?i7qROND&M7FinWk=C$&6FLP zWl_}osetJ1+dIswB%*o1|L_4OA-pyJM4%S~14!Uwcj6{PFH*rV>WHMnkz&f2r2n9T zt#wL^Ny29~?NwU#_K8j_dwp~r;&Ka~Bl2-=3QMFFV{VHBT$^Hf>^as-kHdG!5T+{J z4ldUCW6cR;0%PvY&=|}>aiBB3Xc6#t>J|tbohO0T&c80o#vk<+)(QVY#g0$AY;nvm zd0V|)Zhk5z1}FhpkW0${kS__pjp7TG2 zYP=x+JJn~}yNAZ%wyH2g{taZ)-z-x7ZWg}L&ZFT#7st&aZ~5UxBv|c zg`8;)xW5jN!yaqMWD6#JRd%RWtx)v|?j>|4XQz0TeCK;T^K}PJt4Qt^`d%hQ)GBNZ@!-d$e7?nW4OqZa$&*7e4=xkpkowWMJg7z{=qRnoIDGg>vFG*U?M_SaC1i z>C30?#OQDeD*lVNf_DQaeJO6k!&SPWYTq(38-I|5qzyr!ejG57_@5kWf+J>!u)TU8 z+HGPm9l-&<$Q?xDWL;dn=eG;G{r`_Uz8ZJj-q}Wk)D7UJPEQ~(RO)Q2MDAZgx6n8- zjwYfRR{N{CH+rbuD^BJp3p1Kcp|k9A3+gpIzox3-%v#vaPIk2C(r!aXD;|&pMR=*eQ=eYj5P@93+uk8iRFk^1CMe} zY1ewE&6?`3?b4Y`zTX$0>we?BDbjCezOL>rHdST`Z;_ywLc{1s1tBA2!7xp-53Xp* z>vNr-oVgGS{nRi00j>vrtzyx9$9StSSopM$v`G z5VL>UzGgx57#>Kps>xnsf0-}72)^)Mem#&1$#)T5L~X!9JMsFrU?sWT0(hwMh>U9W zH_rgo(>L*#AM=SHbLnGk@s|VJFdo$3jmA119AmbzZX=9`H#t(_@WIcS`Hs9Cyf6`b zu5k%lTf6@gO~sr_*O&p2Q3B;Xq$VwCjWnlp=tqcR{xuL=l(%qECI#jZ zLHV9KMNCh**hHr%beTblRCOJdC7d7g#!i2VvjPC(gbm8mx98WetwLwA7#sIAARi~7 zwtkYErwJYV$M2)APZuErb*No1tuw>q8iYYBSh^rfCAT7;!2{{B%$2L`g&2=l@dLGw z%4wV%U{j4r*3%+du>u)m%mc7n-5KN}G(g;5d>=Ps`I!c<|K&90*c5u9W27Ck-U1{TZzE2j#9hBt40UMA~@rB+(B$|bovLjd&3>u7S!Zd-4SM`g# zJz~6kgiI@k9IpVx$?Y12U};YVil;DigtHhpG$6f z{!Y|=t#3eljMuHU84ekpl~%P|q%^>s>{33G4(Wd$(ave;3aA;WsVB}9Q2Ztkf{r z2*ojZ87W{wlq2!f0nZ@=RuK|S5Jat0@4+G~`x{6qr^A3{v$QHqMew6~n%*)iHX!C{EYeZ6QV> z;7aiT7VAU@J8d8~sy`Rl?DS7^Q1jrx-rN|Ch06(!LP8qFU{Ty!9!nNPOlP?)pI&}) ze~hUbd|GKD*MxCc=QtAB(J){qFldF9O&N5%a?>N*wRm4Bl zT9ONXEA&AUbK^0U8wSf-5%K5^1ZjRJQ}WA!9Gpb8C@qshqh9$Bq{U+IHuLT2MZO%eGDF+MMDMWIV&-$TE|eI=f( zT);3?RqEUXsOlN$Xm1C18eOJBMCmWHKdux?N-YGTkJB0_zW#Ajp4aR8yHTo@YC&AB zFIeKUfdY6M@_`12X0KG8iwQ$DKK+{G<*RD=lT<%_C2?Vobm~2_qWIsi@qD zPrrX(vqQY|l^35D1hlDlRlF|l?Cb^VMII=Ae3qzZ$vmLW;R7mXQ*I9q)2=2#+S)jH zp1Je8<)yjGeqkN?;?RV+g^0Dv%=O*>7xCZyzoide9ORPAxd#+SFgYqrpp1H}6Iao_ z%qL+w+)RM@pnSeWZ2?r#rVvPx?qXOy%3~9i)KIYhwWXLkFPh4s`!yP1o_Ur zOpqodBS0jX(Yf;7N6l9N{`F!L$gHKldM#g@uadQ18F}8=S#s(WNUer$A`Fw@jhFM) zxkl+Rg+soaZ>&v`u7~Ip=m{Tz2i4Wa7*;GU$$7`=dH<%5>KtPOmMRN7`rtktI) zZs2)qI{2*aD!&T|R|TxRXziyvYl*10j3x3w`Bo4>#a+0ILcNJkYAUZ-v;T4lFM& zNa4U32Zv_0K?l!ILh^&3&8+So9J-T1e|?m2)qlsd3r@3mP1)KM|s&WL>d zU3sd+WXqdmf-xIRAtF2HUo<_Zy3L`Egm?3lKsmxVC*7?nilH3KMHA)2WzamiY?9pLYQ_ccLkr%B=(m1ZDPQLA} zvP-l^>x0Mjp@-r+(--OmTn?(#366qwmwJpp;MO=Iw=Hiv5fTbCSTt(PF+1Bh2J|;j zsaiFn@Eis-5v}1j8fM(0SML^4Jvv=E0~{;WexnLEV?-UA3X%mi@OWGs;5{g3CNU61 zuaUp-W!?n5w$Q8iOO(%$QQ>FgrsxH!RIQn2KdXFrL;S);2w(jssDPB#E_qo&VQKH9 zo$wV@PC#Ew-p~~#WBQFe_6LN-pItW-z*@d6WKJ`jW$%Or87QKViRw2w8H#$9mniS` z_&~^-Ra2I>A(oK>Dd(oupOa_SRy8JoQwR4S$kG#KJ=w+`QW+c=#zmLEyDTnh()uVsY@QcYqi#jdmF{<^@eShW2%c? z1cg?R)8I`hp1=)`Zl&dhkmgM=@AmdXm(=3A`}W!6fVHUs%PJ}mnc1RI(F_;odBs`2 z=a#sNQz50L**Sd}&PlI6MVcW{ku%KT_MiYd0|2USyV8 zFxxDgMdcf+Lan-H2|{~s-XxocR;q;eT@Id~fI#G8^WB#9PHNaw?)8U#&e?*lk=hIn z6iCn!ERT5kS~*#bLH`m#iv*}3O)|6YRchJVdMh+{rbT5SK8UALPEJy-UgD?yB$r#h z`7b}IpHYle4?pU3Jc^gaYp08*KCM||(Vr5qFT2NhDl3K~e?c_ZCT8mu*$5>asST+4 zT*6OCpeaVJ^j6VMJh?tluczDxaFFxb&`r`%Cb|cwc5Og;0HQj0K*J0`xE#dDd-w53 z$%l+(als|*G5o|epi;tc#(>7rrsa$7Q8J4HQF<#LRt@Z`G!kBsG~4K*gG1NF8@RFB zv;)pD4)PqlIPI5LCS^*OV;^5xP(e7gwIuuMiL~V_SBb}m?M&KqOI2tQQ!4DTffd0A zkQgG`tt+3}&{7%2TrUSvy4;$wcQ{n4g3u@!d`jt~!eCPM47F{@!)S0h!f`#P8bRcN zFprkV>H}%~@HSD6s&(SJ5sWAsfWE2`S1;VgC1LJf90i=F|9m|fIK4o%`ROeQ|AmX| zuoNDh-Xvx3NZ>$%7MH7pmf{WL-1SCzahut7cmf!k6EZ*<-dCTR8tOm{+~(?oF`fVn zXYC(zCQt;YtG^{jGORwh6NTIY;ZPspH&GiovCrV^?U5OcCq!1`FEvUrTQTAYDuwnb zYUdr|V$*zF|zsYzSvfrdrzmy1Mc`hdE1f6z@@ zQ4$g#JvynWKE)2L+R>+6R>02~&bXKoZ##eN=~#Kc4CRil>^z1`@P#?`FO*6{HP4qNU)AQCGM z>2J>rEg>2U;-(^3%7IX-r+?xX)hG)Q^TB%p@Do;xgH0@bA-v$jc5YL2+tWyWNOtjB z=;6~jo?;wS6_$ddjUXUEUl-8HX=)XfINNIBg<`JUB)He9Jz0K&>;rJ`)4 z5=Q{RFx0`ZH>I3AxNuTSuoAKD#>f+T4?sCa#h~=Ixs15(p9KNdGqbQg5%eCNcH7~k zBuWG8)q^N1H9gw;o{jA}D?+|utl}vC#Td#eX5m>f{bET#)gi(ilPg(rFYxht+Mda#hIq zKA+TkzpQ`McDc&ApT$YcLBhp;hn>HK^EQ zsLv{b?r{}RD(1O^#*<9KrN{Wo*)M08Jzz7Jf_eC1$3;M}3;~jQo-KtiO`=Iib+_;} zW3+dGheBEPrrEkKz8Q$jyO)%(JUQ1bewoD|yfEu(=dE%cUz$V+={^jD+R&T_ zOVmSU(cly=psCd@I!u$AHbY%xZ2{NVT$p@XK!`_|0p z@Bb+;Dh8_P03zTi0j{* zKcU-;+H4G=d#)4`0!lb%iSD-NOh&i%x(ce08#q}9F*6IL{!`Hoy{YJN1H6!1lC7Z5DmZ?softq$yiYnq`CKEX^x~}ychbP zI1;(Y-FNvv&S-S@k;ixp3{`8g2#1L0*UMX=?{Nf_L}n8$5p%K|yXd$wXS@A13>6xW zdV5O-FM}o|2mx&Z`9PRond?U7Es%N=npWQK&CkP*`$M#aOJk|d7gTU@KBvLyYt>;0 z50?X2L0WHZ*nhi)_9i)#m~~v<@OT}Lp4*EL>hcL(AecPKmfNowPx)X$Gh^Xu6lA;J zprf`9*1ablRl0=zcGHetl0IW`m;Q-j2yP)^%C6_xQA}yrLmO~J)5@lXQUpIS|83VW zA=}}>f3nxGCUG9SKx?t;o8sl-&BaBkkdc)xA7*PxT;?MdloioBJH`>MII@pPBFPe( zPz-ONY!#F4qMq@R_^&)pHGL{)cdl-4@=;9$Ma!wRf5h9oBgN@xt$V8+)=iNS7L9+d z(+ojkRQJaMSEWlV2RKhgaEh|MUFqR=wa#3rWg}|Zpp_COai3*#?GMttv{?EdBZXO$ zYFM_T=Ugw1R?~?R#+P-uZH??$_@Iw?6G$+;EqaQV z5IX0unD`|PtGXc#cS8DbQiQF*82BH=+I7rKs6uA%wSuMIrFB=qVaQ8x#{@`7&}5f@ zWJuydw69h2_-H!!MljbxGtk;`A@NQ_8ol9_XxG z?$ll0N9-BRD`X$Eqayb73?HMpMO$WDh(oajn6fc4FZ+0OGMXu-AQR{Q=G&E%f8`}D z)g>Lg$Zuuhm{EoQt#O^C{z+3qF@Ky|7+u5%^yP?L3}*tUgu3j`m|hI{n`{*|>-=0| zg@aJVQyQ#xarY4CdHMHmh;RkX{DIfL)xcH`)3y{+FZa3kngbxNzeo(qj=Gn zV@Acf-W?I}b{Y7=*RWixuLA0@qi7UF4Xue=HN>6~%;NtbEP z7^5`&*1hr`*zWciy;LNNjbtdWMh3@-`zVTt)K2-xJHdYKF))?Yq|KzG4S5YfNT;w! z$7#|4Gq~_D5&w4hfIMRM%M?b8`lk;llS70$y^G*d<+1Kv%Lk|hj>myp_`m_=U6xc< z5EG%);VkNa!vM}Vf*!A>M!WmJTD1)y+f_;eWnhKkw0XgY* zZ49rKh3RDaZq5oi(8(=%)7fI2f-j}u_HP~HiN^O#z~<-PCMeg7LmiPo521VlHkas^ z(`2G3iO)Zg`lQyb`zBye*W{z|oTt%XaM+ zFqa^O77Algayd1{8zFtbzY3!_q2$kgKbEj;_==de=x5> zvSJ8O3J{0{LW^w$RRh+2Z0gc|z4J&QvCNURwCrING;Q2Z5CF?9z(8EK$bf~)W$$S~ zquGn+?A?dnQdyWTe16X$+T_5#GES}?651D2+7JJkcdBf#O2!bcU`!BtC@jX0)64k? z%4r*@kd8mwu0*Z+p4wQ&XM}2C6l$|4L4*O(6myiF-h#dw8Ga82)nTVd_W&KCyd8%f zOI<{~UxN`TDci#~9+UC+IJ%WP8kj{nBr%l}%Di~CbW$E+tyh(JDrenP=X#|Z0XpXa zd;H!rUZnn^GL?QhLoiNH79nKU-|?i4F+yW)r1)9BLY#&r2I3{@n9e~boOegErN{6x z3L*nw-HuD*z?(U2H|p2kGhv8Rm>t8rpz>AVqNZ3W7>(=2dH0aYw5oC>=>gs>e9c}d zpP-wEVGVHXXIG)-f-Ti&!F>l7s=$Av*s$Y5)&H_)N9PgU6UiaKgl3CskP z8Z$a3_nFf8E-{M{R~%*Fwm2#W=m>JVV4v(R)?p5gAct;FqacAR`?*R=Pslne{B&;u z)?tcrS_Rh3Bguck{1|iePo78N&da5}g_ls? zzfXc=lR;$8Q&m^DE2_y_moq3SPkD$d0y@y`Yv+Aj$5=U)1E56(DIx86rK;Q0ocBX&fH`+D)LA7S z#t7u^G=o~JN_JV0CcRwHZxztF9vfvEo_PK+8@^DBP8rH3v-8Z65Nx_^&*L0JB={@c zj#?$tmjg}L%*U27R9vhF-$mOzX9{>f;516(WOr zY06XrH$k*K;jywl=Cl@j%`*MF|Jz01tN~?CE>Hf(q^`b zJs$H62j%Ya`drF?LmY>1UI!^y49(J)uD~JnQWSS5JVKN7DzQWE7q!k(!wC;t!ouv| z=O||XH+5Y9P56DZ4*6jW-IsRA>#~Fw!c2y;QHzi`kOl?G zhJUcsp`Pv-z!g-(+AwaZjswQc>4nGUaVE~#Z1I*d149q!G>nRd%f}N5#5t<%kqCA} zDEU-;37rrROn+Ei&J4AJu<{+cJKi0?!EDN|qtriM29kld>c>IwI{r#`ZK^ur0u_*r zdzwhU9|5DL8Stv;ivxJm#>EkmKT0v2M6LESk|7o}dR9J+y3B^n+{R-M z^0P@w?Mu_;md4NWS;~j2_V9?qGwYo*D~q6;zYjz$yp-@@h%)u)-H0&Cg;}a19Nz0> zzj~OjmAt58Js1i%_ZzZU0~nqh*3K2dZB%BCGMM5=GcjGEwr~XP@8aw02{O^yWTyau z6oasgupDu44`rzu(y@DRc(Kwer`iwncGSB{BT-Wp)+TFZrr=E-aOb=V2my(Qik3LE zjq;fU`%n8Uu&jiH!~%4JwE26XF%b0xb*e;oyCiKmR|tzNfUgDu(EvY5+R#L%$XH80khh-XQidHZ-xSY$(90-a2ra)4V zMts;E6Id~%&lJ4i#0ugSo%3@cN_-+a;Jww7DOIMRO9$7esp`l8?_ZPaNC|$R>HrN~ z#D{XQLUn9CmcbT%7QUWV6vK;cCRpuuCX@YFB^>-&wW2LE4uV zIjK-V)j%SsL)*%So}$f1?Ho6r3ksTHg5?NmQtQLPz#j}YR_U(7JaL3PC_0T$LRKE) z+W|O}AZEFH&V%~YML}9zE3BFVj`A@~4p9xpT(ASbVjiPI17bA-tNWEJ|xgYMg3NbL(d=le>bjHROp&aH7 zKr7VeStj7P7&kR-2ZBS-I`EFd5DI#(Qw2rjgEVULFa9M2j^^0-KyjBs9g=u4EIN{6 zU(MWN4Cf9vgB;q+s@v)tL`IUjkUWY?dP94&fu97B$s@@PPe2|`%KaE$!XLm^`T&dD ztGA`52LU4tf<#1~ArOEC6FoJW-V*IPV9uLNdJFNy3t7v-hgG8B<2sWowZeOKC|78O zlvg%ZI4Y)12@YmEEx=0=Un#%_O+;LzKrF1$#$Rs)_rxJbxHuBeX|b`UvPnLnE`Q+b zaR-EDbB*?{aI1rZId$eDZl{7smGTe^^V7NwvJLm@kL-DdoZ=U{jbJLWrc8HL6GG-t z<@DcK-$68^>M0nfe0fbZfxv8pCqHztZHCs37eiL6a3W5((K;_FM~+0o)qhZiMK7nS zk@!dl2bn*C{;0cC@r?jZtEBS96`UT>Vi7P>a89Imy}I%_<_JQ4MS)4?S+S)na$L1>SZ{c=d_18%#zBCy^rBN{)9EP6Te zohArFO~yjJ`F;GGZIew}BmWs`T~5110dbrrqQDyy`8U`K{ncjtbCu3FZJo%8>r;bykEsc1rMBLa2%TVr zwqaOemBQ`pF*Tnznv%ea&o{SmX0$0!U70x=B&Q-Qq3gy+pDRbE^-c@C6IJK6@OR|0 zn4!FNl@?@KGTl>^ItVrD4jbvwN;G9Cv#>GtfO()frfK;5{m17_^I7};gx-KGx-xrjx?=DUI1r=sC@xO z!muNNsxxNWb;eg)Qr!qwm}jd|2a?f(qd#KBpE zsK7ctWjwHUb^ieit@%GXP88^f8i7ukBBqxO*c`uTG` z07_B&c(#lAI+jxnLuHft5c=f&8O+j~TTx#T=@lFCE~FzVPQ&qwT@IT;MizahNvFf_ zTRvTCMImuz#~(^bX`Q`{C&N#~nJ|mJ!FWQnvAAh7K8RWW<&zl{qi}eNdAEkCY>&zX zJt~#!ehSNv;y|IDfd&xyN;QpcO|(tPAgLRyEbgIgJwPlC%>0A{yyJKNey9hgu6*?9 z?g^Dy9bK^`FG-TsVV+_}m4>8%cGk+cyT4T*`5XG5*Zspu6L(%};^6sZ2!z@8lBcv} z>kwrPoF@{$1Wh^}!eMH+AA*#anf#?v4!n>=;x>n=!-nPSd7Yz{A7Gbsqv0Z=*a-<< zcEwd>9vZ%5(QRon{aVj(UYDgw8I;qrIr2}p?)9O;$_!Gfd@Eob(*ZaJyD}m$Z-q6B zz#abww06Aex_Yff-K8(*JK?v-yiHCbgF)XX51=jbYw^I_s1a-g!4b+62rEWk>ZjEtI>nvz8|?QTLVKn{X|Vk0}?ArG*{< zFsIK;js>$R=Mnz`A0QU%il%MOQsaY^qARd6i4zcodZkQ+e~LZ{-&C3-rpBvhOWlqJ zr&C)#azYxm9?#p)rOTEF6EB^OmW!>?tv+(B~RTBUzwCu#N5o?bj1 zL0T5E<)R^ts$&sgu_1Uf%_FlsGZps(y4d-yY8&4n<~;mFl*vH?OUp3itqHeust&%B z^_Ud<`vMBo`dYu+h_4_CNG#@30!KMjbX4h9K{;szw!wW0-m?o+RBC6ddTz0L_9B$+ zew`a{vc7kFN8>x6_*Iw6+X}TAbACTnG1t^XQA8orF${KRVa@vr0_pbO?)MQt72ME1iGnBs_!TI zaSj8#87Ep3ao~qq*!a+erA-SQzY6$cNz*Mzbj#WFKaWlZPv77!BpfbJj--Vs$Cn)6 z4}-|S&;Xrt*SQ7nfhWqk zVO84KM5mPIASRj^C90PXSM0|5GIN?_Nk&-zRp(~sO;YRTaSp+d_sog7fjbW(CsNNv z9h;`0hqq!cdzWW|0dI<4_D=U=Q{?T*-u72{;q&u!XbC-2pZ5ltAbJ|KqV^^|w^vmR z+(K{0Mu(kg*9~)|JOh=JSikC;Fj?5CD2S~>x%!aU@PWGV=ret)S*v$z!hYicw*4Q~ zN^L71cXnZ=aBHwlTDrqw`4&Ew1a^Pd6oZ+ZFznsd?JP78VFA_TuN1Ta8DCFjHb%j; z*kJ3VNFY*mqkoGdq-XT*=B6sN0Ewvclj-nhf5v_Cdbb46xHS%`Q~I~(b}%E(4u|Q> z*m7dhY?)wlVXtz|woKnp)@UzXH|wn^;2Y~&?Tu8K*G^r8_FVP|J8KArN?jdF(Ya{d z)w+_#lPzb**ZkVVIW*o;)Ru$1iG%U~q|}tlv6F}T^_&m-Xj|+P(*{_*0|wo}+9Iyh z+T-`a@2iPFyo?+TY_5-7(KwTNy#;A2F&%7Kjs!dDA-f;TtzVTaAH(o#Cor(IY#cVB z^b@RmqXKy*hA-+hMkZN(Aya@|RGpZzQGS1|soHmV=6rQ5v#z$g98yxu<19(N#%#$! z=c+~8br=>d2w_m|BsOz2UFj#{KJ{B7Ik;sj8Y=7>2y-9=mUOSKgD!hCA^ml)h3>VMfZI;H0W$&<=u*vuW?lORTG&&t0arT-=`SI#n?BzzvnJ`S)Qr zyVK(Ouspe(Ss`5Y!VXUb+m91{IuhUrMFAnBLso$#+c(7U z>T42Jy|ZIeq)E*wzD{UxXd24qH$`&=E0vGXW1b%k<8vO|BWV}#428y2W3!pzEy?bD z0A`F4cEl4G(*q|oQxA@c^fbtf5TX|Sft-~7Dh+`06A2xKH;c^+;h~r|W7Zv8C^yE| zg+{_0aF~1)?D)}a(DK0Bhm*+5D>BY?pl$%vs**T4pRr*|TRADA7@4fX zvqzRPINo(rDhRrznh-H4-vxJEb>%$YxqkG?$aS8;?M-gyl^^V5-K0P zS6=E?j=tJi19<*zga?w=YSJL~28T-tbUE#)&05eJVaQl|xQ$)2@;)a!$(o}r!QXas zXpN&iLy(?qgh8v7^l}U7T%ci?ngga(kp?8*XTl-ynz)4Ysf%j-o-T0*DXc z(pn!$kO7DSKZ^&^3-@@nQCD3W>d~j(E8nAyrAqzJ!XoeKR>oLHUsUnu!N|)w(HM4+ zgQA|t9@n=@+Xw*^uf9#OB1s(_#zt81yI_4mkAT5vCb*>86VJ~KAzFp;-1iNvrk?#b z8DbBgy7JH@i_2IFv(12|-eG7r>Z@y+P}uum$F&1dE5OP3ICCxz$RP_^Nti-7u0mip zbExD4tr6}Hsa0VW%^s!9LjCBV#Q#P;8%?!UB9siTNF!~tu|swxt{yrvMl@Yx2Zvm} z+(dgYCLY^h8?6nD8V{AM##WE10YtU|=<7jdk;MjRdalNzq_sMmc@f4v3~xq6Ul2A? z2sXs|l=gvZ40N+TAbF3Pf(Y&po|kDZFUk*}gqtbA&T?JS-Y^IyWMb$Swav5k@4Zj> z3gEEiB!vu~GQk2CpgQ$g?n)&tfTlX7%?v%V452taAu>JJc?XB|q3d+&^6L8c*l%I_ znEy9D#6r)Ge$yJoW`s`ZWCx_RNu>&h<9<{&OM=GBFY8EzTbh>8>9d4n^8Af-;XJI)HA1%Yxbriw-T=Aw9v*)I55SBZ0 zShK+Dm@OYr!i;OG8@D!K)Hu$LoDf7oiKtx5ZxK+;tjwc+pz>daVHk_WvYmBY7MJJ? zt%L+v^vShT05z)T*RH~F3%~9XNT)jqXiIYlsqbow=PJHEMe^z&5Mbzo18Iguj7XI2 z;&?L?PPb8nVrWX30QrAv({bV8G5Zk4AT0ryiSFS^J$OMd945bK1+(rnF5WG`w(y5Ad(; z6xn_2`lS=FcmlOKN&KEB8>-~x?VKJFQ!;$lr@-WLI>t@3*$Bq(ehLPp4DNnnUwQ$T zQWHF;U{?x}RYozwX@^vr9K0Z-2m3Ip)q8)Ly)Q4$5qeFsM;h4^1f~NdXt%KWxV&h0 zT*D!b<#Hy~R8!u>T;kGjL!rP{G(++?N)VbrHLV#tKvF3Ey}Lw@Y~6*AKKUHjl5v!u zoIhX2i2nu4j!zqel7r@i058pPemUpqy(`npy@Mc0b@z_qurHzRB@{8?4|ox1bA2-2 zuC^qq;Brn-u$7l2$O^d%3I%`b9wbaHdGAGgC_*I+gz?_7F&EFDPo1!~NhBw*zVRKtezi@X>HU2tsN zjID8}M}6U_S%i!VHFYd)Jv7rDrs5Rm8Tu^Lji!hVg19~@?2PnZd?d6n-u+|0Jj_8b zl@2^pcZV*-*izU;$2N*Fl&1cb+y1T)R^H151ER1q2KP&W0%B@Z7d_NMzcsBWT(yU7 z*hl;fzCjfct>HpdD|@)vtb^w@-r(TqbjPubZLZA5`6P`?=y!AbuVjpKNMdt%w8GKj z^BM>|lMGxA>lNq4zvZa%hYzvOY`H~U9)c=eOe(#qk`~*vlH-(}ts6*Y+_`)8e^Azr z!|4VBxqsSPPBkIKm#2qAlwNDHhr4og+i)BE=ZK!p-H`*PD+o!{ZPb%XoCr=7rr&}q z=z(?A;)W#FgNK@D;tp|;5>+`@A70hS1SEupHfpW7bK>XD`jrnRY80Q}*UM2d9H0$u zQM=mlo}DntERc=6>&}#@gG;kY;Ox>+eo13f0z-s7?5H+{^r2p;ax;AuXhwlktPrG( zzKDPkf*!KAO2p6{g{k*tMnj-jr-`G_MR=Nn9!Kr~5I)Wg-}Nr%`$!S)une)mVq-HWOS2;sxY(f{o;a3 zF*xlWX*^LtiWEsFU`pO=XjMQS3iu4An{!AUQ0=W?6#o>xjhqN<3y-!t zvnrZ58@Ik+n?#y|3Uo`piIVTe`oUIb(!d3(L8%}Rvr$8wUdHuP zqO!|mQQ|`#~9k`0xQL#oOT<^IFJk3 zlWuo<#c;7v&ekE43Og_WzyS?;?>X3PJEjh=TUy;^1!LDJ;1f#IsLBCwjxK!P+LY-# z-$%4?)4_K+%wAflX82HlG{$r4v1%2K?_DIFj9qCrB{L-sG`X=tI2n}8-Q^xGUV}}j zk{+++;c8IDW4tZrEQ4(5V$DFIl2V_J(ngoS%b#Rrm*%NiZHm%f`o?3tQ{ppC-G5)1 zQp1e_plXdWiCB7st;zs#AFa^(3k0&4gH7j9p{v^)t0vg}A93&xUR+KHmjjC;ok_5r zEa)SPl4*ATdtH5GuI&u-ZaQKf9G%MIc;22=`K?WyaQdNQ+2wO0H-;{fjv=M23hW0@ znkvH$*oMF9l*FOssA4t~Ujy4K$uHiFL)5gH4+c1!`M{bQ!_b6|~IYsn~)Xbq! zk~XvO_j76r;EAlP!U1>m6l-c{AQO(m04j}od3I_}dRN6KczomK^97^{b1Ica1d~Fg z*-Y|VAFDzIxSWt#Bx^h!TlCNzTL3>w z_BC3me0g1F+KUIJA)wN$QntBiP`#IDsV2}5*W);~4c@f|h+|s;IVmFD#TOu{2#!$O zo*Hix5%2NH^K(qn^%L8h>7jdSIo;>|={(xfx%Y`;^NX>GTbh_83Og|YOvcN#`?^zH z+=ve(vo<3L09V}?SZV2%k!8Iae8M4^QRkt!Ls|EvZys5~CAXW<<~caz)Q^rV zfKQ5A3mKM33CPAnohl><_TXp0M+Yxl@o|!SKuX+Jr*x@|P;f4_5YletgJnibY^kv# zHwFnMKJuUhD%Wusd1doE&o`{GzkN{>I0MA{mj+Qo;HxhW^tE?HUXY8{;40IHhokt$8 zscTI7?S}^^hI8BpFRFe|Iebn-ez|cbu9CD87}F~qH(jsXR8Ay2Ob0uZ6S0+xxt0Dt zH)6m>prDg;0W0Rpbb&| z;Kc~#ZgHfEj`0rJdR-IMS)Ppwzf%}42BWL%Nf{-iq$X5Z@u-icQUqZ}jXQSJYB>I7 zvjDiAU6BGtKa!TA%txwT%f_7WXj(y3Nz7}Z^*4{31dHpVP zO^hVDfShRuf-lgrn80$*@e&v-M28`ya)1WW(Uq5eSjDB(SrwWl|Iq+0fbtj@9dM!3xzVv3k9h6idGK!B7eZtN8TQA+b`9K`4aOdIx%e4zR9FruVgDk! z;U|Q`swW`R3vC&luTAG8a=J&2V8&g_DQ2KZBsl65{R)6D-E{@=7w0c;SR+U-PEkLI zI}bHe+|Xt^t--FX#=6V2JVAQ}$yXv`;j95Mihec&f0mbPdWTy_!yyKk5m_K7%NQZN zBxLeiC3k34BVUQzOAZUQO044{DKL0ck;PFMbPv`1CNFm@#*&xI+DLI5ra&5xx(DgP zOjYACKGK1jY$XV#LiAE7SRj0{x`-)l)%}ZESCI z5C)YtPxi`T=>_u#o8+B?g-rczU<4Ej?agc&{*BLQ3npzhtDV14K*cc_9fW>uuY6(8 z&0a^OL8>YZLzAgmbUJ)je;CeKIYI9%gK5-=LL3k3(|u3M=QErK^IgtVr>Ni|k$_-G zEm~bb{DD+h^t~$rWmr))g}fdK8Q2i2hdXgg`~=_Xx(pFiD9-&Ae(mDi1Mf&gQ!PHJC{W`xP^pe`z;!IrkdO z%=OJ%Bixe>D6gpGS^|oO-R6iTy?yzH^Lv>0M3w}J2>*mHU4^x$007IGraiOo@&R0V zfpg9%L;!QHk_ sZ5Umj1meif$2V(OSV4-^M61p`0Jd=SnMeT%xFNu{RrAD47=& zjjTOeLy|9kSOu4qu@1*fsvq?kLWn-uXS#}h-n5BaUXDh@m|a*w(V>)xm&L-_{TE zA=gJh(;b|-lV}2YqA;L=2b3Tqo?$OOaPMT9jh?J{mFlJ6Q7^nBqk6jFv_ zlu=GG6h$z{Y~@DzGFYtHZcqXl8sRRYV8l>*Ia_~eh0b?B0bV03f~QS%w^vS~vZK|2 zHDc=b$~QiQOD z@ReZUX*pZLI-td<%JJo8I!FZ<=JH-IB|;{Vb_#DUf6oCjB!4;gu_gYS+8o5hVz$YpcaA@X)# zZqo@cGe&X>pHwiX1Hu(PPb-1)>N0+?8r<3I=C&!;Bj(%~L7j=X<&|mj4cV78kVxUX zl;feQMrNGpEvJ@a%-F;ul%)No*KP=gQtX+2Hhr;s8|TU!%6W-b1!Cz;12`lUkMQbp zT9in4F@0|mX>4XrT8M~gXQ|T}+a$)6IMiY|Io${)Q z4rYKWDZE0FGb@6bQchdZCbCJvfQlDLor~sK#i>JrU}OY`N*J7iyD|bnC_>d+t2EUS zRB29?ADt&C$GC=%gr&bBIIOdy_hUf{IVgPcG=_Dt= zrUtZyDX%SRm3vQ^D9gU^CJrdi5y(pSMIax>sj4wHIje1*W+~IlH8=UaYTxN-BQDb$ zt=U|FDE&#r9)HJ6J@sme*vbbP+|9lLcR zh&+zBVx}?XT?JC~71U8t0mDW8gz}FrWl_+Mp#)$6^ASfJwok&ul#TML$?kwsZyg83 zmk%L&tv;=A(xe|n7=g%RdJY^VcWk)~$WKxi1KB~U zLEklIM5zGW1u*X{_z;u#{F3rDAea0NKx%=Q)niGi<*}U9ljd`Cn-~STKpkv6)<0pI z4jjq?t*UqksuchSHVahIFO5+zvzjJiJ_sjLx|WU9%xzN-g3}>GN{vj6Q9Q22 zpC!N*`bAK&gDxu8i1F}@qw=%^ZaIeK8IerfMeZYal3 z;{RU(`l(L0p~56vqLA0{Z3_UvCK|L$ms+MfaxXVT1L4%kDy0Dhu;VQt8eO% z0W&qep9*iFoJ7$^jj&T5tS$wAi`>^qWB1&85Za}DONbGP;P@cd+Zk_MCY|KNcobY< ziXM=E*%U2L-Du^znH<}PJk+DzO;r=K>TMZcsoog;s2_s}>NVJHdlP6Tyr>(5#rR|M z8rS-J#mM{tk5Ev=C%L6W{GQ)yPuz?rdm;xa)Vn>qJ1R7u(hmFmgtALL#r}Gd1tfwt zR2~V{WNWQV$r+PkHR2KCOAIy`o6_b%F@9Y>LkVV43J03&^NBv6sn+DdQbg!At1bYsted0F7-bQ)l^@2E z1i)yFBqqh0U#*3^6jPE9b0HHVqj*X&9i*)jyqi8KA5$l+lpUPUT2*Q5Ar88WWY|$IED?iJ2t57a@iqF`9jK=6Lyn{*bR1pavznh#6rYV zsOb9`1C@`_p!U$KE#Ih4)bEK9==!ds!<1ZY)w7}Ko5YbUg~>Pa4%l<=3&pK@%3cr`{B@ARtB znv816``@vyV3H>F5qnCgwK#m~JDG2n9+flY3}2^yJGNws=@cO|DZJlF7s?dCHvDa^ zB~5UjQem=JOIUt{g<|-1V!>)>sC}145G&`nDN)d8)x2S3YKa^tL?C8y_r0eT5!>h z+tcVqov$;p%(`)${BKO_?b~QRzYDyBeUGu&o*3&Utovk59eW8d7w~dN&dIkj_)$B+ z!XqDr914{u(+4so*aj|_GLfWcLHI?ghz&!3Y64@etPM>yI1HpHrUcjdlf18s6(B~~ zB?*EI3RhLCQ!e+^v&!$6vt~-}M_%2X9%t9S1ziz+xZw>Di-?Q75%zSR&3Md!LQOu! z3ybllDpBD?toV>HG-kR;AHqcPHvBxFDG}UmQavn_lbOb{Dpl2rv`kv3&L^$*Lc7eKKfL1px@958(>R#MQY!y6G6+(jv4Pvy{NZf5`Wd^i{^H+D&_C@zzCM>x?GyVXFb{@OYCLWkxo_evCFhzcr7y&Q6uG zu`25Weu|OH;o}Gff5_1;llI9gMLI{1wL%(&0wgaNaQoZLiq zHp{#bYD;Pznn*1P--HbEdlhTRLEF_{B6H#$KnsFJNdZ-gCIFd&2YdThhEy}A`q3TE zXz877i54W^0HL*?ie@w~NhsbU=xp4?R+?L4(B^SsNWPC;i2|xJl^mBJ6OOPTjBa*t zqou8DjJiT`7DSn!`UQn4uI;-Xrp(N&gOgP4F6#lC zX12y-?6VcHZ|&zu%?UzCNtU?i&?IKknvnF_HPQGMB64UhzKtx2zDenjvxl!B^@gP! zQpTbaSs$P>@@Wl>i!m-8(fLE{2yE^`cN#j+Iux;eD3kZ=Z5G znuG9D7shs}b6@N~jF&6_q8r${QyBShO!X?>qxK}6BOEUpgQEl7b`4$=8lXv~uJ|D| zBg`PQl{3>ci=YtPQmPl%fJCtvd?u2}zZiI!!!h3gJs(F2g318>VE8!M`X9}PgbY2KP@(q>4=YDj$h3_A)A5Pw>|7biSr(=~^{|{;It!JI`3|ARq_$2DB5+&DbAQWM)L&}8cRdF< z_9L=S34K_qJLIaj9p6;HC+Os7<-V{()U=q)71Usp>k-N7D`k6@GQf@?eYVEi6v4VI zD)Ing#V0cQ!YRT+WY~-jWur+#0D0JXbp?F6?A}Dx&{}LzJ?eUrS~&OJmP^2-@lSYPZg0>loiu_oX%$ox8&o@ipyIvqkMe#=qAU!05n6$A zQ#kwuO41*ReEukD@_;DanP|=?g(6)B249K9DKkVq>Y$xA9)!mOmT!zT1(9K!&Z-Mo zV1h^=KMK>nSH5-`9)oT8BWx+vc?Ji>VO6iF7)s3}tE@yI5V*GzHel_y?$P>Bji=Cr zP`KN)!eM_hJ&>aj9#`EZ>?{9R>4^ z7(&oDj8Z3oE4{Z#BP34UqjkDQ2)Ypge2`#*@@2PCYWxyz^(f*NXar)@j0Jp6ex;#! zf^v*WEK)2Cw_=aVx4cCDJ9o@_Y89jFJ;D&}wDU8xsCGA8Hdt)`ORW3C>SO8g5Y${H zX5CeZn(D!J+WkrW8HQX<19VS3WrDq}|BU^%b&a3T{IfsrS#L3uf2#nYXDZ2A*8~U~ z++>d^YM(KeUr|*?^`8*@5NK)Fo@0$mhEqW1C%ez8GcLb4oNkl>fv)m{zBiDh#+4dq zt|o#Kl8px_0jVKO7>fqRYFLuG5_4-a*rQLSzj(p6^>Bf$oP;-e)OyHOT}t_C)+oA+ zV&Z1A@(?Ah3 z2ZSNHDycyKpk>8#66D&84BN_>VK1eJpkr9`uO^guSl@7+4SuKXDc_Ozws{GT?oiUY zKbV^F@+}%TTat-}ofx9}LhsJgH;CiRsfk|PE{k*R zD_9p)=X_W-e+CkXgP}=kJsl`(1K5SFXD9Hil&xx>n0L!q{uM-FtcIQ8x_BGpjwtSg zY<)aG9M2qpEWZGI?otUP@hL-7<=`*C<$}pNs9Z(_iq8IvelI#SAHt5LN(s@nRLiKT zWPJ2w%t1dj)r``O2#Stg)xhHkYxQz+fjI6-FAsR?M&wN=k?Yu@lT(r%1n)9@JiWie zDS=JNj0~*aci)7I%c=h0!5c_m_zg;K#Z}2c#do@hnbWZ|EY!FOgaiE?`ETKFp2Oqr>@mg0C{j<44HHTsj|XbS zWF5b(e7wlxchQmp4Yj!uY{@{$9l+9ofhZlNV<@`^c5o>2Cxo}+>WPV2y> z===v{$$8Kk%aFO(Ta1_AD-!V2@e{G2EfK9uwX7s;ULjt^{!dWcaDr?xx*0d1e6=7s zF`2Nely1fB60*iNVNzQ{aQxOrNd3YtjmL>e`_bu_j<7NQf#M{^PC92rRd3=+Dy8xuKoG{=md{}kKxK*;Z;a(S|-{2qOWV9hI z)@oOIu=7Jl{JLv&fQyWFY%;aa_Ti4E%~TID9WE5>Iru>D2tlp^XWm&ckrJa~i6Nos zBn>m?8B1d^=bFTwlLnB&tX1+{FB&+%fBEp#s&a6NW=DAbrb)O}T ze<1+M`6vHO2$YNcDTMi#es1e(`!9w-v@6YZD6?@cy!_GQVOd_9tD1KyHE(%!R$6gB zwBi85(#AlE;5<5P!a8hX9j;M%!*= zW}=QyTP7|?K}>mhU_X4;r&z=t!m2C>2Q4jLPxC{zVWWYIJxlX0mWDzi=XfS&fEdDg_ z>fn{g>dJLo2mGzz4DK|_-o8fzqLWL}m$W8;{VV?~H{K-q3BJ}!>ecw@?S?81!v^74 ze}uv-p9lt#7SOX$Nfnc8DBf7ZnIykrGf7C#uSW$?>w?UYfs%$0kEM-Z8}jG@hs~%A z23pb{_<5(S=rHFHy+ndmGp2L=qv93rs6?tE0LQ&^6vwJUO){QMVEJ5ERYCHxTa1zv zfgzVrwSLH=#DFL!(4c(Y;e1su5_@&2X|zU0PdWUz(f@NoAI!9PGVLynXmrM?jDcPr zrf3l!>~F2{^cLA*x9EiUQqErNTB3*Eyw0;~coX)3$iM;&Nk#K{1r#Eos3DjhWLJ$+ zzC%>jxSr6_I*-oYY$C=OF(vasp*yNt({*RkKf?P`4F-0C^s#P8aJIM4XG94B!RClF zWt+3k225Fr<~uCvQ*=J3&tQ9THSlkQr)qF^_E=NLkYne#zf!+ENX?HIoOaN!8hV-x zWJd#1;wte8ju@eeq2`NS=w$*zYAY|T+j=LAQ;dDdQ1_C9jihwM8cF>2)-C3$g$p9} zTm^{Dws+M3s-!G#tSiqTZhN-bj(3LFI0}YoEn+>70S0h|&cE;SF&PZu3D+ejE|*;2 zDs%f4VUW1D@3+Lg<}(Ff+Dt4R#h0vzjUZ}hc+ma@0)-hRzw^CCnC&~5Uy_XpZ+CWA zE;8SuTA1*oM3jiFfud%v{+uSe6ymO&f)?N)0{8GPpHdgjmeJU+vd+l~dQFWLvyW25 zyOM@x=)gslsQO_CA4-XK3fsn%-{{p3;0`<$qsdnM)DCAgR7)o6qD>|Zvz1@}b2qSl zwa0m*e48Aw%@ zUo52>nXYPt9!HAlmo>Rz5Gd0;g3hT}2+DGZSAgm`SQ*&GBV7&XN3872^NOqHI)yRi zg$a+(-Xq_8!6y4-!myIFt=P zr&H(FWph)iBcdBQ?tA@FV~!s_$%LKUlB#^Li_il0c%ryUDN2e5@Goi-bi7<7UE)nt zMxZW-^BQPs&=FKvTXOW7V^2R3)v0$@Q-Szj97*SaGi5)yZJvKW|vc2-S_A!9w0}60%?F{p|}oUXt0ig*T~$cJhMi! zIvv_6Gft_60$P{j;Llcg4JGfoEv(^A@6hI2z1j85 zkt;hH1IbMffgKE@+%5#Elomra_tHnd&r^vKxh|PTEMJoviE&4%8I=T9awU{qbwE`J zeIN7^Whj?}!{k=??lTY-g$qVSbFSJHkgA`o7UEOuX3zW5-Qk=` zTvT_f>82@!?cVmmJ0*?t{3J+gIVM$h1)^y3K{>3yW(8|^`ivn^R#Q8Iw!W@)jrcaL zbw;MkFpt}6wBO*V(PuBkA_7KJ)}^XaC`!N=;Wt&pb;QlieLeva+m0IUE|e=1wJfv# zj!naDyu&56Qm9wW&;viDTp~jIOz0Q6PAvtk!*0`2@1dqRh@e+dnBUe@0XQ-)!<7_0 zd?KLX>hfnA2qw%T33->wYQW=#5pzF%EtVXGVN_XxG8uduriX#5oP&JHsw7=!bg0XG z`U*ua21T2Gj}WDNCB@`CWqUcaA6(n2(vqolT2l0LHw99@liby3+73Le5AZpfLlV9Z zv5LF^ykj()jC6==nQPDA+juN(5X&GA!5~|B+L}W0)TQv00!!(d%6xYXr1&AbJK|E8 zs$unO1g=%`*YO%{PiBVpq;2?5q`NI!DtVV8KCpT0IU_x+ny&b5fo$Ya;_f4qC`gdX zC=2LCg0X0l;R0D2at}8ut&dKgsM0Y+pWLR{og~v!RB8J)dL(d;la30y=C*QdPU$Q7 z{wmc#yW5iFV-o+7%LzpsmEhMutP>g*VwXt4ZN6AfGyLM<5TWJP`oZ(~%-aWt(C3

R(MrgCG)EN5 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 tACq$Uzog{N!PSQcSmIlmEp@ypFkzX2ojwow28 literal 0 HcmV?d00001 diff --git a/noxprompter/common.py b/noxprompter/common.py new file mode 100644 index 0000000..1499f21 --- /dev/null +++ b/noxprompter/common.py @@ -0,0 +1,325 @@ +"""Common utilities shared across Nox Prompter nodes.""" + +from __future__ import annotations + +import json +import os +import re +from typing import Any, Dict, List, Tuple + +from .constants import CUSTOM_OPTION, PRESET_ROOT, PROMPT_CONTRADICTION_RULES + + +class PromptFragmentFilter: + """Organize, deduplicate, and lightly curate prompt fragments.""" + + PROFILE_PRESETS = { + "balanced": { + "remove": {"placeholder", "generic"}, + "max_fragments": 18, + "priority": ["subject", "scene", "motion", "camera", "style", "extra"], + "keywords": { + "motion": ["motion", "camera", "move", "tempo"], + "style": ["style", "palette", "effect", "lighting"], + "scene": ["scene", "environment", "setting", "location"], + "subject": ["subject", "portrait", "hero", "character"], + "camera": ["lens", "framing", "focus"], + }, + "insight": "Balanced profile organizes fragments from subject through stylistic flourishes.", + }, + "cinematic": { + "remove": {"generic", "boring", "placeholder"}, + "max_fragments": 20, + "priority": ["subject", "scene", "camera", "motion", "style", "extra"], + "keywords": { + "camera": ["camera", "lens", "framing", "cinematic"], + "style": ["film", "cinematic", "grade", "bokeh"], + "scene": ["scene", "set", "location", "environment"], + "motion": ["move", "motion", "tempo", "beat"], + "subject": ["subject", "hero", "character", "actor"], + }, + "insight": "Cinematic profile spotlights camera language alongside scene context.", + }, + "minimal": { + "remove": {"redundant", "generic", "placeholder"}, + "max_fragments": 12, + "priority": ["subject", "scene", "style", "extra"], + "keywords": { + "style": ["palette", "style", "mood", "tone"], + "scene": ["scene", "environment", "location"], + "subject": ["subject", "portrait", "focus"], + }, + "insight": "Minimal profile trims to the essentials for shorter base prompts.", + }, + "narrative": { + "remove": {"placeholder"}, + "max_fragments": 22, + "priority": ["subject", "scene", "motion", "style", "extra"], + "keywords": { + "motion": ["motion", "beat", "arc", "tempo"], + "scene": ["scene", "setting", "environment", "beat"], + "subject": ["subject", "character", "hero", "protagonist"], + "style": ["tone", "mood", "style", "palette"], + }, + "insight": "Narrative profile preserves motion arcs and tonal descriptors for storytelling prompts.", + }, + } + + def __init__(self, profile: str = "balanced") -> None: + self.profile_name = (profile or "balanced").lower() + self.profile = self.PROFILE_PRESETS.get(self.profile_name, self.PROFILE_PRESETS["balanced"]) + self._removed: List[str] = [] + + def organize(self, fragments: List[str]) -> List[str]: + prioritized: List[Tuple[str, str]] = [] + seen = set() + for fragment in fragments: + cleaned = self._normalize(fragment) + if not cleaned: + continue + lowered = cleaned.lower() + if any(token in lowered for token in self.profile.get("remove", [])): + self._removed.append(cleaned) + continue + if lowered in seen: + continue + seen.add(lowered) + bucket = self._bucket_for(cleaned) + prioritized.append((bucket, cleaned)) + + priority_order = {label: index for index, label in enumerate(self.profile.get("priority", []))} + prioritized.sort(key=lambda item: (priority_order.get(item[0], len(priority_order)), item[1])) + limited = prioritized[: self.profile.get("max_fragments", len(prioritized))] + return [item[1] for item in limited] + + def summarize(self) -> str: + insight = self.profile.get("insight", "Prompt fragments were harmonized.") + if self._removed: + removed = ", ".join(self._removed[:3]) + if len(self._removed) > 3: + removed += ", ..." + return f"{insight} Removed duplicates/noise: {removed}." + return insight + + def _normalize(self, fragment: str) -> str: + cleaned = (fragment or "").strip() + cleaned = re.sub(r"\s+", " ", cleaned) + return cleaned + + def _bucket_for(self, fragment: str) -> str: + lowered = fragment.lower() + keyword_map = self.profile.get("keywords", {}) + for bucket, keywords in keyword_map.items(): + if any(keyword in lowered for keyword in keywords): + return bucket + if lowered.startswith("camera"): + return "camera" + if lowered.startswith("palette"): + return "style" + return "extra" + + +def _sanitize_preset_name(name: str) -> str: + normalized = (name or "").strip() + if not normalized: + return "" + return re.sub(r"[^0-9A-Za-z._-]+", "_", normalized) + + +class PresetManager: + """Simple JSON-based preset storage scoped per node.""" + + def __init__(self, base_dir: str = PRESET_ROOT): + self.base_dir = base_dir + + def _node_dir(self, node_key: str, ensure: bool = False) -> str: + path = os.path.join(self.base_dir, node_key) + if ensure: + os.makedirs(path, exist_ok=True) + return path + + def save(self, node_key: str, preset_name: str, data: Dict[str, Any]) -> str: + sanitized = _sanitize_preset_name(preset_name) + if not sanitized: + raise ValueError("Preset name is required to save.") + directory = self._node_dir(node_key, ensure=True) + file_path = os.path.join(directory, f"{sanitized}.json") + with open(file_path, "w", encoding="utf-8") as handle: + json.dump(data, handle, indent=2, ensure_ascii=False) + return file_path + + def load(self, node_key: str, preset_name: str) -> Dict[str, Any]: + sanitized = _sanitize_preset_name(preset_name) + if not sanitized: + raise ValueError("Preset name is required to load.") + directory = self._node_dir(node_key) + file_path = os.path.join(directory, f"{sanitized}.json") + if not os.path.isfile(file_path): + raise FileNotFoundError(f"Preset '{preset_name}' not found for {node_key}.") + with open(file_path, "r", encoding="utf-8") as handle: + return json.load(handle) + + def list(self, node_key: str) -> List[str]: + directory = self._node_dir(node_key) + if not os.path.isdir(directory): + return [] + names = [] + for entry in os.listdir(directory): + if entry.lower().endswith(".json"): + names.append(os.path.splitext(entry)[0]) + return sorted(names) + + +class PresetMixin: + _preset_manager = PresetManager() + + def _apply_preset_action( + self, + node_key: str, + preset_action: str, + preset_name: str, + data: Dict[str, Any], + ) -> Tuple[Dict[str, Any], str]: + action = (preset_action or "none").strip().lower() + name = (preset_name or "").strip() + status_messages: List[str] = [] + result = dict(data) + + try: + if action == "save": + path = self._preset_manager.save(node_key, name, result) + rel_path = os.path.relpath(path, os.path.dirname(__file__)) + status_messages.append(f"Preset saved: {rel_path}") + elif action == "load": + loaded = self._preset_manager.load(node_key, name) + for key, value in loaded.items(): + if key in result: + result[key] = value + status_messages.append(f"Preset loaded: {name}") + elif action == "list": + available = self._preset_manager.list(node_key) + if available: + status_messages.append("Available presets: " + ", ".join(available)) + else: + status_messages.append("No presets saved yet.") + except FileNotFoundError as exc: + status_messages.append(str(exc)) + except json.JSONDecodeError as exc: + status_messages.append(f"Preset JSON invalid: {exc}") + except ValueError as exc: + status_messages.append(str(exc)) + except OSError as exc: # pragma: no cover + status_messages.append(f"Preset action failed: {exc}") + + status = " | ".join(status_messages) + return result, status + + +def _resolve_action_option( + selection: str, + custom_prompt: str, + table: Dict[str, Dict[str, str]], +) -> Tuple[str, str]: + if selection == CUSTOM_OPTION: + value = (custom_prompt or "").strip() + return value, "" + record = table.get(selection) + if record: + return record.get("prompt", ""), record.get("summary", "") + fallback = (selection or "").strip() + return fallback, "" + + +def _resolve_option(selection: str, table: Dict[str, Dict[str, str]]) -> Tuple[str, str]: + record = table.get(selection) + if record: + return record.get("prompt", ""), record.get("notes", record.get("summary", "")) + fallback = (selection or "").strip() + return fallback, "" + + +def _split_tokens(value: str) -> List[str]: + if not value: + return [] + tokens: List[str] = [] + for chunk in value.replace("\r", "\n").split("\n"): + parts = [token.strip() for token in chunk.split(",") if token.strip()] + tokens.extend(parts) + return tokens + + +def _resolve_with_custom( + selection: str, + custom_value: str, + table: Dict[str, Dict[str, str]], +) -> Tuple[str, str]: + if selection == CUSTOM_OPTION: + value = (custom_value or "").strip() + return value, "Custom selection provided." + prompt, notes = _resolve_option(selection, table) + return prompt, notes + + +def _format_notes(label: str, notes: str, fallback: str) -> str: + content = (notes or fallback or "").strip() + if not content: + return "" + return f"{label}: {content}" if label else content + + +def detect_prompt_contradictions(chunks: List[str]) -> List[str]: + """Identify conflicting descriptors across provided prompt fragments.""" + + if not chunks: + return [] + + lowered_sources: List[str] = [] + for chunk in chunks: + cleaned = (chunk or "").strip() + if not cleaned: + continue + lowered_sources.append(cleaned.lower()) + + warnings: List[str] = [] + + for rule in PROMPT_CONTRADICTION_RULES: + groups = rule.get("groups", {}) + detected: Dict[str, List[str]] = {} + for group_name, keywords in groups.items(): + for keyword in keywords: + needle = keyword.lower().strip() + if not needle: + continue + for source in lowered_sources: + if needle in source: + detected.setdefault(group_name, []) + if keyword not in detected[group_name]: + detected[group_name].append(keyword) + break + if len(detected) > 1: + fragments = [] + for group_name, matches in detected.items(): + sample = ", ".join(matches[:3]) + if len(matches) > 3: + sample += ", ..." + fragments.append(f"{group_name}: {sample}") + message = f"{rule.get('label', 'Consistency')}: " + " | ".join(fragments) + hint = rule.get("hint") + if hint: + message += f" ({hint})" + warnings.append(message) + + return warnings + + +__all__ = [ + "PromptFragmentFilter", + "PresetManager", + "PresetMixin", + "_resolve_action_option", + "_resolve_option", + "_resolve_with_custom", + "_split_tokens", + "_format_notes", + "detect_prompt_contradictions", +] diff --git a/noxprompter/constants.py b/noxprompter/constants.py new file mode 100644 index 0000000..3cf3c10 --- /dev/null +++ b/noxprompter/constants.py @@ -0,0 +1,3644 @@ +"""Shared constant dictionaries for Nox Prompter nodes.""" + +from __future__ import annotations + +import os + +PRESET_ROOT = os.path.join(os.path.dirname(os.path.dirname(__file__)), "presets") + +CUSTOM_OPTION = "Custom Input" + + +FORMULA_CONFIGS = { + "Basic Formula": { + "description": "Subject + Scene + Motion — perfect for ideation and quick inspiration.", + "structure": ["subject", "scene", "motion"], + }, + "Advanced Formula": { + "description": "Subject (desc) + Scene (desc) + Motion (desc) + Aesthetic controls + Stylization.", + "structure": ["subject", "scene", "motion", "aesthetic", "stylization"], + }, + "Story Spotlight": { + "description": "Focus subject + environmental beat + camera language + emotional tone summary.", + "structure": ["subject", "scene", "motion", "emotion", "camera"], + }, + "Trailer Teaser": { + "description": "Cold open hook + subject descriptor + kinetic beat + stylization tag cloud.", + "structure": ["scene", "subject", "motion", "stylization"], + }, +} +LIGHT_SOURCE_OPTIONS = { + "None": {"prompt": "", "summary": "No dedicated light source emphasis."}, + "Daylight": {"prompt": "daylight, sunlit ambience, warm highlights, natural light", "summary": "Sunlit scene with warm, natural illumination."}, + "Artificial Light": {"prompt": "artificial lighting, tungsten glow, controlled practicals", "summary": "Moody artificial lighting with cinematic practical sources."}, + "Moonlight": {"prompt": "moonlight, cool lunar glow, silver rim lighting", "summary": "Cool moonlit ambience with gentle highlights."}, + "Practical Light": {"prompt": "practical lighting, lamps, diegetic light sources", "summary": "Scene lit by visible practical fixtures."}, + "Firelight": {"prompt": "firelight, ember glow, flickering warm highlights", "summary": "Flickering firelight with warm dancing shadows."}, + "Fluorescent Light": {"prompt": "fluorescent lighting, clinical glow, cool overhead wash", "summary": "Flat fluorescent illumination with slight green tint."}, + "Overcast Light": {"prompt": "overcast sky, diffuse natural light, soft ambient shadows", "summary": "Soft overcast diffusion with muted contrast."}, + "Mixed Light": {"prompt": "mixed lighting sources, color contrast, layered temperatures", "summary": "Mixed lighting with contrasting color temperatures."}, + "Sunlight": {"prompt": "direct sunlight, high contrast shadows, golden highlights", "summary": "Direct sun with crisp highlights and defined shadows."}, + "Lantern Glow": {"prompt": "lantern-lit ambience, swaying warm pools, intimate highlights", "summary": "Lantern pools of light creating intimate pockets of warmth."}, + "Bioluminescence": {"prompt": "bioluminescent glow, organic shimmer, underwater luminosity", "summary": "Organic bioluminescence casting ethereal aquatic light."}, + "Holographic Panels": {"prompt": "holographic panel lighting, refracted neon, cascading spectrum wash", "summary": "Glitchy holographic panels bathing the scene in shimmering gradients."}, + "Torchline": {"prompt": "torch-lit procession, wavering flame line, ember flicker", "summary": "Row of torches casting rhythmic pulses of warm light."}, + "Neon Billboard": {"prompt": "neon billboard glow, saturated signage wash, flickering letters", "summary": "Vibrant billboard light bathing the scene in rhythmic neon pulses."}, + "Campfire Circle": {"prompt": "campfire circle, ember sparks, storytelling glow", "summary": "Intimate fire pit lighting painting faces with warm flicker."}, + "Stage Spotlight": {"prompt": "stage spotlight beam, hard edge falloff, performer isolation", "summary": "Focused theatrical spotlight carving subject from darkness."}, + "Lightning Storm": {"prompt": "lightning storm bursts, strobing sky flashes, electric contrast", "summary": "Unpredictable lightning bolts freezing action in high contrast."}, + "Volcanic Flow": {"prompt": "volcanic magma glow, molten spill, infernal ambience", "summary": "Seething lava light casting ominous crimson across surfaces."}, + "Candlelit Vigil": {"prompt": "clustered candlelight, fluttering wicks, hushed warmth", "summary": "Sea of candles producing soft, reverent illumination."}, + "Aurora Curtain": {"prompt": "aurora curtain glow, shifting greens and violets, sky drape", "summary": "Northern lights bathing subjects in dreamy celestial hues."}, + "Starlight": {"prompt": "pure starlight, silver speckle, expansive night shimmer", "summary": "Subtle starlit exposure lending gentle highlights to silhouettes."}, + "Underwater Caustics": {"prompt": "underwater caustic light, dancing wave reflections, aquatic shimmer", "summary": "Liquid light patterns rippling across submerged subjects."}, + "Snow Reflection": {"prompt": "snowfield bounce light, diffuse white glow, winter reflection", "summary": "Snow bounce filling shadows with cool diffuse brightness."}, + "Forge Hearth": {"prompt": "forge hearth blaze, iron sparks, molten core", "summary": "Blacksmith forge light casting muscular orange highlights."}, + "Alchemical Vat": {"prompt": "alchemical vat glow, bubbling neon liquids, laboratory luminescence", "summary": "Arcane vats emitting surreal colored glows through mist."}, + "Cyber Street Lamps": {"prompt": "cyberpunk street lamps, holographic ads, saturated sidewalk beams", "summary": "Urban street lamps layering colored pools across rain-slick pavement."}, + "Gaslight Row": {"prompt": "gaslight row glow, amber halos, Victorian haze", "summary": "Historical gas lamps lining the street with soft amber halos."}, + "Lantern Parade": {"prompt": "festival lantern parade, drifting paper lights, communal glow", "summary": "Floating lanterns surrounding characters in celebratory radiance."}, + "Bioreactor": {"prompt": "bioreactor glow, pulsing biofluorescence, vat illumination", "summary": "Contained bioluminescent reactors bathing spaces in organic light."}, + "Laser Grid": {"prompt": "laser grid, intersecting beams, chromatic lattice", "summary": "Precision laser lattice generating futuristic lighting geometry."}, + "Holographic Dome": {"prompt": "holographic dome canopy, rotating gradients, virtual sky", "summary": "Immersive holographic ceiling diffusing programmable spectrum washes."}, + "Meteor Shower": {"prompt": "meteor shower streaks, incandescent trails, cosmic sparks", "summary": "Sky filled with glowing meteors streaking luminous trails downward."}, + "Flare Burst": {"prompt": "signal flare burst, smoke-tinted glow, emergency wash", "summary": "Emergency flare casting dramatic red light through haze."}, + "Oil Lantern": {"prompt": "oil lantern glow, soot-streaked glass, steady warm pool", "summary": "Portable lantern providing steadfast warm illumination."}, + "Arc Welder": {"prompt": "arc welder flash, intense blue-white, showering sparks", "summary": "Industrial arc welding light freezing motion with harsh brilliance."}, + "Citylight Spill": {"prompt": "distant citylight spill, skyline glow, sodium haze", "summary": "Ambient glow from a distant skyline softly filling the frame."}, + "Crystal Chandelier": {"prompt": "crystal chandelier glitter, prismatic sparkle, ballroom glow", "summary": "Opulent chandelier refracting light into glistening shards."}, + "Bonfire Blaze": {"prompt": "towering bonfire blaze, roaring embers, sparks aloft", "summary": "Massive bonfire engulfing the environment in roaring orange light."}, + "Laneway Neon": {"prompt": "laneway neon strips, saturated signage, puddle reflections", "summary": "Tight alley bathed in stacked neon signage reflections."}, + "Bio-Lanterns": {"prompt": "bio-lantern organisms, glowing fauna, natural luminance", "summary": "Living lantern creatures casting organic multi-toned light."}, + "Ice Cavern Crystals": {"prompt": "ice cavern crystals, refracted glimmer, frozen glow", "summary": "Translucent ice crystals bending light through cavern walls."}, + "Plasma Torches": {"prompt": "plasma torch banks, blue-white arcs, industrial intensity", "summary": "Rows of plasma torches bathing subjects in blistering brilliance."}, + "Sunrise Window": {"prompt": "sunrise window beam, golden shafts, dust motes", "summary": "Early sun cascading through windows with painterly rays."}, + "Projector Spill": {"prompt": "film projector spill, dancing shadows, analog flicker", "summary": "Projector light spilling off screen with nostalgic flicker."}, + "Glimmering Reef": {"prompt": "coral reef glow, phosphorescent marine life, tropical illumination", "summary": "Undersea reef emitting soft gradients from luminous coral."}, + "Supernova Remnant": {"prompt": "supernova remnant glow, cosmic dust, radiant nebula", "summary": "Interstellar residue enveloping scene in vibrant nebular light."}, + "Iridescent Canopy": {"prompt": "iridescent canopy panels, shifting oil-slick colors", "summary": "Architectural canopy reflecting iridescent hues onto subjects."}, +} + + +LIGHT_QUALITY_OPTIONS = { + "None": {"prompt": "", "summary": "No additional light quality emphasis."}, + "Soft Light": {"prompt": "soft light, diffused shadows, gentle transition", "summary": "Soft, diffused lighting with subtle shadow falloff."}, + "Hard Light": {"prompt": "hard light, sharp shadows, chiseled contrast", "summary": "Hard-edged lighting with defined contrast."}, + "Top Light": {"prompt": "top lighting, overhead light source, dramatic downward shadows", "summary": "Top-down lighting with sculpted shadows."}, + "Side Light": {"prompt": "side lighting, directional key light, lateral highlights", "summary": "Side lighting with strong modeling."}, + "Backlight": {"prompt": "backlighting, halo rim, silhouette highlights", "summary": "Backlighting that separates subject from background."}, + "Bottom Light": {"prompt": "uplighting, underlight, theatrical glow", "summary": "Bottom lighting creating dramatic, eerie ambience."}, + "Rim Light": {"prompt": "rim lighting, edge highlight, subject outline glow", "summary": "Highlighted subject edges for separation."}, + "Silhouette": {"prompt": "silhouette lighting, subject in shadow, strong backlight", "summary": "High-contrast silhouette composition."}, + "Low Contrast": {"prompt": "low contrast lighting, gentle tonal range, subtle shadows", "summary": "Low-contrast lighting for a soft palette."}, + "High Contrast": {"prompt": "high contrast lighting, stark shadows, deep blacks", "summary": "High-contrast lighting with dramatic tonality."}, + "Dappled Light": {"prompt": "dappled light, patterned highlights, dancing shade", "summary": "Patterned illumination filtered through foliage or lattice."}, + "Volumetric Rays": {"prompt": "volumetric god rays, particulate beams, dramatic shafts", "summary": "Visible shafts of light carving through atmosphere."}, + "Specular Punch": {"prompt": "specular punch lighting, crisp highlights, lacquered sheen", "summary": "High-specular highlights carving glossy definition across surfaces."}, + "Veiled Diffusion": {"prompt": "veiled diffusion, gauzy wrap, mist-filtered glow", "summary": "Soft veil of diffused light for hazy, dreamlike coverage."}, + "Specular Mosaic": {"prompt": "specular mosaic, shattered highlights, jeweled reflections", "summary": "Fragmented highlights breaking across faceted surfaces."}, + "Feathered Rim": {"prompt": "feathered rim lighting, gradual halo, airy separation", "summary": "Gentle rim light with feathery falloff outlining silhouettes."}, + "Split Tone": {"prompt": "split-toned lighting, opposing color temperatures", "summary": "Two-tone lighting dividing the subject into contrasting hues."}, + "Motivated Practical": {"prompt": "motivated practicals, believable diegetic spill", "summary": "Stylized yet grounded lighting justified by on-set fixtures."}, + "Softbox Wrap": {"prompt": "giant softbox wrap, wrapping highlights, creamy falloff", "summary": "Massive soft source hugging the subject with gentle gradient."}, + "Kicker Highlights": {"prompt": "kicker highlights, sharp accent lines, sculpted edges", "summary": "Edge kickers introducing crisp accent streaks."}, + "High Key": {"prompt": "high-key lighting, airy brightness, minimal shadow", "summary": "Bright, buoyant scheme minimizing contrast for cheerful tone."}, + "Low Key": {"prompt": "low-key lighting, deep blacks, selective highlights", "summary": "Moody low-key envelope with pockets of illumination."}, + "Chromatic Spill": {"prompt": "chromatic spill, rainbow edges, spectral fringe", "summary": "Prismatic spill generating colorful haloing around forms."}, + "Firefly Scatter": {"prompt": "firefly scatter, pinpoint twinkles, drifting motes", "summary": "Micro light points swirling through the air like fireflies."}, + "Soft Focus Glow": {"prompt": "soft focus glow, diffusion bloom, romantic haze", "summary": "Dreamy glow softening detail for romantic imagery."}, + "Mirror Bounce": {"prompt": "mirror bounce lighting, crisp reflection, mirrored key", "summary": "Mirrored bounce producing sharp reflected highlights."}, + "Patterned Gobos": {"prompt": "patterned gobo wash, carved shadows, cut shapes", "summary": "Gobo patterns casting intricate silhouettes across surfaces."}, + "Industrial Strip": {"prompt": "industrial strip lighting, linear highlights, warehouse tone", "summary": "Strip fixtures painting parallel lines of illumination."}, + "Dappled Canopy": {"prompt": "dappled canopy light, leafy pattern, natural stipple", "summary": "Filtered sunlight flickering through foliage onto subjects."}, +} + + +TIME_OF_DAY_OPTIONS = { + "None": {"prompt": "", "summary": "No specific time-of-day emphasis."}, + "Daytime": {"prompt": "daytime setting, bright ambience", "summary": "Bright daytime environment."}, + "Nighttime": {"prompt": "nighttime scene, deep shadows, artificial accents", "summary": "Nighttime backdrop with dramatic contrast."}, + "Dusk": {"prompt": "dusk, golden hour, lingering warmth", "summary": "Golden hour glow with soft warmth."}, + "Sunset": {"prompt": "sunset sky, saturated horizon, long shadows", "summary": "Sunset palette with vivid horizon."}, + "Dawn": {"prompt": "pre-dawn hues, gentle blue hour, quiet ambience", "summary": "Pre-dawn tones with soft gradients."}, + "Sunrise": {"prompt": "sunrise light, fresh warmth, emerging glow", "summary": "Sunrise optimism with warm highlights."}, + "Midnight": {"prompt": "midnight sky, star-pricked darkness, tranquil hush", "summary": "Deep midnight calm with subtle celestial accents."}, + "Blue Hour": {"prompt": "blue hour tones, twilight glow, cobalt horizon", "summary": "Cool twilight wash bridging night and day."}, + "Golden Hour": {"prompt": "golden hour light, honeyed tones, elongated shadows", "summary": "Golden hour baths everything in soft amber light."}, + "Storm Night": {"prompt": "stormy midnight, lightning flashes, rain-lashed darkness", "summary": "Charged midnight storm with intermittent lightning wash."}, + "Pre-Dawn Blue": {"prompt": "pre-dawn blue hour, hushed gradients, sleeping city", "summary": "Quiet pre-dawn stillness awash in cool blues."}, + "High Noon": {"prompt": "high noon sun, overhead blaze, sharp shadows", "summary": "Unforgiving midday sun casting stark overhead shadows."}, + "Twilight": {"prompt": "twilight blend, fading warmth, encroaching cool", "summary": "Transitional twilight mixing warm and cool tones."}, + "Solar Eclipse": {"prompt": "solar eclipse dimming, surreal twilight, corona rim", "summary": "Eerie eclipse atmosphere with dramatic ambient shift."}, + "Lunar Eclipse": {"prompt": "lunar eclipse glow, copper moon, penumbral hues", "summary": "Earth-shadowed moon painting night with copper undertones."}, + "Rainy Afternoon": {"prompt": "rainy afternoon light, overcast drizzle, reflective streets", "summary": "Soft grey daylight with shimmering rain reflections."}, + "Winter Dawn": {"prompt": "winter dawn, frosted breath, pale sun", "summary": "Chilly dawn with subdued pastel sunlight."}, + "Summer Midnight": {"prompt": "summer midnight twilight, lingering glow, horizon gradients", "summary": "High-latitude midnight where sun barely dips, leaving glow."}, + "Desert High Sun": {"prompt": "desert high sun, heat shimmer, bleached highlights", "summary": "Scorching desert midday flattening contrast under glare."}, + "Golden Dusk": {"prompt": "deep golden dusk, saturated clouds, elongated shadows", "summary": "Late dusk deepening golds and oranges before nightfall."}, + "Aurora Midnight": {"prompt": "aurora midnight, dancing lights, polar night", "summary": "Polar midnight illuminated by auroral ribbons."}, + "Festival Evening": {"prompt": "festival evening glow, lanterns rising, celebratory warmth", "summary": "Community evening with layered practicals and warm ambiance."}, + "Stormy Noon": {"prompt": "stormy noon, bruise-cloud canopy, diffused fury", "summary": "Midday storm muting sun into dramatic diffuse glow."}, +} + + +SHOT_SIZE_OPTIONS = { + "None": {"prompt": "", "summary": "No shot size emphasis."}, + "Close-up": {"prompt": "close-up shot, intimate framing, facial detail", "summary": "Close-up framing for emotional focus."}, + "Medium Close-up": {"prompt": "medium close-up composition, chest-up framing", "summary": "Medium close-up showcasing shoulders and expression."}, + "Medium Shot": {"prompt": "medium shot, balanced subject framing", "summary": "Medium shot capturing subject and context."}, + "Medium Long Shot": {"prompt": "medium long shot, knees-up framing", "summary": "Medium long shot blending subject and space."}, + "Long Shot": {"prompt": "long shot, full body, environmental scale", "summary": "Long shot emphasizing subject within environment."}, + "Full Shot": {"prompt": "full shot, entire figure, spatial context", "summary": "Full body framing with contextual surroundings."}, + "Wide Angle": {"prompt": "wide angle framing, expansive field of view", "summary": "Wide angle perspective with environmental breadth."}, + "Extreme Close-up": {"prompt": "extreme close-up, micro expression, intimate texture", "summary": "Intense framing that magnifies subtle detail."}, + "Bird's Eye Wide": {"prompt": "bird's-eye master shot, sweeping overhead, geographic context", "summary": "High vantage shot revealing spatial relationships."}, + "Extreme Wide": {"prompt": "extreme wide vista, monumental scale, tiny subject silhouette", "summary": "Vast scale framing dwarfs the subject within the environment."}, + "Macro Detail": {"prompt": "macro detail insert, texture emphasis, ultra close focus", "summary": "Macro detail shot isolating tactile surfaces and micro storytelling."}, + "Extreme Macro": {"prompt": "extreme macro, microscopic textures, abstract detail", "summary": "Ultra-magnified shot revealing microscopic textures."}, + "Medium Group": {"prompt": "medium group shot, waist-up ensemble, shared frame", "summary": "Medium framing accommodating multiple subjects in balance."}, + "Environmental Portrait": {"prompt": "environmental portrait, subject within meaningful setting", "summary": "Portrait capturing subject alongside context-rich environment."}, + "Architectural Wide": {"prompt": "architectural wide shot, towering structures, spatial clarity", "summary": "Wide framing highlighting grand architectural scale."}, + "Aerial Panorama": {"prompt": "aerial panorama, sweeping vista, expansive scope", "summary": "High-altitude panorama showcasing sprawling landscapes."}, + "Insert Detail": {"prompt": "insert detail shot, key prop close-up", "summary": "Tight insert isolating a crucial storytelling detail."}, + "Hero Medium": {"prompt": "hero medium shot, waist-up heroic framing", "summary": "Classic hero framing balancing intimacy and stature."}, + "Crowd Master": {"prompt": "crowd master shot, large-scale ensemble, layered depth", "summary": "Wide master capturing energetic crowd dynamics."}, + "Tableau": {"prompt": "tableau shot, staged composition, painterly arrangement", "summary": "Static tableau emphasizing composition and collective pose."}, +} + + +COMPOSITION_OPTIONS = { + "None": {"prompt": "", "summary": "No composition emphasis."}, + "Centered": {"prompt": "centered composition, balanced subject placement", "summary": "Subject centered for symmetry."}, + "Balanced": {"prompt": "balanced framing, visual equilibrium", "summary": "Balanced composition with even weight."}, + "Right-weighted": {"prompt": "right-weighted composition, dynamic off-center balance", "summary": "Subject anchored to the right for motion."}, + "Left-weighted": {"prompt": "left-weighted composition, directional focus", "summary": "Subject anchored to the left for leading motion."}, + "Symmetrical": {"prompt": "symmetrical framing, mirrored balance", "summary": "Symmetrical layout with mirrored elements."}, + "Short-siding": {"prompt": "short-siding composition, open space behind subject", "summary": "Short-siding for tension and anticipation."}, + "Leading Lines": {"prompt": "leading lines composition, guiding geometry, directional flow", "summary": "Structural lines that guide the eye to the subject."}, + "Rule of Thirds": {"prompt": "rule of thirds framing, dynamic balance, offset subject", "summary": "Offset placement using thirds for energetic balance."}, + "Radial Burst": {"prompt": "radial burst composition, converging energy, center highlight", "summary": "Radiating lines pull the eye to a powerful center focal point."}, + "Negative Space": {"prompt": "negative space framing, minimal subject occupancy, breathing room", "summary": "Purposeful negative space emphasizing subject isolation."}, + "Golden Spiral": {"prompt": "golden spiral composition, dynamic curve, natural flow", "summary": "Golden spiral guiding viewer eye along elegant arc."}, + "Framed Within": {"prompt": "framed-within composition, doorway framing, nested focus", "summary": "Subject framed by architectural or natural elements for depth."}, + "Leading Curve": {"prompt": "leading curve composition, sweeping arc, graceful guide", "summary": "Curvilinear leading line drawing attention across frame."}, + "Foreground Obscure": {"prompt": "foreground obscure, partial blockage, voyeur framing", "summary": "Foreground elements partially obscuring action for intrigue."}, + "Layered Depth": {"prompt": "layered depth composition, foreground midground background", "summary": "Stacked planes creating rich depth cues."}, + "Diagonal Drive": {"prompt": "diagonal drive composition, kinetic tilt, motion emphasis", "summary": "Dynamic diagonals introducing energy and momentum."}, + "Central Void": {"prompt": "central void composition, subject encircling emptiness", "summary": "Elements arranged around intentional empty core."}, + "Asymmetrical Balance": {"prompt": "asymmetrical balance, weighted contrast, tension harmony", "summary": "Unequal elements balancing through visual weight."}, + "Mirror Symmetry": {"prompt": "mirror symmetry, reflective imagery, cloned halves", "summary": "Mirrored halves creating duality and repetition."}, +} + + +LENS_FOCAL_OPTIONS = { + "None": {"prompt": "", "summary": "No focal length emphasis."}, + "Medium": {"prompt": "medium focal length, natural perspective", "summary": "Medium focal length for balanced view."}, + "Wide Angle": {"prompt": "wide angle lens, expanded perspective, mild distortion", "summary": "Wide angle depth with sweeping environment."}, + "Long": {"prompt": "long lens, compressed background, shallow depth", "summary": "Long lens compression with tight focus."}, + "Telephoto": {"prompt": "telephoto lens, extreme compression, isolated subject", "summary": "Telephoto compression isolating subject."}, + "Fisheye": {"prompt": "fisheye lens, dramatic curvature, immersive distortion", "summary": "Fisheye distortion for stylized wrap."}, + "Ultra Wide": {"prompt": "ultra wide focal length, expansive sweep, dramatic scale", "summary": "Ultra wide view exaggerating space and scope."}, + "Macro": {"prompt": "macro lens, magnified detail, shallow depth of field", "summary": "Macro magnification revealing intricate textures."}, + "Tilt Shift": {"prompt": "tilt-shift focal plane, selective focus, miniature illusion", "summary": "Tilt-shift lens controlling focus plane for miniature effect."}, + "Anamorphic": {"prompt": "anamorphic lens, cinematic squeeze, oval bokeh", "summary": "Anamorphic lensing for widescreen flare and oval bokeh."}, + "Super Telephoto": {"prompt": "super-telephoto lens, extreme reach, compressed horizon", "summary": "Ultra-long focal length flattening space to dramatic effect."}, + "Portrait Prime": {"prompt": "portrait prime lens, creamy bokeh, flattering compression", "summary": "Classic portrait prime emphasizing subject isolation and bokeh."}, + "Vintage Glass": {"prompt": "vintage lens, subtle aberrations, nostalgic softness", "summary": "Retro lens character introducing romantic imperfection."}, + "Soft Focus Lens": {"prompt": "soft focus lens, haloed highlights, blooming edges", "summary": "Specialty lens diffusing focus for dreamy imagery."}, + "Ultra Macro": {"prompt": "ultra macro lens, life-size magnification, shallow plane", "summary": "Macro lens capturing extreme close detail at 1:1 magnification."}, + "Pinhole": {"prompt": "pinhole optics, infinite focus, vignetted edges", "summary": "Pinhole lens aesthetic offering deep focus and organic vignette."}, + "Shift Lens": {"prompt": "shift lens, perspective control, architectural correction", "summary": "Perspective-control lens keeping verticals true."}, + "Catadioptric": {"prompt": "catadioptric mirror lens, donut bokeh, compact telephoto", "summary": "Mirror telephoto lens creating signature donut-shaped bokeh."}, +} + + +LENS_ANGLE_OPTIONS = { + "None": {"prompt": "", "summary": "No camera angle emphasis."}, + "Over-the-shoulder": {"prompt": "over-the-shoulder view, subjective framing", "summary": "Over-the-shoulder perspective for dialogue."}, + "High Angle": {"prompt": "high angle shot, elevated vantage", "summary": "High angle perspective for vulnerability."}, + "Low Angle": {"prompt": "low angle shot, heroic scale", "summary": "Low angle for power and grandeur."}, + "Tilted Angle": {"prompt": "dutch angle, tilted horizon, dynamic tension", "summary": "Tilted angle introducing unease."}, + "Aerial Shot": {"prompt": "aerial perspective, sweeping overhead view", "summary": "Aerial viewpoint capturing scale."}, + "Top-down View": {"prompt": "top-down shot, orthographic view, planar composition", "summary": "Top-down vantage for graphic patterns."}, + "Ground Level": {"prompt": "ground-level angle, horizon skimming, towering subject", "summary": "Camera placed at ground height for towering emphasis."}, + "Point-of-View": {"prompt": "first-person POV angle, subjective framing, immersive gaze", "summary": "Immersive first-person angle echoing the character's view."}, + "Drone Chase": {"prompt": "drone chase angle, high-speed pursuit, aerial tracking", "summary": "Fast-moving drone perspective sweeping through the action."}, + "Shoulder Cam": {"prompt": "over-shoulder handheld cam, intimate proximity, reactive framing", "summary": "Shoulder-mounted camera adding immersive immediacy."}, + "Dutch Low": {"prompt": "low dutch angle, canted horizon near ground", "summary": "Low-positioned dutch angle heightening unease and power."}, + "Head-On": {"prompt": "head-on angle, subject facing camera, direct confrontation", "summary": "Direct frontal angle confronting viewer with subject."}, + "Three-Quarter": {"prompt": "three-quarter angle, slight turn, dimensionality", "summary": "Classic three-quarter angle revealing facial depth."}, + "Worm's Eye": {"prompt": "worm's-eye view, extreme upward gaze, towering scale", "summary": "Extreme low angle exaggerating vertical dominance."}, + "Ceiling Mount": {"prompt": "ceiling mounted camera, surveillance vantage", "summary": "High fixed vantage delivering surveillance aesthetic."}, + "Foot-Level": {"prompt": "foot-level angle, grounded perspective, kinetic motion", "summary": "Camera placed at ground level to emphasize motion and speed."}, + "Mirror POV": {"prompt": "mirror point-of-view, reflective perspective, dual framing", "summary": "Angle captured via mirror reflection for dual-layer storytelling."}, + "Drone Descent": {"prompt": "drone descent angle, rapidly dropping aerial", "summary": "Descending aerial angle transitioning from high to mid altitude."}, + "Shoulder Reverse": {"prompt": "reverse over-shoulder, conversational counter", "summary": "Counter-angle OTS capturing opposing subject in dialogue."}, +} + + +LENS_TYPE_OPTIONS = { + "None": {"prompt": "", "summary": "No shot type emphasis."}, + "Single Shot": {"prompt": "single shot focus, solo subject", "summary": "Single shot isolating the hero."}, + "Two Shot": {"prompt": "two shot framing, dual subjects", "summary": "Two shot balancing interactions."}, + "Three Shot": {"prompt": "three shot composition, trio dynamic", "summary": "Three shot capturing group interplay."}, + "Group Shot": {"prompt": "group shot, ensemble framing", "summary": "Group shot highlighting ensemble relationships."}, + "Establishing Shot": {"prompt": "establishing shot, wide context, scene-setting", "summary": "Establishing shot grounding the location."}, + "Insert Shot": {"prompt": "insert shot detail, object emphasis, storytelling prop", "summary": "Tight detail shot spotlighting a crucial element."}, + "Crowd Panorama": {"prompt": "crowd panorama, sweeping ensemble, layered depth", "summary": "Expansive ensemble shot capturing scale and energy."}, + "Overhead Master": {"prompt": "overhead master shot, architectural layout, orchestrated movement", "summary": "High overhead master framing to chart spatial choreography."}, + "Detail Montage": {"prompt": "detail montage series, rapid inserts, sensory collage", "summary": "Montage of detail shots creating rhythmic storytelling."}, + "Reaction Shot": {"prompt": "reaction shot, emotional response focus", "summary": "Shot capturing character reaction for emotional punctuation."}, + "POV Shot": {"prompt": "point-of-view shot, first-person perspective", "summary": "Subjective POV immersing viewer in character perspective."}, + "Montage Sequence": {"prompt": "montage sequence, time compression, rhythmic cuts", "summary": "Collection of shots compressing time through rhythmic editing."}, + "Tracking Shot": {"prompt": "tracking shot, continuous movement, follow focus", "summary": "Shot moving alongside subject to retain consistent framing."}, + "Dialogue Two Shot": {"prompt": "dialogue two shot, conversational framing", "summary": "Balanced two shot highlighting dialogue between characters."}, + "Extreme Long": {"prompt": "extreme long establishing, distant subjects, landscape emphasis", "summary": "Very wide framing situating characters within vast landscape."}, + "Hero Shot": {"prompt": "hero shot, triumphant pose, low angle emphasis", "summary": "Dramatic hero framing underscoring power and victory."}, + "Insert POV": {"prompt": "insert POV shot, detail through character eyes", "summary": "POV insert focusing on critical detail as character sees it."}, + "Crowd Reaction": {"prompt": "crowd reaction shot, ensemble response", "summary": "Group reaction capturing collective emotional beat."}, +} + + +COLOR_TONE_OPTIONS = { + "None": {"prompt": "", "summary": "No color tone emphasis."}, + "Warm Tone": {"prompt": "warm color palette, golden hues, inviting saturation", "summary": "Warm palette with golden highlights."}, + "Cool Tone": {"prompt": "cool color palette, blue undertones, serene mood", "summary": "Cool palette with calming tones."}, + "High Saturation": {"prompt": "high saturation, vivid colors, bold intensity", "summary": "Highly saturated color treatment."}, + "Low Saturation": {"prompt": "low saturation, muted palette, cinematic restraint", "summary": "Muted palette with cinematic restraint."}, + "Pastel Tone": {"prompt": "pastel color palette, powdery hues, gentle saturation", "summary": "Soft pastel wash for dreamy, delicate moods."}, + "Neo Noir": {"prompt": "neo-noir palette, electric highlights, deep shadows", "summary": "High-contrast neon noir treatment with moody blacks."}, + "Bioluminescent": {"prompt": "bioluminescent palette, teal-magenta glow, organic luminance", "summary": "Bioluminescent-inspired palette with glowing cool hues."}, + "Sepia Film": {"prompt": "sepia-toned film wash, vintage warmth, gentle fade", "summary": "Vintage sepia palette with analog softness."}, + "Teal & Orange": {"prompt": "teal and orange palette, cinematic contrast, blockbuster grade", "summary": "High-contrast teal/orange pairing for cinematic punch."}, + "Muted Pastel": {"prompt": "muted pastel tones, powdery softness, delicate wash", "summary": "Soft pastel palette dialed down for subtle refinement."}, + "Monochrome Steel": {"prompt": "monochrome steel palette, cool grays, industrial vibe", "summary": "Steely monochrome emphasizing industrial mood."}, + "Iridescent": {"prompt": "iridescent color play, oil-slick spectrum, shifting hues", "summary": "Shimmering palette shifting colors with angle."}, + "Sun-Bleached": {"prompt": "sun-bleached palette, desaturated warmth, desert fade", "summary": "Faded warm palette recalling sun-weathered surfaces."}, + "Candy Pop": {"prompt": "candy pop palette, sugary brights, high-gloss saturation", "summary": "Playful high-saturation palette reminiscent of candy wrappers."}, + "Neon Pulse": {"prompt": "neon pulse palette, electric magentas, cyan highlights", "summary": "Glowing neon palette pulsing with nightlife energy."}, + "Mocha": {"prompt": "mocha palette, creamy browns, cozy latte warmth", "summary": "Comforting brown palette echoing café tones."}, + "Frosted Glass": {"prompt": "frosted glass palette, opaque pastels, milky diffusion", "summary": "Milky translucent palette softening contrast with frost-like hues."}, +} + + +MOTION_TYPE_OPTIONS = { + "None": {"prompt": "", "summary": "No specific motion descriptor."}, + "Running": {"prompt": "dynamic running motion, explosive sprint, powerful stride", "summary": "High-energy running sequence."}, + "Skateboarding": {"prompt": "skateboarding trick, ollie, urban street energy", "summary": "Skateboarding stunt with street flair."}, + "Soccer": {"prompt": "soccer play, agile footwork, goal attempt", "summary": "Soccer action with athletic control."}, + "Tennis": {"prompt": "tennis rally, racket swing, athletic lunge", "summary": "Tennis motion with decisive swing."}, + "Ping Pong": {"prompt": "table tennis volley, rapid reflexes", "summary": "Ping pong rally with rapid exchanges."}, + "Skiing": {"prompt": "downhill skiing, carving turns, powder spray", "summary": "Skiing descent with dynamic carving."}, + "Basketball": {"prompt": "basketball drive, crossover dribble, slam dunk", "summary": "Basketball highlight moment."}, + "Football": {"prompt": "football play, sprint downfield, defensive pressure", "summary": "Football play with explosive impact."}, + "Bowl Dance": {"prompt": "bowl dance motion, rhythmic choreography", "summary": "Stylized bowl dance movement."}, + "Cartwheel": {"prompt": "cartwheel, acrobatic rotation, graceful motion", "summary": "Cartwheel showcasing athletic grace."}, + "Hovering": {"prompt": "hovering suspension, weightless balance, gentle drift", "summary": "Weightless hover capturing poised suspension."}, + "Freefall": {"prompt": "freefall descent, gravity rush, wind-torn limbs", "summary": "High-velocity drop frozen mid freefall."}, + "Parkour": {"prompt": "urban parkour vault, wall run, kinetic agility", "summary": "Parkour motion exploding across rooftops and railings."}, + "Aerial Silk": {"prompt": "aerial silk routine, suspended twirl, fluid acrobatics", "summary": "Aerial silks performance capturing graceful aerial control."}, + "Park Skating": {"prompt": "park skating session, grinding rails, flowing lines", "summary": "Skatepark action blending tricks with fluid movement."}, + "Martial Kata": {"prompt": "martial arts kata, precise forms, disciplined strikes", "summary": "Structured martial sequence emphasizing precision and flow."}, + "Ballet Leap": {"prompt": "grand jeté ballet leap, extended lines, airborne grace", "summary": "Classical ballet leap freezing dancer mid-flight."}, + "Free Diving": {"prompt": "free diving descent, streamlined motion, underwater calm", "summary": "Graceful underwater descent captured mid glide."}, + "Breakdance Spin": {"prompt": "breakdance windmill spin, kinetic sweep, dynamic floorwork", "summary": "Explosive breakdance spin radiating urban energy."}, + "Hoverboard Drift": {"prompt": "hoverboard drift, antigrav carve, futuristic glide", "summary": "Futuristic hoverboard carving arcs through the air."}, + "Park Parade": {"prompt": "parade procession, synchronized steps, celebratory rhythm", "summary": "Processional parade motion with coordinated ensemble."}, + "Sword Duel": {"prompt": "sword duel clash, steel arcs, poised footwork", "summary": "Dramatic swordplay with dynamic lunges and parries."}, + "Mountain Climb": {"prompt": "mountain climb ascent, rope work, vertical challenge", "summary": "Climber scaling sheer rock with dynamic upward movement."}, + "Surf Cutback": {"prompt": "surf cutback maneuver, spray arc, fluid carve", "summary": "Surfer executing stylish cutback with water spray."}, + "Aerial Drone": {"prompt": "drone maneuver, looping flight, agile hover", "summary": "Agile drone weaving loops through obstacles."}, + "Samba Dance": {"prompt": "samba dance rhythm, fast footwork, carnival flair", "summary": "Energetic samba steps pulsing with festive rhythm."}, + "Sprinting Relay": {"prompt": "relay sprint handoff, baton exchange, explosive acceleration", "summary": "Team relay sprint capturing baton handoff intensity."}, +} + + +ACTION_PRIMARY_OPTIONS = { + "None": {"prompt": "", "summary": "No primary action emphasis."}, + "Heroic Sprint": {"prompt": "explosive sprint, knees driving high, purposeful forward lean", "summary": "Powerful sprinting burst projecting urgency and determination."}, + "Focused Jog": {"prompt": "steady jog, rhythmic breathing, relaxed arm swing", "summary": "Measured jogging pace emphasizing stamina and calm focus."}, + "Casual Walk": {"prompt": "casual walk, easy stride, observant gaze", "summary": "Unhurried walking pace conveying confidence and ease."}, + "Power Stride": {"prompt": "power stride, decisive footfalls, shoulders squared", "summary": "Intentional march underscoring leadership and resolve."}, + "Parade March": {"prompt": "synchronized march, precision cadence, ceremonial posture", "summary": "Disciplined marching cadence suited for procession beats."}, + "Tactical Creep": {"prompt": "controlled prowl, low center of gravity, silent footsteps", "summary": "Slow stealth advance ideal for infiltration storytelling."}, + "Graceful Turn": {"prompt": "graceful pivot, fluid hip rotation, balanced poise", "summary": "Elegant pirouette moment showcasing refined control."}, + "Combat Roll": {"prompt": "combat roll, momentum carry, swift recovery stance", "summary": "Dynamic evasive roll leading into combat readiness."}, + "Acrobatic Flip": {"prompt": "tight aerial flip, tucked knees, precise landing", "summary": "Athletic flip highlighting agility and aerial confidence."}, + "Vault Leap": {"prompt": "vault leap over obstacle, palms planted, legs split", "summary": "Momentum-driven vault clearing barriers with flair."}, + "Wall Run": {"prompt": "wall run, angled sprint, defying gravity", "summary": "Parkour-inspired wall run accentuating daring motion."}, + "Ledge Climb": {"prompt": "vertical climb, fingertips gripping, core engaged", "summary": "Determined ascent capturing grit and physical strength."}, + "Grapple Swing": {"prompt": "grappling swing, cable taut, arc through air", "summary": "Swinging traversal showcasing momentum and heroics."}, + "Hover Drift": {"prompt": "hoverboard drift, counter lean, plasma trail", "summary": "Futuristic glide sequences with dramatic banking."}, + "Aerial Hover": {"prompt": "suspended hover, subtle limb adjustments, anti-grav hum", "summary": "Weightless hover emphasizing controlled stillness."}, + "Sword Combo": {"prompt": "sword combo slash, blade flare, precise follow-through", "summary": "Martial sword sequence brimming with kinetic intent."}, + "Shield Brace": {"prompt": "shield brace, wide stance, impact-ready", "summary": "Protective stance built for blocking heavy blows."}, + "Bow Release": {"prompt": "drawn bow release, focused gaze, taut string snap", "summary": "Archery moment capturing tension and accuracy."}, + "Spell Channel": {"prompt": "channeling spell energy, sigils orbiting, outstretched palm", "summary": "Arcane casting posture radiating cinematic power."}, + "Healing Surge": {"prompt": "healing surge, warm aura, hands hovering over ally", "summary": "Supportive restorative action showcasing compassion."}, + "Tech Interface": {"prompt": "rapid console interface, holographic projections, swift keystrokes", "summary": "High-speed tech interaction driven by intellect."}, + "Drone Pilot": {"prompt": "remote drone coordination, dual sticks, analytical focus", "summary": "Precision drone piloting highlighting dexterity."}, + "Vehicle Drift": {"prompt": "vehicle drift, counter steering, tire smoke curl", "summary": "High-octane driving beat with cinematic drift."}, + "Mech Command": {"prompt": "mech command gestures, haptic controls, cockpit glow", "summary": "Piloting a giant mech with commanding gestures."}, + "Gun Kata": {"prompt": "gun kata flow, synchronized dual pistols, spinning disarm", "summary": "Stylized firearm choreography blending combat and dance."}, + "Cover Slide": {"prompt": "slide into cover, sparks skimming, low ready", "summary": "Tactical slide maneuver transitioning into cover."}, + "Reload Snap": {"prompt": "speed reload snap, magazine swap, decisive click", "summary": "Swift weapon reload underscoring discipline."}, + "Data Hack": {"prompt": "neural hack posture, luminous code stream, intense focus", "summary": "Cyber intrusion beat with immersive hacking visuals."}, + "Creative Sketch": {"prompt": "rapid sketch strokes, charcoal dust, concentrated expression", "summary": "Artistic gesture capturing creativity in motion."}, + "Brush Flourish": {"prompt": "brush flourish across canvas, pigment spray, painterly arc", "summary": "Expressive painting movement bursting with color."}, + "Sculpting Strike": {"prompt": "sculpting chisel strike, stone shards, deliberate rhythm", "summary": "Sculptural carving moment emphasizing craftsmanship."}, + "Conducting Cue": {"prompt": "orchestral conducting cue, baton sweep, poised crescendo", "summary": "Commanding conducting gesture driving musical energy."}, + "Freestyle Dance": {"prompt": "freestyle dance burst, syncopated limbs, playful sync", "summary": "Improvisational dance packed with personality."}, + "Contemporary Flow": {"prompt": "contemporary dance flow, sweeping floor contact, expressive torso", "summary": "Fluid dance sequence mixing control and emotion."}, + "Martial Spar": {"prompt": "martial spar exchange, block and counter, sharpened reflexes", "summary": "Friendly sparring beat balancing offense and defense."}, + "Meditative Breath": {"prompt": "meditative breath cycle, grounded stance, centered palms", "summary": "Stillness-focused breathwork radiating calm strength."}, + "Negotiation Gesture": {"prompt": "open negotiation gesture, hands extended, emphatic cadence", "summary": "Persuasive communication action highlighting diplomacy."}, + "Briefing Command": {"prompt": "mission briefing command stance, holo-map gesture, directive tone", "summary": "Mission leadership beat rallying the team."}, + "Victory Cheer": {"prompt": "victory cheer, arms raised, triumphant shout", "summary": "Celebratory climax conveying success and joy."}, + "Reassuring Embrace": {"prompt": "reassuring embrace, protective posture, supportive warmth", "summary": "Supportive hug emphasizing trust and care."}, + "Rescue Carry": {"prompt": "fireman carry rescue, urgent pace, determined grit", "summary": "Heroic rescue action grounded in strength and urgency."}, + "Scientific Scan": {"prompt": "handheld scanner sweep, sensor readouts, analytical calm", "summary": "Investigative scanning beat revealing discoveries."}, +} + + +ACTION_SECONDARY_OPTIONS = { + "None": {"prompt": "", "summary": "No secondary beat."}, + "Evasive Sidestep": {"prompt": "evasive sidestep, dust kick, narrow escape", "summary": "Quick lateral dodge threading through danger."}, + "Counter Strike": {"prompt": "counter strike, sudden riposte, momentum reversal", "summary": "Instant retaliation that flips the battlefield tempo."}, + "Guard Break": {"prompt": "guard break shove, stance disruption, advantage claimed", "summary": "Aggressive shove opening defense lines."}, + "Precision Aim": {"prompt": "precision aim, steady breath hold, laser focus", "summary": "Focused targeting moment capturing suspense."}, + "Scan Sweep": {"prompt": "environmental scan sweep, HUD overlay, alerts ping", "summary": "Data-gathering sweep amplifying situational awareness."}, + "Signal Call": {"prompt": "hand signal callout, coordinated motion, squad synch", "summary": "Silent coordination cue enhancing teamwork."}, + "Shield Raise": {"prompt": "shield raise, sparks deflected, resilience flare", "summary": "Defensive beat underlining protection."}, + "Momentum Slide": {"prompt": "momentum slide, low friction drift, sparks trailing", "summary": "Stylized slide maintaining kinetic energy."}, + "Ground Pound": {"prompt": "ground pound impact, shockwave ring, dust burst", "summary": "Impactful finishing beat showcasing raw force."}, + "Air Twist": {"prompt": "aerial twist kick, suspended rotation, dramatic follow-through", "summary": "Signature kick sequence loaded with flair."}, + "Grapple Lock": {"prompt": "grapple lock, leverage shift, controlled takedown", "summary": "Submission-oriented maneuver demonstrating technique."}, + "Companion Assist": {"prompt": "companion assist boost, interlocked hands, cooperative lift", "summary": "Supportive teammate beat accentuating trust."}, + "Barrier Deploy": {"prompt": "deployable barrier throw, emitter unfold, cover established", "summary": "Instant cover creation for tactical repositioning."}, + "Drone Launch": {"prompt": "drone launch flick, rotor ignition, rapid ascent", "summary": "Secondary action launching support drone units."}, + "Med-Pack Toss": {"prompt": "med-pack toss, side-arm throw, lifesaving delivery", "summary": "Supportive aid thrown to allies mid-conflict."}, + "Energy Burst": {"prompt": "energy burst discharge, luminous surge, crackling aftermath", "summary": "Secondary flare of power emphasizing impact."}, + "Telemetry Check": {"prompt": "telemetry check, wrist holo, data validation", "summary": "Brief systems check ensuring mission alignment."}, + "Environmental Interaction": {"prompt": "interactive environment cue, trigger pull, machinery response", "summary": "Action tied to world mechanics and set pieces."}, + "Emotional Accent": {"prompt": "emotional accent glance, micro-expression, character depth", "summary": "Subtle emotional beat layered into action."}, +} + + +ACTION_TEMPO_OPTIONS = { + "None": {"prompt": "", "summary": "Neutral tempo."}, + "Slow Burn": {"prompt": "slow burn cadence, deliberate pacing, controlled breath", "summary": "Measured tempo suited for stealth or tension building."}, + "Rising Momentum": {"prompt": "rising momentum, energy ramp, quickening steps", "summary": "Tempo increase signaling imminent breakthrough."}, + "Pulse Pounding": {"prompt": "pulse-pounding rhythm, heartbeat percussion, kinetic urgency", "summary": "High-tempo drive keeping adrenaline elevated."}, + "Frantic Rush": {"prompt": "frantic rush, untamed velocity, scattered sparks", "summary": "Chaotic tempo perfect for chase sequences."}, + "Steady Cadence": {"prompt": "steady cadence, balanced breathing, tactical precision", "summary": "Even tempo highlighting discipline and stamina."}, + "Staccato Beats": {"prompt": "staccato beats, rapid-fire cuts, short bursts of motion", "summary": "Choppy tempo ideal for montage or combat edits."}, + "Weightless Drift": {"prompt": "weightless drift tempo, slow-floating beats, elongated frames", "summary": "Dreamlike tempo emphasizing suspended motion."}, + "Heroic Crescendo": {"prompt": "heroic crescendo, swelling momentum, triumphant surge", "summary": "Epic tempo climb culminating in a victorious beat."}, + "Sneaking Pulse": {"prompt": "sneaking pulse, low heart rate, suppressed motion", "summary": "Subtle tempo maintaining stealth tension."}, +} + + +ACTION_INTERACTION_OPTIONS = { + "None": {"prompt": "", "summary": "Solo focus."}, + "Solo Focus": {"prompt": "solo-focused action, spotlight isolated performer", "summary": "Highlights individual capability without distractions."}, + "Duo Synchrony": {"prompt": "duo synchrony, mirrored movements, interlocking timing", "summary": "Two-person coordination conveying trust and chemistry."}, + "Squad Coordination": {"prompt": "squad coordination, layered formation, call-and-response", "summary": "Team-based action balanced across multiple performers."}, + "Crowd Navigation": {"prompt": "crowd navigation, weaving through onlookers, reactive adjustments", "summary": "Action threaded through dense crowd dynamics."}, + "Rival Showdown": {"prompt": "rival showdown, charged eye-line, matched intensity", "summary": "Dramatic confrontation between evenly matched opponents."}, + "Mentor Guidance": {"prompt": "mentor guidance, supportive coaching, corrective touch", "summary": "Instructional beat where mentor refines protege technique."}, + "Rescue Support": {"prompt": "rescue support, stabilizing assist, collaborative lift", "summary": "Mutual aid moment during high-stakes scenario."}, + "Celebration Group": {"prompt": "celebration group embrace, shared triumph, collective cheer", "summary": "Group celebration amplifying emotional payoff."}, + "Spectator Engagement": {"prompt": "spectator engagement, audience interaction, responsive energy", "summary": "Performer interacting directly with enthusiastic crowd."}, + "Tactical Tandem": {"prompt": "tactical tandem, overlapping cover fire, seamless transitions", "summary": "Combat partnership moving as a unified unit."}, +} + + +NSFW_ACTION_PRIMARY_OPTIONS = { + "None": {"prompt": "", "summary": "No specific mature action emphasis."}, + "Sensual Caress": {"prompt": "sensual caress, fingertips tracing curves, lingering warmth", "summary": "Tender caress lingering over consenting adult partner."}, + "Slow Undress": {"prompt": "slow undress, deliberate fabric slide, teasing reveal", "summary": "Gradual wardrobe peel emphasizing anticipation."}, + "Passionate Embrace": {"prompt": "passionate embrace, bodies pressed, breath mingling", "summary": "Full-body embrace radiating mutual desire."}, + "Intimate Sway": {"prompt": "intimate sway, hips aligned, subtle grind", "summary": "Synchronized sway capturing shared rhythm."}, + "Seductive Strut": {"prompt": "seductive strut, confident hips, smoldering gaze", "summary": "Confident walk engineered to entice."}, + "Sultry Floorwork": {"prompt": "sultry floorwork, rolling shoulders, arched back", "summary": "Floor routine showcasing curves and fluid control."}, + "Chair Tease": {"prompt": "chair straddle tease, playful lean back, fingertip glide", "summary": "Chair-focused routine blending tease and control."}, + "Lap Dance Flourish": {"prompt": "lap dance flourish, measured grind, eye contact", "summary": "Exploratory lap dance centered on consent and connection."}, + "Wall Pin": {"prompt": "pinned to wall, dominant arm brace, electric tension", "summary": "Consensual wall pin layering controlled dominance."}, + "Silk Rope Display": {"prompt": "silk rope display, decorative binds, supportive suspension", "summary": "Artful rope styling highlighting trust and safety."}, + "Blindfold Reveal": {"prompt": "blindfold reveal, silk slip, heightened senses", "summary": "Playful sensory deprivation moment building anticipation."}, + "Oil Massage": {"prompt": "warm oil massage, glistening skin, affectionate knead", "summary": "Sensual massage exchanging comfort and pleasure."}, + "Body Trace": {"prompt": "body tracing, nails grazing, deliberate pace", "summary": "Slow tracing that savors every contour thoughtfully."}, + "Corset Tighten": {"prompt": "corset tightening, breathy gasp, mirrored gaze", "summary": "Intimate wardrobe cinch celebrating curves."}, + "Stocking Roll": {"prompt": "stocking roll, thigh-high descent, playful smirk", "summary": "Tempting hosiery peel that spotlights legs."}, + "Garter Snap": {"prompt": "garter snap tease, playful sting, laughing eyes", "summary": "Cheeky garter snap punctuating flirtatious energy."}, + "Mirror Worship": {"prompt": "mirror worship pose, self-admiring touch, glossy lighting", "summary": "Self-love moment honoring adult confidence."}, + "Bath Embrace": {"prompt": "shared bubble bath embrace, water shimmer, relaxed smiles", "summary": "Steamy bath cuddle under soft candlelight."}, + "Dominant Command": {"prompt": "dominant command stance, guiding touch, steady gaze", "summary": "Command-driven beat emphasizing trusted leadership."}, + "Submissive Offer": {"prompt": "submissive offer, kneeling poise, open palms", "summary": "Reverent offering posture celebrating mutual trust."}, + "Morning Glow": {"prompt": "morning-after glow, sheet drape, satisfied stretch", "summary": "Post-intimacy glow capturing tenderness and safety."}, +} + + +NSFW_ACTION_SECONDARY_OPTIONS = { + "None": {"prompt": "", "summary": "No secondary mature beat."}, + "Lip Bite": {"prompt": "soft lip bite, playful tension, smoldering glance", "summary": "Subtle lip bite amplifying chemistry."}, + "Neck Kiss": {"prompt": "neck kiss, lingering breath, gentle tilt", "summary": "Neck kiss moment underscoring intimacy."}, + "Ear Whisper": {"prompt": "ear whisper tease, warm breath, secretive smile", "summary": "Close whisper sharing private promises."}, + "Hair Sweep": {"prompt": "hair sweep aside, revealing collarbone, seductive focus", "summary": "Hair sweep opening the frame to more skin."}, + "Finger Trail": {"prompt": "fingertip trail along spine, shiver ripple", "summary": "Slow finger trail sending shivers down the spine."}, + "Ribbon Tug": {"prompt": "ribbon tug release, fluttering fabric, playful gasp", "summary": "Ribbon pull unlocking the next reveal."}, + "Stocking Adjust": {"prompt": "stocking adjustment, deliberate roll, thigh emphasis", "summary": "Adjusting stockings to heighten leg focus."}, + "Gaze Hold": {"prompt": "gaze lock, dilated pupils, whispered invitation", "summary": "Mutual gaze hold that communicates consent clearly."}, + "Hand Restraint": {"prompt": "gentle wrist restraint, supportive grip, caring check-in", "summary": "Soft restraint paired with constant reassurance."}, + "Body Dip": {"prompt": "dramatic body dip, partner support, arched torso", "summary": "Supported dip highlighting trust and strength."}, + "Hip Grind": {"prompt": "hip grind accent, rolling motion, synchronized breath", "summary": "Rhythmic hip grind deepening sensual flow."}, + "Glove Peel": {"prompt": "glove peel, fingertip tease, slow reveal", "summary": "Slow glove peel coaxing attention to touch."}, + "Perfume Mist": {"prompt": "perfume mist spray, lingering scent, coy smile", "summary": "Scented flourish prepping for closeness."}, + "Velvet Stroke": {"prompt": "velvet glove stroke along torso, indulgent pace", "summary": "Soft fabric glide delighting sensitive skin."}, +} + + +NSFW_ACTION_TEMPO_OPTIONS = { + "None": {"prompt": "", "summary": "Neutral tempo for mature scene."}, + "Slow Tease": {"prompt": "slow tease tempo, patient pacing, suspended breath", "summary": "Lingering tempo that savors every second."}, + "Languid Flow": {"prompt": "languid flow, fluid transitions, mellow rhythm", "summary": "Relaxed cadence for indulgent sequences."}, + "Measured Build": {"prompt": "measured build, rising heat, controlled escalation", "summary": "Gradual crescendo balancing excitement."}, + "Playful Bounce": {"prompt": "playful bounce, upbeat flirtation, lively pace", "summary": "Light-hearted tempo packed with charm."}, + "Breathless Waves": {"prompt": "breathless wave tempo, rising and crashing energy", "summary": "Repeating waves of intensity and release."}, + "Urgent Clutch": {"prompt": "urgent clutch tempo, hungry pace, desperate energy", "summary": "Fast pace fueled by mutual craving."}, + "Commanded Pace": {"prompt": "commanded pace, deliberate cues, guided rhythm", "summary": "Dominant partner dictating tempo with care."}, + "Hypnotic Loop": {"prompt": "hypnotic loop, repeating pattern, trance-like focus", "summary": "Repetitive motions that mesmerize audience."}, + "Tender Pause": {"prompt": "tender pause beats, held breaths, eye contact linger", "summary": "Strategic pauses that celebrate connection."}, +} + + +CAMZ_PERSONA_OPTIONS = { + "Soft Glam Muse": { + "prompt": "soft glam cam performer, luminous skin, encouraging smile, relaxed confidence", + "notes": "Lean into approachable warmth blended with polished glam cues.", + }, + "Cyber Starlet": { + "prompt": "futuristic cyber cam muse, neon accent liner, playful tech accessories", + "notes": "Pair shimmering highlights with synth-forward ambience for modern edge.", + }, + "Fitness Tease": { + "prompt": "athletic cam goddess, toned silhouette, playful flex moments", + "notes": "Incorporate light workout props and confident body language cues.", + }, + "Sultry Jazz": { + "prompt": "jazz lounge cam siren, velvety voice, deliberate slow gestures", + "notes": "Match low tempo jazz soundtrack with honeyed lighting for intimacy.", + }, +} + + +CAMZ_STAGE_OPTIONS = { + "Neon Loft": { + "prompt": "neon loft backdrop, led strip accents, reflective plexi floor", + "notes": "Balance saturated highlights with soft fill to flatter skin.", + }, + "Velvet Boudoir": { + "prompt": "crimson velvet boudoir set, fairy lights, plush textures", + "notes": "Layer candles and warm bulbs to deepen romantic ambience.", + }, + "Glow Studio": { + "prompt": "glow studio backdrop, gradient colorwash, cloud diffuser panels", + "notes": "Use color cycling and haze for dreamy, stylized streaming look.", + }, + "Gamer Den": { + "prompt": "rgb gamer desk, holographic screens, soft plush seating", + "notes": "Mix animated overlays with keyboard glow for interactive energy.", + }, +} + + +CAMZ_CAMERA_OPTIONS = { + "Intimate Close": { + "prompt": "intimate close framing, direct lens eye contact, creamy bokeh", + "notes": "Keep lens near eye level for immediate connection and sparkle.", + }, + "Desk Peek": { + "prompt": "desk peek composition, slightly elevated perspective, flattering angle", + "notes": "Angle camera from high-left to emphasize eyes and decolletage.", + }, + "Full Body": { + "prompt": "full body cam frame, full-length mirror, responsive lighting", + "notes": "Stage lighting evenly to avoid clipping during motion demos.", + }, + "Dynamic Dolly": { + "prompt": "slow dolly glide, remote gimbal, smooth parallax", + "notes": "Automate subtle motion to keep stream kinetic without distraction.", + }, +} + + +CAMZ_ENERGY_OPTIONS = { + "Gentle Sway": { + "prompt": "gentle swaying motion, soft giggles, tender pacing", + "notes": "Use lingering gestures and ASMR whisper tones for relaxation.", + }, + "Hype Spark": { + "prompt": "high-energy bounce, playful chatter, animated laughter", + "notes": "Cue upbeat playlists and interactive games for tip spikes.", + }, + "Slow Burn": { + "prompt": "slow-burn tease, deliberate pauses, smoldering eye contact", + "notes": "Layer progressive reveals with countdown widgets for tension.", + }, + "Command Mode": { + "prompt": "commanding domme cadence, confident directives, teasing coaching", + "notes": "Balance assertive tone with check-ins on boundaries and comfort.", + }, +} + + +NSFW_ACTION_INTERACTION_OPTIONS = { + "None": {"prompt": "", "summary": "Solo highlight by default."}, + "Solo Self-Admiration": {"prompt": "solo self-admiration, mirrored touch, confident posture", "summary": "Spotlights a single adult indulging in self-appreciation."}, + "Mirror Performance": {"prompt": "mirror performance, dual reflections, choreographed tease", "summary": "Performer using mirrors to amplify allure."}, + "Mutual Embrace": {"prompt": "mutual embrace, intertwined limbs, tender reciprocity", "summary": "Two consenting adults wrapped in equal affection."}, + "Dominant Lead": {"prompt": "dominant lead, guiding hand, steady reassurance", "summary": "Confident lead steering an enthusiastic partner."}, + "Submissive Offering": {"prompt": "submissive offering, lowered gaze, trusting openness", "summary": "Trust-forward posture celebrating consensual vulnerability."}, + "Power Switch": {"prompt": "power exchange switch, playful role reversal", "summary": "Partners swapping roles with delighted consent."}, + "Voyeur Tease": {"prompt": "voyeur tease, aware of onlookers, controlled reveal", "summary": "Flirtatious performance directed at an invited audience."}, + "Stage Showcase": {"prompt": "stage showcase, spotlighted solo, theatrical flourish", "summary": "Burlesque-style spotlight designed for adoring fans."}, + "Lap Dance Delivery": {"prompt": "lap dance delivery, partner seated, performer leading", "summary": "Classic lap dance built on clear boundaries."}, + "Sensual Massage Exchange": {"prompt": "mutual massage exchange, alternating focus, satisfied sighs", "summary": "Partners trading soothing touch in balanced turns."}, + "Tandem Pose": {"prompt": "tandem pose alignment, sculpted silhouettes, mutual balance", "summary": "Dual posing moment engineered for visual harmony."}, +} + +PRESET_ROOT = os.path.join(os.path.dirname(os.path.dirname(__file__)), "presets") + +CUSTOM_OPTION = "Custom Input" + +FORMULA_CONFIGS = { + "Basic Formula": { + "description": "Subject + Scene + Motion — perfect for ideation and quick inspiration.", + "structure": ["subject", "scene", "motion"], + }, + "Advanced Formula": { + "description": "Subject (desc) + Scene (desc) + Motion (desc) + Aesthetic controls + Stylization.", + "structure": ["subject", "scene", "motion", "aesthetic", "stylization"], + }, + "Story Spotlight": { + "description": "Focus subject + environmental beat + camera language + emotional tone summary.", + "structure": ["subject", "scene", "motion", "emotion", "camera"], + }, + "Trailer Teaser": { + "description": "Cold open hook + subject descriptor + kinetic beat + stylization tag cloud.", + "structure": ["scene", "subject", "motion", "stylization"], + }, +} + +LIGHT_SOURCE_OPTIONS = { + "None": {"prompt": "", "summary": "No dedicated light source emphasis."}, + "Daylight": {"prompt": "daylight, sunlit ambience, warm highlights, natural light", "summary": "Sunlit scene with warm, natural illumination."}, + "Artificial Light": {"prompt": "artificial lighting, tungsten glow, controlled practicals", "summary": "Moody artificial lighting with cinematic practical sources."}, + "Moonlight": {"prompt": "moonlight, cool lunar glow, silver rim lighting", "summary": "Cool moonlit ambience with gentle highlights."}, + "Practical Light": {"prompt": "practical lighting, lamps, diegetic light sources", "summary": "Scene lit by visible practical fixtures."}, + "Firelight": {"prompt": "firelight, ember glow, flickering warm highlights", "summary": "Flickering firelight with warm dancing shadows."}, + "Fluorescent Light": {"prompt": "fluorescent lighting, clinical glow, cool overhead wash", "summary": "Flat fluorescent illumination with slight green tint."}, + "Overcast Light": {"prompt": "overcast sky, diffuse natural light, soft ambient shadows", "summary": "Soft overcast diffusion with muted contrast."}, + "Mixed Light": {"prompt": "mixed lighting sources, color contrast, layered temperatures", "summary": "Mixed lighting with contrasting color temperatures."}, + "Sunlight": {"prompt": "direct sunlight, high contrast shadows, golden highlights", "summary": "Direct sun with crisp highlights and defined shadows."}, + "Lantern Glow": {"prompt": "lantern-lit ambience, swaying warm pools, intimate highlights", "summary": "Lantern pools of light creating intimate pockets of warmth."}, + "Bioluminescence": {"prompt": "bioluminescent glow, organic shimmer, underwater luminosity", "summary": "Organic bioluminescence casting ethereal aquatic light."}, + "Holographic Panels": {"prompt": "holographic panel lighting, refracted neon, cascading spectrum wash", "summary": "Glitchy holographic panels bathing the scene in shimmering gradients."}, + "Torchline": {"prompt": "torch-lit procession, wavering flame line, ember flicker", "summary": "Row of torches casting rhythmic pulses of warm light."}, + "Neon Billboard": {"prompt": "neon billboard glow, saturated signage wash, flickering letters", "summary": "Vibrant billboard light bathing the scene in rhythmic neon pulses."}, + "Campfire Circle": {"prompt": "campfire circle, ember sparks, storytelling glow", "summary": "Intimate fire pit lighting painting faces with warm flicker."}, + "Stage Spotlight": {"prompt": "stage spotlight beam, hard edge falloff, performer isolation", "summary": "Focused theatrical spotlight carving subject from darkness."}, + "Lightning Storm": {"prompt": "lightning storm bursts, strobing sky flashes, electric contrast", "summary": "Unpredictable lightning bolts freezing action in high contrast."}, + "Volcanic Flow": {"prompt": "volcanic magma glow, molten spill, infernal ambience", "summary": "Seething lava light casting ominous crimson across surfaces."}, + "Candlelit Vigil": {"prompt": "clustered candlelight, fluttering wicks, hushed warmth", "summary": "Sea of candles producing soft, reverent illumination."}, + "Aurora Curtain": {"prompt": "aurora curtain glow, shifting greens and violets, sky drape", "summary": "Northern lights bathing subjects in dreamy celestial hues."}, + "Starlight": {"prompt": "pure starlight, silver speckle, expansive night shimmer", "summary": "Subtle starlit exposure lending gentle highlights to silhouettes."}, + "Underwater Caustics": {"prompt": "underwater caustic light, dancing wave reflections, aquatic shimmer", "summary": "Liquid light patterns rippling across submerged subjects."}, + "Snow Reflection": {"prompt": "snowfield bounce light, diffuse white glow, winter reflection", "summary": "Snow bounce filling shadows with cool diffuse brightness."}, + "Forge Hearth": {"prompt": "forge hearth blaze, iron sparks, molten core", "summary": "Blacksmith forge light casting muscular orange highlights."}, + "Alchemical Vat": {"prompt": "alchemical vat glow, bubbling neon liquids, laboratory luminescence", "summary": "Arcane vats emitting surreal colored glows through mist."}, + "Cyber Street Lamps": {"prompt": "cyberpunk street lamps, holographic ads, saturated sidewalk beams", "summary": "Urban street lamps layering colored pools across rain-slick pavement."}, + "Gaslight Row": {"prompt": "gaslight row glow, amber halos, Victorian haze", "summary": "Historical gas lamps lining the street with soft amber halos."}, + "Lantern Parade": {"prompt": "festival lantern parade, drifting paper lights, communal glow", "summary": "Floating lanterns surrounding characters in celebratory radiance."}, + "Bioreactor": {"prompt": "bioreactor glow, pulsing biofluorescence, vat illumination", "summary": "Contained bioluminescent reactors bathing spaces in organic light."}, + "Laser Grid": {"prompt": "laser grid, intersecting beams, chromatic lattice", "summary": "Precision laser lattice generating futuristic lighting geometry."}, + "Holographic Dome": {"prompt": "holographic dome canopy, rotating gradients, virtual sky", "summary": "Immersive holographic ceiling diffusing programmable spectrum washes."}, + "Meteor Shower": {"prompt": "meteor shower streaks, incandescent trails, cosmic sparks", "summary": "Sky filled with glowing meteors streaking luminous trails downward."}, + "Flare Burst": {"prompt": "signal flare burst, smoke-tinted glow, emergency wash", "summary": "Emergency flare casting dramatic red light through haze."}, + "Oil Lantern": {"prompt": "oil lantern glow, soot-streaked glass, steady warm pool", "summary": "Portable lantern providing steadfast warm illumination."}, + "Arc Welder": {"prompt": "arc welder flash, intense blue-white, showering sparks", "summary": "Industrial arc welding light freezing motion with harsh brilliance."}, + "Citylight Spill": {"prompt": "distant citylight spill, skyline glow, sodium haze", "summary": "Ambient glow from a distant skyline softly filling the frame."}, + "Crystal Chandelier": {"prompt": "crystal chandelier glitter, prismatic sparkle, ballroom glow", "summary": "Opulent chandelier refracting light into glistening shards."}, + "Bonfire Blaze": {"prompt": "towering bonfire blaze, roaring embers, sparks aloft", "summary": "Massive bonfire engulfing the environment in roaring orange light."}, + "Laneway Neon": {"prompt": "laneway neon strips, saturated signage, puddle reflections", "summary": "Tight alley bathed in stacked neon signage reflections."}, + "Bio-Lanterns": {"prompt": "bio-lantern organisms, glowing fauna, natural luminance", "summary": "Living lantern creatures casting organic multi-toned light."}, + "Ice Cavern Crystals": {"prompt": "ice cavern crystals, refracted glimmer, frozen glow", "summary": "Translucent ice crystals bending light through cavern walls."}, + "Plasma Torches": {"prompt": "plasma torch banks, blue-white arcs, industrial intensity", "summary": "Rows of plasma torches bathing subjects in blistering brilliance."}, + "Sunrise Window": {"prompt": "sunrise window beam, golden shafts, dust motes", "summary": "Early sun cascading through windows with painterly rays."}, + "Projector Spill": {"prompt": "film projector spill, dancing shadows, analog flicker", "summary": "Projector light spilling off screen with nostalgic flicker."}, + "Glimmering Reef": {"prompt": "coral reef glow, phosphorescent marine life, tropical illumination", "summary": "Undersea reef emitting soft gradients from luminous coral."}, + "Supernova Remnant": {"prompt": "supernova remnant glow, cosmic dust, radiant nebula", "summary": "Interstellar residue enveloping scene in vibrant nebular light."}, + "Iridescent Canopy": {"prompt": "iridescent canopy panels, shifting oil-slick colors", "summary": "Architectural canopy reflecting iridescent hues onto subjects."}, +} + +LIGHT_QUALITY_OPTIONS = { + "None": {"prompt": "", "summary": "No additional light quality emphasis."}, + "Soft Light": {"prompt": "soft light, diffused shadows, gentle transition", "summary": "Soft, diffused lighting with subtle shadow falloff."}, + "Hard Light": {"prompt": "hard light, sharp shadows, chiseled contrast", "summary": "Hard-edged lighting with defined contrast."}, + "Top Light": {"prompt": "top lighting, overhead light source, dramatic downward shadows", "summary": "Top-down lighting with sculpted shadows."}, + "Side Light": {"prompt": "side lighting, directional key light, lateral highlights", "summary": "Side lighting with strong modeling."}, + "Backlight": {"prompt": "backlighting, halo rim, silhouette highlights", "summary": "Backlighting that separates subject from background."}, + "Bottom Light": {"prompt": "uplighting, underlight, theatrical glow", "summary": "Bottom lighting creating dramatic, eerie ambience."}, + "Rim Light": {"prompt": "rim lighting, edge highlight, subject outline glow", "summary": "Highlighted subject edges for separation."}, + "Silhouette": {"prompt": "silhouette lighting, subject in shadow, strong backlight", "summary": "High-contrast silhouette composition."}, + "Low Contrast": {"prompt": "low contrast lighting, gentle tonal range, subtle shadows", "summary": "Low-contrast lighting for a soft palette."}, + "High Contrast": {"prompt": "high contrast lighting, stark shadows, deep blacks", "summary": "High-contrast lighting with dramatic tonality."}, + "Dappled Light": {"prompt": "dappled light, patterned highlights, dancing shade", "summary": "Patterned illumination filtered through foliage or lattice."}, + "Volumetric Rays": {"prompt": "volumetric god rays, particulate beams, dramatic shafts", "summary": "Visible shafts of light carving through atmosphere."}, + "Specular Punch": {"prompt": "specular punch lighting, crisp highlights, lacquered sheen", "summary": "High-specular highlights carving glossy definition across surfaces."}, + "Veiled Diffusion": {"prompt": "veiled diffusion, gauzy wrap, mist-filtered glow", "summary": "Soft veil of diffused light for hazy, dreamlike coverage."}, + "Specular Mosaic": {"prompt": "specular mosaic, shattered highlights, jeweled reflections", "summary": "Fragmented highlights breaking across faceted surfaces."}, + "Feathered Rim": {"prompt": "feathered rim lighting, gradual halo, airy separation", "summary": "Gentle rim light with feathery falloff outlining silhouettes."}, + "Split Tone": {"prompt": "split-toned lighting, opposing color temperatures", "summary": "Two-tone lighting dividing the subject into contrasting hues."}, + "Motivated Practical": {"prompt": "motivated practicals, believable diegetic spill", "summary": "Stylized yet grounded lighting justified by on-set fixtures."}, + "Softbox Wrap": {"prompt": "giant softbox wrap, wrapping highlights, creamy falloff", "summary": "Massive soft source hugging the subject with gentle gradient."}, + "Kicker Highlights": {"prompt": "kicker highlights, sharp accent lines, sculpted edges", "summary": "Edge kickers introducing crisp accent streaks."}, + "High Key": {"prompt": "high-key lighting, airy brightness, minimal shadow", "summary": "Bright, buoyant scheme minimizing contrast for cheerful tone."}, + "Low Key": {"prompt": "low-key lighting, deep blacks, selective highlights", "summary": "Moody low-key envelope with pockets of illumination."}, + "Chromatic Spill": {"prompt": "chromatic spill, rainbow edges, spectral fringe", "summary": "Prismatic spill generating colorful haloing around forms."}, + "Firefly Scatter": {"prompt": "firefly scatter, pinpoint twinkles, drifting motes", "summary": "Micro light points swirling through the air like fireflies."}, + "Soft Focus Glow": {"prompt": "soft focus glow, diffusion bloom, romantic haze", "summary": "Dreamy glow softening detail for romantic imagery."}, + "Mirror Bounce": {"prompt": "mirror bounce lighting, crisp reflection, mirrored key", "summary": "Mirrored bounce producing sharp reflected highlights."}, + "Patterned Gobos": {"prompt": "patterned gobo wash, carved shadows, cut shapes", "summary": "Gobo patterns casting intricate silhouettes across surfaces."}, + "Industrial Strip": {"prompt": "industrial strip lighting, linear highlights, warehouse tone", "summary": "Strip fixtures painting parallel lines of illumination."}, + "Dappled Canopy": {"prompt": "dappled canopy light, leafy pattern, natural stipple", "summary": "Filtered sunlight flickering through foliage onto subjects."}, +} + +TIME_OF_DAY_OPTIONS = { + "None": {"prompt": "", "summary": "No specific time-of-day emphasis."}, + "Daytime": {"prompt": "daytime setting, bright ambience", "summary": "Bright daytime environment."}, + "Nighttime": {"prompt": "nighttime scene, deep shadows, artificial accents", "summary": "Nighttime backdrop with dramatic contrast."}, + "Dusk": {"prompt": "dusk, golden hour, lingering warmth", "summary": "Golden hour glow with soft warmth."}, + "Sunset": {"prompt": "sunset sky, saturated horizon, long shadows", "summary": "Sunset palette with vivid horizon."}, + "Dawn": {"prompt": "pre-dawn hues, gentle blue hour, quiet ambience", "summary": "Pre-dawn tones with soft gradients."}, + "Sunrise": {"prompt": "sunrise light, fresh warmth, emerging glow", "summary": "Sunrise optimism with warm highlights."}, + "Midnight": {"prompt": "midnight sky, star-pricked darkness, tranquil hush", "summary": "Deep midnight calm with subtle celestial accents."}, + "Blue Hour": {"prompt": "blue hour tones, twilight glow, cobalt horizon", "summary": "Cool twilight wash bridging night and day."}, + "Golden Hour": {"prompt": "golden hour light, honeyed tones, elongated shadows", "summary": "Golden hour baths everything in soft amber light."}, + "Storm Night": {"prompt": "stormy midnight, lightning flashes, rain-lashed darkness", "summary": "Charged midnight storm with intermittent lightning wash."}, + "Pre-Dawn Blue": {"prompt": "pre-dawn blue hour, hushed gradients, sleeping city", "summary": "Quiet pre-dawn stillness awash in cool blues."}, + "High Noon": {"prompt": "high noon sun, overhead blaze, sharp shadows", "summary": "Unforgiving midday sun casting stark overhead shadows."}, + "Twilight": {"prompt": "twilight blend, fading warmth, encroaching cool", "summary": "Transitional twilight mixing warm and cool tones."}, + "Solar Eclipse": {"prompt": "solar eclipse dimming, surreal twilight, corona rim", "summary": "Eerie eclipse atmosphere with dramatic ambient shift."}, + "Lunar Eclipse": {"prompt": "lunar eclipse glow, copper moon, penumbral hues", "summary": "Earth-shadowed moon painting night with copper undertones."}, + "Rainy Afternoon": {"prompt": "rainy afternoon light, overcast drizzle, reflective streets", "summary": "Soft grey daylight with shimmering rain reflections."}, + "Winter Dawn": {"prompt": "winter dawn, frosted breath, pale sun", "summary": "Chilly dawn with subdued pastel sunlight."}, + "Summer Midnight": {"prompt": "summer midnight twilight, lingering glow, horizon gradients", "summary": "High-latitude midnight where sun barely dips, leaving glow."}, + "Desert High Sun": {"prompt": "desert high sun, heat shimmer, bleached highlights", "summary": "Scorching desert midday flattening contrast under glare."}, + "Golden Dusk": {"prompt": "deep golden dusk, saturated clouds, elongated shadows", "summary": "Late dusk deepening golds and oranges before nightfall."}, + "Aurora Midnight": {"prompt": "aurora midnight, dancing lights, polar night", "summary": "Polar midnight illuminated by auroral ribbons."}, + "Festival Evening": {"prompt": "festival evening glow, lanterns rising, celebratory warmth", "summary": "Community evening with layered practicals and warm ambiance."}, + "Stormy Noon": {"prompt": "stormy noon, bruise-cloud canopy, diffused fury", "summary": "Midday storm muting sun into dramatic diffuse glow."}, +} + +SHOT_SIZE_OPTIONS = { + "None": {"prompt": "", "summary": "No shot size emphasis."}, + "Close-up": {"prompt": "close-up shot, intimate framing, facial detail", "summary": "Close-up framing for emotional focus."}, + "Medium Close-up": {"prompt": "medium close-up composition, chest-up framing", "summary": "Medium close-up showcasing shoulders and expression."}, + "Medium Shot": {"prompt": "medium shot, balanced subject framing", "summary": "Medium shot capturing subject and context."}, + "Medium Long Shot": {"prompt": "medium long shot, knees-up framing", "summary": "Medium long shot blending subject and space."}, + "Long Shot": {"prompt": "long shot, full body, environmental scale", "summary": "Long shot emphasizing subject within environment."}, + "Full Shot": {"prompt": "full shot, entire figure, spatial context", "summary": "Full body framing with contextual surroundings."}, + "Wide Angle": {"prompt": "wide angle framing, expansive field of view", "summary": "Wide angle perspective with environmental breadth."}, + "Extreme Close-up": {"prompt": "extreme close-up, micro expression, intimate texture", "summary": "Intense framing that magnifies subtle detail."}, + "Bird's Eye Wide": {"prompt": "bird's-eye master shot, sweeping overhead, geographic context", "summary": "High vantage shot revealing spatial relationships."}, + "Extreme Wide": {"prompt": "extreme wide vista, monumental scale, tiny subject silhouette", "summary": "Vast scale framing dwarfs the subject within the environment."}, + "Macro Detail": {"prompt": "macro detail insert, texture emphasis, ultra close focus", "summary": "Macro detail shot isolating tactile surfaces and micro storytelling."}, + "Extreme Macro": {"prompt": "extreme macro, microscopic textures, abstract detail", "summary": "Ultra-magnified shot revealing microscopic textures."}, + "Medium Group": {"prompt": "medium group shot, waist-up ensemble, shared frame", "summary": "Medium framing accommodating multiple subjects in balance."}, + "Environmental Portrait": {"prompt": "environmental portrait, subject within meaningful setting", "summary": "Portrait capturing subject alongside context-rich environment."}, + "Architectural Wide": {"prompt": "architectural wide shot, towering structures, spatial clarity", "summary": "Wide framing highlighting grand architectural scale."}, + "Aerial Panorama": {"prompt": "aerial panorama, sweeping vista, expansive scope", "summary": "High-altitude panorama showcasing sprawling landscapes."}, + "Insert Detail": {"prompt": "insert detail shot, key prop close-up", "summary": "Tight insert isolating a crucial storytelling detail."}, + "Hero Medium": {"prompt": "hero medium shot, waist-up heroic framing", "summary": "Classic hero framing balancing intimacy and stature."}, + "Crowd Master": {"prompt": "crowd master shot, large-scale ensemble, layered depth", "summary": "Wide master capturing energetic crowd dynamics."}, + "Tableau": {"prompt": "tableau shot, staged composition, painterly arrangement", "summary": "Static tableau emphasizing composition and collective pose."}, +} + +COMPOSITION_OPTIONS = { + "None": {"prompt": "", "summary": "No composition emphasis."}, + "Centered": {"prompt": "centered composition, balanced subject placement", "summary": "Subject centered for symmetry."}, + "Balanced": {"prompt": "balanced framing, visual equilibrium", "summary": "Balanced composition with even weight."}, + "Right-weighted": {"prompt": "right-weighted composition, dynamic off-center balance", "summary": "Subject anchored to the right for motion."}, + "Left-weighted": {"prompt": "left-weighted composition, directional focus", "summary": "Subject anchored to the left for leading motion."}, + "Symmetrical": {"prompt": "symmetrical framing, mirrored balance", "summary": "Symmetrical layout with mirrored elements."}, + "Short-siding": {"prompt": "short-siding composition, open space behind subject", "summary": "Short-siding for tension and anticipation."}, + "Leading Lines": {"prompt": "leading lines composition, guiding geometry, directional flow", "summary": "Structural lines that guide the eye to the subject."}, + "Rule of Thirds": {"prompt": "rule of thirds framing, dynamic balance, offset subject", "summary": "Offset placement using thirds for energetic balance."}, + "Radial Burst": {"prompt": "radial burst composition, converging energy, center highlight", "summary": "Radiating lines pull the eye to a powerful center focal point."}, + "Negative Space": {"prompt": "negative space framing, minimal subject occupancy, breathing room", "summary": "Purposeful negative space emphasizing subject isolation."}, + "Golden Spiral": {"prompt": "golden spiral composition, dynamic curve, natural flow", "summary": "Golden spiral guiding viewer eye along elegant arc."}, + "Framed Within": {"prompt": "framed-within composition, doorway framing, nested focus", "summary": "Subject framed by architectural or natural elements for depth."}, + "Leading Curve": {"prompt": "leading curve composition, sweeping arc, graceful guide", "summary": "Curvilinear leading line drawing attention across frame."}, + "Foreground Obscure": {"prompt": "foreground obscure, partial blockage, voyeur framing", "summary": "Foreground elements partially obscuring action for intrigue."}, + "Layered Depth": {"prompt": "layered depth composition, foreground midground background", "summary": "Stacked planes creating rich depth cues."}, + "Diagonal Drive": {"prompt": "diagonal drive composition, kinetic tilt, motion emphasis", "summary": "Dynamic diagonals introducing energy and momentum."}, + "Central Void": {"prompt": "central void composition, subject encircling emptiness", "summary": "Elements arranged around intentional empty core."}, + "Asymmetrical Balance": {"prompt": "asymmetrical balance, weighted contrast, tension harmony", "summary": "Unequal elements balancing through visual weight."}, + "Mirror Symmetry": {"prompt": "mirror symmetry, reflective imagery, cloned halves", "summary": "Mirrored halves creating duality and repetition."}, +} + +LENS_FOCAL_OPTIONS = { + "None": {"prompt": "", "summary": "No focal length emphasis."}, + "Medium": {"prompt": "medium focal length, natural perspective", "summary": "Medium focal length for balanced view."}, + "Wide Angle": {"prompt": "wide angle lens, expanded perspective, mild distortion", "summary": "Wide angle depth with sweeping environment."}, + "Long": {"prompt": "long lens, compressed background, shallow depth", "summary": "Long lens compression with tight focus."}, + "Telephoto": {"prompt": "telephoto lens, extreme compression, isolated subject", "summary": "Telephoto compression isolating subject."}, + "Fisheye": {"prompt": "fisheye lens, dramatic curvature, immersive distortion", "summary": "Fisheye distortion for stylized wrap."}, + "Ultra Wide": {"prompt": "ultra wide focal length, expansive sweep, dramatic scale", "summary": "Ultra wide view exaggerating space and scope."}, + "Macro": {"prompt": "macro lens, magnified detail, shallow depth of field", "summary": "Macro magnification revealing intricate textures."}, + "Tilt Shift": {"prompt": "tilt-shift focal plane, selective focus, miniature illusion", "summary": "Tilt-shift lens controlling focus plane for miniature effect."}, + "Anamorphic": {"prompt": "anamorphic lens, cinematic squeeze, oval bokeh", "summary": "Anamorphic lensing for widescreen flare and oval bokeh."}, + "Super Telephoto": {"prompt": "super-telephoto lens, extreme reach, compressed horizon", "summary": "Ultra-long focal length flattening space to dramatic effect."}, + "Portrait Prime": {"prompt": "portrait prime lens, creamy bokeh, flattering compression", "summary": "Classic portrait prime emphasizing subject isolation and bokeh."}, + "Vintage Glass": {"prompt": "vintage lens, subtle aberrations, nostalgic softness", "summary": "Retro lens character introducing romantic imperfection."}, + "Soft Focus Lens": {"prompt": "soft focus lens, haloed highlights, blooming edges", "summary": "Specialty lens diffusing focus for dreamy imagery."}, + "Ultra Macro": {"prompt": "ultra macro lens, life-size magnification, shallow plane", "summary": "Macro lens capturing extreme close detail at 1:1 magnification."}, + "Pinhole": {"prompt": "pinhole optics, infinite focus, vignetted edges", "summary": "Pinhole lens aesthetic offering deep focus and organic vignette."}, + "Shift Lens": {"prompt": "shift lens, perspective control, architectural correction", "summary": "Perspective-control lens keeping verticals true."}, + "Catadioptric": {"prompt": "catadioptric mirror lens, donut bokeh, compact telephoto", "summary": "Mirror telephoto lens creating signature donut-shaped bokeh."}, +} + +LENS_ANGLE_OPTIONS = { + "None": {"prompt": "", "summary": "No camera angle emphasis."}, + "Over-the-shoulder": {"prompt": "over-the-shoulder view, subjective framing", "summary": "Over-the-shoulder perspective for dialogue."}, + "High Angle": {"prompt": "high angle shot, elevated vantage", "summary": "High angle perspective for vulnerability."}, + "Low Angle": {"prompt": "low angle shot, heroic scale", "summary": "Low angle for power and grandeur."}, + "Tilted Angle": {"prompt": "dutch angle, tilted horizon, dynamic tension", "summary": "Tilted angle introducing unease."}, + "Aerial Shot": {"prompt": "aerial perspective, sweeping overhead view", "summary": "Aerial viewpoint capturing scale."}, + "Top-down View": {"prompt": "top-down shot, orthographic view, planar composition", "summary": "Top-down vantage for graphic patterns."}, + "Ground Level": {"prompt": "ground-level angle, horizon skimming, towering subject", "summary": "Camera placed at ground height for towering emphasis."}, + "Point-of-View": {"prompt": "first-person POV angle, subjective framing, immersive gaze", "summary": "Immersive first-person angle echoing the character's view."}, + "Drone Chase": {"prompt": "drone chase angle, high-speed pursuit, aerial tracking", "summary": "Fast-moving drone perspective sweeping through the action."}, + "Shoulder Cam": {"prompt": "over-shoulder handheld cam, intimate proximity, reactive framing", "summary": "Shoulder-mounted camera adding immersive immediacy."}, + "Dutch Low": {"prompt": "low dutch angle, canted horizon near ground", "summary": "Low-positioned dutch angle heightening unease and power."}, + "Head-On": {"prompt": "head-on angle, subject facing camera, direct confrontation", "summary": "Direct frontal angle confronting viewer with subject."}, + "Three-Quarter": {"prompt": "three-quarter angle, slight turn, dimensionality", "summary": "Classic three-quarter angle revealing facial depth."}, + "Worm's Eye": {"prompt": "worm's-eye view, extreme upward gaze, towering scale", "summary": "Extreme low angle exaggerating vertical dominance."}, + "Ceiling Mount": {"prompt": "ceiling mounted camera, surveillance vantage", "summary": "High fixed vantage delivering surveillance aesthetic."}, + "Foot-Level": {"prompt": "foot-level angle, grounded perspective, kinetic motion", "summary": "Camera placed at ground level to emphasize motion and speed."}, + "Mirror POV": {"prompt": "mirror point-of-view, reflective perspective, dual framing", "summary": "Angle captured via mirror reflection for dual-layer storytelling."}, + "Drone Descent": {"prompt": "drone descent angle, rapidly dropping aerial", "summary": "Descending aerial angle transitioning from high to mid altitude."}, + "Shoulder Reverse": {"prompt": "reverse over-shoulder, conversational counter", "summary": "Counter-angle OTS capturing opposing subject in dialogue."}, +} + +LENS_TYPE_OPTIONS = { + "None": {"prompt": "", "summary": "No shot type emphasis."}, + "Single Shot": {"prompt": "single shot focus, solo subject", "summary": "Single shot isolating the hero."}, + "Two Shot": {"prompt": "two shot framing, dual subjects", "summary": "Two shot balancing interactions."}, + "Three Shot": {"prompt": "three shot composition, trio dynamic", "summary": "Three shot capturing group interplay."}, + "Group Shot": {"prompt": "group shot, ensemble framing", "summary": "Group shot highlighting ensemble relationships."}, + "Establishing Shot": {"prompt": "establishing shot, wide context, scene-setting", "summary": "Establishing shot grounding the location."}, + "Insert Shot": {"prompt": "insert shot detail, object emphasis, storytelling prop", "summary": "Tight detail shot spotlighting a crucial element."}, + "Crowd Panorama": {"prompt": "crowd panorama, sweeping ensemble, layered depth", "summary": "Expansive ensemble shot capturing scale and energy."}, + "Overhead Master": {"prompt": "overhead master shot, architectural layout, orchestrated movement", "summary": "High overhead master framing to chart spatial choreography."}, + "Detail Montage": {"prompt": "detail montage series, rapid inserts, sensory collage", "summary": "Montage of detail shots creating rhythmic storytelling."}, + "Reaction Shot": {"prompt": "reaction shot, emotional response focus", "summary": "Shot capturing character reaction for emotional punctuation."}, + "POV Shot": {"prompt": "point-of-view shot, first-person perspective", "summary": "Subjective POV immersing viewer in character perspective."}, + "Montage Sequence": {"prompt": "montage sequence, time compression, rhythmic cuts", "summary": "Collection of shots compressing time through rhythmic editing."}, + "Tracking Shot": {"prompt": "tracking shot, continuous movement, follow focus", "summary": "Shot moving alongside subject to retain consistent framing."}, + "Dialogue Two Shot": {"prompt": "dialogue two shot, conversational framing", "summary": "Balanced two shot highlighting dialogue between characters."}, + "Extreme Long": {"prompt": "extreme long establishing, distant subjects, landscape emphasis", "summary": "Very wide framing situating characters within vast landscape."}, + "Hero Shot": {"prompt": "hero shot, triumphant pose, low angle emphasis", "summary": "Dramatic hero framing underscoring power and victory."}, + "Insert POV": {"prompt": "insert POV shot, detail through character eyes", "summary": "POV insert focusing on critical detail as character sees it."}, + "Crowd Reaction": {"prompt": "crowd reaction shot, ensemble response", "summary": "Group reaction capturing collective emotional beat."}, +} + +COLOR_TONE_OPTIONS = { + "None": {"prompt": "", "summary": "No color tone emphasis."}, + "Warm Tone": {"prompt": "warm color palette, golden hues, inviting saturation", "summary": "Warm palette with golden highlights."}, + "Cool Tone": {"prompt": "cool color palette, blue undertones, serene mood", "summary": "Cool palette with calming tones."}, + "High Saturation": {"prompt": "high saturation, vivid colors, bold intensity", "summary": "Highly saturated color treatment."}, + "Low Saturation": {"prompt": "low saturation, muted palette, cinematic restraint", "summary": "Muted palette with cinematic restraint."}, + "Pastel Tone": {"prompt": "pastel color palette, powdery hues, gentle saturation", "summary": "Soft pastel wash for dreamy, delicate moods."}, + "Neo Noir": {"prompt": "neo-noir palette, electric highlights, deep shadows", "summary": "High-contrast neon noir treatment with moody blacks."}, + "Bioluminescent": {"prompt": "bioluminescent palette, teal-magenta glow, organic luminance", "summary": "Bioluminescent-inspired palette with glowing cool hues."}, + "Sepia Film": {"prompt": "sepia-toned film wash, vintage warmth, gentle fade", "summary": "Vintage sepia palette with analog softness."}, + "Teal & Orange": {"prompt": "teal and orange palette, cinematic contrast, blockbuster grade", "summary": "High-contrast teal/orange pairing for cinematic punch."}, + "Muted Pastel": {"prompt": "muted pastel tones, powdery softness, delicate wash", "summary": "Soft pastel palette dialed down for subtle refinement."}, + "Monochrome Steel": {"prompt": "monochrome steel palette, cool grays, industrial vibe", "summary": "Steely monochrome emphasizing industrial mood."}, + "Iridescent": {"prompt": "iridescent color play, oil-slick spectrum, shifting hues", "summary": "Shimmering palette shifting colors with angle."}, + "Sun-Bleached": {"prompt": "sun-bleached palette, desaturated warmth, desert fade", "summary": "Faded warm palette recalling sun-weathered surfaces."}, + "Candy Pop": {"prompt": "candy pop palette, sugary brights, high-gloss saturation", "summary": "Playful high-saturation palette reminiscent of candy wrappers."}, + "Neon Pulse": {"prompt": "neon pulse palette, electric magentas, cyan highlights", "summary": "Glowing neon palette pulsing with nightlife energy."}, + "Mocha": {"prompt": "mocha palette, creamy browns, cozy latte warmth", "summary": "Comforting brown palette echoing café tones."}, + "Frosted Glass": {"prompt": "frosted glass palette, opaque pastels, milky diffusion", "summary": "Milky translucent palette softening contrast with frost-like hues."}, +} + +MOTION_TYPE_OPTIONS = { + "None": {"prompt": "", "summary": "No specific motion descriptor."}, + "Running": {"prompt": "dynamic running motion, explosive sprint, powerful stride", "summary": "High-energy running sequence."}, + "Skateboarding": {"prompt": "skateboarding trick, ollie, urban street energy", "summary": "Skateboarding stunt with street flair."}, + "Soccer": {"prompt": "soccer play, agile footwork, goal attempt", "summary": "Soccer action with athletic control."}, + "Tennis": {"prompt": "tennis rally, racket swing, athletic lunge", "summary": "Tennis motion with decisive swing."}, + "Ping Pong": {"prompt": "table tennis volley, rapid reflexes", "summary": "Ping pong rally with rapid exchanges."}, + "Skiing": {"prompt": "downhill skiing, carving turns, powder spray", "summary": "Skiing descent with dynamic carving."}, + "Basketball": {"prompt": "basketball drive, crossover dribble, slam dunk", "summary": "Basketball highlight moment."}, + "Football": {"prompt": "football play, sprint downfield, defensive pressure", "summary": "Football play with explosive impact."}, + "Bowl Dance": {"prompt": "bowl dance motion, rhythmic choreography", "summary": "Stylized bowl dance movement."}, + "Cartwheel": {"prompt": "cartwheel, acrobatic rotation, graceful motion", "summary": "Cartwheel showcasing athletic grace."}, + "Hovering": {"prompt": "hovering suspension, weightless balance, gentle drift", "summary": "Weightless hover capturing poised suspension."}, + "Freefall": {"prompt": "freefall descent, gravity rush, wind-torn limbs", "summary": "High-velocity drop frozen mid freefall."}, + "Parkour": {"prompt": "urban parkour vault, wall run, kinetic agility", "summary": "Parkour motion exploding across rooftops and railings."}, + "Aerial Silk": {"prompt": "aerial silk routine, suspended twirl, fluid acrobatics", "summary": "Aerial silks performance capturing graceful aerial control."}, + "Park Skating": {"prompt": "park skating session, grinding rails, flowing lines", "summary": "Skatepark action blending tricks with fluid movement."}, + "Martial Kata": {"prompt": "martial arts kata, precise forms, disciplined strikes", "summary": "Structured martial sequence emphasizing precision and flow."}, + "Ballet Leap": {"prompt": "grand jeté ballet leap, extended lines, airborne grace", "summary": "Classical ballet leap freezing dancer mid-flight."}, + "Free Diving": {"prompt": "free diving descent, streamlined motion, underwater calm", "summary": "Graceful underwater descent captured mid glide."}, + "Breakdance Spin": {"prompt": "breakdance windmill spin, kinetic sweep, dynamic floorwork", "summary": "Explosive breakdance spin radiating urban energy."}, + "Hoverboard Drift": {"prompt": "hoverboard drift, antigrav carve, futuristic glide", "summary": "Futuristic hoverboard carving arcs through the air."}, + "Park Parade": {"prompt": "parade procession, synchronized steps, celebratory rhythm", "summary": "Processional parade motion with coordinated ensemble."}, + "Sword Duel": {"prompt": "sword duel clash, steel arcs, poised footwork", "summary": "Dramatic swordplay with dynamic lunges and parries."}, + "Mountain Climb": {"prompt": "mountain climb ascent, rope work, vertical challenge", "summary": "Climber scaling sheer rock with dynamic upward movement."}, + "Surf Cutback": {"prompt": "surf cutback maneuver, spray arc, fluid carve", "summary": "Surfer executing stylish cutback with water spray."}, + "Aerial Drone": {"prompt": "drone maneuver, looping flight, agile hover", "summary": "Agile drone weaving loops through obstacles."}, + "Samba Dance": {"prompt": "samba dance rhythm, fast footwork, carnival flair", "summary": "Energetic samba steps pulsing with festive rhythm."}, + "Sprinting Relay": {"prompt": "relay sprint handoff, baton exchange, explosive acceleration", "summary": "Team relay sprint capturing baton handoff intensity."}, +} + +ACTION_PRIMARY_OPTIONS = { + "None": {"prompt": "", "summary": "No primary action emphasis."}, + "Heroic Sprint": {"prompt": "explosive sprint, knees driving high, purposeful forward lean", "summary": "Powerful sprinting burst projecting urgency and determination."}, + "Focused Jog": {"prompt": "steady jog, rhythmic breathing, relaxed arm swing", "summary": "Measured jogging pace emphasizing stamina and calm focus."}, + "Casual Walk": {"prompt": "casual walk, easy stride, observant gaze", "summary": "Unhurried walking pace conveying confidence and ease."}, + "Power Stride": {"prompt": "power stride, decisive footfalls, shoulders squared", "summary": "Intentional march underscoring leadership and resolve."}, + "Parade March": {"prompt": "synchronized march, precision cadence, ceremonial posture", "summary": "Disciplined marching cadence suited for procession beats."}, + "Tactical Creep": {"prompt": "controlled prowl, low center of gravity, silent footsteps", "summary": "Slow stealth advance ideal for infiltration storytelling."}, + "Graceful Turn": {"prompt": "graceful pivot, fluid hip rotation, balanced poise", "summary": "Elegant pirouette moment showcasing refined control."}, + "Combat Roll": {"prompt": "combat roll, momentum carry, swift recovery stance", "summary": "Dynamic evasive roll leading into combat readiness."}, + "Acrobatic Flip": {"prompt": "tight aerial flip, tucked knees, precise landing", "summary": "Athletic flip highlighting agility and aerial confidence."}, + "Vault Leap": {"prompt": "vault leap over obstacle, palms planted, legs split", "summary": "Momentum-driven vault clearing barriers with flair."}, + "Wall Run": {"prompt": "wall run, angled sprint, defying gravity", "summary": "Parkour-inspired wall run accentuating daring motion."}, + "Ledge Climb": {"prompt": "vertical climb, fingertips gripping, core engaged", "summary": "Determined ascent capturing grit and physical strength."}, + "Grapple Swing": {"prompt": "grappling swing, cable taut, arc through air", "summary": "Swinging traversal showcasing momentum and heroics."}, + "Hover Drift": {"prompt": "hoverboard drift, counter lean, plasma trail", "summary": "Futuristic glide sequences with dramatic banking."}, + "Aerial Hover": {"prompt": "suspended hover, subtle limb adjustments, anti-grav hum", "summary": "Weightless hover emphasizing controlled stillness."}, + "Sword Combo": {"prompt": "sword combo slash, blade flare, precise follow-through", "summary": "Martial sword sequence brimming with kinetic intent."}, + "Shield Brace": {"prompt": "shield brace, wide stance, impact-ready", "summary": "Protective stance built for blocking heavy blows."}, + "Bow Release": {"prompt": "drawn bow release, focused gaze, taut string snap", "summary": "Archery moment capturing tension and accuracy."}, + "Spell Channel": {"prompt": "channeling spell energy, sigils orbiting, outstretched palm", "summary": "Arcane casting posture radiating cinematic power."}, + "Healing Surge": {"prompt": "healing surge, warm aura, hands hovering over ally", "summary": "Supportive restorative action showcasing compassion."}, + "Tech Interface": {"prompt": "rapid console interface, holographic projections, swift keystrokes", "summary": "High-speed tech interaction driven by intellect."}, + "Drone Pilot": {"prompt": "remote drone coordination, dual sticks, analytical focus", "summary": "Precision drone piloting highlighting dexterity."}, + "Vehicle Drift": {"prompt": "vehicle drift, counter steering, tire smoke curl", "summary": "High-octane driving beat with cinematic drift."}, + "Mech Command": {"prompt": "mech command gestures, haptic controls, cockpit glow", "summary": "Piloting a giant mech with commanding gestures."}, + "Gun Kata": {"prompt": "gun kata flow, synchronized dual pistols, spinning disarm", "summary": "Stylized firearm choreography blending combat and dance."}, + "Cover Slide": {"prompt": "slide into cover, sparks skimming, low ready", "summary": "Tactical slide maneuver transitioning into cover."}, + "Reload Snap": {"prompt": "speed reload snap, magazine swap, decisive click", "summary": "Swift weapon reload underscoring discipline."}, + "Data Hack": {"prompt": "neural hack posture, luminous code stream, intense focus", "summary": "Cyber intrusion beat with immersive hacking visuals."}, + "Creative Sketch": {"prompt": "rapid sketch strokes, charcoal dust, concentrated expression", "summary": "Artistic gesture capturing creativity in motion."}, + "Brush Flourish": {"prompt": "brush flourish across canvas, pigment spray, painterly arc", "summary": "Expressive painting movement bursting with color."}, + "Sculpting Strike": {"prompt": "sculpting chisel strike, stone shards, deliberate rhythm", "summary": "Sculptural carving moment emphasizing craftsmanship."}, + "Conducting Cue": {"prompt": "orchestral conducting cue, baton sweep, poised crescendo", "summary": "Commanding conducting gesture driving musical energy."}, + "Freestyle Dance": {"prompt": "freestyle dance burst, syncopated limbs, playful sync", "summary": "Improvisational dance packed with personality."}, + "Contemporary Flow": {"prompt": "contemporary dance flow, sweeping floor contact, expressive torso", "summary": "Fluid dance sequence mixing control and emotion."}, + "Martial Spar": {"prompt": "martial spar exchange, block and counter, sharpened reflexes", "summary": "Friendly sparring beat balancing offense and defense."}, + "Meditative Breath": {"prompt": "meditative breath cycle, grounded stance, centered palms", "summary": "Stillness-focused breathwork radiating calm strength."}, + "Negotiation Gesture": {"prompt": "open negotiation gesture, hands extended, emphatic cadence", "summary": "Persuasive communication action highlighting diplomacy."}, + "Briefing Command": {"prompt": "mission briefing command stance, holo-map gesture, directive tone", "summary": "Mission leadership beat rallying the team."}, + "Victory Cheer": {"prompt": "victory cheer, arms raised, triumphant shout", "summary": "Celebratory climax conveying success and joy."}, + "Reassuring Embrace": {"prompt": "reassuring embrace, protective posture, supportive warmth", "summary": "Supportive hug emphasizing trust and care."}, + "Rescue Carry": {"prompt": "fireman carry rescue, urgent pace, determined grit", "summary": "Heroic rescue action grounded in strength and urgency."}, + "Scientific Scan": {"prompt": "handheld scanner sweep, sensor readouts, analytical calm", "summary": "Investigative scanning beat revealing discoveries."}, +} + +ACTION_SECONDARY_OPTIONS = { + "None": {"prompt": "", "summary": "No secondary beat."}, + "Evasive Sidestep": {"prompt": "evasive sidestep, dust kick, narrow escape", "summary": "Quick lateral dodge threading through danger."}, + "Counter Strike": {"prompt": "counter strike, sudden riposte, momentum reversal", "summary": "Instant retaliation that flips the battlefield tempo."}, + "Guard Break": {"prompt": "guard break shove, stance disruption, advantage claimed", "summary": "Aggressive shove opening defense lines."}, + "Precision Aim": {"prompt": "precision aim, steady breath hold, laser focus", "summary": "Focused targeting moment capturing suspense."}, + "Scan Sweep": {"prompt": "environmental scan sweep, HUD overlay, alerts ping", "summary": "Data-gathering sweep amplifying situational awareness."}, + "Signal Call": {"prompt": "hand signal callout, coordinated motion, squad synch", "summary": "Silent coordination cue enhancing teamwork."}, + "Shield Raise": {"prompt": "shield raise, sparks deflected, resilience flare", "summary": "Defensive beat underlining protection."}, + "Momentum Slide": {"prompt": "momentum slide, low friction drift, sparks trailing", "summary": "Stylized slide maintaining kinetic energy."}, + "Ground Pound": {"prompt": "ground pound impact, shockwave ring, dust burst", "summary": "Impactful finishing beat showcasing raw force."}, + "Air Twist": {"prompt": "aerial twist kick, suspended rotation, dramatic follow-through", "summary": "Signature kick sequence loaded with flair."}, + "Grapple Lock": {"prompt": "grapple lock, leverage shift, controlled takedown", "summary": "Submission-oriented maneuver demonstrating technique."}, + "Companion Assist": {"prompt": "companion assist boost, interlocked hands, cooperative lift", "summary": "Supportive teammate beat accentuating trust."}, + "Barrier Deploy": {"prompt": "deployable barrier throw, emitter unfold, cover established", "summary": "Instant cover creation for tactical repositioning."}, + "Drone Launch": {"prompt": "drone launch flick, rotor ignition, rapid ascent", "summary": "Secondary action launching support drone units."}, + "Med-Pack Toss": {"prompt": "med-pack toss, side-arm throw, lifesaving delivery", "summary": "Supportive aid thrown to allies mid-conflict."}, + "Energy Burst": {"prompt": "energy burst discharge, luminous surge, crackling aftermath", "summary": "Secondary flare of power emphasizing impact."}, + "Telemetry Check": {"prompt": "telemetry check, wrist holo, data validation", "summary": "Brief systems check ensuring mission alignment."}, + "Environmental Interaction": {"prompt": "interactive environment cue, trigger pull, machinery response", "summary": "Action tied to world mechanics and set pieces."}, + "Emotional Accent": {"prompt": "emotional accent glance, micro-expression, character depth", "summary": "Subtle emotional beat layered into action."}, +} + +ACTION_TEMPO_OPTIONS = { + "None": {"prompt": "", "summary": "Neutral tempo."}, + "Slow And Steady": {"prompt": "slow and steady tempo, measured steps, deliberate pace", "summary": "Calm, controlled tempo emphasizing precision."}, + "Erotic Pulse": {"prompt": "erotic pulse tempo, sultry sway, rhythmic hips", "summary": "Sensual tempo emphasizing allure and intimacy."}, + "Erotic Flow": {"prompt": "erotic flow tempo, fluid transitions, languid movement", "summary": "Smooth, flowing tempo highlighting sensuality."}, + "Slow Burn": {"prompt": "slow burn cadence, deliberate pacing, controlled breath", "summary": "Measured tempo suited for stealth or tension building."}, + "Rising Momentum": {"prompt": "rising momentum, energy ramp, quickening steps", "summary": "Tempo increase signaling imminent breakthrough."}, + "Pulse Pounding": {"prompt": "pulse-pounding rhythm, heartbeat percussion, kinetic urgency", "summary": "High-tempo drive keeping adrenaline elevated."}, + "Frantic Rush": {"prompt": "frantic rush, untamed velocity, scattered sparks", "summary": "Chaotic tempo perfect for chase sequences."}, + "Steady Cadence": {"prompt": "steady cadence, balanced breathing, tactical precision", "summary": "Even tempo highlighting discipline and stamina."}, + "Staccato Beats": {"prompt": "staccato beats, rapid-fire cuts, short bursts of motion", "summary": "Choppy tempo ideal for montage or combat edits."}, + "Weightless Drift": {"prompt": "weightless drift tempo, slow-floating beats, elongated frames", "summary": "Dreamlike tempo emphasizing suspended motion."}, + "Heroic Crescendo": {"prompt": "heroic crescendo, swelling momentum, triumphant surge", "summary": "Epic tempo climb culminating in a victorious beat."}, + "Sneaking Pulse": {"prompt": "sneaking pulse, low heart rate, suppressed motion", "summary": "Subtle tempo maintaining stealth tension."}, +} + +ACTION_INTERACTION_OPTIONS = { + "None": {"prompt": "", "summary": "Solo focus."}, + "Flirty Bounce": {"prompt": "flirty bounce, playful winks, teasing smiles", "summary": "Lighthearted interaction with a playful edge."}, + "Solo Focus": {"prompt": "solo-focused action, spotlight isolated performer", "summary": "Highlights individual capability without distractions."}, + "Duo Synchrony": {"prompt": "duo synchrony, mirrored movements, interlocking timing", "summary": "Two-person coordination conveying trust and chemistry."}, + "Squad Coordination": {"prompt": "squad coordination, layered formation, call-and-response", "summary": "Team-based action balanced across multiple performers."}, + "Crowd Navigation": {"prompt": "crowd navigation, weaving through onlookers, reactive adjustments", "summary": "Action threaded through dense crowd dynamics."}, + "Rival Showdown": {"prompt": "rival showdown, charged eye-line, matched intensity", "summary": "Dramatic confrontation between evenly matched opponents."}, + "Mentor Guidance": {"prompt": "mentor guidance, supportive coaching, corrective touch", "summary": "Instructional beat where mentor refines protege technique."}, + "Rescue Support": {"prompt": "rescue support, stabilizing assist, collaborative lift", "summary": "Mutual aid moment during high-stakes scenario."}, + "Celebration Group": {"prompt": "celebration group embrace, shared triumph, collective cheer", "summary": "Group celebration amplifying emotional payoff."}, + "Spectator Engagement": {"prompt": "spectator engagement, audience interaction, responsive energy", "summary": "Performer interacting directly with enthusiastic crowd."}, + "Tactical Tandem": {"prompt": "tactical tandem, overlapping cover fire, seamless transitions", "summary": "Combat partnership moving as a unified unit."}, +} + +NSFW_ACTION_PRIMARY_OPTIONS = { + "None": {"prompt": "", "summary": "No specific mature action emphasis."}, + "sensual_fingering": {"prompt": "sensual_fingering, teasing touch, deliberate pace", "summary": "Slow, teasing finger play emphasizing sensation."}, + "dildoplay": {"prompt": "dildoplay, rhythmic thrusts, focused attention", "summary": "Intentional dildo use highlighting control and pleasure."}, + "dildo plunge": {"prompt": "dildo plunge, deep thrusts, intense focus", "summary": "Deep, purposeful dildo penetration emphasizing intensity."}, + "striptease": {"prompt": "striptease, slow fabric removal, teasing reveal", "summary": "Flirtatious undressing emphasizing anticipation."}, + "Lap Dance On A Pillow": {"prompt": "lap dance on a pillow, sultry grind, intimate proximity", "summary": "Sensual lap dance focusing on close contact."}, + "nipple_play": {"prompt": "nipple play, gentle pinches, teasing twists", "summary": "Delicate nipple stimulation enhancing arousal."}, + "breast_play": {"prompt": "breast play, cupping hands, soft squeezes", "summary": "Tender breast handling emphasizing pleasure."}, + "butt_play": {"prompt": "butt play, firm grabs, playful spanks", "summary": "Cheeky butt interaction blending fun and sensation."}, + "thigh_play": {"prompt": "thigh play, light strokes, teasing caress", "summary": "Sensual thigh touch building anticipation."}, + "intimate_kiss": {"prompt": "intimate kiss, parted lips, lingering tongue", "summary": "Deep, passionate kissing emphasizing connection."}, + "deep_throat": {"prompt": "deep throat, wide gape, focused eye contact", "summary": "Intense oral engagement highlighting depth and control."}, + "oral_tease": {"prompt": "oral tease, soft licks, anticipatory nibbles", "summary": "Playful oral teasing building anticipation."}, + "intimate_dance": {"prompt": "intimate_dance, close body contact, rhythmic sway", "summary": "Close-contact dance highlighting mutual attraction."}, + "tease_undress": {"prompt": "tease undress, fabric slide, playful reveal", "summary": "Flirtatious wardrobe removal emphasizing anticipation."}, + "Sensual Caress": {"prompt": "sensual caress, fingertips tracing curves, lingering warmth", "summary": "Tender caress lingering over consenting adult partner."}, + "Slow Undress": {"prompt": "slow undress, deliberate fabric slide, teasing reveal", "summary": "Gradual wardrobe peel emphasizing anticipation."}, + "Passionate Embrace": {"prompt": "passionate embrace, bodies pressed, breath mingling", "summary": "Full-body embrace radiating mutual desire."}, + "Intimate Sway": {"prompt": "intimate sway, hips aligned, subtle grind", "summary": "Synchronized sway capturing shared rhythm."}, + "Seductive Strut": {"prompt": "seductive strut, confident hips, smoldering gaze", "summary": "Confident walk engineered to entice."}, + "Sultry Floorwork": {"prompt": "sultry floorwork, rolling shoulders, arched back", "summary": "Floor routine showcasing curves and fluid control."}, + "Chair Tease": {"prompt": "chair straddle tease, playful lean back, fingertip glide", "summary": "Chair-focused routine blending tease and control."}, + "Lap Dance Flourish": {"prompt": "lap dance flourish, measured grind, eye contact", "summary": "Exploratory lap dance centered on consent and connection."}, + "Wall Pin": {"prompt": "pinned to wall, dominant arm brace, electric tension", "summary": "Consensual wall pin layering controlled dominance."}, + "Silk Rope Display": {"prompt": "silk rope display, decorative binds, supportive suspension", "summary": "Artful rope styling highlighting trust and safety."}, + "Blindfold Reveal": {"prompt": "blindfold reveal, silk slip, heightened senses", "summary": "Playful sensory deprivation moment building anticipation."}, + "Oil Massage": {"prompt": "warm oil massage, glistening skin, affectionate knead", "summary": "Sensual massage exchanging comfort and pleasure."}, + "Body Trace": {"prompt": "body tracing, nails grazing, deliberate pace", "summary": "Slow tracing that savors every contour thoughtfully."}, + "Corset Tighten": {"prompt": "corset tightening, breathy gasp, mirrored gaze", "summary": "Intimate wardrobe cinch celebrating curves."}, + "Stocking Roll": {"prompt": "stocking roll, thigh-high descent, playful smirk", "summary": "Tempting hosiery peel that spotlights legs."}, + "Garter Snap": {"prompt": "garter snap tease, playful sting, laughing eyes", "summary": "Cheeky garter snap punctuating flirtatious energy."}, + "Mirror Worship": {"prompt": "mirror worship pose, self-admiring touch, glossy lighting", "summary": "Self-love moment honoring adult confidence."}, + "Bath Embrace": {"prompt": "shared bubble bath embrace, water shimmer, relaxed smiles", "summary": "Steamy bath cuddle under soft candlelight."}, + "Dominant Command": {"prompt": "dominant command stance, guiding touch, steady gaze", "summary": "Command-driven beat emphasizing trusted leadership."}, + "Submissive Offer": {"prompt": "submissive offer, kneeling poise, open palms", "summary": "Reverent offering posture celebrating mutual trust."}, + "Morning Glow": {"prompt": "morning-after glow, sheet drape, satisfied stretch", "summary": "Post-intimacy glow capturing tenderness and safety."}, + "rope_bondage": {"prompt": "rope_bondage, intricate knots, secure ties", "summary": "Artful rope bondage emphasizing safety and aesthetics."}, + "Feather Tease": {"prompt": "feather tease, light strokes, playful shivers", "summary": "Delicate feather touch eliciting gentle reactions."}, + "Ice Play": {"prompt": "ice play, cool glide, sharp contrast", "summary": "Icy touch introducing thrilling temperature play."}, + "Candle Drip": {"prompt": "candle wax drip, slow melt, heated skin", "summary": "Sensual wax play balancing warmth and sensation."}, + "Spanking": {"prompt": "spanking rhythm, open palm, playful smirk", "summary": "Consensual spanking beat blending fun and sensation."}, + "Hair Pull": {"prompt": "hair pull tease, gentle tug, sultry glance", "summary": "Controlled hair pull enhancing connection."}, + "Collar Clasp": {"prompt": "collar clasp, firm grip, submissive tilt", "summary": "Symbolic collaring moment underscoring roles."}, + "Cuffs Click": {"prompt": "cuffs click, wrist restraint, daring smile", "summary": "Playful cuffing action highlighting consensual restraint."}, + +} + +NSFW_ACTION_SECONDARY_OPTIONS = { + "None": {"prompt": "", "summary": "No secondary mature beat."}, + "Touch Genitals": {"prompt": "touching genitals, teasing caress, deliberate pace", "summary": "Focused genital touch emphasizing sensation."}, + "dildoplay": {"prompt": "dildoplay, rhythmic thrusts, focused attention", "summary": "Intentional dildo use highlighting control and pleasure."}, + "dildo plunge": {"prompt": "dildo plunge, deep thrusts, intense focus", "summary": "Deep, purposeful dildo penetration emphasizing intensity."}, + "Body Grind": {"prompt": "body grind, close contact, rhythmic hips", "summary": "Full-body grind highlighting mutual attraction."}, + "Lip Bite": {"prompt": "soft lip bite, playful tension, smoldering glance", "summary": "Subtle lip bite amplifying chemistry."}, + "Neck Kiss": {"prompt": "neck kiss, lingering breath, gentle tilt", "summary": "Neck kiss moment underscoring intimacy."}, + "Ear Whisper": {"prompt": "ear whisper tease, warm breath, secretive smile", "summary": "Close whisper sharing private promises."}, + "Hair Sweep": {"prompt": "hair sweep aside, revealing collarbone, seductive focus", "summary": "Hair sweep opening the frame to more skin."}, + "Finger Trail": {"prompt": "fingertip trail along spine, shiver ripple", "summary": "Slow finger trail sending shivers down the spine."}, + "Ribbon Tug": {"prompt": "ribbon tug release, fluttering fabric, playful gasp", "summary": "Ribbon pull unlocking the next reveal."}, + "Stocking Adjust": {"prompt": "stocking adjustment, deliberate roll, thigh emphasis", "summary": "Adjusting stockings to heighten leg focus."}, + "Gaze Hold": {"prompt": "gaze lock, dilated pupils, whispered invitation", "summary": "Mutual gaze hold that communicates consent clearly."}, + "Hand Restraint": {"prompt": "gentle wrist restraint, supportive grip, caring check-in", "summary": "Soft restraint paired with constant reassurance."}, + "Body Dip": {"prompt": "dramatic body dip, partner support, arched torso", "summary": "Supported dip highlighting trust and strength."}, + "Hip Grind": {"prompt": "hip grind accent, rolling motion, synchronized breath", "summary": "Rhythmic hip grind deepening sensual flow."}, + "Glove Peel": {"prompt": "glove peel, fingertip tease, slow reveal", "summary": "Slow glove peel coaxing attention to touch."}, + "Perfume Mist": {"prompt": "perfume mist spray, lingering scent, coy smile", "summary": "Scented flourish prepping for closeness."}, + "Velvet Stroke": {"prompt": "velvet glove stroke along torso, indulgent pace", "summary": "Soft fabric glide delighting sensitive skin."}, + "Silk Caress": {"prompt": "silk scarf caress, fluid motion, teasing glide", "summary": "Silk touch enhancing tactile sensation."}, + "Body Arch": {"prompt": "body arch, extended lines, breathy moan", "summary": "Full-body arch emphasizing curves and openness."}, + "Thigh Squeeze": {"prompt": "thigh squeeze, firm grip, shared smile", "summary": "Mutual thigh squeeze reinforcing connection."}, + "Knee Spread": {"prompt": "knee spread, grounded base, inviting posture", "summary": "Open knee stance signaling readiness and consent."}, + "Ankle Tie": {"prompt": "ankle tie, secure knot, supportive stance", "summary": "Ankle bondage emphasizing safety and trust."}, + "Body Slide": {"prompt": "body slide, close contact, synchronized rhythm", "summary": "Full-body slide enhancing shared motion."}, + "Teasing Flick": {"prompt": "teasing flick with feather, light touch, playful grin", "summary": "Feather flick eliciting light, pleasurable reactions."}, + "Ice Trace": {"prompt": "ice cube trace along skin, cool contrast, shiver response", "summary": "Icy glide creating thrilling temperature shifts."}, + "Wax Drip": {"prompt": "candle wax drip, slow melt, heated skin", "summary": "Sensual wax drop balancing warmth and sensation."}, + +} + +NSFW_ACTION_TEMPO_OPTIONS = { + "None": {"prompt": "", "summary": "Neutral tempo for mature scene."}, + "orgasmic_rush": {"prompt": "orgasmic rush tempo, escalating pace, breathless energy", "summary": "Rapid tempo capturing peak intensity."}, + "teasing_tempo": {"prompt": "teasing tempo, slow build, tantalizing rhythm", "summary": "Slow, tantalizing tempo emphasizing anticipation."}, + + "Slow Tease": {"prompt": "slow tease tempo, patient pacing, suspended breath", "summary": "Lingering tempo that savors every second."}, + "Languid Flow": {"prompt": "languid flow, fluid transitions, mellow rhythm", "summary": "Relaxed cadence for indulgent sequences."}, + "Measured Build": {"prompt": "measured build, rising heat, controlled escalation", "summary": "Gradual crescendo balancing excitement."}, + "Playful Bounce": {"prompt": "playful bounce, upbeat flirtation, lively pace", "summary": "Light-hearted tempo packed with charm."}, + "Breathless Waves": {"prompt": "breathless wave tempo, rising and crashing energy", "summary": "Repeating waves of intensity and release."}, + "Urgent Clutch": {"prompt": "urgent clutch tempo, hungry pace, desperate energy", "summary": "Fast pace fueled by mutual craving."}, + "Commanded Pace": {"prompt": "commanded pace, deliberate cues, guided rhythm", "summary": "Dominant partner dictating tempo with care."}, + "Hypnotic Loop": {"prompt": "hypnotic loop, repeating pattern, trance-like focus", "summary": "Repetitive motions that mesmerize audience."}, + "Tender Pause": {"prompt": "tender pause beats, held breaths, eye contact linger", "summary": "Strategic pauses that celebrate connection."}, +} + +CAMZ_PERSONA_OPTIONS = { + "Soft Glam Muse": { + "prompt": "soft glam cam performer, luminous skin, encouraging smile, relaxed confidence", + "notes": "Lean into approachable warmth blended with polished glam cues.", + }, + "Edgy Vixen": { + "prompt": "edgy cam vixen, smoky eye makeup, bold lip, provocative poses", + "notes": "Combine high-contrast lighting with daring wardrobe for impact.", + }, + "Boho Dreamer": { + "prompt": "boho cam enchantress, natural makeup, flowing fabrics, earthy tones", + "notes": "Use soft, diffused light and organic textures to enhance free-spirit vibe.", + }, + "Nude Preformer": { + "prompt": "nude cam performer, confident posture, direct eye contact, natural beauty", + "notes": "Highlight skin tones with warm lighting and minimal distractions.", + }, + + "Nude Lingerie": { + "prompt": "nude lingerie cam performer, delicate lace, soft lighting, inviting expression", + "notes": "Blend sensual fabrics with flattering light to enhance allure.", + }, + + "Legs Up": { + "prompt": "legs up position, open posture, inviting gaze", + "notes": "Emphasize vulnerability and openness with body language.", + }, + + "Genital Focus": { + "prompt": "genital focus, explicit detail, intimate angle", + "notes": "Use close-up framing and sharp focus to highlight anatomy.", + }, + + "Knee Spread": { + "prompt": "knee spread position, open posture, inviting gaze", + "notes": "Emphasize vulnerability and openness with body language.", + }, + + + "Gamer Girl": { + "prompt": "gamer girl cam performer, playful expressions, gaming gear, vibrant colors", + "notes": "Incorporate RGB lighting and interactive overlays for dynamic energy.", + }, + + "Cyber Starlet": { + "prompt": "futuristic cyber cam muse, neon accent liner, playful tech accessories", + "notes": "Pair shimmering highlights with synth-forward ambience for modern edge.", + }, + "Fitness Tease": { + "prompt": "athletic cam goddess, toned silhouette, playful flex moments", + "notes": "Incorporate light workout props and confident body language cues.", + }, + "Sultry Jazz": { + "prompt": "jazz lounge cam siren, velvety voice, deliberate slow gestures", + "notes": "Match low tempo jazz soundtrack with honeyed lighting for intimacy.", + }, + + "Classic Burlesque": { + "prompt": "classic burlesque cam performer, vintage glamour, theatrical poses", + "notes": "Use spotlighting and rich textures to evoke old-school allure.", + }, +} + +CAMZ_STAGE_OPTIONS = { + "Neon Loft": { + "prompt": "neon loft backdrop, led strip accents, reflective plexi floor", + "notes": "Balance saturated highlights with soft fill to flatter skin.", + }, + "Velvet Boudoir": { + "prompt": "crimson velvet boudoir set, fairy lights, plush textures", + "notes": "Layer candles and warm bulbs to deepen romantic ambience.", + }, + "Glow Studio": { + "prompt": "glow studio backdrop, gradient colorwash, cloud diffuser panels", + "notes": "Use color cycling and haze for dreamy, stylized streaming look.", + }, + "Gamer Den": { + "prompt": "rgb gamer desk, holographic screens, soft plush seating", + "notes": "Mix animated overlays with keyboard glow for interactive energy.", + }, + "Minimalist Chic": { + "prompt": "minimalist chic set, clean lines, monochrome palette", + "notes": "Emphasize simplicity and elegance with strategic lighting." + }, + "Bedroom Retreat": { + "prompt": "cozy bedroom retreat, soft linens, ambient string lights", + "notes": "Create a warm, inviting atmosphere with layered textiles and soft lighting.", + }, + "Outdoor Oasis": { + "prompt": "lush outdoor oasis, tropical plants, twinkling fairy lights", + "notes": "Create a serene, nature-inspired backdrop with soft, diffused lighting.", + }, + "Regular Bedroom": { + "prompt": "regular bedroom setting, casual decor, natural lighting", + "notes": "Incorporate personal touches and soft lighting for a lived-in feel.", + }, + "messy_bedroom": { + "prompt": "messy bedroom setting, casual decor, natural lighting", + "notes": "Capture the lived-in feel with unmade beds and personal items.", + }, + "Gamer Bedroom": { + "prompt": "gamer bedroom setup, gaming posters, rgb lighting", + "notes": "Incorporate dynamic lighting and gaming elements for an immersive vibe.", + }, +} + + +CAMZ_CAMERA_OPTIONS = { + "Intimate Close": { + "prompt": "intimate close framing, direct lens eye contact, creamy bokeh", + "notes": "Keep lens near eye level for immediate connection and sparkle.", + }, + "Desk Peek": { + "prompt": "desk peek composition, slightly elevated perspective, flattering angle", + "notes": "Angle camera from high-left to emphasize eyes and decolletage.", + }, + "Overhead View": { + "prompt": "overhead cam angle, top-down view, dynamic foreshortening", + "notes": "Use wide lens to capture full figure and engaging perspective.", + }, + "Shoulder Cam": { + "prompt": "shoulder cam angle, partial profile, soft focus", + "notes": "Create a dreamy, intimate vibe with gentle focus and framing.", + }, + "Full Body": { + "prompt": "full body cam frame, full-length mirror, responsive lighting", + "notes": "Stage lighting evenly to avoid clipping during motion demos.", + }, + "Dynamic Dolly": { + "prompt": "slow dolly glide, remote gimbal, smooth parallax", + "notes": "Automate subtle motion to keep stream kinetic without distraction.", + }, +} + +CAMZ_ENERGY_OPTIONS = { + + "Sexy Bounce": { + "prompt": "sexy bounce, rhythmic hip sway, playful smiles", + "notes": "Incorporate playful expressions and dynamic angles for added allure.", + }, + + "Gentle Sway": { + "prompt": "gentle swaying motion, soft giggles, tender pacing", + "notes": "Use lingering gestures and ASMR whisper tones for relaxation.", + }, + "Flirty Bounce": { + "prompt": "flirty bounce, playful winks, teasing smiles", + "notes": "Incorporate light, rhythmic music and interactive chat games.", + }, + "Grind Groove": { + "prompt": "sensual grind, rhythmic hip sway, sultry eye contact", + "notes": "Sync movements to mid-tempo beats for hypnotic allure.", + }, + "To The Music": { + "prompt": "music-driven moves, beat sync, expressive dance, energetic vibe, hips move to the beat", + "notes": "Encourage spontaneous movement and playful interaction with the rhythm.", + }, + "Hype Spark": { + "prompt": "high-energy bounce, playful chatter, animated laughter", + "notes": "Cue upbeat playlists and interactive games for tip spikes.", + }, + "Slow Burn": { + "prompt": "slow-burn tease, deliberate pauses, smoldering eye contact", + "notes": "Layer progressive reveals with countdown widgets for tension.", + }, + "Command Mode": { + "prompt": "commanding domme cadence, confident directives, teasing coaching", + "notes": "Balance assertive tone with check-ins on boundaries and comfort.", + }, +} + +NSFW_ACTION_INTERACTION_OPTIONS = { + "None": {"prompt": "", "summary": "Solo highlight by default."}, + "dildoplay": {"prompt": "dildoplay, rhythmic thrusts, focused attention", "summary": "Intentional dildo use highlighting control and pleasure."}, + "dildo plunge": {"prompt": "dildo plunge, deep thrusts, intense focus", "summary": "Deep, purposeful dildo penetration emphasizing intensity."}, + "Couple's Dance": {"prompt": "couple's dance, synchronized sway, intimate proximity", "summary": "Two-person dance emphasizing connection and chemistry."}, + "Mutual Tease": {"prompt": "mutual tease, playful touches, shared smiles", "summary": "Interactive teasing between consenting adults."}, + "Shared Focus": {"prompt": "shared focus, alternating attention, balanced engagement", "summary": "Equal spotlight on both participants."}, + "Playful Interaction": {"prompt": "playful interaction, light touches, teasing glances", "summary": "Fun, flirtatious exchanges between partners."}, + "Intimate Connection": {"prompt": "intimate connection, close body contact, lingering gazes", "summary": "Deep, affectionate moments highlighting emotional bond."}, + "Consensual Exploration": {"prompt": "consensual exploration, mutual discovery, open communication", "summary": "Partners exploring each other's desires with clear consent."}, + "Teasing Dynamics": {"prompt": "teasing dynamics, push and pull, playful tension", "summary": "Flirtatious back-and-forth emphasizing chemistry."}, + "Sensual Mirror": {"prompt": "sensual mirror play, synchronized movements, reflective allure", "summary": "Partners mirroring each other's sensuality."}, + "Sensual Fingering": {"prompt": "sensual_fingering, teasing touch, 2 fingers inside, deliberate pace", "summary": "Solo finger play emphasizing sensation."}, + "Solo Admiration": {"prompt": "solo admiration, self-touch, reflective gaze", "summary": "Focuses on individual self-appreciation and confidence."}, + "Solo Self-Admiration": {"prompt": "solo self-admiration, mirrored touch, confident posture", "summary": "Spotlights a single adult indulging in self-appreciation."}, + "Mirror Performance": {"prompt": "mirror performance, dual reflections, choreographed tease", "summary": "Performer using mirrors to amplify allure."}, + "Mutual Embrace": {"prompt": "mutual embrace, intertwined limbs, tender reciprocity", "summary": "Two consenting adults wrapped in equal affection."}, + "Dominant Lead": {"prompt": "dominant lead, guiding hand, steady reassurance", "summary": "Confident lead steering an enthusiastic partner."}, + "Submissive Offering": {"prompt": "submissive offering, lowered gaze, trusting openness", "summary": "Trust-forward posture celebrating consensual vulnerability."}, + "Power Switch": {"prompt": "power exchange switch, playful role reversal", "summary": "Partners swapping roles with delighted consent."}, + "Voyeur Tease": {"prompt": "voyeur tease, aware of onlookers, controlled reveal", "summary": "Flirtatious performance directed at an invited audience."}, + "Stage Showcase": {"prompt": "stage showcase, spotlighted solo, theatrical flourish", "summary": "Burlesque-style spotlight designed for adoring fans."}, + "Lap Dance Delivery": {"prompt": "lap dance delivery, partner seated, performer leading", "summary": "Classic lap dance built on clear boundaries."}, + "Sensual Massage Exchange": {"prompt": "mutual massage exchange, alternating focus, satisfied sighs", "summary": "Partners trading soothing touch in balanced turns."}, + "Tandem Pose": {"prompt": "tandem pose alignment, sculpted silhouettes, mutual balance", "summary": "Dual posing moment engineered for visual harmony."}, +} + +EMOTION_OPTIONS = { + "None": {"prompt": "", "summary": "No explicit emotional cue."}, + "Anger": {"prompt": "fiery anger, clenched expression, intense gaze", "summary": "Emotion of anger and determination."}, + "Pleasure": {"prompt": "pleasure, satisfied smile, relaxed demeanor", "summary": "Contentment and enjoyment."}, + "Slight sadness": {"prompt": "slight sadness, downturned eyes, soft sigh", "summary": "Subtle sadness with a hint of vulnerability."}, + "A Little Fear": {"prompt": "a little fear, cautious eyes, hesitant posture", "summary": "Mild fear and apprehension."}, + "Fear": {"prompt": "subtle fear, wide eyes, tense posture", "summary": "Emotion of fear and uncertainty."}, + "Joy": {"prompt": "radiant joy, bright smile, delighted energy", "summary": "Joyful optimism."}, + "Sadness": {"prompt": "somber sadness, reflective emotion", "summary": "Quiet sadness and introspection."}, + "Surprise": {"prompt": "surprised reaction, widened expression", "summary": "Emotion of surprise and wonder."}, + "Determination": {"prompt": "steely determination, set jaw, unwavering focus", "summary": "Focused resolve radiating grit and purpose."}, + "Serenity": {"prompt": "calm serenity, relaxed features, centered presence", "summary": "Peaceful composure with gentle poise."}, + "Elation": {"prompt": "bursting elation, effervescent grin, expressive sparkle", "summary": "Effusive elation spilling over with joy."}, + "Stoic Resolve": {"prompt": "stoic resolve, guarded gaze, iron composure", "summary": "Quiet, unshakable resolve under pressure."}, + "Wonder": {"prompt": "awestruck wonder, widened eyes, open breath", "summary": "Fresh awe illuminating expression with curiosity."}, + "Melancholy": {"prompt": "gentle melancholy, softened gaze, reflective sorrow", "summary": "Quiet melancholy with tender introspection."}, + "Euphoria": {"prompt": "surging euphoria, lifted arms, unstoppable joy", "summary": "High-energy euphoria bursting with celebratory glee."}, + "Determined Calm": {"prompt": "determined calm, focused breathing, ready poise", "summary": "Calm exterior masking fierce determination."}, + "Playfulness": {"prompt": "playful mischief, sly smile, twinkling eyes", "summary": "Lighthearted playfulness teasing at spontaneity."}, + "Grief": {"prompt": "profound grief, tear-lined cheeks, trembling lip", "summary": "Visceral grief communicated through fragile posture."}, + "Triumph": {"prompt": "triumphant exhale, victorious grin, fists raised", "summary": "Post-victory triumph radiating unabashed pride."}, + "Suspicion": {"prompt": "suspicious glance, narrowed eyes, tightened posture", "summary": "Guarded suspicion hinting at underlying tension."}, + "Zen": {"prompt": "zen tranquility, serene smile, centered breathing", "summary": "Meditative serenity balancing body and mind."}, + "Frustration": {"prompt": "frustration, clenched jaw, furrowed brow", "summary": "Building frustration conveyed through tense features."}, +} + +CAMERA_BASIC_OPTIONS = { + "None": {"prompt": "", "summary": "No basic camera move."}, + "Dolly In": {"prompt": "dolly in, smooth forward track, subject focus", "summary": "Forward dolly move centering subject."}, + "Push-in": {"prompt": "camera push-in, forward dolly, tightening focus", "summary": "Push-in move for increasing intensity."}, + "Pull-out": {"prompt": "camera pull-out, backward dolly, reveal context", "summary": "Pull-out move to reveal context."}, + "Pan Right": {"prompt": "pan right, lateral camera sweep", "summary": "Pan right following subject motion."}, + "Pan Left": {"prompt": "pan left, lateral follow", "summary": "Pan left tracking movement."}, + "Tilt Up": {"prompt": "tilt up, vertical reveal", "summary": "Tilt up uncovering grandeur."}, + "Crane Sweep": {"prompt": "crane sweep, rising arc, cinematic reveal", "summary": "Elevated crane arc that lifts to reveal scope."}, + "Whip Pan": {"prompt": "whip pan, rapid pivot, motion blur streaks", "summary": "Snap pan creating dynamic motion streaks."}, + "Push-Pull": {"prompt": "push-pull dolly zoom, perspective warp, tension swell", "summary": "Push-pull move warping background scale for dramatic tension."}, + "Pedestal Rise": {"prompt": "pedestal rise, vertical travel, unveiling scale", "summary": "Vertical pedestal move revealing layers of the scene."}, + "Pedestal Drop": {"prompt": "pedestal drop, descending move, revealing height", "summary": "Vertical descent revealing elevated vantage down to subject."}, + "Push Tilt": {"prompt": "push-in with tilt, converging axes, dynamic reveal", "summary": "Combined push and tilt creating layered dramatic emphasis."}, + "Dolly Out": {"prompt": "dolly out, expanding context, breath of scale", "summary": "Backward dolly expanding environment around subject."}, + "Arc Sweep": {"prompt": "arc sweep, curved track move, wraparound", "summary": "Semi-circular move hugging subject to show dimensionality."}, + "Static Hold": {"prompt": "static hold, motionless frame, patience", "summary": "Deliberate static shot sustaining tension through stillness."}, + "Push Past": {"prompt": "push past subject, handoff focus, motivated move", "summary": "Camera slides past subject to reveal new focal point."}, + "Rise and Drift": {"prompt": "vertical rise with lateral drift, floating move", "summary": "Combined vertical and lateral move producing gentle drift."}, + "Whip Tilt": {"prompt": "whip tilt, rapid vertical pivot, dynamic smear", "summary": "Fast tilt generating energetic motion blur streaks."}, + "First Person Perspective": {"prompt": "first-person perspective, immersive viewpoint, direct engagement", "summary": "POV angle placing viewer in character's shoes for intimacy."}, +} + +CAMERA_ADVANCED_OPTIONS = { + "None": {"prompt": "", "summary": "No advanced camera move."}, + "Handheld": {"prompt": "handheld camera, organic sway, documentary feel", "summary": "Handheld movement for visceral texture."}, + "Compound": {"prompt": "compound move, layered dolly and pan", "summary": "Compound move blending multiple axes."}, + "Following": {"prompt": "dynamic follow shot, tracking movement", "summary": "Following shot tracking the subject."}, + "Orbit": {"prompt": "orbiting camera, circular move, wraparound view", "summary": "Orbiting move for immersive coverage."}, + "Steadicam Glide": {"prompt": "steadicam glide, fluid tracking, stabilized motion", "summary": "Stabilized glide preserving fluid, anchored movement."}, + "Cable Cam Chase": {"prompt": "cable cam chase, high-speed lateral track, aerial pursuit", "summary": "High-speed cable run carving dynamic parallax."}, + "Gimbal Glide": {"prompt": "gimbal glide, balanced footsteps, cinematic float", "summary": "Motorized gimbal smooths footsteps for floating camera energy."}, + "VR Orbit": {"prompt": "immersive vr orbit, spherical capture, wraparound viewpoint", "summary": "VR-ready orbit delivering 360° spatial immersion."}, + "Jib Cascade": {"prompt": "jib cascade, layered vertical arcs, floaty transitions", "summary": "Multi-level jib move creating cascading vertical arcs."}, + "Snorricam": {"prompt": "snorricam mount, subject-locked frame, disorienting world", "summary": "Camera rigidly mounted to subject keeping them static while world moves."}, + "Gimbal Sprint": {"prompt": "gimbal sprint, stabilized run, pursuit energy", "summary": "Stabilized sprint following subject through chaotic motion."}, + "Cable Drop": {"prompt": "cable cam drop, high-to-low plunge, overhead chase", "summary": "Cable cam diving from altitude toward ground-level action."}, + "Drone Orbit": {"prompt": "drone orbit, aerial wrap, wide-scale coverage", "summary": "Drone encircling target for expansive orbital perspective."}, + "Technocrane Lash": {"prompt": "technocrane lash, sweeping extension, cinematic flourish", "summary": "Long reach crane delivering dramatic swoops and lashes."}, + "Handheld Crash": {"prompt": "handheld crash zoom, frenetic push, documentary grit", "summary": "Handheld crash zoom injecting adrenaline and immediacy."}, + "Steadicam Stair": {"prompt": "steadicam stair descent, floating steps, continuous move", "summary": "Steadicam gliding down stairs maintaining smooth coverage."}, + "Robot Arm": {"prompt": "robot arm precision move, programmable path, high-speed control", "summary": "Motion-control robot arm executing precise, repeatable camera paths."}, + "Helmet Cam POV": {"prompt": "helmet-cam first-person view, breath fog, immersive dash", "summary": "Head-mounted POV aligning the viewer with the character's gaze."}, + "Body Cam Pulse": {"prompt": "body-mounted camera POV, chest-level jitter, urgent micro-shake", "summary": "Chest-mounted camera delivering raw first-person immediacy."}, + "Gauntlet POV Sweep": {"prompt": "weapon gauntlet POV, hands in frame, kinetic swings", "summary": "First-person gauntlet view keeping hands and weapon in frame."}, + "Shoulder Rig POV": {"prompt": "shoulder-rig POV, reactive head sway, human-scale perspective", "summary": "Over-shoulder first-person angle balancing stability with intimacy."}, + "Maglev Rail Glide": {"prompt": "maglev rail glide, zero-friction track, hovering sweep", "summary": "Magnetic rail system delivering vibration-free lateral sweeps."}, + "Barrel Roll Orbit": {"prompt": "barrel roll orbit, 360 spin, stabilized center lock", "summary": "Stabilized orbit that rolls around subject while keeping focus pinned."}, + "Spidercam Net": {"prompt": "spidercam cable net, multi-axis chase, rapid altitude shifts", "summary": "Multi-cable spidercam racing across set with agile altitude pivots."}, + "Submersible Pursuit": {"prompt": "submersible pursuit cam, underwater glide, bubble trails", "summary": "Waterproof chase rig gliding beside subjects beneath the surface."}, + "Micro Drone Dash": {"prompt": "micro drone dash, narrow corridor sweep, obstacle dodge", "summary": "Palm-sized drone weaving through tight spaces for kinetic POV."}, + "Holo Stage Orbit": {"prompt": "holo-stage tracked orbit, volumetric capture, light field shimmer", "summary": "Volumetric tracker orbiting holographic performers without parallax drift."}, +} + +VISUAL_STYLE_OPTIONS = { + "None": {"prompt": "", "summary": "No specific visual style."}, + "Felt Style": {"prompt": "felt texture style, handcrafted softness", "summary": "Felt-inspired tactile aesthetic."}, + "3D Cartoon": {"prompt": "3D cartoon aesthetic, stylized characters", "summary": "3D cartoon look with bold forms."}, + "Pixel Art": {"prompt": "pixel art style, retro resolution", "summary": "Pixel art aesthetic with retro charm."}, + "Puppet Animation": {"prompt": "puppet animation style, tangible materials", "summary": "Puppet animation with stop-motion feel."}, + "3D Game": {"prompt": "3D game visuals, real-time rendering", "summary": "3D game engine aesthetic."}, + "Claymation": {"prompt": "claymation style, sculpted forms", "summary": "Claymation stop-motion texture."}, + "Anime": {"prompt": "anime style, crisp linework, expressive shading", "summary": "Anime-inspired illustrative style."}, + "Watercolor": {"prompt": "watercolor wash, fluid pigment, paper texture", "summary": "Watercolor painting feel."}, + "B&W Animation": {"prompt": "black and white animation, noir contrast", "summary": "Monochrome animation aesthetic."}, + "Oil Painting": {"prompt": "oil painting style, rich brushwork", "summary": "Oil painting depth and texture."}, + "Ink Sketch": {"prompt": "ink sketch style, bold linework, crosshatch shading", "summary": "Expressive inked line art with graphic contrast."}, + "Voxel Art": {"prompt": "voxel art style, blocky volumetric pixels, retro 3D", "summary": "Voxel-based 3D aesthetic with chunky forms."}, + "Retro Anime": {"prompt": "retro anime cel shading, grainy cels, vintage palette", "summary": "Retro anime throwback with painterly cels and nostalgic hues."}, + "Lofi Vapor": {"prompt": "lofi vapor style, pastel gradients, soft noise texture", "summary": "Lo-fi vapor aesthetic with gentle gradients and analog fuzz."}, + "Synthwave": {"prompt": "synthwave style, neon grids, retro futurism", "summary": "Retro-futuristic synthwave visuals drenched in neon gradients."}, + "Baroque Painting": {"prompt": "baroque oil painting, dramatic chiaroscuro, ornate detail", "summary": "Lavish baroque aesthetic with rich brushwork and contrast."}, + "Cyber Noir": {"prompt": "cyber noir aesthetic, neon noir, rain-soaked streets", "summary": "Futuristic noir blending neon palettes with shadowy grit."}, + "Papercraft": {"prompt": "papercraft collage, layered cutouts, tactile edges", "summary": "Hand-cut paper aesthetic with layered dimensionality."}, + "Editorial Fashion": {"prompt": "editorial fashion photography, high gloss lighting, couture styling", "summary": "Magazine-ready editorial look with glossy polish."}, + "Solarized": {"prompt": "solarized film effect, inverted tones, experimental contrast", "summary": "Solarized treatment flipping tones into psychedelic inversion."}, + "Ink Wash Animation": {"prompt": "ink wash animation, fluid brushstrokes, sumi-e motion", "summary": "Animated ink wash blending flowing brushwork with motion."}, + "Voxel Neon": {"prompt": "voxel neon city, glowing cubes, pixel volumetrics", "summary": "Glowing voxel aesthetic fusing retro pixels with neon depth."}, + "AI Glitch": {"prompt": "AI glitch art, datamosh streaks, corrupted frames", "summary": "Glitch-forward visuals with datamosh artifacts and distortions."}, + "Hyperreal CGI": {"prompt": "hyperreal CGI, physically based shading, immaculate detail", "summary": "Cutting-edge CGI look with immaculate physical accuracy."}, + "Analog Horror": {"prompt": "analog horror aesthetic, VHS static, uncanny grading", "summary": "Grainy analog horror styling with unsettling VHS artifacts."}, + "Water Ink Hybrid": {"prompt": "watercolor ink hybrid, splatter gradients, bleeding pigments", "summary": "Hybrid watercolor and ink aesthetic with bleeding pigments."}, + "Painterly Sci-Fi": {"prompt": "painterly sci-fi, illustrated futurism, saturated scapes", "summary": "Illustrated sci-fi aesthetic with painterly strokes."}, + "Ceramic Render": {"prompt": "ceramic render style, glazed highlights, sculpted forms", "summary": "Rendered ceramics with glossy glaze and sculptural shapes."}, +} + +SPECIAL_EFFECT_OPTIONS = { + "None": {"prompt": "", "summary": "No special effect emphasis."}, + "Tilt-shift": {"prompt": "tilt-shift effect, miniature depth of field", "summary": "Tilt-shift miniature illusion."}, + "Time-lapse": {"prompt": "time-lapse effect, accelerated motion", "summary": "Time-lapse acceleration for dynamic pacing."}, + "Light Trails": {"prompt": "long-exposure light trails, kinetic streaks, luminous ribbons", "summary": "Dynamic light streaks painting motion paths."}, + "Chromatic Bloom": {"prompt": "chromatic bloom effect, spectral haze, prismatic fringe", "summary": "Prismatic bloom that radiates color halos."}, + "Particle Swarm": {"prompt": "particle swarm effect, floating embers, suspended motes", "summary": "Particle swarm drifting through the frame for tactile atmosphere."}, + "Holographic Static": {"prompt": "holographic static, glitch shimmer, pixel flutter", "summary": "Glitch-laced holographic static overlays for cybernetic flair."}, + "Fog Burst": {"prompt": "fog burst effect, rolling clouds, atmospheric reveal", "summary": "Timed fog bursts adding dramatic atmospheric volume."}, + "Spark Shower": {"prompt": "spark shower effect, cascading embers, industrial flare", "summary": "Controlled spark cascade igniting kinetic energy."}, + "Rain Simulation": {"prompt": "rain simulation, directional droplets, reflective sheen", "summary": "Simulated rainfall coating scene in reflective droplets."}, + "Particle Glimmer": {"prompt": "particle glimmer effect, floating motes, glitter drift", "summary": "Glittering particles drifting slowly through air."}, + "Light Leak": {"prompt": "film light leak flare, colorful streaks, analog bleed", "summary": "Analog-style light leaks bleeding vibrant streaks across frame."}, + "Speed Ramp": {"prompt": "speed ramp effect, time shift, motion stretch", "summary": "Dynamic speed ramp bending time for emphasis."}, + "Digital Rain": {"prompt": "digital rain code, cascading glyphs, matrix cascade", "summary": "Digital code rain overlay cascading across scene."}, + "Dust Explosion": {"prompt": "dust explosion effect, powder bloom, slow-motion plume", "summary": "Choreographed dust burst expanding in cinematic slow motion."}, + "Energy Ribbons": {"prompt": "energy ribbon trails, luminous arcs, kinetic swirls", "summary": "Luminous energy ribbons weaving through motion path."}, +} + +PALETTE_PROFILES = { + "Moody Nightfall": { + "description": "Storm-lashed nocturnal palette with electrified highlights and handheld energy.", + "overrides": { + "light_source": ["Moonlight", "Practical Light"], + "light_quality": ["Soft Light"], + "time_of_day": ["Nighttime"], + "color_tone": ["Cool Tone"], + "camera_advanced": ["Handheld"], + }, + "intense_overrides": { + "motion_type": ["Running"], + "lens_angle": ["Low Angle"], + }, + "effects": ["Time-lapse"], + "keywords": [ + "storm-lashed", + "suspended rain droplets", + "electric skyline", + "brooding shadows", + ], + "intense_keywords": [ + "staccato lightning bursts", + "ground-level spray", + ], + "effect_keywords": ["temporal smearing"], + }, + "Action Spotlight": { + "description": "High-contrast spotlighting that carves out kinetic hero beats on stage-like sets.", + "overrides": { + "light_source": ["Artificial Light"], + "light_quality": ["Hard Light"], + "time_of_day": ["Daytime"], + "shot_size": ["Medium Close-up"], + "composition": ["Right-weighted"], + "camera_basic": ["Push-in"], + }, + "intense_overrides": { + "motion_type": ["Basketball"], + "camera_advanced": ["Orbit"], + }, + "effects": ["Tilt-shift"], + "keywords": [ + "razor-edged highlights", + "charged air", + "spotlit sweat sheen", + ], + "intense_keywords": ["dynamic crowd blur"], + "effect_keywords": ["miniature stage illusion"], + }, + "Dreamscape Glow": { + "description": "Soft dawn-drenched dreamscape with painterly diffusion and weightless pacing.", + "overrides": { + "light_source": ["Daylight"], + "light_quality": ["Soft Light"], + "time_of_day": ["Dawn"], + "color_tone": ["Warm Tone"], + "visual_style": ["Watercolor"], + "lens_focal": ["Wide Angle"], + }, + "intense_overrides": { + "composition": ["Symmetrical"], + "special_effect": ["Tilt-shift"], + }, + "effects": ["Tilt-shift"], + "keywords": [ + "ethereal mist", + "periwinkle glow", + "feathered highlights", + ], + "intense_keywords": ["floating pollen shimmer"], + "effect_keywords": ["miniature haze"], + }, + "Urban Neon Rush": { + "description": "High-voltage city palette pulsing with reflected neon and kinetic energy.", + "overrides": { + "light_source": ["Artificial Light", "Mixed Light"], + "light_quality": ["High Contrast"], + "time_of_day": ["Nighttime"], + "color_tone": ["High Saturation"], + "camera_basic": ["Whip Pan"], + }, + "intense_overrides": { + "motion_type": ["Running"], + "camera_advanced": ["Cable Cam Chase"], + }, + "effects": ["Light Trails"], + "keywords": [ + "neon signage", + "slick pavement", + "electric reflections", + ], + "intense_keywords": [ + "crowd rush blur", + "pulsebeat traffic", + ], + "effect_keywords": ["holographic streaks"], + }, + "Aurora Reverie": { + "description": "Celestial aurora palette awash with drifting color curtains and tranquil wonder.", + "overrides": { + "time_of_day": ["Midnight"], + "color_tone": ["Cool Tone"], + "visual_style": ["Watercolor"], + }, + "intense_overrides": { + "composition": ["Leading Lines"], + "lens_angle": ["Ground Level"], + }, + "effects": ["Chromatic Bloom"], + "keywords": [ + "dancing aurora", + "glacial horizon", + "whispered starlight", + ], + "intense_keywords": [ + "skyfire curtains", + "crystalline hush", + ], + "effect_keywords": ["spectral mist"], + }, + "Crystal Metropolis": { + "description": "Prismatic mega-city palette where glass towers refract neon pulse and rainfall.", + "overrides": { + "light_source": ["Holographic Panels", "Mixed Light"], + "light_quality": ["Specular Punch"], + "time_of_day": ["Nighttime"], + "color_tone": ["Bioluminescent"], + "visual_style": ["Lofi Vapor"], + "special_effect": ["Holographic Static"], + }, + "intense_overrides": { + "motion_type": ["Parkour"], + "camera_advanced": ["Gimbal Glide"], + }, + "effects": ["Light Trails"], + "keywords": [ + "crystal canopy", + "rain-slick plazas", + "neon diffraction", + ], + "intense_keywords": [ + "mirror-flare cascades", + "vertigo sprint", + ], + "effect_keywords": ["glitch shimmer"], + }, + "Mythic Canopy": { + "description": "Sun-dappled ancient forest palette infused with luminous spores and folklore hush.", + "overrides": { + "light_source": ["Overcast Light"], + "light_quality": ["Veiled Diffusion"], + "time_of_day": ["Golden Hour"], + "color_tone": ["Warm Tone"], + "visual_style": ["Watercolor"], + "composition": ["Radial Burst"], + }, + "intense_overrides": { + "motion_type": ["Aerial Silk"], + "lens_focal": ["Tilt Shift"], + }, + "effects": ["Particle Swarm"], + "keywords": [ + "sunlit spores", + "ancient canopy", + "myth-woven moss", + ], + "intense_keywords": [ + "floating pollen aurora", + "silk dancer arc", + ], + "effect_keywords": ["ember motes"], + }, +} + +CHARACTER_SPECIES_OPTIONS = { + "Human": {"prompt": "human lineage, adaptable physiology", "notes": "Versatile baseline physiology built for broad storytelling."}, + "Woman": {"prompt": "a woman", "notes": "Baseline human woman—ideal for grounded or genre-flexible prompts."}, + "Man": {"prompt": "a man", "notes": "Baseline human man—works across modern, historical, and speculative settings."}, + "Nonbinary Icon": {"prompt": "a non-binary hero", "notes": "Gender-expansive protagonist centered on confidence and agency."}, + "High Elf": {"prompt": "high elf heritage, luminous eyes, tapered ears", "notes": "Elegant, ageless presence steeped in arcane tradition."}, + "Cyborg": {"prompt": "cybernetic grafts, chrome plating, biomechanical synergy", "notes": "Synthetic augmentation threaded through organic form."}, + "Beastkin": {"prompt": "beastkin traits, digitigrade stance, tufted ears, expressive tail", "notes": "Animal lineage lends primal motion and sensory acuity."}, + "Voidborn": {"prompt": "voidborn aura, astral freckles, gravity-bent silhouette", "notes": "Born in deep space, physics bends gently around them."}, + "Chronomancer": {"prompt": "chronomancer attunement, temporal glyphs inlaid across skin", "notes": "Timeline walker carrying echoes of alternate outcomes."}, + "Draconic Heir": {"prompt": "draconic bloodline, scaled accents, ember glow veins", "notes": "Ancestral dragon heritage radiating latent ferocity."}, + "Aether Construct": {"prompt": "aether-forged shell, floating sigils, hum of resonant crystals", "notes": "Sapient construct suspended by arcane reinforcement fields."}, + "Mycelid": {"prompt": "mycelid collective body, filament hair, luminous spores", "notes": "Fungal network embodied in a collaborative biped form."}, + "Solaris": {"prompt": "solar-charged aura, corona halo, radiant freckles", "notes": "Photosynthetic physiology that stores stellar energy."}, + "Android Emissary": {"prompt": "android emissary, polished synth-skin, bioluminescent joints", "notes": "Diplomatic artificial being fluent in human and machine cultures."}, + "Gene-Spliced": {"prompt": "gene-spliced hybrid, mosaic DNA, adaptive phenotype", "notes": "Experimental hybrid whose form shifts with environmental cues."}, + "Stoneborn": {"prompt": "stoneborn titan, living granite plates, glowing rune veins", "notes": "Earth-wrought giant shaped from enchanted stone."}, + "Celestial Seraph": {"prompt": "celestial seraph, radiant wings, constellation eyes", "notes": "Heaven-forged being channeling starlight through every gesture."}, + "Abyssal Diver": {"prompt": "abyssal diver lineage, bioluminescent markings, pressure-adapted", "notes": "Deep-sea evolved being comfortable in crushing depths."}, + "Chrono Echo": {"prompt": "chrono echo, multiple temporal afterimages, layered timelines", "notes": "Time-split individual existing across overlapping moments."}, + "Glimmer Sprite": {"prompt": "glimmer sprite, crystalline wings, iridescent glow", "notes": "Feykin sprite carried on refractive wings of light."}, + "Terran Ranger": {"prompt": "terra-born ranger, rugged human variant, survival scar", "notes": "Human offshoot adapted for harsh frontier worlds."}, + "Nebula Djinn": {"prompt": "nebula djinn, gaseous limbs, swirling cosmic dust", "notes": "Wish-bending being composed of nebular gases."}, + "Arc Forge": {"prompt": "arc-forged entity, metallic flesh, humming coils", "notes": "Electro-forged lifeform powered by humming arc reactors."}, + "Fungal Scholar": {"prompt": "fungal scholar, mycelial tendrils, spore library", "notes": "Sapient fungus network storing ancestral knowledge."}, + "Solaris Twin": {"prompt": "solar twin pair, bi-phasic body, orbit-linked", "notes": "Twin-bodied organism connected by radiant energy tether."}, + "Glacial Avatar": {"prompt": "glacial avatar, ice-armored skin, frost aura", "notes": "Ice-forged guardian radiating crystalline chill."}, + "Myth-Machine": {"prompt": "myth-machine construct, engraved chassis, story sigils", "notes": "Mechanical being animated by legendary narratives."}, + "Bio-Siren": {"prompt": "bio-siren lineage, harmonic voice, ripple scales", "notes": "Amphibious singer whose tones shape currents and minds."}, + "Shadowweave": {"prompt": "shadowweave entity, semi-corporeal dusk, shifting outline", "notes": "Living shadow capable of phasing through thin light."}, + "Sky Leviathan": {"prompt": "sky leviathan rider lineage, aerodynamic membranes, storm glands", "notes": "Aerial giant evolved to glide on planetary jet streams."}, + "Marrowbound": {"prompt": "marrowbound wanderer, biolattice bones, internal glow", "notes": "Biomechanical nomad whose bones emit soft luminescence."}, + "Quantum Bloom": {"prompt": "quantum bloom organism, petal fractals, probability petals", "notes": "Quantum-born entity blossoming into countless outcomes."}, +} + +CHARACTER_ARCHETYPE_OPTIONS = { + "Champion": {"prompt": "heroic champion stance, shield of hope", "notes": "Frontline defender sworn to inspire and protect."}, + "Trickster": {"prompt": "trickster charisma, sly grin, sleight-of-hand readiness", "notes": "Unpredictable agent of chaos and clever subversion."}, + "Scholar": {"prompt": "erudite scholar poise, layered tomes, analytical gaze", "notes": "Driven by curiosity, cataloging the unknown."}, + "Guardian": {"prompt": "guardian vigilance, anchored footing, protective aura", "notes": "Stalwart protector holding the line against threats."}, + "Nomad": {"prompt": "wandering nomad stride, horizon-focused gaze", "notes": "Pathfinder drawn to uncharted horizons and cultural exchange."}, + "Inquisitor": {"prompt": "unyielding inquisitor presence, piercing eyes", "notes": "Relentless truth-seeker weighed down by difficult judgments."}, + "Visionary": {"prompt": "visionary dreamer, star-mapped cloak, prophetic whispers", "notes": "Sees futures yet unwritten, shaping destiny with conviction."}, + "Survivor": {"prompt": "scarred survivor grit, improvised kit", "notes": "Battle-hardened resilience forged through catastrophe."}, + "Diplomat": {"prompt": "compassionate diplomat posture, open palms", "notes": "Bridge-builder skilled in cultural nuance and negotiation."}, + "Warden": {"prompt": "ancient warden gravitas, rooted stance", "notes": "Sworn caretaker of sacred territories and traditions."}, + "Innovator": {"prompt": "visionary innovator, prototype-laden belt, spark of insight", "notes": "Constant tinkerer reshaping the world with daring ideas."}, + "Shadow Broker": {"prompt": "shadow broker, encrypted dossiers, whisper alliances", "notes": "Information powerbroker trading secrets across factions."}, + "Healer": {"prompt": "battlefield healer, luminous salves, steady compassion", "notes": "Medic whose calm precision restores allies under pressure."}, + "Storm Herald": {"prompt": "storm herald, cloak of thunderheads, prophetic warnings", "notes": "Weather-calling herald reading omens in turbulent skies."}, + "Beacon": {"prompt": "living beacon, radiant posture, hope incarnate", "notes": "Symbol of inspiration whose presence uplifts entire movements."}, + "Archivist": {"prompt": "memory archivist, floating data shards, chronicle voice", "notes": "Keeper of histories extracting lessons from layered timelines."}, + "Outlaw Poet": {"prompt": "outlaw poet, handwritten manifestos, rebel charm", "notes": "Romantic renegade weaving revolution through verse."}, + "Voidwalker": {"prompt": "voidwalker stride, starless cloak, planar map", "notes": "Interplanar traveler charting hidden routes between realities."}, + "Caretaker": {"prompt": "gentle caretaker, calming aura, nurturing stance", "notes": "Guardian of communities prioritizing empathy and stability."}, + "Relic Hunter": {"prompt": "relic hunter, artifact harness, keen appraisal", "notes": "Treasure-seeking scholar adept at deciphering ancient wards."}, + "Tactician": {"prompt": "war tactician, strategy lattice, anticipatory gaze", "notes": "Battlefield planner forecasting outcomes with precision."}, + "Dreamweaver": {"prompt": "lucid dreamweaver, shimmering sleep threads, tranquil poise", "notes": "Architect of shared dreams sculpting surreal landscapes."}, + "Firebrand": {"prompt": "street firebrand, megaphone rhetoric, burning conviction", "notes": "Agitator sparking uprisings with bold speeches."}, + "Seer": {"prompt": "veil-wrapped seer, clairvoyant focus, second-sight glow", "notes": "Oracle translating cryptic visions into actionable guidance."}, + "Envoy": {"prompt": "cosmopolitan envoy, emblem sashes, persuasive grace", "notes": "Bridge-builder negotiating peace among rival factions."}, + "Rift Knight": {"prompt": "rift knight, dimensional plate armor, phase-blade", "notes": "Guardian soldier defending portals between worlds."}, + "Songsmith": {"prompt": "battle songsmith, resonant instruments, sonic wards", "notes": "Musical mage channeling melodies into protective sigils."}, + "Lorebreaker": {"prompt": "rule-bending lorebreaker, annotated laws, sly smirk", "notes": "Legal rebel hacking loopholes to reshape society."}, + "Channeler": {"prompt": "elemental channeler, swirling energy conduits, focused stance", "notes": "Conduit for raw natural forces balancing immense power."}, + "Pathfinder": {"prompt": "frontier pathfinder, holographic maps, adaptive gear", "notes": "Explorer leading expeditions through perilous territories."}, + "Sentinel": {"prompt": "midnight sentinel, vigilant guard stance, unwavering gaze", "notes": "Unblinking protector standing between chaos and calm."}, + "Harbinger": {"prompt": "ominous harbinger, windblown heraldry, portentous aura", "notes": "Messenger whose arrival signals profound change."}, + "Artificer": {"prompt": "clockwork artificer, modular gauntlets, analytical eyes", "notes": "Inventor crafting adaptive gadgets under pressure."}, + "Mystic": {"prompt": "hermit mystic, incense haze, layered talismans", "notes": "Spiritual guide searching for enlightenment in silent rituals."}, + "Caretaker AI": {"prompt": "caretaker AI avatar, holographic interface, calming pulse", "notes": "Artificial intelligence embodying empathy as its core directive."}, + "Courier": {"prompt": "sky courier, jet-assisted boots, urgent posture", "notes": "Risk-taking messenger delivering vital packages across hazards."}, + "Saboteur": {"prompt": "precision saboteur, compact charges, disarming wit", "notes": "Expert infiltrator dismantling oppressive infrastructure."}, + "Sage": {"prompt": "wandering sage, humble robes, gentle insight", "notes": "Wisdom keeper gifting balanced counsel to seekers."}, + "Beast Tamer": {"prompt": "stellar beast tamer, companion creature, confident rapport", "notes": "Handler forging bonds with extraordinary fauna."}, + "Soul Forger": {"prompt": "soul forger, radiant anvil, sparks of memory", "notes": "Craftsperson reshaping destinies through metaphysical metallurgy."}, +} + +CHARACTER_PROFESSION_OPTIONS = { + "Starship Captain": {"prompt": "starship captain regalia, command epaulettes", "notes": "Seasoned navigator balancing crew morale with tactical foresight."}, + "Shadow Operative": {"prompt": "shadow operative gear, stealth field emitter", "notes": "Specialist in covert insertions armed with cutting-edge espionage tools."}, + "Battle Bard": {"prompt": "battle bard instrument rig, harmonic weaponry", "notes": "Weaves music into battlefield momentum and morale."}, + "Bioengineer": {"prompt": "bioengineer toolkit, gene-etched gloves", "notes": "Cultivates living solutions to impossible problems."}, + "Runesmith": {"prompt": "runesmith hammer, enchanted anvil sparks", "notes": "Artisan forging sigils into resonant artifacts."}, + "Sky Courier": {"prompt": "sky courier harness, aero-satchel", "notes": "Thrill-seeking messenger skimming the jet stream."}, + "Mythic Chef": {"prompt": "culinary alchemist, floating spice jars", "notes": "Chef crafting memory-infused cuisine that shapes emotion."}, + "Chronicle Scribe": {"prompt": "chronicle scribe quills, holographic folios", "notes": "Living historian who witnesses and records pivotal events."}, + "Stormcaller": {"prompt": "stormcaller conduit staff, charged gauntlets", "notes": "Elementalist channeling tempest forces through precise rituals."}, + "Digital Oracle": {"prompt": "digital oracle visor, cascading data-streams", "notes": "Predictive analyst interfaced with sentient archives."}, + "Terraform Architect": {"prompt": "terraform architect console, eco-drone entourage", "notes": "Engineer sculpting habitable worlds from hostile environments."}, + "Rift Cartographer": {"prompt": "rift cartographer holo-astrolabe, dimensional charts", "notes": "Survey specialist mapping unstable planar anomalies."}, + "Plasma Knight": {"prompt": "plasma knight armor, energized lance", "notes": "Elite warrior clad in superheated shielding."}, + "Memory Diver": {"prompt": "memory diver neural rig, mnemonic tether", "notes": "Psychic operative recovering truths buried in minds."}, + "Chrono Prosecutor": {"prompt": "chrono prosecutor robes, temporal evidence glyphs", "notes": "Legal tactician litigating crimes across timefolds."}, + "Skywright": {"prompt": "skywright workshop, levitation tools", "notes": "Artisan crafting airborne vessels and floating habitats."}, + "Pulse DJ": {"prompt": "pulse DJ booth, rhythm gauntlets", "notes": "Sound sculptor remixing reality through bassline manipulation."}, + "Gene Shepherd": {"prompt": "gene shepherd caretaking lab, bio-cradle", "notes": "Caretaker guiding bespoke organisms to maturity."}, + "Quantum Broker": {"prompt": "quantum broker ledger, entangled contracts", "notes": "Negotiator orchestrating probability-weighted deals."}, + "Mythos Curator": {"prompt": "mythos curator reliquary, floating archives", "notes": "Museum steward preserving legendary artifacts."}, + "Flux Gladiator": {"prompt": "flux gladiator arena gear, adaptive plating", "notes": "Arena combatant thriving in transforming battlegrounds."}, + "Aurora Ranger": {"prompt": "aurora ranger scout armor, chromatic cloak", "notes": "Guardian patrolling polar frontiers and lightstorms."}, + "Nanite Surgeon": {"prompt": "nanite surgeon sterile swarm, precision halo", "notes": "Microsurgical expert deploying autonomous healing units."}, + "Dream Cartomancer": {"prompt": "dream cartomancer deck, lucid tarot", "notes": "Sleep-mage reading futures via animated cards."}, + "Ember Smith": {"prompt": "ember smith forge, molten glasswork", "notes": "Artist shaping fire into ornamental weaponry."}, + "Cybernetic Shepherd": {"prompt": "cybernetic shepherd drone-flock, command baton", "notes": "Technician wrangling autonomous machines across fields."}, + "Tidal Envoy": {"prompt": "tidal envoy amphibious gear, water glyph tattoos", "notes": "Diplomat mediating treaties between aquatic nations."}, + "Grav-Law Marshal": {"prompt": "grav-law marshal badge, stabilizer harness", "notes": "Peacekeeper enforcing order in zero-G jurisdictions."}, + "Solar Cantor": {"prompt": "solar cantor choir robe, light harmonics", "notes": "Clerical vocalist channeling solar hymns for healing."}, +} + +CHARACTER_TEMPERAMENT_OPTIONS = { + "Stoic": {"prompt": "stoic demeanor, measured breathing", "notes": "Calm exterior shielding unshakeable resolve."}, + "Reckless": {"prompt": "reckless spark, ready grin, forward lean", "notes": "Thrives on risk and improvisational momentum."}, + "Empathic": {"prompt": "empathic warmth, soft cadence, open posture", "notes": "Absorbs emotional atmosphere, reflecting compassion."}, + "Calculating": {"prompt": "calculating gaze, micro-expressions measured", "notes": "Strategist evaluating probabilities before striking."}, + "Haunted": {"prompt": "haunted distance, shadowed eyes", "notes": "Trauma-laden presence haunted by past choices."}, + "Joyous": {"prompt": "joyous energy, kinetic laughter", "notes": "Infectious enthusiasm lifting the spirits of allies."}, + "Obsessive": {"prompt": "obsessive focus, ink-stained fingers", "notes": "Won't rest until every thread of a mystery is resolved."}, + "Serene": {"prompt": "serene center, meditative aura", "notes": "Anchored mindfulness even amid chaos."}, + "Commanding": {"prompt": "commanding cadence, authoritative presence", "notes": "Compels attention with natural leadership."}, + "Unpredictable": {"prompt": "mercurial energy, sudden shifts", "notes": "Volatile impulse shaping chaotic adventures."}, + "Zenith": {"prompt": "zenith poise, radiant calm", "notes": "Steady optimist radiating unwavering positive focus."}, + "Brooding": {"prompt": "brooding quiet, tempest behind eyes", "notes": "Reserved intensity masking deep internal storms."}, + "Mischievous": {"prompt": "mischievous glint, playful tilt", "notes": "Prankster spirit sparking levity amid danger."}, + "Stoic Ember": {"prompt": "stoic ember, quiet burn", "notes": "Outwardly composed yet fueled by simmering drive."}, + "Nurturing": {"prompt": "nurturing warmth, reassuring cadence", "notes": "Care-taker temperament offering continual reassurance."}, + "Relentless": {"prompt": "relentless focus, forward momentum", "notes": "Determination that bulldozes obstacles without hesitation."}, + "Mercurial": {"prompt": "mercurial shift, rapid mood flux", "notes": "Emotion swings fast, channeling creativity and chaos."}, + "Wry": {"prompt": "wry amusement, raised brow", "notes": "Dry-humored commentator defusing tension with wit."}, + "Haute": {"prompt": "haute composure, refined posture", "notes": "Elegant confidence curating every moment with taste."}, +} + +CHARACTER_POWER_SOURCE_OPTIONS = { + "Arcane Relic": {"prompt": "arcane relic focus, carved sigils pulsating", "notes": "Ancient artifact channels raw eldritch potency."}, + "Bio-Augment": {"prompt": "bio-augment grafts, living circuitry", "notes": "Symbiotic biotech amplifies physical capabilities."}, + "Symbiotic Spirit": {"prompt": "spirit-bound ally whispering, spectral tether", "notes": "Bonded with an ancestral guardian spirit."}, + "Quantum Reservoir": {"prompt": "quantum reservoir core, probability halos", "notes": "Manipulates chance via micro-singularity core."}, + "Celestial Choir": {"prompt": "celestial choir resonance, harmonized aura", "notes": "Channeling melodies sung beyond mortal perception."}, + "Primeval Blood": {"prompt": "primeval blood surge, pulse-light veins", "notes": "Ancient lineage awakens primal ferocity."}, + "Dreamforge": {"prompt": "dreamforge conduit, lucid shimmer", "notes": "Shapes reality by weaving lucid dream fragments."}, + "Nano Swarm": {"prompt": "nano swarm cloud, adaptive armor", "notes": "Programmable matter responds to instinctive commands."}, + "Shadow Accord": {"prompt": "shadow accord pact, umbral tendrils", "notes": "Dark pact enabling controlled umbral manifestations."}, + "Empyreal Beacon": {"prompt": "empyreal beacon flares, feathered light", "notes": "Holy conduit radiating restorative brilliance."}, + "Bio-Lum Core": {"prompt": "bio-lum core, radiant organ glow", "notes": "Organ grown to store luminescent energy for spells."}, + "Aeon Engine": {"prompt": "aeon engine heart, rotating chronogears", "notes": "Temporal reactor granting longevity and time manipulation."}, + "Starfall Fragment": {"prompt": "starfall fragment shard, cosmic resonance", "notes": "Meteorite shard humming with stellar ferocity."}, + "Echo Choir": {"prompt": "echo choir resonance, harmonic halo", "notes": "Networked voices empowering abilities when harmonized."}, + "Dream Fathom": {"prompt": "dream fathom sigil, lucid tides", "notes": "Draws strength from collective subconscious oceans."}, + "Glyph Circuit": {"prompt": "glyph circuit lattice, rune-lit veins", "notes": "Runic circuitry embedded beneath the skin."}, + "Primal Symbiosis": {"prompt": "primal symbiote companion, entwined motion", "notes": "Living companion organism amplifies raw instincts."}, + "Entropy Well": {"prompt": "entropy well core, orbiting decay motes", "notes": "Harvests decay energy to fuel destructive potential."}, + "Aurora Seed": {"prompt": "aurora seed heart, chromatic bloom", "notes": "Seed of living light blossoming into protective auroras."}, +} + +CHARACTER_SIGNATURE_GEAR_OPTIONS = { + "Living Blade": {"prompt": "living blade companion, shifting edge geometry", "notes": "Sentient weapon that adapts to combat rhythm."}, + "Modular Gauntlet": {"prompt": "modular gauntlet, deployable tools", "notes": "Swiss-army gauntlet that reconfigures for any task."}, + "Archivist Drone": {"prompt": "archivist drone orbiting, holo-scribe projectors", "notes": "Floating aide capturing and projecting key intel."}, + "Aether Cloak": {"prompt": "aether cloak, phase-shifting weave", "notes": "Cloak that slips between visibility bands to protect the wearer."}, + "Phoenix Sigil": {"prompt": "phoenix sigil brooch, ember trails", "notes": "Symbol igniting morale and triggering regenerative surges."}, + "Gravity Boots": {"prompt": "gravity boots, thruster vents, magnetic soles", "notes": "Footwear manipulating gravity wells for agile traversal."}, + "Warden Shield": {"prompt": "warden shield, etched wardlines", "notes": "Monolithic shield storing protective ward glyphs."}, + "Chrono Dice": {"prompt": "chrono dice reliquary, suspended numbers", "notes": "Randomizer glimpsing potential futures to adjust tactics."}, + "Liminal Loom": {"prompt": "liminal loom toolkit, reality-thread spindles", "notes": "Portable loom stitching scenario-specific disguises."}, + "Mycelial Satchel": {"prompt": "mycelial satchel, cultivated spores", "notes": "Organic pack sprouting bespoke remedies on demand."}, + "Echo Bow": {"prompt": "echo bow, resonance arrows", "notes": "Bow that replicates shots across parallel timelines."}, + "Solar Bracers": {"prompt": "solar bracers, photon vents", "notes": "Gauntlets storing sunlight for explosive discharges."}, + "Grav Whip": {"prompt": "gravity whip, collapsing nodes", "notes": "Whip manipulating localized gravity wells mid-strike."}, + "Chrono Compass": {"prompt": "chrono compass, spinning epochs", "notes": "Navigational relic pointing toward optimal futures."}, + "Nimbus Kite": {"prompt": "nimbus kite familiar, tethered cloud", "notes": "Weather-bound companion providing lift and surveillance."}, + "Quantum Satchel": {"prompt": "quantum satchel, nested storage fields", "notes": "Bag storing entire arsenals within compressed dimensions."}, + "Runebound Mask": {"prompt": "runebound mask, animated etchings", "notes": "Mask that shifts identities through carved sigils."}, + "Pulse Hammer": {"prompt": "pulse hammer, shockwave coils", "notes": "Weapon releasing seismic bursts on impact."}, + "Lullaby Harp": {"prompt": "lullaby harp, starlit strings", "notes": "Instrument weaving calming enchantments mid-conflict."}, +} + +CHARACTER_VISUAL_MOTIF_OPTIONS = { + "Solar Flare": {"prompt": "solar flare motifs, golden filigree", "notes": "Radiant motifs framing them in warm celestial geometry."}, + "Glacial Facets": {"prompt": "glacial facet accents, refracted highlights", "notes": "Crystalline textures evoking frozen elegance."}, + "Verdant Bloom": {"prompt": "verdant bloom vines, botanical flourish", "notes": "Living flora that responds to their emotional cadence."}, + "Neon Glyphs": {"prompt": "neon glyph tattoos, animated circuitry", "notes": "Luminous glyphwork channeling cybernetic runes."}, + "Ink Wash": {"prompt": "ink wash gradients, sumi-e splashes", "notes": "Brushstroke aesthetics trailing each movement."}, + "Storm Wreath": {"prompt": "storm wreath corona, static arcs", "notes": "Micro-lightning dances around their silhouette."}, + "Mirror Shards": {"prompt": "mirror shard fragments, reflective armor", "notes": "Fragmented reflections distorting perception."}, + "Bone Filigree": {"prompt": "ivory bone filigree, ancestral carvings", "notes": "Ornate osteal motifs honoring ancient lineage."}, + "Lunar Petals": {"prompt": "lunar petal embroidery, silver sheen", "notes": "Delicate moonlit blossoms drifting in their wake."}, + "Aurora Stream": {"prompt": "aurora stream ribbons, chromatic trails", "notes": "Flowing gradients reminiscent of polar skies."}, + "Obsidian Fractals": {"prompt": "obsidian fractal veins, dark sheen", "notes": "Geometric void patterns refracting shadow light."}, + "Circuit Lace": {"prompt": "circuit lace filaments, glowing traces", "notes": "Delicate techno-filagree tracing along fabrics."}, + "Celestial Manuscript": {"prompt": "celestial manuscript constellations, inked nebulae", "notes": "Scripted galaxies arcing across garments."}, + "Tidal Plumes": {"prompt": "tidal plume motifs, misted gradients", "notes": "Ocean-swept vapor ribbons swirling around them."}, + "Floral Geodes": {"prompt": "floral geode facets, blooming crystals", "notes": "Gemstone blossoms bursting through attire."}, + "Sonic Waves": {"prompt": "sonic wave glyphs, rippling rings", "notes": "Resonant patterns oscillating with each step."}, + "Origami Armor": {"prompt": "origami armor plates, fold-shift edges", "notes": "Artful folds rearranging protective layers in motion."}, + "Ink Nebula": {"prompt": "ink nebula swirls, gaseous gradients", "notes": "Liquid galaxy motifs pooling into nocturnal clouds."}, + "Feather Storm": {"prompt": "feather storm cascade, drifting barbs", "notes": "Swirling plumage orbiting in controlled spirals."}, +} + +CHARACTER_ERA_OPTIONS = { + "Mythic Age": {"prompt": "mythic age setting, legendary sagas", "notes": "Epic era of gods, titans, and heroic epics."}, + "Clockwork Renaissance": {"prompt": "clockwork renaissance metropolis, brasswork skyline", "notes": "Mechanical rebirth blending artistry and automation."}, + "Cyberpunk Dawn": {"prompt": "neon-drenched cyberpunk dawn, megacity sprawl", "notes": "Edge-of-revolt techno metropolis soaked in neon fog."}, + "Frontier Colony": {"prompt": "frontier colony outpost, terraforming rigs", "notes": "Newly settled world balancing survival and exploration."}, + "Magepunk Capital": {"prompt": "magepunk capital spires, arcano-engineered transit", "notes": "City powered by spellwork woven into infrastructure."}, + "Post-Singularity": {"prompt": "post-singularity skylines, post-human society", "notes": "Society navigates coexistence with transcendent AI."}, + "Solar Kingdom": {"prompt": "solar kingdom citadel, mirrored battlements", "notes": "Sun-worshiping empire shimmering with mirrored stone."}, + "Deep Time Ruins": {"prompt": "deep time ruins, subsumed by bioluminescent jungle", "notes": "Fallen civilization enveloped by luminous overgrowth."}, + "Floating Archipelago": {"prompt": "floating archipelago isles, gravitic bridges", "notes": "Linked sky islands anchored by ancient runes."}, + "Quantum Bazaar": {"prompt": "quantum bazaar interdimensional market", "notes": "Marketplace perched between layered realities."}, + "Crystal Regency": {"prompt": "crystal regency court, prismatic halls", "notes": "Opulent monarchy thriving on gemstone alchemy."}, + "Afterlight Reclamation": {"prompt": "afterlight reclamation camps, biolum salvage", "notes": "Post-cataclysm rebuilding focused on sustainable tech."}, + "Aether Age": {"prompt": "aether age dirigibles, brass conduits", "notes": "Era of pseudo-science airships and arcane industry."}, + "Galactic Commons": {"prompt": "galactic commons hub, multicultural convergence", "notes": "Peace-period fostering shared interstellar culture."}, + "Shadow Dominion": {"prompt": "shadow dominion citadels, midnight governance", "notes": "Age where darkness-ruled empires sculpt society."}, + "Eco Renaissance": {"prompt": "eco renaissance arcologies, lush rooftops", "notes": "Green-forward rebirth harmonizing architecture with nature."}, + "Neon Restoration": {"prompt": "neon restoration alleys, vibrant murals", "notes": "Rebuilding dystopian streets with art activism."}, + "Chronicle Epoch": {"prompt": "chronicle epoch archives, time-locked vaults", "notes": "Era dedicated to preserving histories across timelines."}, + "Stellar Crusade": {"prompt": "stellar crusade fleets, radiant armadas", "notes": "Interstellar holy wars shaping galactic borders."}, +} + +CHARACTER_BODY_TYPE_OPTIONS = { + "Athletic": {"prompt": "athletic build, balanced musculature", "notes": "Functional physique tuned for mobility and strength."}, + "Slender": {"prompt": "slender frame, poised elegance", "notes": "Graceful lines emphasizing finesse."}, + "Compact": {"prompt": "compact stature, coiled energy", "notes": "Dense power focused in a smaller frame."}, + "Towering": {"prompt": "towering height, elongated proportions", "notes": "Commanding presence with sweeping reach."}, + "Curved": {"prompt": "curved silhouette, soft strength", "notes": "Embraces fullness with confident poise."}, + "Augmented": {"prompt": "augmented bodywork, reinforced plating", "notes": "Engineered enhancements broadening capability."}, + "Aerodynamic": {"prompt": "streamlined physique, aerodynamic contours", "notes": "Built for velocity with minimal drag."}, + "Ethereal": {"prompt": "ethereal weightlessness, semi-translucent form", "notes": "Lightfooted, almost intangible presence."}, + "Stalwart": {"prompt": "stalwart frame, fortress-like build", "notes": "Broad, enduring structure anchored in resilience."}, + "Adaptive": {"prompt": "adaptive biomorph body, shifting contours", "notes": "Morphing physique responding to situational demands."}, + "Willowy": {"prompt": "willowy silhouette, elongated grace", "notes": "Delicate length balanced with flexible poise."}, + "Titanic": {"prompt": "titanic frame, colossal scale", "notes": "Monumental proportions towering above companions."}, + "Featherlight": {"prompt": "featherlight carriage, airy steps", "notes": "Almost weightless movement, hovering transitions."}, + "Stonecarved": {"prompt": "stonecarved musculature, chiseled lines", "notes": "Hard-edged build reminiscent of sculpted statues."}, + "Gossamer": {"prompt": "gossamer ethereality, translucent limbs", "notes": "Fine, delicate structure with subtle translucence."}, + "Cyclonic": {"prompt": "cyclonic spin, coiled torque", "notes": "Body primed for rotational force and sudden action."}, + "Nebulous": {"prompt": "nebulous outline, diffuse limbs", "notes": "Semi-fluid form shifting like condensed mist."}, + "Anchored": {"prompt": "anchored stance, low center", "notes": "Grounded build exuding steadfast stability."}, + "Hybridized": {"prompt": "hybridized morphology, contrasting halves", "notes": "Body blending dual species traits in symmetrical harmony."}, +} + +CHARACTER_ALIGNMENT_OPTIONS = { + "Lawful Good": {"prompt": "lawful good code, honorable aura", "notes": "Upholds justice with unwavering ethics."}, + "Neutral Good": {"prompt": "neutral good compassion, balanced heart", "notes": "Helps others guided by empathy over law."}, + "Chaotic Good": {"prompt": "chaotic good rebellion, liberating spark", "notes": "Breaks rules for the greater good."}, + "Lawful Neutral": {"prompt": "lawful neutral discipline, ritual precision", "notes": "Follows codes and hierarchies to the letter."}, + "True Neutral": {"prompt": "true neutral equilibrium, contemplative restraint", "notes": "Seeks balance over allegiance."}, + "Chaotic Neutral": {"prompt": "chaotic neutral unpredictability, personal freedom", "notes": "Acts on instinct, answerable to no one."}, + "Lawful Evil": {"prompt": "lawful evil supremacy, cold authority", "notes": "Twists legal structures for personal gain."}, + "Neutral Evil": {"prompt": "neutral evil opportunism, quiet menace", "notes": "Self-serving tactician with ruthless pragmatism."}, + "Chaotic Evil": {"prompt": "chaotic evil cataclysm, feral grin", "notes": "Unleashes mayhem purely for domination."}, + "Reformed": {"prompt": "reformed antagonist, redemption arc", "notes": "Clawing back toward the light after dark deeds."}, + "Apathetic": {"prompt": "apathetic drift, muted aura", "notes": "Detached from moral stakes, acting only when forced."}, + "Paragon": {"prompt": "paragon brilliance, unwavering ethics", "notes": "Elevated idealist constantly striving for exemplary conduct."}, + "Anarch Idealist": {"prompt": "anarch idealist blaze, banner of autonomy", "notes": "Rejects authority while protecting community freedom."}, + "Pragmatic": {"prompt": "pragmatic balance, evaluating scales", "notes": "Focuses on outcomes over ideology, flexible morality."}, + "Ruthless": {"prompt": "ruthless precision, chill resolve", "notes": "Will sacrifice anything for objectives without remorse."}, + "Devoted": {"prompt": "devoted loyalty, sworn oath light", "notes": "Anchored to vows that guide every decision."}, + "Contrite": {"prompt": "contrite humility, penitent aura", "notes": "Driven by guilt to make amends through action."}, +} + +CHARACTER_ROLE_OPTIONS = { + "Party Leader": {"prompt": "party leader coordination, tactical bark", "notes": "Coordinates allies with decisive clarity."}, + "Scout": {"prompt": "pathfinder scout awareness, quiet tread", "notes": "Hunts for vantage and hidden threats."}, + "Support": {"prompt": "support specialist aura, restorative gestures", "notes": "Bolsters allies through buffs and morale."}, + "Artillery": {"prompt": "arcane artillery stance, wide-range focus", "notes": "Rains devastation from a distance."}, + "Face": {"prompt": "party face charm, negotiation toolkit", "notes": "First to speak in courts and bazaars alike."}, + "Infiltrator": {"prompt": "infiltrator shadow-step, code-break tools", "notes": "Silently cracks defenses from within."}, + "Tank": {"prompt": "frontline bulwark posture, threat magnet", "notes": "Soaks enemy focus while allies execute."}, + "Controller": {"prompt": "battlefield controller gestures, zone manipulation", "notes": "Shapes spaces to restrict foes."}, + "Summoner": {"prompt": "summoner sigils, conjured allies", "notes": "Commands linked entities for versatile tactics."}, + "Icon": {"prompt": "cultural icon presence, symbolic costumes", "notes": "Embodies a movement bigger than themselves."}, + "Tactician": {"prompt": "party tactician, battlefield schematics", "notes": "Coordinates formations and counters mid-battle."}, + "Healer": {"prompt": "dedicated healer focus, restorative glow", "notes": "Keeps allies standing through triage and magic."}, + "Saboteur": {"prompt": "behind-the-lines saboteur, trap rig", "notes": "Destroys enemy capabilities before conflicts erupt."}, + "Lorekeeper": {"prompt": "lorekeeper recitation, living archive", "notes": "Supplies critical intel and historical context."}, + "Logistician": {"prompt": "logistician organizer, supply drones", "notes": "Ensures resources and contingency plans never run dry."}, + "Champion": {"prompt": "dueling champion stance, challenge token", "notes": "Takes on the strongest foes in honorable combat."}, + "Handler": {"prompt": "handler coordination, companion command", "notes": "Directs allied creatures or constructs in battle."}, + "Oracle": {"prompt": "battle oracle trance, foresight glow", "notes": "Provides prophetic positions and warnings in real time."}, + "Envoy": {"prompt": "diplomatic envoy, crisis mediation kit", "notes": "Soothes tensions and secures alliances under duress."}, +} + +WARDROBE_BASE_GARMENT_OPTIONS = { + "Battle Dress": {"prompt": "battle dress core, reinforced bodice, articulated plating", "notes": "Armor-meets-fashion silhouette balancing protection and movement."}, + "Tactical Suit": {"prompt": "nano-fiber tactical suit, segmented plating", "notes": "Flexible tactical suit layered with modular attachment points."}, + "Gala Gown": {"prompt": "cascade gala gown, luminescent panels", "notes": "Statement gown engineered for spotlight drama."}, + "Streetwear Ensemble": {"prompt": "layered streetwear ensemble, asymmetric drape", "notes": "Urban streetwear mixing comfort with avant-garde tailoring."}, + "T-Shirt & Jeans": {"prompt": "soft cotton t-shirt paired with lived-in denim jeans", "notes": "Approachable everyday combo for grounded, contemporary storytelling."}, + "Button-Down & Slacks": {"prompt": "crisp button-down shirt with tailored slacks", "notes": "Smart casual office-ready base with clean lines."}, + "Hoodie & Joggers": {"prompt": "cozy hoodie layered over tapered joggers", "notes": "Comfort-forward athleisure ideal for relaxed scenes."}, + "Summer Sundress": {"prompt": "lightweight summer sundress, breezy hem", "notes": "Sun-ready silhouette perfect for daytime leisure moments."}, + "Business Suit": {"prompt": "structured business suit, sharp lapels", "notes": "Professional ensemble projecting confidence and polish."}, + "Casual Shorts Set": {"prompt": "relaxed tee with cuffed shorts", "notes": "Warm-weather casual look for carefree outings."}, + "Athletic Leggings Set": {"prompt": "moisture-wicking athletic tank with compression leggings", "notes": "Performance activewear tuned for training and dynamic motion."}, + "Classic Sweater & Skirt": {"prompt": "chunky knit sweater tucked into pleated skirt", "notes": "Cozy layered look balancing texture and movement."}, + "Bikini Set": {"prompt": "two-piece bikini with supportive top and high-cut bottoms", "notes": "Swim-ready ensemble for poolside or beach visuals."}, + "Swim Trunks": {"prompt": "quick-dry swim trunks with elastic waistband", "notes": "Casual swimwear for aquatic adventures."}, + "Aviator Uniform": {"prompt": "retro aviator uniform, bomber jacket, harnessed straps", "notes": "Flight-ready uniform with nostalgic military cues."}, + "Monastic Robes": {"prompt": "monastic robes, incense-scented fabric, hand-stitched trim", "notes": "Spiritual vestments woven with ceremonial symbolism."}, + "Void Suit": {"prompt": "void-rated suit, thermal seals, reflective plating", "notes": "Spacefaring ensemble engineered for vacuum resilience."}, + "Ranger Leathers": {"prompt": "weathered ranger leathers, botanical embossing", "notes": "Travel-ready leatherwork infused with natural motifs."}, + "Cyber Kimono": {"prompt": "cyber kimono, fiber-optic seams, programmable patterns", "notes": "Traditional silhouette fused with luminous smart textiles."}, + "Mage Tunic": {"prompt": "arcane mage tunic, rune-stitched layers", "notes": "Spellcaster attire optimized for ceremonial focus flows."}, + "Pilot Flight Suit": {"prompt": "pilot flight suit, sealed seams, mission patches", "notes": "All-purpose flight uniform with vacuum-rated protections."}, + "Desert Wraps": {"prompt": "desert nomad wraps, sun-bleached layers", "notes": "Breathable, protective layers designed for arid climates."}, + "Stealth Bodysuit": {"prompt": "stealth bodysuit, adaptive camouflage", "notes": "Low-profile stealth suit with active concealment."}, + "Festival Jumpsuit": {"prompt": "festival jumpsuit, holographic tassels", "notes": "Party-ready jumpsuit with playful color-reactive accents."}, + "Vintage Suit Dress": {"prompt": "mid-century suit dress, cinched waist", "notes": "Retro formalwear channeling classic silhouettes."}, + "Street Armor": {"prompt": "street armor jacket, panel plating", "notes": "Urban battle jacket blending style and protection."}, + "Battle Kimono": {"prompt": "battle kimono, reinforced sleeves, tactical obi", "notes": "War-ready robes designed for martial arts agility."}, + "Synthwave Tracksuit": {"prompt": "synthwave tracksuit, neon piping", "notes": "80s-inspired athletic suit glowing with retro flair."}, + "Explorer Coveralls": {"prompt": "explorer coveralls, tool loops", "notes": "Rugged field suit loaded with expedition-friendly pockets."}, + "Monarch Regalia": {"prompt": "monarch regalia, gem-set bodice", "notes": "Ceremonial finery befitting a ruling sovereign."}, + "Artisan Apron": {"prompt": "artisan apron, paint-splattered", "notes": "Studio-ready layers adorned with creative wear."}, + "Holographic Dress": {"prompt": "holographic dinner dress, shimmering panels", "notes": "Eveningwear projecting shifting light displays."}, + "Mech Pilot Rig": {"prompt": "mech pilot rig, reinforced joints", "notes": "Exosuit-compatible base layer for mechanized combat."}, + "Glacial Parka": {"prompt": "glacial expedition parka, fur-lined hood", "notes": "Insulated outerwear made for frozen vistas."}, + "Operatic Costume": {"prompt": "operatic costume, dramatic farthingale", "notes": "Stage costume bursting with theatrical grandeur."}, + "Biohazard Suit": {"prompt": "biohazard suit, sealed visors", "notes": "Containment gear for hazardous environments."}, + "Lab Coat Set": {"prompt": "tailored lab coat, utility pockets", "notes": "Professional scientific ensemble with clean precision."}, + "Nomad Armor": {"prompt": "nomad armor, scavenged plates", "notes": "Reclaimed armor pieces assembled for wasteland survival."}, + "Ceremonial Hanfu": {"prompt": "ceremonial hanfu, sweeping sleeves", "notes": "Traditional attire layered with historical elegance."}, +} + +WARDROBE_LAYER_OPTIONS = { + "Armor Plating": {"prompt": "layered armor plating, reactive segments", "notes": "Deployable plating that stiffens on impact."}, + "Sheer Overlay": {"prompt": "sheer organza overlay, cascading transparency", "notes": "Gossamer layer that refracts stage lighting."}, + "Draped Cape": {"prompt": "floor-length draped cape, weighted hem", "notes": "Dramatic cloak engineered to billow with motion."}, + "Harness Rig": {"prompt": "utility harness rig, quick-release clasps", "notes": "Functional harness ready for gear clips and tether lines."}, + "Layered Sash": {"prompt": "layered sash bundle, symbolic knots", "notes": "Sashes encoded with cultural symbology and achievements."}, + "Feather Mantle": {"prompt": "feather mantle, iridescent plumage", "notes": "Plumage mantle rippling with chromatic sheen."}, + "Hooded Shawl": {"prompt": "hooded shawl, quieted interior", "notes": "Noise-dampening hood for stealth and focus."}, + "Holster Web": {"prompt": "holster webbing, magnetized docks", "notes": "Holster network distributing signature tools."}, + "Scale Veil": {"prompt": "scale veil drape, metallic tessellation", "notes": "Articulated scales shimmering like liquid metal."}, + "Thermal Cloak": {"prompt": "thermal cloak, heating filaments", "notes": "Climate-responsive cloak shielding from extremes."}, + "Denim Jacket": {"prompt": "faded denim jacket, lived-in creases", "notes": "Casual layer adding texture to modern outfits."}, + "Cardigan": {"prompt": "soft knit cardigan, open front drape", "notes": "Comfortable knit layer ideal for cozy scenes."}, + "Tailored Blazer": {"prompt": "tailored blazer, structured shoulders", "notes": "Polished layer dialing up professional energy."}, + "Raincoat": {"prompt": "lightweight raincoat, snap closures", "notes": "Weather-ready shell keeping things practical."}, + "Fur Stole": {"prompt": "luxury fur stole, draped shoulders", "notes": "Plush accent layer radiating classic glamour."}, + "Holo Poncho": {"prompt": "holo poncho, refractive fringe", "notes": "Digital poncho projecting responsive patterns."}, + "Battle Tabard": {"prompt": "house tabard, banner insignia", "notes": "Battlefield garment emblazoned with heraldry."}, + "Utility Shrug": {"prompt": "utility shrug, magnetic closures", "notes": "Compact shrug integrating quick-access compartments."}, + "Chain Mantle": {"prompt": "chain mantle, draped links", "notes": "Heavy chains draping the shoulders in metallic arcs."}, + "Solar Shawl": {"prompt": "solar shawl, light-absorbing fibers", "notes": "Sun-charged wrap emitting gentle warmth at night."}, + "Wing Harness": {"prompt": "wing harness, kinetic joints", "notes": "Support rig for detachable wings or gliders."}, + "Sequin Bolero": {"prompt": "sequin bolero, cropped sparkle", "notes": "Showpiece bolero packed with glittering sequins."}, + "Tech Gilet": {"prompt": "tech gilet, embedded processors", "notes": "Sleeveless interface layer managing wearable hardware."}, +} + +WARDROBE_FABRIC_OPTIONS = { + "Velvet": {"prompt": "plush velvet, deep pile sheen", "notes": "Luxurious velvet catching soft highlights."}, + "Ballistic Weave": {"prompt": "ballistic weave textile, abrasion guard", "notes": "Impact-resistant weave favored by tactical crews."}, + "Silk Brocade": {"prompt": "silk brocade, gold-thread relief", "notes": "Opulent brocade shimmering with embroidered relief."}, + "Smartfabric": {"prompt": "adaptive smartfabric, mood-responsive pigments", "notes": "Interactive textile shifting hue with biometrics."}, + "Holo Mesh": {"prompt": "holographic mesh, prismatic sheen", "notes": "Spectral mesh projecting animated motifs."}, + "Leather": {"prompt": "supple oil-tanned leather, weathered patina", "notes": "Durable leather aged by countless journeys."}, + "Linen": {"prompt": "breathable linen, handloom weave", "notes": "Natural linen lending breathable ease."}, + "Chainmail Lace": {"prompt": "chainmail lace, interlinked micro rings", "notes": "Metallic lacework balancing defense and grace."}, + "Chitin Laminate": {"prompt": "chitin laminate plates, biopolymer sheen", "notes": "Organic exoshell laminate reinforcing structure."}, + "Plasma Gauze": {"prompt": "plasma-reactive gauze, ghostlight shimmer", "notes": "Gauze that glows softly when energized."}, + "Cotton": {"prompt": "soft combed cotton, breathable comfort", "notes": "Everyday cotton knit suited for relaxed wear."}, + "Denim": {"prompt": "sturdy denim twill, indigo wash", "notes": "Classic denim texture adding casual grit."}, + "Fleece": {"prompt": "plush fleece interior, cozy warmth", "notes": "Fleece lining delivering soft insulation."}, + "Jersey Knit": {"prompt": "stretch jersey knit, fluid drape", "notes": "Flexible knit fabric following natural movement."}, + "Neoprene": {"prompt": "sleek neoprene, smooth wetsuit sheen", "notes": "Water-friendly neoprene perfect for swim silhouettes."}, + "SeaSilk": {"prompt": "seasilk fibers, pearl sheen", "notes": "Rare ocean-spun fabric shimmering with aquatic gloss."}, + "Aerogel Weave": {"prompt": "aerogel weave, translucent insulation", "notes": "Ultra-light material offering vacuum-grade insulation."}, + "Reactive Latex": {"prompt": "reactive latex panels, liquid gloss", "notes": "High-shine polymer shifting texture with temperature."}, + "Feather Felt": {"prompt": "feather felt layers, downy softness", "notes": "Feather-compacted textile balancing warmth and lightness."}, + "Nano Knit": {"prompt": "nano-knit fibers, self-healing stitches", "notes": "Microbots continually repair the fabric's surface."}, + "Solar Mesh": {"prompt": "solar mesh filaments, photovoltaic strands", "notes": "Energy-harvesting mesh powering embedded devices."}, + "Crystalline Tulle": {"prompt": "crystalline tulle, glittering nodes", "notes": "Filmy net accented with suspended gemstones."}, + "Biolume Silk": {"prompt": "biolume silk, glowing threads", "notes": "Living silk colony generating soft ambient light."}, + "Memory Denim": {"prompt": "memory denim, shapeshift seams", "notes": "Denim that memorizes personal fits and reshapes as needed."}, +} + +WARDROBE_ACCESSORY_OPTIONS = { + "Relic Pendant": {"prompt": "relic pendant, memory-locked gem", "notes": "Pendant storing ancestral recordings."}, + "Utility Belt": {"prompt": "utility belt, compartmentalized pouches", "notes": "Belt organizing mission-critical tools."}, + "Quantum Bracelets": {"prompt": "quantum-entangled bracelets, flickering runes", "notes": "Linked bracelets enabling instant communication."}, + "Feathered Circlet": {"prompt": "feathered circlet, iridescent quills", "notes": "Headpiece crowned with skyborn plumage."}, + "Aether Earrings": {"prompt": "aether earrings, suspended crystals", "notes": "Earrings humming with harmonic resonance."}, + "Tactical Visor": {"prompt": "tactical visor, augmented reality HUD", "notes": "Visor projecting real-time battlefield overlays."}, + "Potion Bandolier": {"prompt": "potion bandolier, alchemical vials", "notes": "Bandolier stocked with quick-access elixirs."}, + "Charm Bangles": {"prompt": "charm bangles, layered talismans", "notes": "Bracelets jingling with protective charms."}, + "Void Gloves": {"prompt": "void-channeling gloves, fingerless design", "notes": "Gloves designed to safely redirect cosmic currents."}, + "Monocle Display": {"prompt": "monocle display, rotating data readouts", "notes": "Augmented monocle feeding curated intel."}, + "Baseball Cap": {"prompt": "soft baseball cap, curved brim", "notes": "Casual cap that instantly relaxes a look."}, + "Sunglasses": {"prompt": "sleek sunglasses, tinted lenses", "notes": "Shades that add modern swagger and sun protection."}, + "Tote Bag": {"prompt": "canvas tote bag, printed graphic", "notes": "Everyday carryall keeping essentials close."}, + "Data Sash": {"prompt": "data sash holsters, scrolling LEDs", "notes": "Sash storing encrypted data strips for quick reference."}, + "Storm Umbrella": {"prompt": "storm umbrella, lightning rod spine", "notes": "Weatherproof parasol doubling as electro-shield."}, + "Astrolabe Brooch": {"prompt": "astrolabe brooch, rotating rings", "notes": "Pin that charts stellar alignments in real time."}, + "Pulse Anklets": {"prompt": "pulse anklets, kinetic lights", "notes": "Anklets emitting rhythmic glows synchronized with movement."}, + "Holster Purse": {"prompt": "holster purse, tactical compartments", "notes": "Crossover bag balancing style and functionality."}, + "Glyph Gloves": {"prompt": "glyph gloves, glowing sigils", "notes": "Gloves projecting spell matrices during gestures."}, + "Rebreather Necklace": {"prompt": "rebreather necklace, collapsible mouthpiece", "notes": "Emergency respirator disguised as elegant jewelry."}, + "Signal Fan": {"prompt": "signal fan, colored transmitters", "notes": "Fan that conveys covert signals via shifting hues."}, + "Chrono Cuff": {"prompt": "chrono cuff, time-stop dial", "notes": "Temporal cuff offering short-duration field slowdowns."}, +} + +WARDROBE_FOOTWEAR_OPTIONS = { + "Combat Boots": {"prompt": "combat boots, reinforced toe, shock absorbers", "notes": "Mil-spec boots tuned for stability."}, + "Hover Heels": {"prompt": "hover heels, anti-grav emitters", "notes": "Levitation-boosted footwear gliding with each step."}, + "Barefoot Wraps": {"prompt": "barefoot wraps, pressure-mapped grip", "notes": "Foot wraps enabling silent movement."}, + "Ceremonial Sandals": {"prompt": "ceremonial sandals, gilt straps", "notes": "Ritual sandals echoing ancient rites."}, + "Tactical Greaves": {"prompt": "tactical greaves, knee articulation", "notes": "Armored greaves safeguarding lower limbs."}, + "Riding Boots": {"prompt": "knee-high riding boots, polished leather", "notes": "Equestrian boots suited for long journeys."}, + "Mag Boots": {"prompt": "magnetic mag boots, hull-walk capable", "notes": "Zero-g rated boots anchoring to metallic surfaces."}, + "Pulse Sneakers": {"prompt": "pulse sneakers, energy return midsole", "notes": "Athletic footwear with reactive cushioning."}, + "Gilded Slippers": {"prompt": "gilded slippers, opaline finish", "notes": "Luxury slippers radiating opulent charm."}, + "Gecko Pads": {"prompt": "gecko grip pads, wall-scaling microfibers", "notes": "Microfiber pads adhering to vertical terrain."}, + "Classic Sneakers": {"prompt": "classic lace-up sneakers, rubber sole", "notes": "Everyday sneakers built for all-day comfort."}, + "Leather Loafers": {"prompt": "polished leather loafers, stacked heel", "notes": "Smart slip-on footwear for business casual looks."}, + "Strappy Sandals": {"prompt": "strappy leather sandals, cork footbed", "notes": "Warm-weather sandals perfect for beach scenes."}, + "Flip Flops": {"prompt": "foam flip flops, casual beachwear", "notes": "Simple beach footwear for relaxed narratives."}, + "Heeled Ankle Boots": {"prompt": "heeled ankle boots, suede finish", "notes": "Versatile heeled boots adding height and style."}, + "Crystal Heels": {"prompt": "crystal heels, prismatic lift", "notes": "Faceted heels refracting light trails with each step."}, + "Sandwalkers": {"prompt": "sandwalker boots, dune grip", "notes": "Desert-adapted boots dispersing heat and sand."}, + "Storm Galoshes": {"prompt": "storm galoshes, insulating seal", "notes": "Weather armor boots built for torrential conditions."}, + "Kinetic Skates": {"prompt": "kinetic skates, retractable wheels", "notes": "Footwear switching between high-speed glide and stable stance."}, + "Forest Claspers": {"prompt": "forest clasper boots, root hooks", "notes": "Tree-climbing boots with adaptive grip tendrils."}, + "Exo Talons": {"prompt": "exo talon greaves, clawed toes", "notes": "Augmented talons for vertical scaling in heavy armor."}, + "Ceremonial Geta": {"prompt": "ceremonial geta, lacquered platforms", "notes": "Traditional elevated sandals adorned with painted motifs."}, + "Void Slippers": {"prompt": "void slippers, anti-sound lining", "notes": "Silent slippers favored by stealth operatives."}, + "Solar Trainers": {"prompt": "solar trainers, lightstrip soles", "notes": "Footwear powered by sunlight to enhance sprinting bursts."}, +} + +HUMAN_DESIGN_GENDER_OPTIONS = { + "Adult Woman": { + "prompt": "adult woman", + "notes": "Explicitly 18+ feminine presentation with mature proportions.", + }, + "Adult Man": { + "prompt": "adult man", + "notes": "Explicitly 18+ masculine presentation with mature stature.", + }, + "Adult Female": { + "prompt": "adult female body", + "notes": "Female anatomy described with respectful, adult-focused language.", + }, + "Adult Male": { + "prompt": "adult male body", + "notes": "Male anatomy described with respectful, adult-focused language.", + }, + "Adult Nonbinary": { + "prompt": "adult nonbinary body", + "notes": "Androgynous presentation with mature proportions and subtle balance.", + }, + "Adult Androgynous": { + "prompt": "adult androgynous figure", + "notes": "Elegant, mature androgyny blending traditionally masculine and feminine cues.", + }, + "Adult Genderfluid": { + "prompt": "adult genderfluid presence", + "notes": "Fluid presentation celebrated through confident, adult styling.", + }, + "Adult Agender": { + "prompt": "adult agender body", + "notes": "Neutral silhouette minimizing gendered emphasis while remaining distinctly adult.", + }, +} + +HUMAN_DESIGN_HEIGHT_OPTIONS = { + "Petite": { + "prompt": "petite stature around 5'2\" / 157 cm", + "notes": "Compact height with graceful proportions.", + }, + "Average": { + "prompt": "average height near 5'7\" / 170 cm", + "notes": "Balanced height typical for adult humans.", + }, + "Tall": { + "prompt": "tall frame around 6'1\" / 185 cm", + "notes": "Notably tall presence with extended reach.", + }, + "Towering": { + "prompt": "towering height near 6'6\" / 198 cm", + "notes": "Commanding height that stands out immediately.", + }, + "Diminutive": { + "prompt": "diminutive height around 4'11\" / 150 cm", + "notes": "Very short adult frame with agile stature.", + }, + "Statuesque": { + "prompt": "statuesque height near 5'11\" / 180 cm", + "notes": "Gracefully tall proportions ideal for striking silhouettes.", + }, + "Colossal": { + "prompt": "colossal height approaching 7'2\" / 218 cm", + "notes": "Extraordinary height suited to mythical or augmented figures.", + }, + "Average Tall": { + "prompt": "above-average height near 5'9\" / 175 cm", + "notes": "Subtly tall without feeling out of place in everyday scenes.", + }, + "Average Short": { + "prompt": "below-average height near 5'4\" / 163 cm", + "notes": "Slightly short adult presence balancing reach and comfort.", + }, +} + +HUMAN_DESIGN_FACE_OPTIONS = { + "Oval": { + "prompt": "oval face, gently tapered jawline", + "notes": "Balanced proportions with softly rounded features.", + }, + "Heart": { + "prompt": "heart-shaped face, prominent cheekbones, tapered chin", + "notes": "Expressive cheek structure with a delicate chin.", + }, + "Angular": { + "prompt": "angular face, sharp jawline, defined cheek planes", + "notes": "Sculpted geometry lending dramatic presence.", + }, + "Round": { + "prompt": "round face, full cheeks, soft jawline", + "notes": "Youthful softness paired with smooth contours.", + }, + "Diamond": { + "prompt": "diamond face, high cheekbones, narrow forehead and chin", + "notes": "Faceted elegance with striking cheek definition.", + }, + "Square": { + "prompt": "square face, broad jawline, even forehead width", + "notes": "Defined angles conveying strength and confidence.", + }, + "Triangular": { + "prompt": "triangular face, tapered forehead, wider jaw", + "notes": "Foundation-heavy shape grounded by solid jawlines.", + }, + "Oblong": { + "prompt": "oblong face, elongated proportions, gentle angles", + "notes": "Extended vertical lines with refined slender elegance.", + }, + "Heart-Soft": { + "prompt": "soft heart face, rounded hairline, subtle chin", + "notes": "Heart-shaped nuance softened by gentle contours.", + }, +} + +HUMAN_DESIGN_BODY_TYPE_OPTIONS = { + "Athletic": { + "prompt": "athletic muscle tone, defined limbs", + "notes": "Functional strength with evident conditioning.", + }, + "Soft Curvy": { + "prompt": "soft curvy build with gentle lines", + "notes": "Supple silhouette favoring plush contours.", + }, + "Lean": { + "prompt": "lean physique, long lines", + "notes": "Slender frame emphasizing graceful length.", + }, + "Stocky": { + "prompt": "stocky build, dense musculature", + "notes": "Compact power with grounded stance.", + }, + "Plus Size": { + "prompt": "plus-size figure with full curves", + "notes": "Celebrates abundant curves with confident posture.", + }, + "Muscular": { + "prompt": "muscular build with pronounced definition", + "notes": "Gym-honed physique showcasing sculpted muscle groups.", + }, + "Willowy": { + "prompt": "willowy frame, delicate limbs", + "notes": "Graceful slenderness paired with airy posture.", + }, + "Broad": { + "prompt": "broad build, expanded shoulders", + "notes": "Wide frame projecting steadfast presence.", + }, + "Average": { + "prompt": "average build, balanced proportions", + "notes": "Everyday physique offering versatile styling.", + }, +} + +HUMAN_DESIGN_BODY_SHAPE_OPTIONS = { + "Hourglass": { + "prompt": "hourglass proportions, balanced bust and hips, defined waist", + "notes": "Classic hourglass balance with prominent waist definition.", + }, + "Rectangle": { + "prompt": "rectangular proportions, parallel lines from shoulders to hips", + "notes": "Athletic straight silhouette with minimal taper.", + }, + "Pear": { + "prompt": "pear shape, narrower shoulders, fuller hips and thighs", + "notes": "Lower-body emphasis creating grounded elegance.", + }, + "Inverted Triangle": { + "prompt": "inverted triangle shape, broad shoulders, tapered hips", + "notes": "Upper-body strength tapering to lean hips.", + }, + "Apple": { + "prompt": "apple shape, fuller midsection with rounded torso", + "notes": "Central softness complemented by smooth limbs.", + }, + "Athletic V": { + "prompt": "athletic V shape, sculpted shoulders tapering waist", + "notes": "Sport-driven silhouette defined by upper-body strength.", + }, + "Diamond": { + "prompt": "diamond shape, fuller midsection and shoulders, narrow hips", + "notes": "Eye-catching mid-body emphasis with tapered hip line.", + }, + "Oval": { + "prompt": "oval shape, soft curve distribution", + "notes": "Gentle fullness evenly wrapped around torso.", + }, + "Straight": { + "prompt": "straight column shape, minimal waist definition", + "notes": "Clean linear silhouette ideal for draped garments.", + }, +} + +HUMAN_DESIGN_HAIR_LENGTH_OPTIONS = { + "Pixie": { + "prompt": "pixie-length hair cropped close to the scalp", + "notes": "Playful short crop attracting focus to facial structure.", + }, + "Chin": { + "prompt": "chin-length hair framing the jaw", + "notes": "Tailored bob emphasizing jawline definition.", + }, + "Shoulder": { + "prompt": "shoulder-length hair resting on the collarbones", + "notes": "Versatile medium length suited for numerous styles.", + }, + "Mid-back": { + "prompt": "mid-back length hair with flowing strands", + "notes": "Luxurious length that sways with movement.", + }, + "Floor": { + "prompt": "dramatically long hair trailing toward the floor", + "notes": "Fantastical length creating ethereal motion.", + }, + "Buzz": { + "prompt": "buzz-cut hair, ultra-short stubble", + "notes": "Crisp close shave showcasing head shape.", + }, + "Ear": { + "prompt": "ear-length crop, playful bounce", + "notes": "Short cut brushing the ears for lively motion.", + }, + "Waist": { + "prompt": "waist-length hair, heavy braid potential", + "notes": "Sweeping length ideal for intricate styling.", + }, + "Cascading": { + "prompt": "cascading layered hair, mid-waist flow", + "notes": "Layered lengths adding dynamic movement.", + }, +} + +HUMAN_DESIGN_HAIR_COLOR_OPTIONS = { + "Deep Brunette": { + "prompt": "deep brunette hair with espresso undertones", + "notes": "Rich brown palette catching warm highlights.", + }, + "Golden Blonde": { + "prompt": "golden blonde hair with sunlit glow", + "notes": "Radiant blonde gradients with bright sheen.", + }, + "Copper Red": { + "prompt": "copper red hair with fiery vibrance", + "notes": "Spirited red spectrum glowing under light.", + }, + "Midnight Black": { + "prompt": "midnight black hair, mirror-gloss finish", + "notes": "Inky black hair delivering striking contrast.", + }, + "Silver": { + "prompt": "silver hair with metallic shimmer", + "notes": "Cool silver tones hinting at ethereal maturity.", + }, + "Fantasy Pastel": { + "prompt": "pastel fantasy hair blending cotton-candy hues", + "notes": "Playful colorway spanning lilac, teal, and blush.", + }, + "Icy Platinum": { + "prompt": "icy platinum hair, frosted sheen", + "notes": "Bleached platinum with glacial sparkle.", + }, + "Rose Gold": { + "prompt": "rose gold hair, metallic blush", + "notes": "Warm rosy metallic tones shimmering softly.", + }, + "Emerald Green": { + "prompt": "emerald green hair, jewel-toned depth", + "notes": "Vibrant green cascade reminiscent of enchanted forests.", + }, + "Midnight Blue": { + "prompt": "midnight blue hair, cosmic lowlights", + "notes": "Deep indigo hue echoing star-streaked skies.", + }, +} + +HUMAN_DESIGN_HAIR_STYLE_OPTIONS = { + "Sleek Straight": { + "prompt": "sleek straight hair, smooth and polished", + "notes": "Glass-like finish with disciplined alignment.", + }, + "Loose Waves": { + "prompt": "loose waves cascading with gentle movement", + "notes": "Relaxed wave pattern suggesting soft motion.", + }, + "Defined Curls": { + "prompt": "defined curls spiraling with springy volume", + "notes": "Structured curls amplifying texture and bounce.", + }, + "Braided Crown": { + "prompt": "braided crown wrapping the head", + "notes": "Intricate braiding delivering regal symmetry.", + }, + "Locs": { + "prompt": "shoulder-length locs with neat sectioning", + "notes": "Textured locs celebrating cultural heritage.", + }, + "Shaved Fade": { + "prompt": "shaved sides with tapered fade and longer top", + "notes": "Bold undercut combining edge with styling versatility.", + }, + "High Ponytail": { + "prompt": "high ponytail swept upward, dynamic lift", + "notes": "Energetic style emphasizing motion and confidence.", + }, + "Space Buns": { + "prompt": "double space buns perched atop the head", + "notes": "Playful style balancing symmetry and whimsy.", + }, + "Fishtail Braid": { + "prompt": "fishtail braid draping over one shoulder", + "notes": "Detailed plait showcasing craftsmanship and texture.", + }, + "Afro": { + "prompt": "rounded afro halo, voluminous coils", + "notes": "Proud natural coil shape radiating strength.", + }, + "Vintage Rolls": { + "prompt": "vintage victory rolls framing the crown", + "notes": "Retro glam styling channeling mid-century flair.", + }, +} + +HUMAN_DESIGN_GENITAL_OPTIONS = { + "Vulva": { + "prompt": "adult vulva anatomy with soft natural contours", + "notes": "18+ only: describe consensual feminine anatomy respectfully.", + }, + "Penis": { + "prompt": "adult penis anatomy with mature proportion", + "notes": "18+ only: describe consensual masculine anatomy respectfully.", + }, + "Intersex": { + "prompt": "adult intersex anatomy blending traits", + "notes": "18+ only: honor intersex bodies with considerate language.", + }, + "Discrete": { + "prompt": "discreet reference to adult anatomy without explicit detail", + "notes": "18+ only: suggest presence subtly, prioritizing tasteful framing.", + }, + "Covered": { + "prompt": "covered adult anatomy beneath garments", + "notes": "18+ only: indirect reference emphasizing modest coverage.", + }, +} + +HUMAN_DESIGN_EYE_DETAIL_OPTIONS = { + "Almond Focus": { + "prompt": "almond-shaped eyes with soft outer taper", + "notes": "Balanced eye shape conveying attentive, relaxed focus.", + }, + "Round Radiance": { + "prompt": "round eyes with luminous openness", + "notes": "Rounded eyes projecting curiosity and emotive clarity.", + }, + "Monolid Grace": { + "prompt": "sleek monolid eyes with gentle fold", + "notes": "Elegant monolid structure celebrated with respect.", + }, + "Hooded Mystery": { + "prompt": "hooded eyes with soft upper lid shadow", + "notes": "Naturally shaded lids that accentuate subtle expressions.", + }, + "Upturned Spark": { + "prompt": "upturned eyes with lifted outer corners", + "notes": "Playful uplift adding lively charm to the gaze.", + }, + "Downturned Poise": { + "prompt": "downturned eyes with thoughtful softness", + "notes": "Gentle downward tilt evoking contemplative serenity.", + }, +} + +HUMAN_DESIGN_EYE_COLOR_OPTIONS = { + "Amber Luminescence": { + "prompt": "amber eyes with sunlit flecks", + "notes": "Warm golden irises catching honeyed highlights.", + }, + "Ocean Blue": { + "prompt": "ocean blue eyes with mirrored sheen", + "notes": "Vivid blue irises reflecting cool maritime depth.", + }, + "Verdant Green": { + "prompt": "emerald green eyes with jewel clarity", + "notes": "Brilliant green gaze reminiscent of forest light.", + }, + "Smoky Hazel": { + "prompt": "smoky hazel eyes with amber halo", + "notes": "Hazel irises blending brown and gold undertones.", + }, + "Warm Brown": { + "prompt": "warm brown eyes with soft chestnut depth", + "notes": "Comforting brunette hues radiating grounded warmth.", + }, + "Silver Grey": { + "prompt": "silver grey eyes with crystal shimmer", + "notes": "Cool grey irises catching metallic highlights.", + }, + "Violet Aura": { + "prompt": "violet eyes with rare amethyst glow", + "notes": "Distinctive violet tint lending dreamlike allure.", + }, +} + +HUMAN_DESIGN_SKIN_TONE_OPTIONS = { + "Porcelain Dawn": { + "prompt": "porcelain skin with peach undertone", + "notes": "Luminous fair complexion carrying gentle warmth.", + }, + "Warm Honey": { + "prompt": "warm honey skin with golden undertone", + "notes": "Sun-touched glow radiating healthy vibrance.", + }, + "Golden Olive": { + "prompt": "golden olive skin with sunlit depth", + "notes": "Mediterranean-inspired tones balancing green-gold undertones.", + }, + "Copper Ember": { + "prompt": "copper skin with burnished highlights", + "notes": "Rich copper warmth reflecting fiery brilliance.", + }, + "Rich Umber": { + "prompt": "rich umber skin with velvety depth", + "notes": "Deep brown complexion glowing with lowlight sheen.", + }, + "Espresso Luxe": { + "prompt": "espresso skin with satin finish", + "notes": "Inky deep complexion accented by subtle sheen.", + }, + "Rosy Beige": { + "prompt": "rosy beige skin with flushed highlights", + "notes": "Neutral beige infused with natural rosy bloom.", + }, + "Cool Mocha": { + "prompt": "cool mocha skin with muted undertone", + "notes": "Balanced cool-toned brown offering refined softness.", + }, +} + +HUMAN_DESIGN_SKIN_DETAIL_OPTIONS = { + "Soft Glow": { + "prompt": "skin softly luminous with dewy finish", + "notes": "Hydrated complexion with ambient light reflection.", + }, + "Velvet Matte": { + "prompt": "skin with velvet matte texture", + "notes": "Powdery matte finish highlighting contour definition.", + }, + "Sun-Kissed Freckles": { + "prompt": "sun-kissed freckles dusted across cheeks", + "notes": "Sprinkled freckles celebrating authentic outdoor life.", + }, + "Glazed Highlight": { + "prompt": "glazed highpoints catching radiant sheen", + "notes": "Glassy highlights accenting cheekbones and brow ridge.", + }, + "Marble Smooth": { + "prompt": "marble-smooth skin with refined pores", + "notes": "Even texture delivering polished elegance.", + }, + "Lived-In Realism": { + "prompt": "lived-in skin with subtle lines and warmth", + "notes": "Mature complexion honoring natural texture and history.", + }, + "Studio Gloss": { + "prompt": "studio-lit skin with editorial gloss", + "notes": "High-fashion sheen ideal for dramatic lighting.", + }, +} + +HUMAN_DESIGN_BEAUTY_MARK_OPTIONS = { + "Bare": { + "prompt": "", + "notes": "Natural canvas without notable beauty marks.", + }, + "Cheek Constellation": { + "prompt": "cluster of freckles across cheeks", + "notes": "Freckle constellation adding playful charm.", + }, + "Upper Lip Signature": { + "prompt": "signature beauty mark above the lip", + "notes": "Classic beauty mark evoking vintage glamour.", + }, + "Collarbone Accent": { + "prompt": "beauty mark resting at the collarbone", + "notes": "Subtle mark drawing attention to neckline.", + }, + "Eye Corner Fleck": { + "prompt": "tiny fleck near the outer eye corner", + "notes": "Delicate mark accentuating expressive gaze.", + }, + "Shoulder Star": { + "prompt": "freckle cluster along the shoulder", + "notes": "Sun-kissed marks emphasizing lived-in warmth.", + }, + "Hip Muse": { + "prompt": "beauty mark along the hip curve", + "notes": "Intimate placement adding bespoke allure.", + }, +} + +WARDROBE_COLORWAY_OPTIONS = { + "Monochrome Obsidian": {"prompt": "monochrome obsidian palette, layered blacks, matte to gloss", "notes": "Black-on-black layering emphasizing texture over hue."}, + "Solar Burst": {"prompt": "solar burst gradient, saffron to rose gold", "notes": "Sunrise gradient igniting dramatic silhouettes."}, + "Moonlit Pastel": {"prompt": "moonlit pastel wash, lavender mist, silver trims", "notes": "Soft pastels cooled by moonlit silver accents."}, + "Aqua Neon": {"prompt": "aqua neon spectrum, cyan energy veins", "notes": "Vibrant teal with electric highlights."}, + "Autumn Ember": {"prompt": "autumn ember palette, russet, ember orange", "notes": "Warm autumn hues echoing fallen leaves."}, + "Verdigris": {"prompt": "verdigris patina, weathered copper tones", "notes": "Aged metal palette blending green and bronze."}, + "Storm Prism": {"prompt": "storm prism palette, slate blues, lightning white", "notes": "Storm-swept palette with flashes of brilliance."}, + "Ivory & Ink": {"prompt": "ivory and ink contrast, sharp monochrome", "notes": "High contrast interplay of pale and dark surfaces."}, + "Sunset Hologram": {"prompt": "sunset hologram iridescence, shifting magenta gold", "notes": "Angle-dependent holographic sunset sheen."}, + "Toxic Bloom": {"prompt": "toxic bloom palette, biohazard lime, ultraviolet trim", "notes": "Bold palette evoking bioluminescent flora."}, + "Classic Black": {"prompt": "classic black palette, matte and satin blacks", "notes": "Monochrome black styling for timeless minimalism."}, + "Soft Pastels": {"prompt": "soft pastel palette, powder pink, sky blue, mint", "notes": "Gentle candy hues perfect for casual looks."}, + "Earthy Neutrals": {"prompt": "earthy neutrals, warm beige, olive, terracotta", "notes": "Natural neutral mix for grounded realism."}, + "Primary Pop": {"prompt": "primary color pop, bold red, cobalt, sunshine yellow", "notes": "High-energy palette with graphic clarity."}, + "Denim Blues": {"prompt": "denim blue gradient, indigo to faded sky", "notes": "Layered denim-inspired blues for casual wear."}, + "Beach Brights": {"prompt": "beach brights, coral, seafoam, sunbleached white", "notes": "Vibrant beach palette tailored for swim looks."}, + "Arcane Jewel": {"prompt": "arcane jewel tones, amethyst, sapphire, emerald", "notes": "Opulent jewel palette tuned for mage aesthetics."}, + "Cyber Chrome": {"prompt": "cyber chrome palette, silver, electric teal", "notes": "High-gloss metallics paired with neon accents."}, + "Forest Canopy": {"prompt": "forest canopy palette, moss, pine, bark", "notes": "Layered greens capturing woodland depth."}, + "Ember Noir": {"prompt": "ember noir palette, charcoal with ember highlights", "notes": "Smoldering interplay of blackened reds and smoke."}, + "Polar Shift": {"prompt": "polar shift palette, glacier blue, aurora mint", "notes": "Arctic cool palette with luminous ice highlights."}, + "Desert Dusk": {"prompt": "desert dusk palette, mauve sand, twilight purple", "notes": "Muted desert sunset blending warmth and cool."}, + "Vintage Sepia": {"prompt": "vintage sepia wash, parchment tan, faded brown", "notes": "Retro palette invoking aged photographs."}, + "Candy Pop": {"prompt": "candy pop palette, bubblegum pink, lemon fizz", "notes": "Sugary high-saturation palette for playful styling."}, + "Industrial Rust": {"prompt": "industrial rust palette, oxidized orange, iron", "notes": "Weathered industrial hues suggesting grit and age."}, +} + +WARDROBE_PATTERN_OPTIONS = { + "Fractal": {"prompt": "fractal tessellation pattern, recursive geometry", "notes": "Endless fractal motif that feels otherworldly."}, + "Constellation": {"prompt": "constellation embroidery, starlit map", "notes": "Star chart motifs mapping ancestral skies."}, + "Circuitry": {"prompt": "circuit board etching, luminous trace", "notes": "Printed circuits glowing with micro current."}, + "Runic": {"prompt": "runic etching, glowing sigils", "notes": "Glyph script broadcasting protective wards."}, + "Botanical": {"prompt": "botanical prints, climbing vines", "notes": "Organic floral motifs wrapping the silhouette."}, + "Animalistic": {"prompt": "animalistic spotting, gradient stripes", "notes": "Predatory patterns referencing apex creatures."}, + "Plaid": {"prompt": "precision plaid, woven geometry", "notes": "Classic plaid reinterpreted in modern textiles."}, + "Iridescent Marbling": {"prompt": "iridescent marbling, fluid swirls", "notes": "Liquid marble finish shimmering with color shifts."}, + "Sacred Geometry": {"prompt": "sacred geometry lattice, balanced symmetry", "notes": "Meditative geometry aligning chakral flow."}, + "Microtext": {"prompt": "microtext inscriptions, hidden messages", "notes": "Tiny script carrying encoded lore."}, + "Solid": {"prompt": "solid color finish, clean tone", "notes": "Simple solid base letting silhouette shine."}, + "Stripes": {"prompt": "striped pattern, alternating bands", "notes": "Classic stripes adding rhythm and direction."}, + "Polka Dot": {"prompt": "polka dot scatter, playful speckles", "notes": "Cheerful polka dots injecting retro charm."}, + "Chevron": {"prompt": "chevron zigzag, bold angles", "notes": "Dynamic zigzag adding directional energy."}, + "Paisley": {"prompt": "paisley swirls, ornate teardrops", "notes": "Intricate paisley pattern steeped in tradition."}, + "Digital Camo": {"prompt": "digital camouflage pixels, urban fade", "notes": "Pixelated camo bridging tactical and streetwear."}, + "Houndstooth": {"prompt": "houndstooth tessellation, monochrome bite", "notes": "Classic jagged motif reinvented for modern cuts."}, + "Galaxy Print": {"prompt": "galaxy print nebulas, star clusters", "notes": "Cosmic imagery swirling across fabric expanse."}, + "Plaque Lace": {"prompt": "lace overlay, translucent floral", "notes": "Delicate lace overlay adding romantic texture."}, + "Binary Code": {"prompt": "binary code columns, scrolling digits", "notes": "Tech-forward print broadcasting data streams."}, + "Graffiti": {"prompt": "graffiti spray layers, vibrant tags", "notes": "Street art energy splashed across the garment."}, + "Quilt Patchwork": {"prompt": "quilt patchwork panels, mismatched fabrics", "notes": "Upcycled patchwork celebrating eclectic heritage."}, +} + +WARDROBE_WEAR_STATE_OPTIONS = { + "Pristine": {"prompt": "meticulously pristine finish, crisp seams", "notes": "Fresh-from-workshop immaculate condition."}, + "Battle-Worn": {"prompt": "battle-worn scarring, carbon scoring", "notes": "Visible wear narrating countless encounters."}, + "Patched": {"prompt": "hand-patched patches, mismatched thread", "notes": "Resourceful repairs showcasing journey history."}, + "Aged Artisan": {"prompt": "artfully aged patina, curated distress", "notes": "Intentional weathering for heirloom authenticity."}, + "Rain-Slick": {"prompt": "rain-slick sheen, water-bead texture", "notes": "Freshly soaked finish glistening with droplets."}, + "Dusted": {"prompt": "desert dust coating, sun-bleached edges", "notes": "Dry, windworn finish rooted in arid expeditions."}, + "Icy Frost": {"prompt": "frost-rimed edging, crystalline glaze", "notes": "Frozen overcoat capturing winter chill."}, + "Gala Ready": {"prompt": "spotlight-ready polish, no visible seams", "notes": "Perfect presentation for regal events."}, + "Lab-Stained": {"prompt": "alchemical staining, reactive splashes", "notes": "Scientific pigment mapping failed experiments."}, + "Scorched": {"prompt": "heat-scorched panels, ember glow edges", "notes": "Charred accents telling of close calls with fire."}, + "Everyday Fresh": {"prompt": "freshly laundered finish, crisp press", "notes": "Clean ready-to-wear condition for daily life."}, + "Gently Worn": {"prompt": "gently worn patina, soft creases", "notes": "Light lived-in cues suggesting authenticity."}, + "Festival Paint": {"prompt": "festival paint splatter, neon remnants", "notes": "Bright paint streaks leftover from celebratory revelry."}, + "Snow Dusted": {"prompt": "snow-dusted trim, crystalline specks", "notes": "Fresh snowfall collecting along hems and shoulders."}, + "Oil-Stained": {"prompt": "oil-stained smears, mechanic residue", "notes": "Grease and oil marks telling a hands-on story."}, + "Shadow Patina": {"prompt": "shadow patina, subtle soot glow", "notes": "Smoky residue imparting a mysterious aura."}, + "Sun-Faded": {"prompt": "sun-faded gradient, washed pigments", "notes": "Color gently bleached by prolonged sunlight."}, + "Rain-Patched": {"prompt": "rain-patched repairs, waxed overlays", "notes": "Waterproof patches applied after storms."}, + "Field-Tested": {"prompt": "field-tested wear, scuffed plating", "notes": "Function-first condition showing rigorous deployment."}, + "Laboratory Sterile": {"prompt": "sterile lab press, anti-static clean", "notes": "Meticulous sterilization leaving crisp finish."}, + "Ceremonial Shine": {"prompt": "ceremonial high-polish, gleaming highlights", "notes": "Polished to mirror sheen for ritual importance."}, +} + +WARDROBE_SILHOUETTE_OPTIONS = { + "A-Line": {"prompt": "a-line silhouette, flared hem", "notes": "Classic A-line offering graceful flow."}, + "Mermaid": {"prompt": "mermaid silhouette, sculpted curve", "notes": "Tailored to flare dramatically at the calves."}, + "Duster": {"prompt": "duster length, elongated lines", "notes": "Sweeping length amplifying movements."}, + "Armored Bulk": {"prompt": "armored bulk profile, imposing mass", "notes": "Intimidating bulk emphasizing defensive power."}, + "Corseted": {"prompt": "corseted waist, cinched shaping", "notes": "Structured waist crafting hourglass emphasis."}, + "Layercake": {"prompt": "multi-tiered layers, cascading volume", "notes": "Stacked layers creating kinetic volume."}, + "Sleeveless Column": {"prompt": "sleeveless column profile, streamlined line", "notes": "Minimalist column silhouette for sleek modernity."}, + "Crop & Drop": {"prompt": "cropped top with drop panels, dynamic reveals", "notes": "Cropped upper matched with dramatic lower panels."}, + "Flight Ready": {"prompt": "flight-ready tapered silhouette, aerodynamic", "notes": "Aerodynamic tailoring reducing drag mid-air."}, + "Battle Cloak": {"prompt": "battle cloak silhouette, sweeping guards", "notes": "Layered cloak lines designed to intimidate."}, + "Straight Leg": {"prompt": "straight leg silhouette, relaxed drape", "notes": "Classic straight fit for jeans or trousers."}, + "Boxy Crop": {"prompt": "boxy cropped silhouette, squared volume", "notes": "Modern cropped profile balancing relaxed structure."}, + "Relaxed Fit": {"prompt": "relaxed fit silhouette, easy ease", "notes": "Laid-back fit emphasizing comfort and movement."}, + "Empire": {"prompt": "empire waist silhouette, elevated waistline", "notes": "High waist seam creating flowing lines below the bust."}, + "Peplum": {"prompt": "peplum flare silhouette, flounced waist", "notes": "Waist flare adding playful structured volume."}, + "Cocoon": {"prompt": "cocoon silhouette, rounded envelope", "notes": "Protective oval contour wrapping the body."}, + "High-Low": {"prompt": "high-low hem silhouette, cascading back", "notes": "Asymmetric hem creating dramatic trailing movement."}, + "Column Armor": {"prompt": "column armor silhouette, vertical plates", "notes": "Rigid vertical lines reinforcing imposing presence."}, + "Skater": {"prompt": "skater silhouette, fitted bodice, flared skirt", "notes": "Energetic flare ideal for motion-heavy scenes."}, + "Oversized": {"prompt": "oversized silhouette, exaggerated volume", "notes": "Deliberate volume cultivating avant-garde shapes."}, + "Tech Shell": {"prompt": "tech shell silhouette, modular panels", "notes": "Futuristic segmented layering with sleek contour."}, + "Draped Column": {"prompt": "draped column silhouette, fluid vertical folds", "notes": "Elegant draped lines hugging the figure softly."}, +} + +WARDROBE_CULTURE_OPTIONS = { + "Solar Court": {"prompt": "solar court heritage, sunburst sigils", "notes": "Regal culture venerating solar cycles."}, + "Glacier Nomads": {"prompt": "glacier nomad traditions, fur trims, ice beads", "notes": "Cold-adapted culture celebrating endurance."}, + "Desert Caravan": {"prompt": "desert caravan dyes, desert-bleached fabrics", "notes": "Dust-filtering fabrics patterned with trade route maps."}, + "Sky Temple": {"prompt": "sky temple robes, levitation-ready accents", "notes": "Soaring monasteries that blend flight and faith."}, + "Neon Syndicate": {"prompt": "neon syndicate emblem, rebellious graffiti stitches", "notes": "Counterculture collective glowing in neon defiance."}, + "Oceanic Sovereign": {"prompt": "oceanic sovereign regalwear, shell filigree", "notes": "Sea-born royalty with tidal embellishment."}, + "Starborn Dynasty": {"prompt": "starborn dynasty embroideries, constellation pearls", "notes": "Interstellar noble house mapping constellations into garments."}, + "Terracotta Order": {"prompt": "terracotta order armor, kiln-fired plates", "notes": "Ceramic armor steeped in ancient ritual."}, + "Verdant Commune": {"prompt": "verdant commune weaving, living vines", "notes": "Eco-society cultivating garments that grow with the wearer."}, + "Quantum Atelier": {"prompt": "quantum atelier craftsmanship, uncertainty patterns", "notes": "Avant-garde designers manipulating probability fabrics."}, + "Crystal Nomads": {"prompt": "crystal nomad caravans, prism charms", "notes": "Migratory artisans trading faceted crystal ornaments."}, + "Sky Dancers": {"prompt": "sky dancer clans, ribbonstream regalia", "notes": "Aerial performers weaving silk ribbons mid-flight."}, + "Deep Current": {"prompt": "deep current kingdom, biolum armor", "notes": "Undersea civilization plating garments with glowing reefs."}, + "Chronicle Keepers": {"prompt": "chronicle keeper archives, inked robes", "notes": "Historians inscribing outfits with living chronicle text."}, + "Volcanic Forge": {"prompt": "volcanic forge guild, ember-treated hides", "notes": "Smithing culture tempering textiles in molten vents."}, + "Aurora Court": {"prompt": "aurora court finery, polar light trims", "notes": "Courtly attire inspired by shimmering polar skies."}, + "Jungle Kin": {"prompt": "jungle kin tribe, woven lianas", "notes": "Rainforest collective crafting garments from living vines."}, + "Data Monks": {"prompt": "data monk monasteries, fiberoptic prayer beads", "notes": "Ascetics weaving illuminated code prayers into robes."}, + "Obsidian Guild": {"prompt": "obsidian guildtailors, volcanic glass mosaics", "notes": "Crafters inlaying volcanic glass across ceremonial plates."}, +} + +LIGHTING_BLUEPRINT_OPTIONS = { + "None": {"prompt": "", "notes": "No lighting blueprint emphasis."}, + "Three-Point": {"prompt": "three-point lighting setup, key fill back", "notes": "Classic key, fill, and backlight arrangement for balanced subjects."}, + "Rembrandt": {"prompt": "rembrandt key triangle, dramatic portrait shadows", "notes": "Portrait-ready triangular patch of light under the eye."}, + "Split": {"prompt": "split lighting, hard key from side", "notes": "Half the face lit, half in shadow for tension."}, + "Clamshell": {"prompt": "clamshell lighting, beauty dish above, reflector below", "notes": "Soft glamour lighting emphasizing smooth highlights."}, + "High Key": {"prompt": "high key flood, minimal shadows", "notes": "Bright, low-contrast illumination conveying upbeat energy."}, + "Low Key": {"prompt": "low key sculpture, dominant shadow play", "notes": "Moody, high-contrast lighting sculpting form."}, + "Silhouette": {"prompt": "silhouette blueprint, backlight dominant", "notes": "Backlit figure with minimal front fill."}, + "Blade Light": {"prompt": "blade light ribbon, thin slit illumination", "notes": "Precision ribbon of light slicing across the subject."}, + "Ring": {"prompt": "ring light halo, catchlight circle", "notes": "Even frontal illumination with circular catchlights."}, + "Stage Cross": {"prompt": "stage cross lighting, opposing spot beams", "notes": "Concert-style crisscross beams framing the subject."}, +} + +LIGHTING_KEY_STYLE_OPTIONS = { + "None": {"prompt": "", "notes": "No specific key light emphasis."}, + "Softbox Key": {"prompt": "softbox key light, creamy diffusion", "notes": "Large diffused key wrapping the subject in gentle gradients."}, + "Hard Spot": {"prompt": "hard spotlight key, crisp specular", "notes": "Focused key delivering sharp contrast."}, + "Fresnel": {"prompt": "fresnel focus key, cinematic carve", "notes": "Classic fresnel throw with controllable spill."}, + "Sunbeam": {"prompt": "sunbeam key shaft, natural brilliance", "notes": "Simulated sunlight streaming through set architecture."}, + "Lantern": {"prompt": "paper lantern key, omnidirectional glow", "notes": "Ambient lantern source ideal for ensemble coverage."}, + "Neon Strip": {"prompt": "neon strip key, colored edge light", "notes": "Linear neon bar painting chromatic edges."}, + "Volumetric Projector": {"prompt": "volumetric projector, patterned gobo wash", "notes": "Projector key layering complex shape patterns."}, + "Laser Curtain": {"prompt": "laser curtain key, micron-thin beams", "notes": "High-tech curtain of laser filaments slicing space."}, + "Bounce Key": {"prompt": "bounce card key, indirect spill", "notes": "Indirect bounce key for soft natural wrap."}, + "Specular Mirror": {"prompt": "specular mirror key, mirror bounce highlights", "notes": "Mirror-bounced key introducing intense highlights."}, +} + +LIGHTING_FILL_STYLE_OPTIONS = { + "None": {"prompt": "", "notes": "No additional fill style."}, + "Negative Fill": {"prompt": "negative fill flags, shadow deepen", "notes": "Flags absorbing spill to intensify contrast."}, + "Soft Fill": {"prompt": "soft fill reflector, gentle shadow lift", "notes": "Subtle fill lifting shadows while preserving shape."}, + "Bounce Wall": {"prompt": "bounce wall fill, large white surface", "notes": "Broad bounce creating even secondary illumination."}, + "RGB Ambient": {"prompt": "rgb ambient fill, color cycling", "notes": "Programmable ambient fill painting ambient gradients."}, + "Prism Scatter": {"prompt": "prism scatter fill, refracted spectrum", "notes": "Fill light pushed through prisms for chromatic edges."}, + "Firelight": {"prompt": "firelight fill, flicker simulation", "notes": "Dynamic flicker emulating open flame."}, + "Moon Bounce": {"prompt": "moonlight bounce, pale blue lift", "notes": "Cool-toned fill replicating moonlight reflection."}, + "Fog Diffuse": {"prompt": "fog diffuse fill, volumetric scatter", "notes": "Fog-charged fill softening transitions."}, + "Screen Panel": {"prompt": "led screen panel fill, texture playback", "notes": "LED panel looping environment for diegetic fill."}, + "Mirror Pool": {"prompt": "mirror pool bounce, watery shimmer", "notes": "Fill reflecting off shallow water to add shimmer."}, +} + +LIGHTING_BACKLIGHT_OPTIONS = { + "None": {"prompt": "", "notes": "No backlight emphasis."}, + "Rim Strip": {"prompt": "rim strip backlight, edge separation", "notes": "Thin strip lighting carving out silhouette."}, + "Hair Light": {"prompt": "hair light kicker, top-back sparkle", "notes": "Dedicated hair light adding crown highlights."}, + "Halo Arc": {"prompt": "halo arc backlight, 180-degree sweep", "notes": "Arcing backlight sculpting a halo edge."}, + "Laser Back": {"prompt": "laser backlight, volumetric shafts", "notes": "Laser back beams cutting through atmosphere."}, + "Bioluminescent": {"prompt": "bioluminescent backlight, organic glow", "notes": "Organic luminescence emanating from set pieces."}, + "Spark Shower": {"prompt": "spark shower backlight, metal grind", "notes": "Industrial sparks raining behind the subject."}, + "Sunset Flair": {"prompt": "sunset backlight, warm flare", "notes": "Backlit warmth mimicking golden hour edge light."}, + "Neon Halo": {"prompt": "neon halo hoop, saturated rim", "notes": "Neon hoop encircling the subject with color."}, + "Lightning Rig": {"prompt": "lightning rig arcs, strobing bolts", "notes": "Simulated lightning arcs pulsing behind."}, + "Hazer Beam": {"prompt": "hazer-enhanced beam, particulate sparkle", "notes": "Backlight combined with haze for sparkling particles."}, +} + +LIGHTING_PRACTICAL_OPTIONS = { + "None": {"prompt": "", "notes": "No practical lighting additions."}, + "Lantern Cluster": {"prompt": "lantern cluster practicals, hanging lantern glow", "notes": "Cluster of lanterns layering warm diegetic light."}, + "Holo Billboards": {"prompt": "holographic billboard spill, animated adverts", "notes": "Holographic signage bathing scene in neon adverts."}, + "Candle Field": {"prompt": "candle field practicals, dozens of flames", "notes": "Dense arrangement of candles for romantic shimmer."}, + "Arc Reactors": {"prompt": "arc reactor pedestals, blue-white pulses", "notes": "Industrial energy cores pulsing with raw power."}, + "Fiber Nebula": {"prompt": "fiber optic nebula strands, suspended constellations", "notes": "Fiber strands weaving starfield practicals."}, + "Alchemy Vats": {"prompt": "alchemy vats practical, bubbling glow", "notes": "Chemical vats radiating eerie internal light."}, + "City Windows": {"prompt": "tower window grid, staggered luminance", "notes": "Skyscraper windows creating urban texture."}, + "Campfire": {"prompt": "campfire practical, ember crackle", "notes": "Open flame grounding scene in nomadic warmth."}, + "Festival Lanterns": {"prompt": "festival ribbon lanterns, color wash", "notes": "Festival lantern strings enriching cultural mood."}, + "Emergency Strobes": {"prompt": "emergency strobes, red-blue oscillation", "notes": "Emergency lights cycling urgent color beats."}, +} + +LIGHTING_ATMOSPHERE_OPTIONS = { + "None": {"prompt": "", "notes": "No added atmospheric effect."}, + "Haze": {"prompt": "thin haze layer, volumetric definition", "notes": "Mild haze clarifying beams and depth."}, + "Fog Bank": {"prompt": "rolling fog bank, floor-level drift", "notes": "Dense fog hugging ground for mystery."}, + "Snowfall": {"prompt": "slow snowfall, glittering flakes", "notes": "Snow catching light for ethereal sparkle."}, + "Sand Drift": {"prompt": "desert sand drift, suspended grains", "notes": "Wind-blown sands creating scorched ambience."}, + "Ash Fall": {"prompt": "ash fall atmosphere, ember flecks", "notes": "Ash particles narrating a recent blaze."}, + "Petal Storm": {"prompt": "petal storm, floating blossoms", "notes": "Petals swirling for poetic motion."}, + "Electrical Mist": {"prompt": "ionized mist, static crackle", "notes": "Charged mist emitting faint lightning crackle."}, + "Bubble Field": {"prompt": "bubble field, refractive spheres", "notes": "Clusters of bubbles refracting rainbow highlights."}, + "Glitter Rain": {"prompt": "glitter rain fall, sparkling cascade", "notes": "Glitter-laced precipitation glinting in light."}, + "Aurora Veil": {"prompt": "aurora veil atmosphere, spectral curtain", "notes": "Auroral curtain draping across the sky."}, +} + +LIGHTING_COLOR_GEL_OPTIONS = { + "None": {"prompt": "", "notes": "No color gel adjustments."}, + "Teal & Orange": {"prompt": "teal and orange color contrast", "notes": "Cinematic complementary color pairing."}, + "Magenta Cyan": {"prompt": "magenta cyan split tone", "notes": "Vibrant chromatic opposition energizing midtones."}, + "Royal Purple": {"prompt": "royal purple gel wash", "notes": "Luxurious purple saturating highlights."}, + "Emerald": {"prompt": "emerald gel, deep verdant hues", "notes": "Green gel invoking enchanted forest tones."}, + "Amber Glow": {"prompt": "amber glow gel, warm embrace", "notes": "Soft amber warming skin tones elegantly."}, + "Ice Blue": {"prompt": "ice blue gel, crisp chill", "notes": "Cool blue casting a fresh, nocturnal feel."}, + "Infrared": {"prompt": "infrared gel, edge-spectrum tint", "notes": "Infrared filter for surreal color separation."}, + "Grunge Mix": {"prompt": "grunge gel mix, dirty sodium vapor", "notes": "Sodium-esque grime for dystopian scenes."}, + "Neon Pop": {"prompt": "neon pop gel, high saturation", "notes": "High-energy neon saturations for cyberpunk vibes."}, + "Pastel Bloom": {"prompt": "pastel bloom gel, gentle gradient", "notes": "Pastel gradient softening transitions."}, +} + +LIGHTING_CAM_SETTINGS = { + "None": {"prompt": "", "notes": "No camera setting adjustments."}, + "Cinematic": {"prompt": "cinematic exposure, shutter 1/48, iso 400", "notes": "Filmic settings for motion blur authenticity."}, + "Noir": {"prompt": "noir exposure, deep blacks, high contrast", "notes": "High contrast exposure chasing noir shadows."}, + "Documentary": {"prompt": "documentary settings, handheld realism", "notes": "Naturalistic exposure with slight grain."}, + "Hyperreal": {"prompt": "hyperreal hdr settings, crisp micro-detail", "notes": "HDR tuned for extraordinary clarity."}, + "Dreamscape": {"prompt": "dreamscape exposure, bloom-heavy", "notes": "Soft bloom with lifted blacks for dreamy feel."}, + "Retro Film": {"prompt": "retro film stock, iso 200, warm bias", "notes": "Vintage film simulation with warm bias."}, + "Action Sports": {"prompt": "action sports shutter 1/2000, frozen motion", "notes": "Fast shutter freezing explosive action."}, + "Long Exposure": {"prompt": "long exposure trails, tripod stability", "notes": "Intentional motion blur capturing light trails."}, + "Low Light": {"prompt": "low light push, iso 3200, noise texture", "notes": "Low light push processing embracing grain."}, + "Event Stage": {"prompt": "event stage metering, skin priority", "notes": "Spot metering tuned for stage performance."}, +} + +LIGHTING_SPECIAL_TECHNIQUES = { + "None": {"prompt": "", "notes": "No special lighting technique."}, + "Shutter Drag": {"prompt": "shutter drag blur, staccato flash freeze", "notes": "Flash plus slow shutter smearing ambient movement."}, + "Light Painting": {"prompt": "light painting streaks, handheld torch", "notes": "Painterly streaks drawn during long exposure."}, + "Projection Mapping": {"prompt": "projection mapped visuals, surface wrap", "notes": "Animated visuals mapped onto set geometry."}, + "Gobo Patterns": {"prompt": "gobo pattern washes, carved shadows", "notes": "Patterned gobos carving kinetic shadows."}, + "Lens Flares": {"prompt": "intentional lens flares, anamorphic streaks", "notes": "Controlled flares adding cinematic polish."}, + "Strobe Pulse": {"prompt": "strobe pulse bursts, rhythmic flash", "notes": "Timed strobes freezing micro-moments."}, + "Double Exposure": {"prompt": "double exposure in-camera blend", "notes": "Layered exposures blending scenes."}, + "Underlight": {"prompt": "underlighting practical, theatrical creep", "notes": "Upward lighting for eerie, dramatic mood."}, + "Soft Wrap": {"prompt": "soft wrap diffusion, bounce from multiple angles", "notes": "Multiple soft sources wrapping subject completely."}, + "Back Projection": {"prompt": "back projection plate, retro technique", "notes": "Vintage rear-projection mixing live and plate."}, +} + +LIGHTING_ENERGY_LEVEL_OPTIONS = { + "None": {"prompt": "", "notes": "No lighting energy emphasis."}, + "Serene": {"prompt": "serene luminance, gentle gradients", "notes": "Calm lighting with slow falloffs."}, + "Romantic": {"prompt": "romantic warmth, flickering softness", "notes": "Warm, intimate glow suited to close moments."}, + "Dynamic": {"prompt": "dynamic staccato pulses, sharp ratios", "notes": "Energetic lighting fueling action beats."}, + "Sinister": {"prompt": "sinister chiaroscuro, crawling shadows", "notes": "Menacing interplay of darkness and slit light."}, + "Triumphant": {"prompt": "triumphant brilliance, radiant beams", "notes": "Victorious glow drenching subject in light."}, + "Mystic": {"prompt": "mystic iridescence, unpredictable bloom", "notes": "Arcane lighting shimmering with spectral hues."}, + "Industrial": {"prompt": "industrial sodium vapor, gritty ambiance", "notes": "Utility lighting humming with mechanical hum."}, + "Apocalyptic": {"prompt": "apocalyptic firelight, ash-choked glow", "notes": "Harsh lighting referencing catastrophic aftermath."}, + "Celebratory": {"prompt": "celebratory confetti sparkle, multi-color wash", "notes": "Party-ready mix bursting with excitement."}, + "Sci-Fi Clinical": {"prompt": "clinical white glow, sterile gradient", "notes": "Lab-grade illumination crisp and clean."}, +} + +LIGHTING_SAFETY_NOTES = { + "Pyro": "Coordinate with pyro technician, clear safety perimeter.", + "Laser": "Use eye-safe laser modules and avoid reflective surfaces.", + "Water": "Isolate electrical gear from water effects, use GFCI protection.", + "Crowded Set": "Mark cable runs and secure fixtures overhead for extras.", + "Strobe": "Warn crew about strobing to protect those with sensitivities.", + "Fog": "Ventilate space and monitor particulate exposure for crew.", + "Heights": "Harness operators working on elevated lighting truss.", + "Heat": "Insulate fixtures and schedule cooldown windows.", + "Animals": "Acclimate animals to lighting cues gradually.", + "Children": "Soften light intensity and minimize high-heat fixtures around minors.", +} + +NARRATIVE_ARCHETYPES = { + "Resolute Protector": { + "subject": "Resolute protector clad in weathered armor", + "keywords": ["steadfast", "guardian", "battle-tempered"], + }, + "Wandering Visionary": { + "subject": "Wandering visionary draped in layered fabrics", + "keywords": ["imaginative", "inventive", "wide-eyed"], + }, + "Arcane Duelist": { + "subject": "Arcane duelist wreathed in swirling glyphs", + "keywords": ["charged sigils", "arc-light", "precision"], + }, + "Rebel Outrider": { + "subject": "Rebel outrider astride a dust-scarred hoverbike", + "keywords": ["maverick", "reckless", "ferocious"], + }, + "Archivist Seer": { + "subject": "Archivist seer cocooned in floating tomes and light glyphs", + "keywords": ["omniscient", "patient", "enigmatic"], + }, + "Skyline Courier": { + "subject": "Skyline courier weaving between skyscrapers with thruster skates", + "keywords": ["fleet-footed", "aerial", "deadline-driven"], + }, + "Bio-Sculptor": { + "subject": "Bio-sculptor artisan coaxing living flora sculptures from luminous resin", + "keywords": ["organic craft", "bioluminescent", "delicate"], + }, +} + +NARRATIVE_TONES = { + "Hopeful Resurgence": { + "descriptor": "radiating renewed hope", + "ambience": "glints of light pierce the gloom", + "hook": "A surge of hope cuts through the collapse.", + "keywords": ["amber embers", "resilient resolve"], + }, + "Brooding Tension": { + "descriptor": "coiled with restrained fury", + "ambience": "echoing thunder gathers along the horizon", + "hook": "Tension thickens as the storm converges.", + "keywords": ["pressure building", "charged silence"], + }, + "Whimsical Discovery": { + "descriptor": "buoyed by playful curiosity", + "ambience": "glowing motes dance in the periphery", + "hook": "Delight sparks as a hidden wonder unfurls.", + "keywords": ["gossamer spark", "playful wonder"], + }, + "Rising Triumph": { + "descriptor": "charged with impending victory", + "ambience": "anthemic echoes swell across the horizon", + "hook": "Momentum crests as destiny crowds the frame.", + "keywords": ["surging anthem", "victory pulse"], + }, + "Haunting Echo": { + "descriptor": "haunted by distant memories", + "ambience": "soft dirges reverberate through vacant halls", + "hook": "Silence lingers long after the last note falls.", + "keywords": ["ghostlight", "murmured lament"], + }, + "Electric Rebellion": { + "descriptor": "charged by uprising sparks", + "ambience": "holographic flyers pulse across rain-slick streets", + "hook": "Every footstep feeds the citywide surge of defiance.", + "keywords": ["neon defiance", "street anthem"], + }, + "Quiet Reckoning": { + "descriptor": "weighted with soft resolve", + "ambience": "low thunder rumbles beneath hushed breaths", + "hook": "Stillness marks the moment before balance is restored.", + "keywords": ["held breath", "measured justice"], + }, +} + +NARRATIVE_ENVIRONMENTS = { + "Rain-Soaked Citadel": { + "description": "Rain-soaked citadel ramparts shimmer under guttering torchlight", + "details": "rain threads off crenellations, puddles mirror the battle-scarred stone", + "keywords": ["slick masonry", "torchlit haze"], + }, + "Skybound Orchard": { + "description": "Skybound orchard terraces float amid drifting cloudbanks", + "details": "luminous fruit sway on wind-swept branches", + "keywords": ["weightless terraces", "cloud-sifted light"], + }, + "Desert Mirage Bazaar": { + "description": "Sun-baked bazaar shimmers inside a wavering desert mirage", + "details": "textile canopies ripple above sandstone stalls", + "keywords": ["heat shimmer", "spice-laden air"], + }, + "Submerged Temple": { + "description": "Sunken temple halls glimmer beneath rippling water canopy", + "details": "shoals of fish weave through mossy columns", + "keywords": ["refracted light", "ancient murals"], + }, + "Skyline Rooftops": { + "description": "Skyline rooftops knit a maze above neon avenues", + "details": "fluttering tarps snap between antenna arrays", + "keywords": ["city haze", "wind-sliced banners"], + }, + "Luminal Skyway": { + "description": "Glass skyways thread between levitating transit hubs", + "details": "hovercraft blur through light curtains and transit glyphs", + "keywords": ["altitude lanes", "transit glow"], + }, + "Subterranean Grotto": { + "description": "Bioluminescent grotto carved below cascading waterfalls", + "details": "glowing lichens trace the cavern walls and mirrored pools", + "keywords": ["glowstone", "echoing drip"], + }, +} + +NARRATIVE_SETPIECES = { + "Climactic Duel": { + "motion": "locks blades with an adversary as sparks cascade in slow arcs", + "hook": "Every strike echoes like a bell tolling fate.", + "keywords": ["arc of sparks", "ringing steel"], + }, + "Heist Dash": { + "motion": "vaults through market stalls clutching luminous artifacts", + "hook": "The chase snaps into sharp focus as alarms flare.", + "keywords": ["darting silhouettes", "flashing sirens"], + }, + "Revelation Stillness": { + "motion": "stands suspended as constellations form sigils around them", + "hook": "Silence blooms before the cosmos whispers the truth.", + "keywords": ["starlit hush", "sigil glow"], + }, + "Rescue Descent": { + "motion": "rappels through fractured skylights to lift allies from danger", + "hook": "The drop becomes a lifeline threading hope through chaos.", + "keywords": ["tensioned cable", "shattered glass"], + }, + "Diplomatic Standoff": { + "motion": "circles the negotiation table as tempers simmer just below the surface", + "hook": "Every measured word teeters between alliance and collapse.", + "keywords": ["clenched fists", "shifting glances"], + }, + "Skyway Escape": { + "motion": "sprints across translucent skyways as drones weave in pursuit", + "hook": "Every leap risks a plunge through rain-streaked neon abyss.", + "keywords": ["thruster sparks", "altitude slip"], + }, + "Ritual Awakening": { + "motion": "raises a relic as luminous glyphs spiral outward in slow bloom", + "hook": "The chamber hums as ancient power unfurls from the circle.", + "keywords": ["sigil bloom", "resonant chime"], + }, +} + +TEMPO_PROFILES = { + "slow burn": { + "motion_prefix": "With deliberate poise,", + "motion_suffix": "drawing out each telling beat", + "hook_suffix": "The moment lingers, heavy with intent.", + "subject_accent": "poised in measured control", + "keywords": ["elongated cadence", "measured breaths"], + }, + "measured build": { + "motion_prefix": "In a building rhythm,", + "motion_suffix": "stacking momentum with purpose", + "hook_suffix": "Anticipation climbs like a drumline swell.", + "subject_accent": "centered and ready", + "keywords": ["tight rhythm", "rising tempo"], + }, + "surging": { + "motion_prefix": "With a sudden surge,", + "motion_suffix": "as the surrounding world blurs", + "hook_suffix": "The scene ignites in a flash of motion.", + "subject_accent": "caught mid-action", + "keywords": ["accelerated blur", "kinetic rush"], + }, + "frantic": { + "motion_prefix": "In a frantic burst,", + "motion_suffix": "leaving streaks of energy hanging in the air", + "hook_suffix": "Heartbeat percussion rattles every frame.", + "subject_accent": "fractured by adrenaline", + "keywords": ["ragged tempo", "stuttered focus"], + }, + "syncopated pulse": { + "motion_prefix": "On a syncopated pulse,", + "motion_suffix": "as tension snaps between staggered beats", + "hook_suffix": "Rhythmic jolts keep the narrative out of step just enough to thrill.", + "subject_accent": "moving on off-beat pivots", + "keywords": ["stammered rhythm", "counter tempo"], + }, + "glacial stillness": { + "motion_prefix": "Within glacial stillness,", + "motion_suffix": "each breath crystallizes before the next", + "hook_suffix": "Time stretches thin enough to hear the quiet turn.", + "subject_accent": "enveloped in frozen composure", + "keywords": ["suspended hush", "slow tide"], + }, + "thunder build": { + "motion_prefix": "On a rumbling build,", + "motion_suffix": "as rolling thunder stacks beneath each beat", + "hook_suffix": "Pressure mounts until the sky itself is ready to split.", + "subject_accent": "braced against incoming storm", + "keywords": ["gathering charge", "storm cadence"], + }, + "weightless drift": { + "motion_prefix": "In a weightless drift,", + "motion_suffix": "letting gravity pause while motion hangs suspended", + "hook_suffix": "A silent float suspends the story between inhale and exhale.", + "subject_accent": "buoyed by gentle momentum", + "keywords": ["hover pulse", "airy sway"], + }, +} + +NSFW_POSE_PROFILES = { + "None": { + "prompt": "", + "notes": "No specific pose emphasis.", + }, + "Suggestive Pinup": { + "prompt": "suggestive pinup pose, playful hip tilt, confident eye contact", + "notes": "Keep the focus on charisma and silhouette rather than explicit detail.", + }, + "Reclined Elegance": { + "prompt": "reclined pose on plush surface, soft arm drape, languid posture", + "notes": "Use graceful lines and relaxed body language for a calm mood.", + }, + "Backlit Silhouette": { + "prompt": "backlit silhouette pose, curves defined through rim light, raised arms framing the body", + "notes": "Let light shape the figure while facial expression carries the emotion.", + }, + "Arching Stretch": { + "prompt": "arching stretch pose, elongated spine, fingertips brushing hair", + "notes": "Emphasize athletic grace and tension without revealing more than intended.", + }, + "Window Lean": { + "prompt": "window sill lean, soft shoulder drop, contemplative outward gaze", + "notes": "Use architecture and reflections to frame a quiet, alluring moment.", + }, + "S-Curve Stand": { + "prompt": "standing s-curve pose, hip popped, graceful arm framing", + "notes": "Classic glamour stance that accentuates silhouette without explicit focus.", + }, + "Floorline Spotlight": { + "prompt": "floorline pose, one leg extended, torso propped on forearms, spotlight emphasis", + "notes": "Great for stage-style shoots with dramatic uplight.", + }, + "Barstool Tease": { + "prompt": "perched on barstool, crossed legs, arched shoulders, playful lean", + "notes": "Combine confident posture with relaxed, social energy.", + }, + "Over-Shoulder Glance": { + "prompt": "over-shoulder glance, back arched, coy smile, hair tossed", + "notes": "Highlights back definition and adds flirtatious storytelling.", + }, + "XXX Stage Strut": { + "prompt": "bold stage strut, hips leading, spotlight-followed, sultry focus", + "notes": "High-energy adult stage choreography—keep movement confident and empowered.", + }, + "XXX Lounge Arch": { + "prompt": "provocative lounge arch, chest lifted, legs draped over chair edge", + "notes": "Editorial-level eroticism; frame to celebrate form without explicit exposure.", + }, + "Suspended Silk": { + "prompt": "suspended silk inversion, toes pointed, ribbon wrap", + "notes": "Ideal for aerial rigs—spotter safety and secure tie-offs are essential.", + }, + "XXX Wall Brace": { + "prompt": "braced against wall, arched back, dramatic leg extension", + "notes": "Stage explicit power poses—align lighting to sculpt athletic definition.", + }, + "Custom": { + "prompt": "", + "notes": "Use the custom pose prompt input to describe a bespoke stance.", + }, + "XXX Custom": { + "prompt": "", + "notes": "Use the custom pose prompt input for high-heat adult staging while staying policy compliant.", + }, +} + +NSFW_WARDROBE_STYLES = { + "None": { + "prompt": "", + "notes": "No wardrobe styling adjustments.", + }, + "Casual": { + "prompt": "comfortable casual wear, soft fabrics, relaxed fit", + "notes": "Emphasize comfort and ease of movement.", + }, + "Nude": { + "prompt": "bare skin, natural beauty, soft lighting", + "notes": "Celebrate the human form with tasteful nudity.", + }, + "Lace Lingerie": { + "prompt": "delicate lace lingerie set, intricate patterns, sheer panels", + "notes": "Highlight femininity and elegance through intricate lace details.", + }, + "Torn Yoga Pants": { + "prompt": "torn yoga pants, torn crotch, snug fit, athletic vibe", + "notes": "Combine comfort with a hint of edginess.", + }, + "Torn Jeans": { + "prompt": "ripped denim jeans, distressed details, casual cool", + "notes": "Embrace a laid-back vibe with edgy denim.", + }, + "Bra And Panties": { + "prompt": "coordinated bra and panties set, soft fabrics, flattering fit", + "notes": "Focus on color coordination and fabric texture for a polished look.", + }, + + "Bikini": { + "prompt": "stylish bikini set, vibrant colors, beach-ready", + "notes": "Perfect for sun-soaked shoots with a playful vibe.", + }, + + "Silk Lingerie": { + "prompt": "luxurious silk lingerie set, delicate lace trim, coordinated stockings", + "notes": "Highlight texture and craftsmanship for an elevated aesthetic.", + }, + "Sheer Robe": { + "prompt": "sheer chiffon robe tied at the waist, glimpses of toned figure", + "notes": "Let fabric layers provide implied coverage with elegant motion.", + }, + "Artistic Drapery": { + "prompt": "artistic fabric drape, classical fine art styling, implied coverage", + "notes": "Reference fine art photography to keep the composition tasteful.", + }, + "Latex Couture": { + "prompt": "sleek latex couture ensemble, high-shine finish, sculpted fit", + "notes": "Balance bold materials with confident posture for empowered vibes.", + }, + "Velvet Bodysuit": { + "prompt": "plush velvet bodysuit, deep jewel tones, subtle sheen", + "notes": "Use rich lighting to bring out the texture without overpowering skin tones.", + }, + "Corseted Ensemble": { + "prompt": "structured corseted set, ribbon lacing, sculpted hourglass silhouette", + "notes": "Keep posture elongated to emphasize tailoring and refined glamour.", + }, + "Crystal Bikini": { + "prompt": "sparkling crystal bikini set, reflective rhinestone accents, gleaming straps", + "notes": "Ideal for showgirl aesthetics—watch for specular blowouts in lighting.", + }, + "Leather Harness": { + "prompt": "sleek leather harness layering, adjustable straps, metallic buckles", + "notes": "Edgy styling that pairs well with dramatic lighting and strong poses.", + }, + "Body Chain Highlight": { + "prompt": "delicate body chains draped across torso, shimmering highlights", + "notes": "Use focused light to let jewelry catch attention over skin.", + }, + "XXX Latex Catsuit": { + "prompt": "second-skin latex catsuit, high-gloss finish, contour-tracing seams", + "notes": "Channel fetish couture while keeping framing artistic and celebratory.", + }, + "XXX Micro Lingerie": { + "prompt": "ultra-minimal lingerie set, strappy cutouts, bold adult-mag styling", + "notes": "Reserve for explicit glamour contexts; ensure confident expression and agency.", + }, + "Body Paint Couture": { + "prompt": "hand-painted body art, shimmering pigments, gradient coverage", + "notes": "Coordinate palette with lighting to keep body paint vibrant and respectful.", + }, + "XXX Mesh Harness": { + "prompt": "transparent mesh harness, chrome rings, high-shine thigh straps", + "notes": "High-voltage fetish styling—maintain empowered stance and clear consent messaging.", + }, + "Custom": { + "prompt": "", + "notes": "Use the custom wardrobe prompt input to define bespoke styling.", + }, + "XXX Custom": { + "prompt": "", + "notes": "Use the custom wardrobe prompt input for adult-oriented wardrobe design.", + }, +} + +NSFW_TONE_MOODS = { + "None": { + "prompt": "", + "notes": "No additional mood guidance.", + }, + "Erotic" : { + "prompt": "sensual erotic vibe, smoldering gaze, intimate setting", + "notes": "Emphasize connection and allure through body language.", + }, + "Glamorous Editorial": { + "prompt": "glamorous editorial energy, polished makeup, confident gaze", + "notes": "Channel high-fashion storytelling with poised expression.", + }, + "Sensual Noir": { + "prompt": "noir inspired mood, smoky atmosphere, mysterious expression", + "notes": "Lean into shadow play and narrative intrigue.", + }, + "Soft Intimacy": { + "prompt": "soft intimate atmosphere, gentle smile, inviting warmth", + "notes": "Keep gestures small and welcoming for a romantic tone.", + }, + "Artistic Nude": { + "prompt": "fine art nude photography approach, focus on form and composition", + "notes": "Treat the body as sculpture, emphasizing respect and artistry.", + }, + "Sultry Twilight": { + "prompt": "dusky twilight mood, amber window light, languid expression", + "notes": "Balance warmth and shadow to create a quiet, smoldering vibe.", + }, + "Playful Tease": { + "prompt": "playful teasing energy, coy glance over shoulder, bright pops of color", + "notes": "Keep body language energetic and lighthearted to avoid overt intensity.", + }, + "Opulent Burlesque": { + "prompt": "burlesque stage mood, velvet curtains, theatrical wink", + "notes": "Dial up opulence and showmanship with articulate gestures.", + }, + "Magenta Heat": { + "prompt": "electric magenta nightclub glow, pulsing beat, charged allure", + "notes": "Pair with neon or gel lighting for high-energy atmospherics.", + }, + "Sensory Overdrive": { + "prompt": "sensory overdrive mood, pounding bass, adrenaline sparkle", + "notes": "Great for festival or rave-inspired adult sets.", + }, + "XXX After Hours": { + "prompt": "after-hours adult lounge vibe, smoky haze, unabashed swagger", + "notes": "Acknowledge adult-only context while keeping tone empowered.", + }, + "XXX Seductive Drama": { + "prompt": "high-drama erotic tension, intense eye contact, charged pauses", + "notes": "Amplify emotional stakes without shifting into explicit action.", + }, + "Heated Performance": { + "prompt": "stage-heated charisma, performer flair, crowd-responsive energy", + "notes": "Great for live-show vibes where confidence is spotlighted.", + }, + "XXX Outrageous": { + "prompt": "outrageous adult spectacle, unapologetic flair, spotlight fever", + "notes": "Max intensity energy—underscore agency and consent cues.", + }, + "Custom": { + "prompt": "", + "notes": "Use the custom tone prompt input to define bespoke mood cues.", + }, + "XXX Custom": { + "prompt": "", + "notes": "Use the custom tone prompt input for adult-only mood or storytelling.", + }, +} + +NSFW_LIGHTING_SETUPS = { + "None": { + "prompt": "", + "notes": "No special lighting setup.", + }, + "Led Light Panels": { + "prompt": "LED light panels, adjustable color temp, soft diffusion", + "notes": "Versatile lighting for various moods—experiment with colors.", + }, + +"softbox Glow": { + "prompt": "large softbox, even wraparound light, minimal shadows", + "notes": "Ideal for beauty and fashion shoots requiring flattering illumination.", + }, + + "Side light Drama": { + "prompt": "strong side light, deep shadows, high contrast", + "notes": "Creates a dramatic, moody atmosphere—great for storytelling.", + }, + + "Flatering Ring Light": { + "prompt": "ring light, circular catchlights, even facial illumination", + "notes": "Perfect for close-up glamour shots emphasizing facial features.", + }, + + "Warm Side Light": { + "prompt": "warm side light, golden hour mimicry, soft shadow gradients", + "notes": "Use for a natural, inviting glow that enhances skin tones.", + }, + + "Candlelit Warmth": { + "prompt": "candlelit glow, warm highlights, gentle falloff across skin", + "notes": "Use clustered light sources to emphasize depth and comfort.", + }, + "Neon Accent": { + "prompt": "neon accent lighting, complementary color contrast, reflective sheen", + "notes": "Pick two bold hues to accentuate mood without washing out tones.", + }, + "Moody Spotlight": { + "prompt": "single spotlight, sculpted shadows, dramatic contrast", + "notes": "Frame the subject with shadow for a theatrical focal point.", + }, + "Window Backlight": { + "prompt": "soft window backlight, translucent curtains, haloed edges", + "notes": "Let natural spill wrap the figure while keeping front fill minimal.", + }, + "Colored Gel Duo": { + "prompt": "split lighting with complementary gels, gradient washes across skin", + "notes": "Blend opposing hues to carve shape while preserving complexion.", + }, + "Striplight Sculpt": { + "prompt": "studio striplights skimming curves, controlled edge highlights", + "notes": "Ideal for glamour sets that crave clean separation from backdrop.", + }, + "Mirror Bounce": { + "prompt": "mirror bounce lighting, reflective panels amplifying shimmer", + "notes": "Leverage reflective surfaces for layered specular detail.", + }, + "Holographic Wash": { + "prompt": "holographic prism wash, iridescent color shifts, futuristic sheen", + "notes": "Pairs with metallic wardrobe and modern editorial staging.", + }, + "XXX Club Strobe": { + "prompt": "club strobe pulses, alternating gel colors, rapid highlights", + "notes": "Simulate adult club energy—capture motion trails responsibly.", + }, + "XXX Champagne Glow": { + "prompt": "champagne bottle glints, gold bounce fill, indulgent sparkle", + "notes": "Adds decadent adult-party flair with luxurious reflections.", + }, + "Projection Mapping": { + "prompt": "projection-mapped patterns, evolving textures, responsive light", + "notes": "Project visuals that complement body contours for artistic storytelling.", + }, + "XXX Laser Grid": { + "prompt": "laser grid lighting, intersecting beams, electric pulse", + "notes": "High-voltage adult club vibe—mind safety and avoid direct eye exposure.", + }, + "Custom": { + "prompt": "", + "notes": "Use the custom lighting prompt input to detail bespoke setups.", + }, + +"XXX Nightlife": { + "prompt": "nightlife-inspired lighting, vibrant gels, dynamic contrasts", + "notes": "Capture the energy and excitement of nightlife with bold colors and dramatic shadows.", + }, + + + "XXX Custom": { + "prompt": "", + "notes": "Use the custom lighting prompt input for explicit nightlife scenarios.", + }, +} + +NSFW_CAMERA_FRAMING = { + "None": { + "prompt": "", + "notes": "No specific framing guidance.", + }, + "Intimate Portrait": { + "prompt": "intimate portrait framing, medium close-up, shallow depth of field", + "notes": "Highlight eyes and expression to carry the story.", + }, + "Full-Length Showcase": { + "prompt": "full-length framing, elongated lines, elegant posture", + "notes": "Ensure the pose reads clearly from head to toe.", + }, + "Low Angle Admiration": { + "prompt": "low angle lens, empowering perspective, upward gaze", + "notes": "Use perspective to emphasize confidence and strength.", + }, + "Overhead Curve Highlight": { + "prompt": "overhead shot, graceful curvature emphasized, symmetrical layout", + "notes": "Mind the set dressing so shapes remain readable from above.", + }, + "Mirror Reflection": { + "prompt": "mirror reflection framing, doubled imagery, blurred foreground glass", + "notes": "Use the mirror to hint at form while keeping direct exposure restrained.", + }, + "Profile Silhouette": { + "prompt": "profile silhouette framing, edge lighting tracing contours, negative space", + "notes": "Let rim light outline the figure, preserving mystery and mood.", + }, + "Dutch Glamour": { + "prompt": "dutch angle glamour framing, diagonal energy, kinetic composition", + "notes": "Adds motion and drama while centering the subject's presence.", + }, + "Split Focus Mirror": { + "prompt": "split focus with mirrored panels, layered depth cues", + "notes": "Great for editorial concepts mixing reflection and direct gaze.", + }, + "XXX Runway POV": { + "prompt": "runway pov framing, direct eye-line, confident strut captured mid-step", + "notes": "Channels adult runway shows—keep perspective empowering.", + }, + "XXX Close Intimacy": { + "prompt": "hyper-close framing on lips and neckline, shimmering highlights", + "notes": "Use shallow focus to maintain artistry in explicit glamour shots.", + }, + "Custom": { + "prompt": "", + "notes": "Use the custom camera prompt input to define bespoke framing.", + }, + "XXX Custom": { + "prompt": "", + "notes": "Use the custom camera prompt input for adult-centric framing approaches.", + }, +} + +NSFW_SET_DESIGNS = { + "None": { + "prompt": "", + "notes": "No dedicated set dressing emphasis.", + }, + "Sensual Lounge": { + "prompt": "Velvet chaise draped with ambient fabric, clustered candles, and soft-focus drapery staging", + "notes": "Layer deep cushions and candle groupings for an intimate lounge vignette.", + }, + "Velvet Lounge": { + "prompt": "velvet lounge set, jewel-toned drapery, plush seating tiers", + "notes": "Classic boudoir lounge ideal for editorial glamour.", + }, + "Cabaret Stage": { + "prompt": "cabaret stage with velvet curtains, luminous footlights, polished hardwood", + "notes": "Perfect for theatrical burlesque-inspired showcases.", + }, + "Futuristic Capsule": { + "prompt": "futuristic capsule suite, curved glass, ambient neon striping", + "notes": "Great for sci-fi erotica with clean architectural lines.", + }, + "Neon Skyline Suite": { + "prompt": "penthouse skyline suite, floor-to-ceiling glass, holographic signage glow", + "notes": "Pair with reflective materials to amplify urban glamour.", + }, + "Private Dressing Room": { + "prompt": "private dressing room set, marquee bulbs, wardrobe racks", + "notes": "Backstage energy with intimate performer preparation cues.", + }, + "Rope Studio Loft": { + "prompt": "industrial loft rigged with suspension points, coiled ropes, and padded mats", + "notes": "Ensure experienced riggers supervise and prioritize safety briefings.", + }, + "Spa Retreat": { + "prompt": "steam spa retreat, billowing mist, warm cedar benches", + "notes": "Soft spa atmosphere suited for serene sensuality.", + }, + "XXX Champagne Stage": { + "prompt": "champagne stage, mirrored runway, confetti shimmer", + "notes": "High-energy adult stage experience with luxe finishes.", + }, + "XXX Neon Tunnel": { + "prompt": "pulsing neon gauntlet of arches, mirrored floor, bass-thumping fog bursts", + "notes": "Lean into electrified runway energy with confident choreography.", + }, + "XXX Industrial Cage": { + "prompt": "industrial cage set, gridded shadows, steel spotlight", + "notes": "Edgy adult setting—pair with empowering posing.", + }, + "XXX Ritual Chamber": { + "prompt": "ritual chamber with flickering sconces, carved stone plinths, incense haze", + "notes": "Apply consent-forward framing when invoking power-play aesthetics.", + }, + "Custom": { + "prompt": "", + "notes": "Use the custom set prompt input to describe bespoke set pieces.", + }, + "XXX Custom": { + "prompt": "", + "notes": "Use the custom set prompt input for adult-stage set designs.", + }, +} + +NSFW_HEAT_LEVELS = { + "None": { + "prompt": "", + "notes": "No additional heat descriptors applied.", + "negative": "", + }, + "Subtle": { + "prompt": "subtle allure, whispered tension, restrained tease", + "notes": "Ideal for low-intensity boudoir and soft editorial scenarios.", + "negative": "overt explicit acts, graphic focus", + }, + "Sensual": { + "prompt": "sensual cadence, warm magnetism, intimate pace", + "notes": "Balanced sensuality that stays refined and inviting.", + "negative": "aggressive explicit behavior, harsh fetish framing", + }, + "Slow Burn": { + "prompt": "slow-burning heat, patient glances, lingering contact", + "notes": "Great for storytelling arcs that crescendo gradually.", + "negative": "sudden explicit jumps, hard-cut transitions", + }, + "Bold": { + "prompt": "bold seduction, confident heat, commanding gaze", + "notes": "Turn up intensity while celebrating empowered presence.", + "negative": "demeaning scenarios, non-consensual direction", + }, + + "Solo XXX": { + "prompt": "solo xxx energy, unapologetic self-expression, empowered allure, sensual_fingering,", + "notes": "Celebrate solo adult confidence with clear agency messaging.", + "negative": "non-consensual themes, degrading scenarios", + }, + + "XXX Heatwave": { + "prompt": "scorching club frenzy, relentless rhythm, unapologetic showmanship", + "notes": "Maximize adult spectacle with emphasis on mutual enthusiasm.", + "negative": "shaming language, non-consensual escalation", + }, + "XXX Fever": { + "prompt": "xxx feverish energy, charged teasing, spotlight hunger", + "notes": "Maximal adult heat—pair with strong agency messaging.", + "negative": "violent explicit content, bodily fluids, humiliation themes", + }, + "Custom": { + "prompt": "", + "notes": "Use the custom heat prompt input to define bespoke heat language.", + "negative": "", + }, + "XXX Custom": { + "prompt": "", + "notes": "Use the custom heat prompt input for adult-only heat descriptors.", + "negative": "", + }, +} + +NSFW_EXPLICITNESS_LEVELS = { + "None": { + "prompt": "", + "negative": "", + }, + "Soft Suggestive": { + "prompt": "tasteful suggestive tone, emphasis on attitude over exposure", + "negative": "graphic nudity, explicit anatomy, pornographic framing", + }, + "Implied Nude": { + "prompt": "implied nudity, strategic coverage, focus on silhouette and light", + "negative": "fully exposed genitals, spread legs, explicit close-ups", + }, + "Artistic Nude": { + "prompt": "fine art nude composition, respect for subject, sculptural lighting", + "negative": "pornographic framing, fetish extremes, explicit anatomical detail", + }, + "Bold Glamour": { + "prompt": "bold glamour styling, confident pose, editorial polish", + "negative": "hardcore explicit acts, fetish content, degrading scenarios", + }, + "XXX Glamour": { + "prompt": "xxx glamour focus, unapologetic skin-forward posing, adult magazine energy", + "negative": "explicit sexual acts, penetration, fetish violence, non-consensual themes", + }, + "XXX Showcase": { + "prompt": "erotic showcase, full nude confidence, provocative glimmer lighting", + "negative": "pornographic close-ups, bodily fluids, explicit intercourse depiction", + }, + "XXX Performance": { + "prompt": "adult-stage performance vibe, theatrical erotica, high-voltage seduction", + "negative": "graphic sexual acts, fetish degradation, non-consensual scenarios", + }, + + "Fully Adult Nude": { + "prompt": "fully adult nude, explicit anatomy, highly detailed Skin, confident exposure", + "negative": "underage, fetish extremes, non-consensual themes, violence", + }, + "Nude" : { + "prompt": "full nudity, explicit anatomy, detailed skin texture, confident pose", + "negative": "underage, fetish extremes, non-consensual themes, violence", + }, + + "Custom": { + "prompt": "", + "negative": "", + }, + "XXX Custom": { + "prompt": "", + "negative": "", + }, +} + +NSFW_BASELINE_NEGATIVES = [ + "child", + "teen", + "minor", + "underage", + "youth", + "loli", + "shota", + "young-looking", + "non-consensual", + "abuse", + "violence", + "blood", + "gore", + "injury", + "body horror", + "incest", + "bestiality", + "rape", + "drugged", + "sleeping partner", + "unconscious", + "degrading", + "humiliation", + "excrement", + +] + +NSFW_DEFAULT_SAFETY_NOTE = ( + "For mature audiences only. Confirm all depicted subjects are consenting adults and comply with platform policies." +) + + +PROMPT_CONTRADICTION_RULES = [ + { + "label": "Time of Day", + "hint": "Align time-of-day cues across narrative, lighting, and palette selections.", + "groups": { + "day": [ + "daytime", + "day-lit", + "sunlit", + "bright daylight", + "midday", + "high noon", + "noonday", + ], + "night": [ + "night", + "nighttime", + "midnight", + "moonlit", + "nocturnal", + "after-hours", + ], + "dawn": ["dawn", "sunrise", "first light"], + "dusk": ["dusk", "sunset", "twilight", "golden hour"], + }, + }, + { + "label": "Lighting Temperature", + "hint": "Keep lighting descriptors centered on either warm or cool styling for clarity.", + "groups": { + "warm": [ + "warm tone", + "warm color palette", + "golden hue", + "amber glow", + "ember glow", + "sunset glow", + "firelight", + "sun-bathed", + ], + "cool": [ + "cool tone", + "cool color palette", + "icy", + "frosted", + "blue undertones", + "silver rim", + "moonlit", + "glacial", + ], + }, + }, + { + "label": "Weather Mood", + "hint": "Conflicting weather cues can confuse render context—choose a single atmosphere.", + "groups": { + "clear": ["clear skies", "sun-drenched", "sunny", "cloudless"], + "storm": [ + "storm", + "lightning", + "downpour", + "rain-soaked", + "rainfall", + "tempest", + ], + "snow": ["snow", "snowfall", "blizzard", "frost", "snowstorm"], + }, + }, +] + diff --git a/noxprompter/nodes/__init__.py b/noxprompter/nodes/__init__.py new file mode 100644 index 0000000..e9fd2d4 --- /dev/null +++ b/noxprompter/nodes/__init__.py @@ -0,0 +1,75 @@ +"""Node modules for the modular Nox Prompter package.""" + +from __future__ import annotations + +from .nox_prompt_action_director import NoxPromptActionDirector +from .nox_prompt_analyzer import NoxPromptAnalyzer +from .nox_prompt_builder import NoxPromptBuilder +from .nox_prompt_camz import NoxPromptCamz +from .nox_prompt_character_creator import NoxPromptCharacterCreator +from .nox_prompt_combiner import NoxPromptCombiner +from .nox_prompt_enhancer import NoxPromptEnhancer +from .nox_prompt_human_designer import NoxPromptHumanDesigner +from .nox_prompt_lighting_master import NoxPromptLightingMaster +from .nox_prompt_narrative_weaver import NoxPromptNarrativeWeaver +from .nox_prompt_nsfw_action_director import NoxPromptNSFWActionDirector +from .nox_prompt_nsfw_designer import NoxPromptNSFWDesigner +from .nox_prompt_palette_mixer import NoxPromptPaletteMixer +from .nox_prompt_pipeline_combiner import NoxPromptPipelineCombiner +from .nox_prompt_wardrobe_designer import NoxPromptWardrobeDesigner +from .nox_prompt_usage_guide import NoxPromptUsageGuide +from .nox_prompt_prompt_organizer import NoxPromptOrganizer +from .nox_prompt_nsfw_aio import NoxPromptNSFWAIO + +_NODE_CLASSES = ( + NoxPromptUsageGuide, + NoxPromptOrganizer, + NoxPromptEnhancer, + NoxPromptBuilder, + NoxPromptPaletteMixer, + NoxPromptNarrativeWeaver, + NoxPromptCharacterCreator, + NoxPromptHumanDesigner, + NoxPromptWardrobeDesigner, + NoxPromptActionDirector, + NoxPromptNSFWActionDirector, + NoxPromptNSFWAIO, + NoxPromptLightingMaster, + NoxPromptNSFWDesigner, + NoxPromptCamz, + NoxPromptCombiner, + NoxPromptPipelineCombiner, + NoxPromptAnalyzer, +) + +NODE_CLASSES = _NODE_CLASSES + + +def get_node_classes(): + """Return all node classes defined in the modular package.""" + + return NODE_CLASSES + + +__all__ = [ + "NoxPromptUsageGuide", + "NoxPromptOrganizer", + "NoxPromptEnhancer", + "NoxPromptBuilder", + "NoxPromptPaletteMixer", + "NoxPromptNarrativeWeaver", + "NoxPromptCharacterCreator", + "NoxPromptHumanDesigner", + "NoxPromptWardrobeDesigner", + "NoxPromptActionDirector", + "NoxPromptNSFWActionDirector", + "NoxPromptNSFWAIO", + "NoxPromptLightingMaster", + "NoxPromptNSFWDesigner", + "NoxPromptCamz", + "NoxPromptCombiner", + "NoxPromptPipelineCombiner", + "NoxPromptAnalyzer", + "NODE_CLASSES", + "get_node_classes", +] diff --git a/noxprompter/nodes/__pycache__/__init__.cpython-312.pyc b/noxprompter/nodes/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74983083009e84e83e82ff786eccba02c62cfff7 GIT binary patch literal 2012 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

j|d3AgVD+HsSN@f7+fx#;+uY#&g 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 Hg)IIT9yg%V literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_analyzer.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_analyzer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d563f5ad069135b1f4e7d7edf0afd40fd12ae0d5 GIT binary patch literal 6008 zcmb_gTW}j!89uwKl~${Z?f52N6mJseqO>@jTqtAGTu7W7iIc`oOI3qd@2=#HrImYj zl~h710WwpigdwE^c4;V?KGjn?q&$^@8S)6z2V{GuQO|^#3@`(Eb7E$oJn^5ilB}er zWq|YW@45Wv-*f)`|L=TfKL~|n1f_TO4^wZpBlIWQs27i0;Z}A-Wftj3=h7&{#W@b? zye_2qcX`gleD`Qqj3WWnva*iqm|4rdNA;kO+pf72 zTv?mVtsH~)EHY4>(@~t)xwv3>bpA4Wr=}ejbs_H6J#n8d#wFbw_v^mPC@$*~$bjw# z8PsKvp?n|~ByFSF7ti2qX2v;asp)*dz=cz?0_C7$WpzVIX0f8O5z|U3GfY#_rc|tI zj)6_vF*O?$bUJjw)TvU^F!V`Pn;v3zG_GkIBGd(hxRotz7dl5hrF(SoI-lfINcXz0ww*Zx34n>4Q45uN+yvcMZ0^zs|q?+2uw@UkGc z%F9Uh%PU|4#RN?45#jjgI1!JWJbiFHCXkkf8?a%6mvNZtw5~cgVQX1zkTBCL7_}W0 zIvNW%@D8?iaI^*i&?o_@v4EHXm(HRhcfL7XYHIb+wz)h98qM<`(I^QH4KY@V!aFrv zB~KBZ_tpDXpGB^rAr^U^pWX%$;MIkq=MD4|zT~yeL$8PRLNXOa-TQ`z^?wceeXPG5 zEc;h2)@x0cA)Jf8n!PW5wl-)`pFC2U>`A(xu1}ZU_2>a=T@Mx|kfEYS4^IPBU|w&r zBxtqPT5T4muY*;{C2!GN^c2OU0IAhpIKXn0<-42yiZf-@)10O?Hk=%`lnW|0vpHMI z7!HPvcT<~AiwT9`2%ty4HVIRp`b&NJEKmx;XCWgMmXX5>Zh^tVI!KLLVk#_lhLwUa zv@?lY>1gX=mZXNRu$+al=ouGz>OnLtGn4Qd>KFhw_3e;Q4Pp{(?POg&uM>9t)iFQu*hV^u*TI`GoGXZk6trySF5J)F z?`9eayLe^jHFN%k6`RH#{RFJwE@~?ZqM$Ugj(LkB%ENg{wQL!Z0}w+-tzF+QUMXIE zsoMV7-PW$z;H$yc!e#MJXJoGPwRl+uOzBr{_w8Be+jBFy)b~_HirfuE=1wjKwpF_~ z-8gY$Z>95zD@X2zdgs!Mq3zY~SY`Y0`@1T!{guuGtiQ5;$6{zlHM-$;bjLz;$4&22 z^vUvQwR`K^;mX!0E1h4sas*zoe&fT#k^b9}9Saev9(l5Se3|EZL)B1s`Q$R@0r~YTf>7$NC%VesKCq_s#gNjlW3DM}E7dB0aqv z5WCKCu0xI@UBH6kqQvK+ZQ~m(xJ2Vne5{3uxx~w>CsLM1{Awl6Q3-WiH791(Ay0 zPp+lpD*_!Uz+Neu&}RX*UIetO*GrPqz_+5L`|30X&_gObeyE-RiVFE%PXRZh8Fg4i zlOa3+rXOMhltJyHY+xr;zKl3}0HCh|K9o48aSW_N-;cOijzd@5JSdOe6)tiyZZyW> ztuPl(Y<}no;$cZdKcb?rd?5b4DZ?`0%-vTQ{$y6HnZHAO(^<%Cd*4uFE~Q*{Cy2kI ze_TS9=XzJG(hRi9klRffmh}hGq~6Qdc$8z*=M#JGRU3 zh)>H}nla-5WieBxqo#>8qnZFpR%&hM=wSYbT*E!E-x56q8xDfuTB)CtSNKmGy?|`) z>Y$El?u>yuIqJ@N{vNdnJ|$v0d(pta1?Yz%9@tTaVUO+h=}1@wFrZzjp*x}{^^Vb!(xV{2rfeKsy z!w@_O%Jwx75N%D6{839swY}?aUevdKfyUrcba<(E_}62%I)4>k=sj9)xzpWyeb-#> zjp5h6Sq@hFw*G9>+r~TF7y5PsWX|5q?{;l|TUh9NtTMFk zR{O%x;f1clw_d7z<@tr9&o6X6UzYE*th+Wm=Pb1hR;0mCq9pw&5ER@>Kg7<<{?B4? zulNCt?G6`1z=<&ftK)UWL@h0G6k^eN#cNl$j5TOZ3`;w<5t|DXE z54h(C+!NQpJzzn)Jbf7a%w1K05dr7h3tPFm#e<3h-lhjiK3y(JMd35v3}97zGv!rM z&0?_Vg}I)RkAhXvH-_@TSg5eYr6Y`CrOjz0op56OV4EG4X0}>A)()`!KAW2oRY>s)r{XwJxYO2~6UgJq6??J8i(_NK`Fg zKSa!^R!Sw_Y4xINnncv5%t;f5{Tf`RG7#w+sK9;BAfBX|&H$8Qm3717d`Z=ER69?6 z88u}BR1ztZr}sdpc|k7)S1a#Dv!G%E8{91kzKr&}d6A@=8 zvks+Is%F4VLsBPUt#G#_K0Al${z(w<8*ZAEv#IHrkA#^k8m_^oVfXkf70*x+ry@ZG z1cm7*6rZ)ol}K`K3b0aCB<3>u{dG=}QXh-qx#4iI%r^i3OiiW?+YySy>PTmv6> zu95RDrE>}08pn+W@3AQft>I$#srkrU>gKbzf{S}jUgzc`AFnVh^mlOI^L22HagP!o zJ?SubNWlAGE)h+~$k_M*)MN50?vb#24X&-{=Mu_2IytE2qmN*S>J4qkD-u3&c{# zsq#p*rQ_NQ-_2LIJa#QOcW$wD)3OgKpT94oVB~I3w0xr4(+fg*{I;@lLD~7CvisHG zHSt=m+8&u3UTWW5-LU1_2@tQIfO#ACEK8_keI*#JwnRROgC5uf!@)P9^0Idpxj0v% zKQ#d90K-1@#D1ljuXzEt1i-B|xMRuF#OtUY<#oau_$Jaj9z`R}<>BU9^wjF!ybpNW z)-S2HsVR-Cg!_jF?&gm|k(c-<4K?SO@T-LQ00Ig?Q;1|cIm=?TR2HWUxENCAld{aD zsX>|&|HZ7O8yF@5C-UejhD5hD73YZ;Zdf>ri6;da4Tek$Ai;pPaluUgzms*|B`_Fr z9=VZ95#V9|^5hyJTE8td{If=qz6h39nv%5ttV>>QO)Nh3G$Sl{8dUz@Tf*M~qiev- zf4hx5n8TN;psNKcnzc>GyZ{Jb87VjipysoT96)k9#xr1X5yjVxDD_D-))cN!t+yNF z?oYYZan^Rf488EU7*Yd}?a)ye{7=3P22J~E5P%|o>@mt=(sKrg%_^QS&I8|PssHR! z&)FIWYHHsN^vsPe2DVq*qt#H~oV6Hw9IAKQ`&pik#==BOBrG^>6A2o2w93MG)`s{P;8jmSu#1uv`bp<Sr5Bq=QivX)7$}q0KcdmAOJRdIe|^9@Dnxh$nuD^*Y(mq{5njd_X>L>h-HD} hxIdtce?~oj_k4rnqW2M%%LM@i2P*Qww|wmV{{rSBX6yg} literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_analyzer.cpython-313.pyc b/noxprompter/nodes/__pycache__/nox_prompt_analyzer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36b33c7c15def353ad4c787e25e756db0ff6faea GIT binary patch literal 6021 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 Z_cs_OaSu_sFzZ6Wo-*I_O|Sj_{{f?;0=EDF literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_builder.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_builder.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8bfe3323bfb66e058d6e937a90a40d210103776e GIT binary patch literal 19924 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 Ia)kQ)AL+o0?f?J) literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_camz.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_camz.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..316c6929dd8fb43487ce29f6f2d51dc535c00e7b GIT binary patch literal 9649 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 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_camz.cpython-313.pyc b/noxprompter/nodes/__pycache__/nox_prompt_camz.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e555b27d81ed2e18be901d0f0f456d26242c20fd GIT binary patch literal 9958 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 UF!WnpZG_EOuzCNiN%7?W0_`B!+yDRo literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_character_creator.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_character_creator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0bcb7aaea9559b5aaf42737e38b817ffb35a246 GIT binary patch literal 11574 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 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_combiner.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_combiner.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3421537bcb6bbbbb64d0745b944e59c5dd0440c1 GIT binary patch literal 4318 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 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_combiner.cpython-313.pyc b/noxprompter/nodes/__pycache__/nox_prompt_combiner.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f56a61ae76e4acd8195f1d085a629682b6c0020 GIT binary patch literal 4443 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 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_enhancer.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_enhancer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1d871ad0181c137a4143284b9642b4520a34551 GIT binary patch literal 6612 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 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_enhancer.cpython-313.pyc b/noxprompter/nodes/__pycache__/nox_prompt_enhancer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5d8f4fb6e7d2b3d4475deb8b4d4902f87b77f74 GIT binary patch literal 6730 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 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_human_designer.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_human_designer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9736f16aac00c47f2e0e38c1f20d25b5a237b427 GIT binary patch literal 14921 zcmdU0drVtddcPM87(W2>#>VCXhG4=Y?*}1(c?3gv1<1_AGl|D=FJR&abA8DK=DMV9 zq_V4(IWkT358^MIfU zn)DL^(oK>CokeH+vjW-f?109t3FNqQM4iT;8_09#1+;E$Am5!I(7AO1y;~nJxD5fL z+ZZTt7X%93g@Gb>QNZLj1&ZCpff9E~pwwL&D07#QL>4hf&^h-BI+w}*I9n>;T~6oD zlkSR@JcssISmhuEy*C&PabC_B3Wk4`g>~72eu8Df3^(e#>kA5{9uPwQJB;UDAGhF{ zT@G`hfME25m;64?!-Z}$!LXxHF!o)anjRnZj89AtjgJYG>-wm3%+o(GH8eQp=^b!h zpT6N4bxvND0aHRvDq_6F#kRnIjrH0YXEbEn#$x;li1A*JD|Ju4!n$H!Fjvy!(+6uSgGl`hRpjop+yf>sGW5olrg&x;w#! z0!y50Ip7WUGhyF+kYQI(^)0hrj-kBtvY(?Ca2n+edci+e;FqSt+=|~9oTp}3#(SF% zy&DX-&609;@sA1q!K*>O{5HIx4+u_8z(pf%o zE-T4izJmCQ_OcJy(;v~E=72rp5$)w1uvd6Qd%4&io%dmmkD#?l{fI|Qe7<5o=3~3x zA!%Kb&5|S9)XO#vNjA%lXwxX$EJ(6haYUPivdy9-n^i}&X_9RgC)uQd6` z59G9`q&B2TjVY2^!oKzo$ITP;vA-grO-;BJN#!^`H2#33n?KB&A{@uZggly=^TO=D zs&|T${Oci@vvoo9kH!wR_z zjBkE{6Y}T0vy6vZSz?6znGn4qu{x0rFL*(%U-0@^kDm$7a|?oYY9X}j#|CtwdN$+_ zv0oVb8D@#F8q%px<(g(;d@^38G#O+h6n8H>Na;`cdBK?a&SdEt1QP#E^kF;HnQO~Vi^ z2ZX{PGY@M}SY$4V-+93(jh(vOl!j0di`0>nH4crS2|_;!nlSXfpz(*iG*DldV|$?y z4x><{j<{mPv_J5QsyJ!<6`g5+@KaUdpsT9=;Z{^d>Nu+!b!Zh$anh(Nx{~A(Rg~s5 zL#m`1m&E0|$2%(yv|bj2-T(vRsb!gWmVGQk3)+zA30}X$#125Ou=p!2Xl^qrVOERf zQcMak!Rrea?`POzOz>xy#otL5FBDh6d(MNf`X?3V#ACwR^

S~^{UXS@SAQj1ec4VXApty4!eVukmWM@oKDb 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 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_human_designer.cpython-313.pyc b/noxprompter/nodes/__pycache__/nox_prompt_human_designer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39f59746aa55d3a9529732aa12e7151dc1ea9807 GIT binary patch literal 11254 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 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_lighting_master.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_lighting_master.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4baf7d5b874107118ba27a78296c4369c217a71 GIT binary patch literal 10968 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 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_lighting_master.cpython-313.pyc b/noxprompter/nodes/__pycache__/nox_prompt_lighting_master.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..70555b27091a35e4ef488e6c0b63fb8b030022d7 GIT binary patch literal 11191 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 z zLCHM$@?V{UBCegjws{6l7`}eX1*2A~1_MT<-A%M>3BHkNUGDtd#G#s5Xy-0*EUQ{s zc1eh)6L?Os?8oVFVr$3AvavW4Q@Df93$R_mvQZ%d6I&z^7R4nlwJ1bU7tmIz(C`!! z3T`!m3va=Jap+N-Pcp&5snLm`Y91Vz8XlPleW>LWq)E7jWpVd19$~{NC@kmUuLmN_ z=F4sD40&bXGa@`-HHnij{1x$UhW=;9%I7+V(YmQ8OnWx)Y{O5uCdun(Hgpv(6Z}*X zm9^JT<(zL_KLw6n-I8I;(Mr)I3l7L@+>Msl?JLxK#lw z*h5qk{6zv9w8#K22wN?}y*`B5QB4tHX-VJ_?yssAr~%+W&}G+%VWEW+iPuh~nvdtj zeggdspn-1_VmAyMdXgl+Bbxt<2q;A0JHr2z@V+p7N|Hn5%BL>~ZL)DqPf+cWwf(0R H8rS~=qs|kC literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_nsfw_action_director.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_nsfw_action_director.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05cff6f2146c016aeffee648a5f4d0eec0399998 GIT binary patch literal 11479 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 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_nsfw_action_director.cpython-313.pyc b/noxprompter/nodes/__pycache__/nox_prompt_nsfw_action_director.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e16eb572f750e729a1aeda8e746bcff46740ba93 GIT binary patch literal 11868 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 CQOhU* literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_nsfw_designer.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_nsfw_designer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..11a9ff29adaec22967272e71e4341131c2844176 GIT binary patch literal 16135 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 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_nsfw_designer.cpython-313.pyc b/noxprompter/nodes/__pycache__/nox_prompt_nsfw_designer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..63361cde8f747e86c30e6890b036f92a299dd8e1 GIT binary patch literal 16461 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 LrrJL*5C!q?{SMj< literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_palette_mixer.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_palette_mixer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6b5f52dcc0fae0a54b071115ea366c8a3f66801 GIT binary patch literal 6141 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 VZ%}@)F-jt4Z`!o?2l_nM{|k}Gqn7{x literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_palette_mixer.cpython-313.pyc b/noxprompter/nodes/__pycache__/nox_prompt_palette_mixer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4f63ddba405d7dacf3723c2fbf3491d507ea3d3 GIT binary patch literal 6342 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 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_pipeline_combiner.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_pipeline_combiner.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1bef86a817cd928858a93d58cbdf505760e0fc37 GIT binary patch literal 17105 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 Ii`0$(3vd9ZuK)l5 literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_prompt_organizer.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_prompt_organizer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46e9b2d596bcb77b626da64e4ec306896aca8289 GIT binary patch literal 3938 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 YRhHj+CSluL2ui6_^~9-PMul?!53x5UFaQ7m literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_wardrobe_designer.cpython-312.pyc b/noxprompter/nodes/__pycache__/nox_prompt_wardrobe_designer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c9abf73f8280f711dea4802f2a27cc28a7ad5b2 GIT binary patch literal 10708 zcmcIqTTB~Snl2lI?-+Bp4Y+Ks=9Y94av_8}2_ax;Y?6*A8Mkp2#Kad;E|S0=PkL9q zlC>IXyEW2=>}C~u)uU$AJ6if_n5W^PA9`o!;kXM4cPXQ7X-C?fH}|9;cJi|OAKO)! z1dp~mvj%>j|3BaP&;9lt{>^CAQSh-o{_UMldnxKah$4MTa{ZGZsffa- zP>`@Ptc;ZSN+K$sDpKkzjg{|r-^8N+KA4li|Bp&ti3dB__ zciCth)TI~~3GjZ%g^fdoiq2rJG&xBe!$Qa%Mk`IL;pSHh|o<@?m9*?5Zb zl`5Mq)uZ4R2UZHSzls1*0kOS^kDTwRxYq0-qIc=u@BfX(;en z`G!7?d7tG4KC9o*rz!8VqQGbE8~QZoeO4CutbaqFRe7J)e@34*d7rgR9n7fuyk-H- z%G5Kqr^@U|Yj{O(d_`}1MYn@~=#4(2IgV(~BbuD*(E8m&%M{c6PYTGth18}Xed|;0 zPZdntQ>BM;wYx<%!!8BZ!u*C|G{Ey=mL7-YJibvj(lZ^3vKv)1v1lxK2bM>co?VQG zm{1_!o8pdLg#Q;x_ zf*~%NSGiCmz^(fO!64K$#;w0J&a+`C#2g!im75o<;%qPm!6O*M0w016ZJi!v!@O9r zv=$ALJOg3a*ho$e5MiOcH5_|yEyS^msEMuSTF+(V&XA^W zyfHFAK0iA;;U69GO!%*i%+E}?z5dxb@6}niM^xQq*Wti2w&3y3&g8A4O-|#vIz2hN zFyZx1$SEvx5FFn?U(F3-+-vq`0zTIIY(#>OT*p4oZ1;BZ1K35Mfwa_pdA zE{^rzT8Oh;{MHn-Zv56bjCeBf(wJ!Zvl*)OyY0Bsp>uITMgxG+ix2? zXq!y6O$u#Og5%n65?{npj@$U%5Wc&P{TrD6i|6*{{g$DFmhn`}xX^N0uwVJjr7upT z>^Jc(7B9!}4_0y0z2}XNedomk=Wxn7EI3Dn#<9;6d$CmG0)8iegAehIb=hI^S3zU<57uzg{fw5Nb(Fd7pH=%k69+w0 zsh%mJXIki(*?;?oc+Q{d@Z-h1II@8Qe}UUkMo;ht%4{RGEZQvh@C^<(#4|>ME~m^b zA2a)169-*WsjexZYg%ZNytn1NBOFNJ)*od|1YeOa&6R_m=@b;D$1QZs?t9+F^MO=H z05cIBOW@#-aQmZ-ncyoabMw|aJKEh-+mCR|P^O9)tFy*k?-z@y6K~^|`AiM5)>7t< z?T)<`>{&>ixsF{oGIhjU4<+2Xv&($JrB2S{R!_!4tX9hG+CIN`279lk`flLPcQQ6& zZh+!#Yj!T|F7BMb?W37SVrVF6I=5rEc`)N3M&~QW zW@2oin!C1d?kcxuv11?&;m~g7wjVn$rQ3RCr|A>tc3pe(yLQ}rHQmxJ`%U-s?P~W< z?L5NWR|+wrb9C<}p1Y36Z(zqeX}V+k!p`FM32g8GLmy=NjnP@5%TP|O?gh*oxX5vB z@b!v--M}PI(AAR7()>Zj20PVEP{ctBxA~pU|^)l7*i1pj6)eyQ3L~{RK}Q#V5(82jHxVwsXLQprR3l?*ieTzdt&FKHg0Y}F8BPbVA&I3=p&WxC}E&Y5T~z?GmXUZzow;93%`1vnL| z=4D#s2(B&BUVtk_HM~r99Km%YTm`r?RLjfs#SvU*qN@O>Ms>VQNgTm-CwdBS8dT5A zG{X^GZ{kD&PKzwOOdTA-olLw{fYTu>FVg`>aHkTd3vha5D}p$w7}Nw%IZuvW;{3y4Ne)PdSi3+hDOs281F)+YMVS*{uNuQW<|9)2@TjD5v^ zs7oPqR`N**J1MN7(D|aC=pvrZOR0}~Iv&k9-ltIi_-hp6ibE>d;kU%DCN83joB>^A zoEC^=n(r&PQqWp9V5f#S!=Fma)d8~gPhj@pi~ccE6)a*Vwjb)n8<>^-OlbsuBzMJG`2 zG4>PaEp$?{!^j$7P98UUn75!82VU>R#3ghJ={Y4j6IC#$mX(P?bQ<+7DaWb59SSO! zsi5+<8v1Mq4aF5dE<-~fDu1k1QHf!&T!3*mcatX#`zFm%zJyN#%jsE8s6N^nh}mcXMQE z&=ikaJ}D;K+RD$mHe7?BK{S<64R6Afl6qE+V>#=mFuMG&G{K3GdP-|y~ZLz|&k4sniT+Mn1!m%JwUxU4JqF;cIIBEO;qW=3m z3O9RFeN)@sUo#(%L*S1u$Ugd4YO~^R;P3bU1D}nOUdNEA=-sHIFUw>boh93#>Q;It zcAq6A1bvi3>!(H4D3Ft)&T~jgmR-&=j-qBdPf3cJF^Q&B&*ezR4IPk>9PJ@oqo^Ls z(Tk$ya-K!pD523CH!$}O)RMai!lmSV@Fl9_>~fY$DQIc#7Q}q7HvTmvENElr^;G}V z_Jdz9e|!!Cf8=gMG}lg?8%AVO@y&$Osacs5cvc)ov?1X3k3dGo_&<}*Kx8r8?+ zpZRkY+R#DAxXH63nGqN14Ru~|Rl?h3V0AUT4#X3Auav05l4X|X$a#z#CSr_;aUv#& zxJ<+qA|{ErO2iZq*ND)A5X-(r{$tdGPRsf zT`n7P0EtP?M;r3QaxPNlmGiMiiD%A5>$5a-J}&n#kWVL(%~Co^&qW{d&H4CpnShq^ z&A$jp=_Nio7hQ2kLgxXdLrV^#&r|+cIz*qR_OnX4ygcW)5;Oad=K}KE10nKj z-k)80xjE8P6%z9zEfd_^#B4fbV6rof5csfa6_~T-;^qMy9|b|b@h7_na9;4W0kf^h zgL!Xm^(R|lJP1$K?@^gyMMI@7ZLn@mXG$RSwV{5qGgAs7;5UtRo0^Q8gf+RamV|Yb zvF3xNjGlxIl(F`M2N@#?ms3Uyq&1On1!Z*N=Cc_yQ7UtZt4O$-GSb*_DpNz0+B~X` zDD{-lj_H#b3sJ0j#YU6{%GiQi`!kJ1Y05jW6NRRXt+;I<;~_!`4x#FFQuCT_ zu6u0#(7HLfwIrB(l4a@2`p2Cgc5dF>RtuFUlj_&}HMKljd9s3CgLv2@G_?|JX&UO%3FcYiS<^!_NN>4FBv zwyx(b=XdM(hW5P?p(UEqIFg1B?mq7v+N5E)m@kjNf4M7m%w zY@YaCZ8J2pP}`T(KPSNpLTx_;Ep52{ykNPIEPvkM!ky$@0~&xV2=!@0V|JR@(gp}y zv-1U{EmNg)=wRr4Q%jL4z}Rf*@Sa5Dc8)t!TpsUCYco7xLf@z z%P%c^gTjURlxu$9;Q89x^vw0dmC7DJg=UXn^&D7lrmQyw>kpEquk_Z{t5Q zEaKws_^+=1^6K7QVPGC#^k9z{+ZR%Xh2-qFP*Lk<+g9iHUBS@*++f-4#vL$+t_g-| zqIGOd@AL?UVG{1#itfY&!{uD~?#@8o?%KM&;}#5K+0@&*9Y~wS7uuz?&~^>bcJ;6x zTC!6il|2{=&g(+`^`w!syX`Cbbjr}MTbU^>vFVZ~Sde=!?#`yVrv>W*$gS}&=e&P^ z26s;_<6D&yXKmNf)*{&<%9=hBNBzn{EC`~9NM?~lZowJ-@A{Qi4ufp9)X?e{aWAei;R za3CI!u>74E!@0qWXqcaXcU*VwK~&b4gdZEZ-~9qEQ95~#P5-`e_gorS2) ztweP+lR#}ly1Mq;ON+|VSw*gdq;PJRiL}nrTJ~Q#{2X~q5zF}XRrm)8j=Z&Sgp}pr zBtdbc4q{m_7Ky~799(0m+!3A}$g^i}QA;j@Jp2PkJX>)Q8W;Y*#P5HlRYc7|t~Um` zEl5ik!}uu>nG%IU@mtFIpOj0WT>nma1*Z!C4 HY(4%LM(C$+ literal 0 HcmV?d00001 diff --git a/noxprompter/nodes/__pycache__/nox_prompt_wardrobe_designer.cpython-313.pyc b/noxprompter/nodes/__pycache__/nox_prompt_wardrobe_designer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ec8f81971e357c7e0d7f30618ce361bd6a208f8 GIT binary patch literal 10838 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 aVpg%M`<}`$>6Kzi-;A}*zbVPI 0: + style_scores[style] = score + + if style_scores: + # Find the style with the highest score + best_style = None + best_score = 0 + for style, score in style_scores.items(): + if score > best_score: + best_score = score + best_style = style + return best_style + return "general" + + def _analyze_sentiment(self, prompt): + """Basic sentiment analysis of the prompt.""" + positive_words = ['beautiful', 'amazing', 'stunning', 'gorgeous', 'magnificent', 'wonderful'] + negative_words = ['dark', 'scary', 'horror', 'grim', 'sinister', 'evil'] + + prompt_lower = prompt.lower() + positive_count = sum(1 for word in positive_words if word in prompt_lower) + negative_count = sum(1 for word in negative_words if word in prompt_lower) + + if positive_count > negative_count: + return "positive" + elif negative_count > positive_count: + return "negative" + else: + return "neutral" + +__all__ = ["NoxPromptAnalyzer"] diff --git a/noxprompter/nodes/nox_prompt_builder.py b/noxprompter/nodes/nox_prompt_builder.py new file mode 100644 index 0000000..c7481a0 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_builder.py @@ -0,0 +1,444 @@ +from __future__ import annotations + +import random +import re +from typing import Any, Dict, List, Tuple +from ..common import PresetMixin, PromptFragmentFilter, _resolve_option +from ..constants import * # noqa: F403 + +class NoxPromptBuilder(PresetMixin): + """Construct prompts using formulas and curated keyword palettes.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "formula": (list(FORMULA_CONFIGS.keys()), {"default": "Advanced Formula"}), + "subject_focus": ("STRING", {"multiline": True, "default": "Battle-hardened warrior drenched in rain, wearing leather armor and fur"}), + "scene_setting": ("STRING", {"multiline": True, "default": "Muddy battlefield at night, lightning in the distance, flames flickering"}), + "motion_arc": ("STRING", {"multiline": True, "default": "Charges forward roaring, swings blade in a slow-motion crash zoom"}), + }, + "optional": { + "narrative_hook": ("STRING", {"multiline": True, "default": "Camera locks onto her determined eyes as thunder cracks overhead"}), + "model_emphasis": ("STRING", {"multiline": True, "default": "Cinematic aesthetic control, precise semantic adherence"}), + "light_source": (list(LIGHT_SOURCE_OPTIONS.keys()), {"default": "Daylight"}), + "light_quality": (list(LIGHT_QUALITY_OPTIONS.keys()), {"default": "Soft Light"}), + "time_of_day": (list(TIME_OF_DAY_OPTIONS.keys()), {"default": "Dusk"}), + "shot_size": (list(SHOT_SIZE_OPTIONS.keys()), {"default": "Medium Close-up"}), + "composition": (list(COMPOSITION_OPTIONS.keys()), {"default": "Balanced"}), + "lens_focal": (list(LENS_FOCAL_OPTIONS.keys()), {"default": "Medium"}), + "lens_angle": (list(LENS_ANGLE_OPTIONS.keys()), {"default": "None"}), + "lens_type": (list(LENS_TYPE_OPTIONS.keys()), {"default": "Single Shot"}), + "color_tone": (list(COLOR_TONE_OPTIONS.keys()), {"default": "Warm Tone"}), + "motion_type": (list(MOTION_TYPE_OPTIONS.keys()), {"default": "Running"}), + "emotion": (list(EMOTION_OPTIONS.keys()), {"default": "Joy"}), + "camera_basic": (list(CAMERA_BASIC_OPTIONS.keys()), {"default": "Push-in"}), + "camera_advanced": (list(CAMERA_ADVANCED_OPTIONS.keys()), {"default": "Handheld"}), + "visual_style": (list(VISUAL_STYLE_OPTIONS.keys()), {"default": "Anime"}), + "special_effect": (list(SPECIAL_EFFECT_OPTIONS.keys()), {"default": "None"}), + "keyword_style": (["auto", "inline", "compact"], {"default": "auto"}), + "quality_profile": (["Balanced", "Cinematic", "Minimal", "Narrative"], {"default": "Balanced"}), + "randomize_missing": ("BOOLEAN", {"default": False}), + "random_seed": ("INT", {"default": 0, "min": 0, "max": 1_000_000, "step": 1}), + "palette_overrides": ("STRING", {"multiline": True, "default": ""}), + "extra_descriptors": ("STRING", {"multiline": True, "default": "Model feature emphasis: complex & dynamic motion"}), + "custom_keywords": ("STRING", {"multiline": True, "default": ""}), + "negative_prompt": ("STRING", {"multiline": True, "default": ""}), + "prompt_prefix": ("STRING", {"default": ""}), + "prompt_suffix": ("STRING", {"default": ""}), + "prompt_filter_enabled": ("BOOLEAN", {"default": True}), + "prompt_filter_profile": (list(sorted(PromptFragmentFilter.PROFILE_PRESETS.keys())), {"default": "balanced"}), + "preset_action": (["none", "save", "load", "list"], {"default": "none"}), + "preset_name": ("STRING", {"default": ""}), + } + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ("prompt", "negative_prompt", "aesthetic_notes", "dynamic_notes", "preset_status") + FUNCTION = "build_prompt" + CATEGORY = "NoxPrompter/Builders" + + def build_prompt( + self, + formula, + subject_focus, + scene_setting, + motion_arc, + narrative_hook="", + model_emphasis="", + light_source="Daylight", + light_quality="Soft Light", + time_of_day="Dusk", + shot_size="Medium Close-up", + composition="Balanced", + lens_focal="Medium", + lens_angle="None", + lens_type="Single Shot", + color_tone="Warm Tone", + motion_type="Running", + emotion="Joy", + camera_basic="Push-in", + camera_advanced="Handheld", + visual_style="Anime", + special_effect="None", + keyword_style="auto", + quality_profile="Balanced", + randomize_missing=False, + random_seed=0, + palette_overrides="", + extra_descriptors="", + custom_keywords="", + negative_prompt="", + prompt_prefix="", + prompt_suffix="", + prompt_filter_enabled=True, + prompt_filter_profile="balanced", + preset_action="none", + preset_name="", + ): + config = { + "formula": formula, + "subject_focus": subject_focus, + "scene_setting": scene_setting, + "motion_arc": motion_arc, + "narrative_hook": narrative_hook, + "model_emphasis": model_emphasis, + "light_source": light_source, + "light_quality": light_quality, + "time_of_day": time_of_day, + "shot_size": shot_size, + "composition": composition, + "lens_focal": lens_focal, + "lens_angle": lens_angle, + "lens_type": lens_type, + "color_tone": color_tone, + "motion_type": motion_type, + "emotion": emotion, + "camera_basic": camera_basic, + "camera_advanced": camera_advanced, + "visual_style": visual_style, + "special_effect": special_effect, + "keyword_style": keyword_style, + "quality_profile": quality_profile, + "randomize_missing": randomize_missing, + "random_seed": random_seed, + "palette_overrides": palette_overrides, + "extra_descriptors": extra_descriptors, + "custom_keywords": custom_keywords, + "negative_prompt": negative_prompt, + "prompt_prefix": prompt_prefix, + "prompt_suffix": prompt_suffix, + "prompt_filter_enabled": prompt_filter_enabled, + "prompt_filter_profile": prompt_filter_profile, + } + + config, preset_status = self._apply_preset_action( + "prompt_builder", + preset_action, + preset_name, + config, + ) + + formula = config["formula"] + subject_focus = config["subject_focus"] + scene_setting = config["scene_setting"] + motion_arc = config["motion_arc"] + narrative_hook = config.get("narrative_hook", "") + model_emphasis = config.get("model_emphasis", "") + light_source = config.get("light_source", "Daylight") + light_quality = config.get("light_quality", "Soft Light") + time_of_day = config.get("time_of_day", "Dusk") + shot_size = config.get("shot_size", "Medium Close-up") + composition = config.get("composition", "Balanced") + lens_focal = config.get("lens_focal", "Medium") + lens_angle = config.get("lens_angle", "None") + lens_type = config.get("lens_type", "Single Shot") + color_tone = config.get("color_tone", "Warm Tone") + motion_type = config.get("motion_type", "Running") + emotion = config.get("emotion", "Joy") + camera_basic = config.get("camera_basic", "Push-in") + camera_advanced = config.get("camera_advanced", "Handheld") + visual_style = config.get("visual_style", "Anime") + special_effect = config.get("special_effect", "None") + keyword_style = config.get("keyword_style", "auto") + quality_profile = config.get("quality_profile", "Balanced") + randomize_missing = bool(config.get("randomize_missing", False)) + random_seed = int(config.get("random_seed", 0)) + palette_overrides = config.get("palette_overrides", "") + extra_descriptors = config.get("extra_descriptors", "") + custom_keywords = config.get("custom_keywords", "") + negative_prompt = config.get("negative_prompt", "") + prompt_prefix = config.get("prompt_prefix", "") + prompt_suffix = config.get("prompt_suffix", "") + prompt_filter_enabled = bool(config.get("prompt_filter_enabled", True)) + prompt_filter_profile = config.get("prompt_filter_profile", "balanced") + + formula_config = FORMULA_CONFIGS.get(formula, next(iter(FORMULA_CONFIGS.values()))) + overrides_map, override_custom = self._parse_palette_overrides(palette_overrides) + rng = random.Random(random_seed) if randomize_missing else None + + prompt_fragments = [] + + prefix_fragment = prompt_prefix.strip() + if prefix_fragment: + prompt_fragments.append(prefix_fragment) + + subject_clause = subject_focus.strip() + if model_emphasis.strip(): + emphasis = model_emphasis.strip() + subject_clause = f"{subject_clause}, {emphasis}" if subject_clause else emphasis + if subject_clause: + prompt_fragments.append(subject_clause) + + if scene_setting.strip(): + prompt_fragments.append(scene_setting.strip()) + + if motion_arc.strip(): + prompt_fragments.append(motion_arc.strip()) + + if narrative_hook.strip(): + prompt_fragments.append(narrative_hook.strip()) + + if extra_descriptors.strip(): + prompt_fragments.append(extra_descriptors.strip()) + + aesthetic_prompts, aesthetic_summaries = self._collect_category_details( + [ + ("light_source", light_source, LIGHT_SOURCE_OPTIONS), + ("light_quality", light_quality, LIGHT_QUALITY_OPTIONS), + ("time_of_day", time_of_day, TIME_OF_DAY_OPTIONS), + ("shot_size", shot_size, SHOT_SIZE_OPTIONS), + ("composition", composition, COMPOSITION_OPTIONS), + ("lens_focal", lens_focal, LENS_FOCAL_OPTIONS), + ("lens_angle", lens_angle, LENS_ANGLE_OPTIONS), + ("lens_type", lens_type, LENS_TYPE_OPTIONS), + ("color_tone", color_tone, COLOR_TONE_OPTIONS), + ], + overrides_map, + rng=rng, + randomize_missing=randomize_missing, + ) + + dynamic_prompts, dynamic_summaries = self._collect_category_details( + [ + ("motion_type", motion_type, MOTION_TYPE_OPTIONS), + ("emotion", emotion, EMOTION_OPTIONS), + ("camera_basic", camera_basic, CAMERA_BASIC_OPTIONS), + ("camera_advanced", camera_advanced, CAMERA_ADVANCED_OPTIONS), + ], + overrides_map, + rng=rng, + randomize_missing=randomize_missing, + ) + + style_prompts, style_summaries = self._collect_category_details( + [ + ("visual_style", visual_style, VISUAL_STYLE_OPTIONS), + ("special_effect", special_effect, SPECIAL_EFFECT_OPTIONS), + ], + overrides_map, + rng=rng, + randomize_missing=randomize_missing, + ) + + keyword_mode = (keyword_style or "auto").lower() + structure = formula_config.get("structure", []) + + if keyword_mode == "compact": + combined_keywords = aesthetic_prompts + dynamic_prompts + style_prompts + if combined_keywords: + prompt_fragments.append(f"Keywords: {', '.join(combined_keywords)}") + elif keyword_mode == "inline": + for chunk in aesthetic_prompts + dynamic_prompts + style_prompts: + prompt_fragments.append(chunk) + else: + if "aesthetic" in structure and aesthetic_prompts: + prompt_fragments.append(f"Cinematic palette: {', '.join(aesthetic_prompts)}") + elif aesthetic_prompts: + prompt_fragments.extend(aesthetic_prompts) + + if dynamic_prompts: + prompt_fragments.append(f"Motion & camera: {', '.join(dynamic_prompts)}") + + if "stylization" in structure and style_prompts: + prompt_fragments.append(f"Stylization: {', '.join(style_prompts)}") + elif style_prompts: + prompt_fragments.extend(style_prompts) + + combined_custom = self._split_keywords(custom_keywords) + if override_custom: + combined_custom.extend(override_custom) + if combined_custom: + combined_custom = list(dict.fromkeys(filter(None, (item.strip() for item in combined_custom)))) + if combined_custom: + prompt_fragments.append(", ".join(combined_custom)) + + suffix_fragment = prompt_suffix.strip() + if suffix_fragment: + prompt_fragments.append(suffix_fragment) + + filter_summary = "" + if prompt_filter_enabled: + fragment_filter = PromptFragmentFilter(prompt_filter_profile) + filtered_fragments = fragment_filter.organize(prompt_fragments) + if filtered_fragments: + prompt_fragments = filtered_fragments + filter_summary = fragment_filter.summarize() + if prefix_fragment: + try: + prompt_fragments.remove(prefix_fragment) + except ValueError: + pass + prompt_fragments.insert(0, prefix_fragment) + if suffix_fragment: + try: + prompt_fragments.remove(suffix_fragment) + except ValueError: + pass + prompt_fragments.append(suffix_fragment) + + prompt_text = self._assemble_prompt(prompt_fragments) + neg_text = negative_prompt.strip() + + aesthetic_notes = " | ".join(filter(None, aesthetic_summaries + style_summaries)) + dynamic_notes = " | ".join(filter(None, dynamic_summaries + ([filter_summary] if filter_summary else []))) + + return (prompt_text, neg_text, aesthetic_notes, dynamic_notes, preset_status) + + def _collect_category_details(self, selections, overrides, rng=None, randomize_missing=False): + prompts = [] + summaries = [] + overrides = overrides or {} + for key, label, options in selections: + tokens = self._expand_selection(label) + override_tokens = overrides.get(key, []) + if override_tokens: + tokens.extend(override_tokens) + if not tokens: + tokens.append(label) + + for token in tokens: + option = self._resolve_option(token, options, rng=rng, randomize_missing=randomize_missing) + prompt_text = option.get("prompt", "").strip() + summary_text = option.get("summary", "").strip() + if prompt_text: + prompts.append(prompt_text) + if summary_text: + summaries.append(summary_text) + + prompts = list(dict.fromkeys(filter(None, prompts))) + summaries = list(dict.fromkeys(filter(None, summaries))) + return prompts, summaries + + def _resolve_option(self, label, options, rng=None, randomize_missing=False): + normalized = (label or "").strip() + if normalized: + if normalized.lower() == "none": + normalized = "" + else: + lower_map = {name.lower(): name for name in options.keys()} + lookup = lower_map.get(normalized.lower()) + if lookup: + return options.get(lookup, options.get("None", {"prompt": "", "summary": ""})) + if randomize_missing and rng: + candidates = [name for name in options if name.lower() != "none" and options[name].get("prompt")] + if candidates: + choice = rng.choice(candidates) + return options[choice] + return options.get("None", {"prompt": "", "summary": ""}) + + def _expand_selection(self, value): + if not value: + return [] + tokens = [token.strip() for token in re.split(r'[|/,]', value) if token.strip()] + return tokens + + def _parse_palette_overrides(self, text): + if not text or not text.strip(): + return {}, [] + + overrides = {} + custom_chunks = [] + mapping = self._palette_alias_map() + + for line in text.splitlines(): + line = line.strip() + if not line: + continue + if ":" in line: + key_part, values_part = line.split(":", 1) + canonical = mapping.get(self._normalize_token(key_part)) + values = self._split_keywords(values_part) + if canonical and values: + overrides.setdefault(canonical, []).extend(values) + elif values: + custom_chunks.extend(values) + else: + custom_chunks.extend(self._split_keywords(line)) + + for key in list(overrides.keys()): + overrides[key] = list(dict.fromkeys(overrides[key])) + + custom_chunks = list(dict.fromkeys(custom_chunks)) + return overrides, custom_chunks + + def _palette_alias_map(self): + return { + "light source": "light_source", + "light sources": "light_source", + "lighting": "light_source", + "light quality": "light_quality", + "light qualities": "light_quality", + "time of day": "time_of_day", + "time": "time_of_day", + "shot size": "shot_size", + "composition": "composition", + "lens focal": "lens_focal", + "focal length": "lens_focal", + "lens angle": "lens_angle", + "camera angle": "lens_angle", + "lens type": "lens_type", + "shot type": "lens_type", + "color tone": "color_tone", + "color": "color_tone", + "motion type": "motion_type", + "motion": "motion_type", + "emotion": "emotion", + "camera basic": "camera_basic", + "camera move": "camera_basic", + "basic move": "camera_basic", + "camera advanced": "camera_advanced", + "advanced move": "camera_advanced", + "visual style": "visual_style", + "style": "visual_style", + "special effect": "special_effect", + "effects": "special_effect", + } + + def _normalize_token(self, token): + return token.strip().lower().replace("_", " ") + + def _split_keywords(self, text): + if not text: + return [] + separators = [",", "\n", ";"] + tokens = [text] + for sep in separators: + temp = [] + for token in tokens: + temp.extend(token.split(sep)) + tokens = temp + return [token.strip() for token in tokens if token.strip()] + + def _assemble_prompt(self, fragments): + cleaned = [] + for fragment in fragments: + segment = fragment.strip() + if not segment: + continue + if segment[-1] not in ".!?": + segment = f"{segment}." + cleaned.append(segment) + return " ".join(cleaned) + +__all__ = ["NoxPromptBuilder"] diff --git a/noxprompter/nodes/nox_prompt_camz.py b/noxprompter/nodes/nox_prompt_camz.py new file mode 100644 index 0000000..42041cf --- /dev/null +++ b/noxprompter/nodes/nox_prompt_camz.py @@ -0,0 +1,219 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Tuple +from ..common import PresetMixin, _resolve_with_custom, _split_tokens +from ..constants import * # noqa: F403 + +class NoxPromptCamz(PresetMixin): + """Author cam-stream ready prompts with adult-only safeguards and production notes.""" + + @classmethod + def INPUT_TYPES(cls): + persona_keys = list(CAMZ_PERSONA_OPTIONS.keys()) + [CUSTOM_OPTION] + stage_keys = list(CAMZ_STAGE_OPTIONS.keys()) + [CUSTOM_OPTION] + camera_keys = list(CAMZ_CAMERA_OPTIONS.keys()) + [CUSTOM_OPTION] + energy_keys = list(CAMZ_ENERGY_OPTIONS.keys()) + [CUSTOM_OPTION] + + return { + "required": { + "persona": (persona_keys, {"default": "Soft Glam Muse"}), + "stage_theme": (stage_keys, {"default": "Velvet Boudoir"}), + "camera_style": (camera_keys, {"default": "Intimate Close"}), + "energy_profile": (energy_keys, {"default": "Gentle Sway"}), + }, + "optional": { + "wardrobe_style": ("STRING", {"multiline": True, "default": "silky lingerie set with sheer robe"}), + "prop_list": ("STRING", {"multiline": True, "default": "vanity mirror, rose petals"}), + "overlay_effects": ("STRING", {"multiline": True, "default": "tip goal ticker, soft sparkle particles"}), + "chat_tone": ("STRING", {"multiline": True, "default": "sweet compliments, name drops, gratitude"}), + "call_to_action": ("STRING", {"default": "invite viewers to join exclusive private show"}), + "playlist_prompt": ("STRING", {"multiline": True, "default": "slow r&b, lo-fi chill"}), + "camera_motions": ("STRING", {"multiline": True, "default": "slow zooms, minimal pans"}), + "safety_focus": ("STRING", {"multiline": True, "default": "reinforce adult-only audience and mutual consent"}), + "custom_persona": ("STRING", {"multiline": True, "default": ""}), + "custom_stage": ("STRING", {"multiline": True, "default": ""}), + "custom_camera": ("STRING", {"multiline": True, "default": ""}), + "custom_energy": ("STRING", {"multiline": True, "default": ""}), + "custom_negative": ("STRING", {"multiline": True, "default": ""}), + "custom_safety_note": ("STRING", {"multiline": True, "default": ""}), + "include_negative_baseline": ("BOOLEAN", {"default": True}), + "include_safety_note": ("BOOLEAN", {"default": True}), + "preset_action": (["none", "save", "load", "list"], {"default": "none"}), + "preset_name": ("STRING", {"default": ""}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ("stream_prompt", "negative_prompt", "production_notes", "preset_status") + FUNCTION = "stream" + CATEGORY = "NoxPrompter/NSFW" + + def stream( + self, + persona, + stage_theme, + camera_style, + energy_profile, + wardrobe_style="", + prop_list="", + overlay_effects="", + chat_tone="", + call_to_action="", + playlist_prompt="", + camera_motions="", + safety_focus="", + custom_persona="", + custom_stage="", + custom_camera="", + custom_energy="", + custom_negative="", + custom_safety_note="", + include_negative_baseline=True, + include_safety_note=True, + preset_action="none", + preset_name="", + ): + config = { + "persona": persona, + "stage_theme": stage_theme, + "camera_style": camera_style, + "energy_profile": energy_profile, + "wardrobe_style": wardrobe_style, + "prop_list": prop_list, + "overlay_effects": overlay_effects, + "chat_tone": chat_tone, + "call_to_action": call_to_action, + "playlist_prompt": playlist_prompt, + "camera_motions": camera_motions, + "safety_focus": safety_focus, + "custom_persona": custom_persona, + "custom_stage": custom_stage, + "custom_camera": custom_camera, + "custom_energy": custom_energy, + "custom_negative": custom_negative, + "custom_safety_note": custom_safety_note, + "include_negative_baseline": include_negative_baseline, + "include_safety_note": include_safety_note, + } + + config, preset_status = self._apply_preset_action( + "nox_camz", + preset_action, + preset_name, + config, + ) + + persona = config.get("persona", persona) + stage_theme = config.get("stage_theme", stage_theme) + camera_style = config.get("camera_style", camera_style) + energy_profile = config.get("energy_profile", energy_profile) + wardrobe_style = config.get("wardrobe_style", wardrobe_style) + prop_list = config.get("prop_list", prop_list) + overlay_effects = config.get("overlay_effects", overlay_effects) + chat_tone = config.get("chat_tone", chat_tone) + call_to_action = config.get("call_to_action", call_to_action) + playlist_prompt = config.get("playlist_prompt", playlist_prompt) + camera_motions = config.get("camera_motions", camera_motions) + safety_focus = config.get("safety_focus", safety_focus) + custom_persona = config.get("custom_persona", custom_persona) + custom_stage = config.get("custom_stage", custom_stage) + custom_camera = config.get("custom_camera", custom_camera) + custom_energy = config.get("custom_energy", custom_energy) + custom_negative = config.get("custom_negative", custom_negative) + custom_safety_note = config.get("custom_safety_note", custom_safety_note) + include_negative_baseline = bool(config.get("include_negative_baseline", include_negative_baseline)) + include_safety_note = bool(config.get("include_safety_note", include_safety_note)) + + persona_prompt, persona_note = _resolve_with_custom(persona, custom_persona, CAMZ_PERSONA_OPTIONS) + stage_prompt, stage_note = _resolve_with_custom(stage_theme, custom_stage, CAMZ_STAGE_OPTIONS) + camera_prompt, camera_note = _resolve_with_custom(camera_style, custom_camera, CAMZ_CAMERA_OPTIONS) + energy_prompt, energy_note = _resolve_with_custom(energy_profile, custom_energy, CAMZ_ENERGY_OPTIONS) + + wardrobe_tokens = _split_tokens(wardrobe_style) + prop_tokens = _split_tokens(prop_list) + overlay_tokens = _split_tokens(overlay_effects) + chat_tokens = _split_tokens(chat_tone) + playlist_tokens = _split_tokens(playlist_prompt) + motion_tokens = _split_tokens(camera_motions) + + positive_segments = [ + "4k streaming cam capture, 60fps fluidity, direct lens eye contact, cinematic webcam depth-of-field", + persona_prompt, + stage_prompt, + camera_prompt, + energy_prompt, + ] + + if wardrobe_tokens: + positive_segments.append("wardrobe focus: " + ", ".join(wardrobe_tokens)) + if prop_tokens: + positive_segments.append("set dressing: " + ", ".join(prop_tokens)) + if overlay_tokens: + positive_segments.append("live overlays: " + ", ".join(overlay_tokens)) + if chat_tokens: + positive_segments.append("viewer interaction tone: " + ", ".join(chat_tokens)) + if playlist_tokens: + positive_segments.append("audio bed: " + ", ".join(playlist_tokens)) + if motion_tokens: + positive_segments.append("camera motions: " + ", ".join(motion_tokens)) + if call_to_action.strip(): + positive_segments.append(call_to_action.strip()) + + stream_prompt = ", ".join(segment for segment in positive_segments if segment) + + negative_terms = [] + if include_negative_baseline: + negative_terms.extend(NSFW_BASELINE_NEGATIVES) + negative_terms.extend(_split_tokens(custom_negative)) + dedup_terms: List[str] = [] + seen_terms = set() + for term in negative_terms: + normalized = term.strip() + if not normalized: + continue + key = normalized.lower() + if key not in seen_terms: + seen_terms.add(key) + dedup_terms.append(normalized) + if not dedup_terms: + dedup_terms = list(NSFW_BASELINE_NEGATIVES) + negative_prompt = ", ".join(dedup_terms) + + production_lines = [] + if persona_note: + production_lines.append(f"Persona: {persona_note}") + if stage_note: + production_lines.append(f"Stage: {stage_note}") + if camera_note: + production_lines.append(f"Camera: {camera_note}") + if energy_note: + production_lines.append(f"Energy: {energy_note}") + if overlay_tokens: + production_lines.append("Overlay cues: " + ", ".join(overlay_tokens)) + if playlist_tokens: + production_lines.append("Playlist: " + ", ".join(playlist_tokens)) + if motion_tokens: + production_lines.append("Camera moves: " + ", ".join(motion_tokens)) + if safety_focus.strip(): + production_lines.append("Safety focus: " + safety_focus.strip()) + if call_to_action.strip(): + production_lines.append("CTA: " + call_to_action.strip()) + + safety_lines = [] + if include_safety_note: + safety_lines.append("For consenting adult audiences only. Verify age compliance and platform rules before streaming.") + if custom_safety_note.strip(): + safety_lines.append(custom_safety_note.strip()) + if safety_lines: + production_lines.append("Safety: " + " | ".join(safety_lines)) + + production_notes = "\n".join(production_lines) + + return ( + stream_prompt.strip(), + negative_prompt, + production_notes, + preset_status, + ) + +__all__ = ["NoxPromptCamz"] diff --git a/noxprompter/nodes/nox_prompt_character_creator.py b/noxprompter/nodes/nox_prompt_character_creator.py new file mode 100644 index 0000000..050b007 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_character_creator.py @@ -0,0 +1,238 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Tuple +from ..common import PresetMixin, _resolve_with_custom, _split_tokens +from ..constants import * # noqa: F403 + +class NoxPromptCharacterCreator(PresetMixin): + """Build richly-detailed character prompts and quick-reference sheets.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "species": (list(CHARACTER_SPECIES_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Human"}), + "archetype": (list(CHARACTER_ARCHETYPE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Champion"}), + "profession": (list(CHARACTER_PROFESSION_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Starship Captain"}), + }, + "optional": { + "temperament": (list(CHARACTER_TEMPERAMENT_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Stoic"}), + "power_source": (list(CHARACTER_POWER_SOURCE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Arcane Relic"}), + "signature_gear": (list(CHARACTER_SIGNATURE_GEAR_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Living Blade"}), + "visual_motif": (list(CHARACTER_VISUAL_MOTIF_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Solar Flare"}), + "era": (list(CHARACTER_ERA_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Mythic Age"}), + "body_type": (list(CHARACTER_BODY_TYPE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Athletic"}), + "alignment": (list(CHARACTER_ALIGNMENT_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Lawful Good"}), + "team_role": (list(CHARACTER_ROLE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Party Leader"}), + "catchphrase": ("STRING", {"default": ""}), + "signature_move": ("STRING", {"default": ""}), + "backstory_snippet": ("STRING", {"multiline": True, "default": ""}), + "custom_traits": ("STRING", {"multiline": True, "default": ""}), + "negative_prompt": ("STRING", {"multiline": True, "default": ""}), + "presence_rating": ("FLOAT", {"default": 0.75, "min": 0.0, "max": 1.0, "step": 0.05}), + "species_custom": ("STRING", {"default": ""}), + "archetype_custom": ("STRING", {"default": ""}), + "profession_custom": ("STRING", {"default": ""}), + "temperament_custom": ("STRING", {"default": ""}), + "power_source_custom": ("STRING", {"default": ""}), + "signature_gear_custom": ("STRING", {"default": ""}), + "visual_motif_custom": ("STRING", {"default": ""}), + "era_custom": ("STRING", {"default": ""}), + "body_type_custom": ("STRING", {"default": ""}), + "alignment_custom": ("STRING", {"default": ""}), + "team_role_custom": ("STRING", {"default": ""}), + "preset_action": (["none", "save", "load", "list"], {"default": "none"}), + "preset_name": ("STRING", {"default": ""}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ("character_prompt", "character_sheet", "story_hooks", "preset_status") + FUNCTION = "craft" + CATEGORY = "NoxPrompter/Character" + + def craft( + self, + species, + archetype, + profession, + temperament="Stoic", + power_source="Arcane Relic", + signature_gear="Living Blade", + visual_motif="Solar Flare", + era="Mythic Age", + body_type="Athletic", + alignment="Lawful Good", + team_role="Party Leader", + species_custom="", + archetype_custom="", + profession_custom="", + temperament_custom="", + power_source_custom="", + signature_gear_custom="", + visual_motif_custom="", + era_custom="", + body_type_custom="", + alignment_custom="", + team_role_custom="", + catchphrase="", + signature_move="", + backstory_snippet="", + custom_traits="", + negative_prompt="", + presence_rating=0.75, + preset_action="none", + preset_name="", + ): + config = { + "species": species, + "archetype": archetype, + "profession": profession, + "temperament": temperament, + "power_source": power_source, + "signature_gear": signature_gear, + "visual_motif": visual_motif, + "era": era, + "body_type": body_type, + "alignment": alignment, + "team_role": team_role, + "species_custom": species_custom, + "archetype_custom": archetype_custom, + "profession_custom": profession_custom, + "temperament_custom": temperament_custom, + "power_source_custom": power_source_custom, + "signature_gear_custom": signature_gear_custom, + "visual_motif_custom": visual_motif_custom, + "era_custom": era_custom, + "body_type_custom": body_type_custom, + "alignment_custom": alignment_custom, + "team_role_custom": team_role_custom, + "catchphrase": catchphrase, + "signature_move": signature_move, + "backstory_snippet": backstory_snippet, + "custom_traits": custom_traits, + "negative_prompt": negative_prompt, + "presence_rating": presence_rating, + } + + config, preset_status = self._apply_preset_action( + "character_creator", + preset_action, + preset_name, + config, + ) + + species = config.get("species", species) + archetype = config.get("archetype", archetype) + profession = config.get("profession", profession) + temperament = config.get("temperament", temperament) + power_source = config.get("power_source", power_source) + signature_gear = config.get("signature_gear", signature_gear) + visual_motif = config.get("visual_motif", visual_motif) + era = config.get("era", era) + body_type = config.get("body_type", body_type) + alignment = config.get("alignment", alignment) + team_role = config.get("team_role", team_role) + species_custom = config.get("species_custom", species_custom) + archetype_custom = config.get("archetype_custom", archetype_custom) + profession_custom = config.get("profession_custom", profession_custom) + temperament_custom = config.get("temperament_custom", temperament_custom) + power_source_custom = config.get("power_source_custom", power_source_custom) + signature_gear_custom = config.get("signature_gear_custom", signature_gear_custom) + visual_motif_custom = config.get("visual_motif_custom", visual_motif_custom) + era_custom = config.get("era_custom", era_custom) + body_type_custom = config.get("body_type_custom", body_type_custom) + alignment_custom = config.get("alignment_custom", alignment_custom) + team_role_custom = config.get("team_role_custom", team_role_custom) + catchphrase = config.get("catchphrase", catchphrase) + signature_move = config.get("signature_move", signature_move) + backstory_snippet = config.get("backstory_snippet", backstory_snippet) + custom_traits = config.get("custom_traits", custom_traits) + negative_prompt = config.get("negative_prompt", negative_prompt) + presence_rating = float(config.get("presence_rating", presence_rating)) + species_prompt, species_notes = _resolve_with_custom(species, species_custom, CHARACTER_SPECIES_OPTIONS) + archetype_prompt, archetype_notes = _resolve_with_custom(archetype, archetype_custom, CHARACTER_ARCHETYPE_OPTIONS) + profession_prompt, profession_notes = _resolve_with_custom(profession, profession_custom, CHARACTER_PROFESSION_OPTIONS) + temperament_prompt, temperament_notes = _resolve_with_custom(temperament, temperament_custom, CHARACTER_TEMPERAMENT_OPTIONS) + power_prompt, power_notes = _resolve_with_custom(power_source, power_source_custom, CHARACTER_POWER_SOURCE_OPTIONS) + gear_prompt, gear_notes = _resolve_with_custom(signature_gear, signature_gear_custom, CHARACTER_SIGNATURE_GEAR_OPTIONS) + motif_prompt, motif_notes = _resolve_with_custom(visual_motif, visual_motif_custom, CHARACTER_VISUAL_MOTIF_OPTIONS) + era_prompt, era_notes = _resolve_with_custom(era, era_custom, CHARACTER_ERA_OPTIONS) + body_prompt, body_notes = _resolve_with_custom(body_type, body_type_custom, CHARACTER_BODY_TYPE_OPTIONS) + alignment_prompt, alignment_notes = _resolve_with_custom(alignment, alignment_custom, CHARACTER_ALIGNMENT_OPTIONS) + role_prompt, role_notes = _resolve_with_custom(team_role, team_role_custom, CHARACTER_ROLE_OPTIONS) + + traits = _split_tokens(custom_traits) + trait_prompt = ", ".join(traits) + + presence_descriptor = self._describe_presence(presence_rating) + + prompt_parts = [ + species_prompt, + body_prompt, + archetype_prompt, + profession_prompt, + temperament_prompt, + power_prompt, + motif_prompt, + gear_prompt, + alignment_prompt, + role_prompt, + f"signature move: {signature_move.strip()}" if signature_move.strip() else "", + f"operates during {era_prompt}" if era_prompt else "", + trait_prompt, + f"presence reads as {presence_descriptor}", + ] + + character_prompt = ", ".join(part for part in prompt_parts if part).strip() + if backstory_snippet.strip(): + character_prompt = f"{character_prompt}. {backstory_snippet.strip()}" if character_prompt else backstory_snippet.strip() + if character_prompt and character_prompt[-1] not in ".!?": + character_prompt += "." + + sheet_lines = [ + f"Species Notes: {species_notes}" if species_notes else "", + f"Archetype Notes: {archetype_notes}" if archetype_notes else "", + f"Profession Notes: {profession_notes}" if profession_notes else "", + f"Temperament: {temperament_notes}" if temperament_notes else "", + f"Power Source: {power_notes}" if power_notes else "", + f"Signature Gear: {gear_notes}" if gear_notes else "", + f"Visual Motif: {motif_notes}" if motif_notes else "", + f"Era Context: {era_notes}" if era_notes else "", + f"Body Type: {body_notes}" if body_notes else "", + f"Alignment: {alignment_notes}" if alignment_notes else "", + f"Team Role: {role_notes}" if role_notes else "", + ] + if traits: + sheet_lines.append("Distinct Traits: " + ", ".join(traits)) + + character_sheet = " | ".join(line for line in sheet_lines if line) + + hooks = [ + f"Catchphrase: \"{catchphrase.strip()}\"" if catchphrase.strip() else "", + f"Signature Move: {signature_move.strip()}" if signature_move.strip() else "", + f"Team Role: {team_role}" if team_role else "", + f"Alignment: {alignment}" if alignment else "", + f"Presence Rating: {presence_rating:.2f} ({presence_descriptor})", + ] + if negative_prompt.strip(): + hooks.append("Avoid: " + negative_prompt.strip()) + story_hooks = " | ".join(item for item in hooks if item) + + return (character_prompt, character_sheet, story_hooks, preset_status) + + def _describe_presence(self, value: float) -> str: + clamped = max(0.0, min(1.0, value)) + if clamped < 0.15: + return "whisper-soft aura" + if clamped < 0.35: + return "subtle undercurrent" + if clamped < 0.55: + return "balanced presence" + if clamped < 0.75: + return "commanding focus" + if clamped < 0.9: + return "formidable charisma" + return "legendary gravitas" + +__all__ = ["NoxPromptCharacterCreator"] diff --git a/noxprompter/nodes/nox_prompt_combiner.py b/noxprompter/nodes/nox_prompt_combiner.py new file mode 100644 index 0000000..0b5f880 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_combiner.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +from ..constants import * # noqa: F403 + +class NoxPromptCombiner: + """ + A node for combining multiple prompts with different blending modes. + """ + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "prompt_1": ("STRING", { + "multiline": True, + "default": "" + }), + "prompt_2": ("STRING", { + "multiline": True, + "default": "" + }), + "combination_mode": (["concatenate", "blend", "alternate", "weighted"], { + "default": "concatenate" + }), + "weight_1": ("FLOAT", { + "default": 1.0, + "min": 0.0, + "max": 2.0, + "step": 0.1 + }), + "weight_2": ("FLOAT", { + "default": 1.0, + "min": 0.0, + "max": 2.0, + "step": 0.1 + }), + }, + "optional": { + "prompt_3": ("STRING", { + "multiline": True, + "default": "" + }), + "separator": ("STRING", { + "default": ", " + }), + } + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("combined_prompt",) + FUNCTION = "combine_prompts" + CATEGORY = "NoxPrompter/Text" + + def combine_prompts(self, prompt_1, prompt_2, combination_mode, weight_1, weight_2, + prompt_3="", separator=", "): + """ + Combine prompts using different combination modes. + """ + + prompts = [p.strip() for p in [prompt_1, prompt_2, prompt_3] if p.strip()] + + if not prompts: + return ("",) + + if len(prompts) == 1: + return (prompts[0],) + + if combination_mode == "concatenate": + return (separator.join(prompts),) + + elif combination_mode == "blend": + # Simple blending - interleave words + words = [] + all_words = [p.split() for p in prompts] + max_len = max(len(w) for w in all_words) + + for i in range(max_len): + for word_list in all_words: + if i < len(word_list): + words.append(word_list[i]) + + return (" ".join(words),) + + elif combination_mode == "alternate": + # Alternate between prompts sentence by sentence + sentences = [] + for i, prompt in enumerate(prompts): + prompt_sentences = [s.strip() for s in prompt.split('.') if s.strip()] + for j, sentence in enumerate(prompt_sentences): + if (i + j) % len(prompts) == 0: + sentences.append(sentence) + + return (". ".join(sentences) + ".",) + + elif combination_mode == "weighted": + # Apply weights using parentheses + weighted_prompts = [] + weights = [weight_1, weight_2, 1.0] # Default weight for prompt_3 + + for prompt, weight in zip(prompts, weights[:len(prompts)]): + if weight > 1.0: + emphasis_level = int((weight - 1.0) * 10) + emphasis_chars = "(" * min(emphasis_level, 5) + closing_chars = ")" * len(emphasis_chars) + weighted_prompts.append(f"{emphasis_chars}{prompt}{closing_chars}") + elif weight < 1.0: + # Use square brackets for de-emphasis + deemphasis_level = int((1.0 - weight) * 10) + deemphasis_chars = "[" * min(deemphasis_level, 5) + closing_chars = "]" * len(deemphasis_chars) + weighted_prompts.append(f"{deemphasis_chars}{prompt}{closing_chars}") + else: + weighted_prompts.append(prompt) + + return (separator.join(weighted_prompts),) + + return (separator.join(prompts),) + +__all__ = ["NoxPromptCombiner"] diff --git a/noxprompter/nodes/nox_prompt_enhancer.py b/noxprompter/nodes/nox_prompt_enhancer.py new file mode 100644 index 0000000..2bc55e3 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_enhancer.py @@ -0,0 +1,190 @@ +from __future__ import annotations + +import re +from typing import Any, Dict, List, Tuple +from ..common import PresetMixin +from ..constants import * # noqa: F403 + +class NoxPromptEnhancer(PresetMixin): + """ + A custom ComfyUI node for enhancing and manipulating text prompts. + Provides various prompt enhancement features including style application, + keyword emphasis, and prompt combination. + """ + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "base_prompt": ("STRING", { + "multiline": True, + "default": "a beautiful landscape" + }), + "enhancement_mode": (["none", "artistic", "photorealistic", "cinematic", "fantasy", "sci-fi", "portrait"], { + "default": "none" + }), + "emphasis_strength": ("FLOAT", { + "default": 1.0, + "min": 0.0, + "max": 2.0, + "step": 0.1 + }), + "add_quality_tags": ("BOOLEAN", {"default": True}), + }, + "optional": { + "secondary_prompt": ("STRING", { + "multiline": True, + "default": "" + }), + "negative_prompt": ("STRING", { + "multiline": True, + "default": "" + }), + "custom_style": ("STRING", { + "multiline": True, + "default": "" + }), + "preset_action": (["none", "save", "load", "list"], {"default": "none"}), + "preset_name": ("STRING", {"default": ""}), + } + } + + RETURN_TYPES = ("STRING", "STRING", "STRING") + RETURN_NAMES = ("enhanced_prompt", "negative_prompt", "preset_status") + FUNCTION = "enhance_prompt" + CATEGORY = "NoxPrompter/Text" + + def enhance_prompt(self, base_prompt, enhancement_mode, emphasis_strength, add_quality_tags, + secondary_prompt="", negative_prompt="", custom_style="", + preset_action="none", preset_name=""): + """ + Enhance the input prompt based on the selected parameters. + """ + + config = { + "base_prompt": base_prompt, + "enhancement_mode": enhancement_mode, + "emphasis_strength": emphasis_strength, + "add_quality_tags": add_quality_tags, + "secondary_prompt": secondary_prompt, + "negative_prompt": negative_prompt, + "custom_style": custom_style, + } + + config, preset_status = self._apply_preset_action( + "prompt_enhancer", + preset_action, + preset_name, + config, + ) + + base_prompt = config["base_prompt"] + enhancement_mode = config["enhancement_mode"] + emphasis_strength = float(config["emphasis_strength"]) + add_quality_tags = bool(config["add_quality_tags"]) + secondary_prompt = config.get("secondary_prompt", "") + negative_prompt = config.get("negative_prompt", "") + custom_style = config.get("custom_style", "") + + # Start with the base prompt + enhanced_prompt = base_prompt.strip() + + # Apply emphasis strength if greater than 1.0 + if emphasis_strength > 1.0: + # Add parentheses for emphasis (ComfyUI/Stable Diffusion syntax) + emphasis_level = int((emphasis_strength - 1.0) * 10) + emphasis_chars = "(" * min(emphasis_level, 5) # Cap at 5 levels + closing_chars = ")" * len(emphasis_chars) + enhanced_prompt = f"{emphasis_chars}{enhanced_prompt}{closing_chars}" + + # Apply enhancement mode styles + style_additions = self._get_style_additions(enhancement_mode) + if style_additions: + enhanced_prompt = f"{enhanced_prompt}, {style_additions}" + + # Add custom style if provided + if custom_style.strip(): + enhanced_prompt = f"{enhanced_prompt}, {custom_style.strip()}" + + # Combine with secondary prompt if provided + if secondary_prompt.strip(): + enhanced_prompt = f"{enhanced_prompt}, {secondary_prompt.strip()}" + + # Add quality tags if enabled + if add_quality_tags: + quality_tags = self._get_quality_tags(enhancement_mode) + enhanced_prompt = f"{enhanced_prompt}, {quality_tags}" + + # Process negative prompt + processed_negative = self._process_negative_prompt(negative_prompt, enhancement_mode) + + # Clean up the final prompt + enhanced_prompt = self._clean_prompt(enhanced_prompt) + + return (enhanced_prompt, processed_negative, preset_status) + + def _get_style_additions(self, mode): + """Get style-specific prompt additions based on enhancement mode.""" + styles = { + "artistic": "artistic, painterly, expressive brushstrokes, vibrant colors", + "photorealistic": "photorealistic, highly detailed, sharp focus, professional photography", + "cinematic": "cinematic lighting, dramatic composition, film grain, movie still", + "fantasy": "fantasy art, magical atmosphere, ethereal lighting, mystical", + "sci-fi": "sci-fi, futuristic, high-tech, neon lighting, cyberpunk aesthetic", + "portrait": "portrait photography, professional lighting, shallow depth of field, bokeh" + } + return styles.get(mode, "") + + def _get_quality_tags(self, mode): + """Get quality enhancement tags based on the mode.""" + base_quality = "high quality, detailed, masterpiece" + + mode_specific = { + "artistic": "8k resolution, trending on artstation", + "photorealistic": "8k uhd, dslr, soft lighting, high quality", + "cinematic": "4k, imax, film photography", + "fantasy": "concept art, digital painting, trending on artstation", + "sci-fi": "concept art, digital art, trending on artstation", + "portrait": "professional photography, studio lighting" + } + + specific_tags = mode_specific.get(mode, "best quality") + return f"{base_quality}, {specific_tags}" + + def _process_negative_prompt(self, negative_prompt, mode): + """Process and enhance the negative prompt based on mode.""" + base_negative = negative_prompt.strip() if negative_prompt.strip() else "" + + # Add common negative prompts based on mode + mode_negatives = { + "photorealistic": "cartoon, anime, painting, drawing, illustration", + "artistic": "photograph, photo, realistic", + "cinematic": "amateur, low quality, blurry", + "fantasy": "modern, contemporary, realistic photography", + "sci-fi": "medieval, ancient, primitive", + "portrait": "full body, landscape, multiple people" + } + + mode_specific = mode_negatives.get(mode, "") + + # Combine negatives + if base_negative and mode_specific: + return f"{base_negative}, {mode_specific}" + elif mode_specific: + return mode_specific + else: + return base_negative + + def _clean_prompt(self, prompt): + """Clean up the prompt by removing duplicate commas and extra spaces.""" + # Remove multiple consecutive commas + prompt = re.sub(r',\s*,', ',', prompt) + # Remove spaces before commas + prompt = re.sub(r'\s+,', ',', prompt) + # Remove multiple consecutive spaces + prompt = re.sub(r'\s+', ' ', prompt) + # Add space after commas if missing + prompt = re.sub(r',([^\s])', r', \1', prompt) + return prompt.strip() + +__all__ = ["NoxPromptEnhancer"] diff --git a/noxprompter/nodes/nox_prompt_human_designer.py b/noxprompter/nodes/nox_prompt_human_designer.py new file mode 100644 index 0000000..3e851c3 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_human_designer.py @@ -0,0 +1,299 @@ +from __future__ import annotations + +from ..common import PresetMixin, _resolve_with_custom, _split_tokens +from ..constants import ( + CUSTOM_OPTION, + HUMAN_DESIGN_BEAUTY_MARK_OPTIONS, + HUMAN_DESIGN_BODY_SHAPE_OPTIONS, + HUMAN_DESIGN_BODY_TYPE_OPTIONS, + HUMAN_DESIGN_EYE_COLOR_OPTIONS, + HUMAN_DESIGN_EYE_DETAIL_OPTIONS, + HUMAN_DESIGN_FACE_OPTIONS, + HUMAN_DESIGN_GENDER_OPTIONS, + HUMAN_DESIGN_GENITAL_OPTIONS, + HUMAN_DESIGN_HAIR_COLOR_OPTIONS, + HUMAN_DESIGN_HAIR_LENGTH_OPTIONS, + HUMAN_DESIGN_HAIR_STYLE_OPTIONS, + HUMAN_DESIGN_HEIGHT_OPTIONS, + HUMAN_DESIGN_SKIN_DETAIL_OPTIONS, + HUMAN_DESIGN_SKIN_TONE_OPTIONS, +) + +class NoxPromptHumanDesigner(PresetMixin): + """Curate adult human anatomy and styling breakdowns.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "gender": (list(HUMAN_DESIGN_GENDER_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Adult Woman"}), + "height": (list(HUMAN_DESIGN_HEIGHT_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Average"}), + "face_type": (list(HUMAN_DESIGN_FACE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Oval"}), + "body_type": (list(HUMAN_DESIGN_BODY_TYPE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Athletic"}), + "body_shape": (list(HUMAN_DESIGN_BODY_SHAPE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Hourglass"}), + }, + "optional": { + "hair_length": (list(HUMAN_DESIGN_HAIR_LENGTH_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Shoulder"}), + "hair_color": (list(HUMAN_DESIGN_HAIR_COLOR_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Deep Brunette"}), + "hair_style": (list(HUMAN_DESIGN_HAIR_STYLE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Loose Waves"}), + "eye_detail": (list(HUMAN_DESIGN_EYE_DETAIL_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Almond Focus"}), + "eye_color": (list(HUMAN_DESIGN_EYE_COLOR_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Amber Luminescence"}), + "skin_tone": (list(HUMAN_DESIGN_SKIN_TONE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Warm Honey"}), + "skin_detail": (list(HUMAN_DESIGN_SKIN_DETAIL_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Soft Glow"}), + "beauty_marks": (list(HUMAN_DESIGN_BEAUTY_MARK_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Bare"}), + "genital_description": (list(HUMAN_DESIGN_GENITAL_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Discrete"}), + "accent_features": ("STRING", {"multiline": True, "default": ""}), + "pose_hint": ("STRING", {"default": ""}), + "consent_language": ("STRING", {"default": "All subjects depicted are consenting adults aged 18+."}), + "safety_addendum": ("STRING", {"default": ""}), + "negative_prompt": ("STRING", {"multiline": True, "default": ""}), + "gender_custom": ("STRING", {"default": ""}), + "height_custom": ("STRING", {"default": ""}), + "face_type_custom": ("STRING", {"default": ""}), + "body_type_custom": ("STRING", {"default": ""}), + "body_shape_custom": ("STRING", {"default": ""}), + "hair_length_custom": ("STRING", {"default": ""}), + "hair_color_custom": ("STRING", {"default": ""}), + "hair_style_custom": ("STRING", {"default": ""}), + "eye_detail_custom": ("STRING", {"default": ""}), + "eye_color_custom": ("STRING", {"default": ""}), + "skin_tone_custom": ("STRING", {"default": ""}), + "skin_detail_custom": ("STRING", {"default": ""}), + "beauty_marks_custom": ("STRING", {"default": ""}), + "genital_description_custom": ("STRING", {"default": ""}), + "preset_action": (["none", "save", "load", "list"], {"default": "none"}), + "preset_name": ("STRING", {"default": ""}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ("design_prompt", "feature_sheet", "adult_notes", "preset_status") + FUNCTION = "design" + CATEGORY = "NoxPrompter/Human" + + def design( + self, + gender, + height, + face_type, + body_type, + body_shape, + hair_length="Shoulder", + hair_color="Deep Brunette", + hair_style="Loose Waves", + eye_detail="Almond Focus", + eye_color="Amber Luminescence", + skin_tone="Warm Honey", + skin_detail="Soft Glow", + beauty_marks="Bare", + genital_description="Discrete", + accent_features="", + pose_hint="", + consent_language="All subjects depicted are consenting adults aged 18+.", + safety_addendum="", + negative_prompt="", + gender_custom="", + height_custom="", + face_type_custom="", + body_type_custom="", + body_shape_custom="", + hair_length_custom="", + hair_color_custom="", + hair_style_custom="", + eye_detail_custom="", + eye_color_custom="", + skin_tone_custom="", + skin_detail_custom="", + beauty_marks_custom="", + genital_description_custom="", + preset_action="none", + preset_name="", + ): + config = { + "gender": gender, + "height": height, + "face_type": face_type, + "body_type": body_type, + "body_shape": body_shape, + "hair_length": hair_length, + "hair_color": hair_color, + "hair_style": hair_style, + "eye_detail": eye_detail, + "eye_color": eye_color, + "skin_tone": skin_tone, + "skin_detail": skin_detail, + "beauty_marks": beauty_marks, + "genital_description": genital_description, + "accent_features": accent_features, + "pose_hint": pose_hint, + "consent_language": consent_language, + "safety_addendum": safety_addendum, + "negative_prompt": negative_prompt, + "gender_custom": gender_custom, + "height_custom": height_custom, + "face_type_custom": face_type_custom, + "body_type_custom": body_type_custom, + "body_shape_custom": body_shape_custom, + "hair_length_custom": hair_length_custom, + "hair_color_custom": hair_color_custom, + "hair_style_custom": hair_style_custom, + "eye_detail_custom": eye_detail_custom, + "eye_color_custom": eye_color_custom, + "skin_tone_custom": skin_tone_custom, + "skin_detail_custom": skin_detail_custom, + "beauty_marks_custom": beauty_marks_custom, + "genital_description_custom": genital_description_custom, + } + + config, preset_status = self._apply_preset_action( + "human_designer", + preset_action, + preset_name, + config, + ) + + gender = config.get("gender", gender) + height = config.get("height", height) + face_type = config.get("face_type", face_type) + body_type = config.get("body_type", body_type) + body_shape = config.get("body_shape", body_shape) + hair_length = config.get("hair_length", hair_length) + hair_color = config.get("hair_color", hair_color) + hair_style = config.get("hair_style", hair_style) + eye_detail = config.get("eye_detail", eye_detail) + eye_color = config.get("eye_color", eye_color) + skin_tone = config.get("skin_tone", skin_tone) + skin_detail = config.get("skin_detail", skin_detail) + beauty_marks = config.get("beauty_marks", beauty_marks) + genital_description = config.get("genital_description", genital_description) + accent_features = config.get("accent_features", accent_features) + pose_hint = config.get("pose_hint", pose_hint) + consent_language = config.get("consent_language", consent_language) + safety_addendum = config.get("safety_addendum", safety_addendum) + negative_prompt = config.get("negative_prompt", negative_prompt) + gender_custom = config.get("gender_custom", gender_custom) + height_custom = config.get("height_custom", height_custom) + face_type_custom = config.get("face_type_custom", face_type_custom) + body_type_custom = config.get("body_type_custom", body_type_custom) + body_shape_custom = config.get("body_shape_custom", body_shape_custom) + hair_length_custom = config.get("hair_length_custom", hair_length_custom) + hair_color_custom = config.get("hair_color_custom", hair_color_custom) + hair_style_custom = config.get("hair_style_custom", hair_style_custom) + eye_detail_custom = config.get("eye_detail_custom", eye_detail_custom) + eye_color_custom = config.get("eye_color_custom", eye_color_custom) + skin_tone_custom = config.get("skin_tone_custom", skin_tone_custom) + skin_detail_custom = config.get("skin_detail_custom", skin_detail_custom) + beauty_marks_custom = config.get("beauty_marks_custom", beauty_marks_custom) + genital_description_custom = config.get("genital_description_custom", genital_description_custom) + + gender_prompt, gender_notes = _resolve_with_custom(gender, gender_custom, HUMAN_DESIGN_GENDER_OPTIONS) + height_prompt, height_notes = _resolve_with_custom(height, height_custom, HUMAN_DESIGN_HEIGHT_OPTIONS) + face_prompt, face_notes = _resolve_with_custom(face_type, face_type_custom, HUMAN_DESIGN_FACE_OPTIONS) + body_type_prompt, body_type_notes = _resolve_with_custom(body_type, body_type_custom, HUMAN_DESIGN_BODY_TYPE_OPTIONS) + body_shape_prompt, body_shape_notes = _resolve_with_custom(body_shape, body_shape_custom, HUMAN_DESIGN_BODY_SHAPE_OPTIONS) + hair_length_prompt, hair_length_notes = _resolve_with_custom(hair_length, hair_length_custom, HUMAN_DESIGN_HAIR_LENGTH_OPTIONS) + hair_color_prompt, hair_color_notes = _resolve_with_custom(hair_color, hair_color_custom, HUMAN_DESIGN_HAIR_COLOR_OPTIONS) + hair_style_prompt, hair_style_notes = _resolve_with_custom(hair_style, hair_style_custom, HUMAN_DESIGN_HAIR_STYLE_OPTIONS) + eye_detail_prompt, eye_detail_notes = _resolve_with_custom(eye_detail, eye_detail_custom, HUMAN_DESIGN_EYE_DETAIL_OPTIONS) + eye_color_prompt, eye_color_notes = _resolve_with_custom(eye_color, eye_color_custom, HUMAN_DESIGN_EYE_COLOR_OPTIONS) + skin_tone_prompt, skin_tone_notes = _resolve_with_custom(skin_tone, skin_tone_custom, HUMAN_DESIGN_SKIN_TONE_OPTIONS) + skin_detail_prompt, skin_detail_notes = _resolve_with_custom(skin_detail, skin_detail_custom, HUMAN_DESIGN_SKIN_DETAIL_OPTIONS) + beauty_marks_prompt, beauty_marks_notes = _resolve_with_custom(beauty_marks, beauty_marks_custom, HUMAN_DESIGN_BEAUTY_MARK_OPTIONS) + genital_prompt, genital_notes = _resolve_with_custom(genital_description, genital_description_custom, HUMAN_DESIGN_GENITAL_OPTIONS) + + hair_components = [hair_length_prompt, hair_color_prompt, hair_style_prompt] + hair_description = ", ".join(part for part in hair_components if part) + + eye_components = [eye_detail_prompt, eye_color_prompt] + eye_description = ", ".join(part for part in eye_components if part) + + skin_components = [skin_tone_prompt, skin_detail_prompt] + skin_description = ", ".join(part for part in skin_components if part) + + def _as_sentence(text: str) -> str: + cleaned = (text or "").strip() + if not cleaned: + return "" + return cleaned if cleaned[-1] in ".!?" else f"{cleaned}." + + core_segments = [gender_prompt, height_prompt, body_type_prompt, body_shape_prompt, face_prompt] + core_description = ", ".join(part for part in core_segments if part) + + prompt_fragments = [] + if core_description: + prompt_fragments.append(core_description) + if skin_description: + prompt_fragments.append(f"Skin: {skin_description}") + if eye_description: + prompt_fragments.append(f"Eyes: {eye_description}") + if hair_description: + prompt_fragments.append(f"Hair: {hair_description}") + if beauty_marks_prompt: + prompt_fragments.append(f"Beauty marks: {beauty_marks_prompt}") + if genital_prompt: + prompt_fragments.append(f"Anatomy: {genital_prompt}") + + accent_tokens = _split_tokens(accent_features) + if accent_tokens: + prompt_fragments.append("Accent features: " + ", ".join(dict.fromkeys(accent_tokens))) + + if pose_hint.strip(): + prompt_fragments.append(f"Pose cue: {pose_hint.strip()}") + + design_prompt = " ".join(filter(None, (_as_sentence(fragment) for fragment in prompt_fragments))) + + feature_lines = [ + f"Gender/Sex: {gender_prompt}" if gender_prompt else "", + f"Height: {height_prompt}" if height_prompt else "", + f"Face Type: {face_prompt}" if face_prompt else "", + f"Body Type: {body_type_prompt}" if body_type_prompt else "", + f"Body Shape: {body_shape_prompt}" if body_shape_prompt else "", + ] + if skin_description: + feature_lines.append(f"Skin: {skin_description}") + if hair_description: + feature_lines.append(f"Hair: {hair_description}") + if eye_description: + feature_lines.append(f"Eyes: {eye_description}") + if genital_prompt: + feature_lines.append(f"Genital Description: {genital_prompt}") + if beauty_marks_prompt: + feature_lines.append(f"Beauty Marks: {beauty_marks_prompt}") + if accent_tokens: + feature_lines.append("Accent Features: " + ", ".join(dict.fromkeys(accent_tokens))) + if pose_hint.strip(): + feature_lines.append(f"Pose Cue: {pose_hint.strip()}") + feature_sheet = " | ".join(line for line in feature_lines if line) + + adult_notes_parts = [ + "All descriptions apply to consenting adults aged 18+.", + consent_language.strip(), + ] + + curated_notes = [ + gender_notes, + height_notes, + face_notes, + body_type_notes, + body_shape_notes, + hair_length_notes, + hair_color_notes, + hair_style_notes, + eye_detail_notes, + eye_color_notes, + skin_tone_notes, + skin_detail_notes, + beauty_marks_notes, + genital_notes, + ] + adult_notes_parts.extend(note for note in curated_notes if note) + if safety_addendum.strip(): + adult_notes_parts.append(safety_addendum.strip()) + if negative_prompt.strip(): + adult_notes_parts.append("Negative Prompt: " + negative_prompt.strip()) + + adult_notes = " | ".join(dict.fromkeys(part for part in adult_notes_parts if part)) + + return (design_prompt, feature_sheet, adult_notes, preset_status) + +__all__ = ["NoxPromptHumanDesigner"] diff --git a/noxprompter/nodes/nox_prompt_lighting_master.py b/noxprompter/nodes/nox_prompt_lighting_master.py new file mode 100644 index 0000000..85ce15f --- /dev/null +++ b/noxprompter/nodes/nox_prompt_lighting_master.py @@ -0,0 +1,226 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Tuple +from ..common import PresetMixin, _resolve_with_custom +from ..constants import * # noqa: F403 + +class NoxPromptLightingMaster(PresetMixin): + """Construct cinematic lighting blueprints with mood and safety notes.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "blueprint": (list(LIGHTING_BLUEPRINT_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Three-Point"}), + "key_style": (list(LIGHTING_KEY_STYLE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Softbox Key"}), + }, + "optional": { + "fill_style": (list(LIGHTING_FILL_STYLE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Soft Fill"}), + "backlight": (list(LIGHTING_BACKLIGHT_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Rim Strip"}), + "practical": (list(LIGHTING_PRACTICAL_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Lantern Cluster"}), + "atmosphere": (list(LIGHTING_ATMOSPHERE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Haze"}), + "color_gel": (list(LIGHTING_COLOR_GEL_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Teal & Orange"}), + "camera_settings": (list(LIGHTING_CAM_SETTINGS.keys()) + [CUSTOM_OPTION], {"default": "Cinematic"}), + "special_technique": (list(LIGHTING_SPECIAL_TECHNIQUES.keys()) + [CUSTOM_OPTION], {"default": "Shutter Drag"}), + "energy_level": (list(LIGHTING_ENERGY_LEVEL_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Dynamic"}), + "safety_profile": (list(["None"] + list(LIGHTING_SAFETY_NOTES.keys()) + [CUSTOM_OPTION]), {"default": "None"}), + "subject_description": ("STRING", {"multiline": True, "default": ""}), + "environment_description": ("STRING", {"multiline": True, "default": ""}), + "accent_notes": ("STRING", {"multiline": True, "default": ""}), + "intensity_bias": ("FLOAT", {"default": 0.6, "min": 0.0, "max": 1.0, "step": 0.05}), + "blueprint_custom": ("STRING", {"default": ""}), + "key_style_custom": ("STRING", {"default": ""}), + "fill_style_custom": ("STRING", {"default": ""}), + "backlight_custom": ("STRING", {"default": ""}), + "practical_custom": ("STRING", {"default": ""}), + "atmosphere_custom": ("STRING", {"default": ""}), + "color_gel_custom": ("STRING", {"default": ""}), + "camera_settings_custom": ("STRING", {"default": ""}), + "special_technique_custom": ("STRING", {"default": ""}), + "energy_level_custom": ("STRING", {"default": ""}), + "safety_profile_custom": ("STRING", {"default": ""}), + "preset_action": (["none", "save", "load", "list"], {"default": "none"}), + "preset_name": ("STRING", {"default": ""}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ("lighting_prompt", "mood_notes", "technical_notes", "preset_status") + FUNCTION = "illuminate" + CATEGORY = "NoxPrompter/Lighting" + + def illuminate( + self, + blueprint, + key_style, + fill_style="Soft Fill", + backlight="Rim Strip", + practical="Lantern Cluster", + atmosphere="Haze", + color_gel="Teal & Orange", + camera_settings="Cinematic", + special_technique="Shutter Drag", + energy_level="Dynamic", + safety_profile="None", + blueprint_custom="", + key_style_custom="", + fill_style_custom="", + backlight_custom="", + practical_custom="", + atmosphere_custom="", + color_gel_custom="", + camera_settings_custom="", + special_technique_custom="", + energy_level_custom="", + safety_profile_custom="", + subject_description="", + environment_description="", + accent_notes="", + intensity_bias=0.6, + preset_action="none", + preset_name="", + ): + config = { + "blueprint": blueprint, + "key_style": key_style, + "fill_style": fill_style, + "backlight": backlight, + "practical": practical, + "atmosphere": atmosphere, + "color_gel": color_gel, + "camera_settings": camera_settings, + "special_technique": special_technique, + "energy_level": energy_level, + "safety_profile": safety_profile, + "blueprint_custom": blueprint_custom, + "key_style_custom": key_style_custom, + "fill_style_custom": fill_style_custom, + "backlight_custom": backlight_custom, + "practical_custom": practical_custom, + "atmosphere_custom": atmosphere_custom, + "color_gel_custom": color_gel_custom, + "camera_settings_custom": camera_settings_custom, + "special_technique_custom": special_technique_custom, + "energy_level_custom": energy_level_custom, + "safety_profile_custom": safety_profile_custom, + "subject_description": subject_description, + "environment_description": environment_description, + "accent_notes": accent_notes, + "intensity_bias": intensity_bias, + } + + config, preset_status = self._apply_preset_action( + "lighting_master", + preset_action, + preset_name, + config, + ) + + blueprint = config.get("blueprint", blueprint) + key_style = config.get("key_style", key_style) + fill_style = config.get("fill_style", fill_style) + backlight = config.get("backlight", backlight) + practical = config.get("practical", practical) + atmosphere = config.get("atmosphere", atmosphere) + color_gel = config.get("color_gel", color_gel) + camera_settings = config.get("camera_settings", camera_settings) + special_technique = config.get("special_technique", special_technique) + energy_level = config.get("energy_level", energy_level) + safety_profile = config.get("safety_profile", safety_profile) + blueprint_custom = config.get("blueprint_custom", blueprint_custom) + key_style_custom = config.get("key_style_custom", key_style_custom) + fill_style_custom = config.get("fill_style_custom", fill_style_custom) + backlight_custom = config.get("backlight_custom", backlight_custom) + practical_custom = config.get("practical_custom", practical_custom) + atmosphere_custom = config.get("atmosphere_custom", atmosphere_custom) + color_gel_custom = config.get("color_gel_custom", color_gel_custom) + camera_settings_custom = config.get("camera_settings_custom", camera_settings_custom) + special_technique_custom = config.get("special_technique_custom", special_technique_custom) + energy_level_custom = config.get("energy_level_custom", energy_level_custom) + safety_profile_custom = config.get("safety_profile_custom", safety_profile_custom) + subject_description = config.get("subject_description", subject_description) + environment_description = config.get("environment_description", environment_description) + accent_notes = config.get("accent_notes", accent_notes) + intensity_bias = float(config.get("intensity_bias", intensity_bias)) + blueprint_prompt, blueprint_notes = _resolve_with_custom(blueprint, blueprint_custom, LIGHTING_BLUEPRINT_OPTIONS) + key_prompt, key_notes = _resolve_with_custom(key_style, key_style_custom, LIGHTING_KEY_STYLE_OPTIONS) + fill_prompt, fill_notes = _resolve_with_custom(fill_style, fill_style_custom, LIGHTING_FILL_STYLE_OPTIONS) + back_prompt, back_notes = _resolve_with_custom(backlight, backlight_custom, LIGHTING_BACKLIGHT_OPTIONS) + practical_prompt, practical_notes = _resolve_with_custom(practical, practical_custom, LIGHTING_PRACTICAL_OPTIONS) + atmosphere_prompt, atmosphere_notes = _resolve_with_custom(atmosphere, atmosphere_custom, LIGHTING_ATMOSPHERE_OPTIONS) + gel_prompt, gel_notes = _resolve_with_custom(color_gel, color_gel_custom, LIGHTING_COLOR_GEL_OPTIONS) + camera_prompt, camera_notes = _resolve_with_custom(camera_settings, camera_settings_custom, LIGHTING_CAM_SETTINGS) + technique_prompt, technique_notes = _resolve_with_custom(special_technique, special_technique_custom, LIGHTING_SPECIAL_TECHNIQUES) + energy_prompt, energy_notes = _resolve_with_custom(energy_level, energy_level_custom, LIGHTING_ENERGY_LEVEL_OPTIONS) + + intensity_text = self._describe_intensity(intensity_bias) + + lighting_fragments = [ + blueprint_prompt, + key_prompt, + fill_prompt, + back_prompt, + practical_prompt, + atmosphere_prompt, + gel_prompt, + technique_prompt, + f"energy mode: {energy_prompt}" if energy_prompt else "", + f"subject: {subject_description.strip()}" if subject_description.strip() else "", + f"environment: {environment_description.strip()}" if environment_description.strip() else "", + f"intensity bias: {intensity_text}", + ] + lighting_prompt = ", ".join(fragment for fragment in lighting_fragments if fragment).strip() + if lighting_prompt and lighting_prompt[-1] not in ".!?": + lighting_prompt += "." + if accent_notes.strip(): + lighting_prompt += f" Accents: {accent_notes.strip()}" + + mood_sections = [ + f"Blueprint: {blueprint_notes}" if blueprint_notes else "", + f"Key: {key_notes}" if key_notes else "", + f"Fill: {fill_notes}" if fill_notes else "", + f"Backlight: {back_notes}" if back_notes else "", + f"Practical: {practical_notes}" if practical_notes else "", + f"Atmosphere: {atmosphere_notes}" if atmosphere_notes else "", + f"Gel: {gel_notes}" if gel_notes else "", + f"Technique: {technique_notes}" if technique_notes else "", + f"Energy: {energy_notes}" if energy_notes else "", + ] + mood_notes = " | ".join(section for section in mood_sections if section) + + technical_sections = [camera_prompt] + if camera_notes: + technical_sections.append(f"Camera Notes: {camera_notes}") + safety_label = "" + safety_text = "" + if safety_profile == CUSTOM_OPTION: + safety_label = (safety_profile_custom or "").strip() + if safety_label: + safety_text = safety_label + elif safety_profile and safety_profile != "None": + safety_label = safety_profile + safety_text = LIGHTING_SAFETY_NOTES.get(safety_profile, "") + if safety_label: + if safety_text and safety_text != safety_label: + technical_sections.append(f"Safety: {safety_label} — {safety_text}") + else: + technical_sections.append(f"Safety: {safety_label}") + technical_notes = " | ".join(section for section in technical_sections if section) + + return (lighting_prompt, mood_notes, technical_notes, preset_status) + + def _describe_intensity(self, value: float) -> str: + clamped = max(0.0, min(1.0, value)) + if clamped < 0.2: + return "featherweight glow" + if clamped < 0.4: + return "gentle build" + if clamped < 0.6: + return "balanced exposure" + if clamped < 0.8: + return "pronounced contrast" + if clamped < 0.95: + return "high drama punch" + return "extreme spectacle" + +__all__ = ["NoxPromptLightingMaster"] diff --git a/noxprompter/nodes/nox_prompt_narrative_weaver.py b/noxprompter/nodes/nox_prompt_narrative_weaver.py new file mode 100644 index 0000000..da98c7a --- /dev/null +++ b/noxprompter/nodes/nox_prompt_narrative_weaver.py @@ -0,0 +1,132 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Tuple +from ..common import PresetMixin +from ..constants import * # noqa: F403 + +class NoxPromptNarrativeWeaver(PresetMixin): + """Craft narrative scaffolding for the prompt builder.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "hero_archetype": (list(NARRATIVE_ARCHETYPES.keys()), {"default": "Resolute Protector"}), + "story_tone": (list(NARRATIVE_TONES.keys()), {"default": "Hopeful Resurgence"}), + "environment": (list(NARRATIVE_ENVIRONMENTS.keys()), {"default": "Rain-Soaked Citadel"}), + }, + "optional": { + "set_piece": (list(NARRATIVE_SETPIECES.keys()), {"default": "Climactic Duel"}), + "tempo": (list(TEMPO_PROFILES.keys()), {"default": "surging"}), + "focus_detail": ("STRING", {"multiline": True, "default": ""}), + "preset_action": (["none", "save", "load", "list"], {"default": "none"}), + "preset_name": ("STRING", {"default": ""}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ( + "subject_focus", + "scene_setting", + "motion_arc", + "narrative_hook", + "extra_descriptors", + "preset_status", + ) + FUNCTION = "weave" + CATEGORY = "NoxPrompter/Companions" + + def weave( + self, + hero_archetype, + story_tone, + environment, + set_piece="Climactic Duel", + tempo="surging", + focus_detail="", + preset_action="none", + preset_name="", + ): + config = { + "hero_archetype": hero_archetype, + "story_tone": story_tone, + "environment": environment, + "set_piece": set_piece, + "tempo": tempo, + "focus_detail": focus_detail, + } + + config, preset_status = self._apply_preset_action( + "narrative_weaver", + preset_action, + preset_name, + config, + ) + + hero_archetype = config.get("hero_archetype", "Resolute Protector") + story_tone = config.get("story_tone", "Hopeful Resurgence") + environment = config.get("environment", "Rain-Soaked Citadel") + set_piece = config.get("set_piece", "Climactic Duel") + tempo = config.get("tempo", "surging") + focus_detail = config.get("focus_detail", "") + + archetype = NARRATIVE_ARCHETYPES.get(hero_archetype, {"subject": hero_archetype, "keywords": []}) + tone = NARRATIVE_TONES.get(story_tone, {"descriptor": story_tone, "ambience": "", "hook": "", "keywords": []}) + env = NARRATIVE_ENVIRONMENTS.get(environment, {"description": environment, "details": "", "keywords": []}) + setpiece = NARRATIVE_SETPIECES.get(set_piece, {"motion": set_piece, "hook": "", "keywords": []}) + tempo_cfg = TEMPO_PROFILES.get(tempo, TEMPO_PROFILES["measured build"]) + + subject_parts = [archetype.get("subject", "")] + if tempo_cfg.get("subject_accent"): + subject_parts.append(tempo_cfg["subject_accent"]) + if tone.get("descriptor"): + subject_parts.append(tone["descriptor"]) + subject_focus = ", ".join(part for part in subject_parts if part).strip(", ") + + scene_parts = [env.get("description", ""), env.get("details", ""), tone.get("ambience", "")] + scene_setting = ", ".join(part for part in scene_parts if part).strip(", ") + + motion_parts = [tempo_cfg.get("motion_prefix"), setpiece.get("motion"), tempo_cfg.get("motion_suffix")] + motion_arc = " ".join(part.strip() for part in motion_parts if part and part.strip()) + + hook_parts = [tone.get("hook"), setpiece.get("hook"), tempo_cfg.get("hook_suffix")] + narrative_hook = " ".join(part.strip() for part in hook_parts if part and part.strip()) + + descriptor_sources = [ + archetype.get("keywords", []), + tone.get("keywords", []), + env.get("keywords", []), + setpiece.get("keywords", []), + tempo_cfg.get("keywords", []), + ] + if focus_detail.strip(): + descriptor_sources.append(self._split_keywords(focus_detail)) + + descriptors = [] + for source in descriptor_sources: + descriptors.extend(source) + descriptors = list(dict.fromkeys(desc.strip() for desc in descriptors if desc and desc.strip())) + extra_descriptors = ", ".join(descriptors) + + return ( + subject_focus, + scene_setting, + motion_arc, + narrative_hook, + extra_descriptors, + preset_status, + ) + + def _split_keywords(self, text): + if not text: + return [] + separators = [",", "\n", ";"] + tokens = [text] + for sep in separators: + temp = [] + for token in tokens: + temp.extend(token.split(sep)) + tokens = temp + return [token.strip() for token in tokens if token.strip()] + +__all__ = ["NoxPromptNarrativeWeaver"] diff --git a/noxprompter/nodes/nox_prompt_nsfw_action_director.py b/noxprompter/nodes/nox_prompt_nsfw_action_director.py new file mode 100644 index 0000000..f91c742 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_nsfw_action_director.py @@ -0,0 +1,230 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Tuple +from ..common import PresetMixin, _resolve_action_option, _split_tokens +from ..constants import * # noqa: F403 + +class NoxPromptNSFWActionDirector(PresetMixin): + """Stage adult-oriented action performances with explicit safety framing.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "primary_action": (list(NSFW_ACTION_PRIMARY_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Sensual Caress"}), + }, + "optional": { + "secondary_action": (list(NSFW_ACTION_SECONDARY_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "None"}), + "tempo": (list(NSFW_ACTION_TEMPO_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Slow Tease"}), + "interaction_mode": (list(NSFW_ACTION_INTERACTION_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Solo Self-Admiration"}), + "props": ("STRING", {"multiline": True, "default": ""}), + "environment_mood": ("STRING", {"multiline": True, "default": ""}), + "heat_descriptor": ("STRING", {"default": ""}), + "camera_language": ("STRING", {"multiline": True, "default": ""}), + "soundscape": ("STRING", {"multiline": True, "default": ""}), + "dialogue_hook": ("STRING", {"multiline": True, "default": ""}), + "consent_focus": ("STRING", {"multiline": True, "default": "enthusiastic consent, explicit verbal check-ins"}), + "boundary_notes": ("STRING", {"multiline": True, "default": ""}), + "aftercare_plan": ("STRING", {"multiline": True, "default": ""}), + "safety_props": ("STRING", {"multiline": True, "default": ""}), + "beat_emphasis": ("STRING", {"multiline": True, "default": ""}), + "risk_notes": ("STRING", {"multiline": True, "default": ""}), + "primary_action_custom": ("STRING", {"multiline": True, "default": ""}), + "secondary_action_custom": ("STRING", {"multiline": True, "default": ""}), + "tempo_custom": ("STRING", {"multiline": True, "default": ""}), + "interaction_mode_custom": ("STRING", {"multiline": True, "default": ""}), + "primary_notes": ("STRING", {"multiline": True, "default": ""}), + "secondary_notes": ("STRING", {"multiline": True, "default": ""}), + "tempo_notes": ("STRING", {"multiline": True, "default": ""}), + "interaction_notes": ("STRING", {"multiline": True, "default": ""}), + "include_safety_note": ("BOOLEAN", {"default": True}), + "custom_safety_note": ("STRING", {"multiline": True, "default": ""}), + "preset_action": (["none", "save", "load", "list"], {"default": "none"}), + "preset_name": ("STRING", {"default": ""}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ("action_prompt", "action_summary", "safety_notes", "preset_status") + FUNCTION = "direct" + CATEGORY = "NoxPrompter/NSFW" + + def direct( + self, + primary_action, + secondary_action="None", + tempo="Slow Tease", + interaction_mode="Solo Self-Admiration", + props="", + environment_mood="", + heat_descriptor="", + camera_language="", + soundscape="", + dialogue_hook="", + consent_focus="enthusiastic consent, explicit verbal check-ins", + boundary_notes="", + aftercare_plan="", + safety_props="", + beat_emphasis="", + risk_notes="", + primary_action_custom="", + secondary_action_custom="", + tempo_custom="", + interaction_mode_custom="", + primary_notes="", + secondary_notes="", + tempo_notes="", + interaction_notes="", + include_safety_note=True, + custom_safety_note="", + preset_action="none", + preset_name="", + ): + config = { + "primary_action": primary_action, + "secondary_action": secondary_action, + "tempo": tempo, + "interaction_mode": interaction_mode, + "props": props, + "environment_mood": environment_mood, + "heat_descriptor": heat_descriptor, + "camera_language": camera_language, + "soundscape": soundscape, + "dialogue_hook": dialogue_hook, + "consent_focus": consent_focus, + "boundary_notes": boundary_notes, + "aftercare_plan": aftercare_plan, + "safety_props": safety_props, + "beat_emphasis": beat_emphasis, + "risk_notes": risk_notes, + "primary_action_custom": primary_action_custom, + "secondary_action_custom": secondary_action_custom, + "tempo_custom": tempo_custom, + "interaction_mode_custom": interaction_mode_custom, + "primary_notes": primary_notes, + "secondary_notes": secondary_notes, + "tempo_notes": tempo_notes, + "interaction_notes": interaction_notes, + "include_safety_note": include_safety_note, + "custom_safety_note": custom_safety_note, + } + + config, preset_status = self._apply_preset_action( + "nsfw_action_director", + preset_action, + preset_name, + config, + ) + + primary_action = config.get("primary_action", primary_action) + secondary_action = config.get("secondary_action", secondary_action) + tempo = config.get("tempo", tempo) + interaction_mode = config.get("interaction_mode", interaction_mode) + props = config.get("props", props) + environment_mood = config.get("environment_mood", environment_mood) + heat_descriptor = config.get("heat_descriptor", heat_descriptor) + camera_language = config.get("camera_language", camera_language) + soundscape = config.get("soundscape", soundscape) + dialogue_hook = config.get("dialogue_hook", dialogue_hook) + consent_focus = config.get("consent_focus", consent_focus) + boundary_notes = config.get("boundary_notes", boundary_notes) + aftercare_plan = config.get("aftercare_plan", aftercare_plan) + safety_props = config.get("safety_props", safety_props) + beat_emphasis = config.get("beat_emphasis", beat_emphasis) + risk_notes = config.get("risk_notes", risk_notes) + primary_action_custom = config.get("primary_action_custom", primary_action_custom) + secondary_action_custom = config.get("secondary_action_custom", secondary_action_custom) + tempo_custom = config.get("tempo_custom", tempo_custom) + interaction_mode_custom = config.get("interaction_mode_custom", interaction_mode_custom) + primary_notes = config.get("primary_notes", primary_notes) + secondary_notes = config.get("secondary_notes", secondary_notes) + tempo_notes = config.get("tempo_notes", tempo_notes) + interaction_notes = config.get("interaction_notes", interaction_notes) + include_safety_note = bool(config.get("include_safety_note", include_safety_note)) + custom_safety_note = config.get("custom_safety_note", custom_safety_note) + + primary_prompt, primary_summary = _resolve_action_option(primary_action, primary_action_custom, NSFW_ACTION_PRIMARY_OPTIONS) + secondary_prompt, secondary_summary = _resolve_action_option(secondary_action, secondary_action_custom, NSFW_ACTION_SECONDARY_OPTIONS) + tempo_prompt, tempo_summary = _resolve_action_option(tempo, tempo_custom, NSFW_ACTION_TEMPO_OPTIONS) + interaction_prompt, interaction_summary = _resolve_action_option(interaction_mode, interaction_mode_custom, NSFW_ACTION_INTERACTION_OPTIONS) + + props_list = _split_tokens(props) + beat_highlights = _split_tokens(beat_emphasis) + risk_list = _split_tokens(risk_notes) + safety_tool_list = _split_tokens(safety_props) + + prompt_parts = [primary_prompt] + for fragment in (secondary_prompt, tempo_prompt, interaction_prompt): + if fragment: + prompt_parts.append(fragment) + if heat_descriptor.strip(): + prompt_parts.append(heat_descriptor.strip()) + if environment_mood.strip(): + prompt_parts.append(f"set mood: {environment_mood.strip()}") + if props_list: + prompt_parts.append("props: " + ", ".join(props_list)) + action_prompt = ", ".join(part for part in prompt_parts if part) + if action_prompt and action_prompt[-1] not in ".!?": + action_prompt += "." + if camera_language.strip(): + action_prompt += f" Camera language: {camera_language.strip()}" + if soundscape.strip(): + action_prompt += f" Soundscape: {soundscape.strip()}" + if dialogue_hook.strip(): + action_prompt += f" Dialogue cue: {dialogue_hook.strip()}" + + summary_segments = [] + if primary_summary: + summary_segments.append(f"Primary: {primary_summary}") + if primary_notes.strip(): + summary_segments.append(primary_notes.strip()) + if secondary_summary: + summary_segments.append(f"Secondary: {secondary_summary}") + if secondary_notes.strip(): + summary_segments.append(secondary_notes.strip()) + if tempo_summary: + summary_segments.append(f"Tempo: {tempo_summary}") + if tempo_notes.strip(): + summary_segments.append(tempo_notes.strip()) + if interaction_summary: + summary_segments.append(f"Interaction: {interaction_summary}") + if interaction_notes.strip(): + summary_segments.append(interaction_notes.strip()) + if heat_descriptor.strip(): + summary_segments.append(f"Heat: {heat_descriptor.strip()}") + if props_list: + summary_segments.append("Props: " + ", ".join(props_list)) + if beat_highlights: + summary_segments.append("Beat focus: " + ", ".join(beat_highlights)) + if environment_mood.strip(): + summary_segments.append(f"Environment: {environment_mood.strip()}") + if camera_language.strip(): + summary_segments.append("Camera: " + camera_language.strip()) + if soundscape.strip(): + summary_segments.append("Sound: " + soundscape.strip()) + if dialogue_hook.strip(): + summary_segments.append("Dialogue: " + dialogue_hook.strip()) + if consent_focus.strip(): + summary_segments.append("Consent: " + consent_focus.strip()) + action_summary = " | ".join(segment for segment in summary_segments if segment) + + safety_parts = [] + if include_safety_note: + safety_parts.append(NSFW_DEFAULT_SAFETY_NOTE) + if consent_focus.strip(): + safety_parts.append("Consent focus: " + consent_focus.strip()) + if boundary_notes.strip(): + safety_parts.append("Boundaries: " + boundary_notes.strip()) + if risk_list: + safety_parts.append("Risks monitored: " + ", ".join(risk_list)) + if safety_tool_list: + safety_parts.append("Safety tools: " + ", ".join(safety_tool_list)) + if aftercare_plan.strip(): + safety_parts.append("Aftercare: " + aftercare_plan.strip()) + if custom_safety_note.strip(): + safety_parts.append(custom_safety_note.strip()) + safety_notes = " | ".join(part for part in safety_parts if part) + + return (action_prompt.strip(), action_summary, safety_notes, preset_status) + +__all__ = ["NoxPromptNSFWActionDirector"] diff --git a/noxprompter/nodes/nox_prompt_nsfw_aio.py b/noxprompter/nodes/nox_prompt_nsfw_aio.py new file mode 100644 index 0000000..84afae1 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_nsfw_aio.py @@ -0,0 +1,116 @@ +from __future__ import annotations + +from typing import List + +from ..common import _split_tokens +from ..constants import NSFW_BASELINE_NEGATIVES, NSFW_DEFAULT_SAFETY_NOTE + + +class NoxPromptNSFWAIO: + """Combine NSFW designer and action fragments into a single, safety-conscious output.""" + + CATEGORY = "NoxPrompter/NSFW" + FUNCTION = "combine" + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ("combined_prompt", "combined_negative", "combined_safety", "fusion_notes") + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "designer_prompt": ("STRING", {"multiline": True, "default": ""}), + "designer_negative": ("STRING", {"multiline": True, "default": ""}), + "designer_safety": ("STRING", {"multiline": True, "default": ""}), + }, + "optional": { + "action_prompt": ("STRING", {"multiline": True, "default": ""}), + "action_summary": ("STRING", {"multiline": True, "default": ""}), + "action_safety": ("STRING", {"multiline": True, "default": ""}), + "camera_notes": ("STRING", {"multiline": True, "default": ""}), + "extra_positive": ("STRING", {"multiline": True, "default": ""}), + "extra_negative": ("STRING", {"multiline": True, "default": ""}), + "extra_safety": ("STRING", {"multiline": True, "default": ""}), + "include_baseline_negatives": ("BOOLEAN", {"default": True}), + "include_default_safety": ("BOOLEAN", {"default": True}), + }, + } + + def combine( + self, + designer_prompt: str, + designer_negative: str, + designer_safety: str, + action_prompt: str = "", + action_summary: str = "", + action_safety: str = "", + camera_notes: str = "", + extra_positive: str = "", + extra_negative: str = "", + extra_safety: str = "", + include_baseline_negatives: bool = True, + include_default_safety: bool = True, + ): + positive_sections = [ + designer_prompt.strip(), + action_prompt.strip(), + extra_positive.strip(), + ] + combined_prompt = "\n".join([section for section in positive_sections if section]).strip() + + negative_tokens: List[str] = [] + for block in (designer_negative, extra_negative): + if not block: + continue + for token in _split_tokens(block.replace("\n", ",")): + cleaned = token.strip() + if cleaned: + negative_tokens.append(cleaned) + if include_baseline_negatives: + negative_tokens.extend(NSFW_BASELINE_NEGATIVES) + combined_negative = ", ".join(self._unique_sequence(negative_tokens)) + + safety_chunks: List[str] = [] + if include_default_safety: + safety_chunks.append(NSFW_DEFAULT_SAFETY_NOTE) + safety_chunks.extend([designer_safety, action_safety, extra_safety]) + combined_safety = "\n".join(self._unique_sequence(self._split_lines(safety_chunks))).strip() + + fusion_lines: List[str] = [] + if action_summary.strip(): + fusion_lines.append(f"Action Summary: {action_summary.strip()}") + if camera_notes.strip(): + fusion_lines.append(f"Camera Notes: {camera_notes.strip()}") + if extra_positive.strip(): + fusion_lines.append("Extras merged into prompt body.") + if extra_negative.strip(): + fusion_lines.append("Extra negative cues merged into combined negatives.") + fusion_notes = "\n".join(fusion_lines) + + return combined_prompt, combined_negative, combined_safety, fusion_notes + + @staticmethod + def _split_lines(chunks: List[str]) -> List[str]: + lines: List[str] = [] + for chunk in chunks: + if not chunk: + continue + for line in chunk.replace("\r", "\n").split("\n"): + cleaned = line.strip() + if cleaned: + lines.append(cleaned) + return lines + + @staticmethod + def _unique_sequence(values: List[str]) -> List[str]: + seen = set() + ordered: List[str] = [] + for value in values: + key = value.lower() + if key in seen: + continue + seen.add(key) + ordered.append(value) + return ordered + + +__all__ = ["NoxPromptNSFWAIO"] diff --git a/noxprompter/nodes/nox_prompt_nsfw_designer.py b/noxprompter/nodes/nox_prompt_nsfw_designer.py new file mode 100644 index 0000000..3fc9d18 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_nsfw_designer.py @@ -0,0 +1,457 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Tuple +from ..common import PresetMixin +from ..constants import * # noqa: F403 + +class NoxPromptNSFWDesigner(PresetMixin): + """Assemble mature-oriented prompts with responsible defaults and safeguards.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "subject_focus": ( + "STRING", + { + "multiline": True, + "default": "Confident adult model with poised expression", + }, + ), + "scene_setting": ( + "STRING", + { + "multiline": True, + "default": "Softly lit studio lounge accented with plush textures", + }, + ), + "pose_profile": (list(NSFW_POSE_PROFILES.keys()), {"default": "Suggestive Pinup"}), + "wardrobe_style": ( + list(NSFW_WARDROBE_STYLES.keys()), + {"default": "Silk Lingerie"}, + ), + "tone_profile": (list(NSFW_TONE_MOODS.keys()), {"default": "Glamorous Editorial"}), + }, + "optional": { + "lighting_setup": ( + list(NSFW_LIGHTING_SETUPS.keys()), + {"default": "Candlelit Warmth"}, + ), + "camera_framing": ( + list(NSFW_CAMERA_FRAMING.keys()), + {"default": "Intimate Portrait"}, + ), + "explicitness_level": ( + list(NSFW_EXPLICITNESS_LEVELS.keys()), + {"default": "Implied Nude"}, + ), + "set_design": ( + list(NSFW_SET_DESIGNS.keys()), + {"default": "None"}, + ), + "heat_profile": ( + list(NSFW_HEAT_LEVELS.keys()), + {"default": "Sensual"}, + ), + "custom_pose_prompt": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_pose_notes": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_wardrobe_prompt": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_wardrobe_notes": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_tone_prompt": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_tone_notes": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_lighting_prompt": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_lighting_notes": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_camera_prompt": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_camera_notes": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_set_prompt": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_set_notes": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_explicitness_prompt": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_explicitness_negative": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_explicitness_notes": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_heat_prompt": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_heat_notes": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "custom_heat_negative": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "detail_accent": ( + "STRING", + { + "multiline": True, + "default": "soft blemish-free skin, natural curves, respectful staging", + }, + ), + "nsfw_tags": ("STRING", {"multiline": True, "default": "tasteful, mature, confident"}), + "negative_prompt": ("STRING", {"multiline": True, "default": ""}), + "include_negative_baseline": ("BOOLEAN", {"default": True}), + "include_safety_note": ("BOOLEAN", {"default": True}), + "custom_safety_note": ( + "STRING", + {"multiline": True, "default": ""}, + ), + "preset_action": (["none", "save", "load", "list"], {"default": "none"}), + "preset_name": ("STRING", {"default": ""}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ("prompt", "negative_prompt", "safety_notes", "preset_status") + FUNCTION = "design_prompt" + CATEGORY = "NoxPrompter/NSFW" + + def design_prompt( + self, + subject_focus, + scene_setting, + pose_profile, + wardrobe_style, + tone_profile, + lighting_setup="Candlelit Warmth", + camera_framing="Intimate Portrait", + explicitness_level="Implied Nude", + set_design="None", + heat_profile="Sensual", + detail_accent="", + nsfw_tags="", + negative_prompt="", + include_negative_baseline=True, + include_safety_note=True, + custom_pose_prompt="", + custom_pose_notes="", + custom_wardrobe_prompt="", + custom_wardrobe_notes="", + custom_tone_prompt="", + custom_tone_notes="", + custom_lighting_prompt="", + custom_lighting_notes="", + custom_camera_prompt="", + custom_camera_notes="", + custom_set_prompt="", + custom_set_notes="", + custom_explicitness_prompt="", + custom_explicitness_negative="", + custom_explicitness_notes="", + custom_safety_note="", + custom_heat_prompt="", + custom_heat_notes="", + custom_heat_negative="", + preset_action="none", + preset_name="", + ): + config = { + "subject_focus": subject_focus, + "scene_setting": scene_setting, + "pose_profile": pose_profile, + "wardrobe_style": wardrobe_style, + "tone_profile": tone_profile, + "lighting_setup": lighting_setup, + "camera_framing": camera_framing, + "explicitness_level": explicitness_level, + "set_design": set_design, + "heat_profile": heat_profile, + "detail_accent": detail_accent, + "nsfw_tags": nsfw_tags, + "negative_prompt": negative_prompt, + "include_negative_baseline": include_negative_baseline, + "include_safety_note": include_safety_note, + "custom_pose_prompt": custom_pose_prompt, + "custom_pose_notes": custom_pose_notes, + "custom_wardrobe_prompt": custom_wardrobe_prompt, + "custom_wardrobe_notes": custom_wardrobe_notes, + "custom_tone_prompt": custom_tone_prompt, + "custom_tone_notes": custom_tone_notes, + "custom_lighting_prompt": custom_lighting_prompt, + "custom_lighting_notes": custom_lighting_notes, + "custom_camera_prompt": custom_camera_prompt, + "custom_camera_notes": custom_camera_notes, + "custom_set_prompt": custom_set_prompt, + "custom_set_notes": custom_set_notes, + "custom_explicitness_prompt": custom_explicitness_prompt, + "custom_explicitness_negative": custom_explicitness_negative, + "custom_explicitness_notes": custom_explicitness_notes, + "custom_safety_note": custom_safety_note, + "custom_heat_prompt": custom_heat_prompt, + "custom_heat_notes": custom_heat_notes, + "custom_heat_negative": custom_heat_negative, + } + + config, preset_status = self._apply_preset_action( + "nsfw_designer", + preset_action, + preset_name, + config, + ) + + subject_focus = config.get("subject_focus", subject_focus) + scene_setting = config.get("scene_setting", scene_setting) + pose_profile = config.get("pose_profile", pose_profile) + wardrobe_style = config.get("wardrobe_style", wardrobe_style) + tone_profile = config.get("tone_profile", tone_profile) + lighting_setup = config.get("lighting_setup", lighting_setup) + camera_framing = config.get("camera_framing", camera_framing) + explicitness_level = config.get("explicitness_level", explicitness_level) + set_design = config.get("set_design", set_design) + heat_profile = config.get("heat_profile", heat_profile) + detail_accent = config.get("detail_accent", detail_accent) + nsfw_tags = config.get("nsfw_tags", nsfw_tags) + negative_prompt = config.get("negative_prompt", negative_prompt) + include_negative_baseline = bool(config.get("include_negative_baseline", include_negative_baseline)) + include_safety_note = bool(config.get("include_safety_note", include_safety_note)) + custom_pose_prompt = config.get("custom_pose_prompt", custom_pose_prompt) + custom_pose_notes = config.get("custom_pose_notes", custom_pose_notes) + custom_wardrobe_prompt = config.get("custom_wardrobe_prompt", custom_wardrobe_prompt) + custom_wardrobe_notes = config.get("custom_wardrobe_notes", custom_wardrobe_notes) + custom_tone_prompt = config.get("custom_tone_prompt", custom_tone_prompt) + custom_tone_notes = config.get("custom_tone_notes", custom_tone_notes) + custom_lighting_prompt = config.get("custom_lighting_prompt", custom_lighting_prompt) + custom_lighting_notes = config.get("custom_lighting_notes", custom_lighting_notes) + custom_camera_prompt = config.get("custom_camera_prompt", custom_camera_prompt) + custom_camera_notes = config.get("custom_camera_notes", custom_camera_notes) + custom_set_prompt = config.get("custom_set_prompt", custom_set_prompt) + custom_set_notes = config.get("custom_set_notes", custom_set_notes) + custom_explicitness_prompt = config.get("custom_explicitness_prompt", custom_explicitness_prompt) + custom_explicitness_negative = config.get("custom_explicitness_negative", custom_explicitness_negative) + custom_explicitness_notes = config.get("custom_explicitness_notes", custom_explicitness_notes) + custom_safety_note = config.get("custom_safety_note", custom_safety_note) + custom_heat_prompt = config.get("custom_heat_prompt", custom_heat_prompt) + custom_heat_notes = config.get("custom_heat_notes", custom_heat_notes) + custom_heat_negative = config.get("custom_heat_negative", custom_heat_negative) + + pose_cfg = self._resolve_nsfw_option( + pose_profile, + NSFW_POSE_PROFILES, + custom_prompt=custom_pose_prompt, + custom_notes=custom_pose_notes, + ) + wardrobe_cfg = self._resolve_nsfw_option( + wardrobe_style, + NSFW_WARDROBE_STYLES, + custom_prompt=custom_wardrobe_prompt, + custom_notes=custom_wardrobe_notes, + ) + tone_cfg = self._resolve_nsfw_option( + tone_profile, + NSFW_TONE_MOODS, + custom_prompt=custom_tone_prompt, + custom_notes=custom_tone_notes, + ) + lighting_cfg = self._resolve_nsfw_option( + lighting_setup, + NSFW_LIGHTING_SETUPS, + custom_prompt=custom_lighting_prompt, + custom_notes=custom_lighting_notes, + ) + camera_cfg = self._resolve_nsfw_option( + camera_framing, + NSFW_CAMERA_FRAMING, + custom_prompt=custom_camera_prompt, + custom_notes=custom_camera_notes, + ) + explicit_cfg = self._resolve_nsfw_option( + explicitness_level, + NSFW_EXPLICITNESS_LEVELS, + custom_prompt=custom_explicitness_prompt, + custom_notes=custom_explicitness_notes, + custom_negative=custom_explicitness_negative, + ) + set_cfg = self._resolve_nsfw_option( + set_design, + NSFW_SET_DESIGNS, + custom_prompt=custom_set_prompt, + custom_notes=custom_set_notes, + ) + heat_cfg = self._resolve_nsfw_option( + heat_profile, + NSFW_HEAT_LEVELS, + custom_prompt=custom_heat_prompt, + custom_notes=custom_heat_notes, + custom_negative=custom_heat_negative, + ) + + fragments = [] + + subject_clause_parts = [subject_focus.strip()] + if tone_cfg.get("prompt"): + subject_clause_parts.append(tone_cfg["prompt"]) + if explicit_cfg.get("prompt"): + subject_clause_parts.append(explicit_cfg["prompt"]) + if heat_cfg.get("prompt"): + subject_clause_parts.append(heat_cfg["prompt"]) + subject_clause = ", ".join(part for part in subject_clause_parts if part) + if subject_clause: + fragments.append(subject_clause) + + scene_fragments = [] + if scene_setting.strip(): + scene_fragments.append(scene_setting.strip()) + set_prompt_text = set_cfg.get("prompt") + if set_prompt_text: + scene_fragments.append(set_prompt_text) + if scene_fragments: + fragments.append(". ".join(scene_fragments)) + + descriptive_chunks = [ + pose_cfg.get("prompt", ""), + wardrobe_cfg.get("prompt", ""), + lighting_cfg.get("prompt", ""), + camera_cfg.get("prompt", ""), + ] + if detail_accent.strip(): + descriptive_chunks.append(detail_accent.strip()) + descriptive_line = ", ".join(chunk for chunk in descriptive_chunks if chunk) + if descriptive_line: + fragments.append(descriptive_line) + + tag_list = self._split_keywords(nsfw_tags) + if tag_list: + fragments.append(f"Keywords: {', '.join(tag_list)}") + + prompt_text = self._assemble_sentences(fragments) + + negative_terms = [] + if include_negative_baseline: + negative_terms.extend(NSFW_BASELINE_NEGATIVES) + explicit_negative = self._split_keywords(explicit_cfg.get("negative", "")) + negative_terms.extend(explicit_negative) + heat_negative = self._split_keywords(heat_cfg.get("negative", "")) + negative_terms.extend(heat_negative) + negative_terms.extend(self._split_keywords(negative_prompt)) + negative_terms = list(dict.fromkeys(term.strip() for term in negative_terms if term.strip())) + negative_text = ", ".join(negative_terms) + + safety_parts = [] + if include_safety_note: + safety_parts.append(NSFW_DEFAULT_SAFETY_NOTE) + notes_sources = [ + pose_cfg.get("notes"), + wardrobe_cfg.get("notes"), + tone_cfg.get("notes"), + lighting_cfg.get("notes"), + camera_cfg.get("notes"), + explicit_cfg.get("notes"), + set_cfg.get("notes"), + heat_cfg.get("notes"), + ] + curated_notes = [note for note in notes_sources if note] + if curated_notes: + safety_parts.extend(list(dict.fromkeys(curated_notes))) + if custom_safety_note.strip(): + safety_parts.append(custom_safety_note.strip()) + safety_notes = " | ".join(part for part in safety_parts if part) + + return (prompt_text, negative_text, safety_notes, preset_status) + + def _assemble_sentences(self, fragments): + sentences = [] + for fragment in fragments: + segment = fragment.strip() + if not segment: + continue + if segment[-1] not in ".!?": + segment = f"{segment}." + sentences.append(segment) + return " ".join(sentences) + + def _split_keywords(self, text): + if not text: + return [] + separators = [",", "\n", ";"] + tokens = [text] + for sep in separators: + expanded = [] + for token in tokens: + expanded.extend(token.split(sep)) + tokens = expanded + return [token.strip() for token in tokens if token.strip()] + + def _resolve_nsfw_option( + self, + selection, + options, + *, + custom_prompt="", + custom_notes="", + custom_negative=None, + ): + key = self._match_option_key(selection, options) + fallback_key = "None" if "None" in options else next(iter(options)) + source_key = key or fallback_key + base = dict(options.get(source_key, {})) + + prompt_parts = [part.strip() for part in [base.get("prompt", ""), custom_prompt] if part and part.strip()] + base["prompt"] = ", ".join(dict.fromkeys(prompt_parts)) if prompt_parts else "" + + notes_parts = [part.strip() for part in [base.get("notes", ""), custom_notes] if part and part.strip()] + if notes_parts: + base["notes"] = " | ".join(dict.fromkeys(notes_parts)) + elif "notes" in base: + base["notes"] = base.get("notes", "") + + if custom_negative is not None: + negative_parts = [part.strip() for part in [base.get("negative", ""), custom_negative] if part and part.strip()] + base["negative"] = ", ".join(dict.fromkeys(negative_parts)) if negative_parts else "" + + return base + + def _match_option_key(self, selection, options): + normalized = (selection or "").strip() + if not normalized: + return None + lookup = normalized.lower() + for key in options.keys(): + if key.lower() == lookup: + return key + return None + +__all__ = ["NoxPromptNSFWDesigner"] diff --git a/noxprompter/nodes/nox_prompt_palette_mixer.py b/noxprompter/nodes/nox_prompt_palette_mixer.py new file mode 100644 index 0000000..a381328 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_palette_mixer.py @@ -0,0 +1,117 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Tuple +from ..common import PresetMixin +from ..constants import * # noqa: F403 + +class NoxPromptPaletteMixer(PresetMixin): + """Generate palette overrides and keyword clusters for the builder.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "mood_profile": (list(PALETTE_PROFILES.keys()), {"default": "Moody Nightfall"}), + "intensity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.05}), + }, + "optional": { + "include_effects": ("BOOLEAN", {"default": True}), + "custom_palette": ("STRING", {"multiline": True, "default": ""}), + "preset_action": (["none", "save", "load", "list"], {"default": "none"}), + "preset_name": ("STRING", {"default": ""}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ("palette_overrides", "custom_keywords", "notes", "preset_status") + FUNCTION = "mix_palette" + CATEGORY = "NoxPrompter/Companions" + + def mix_palette( + self, + mood_profile, + intensity, + include_effects=True, + custom_palette="", + preset_action="none", + preset_name="", + ): + config = { + "mood_profile": mood_profile, + "intensity": intensity, + "include_effects": include_effects, + "custom_palette": custom_palette, + } + + config, preset_status = self._apply_preset_action( + "palette_mixer", + preset_action, + preset_name, + config, + ) + + mood_profile = config.get("mood_profile", "Moody Nightfall") + intensity = float(config.get("intensity", 0.5)) + include_effects = bool(config.get("include_effects", True)) + custom_palette = config.get("custom_palette", "") + + profile = PALETTE_PROFILES.get(mood_profile) + if not profile: + overrides_text = custom_palette.strip() + custom_keywords = ", ".join(self._split_keywords(custom_palette)) + notes = "Custom palette applied. Profile not found." + return (overrides_text, custom_keywords, notes, preset_status) + + overrides = {key: list(values) for key, values in profile.get("overrides", {}).items()} + + if intensity >= 0.6: + for key, values in profile.get("intense_overrides", {}).items(): + overrides.setdefault(key, []).extend(values) + + if include_effects and profile.get("effects"): + overrides.setdefault("special_effect", []).extend(profile["effects"]) + + override_lines = [] + for key, values in overrides.items(): + deduped = list(dict.fromkeys(value for value in values if value)) + if deduped: + override_lines.append(f"{key}: {', '.join(deduped)}") + + if custom_palette.strip(): + override_lines.append(custom_palette.strip()) + + overrides_text = "\n".join(override_lines).strip() + + keywords = list(profile.get("keywords", [])) + if intensity >= 0.6: + keywords.extend(profile.get("intense_keywords", [])) + if include_effects: + keywords.extend(profile.get("effect_keywords", [])) + if custom_palette.strip(): + keywords.extend(self._split_keywords(custom_palette)) + + keywords = list(dict.fromkeys(kw.strip() for kw in keywords if kw and kw.strip())) + custom_keywords = ", ".join(keywords) + + notes_parts = [profile.get("description", mood_profile)] + notes_parts.append(f"Intensity: {intensity:.2f}") + if include_effects and profile.get("effects"): + notes_parts.append("Special effects included: " + ", ".join(profile["effects"])) + if keywords: + notes_parts.append("Keywords curated for emphasis.") + + return (overrides_text, custom_keywords, " | ".join(notes_parts), preset_status) + + def _split_keywords(self, text): + if not text: + return [] + separators = [",", "\n", ";"] + tokens = [text] + for sep in separators: + temp = [] + for token in tokens: + temp.extend(token.split(sep)) + tokens = temp + return [token.strip() for token in tokens if token.strip()] + +__all__ = ["NoxPromptPaletteMixer"] diff --git a/noxprompter/nodes/nox_prompt_pipeline_combiner.py b/noxprompter/nodes/nox_prompt_pipeline_combiner.py new file mode 100644 index 0000000..bfe37b7 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_pipeline_combiner.py @@ -0,0 +1,392 @@ +from __future__ import annotations + +import re +from ..common import detect_prompt_contradictions + +class NoxPromptPipelineCombiner: + """Cascade the outputs from the Nox prompt designer suite into a master prompt.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "core_prompt": ("STRING", {"multiline": True, "default": ""}), + }, + "optional": { + "enhanced_prompt": ("STRING", {"multiline": True, "default": ""}), + "character_prompt": ("STRING", {"multiline": True, "default": ""}), + "human_prompt": ("STRING", {"multiline": True, "default": ""}), + "wardrobe_prompt": ("STRING", {"multiline": True, "default": ""}), + "action_prompt": ("STRING", {"multiline": True, "default": ""}), + "lighting_prompt": ("STRING", {"multiline": True, "default": ""}), + "nsfw_prompt": ("STRING", {"multiline": True, "default": ""}), + "narrative_subject": ("STRING", {"multiline": True, "default": ""}), + "narrative_scene": ("STRING", {"multiline": True, "default": ""}), + "narrative_motion": ("STRING", {"multiline": True, "default": ""}), + "narrative_hook": ("STRING", {"multiline": True, "default": ""}), + "palette_overrides": ("STRING", {"multiline": True, "default": ""}), + "custom_keywords": ("STRING", {"multiline": True, "default": ""}), + "extra_descriptors": ("STRING", {"multiline": True, "default": ""}), + "negative_prompt": ("STRING", {"multiline": True, "default": ""}), + "additional_negative": ("STRING", {"multiline": True, "default": ""}), + "safety_notes": ("STRING", {"multiline": True, "default": ""}), + "sequence_notes": ("STRING", {"multiline": True, "default": ""}), + "section_separator": ("STRING", {"default": "\n\n"}), + "content_separator": ("STRING", {"default": ", "}), + "include_labels": ("BOOLEAN", {"default": True}), + "builder_dynamic_notes": ("STRING", {"multiline": True, "default": ""}), + "action_notes": ("STRING", {"multiline": True, "default": ""}), + "lighting_notes_detail": ("STRING", {"multiline": True, "default": ""}), + "nsfw_action_notes": ("STRING", {"multiline": True, "default": ""}), + "camz_notes": ("STRING", {"multiline": True, "default": ""}), + "camera_overrides": ("STRING", {"multiline": True, "default": ""}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ("master_prompt", "negative_prompt", "pipeline_order", "reference_notes") + FUNCTION = "assemble_pipeline" + CATEGORY = "NoxPrompter/Workflow" + NOTE_SPLITTER = re.compile(r"[|\n]+") + + PIPELINE_SEQUENCE = [ + ("NoxPromptCharacterCreator → define subject identity", ["character_prompt"]), + ("NoxPromptHumanDesigner → confirm adult anatomy & safeguards", ["human_prompt"]), + ("NoxPromptWardrobeDesigner → lock wardrobe & accessories", ["wardrobe_prompt"]), + ("NoxPromptNarrativeWeaver → set subject, scene, motion, hook", ["narrative_subject", "narrative_scene", "narrative_motion", "narrative_hook"]), + ("NoxPromptActionDirector / NSFW Action Director → choreograph movement", ["action_prompt"]), + ("NoxPromptLightingMaster → capture lighting & atmosphere", ["lighting_prompt"]), + ("NoxPromptNSFWDesigner → apply sensual framing & safety", ["nsfw_prompt", "safety_notes"]), + ("NoxPromptPaletteMixer → merge palette overrides & keywords", ["palette_overrides", "custom_keywords"]), + ("NoxPromptBuilder → assemble the hero prompt", ["core_prompt", "negative_prompt"]), + ("NoxPromptEnhancer → polish for delivery", ["enhanced_prompt"]), + ("NoxPromptCombiner → layer alternate blends (optional)", []), + ("NoxPromptAnalyzer → quality check (optional)", []), + ] + + def assemble_pipeline( + self, + core_prompt, + enhanced_prompt="", + character_prompt="", + human_prompt="", + wardrobe_prompt="", + action_prompt="", + lighting_prompt="", + nsfw_prompt="", + narrative_subject="", + narrative_scene="", + narrative_motion="", + narrative_hook="", + palette_overrides="", + custom_keywords="", + extra_descriptors="", + negative_prompt="", + additional_negative="", + safety_notes="", + sequence_notes="", + section_separator="\n\n", + content_separator=", ", + include_labels=True, + builder_dynamic_notes="", + action_notes="", + lighting_notes_detail="", + nsfw_action_notes="", + camz_notes="", + camera_overrides="", + ): + palette_block = self._format_palette_overrides(palette_overrides) + keywords_block = self._format_keywords(custom_keywords) + narrative_block = self._build_narrative([narrative_subject, narrative_scene, narrative_motion, narrative_hook]) + + note_tokens = self._tokenize_notes( + builder_dynamic_notes, + action_notes, + nsfw_action_notes, + lighting_notes_detail, + camz_notes, + camera_overrides, + ) + camera_tokens, motion_tokens, lighting_tokens, safety_tokens, misc_tokens = self._partition_note_tokens(note_tokens) + + action_fragments = [] + if action_prompt.strip(): + action_fragments.append(action_prompt.strip()) + motion_clause = self._format_note_clause("Motion cues", motion_tokens, content_separator) + if motion_clause: + action_fragments.append(motion_clause) + + lighting_fragments = [] + if lighting_prompt.strip(): + lighting_fragments.append(lighting_prompt.strip()) + lighting_clause = self._format_note_clause("Lighting cues", lighting_tokens, content_separator) + if lighting_clause: + lighting_fragments.append(lighting_clause) + + camera_fragments = [] + camera_clause = self._format_note_clause("", camera_tokens, content_separator) + if camera_clause: + camera_fragments.append(camera_clause) + + extra_fragments = [] + if extra_descriptors.strip(): + extra_fragments.append(extra_descriptors.strip()) + misc_clause = self._format_note_clause("", misc_tokens, content_separator) + if misc_clause: + extra_fragments.append(misc_clause) + + safety_fragments = [] + if safety_notes.strip(): + safety_fragments.append(safety_notes.strip()) + safety_clause = self._format_note_clause("", safety_tokens, content_separator) + if safety_clause: + safety_fragments.append(safety_clause) + + positive_sections = [] + + layout = [ + ("Character & Identity", [character_prompt, human_prompt]), + ("Narrative Context", [narrative_block]), + ("Wardrobe & Styling", [wardrobe_prompt]), + ("Action & Motion", action_fragments), + ] + if camera_fragments: + layout.append(("Camera Direction", camera_fragments)) + layout.extend([ + ("Lighting & Atmosphere", lighting_fragments), + ("Sensual Spotlight", [nsfw_prompt]), + ("Palette & Keywords", [palette_block, keywords_block]), + ("Extra Descriptors", extra_fragments), + ("Safety & Compliance", safety_fragments), + ("Core Prompt", [core_prompt]), + ("Enhancement Overlay", [enhanced_prompt]), + ]) + + raw_fragments_for_consistency = [] + + for label, fragments in layout: + cleaned = [fragment.strip() for fragment in (fragments or []) if fragment and fragment.strip()] + if not cleaned: + continue + section_text = content_separator.join(cleaned) + if include_labels: + section_text = f"{label}: {section_text}" + positive_sections.append(section_text) + raw_fragments_for_consistency.extend(cleaned) + + master_prompt = section_separator.join(positive_sections).strip() + + negative_terms = self._collect_terms([negative_prompt, additional_negative]) + aggregated_negative = ", ".join(negative_terms) + + pipeline_lines = [] + context = { + "core_prompt": core_prompt, + "enhanced_prompt": enhanced_prompt, + "character_prompt": character_prompt, + "human_prompt": human_prompt, + "wardrobe_prompt": wardrobe_prompt, + "action_prompt": action_prompt, + "lighting_prompt": lighting_prompt, + "nsfw_prompt": nsfw_prompt, + "narrative_subject": narrative_subject, + "narrative_scene": narrative_scene, + "narrative_motion": narrative_motion, + "narrative_hook": narrative_hook, + "palette_overrides": palette_overrides, + "custom_keywords": custom_keywords, + "negative_prompt": negative_prompt, + "safety_notes": safety_notes, + } + + for index, (description, keys) in enumerate(self.PIPELINE_SEQUENCE, start=1): + provided = any(context.get(key, "").strip() for key in keys) if keys else False + status = "✓" if provided else "○" + pipeline_lines.append(f"{index}. {status} {description}") + + if sequence_notes.strip(): + pipeline_lines.append(f"Notes: {sequence_notes.strip()}") + + pipeline_overview = "\n".join(pipeline_lines) + + # Consistency analysis across assembled fragments + consistency_warnings = detect_prompt_contradictions( + raw_fragments_for_consistency + + [ + palette_overrides, + custom_keywords, + extra_descriptors, + safety_notes, + narrative_subject, + narrative_scene, + narrative_motion, + narrative_hook, + ] + ) + + reference_parts = [] + safety_reference = self._merge_lines(safety_fragments, " | ") + if safety_reference: + reference_parts.append(f"Safety: {safety_reference}") + extra_palette_notes = self._collect_reference_palette(palette_overrides) + if extra_palette_notes: + reference_parts.append(extra_palette_notes) + camera_reference = self._format_note_clause("", camera_tokens, ", ") + if camera_reference: + reference_parts.append(f"Camera: {camera_reference}") + motion_reference = self._format_note_clause("", motion_tokens, ", ") + if motion_reference: + reference_parts.append(f"Motion: {motion_reference}") + lighting_reference = self._format_note_clause("", lighting_tokens, ", ") + if lighting_reference: + reference_parts.append(f"Lighting: {lighting_reference}") + descriptors_reference = self._merge_lines([ + extra_descriptors.strip(), + self._format_note_clause("", misc_tokens, ", ") + ], " | ") + if descriptors_reference: + reference_parts.append(f"Descriptors: {descriptors_reference}") + if consistency_warnings: + reference_parts.append("Consistency: " + " | ".join(consistency_warnings)) + + reference_notes = "\n".join(reference_parts) + + return ( + master_prompt, + aggregated_negative, + pipeline_overview, + reference_notes, + ) + + @classmethod + def _tokenize_notes(cls, *chunks): + tokens = [] + for chunk in chunks: + if not chunk: + continue + for piece in cls.NOTE_SPLITTER.split(chunk): + stripped = piece.strip() + if not stripped: + continue + cleaned = stripped.lstrip("-•").strip() + if cleaned: + tokens.append(cleaned) + return tokens + + @staticmethod + def _partition_note_tokens(tokens): + camera_keywords = ("camera", "lens", "orbit", "gimbal", "steadicam", "pov", "drone", "rig", "crane", "dolly") + motion_keywords = ("motion", "tempo", "pace", "sprint", "movement", "momentum", "flow", "beat", "action", "hover") + lighting_keywords = ("light", "lighting", "illumination", "glow", "shadow", "highlight", "exposure", "bloom") + safety_keywords = ("safety", "consent", "risk", "protocol", "adult", "verify", "compliance", "check-in") + + camera_notes = [] + motion_notes = [] + lighting_notes = [] + safety_notes = [] + misc_notes = [] + + seen = set() + for token in tokens: + normalized = token.strip() + if not normalized: + continue + key = normalized.lower() + if key in seen: + continue + seen.add(key) + if any(keyword in key for keyword in camera_keywords): + camera_notes.append(normalized) + elif any(keyword in key for keyword in motion_keywords): + motion_notes.append(normalized) + elif any(keyword in key for keyword in lighting_keywords): + lighting_notes.append(normalized) + elif any(keyword in key for keyword in safety_keywords): + safety_notes.append(normalized) + else: + misc_notes.append(normalized) + + return camera_notes, motion_notes, lighting_notes, safety_notes, misc_notes + + @staticmethod + def _format_note_clause(label, tokens, separator): + unique = [] + seen = set() + for token in tokens: + cleaned = token.strip() + if not cleaned: + continue + key = cleaned.lower() + if key in seen: + continue + seen.add(key) + unique.append(cleaned) + if not unique: + return "" + body = separator.join(unique) + return f"{label}: {body}" if label else body + + @staticmethod + def _merge_lines(fragments, separator): + unique = [] + seen = set() + for fragment in fragments: + cleaned = (fragment or "").strip() + if not cleaned: + continue + key = cleaned.lower() + if key in seen: + continue + seen.add(key) + unique.append(cleaned) + return separator.join(unique) + + @staticmethod + def _format_palette_overrides(palette_overrides): + content = [line.strip() for line in palette_overrides.replace("\r", "").split("\n") if line.strip()] + return "; ".join(content) + + @staticmethod + def _collect_reference_palette(palette_overrides): + content = [line.strip() for line in palette_overrides.replace("\r", "").split("\n") if line.strip()] + if not content: + return "" + return "Palette: " + "; ".join(content) + + @staticmethod + def _format_keywords(custom_keywords): + tokens = NoxPromptPipelineCombiner._collect_terms([custom_keywords]) + return ", ".join(tokens) + + @staticmethod + def _build_narrative(chunks): + sentences = [] + for chunk in chunks: + cleaned = (chunk or "").strip() + if not cleaned: + continue + if cleaned[-1] not in ".!?": + cleaned = f"{cleaned}." + sentences.append(cleaned) + return " ".join(sentences) + + @staticmethod + def _collect_terms(sections): + tokens = [] + for section in sections: + text = (section or "").replace("\r", "\n") + for part in text.split("\n"): + for token in part.split(","): + normalized = token.strip() + if normalized: + tokens.append(normalized) + seen = set() + ordered = [] + for token in tokens: + if token.lower() in seen: + continue + seen.add(token.lower()) + ordered.append(token) + return ordered + +__all__ = ["NoxPromptPipelineCombiner"] diff --git a/noxprompter/nodes/nox_prompt_prompt_organizer.py b/noxprompter/nodes/nox_prompt_prompt_organizer.py new file mode 100644 index 0000000..6afb406 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_prompt_organizer.py @@ -0,0 +1,129 @@ +from __future__ import annotations + +from typing import Dict, List + + +class NoxPromptOrganizer: + """Collect prompt fragments and present them in a consistent, readable order.""" + + CATEGORY = "NoxPrompter/Utility" + FUNCTION = "organize" + RETURN_TYPES = ("STRING", "STRING", "STRING") + RETURN_NAMES = ("organized_prompt", "missing_sections", "section_overview") + + _STRUCTURE_MODES: Dict[str, List[str]] = { + "Builder-first": [ + "Core Prompt", + "Character", + "Wardrobe", + "Action", + "Lighting", + "Camera", + "Palette", + "Extra", + ], + "Narrative-first": [ + "Narrative", + "Character", + "Action", + "Wardrobe", + "Lighting", + "Camera", + "Palette", + "Core Prompt", + "Extra", + ], + "Lighting-first": [ + "Lighting", + "Camera", + "Action", + "Character", + "Wardrobe", + "Core Prompt", + "Palette", + "Extra", + ], + } + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "core_prompt": ("STRING", {"multiline": True, "default": ""}), + }, + "optional": { + "character_prompt": ("STRING", {"multiline": True, "default": ""}), + "narrative_notes": ("STRING", {"multiline": True, "default": ""}), + "wardrobe_prompt": ("STRING", {"multiline": True, "default": ""}), + "action_prompt": ("STRING", {"multiline": True, "default": ""}), + "lighting_prompt": ("STRING", {"multiline": True, "default": ""}), + "camera_notes": ("STRING", {"multiline": True, "default": ""}), + "palette_overrides": ("STRING", {"multiline": True, "default": ""}), + "extra_descriptors": ("STRING", {"multiline": True, "default": ""}), + "structure_mode": (list(cls._STRUCTURE_MODES.keys()), {"default": "Builder-first"}), + "include_missing_list": ("BOOLEAN", {"default": True}), + }, + } + + def organize( + self, + core_prompt: str, + character_prompt: str = "", + narrative_notes: str = "", + wardrobe_prompt: str = "", + action_prompt: str = "", + lighting_prompt: str = "", + camera_notes: str = "", + palette_overrides: str = "", + extra_descriptors: str = "", + structure_mode: str = "Builder-first", + include_missing_list: bool = True, + ): + structure = self._STRUCTURE_MODES.get(structure_mode, self._STRUCTURE_MODES["Builder-first"]) + + section_values: Dict[str, str] = { + "Core Prompt": core_prompt, + "Narrative": narrative_notes, + "Character": character_prompt, + "Wardrobe": wardrobe_prompt, + "Action": action_prompt, + "Lighting": lighting_prompt, + "Camera": camera_notes, + "Palette": palette_overrides, + "Extra": extra_descriptors, + } + + ordered_lines: List[str] = [] + missing: List[str] = [] + overview: List[str] = [] + + for section in structure: + content = section_values.get(section, "").strip() + if content: + ordered_lines.append(f"{section}: {content}") + overview.append(f"{section} → included") + else: + overview.append(f"{section} → missing") + missing.append(section) + + # Append any additional sections that were populated but not present in the structure definition + for section, value in section_values.items(): + if section in structure: + continue + content = value.strip() + if content: + ordered_lines.append(f"{section}: {content}") + overview.append(f"{section} → included (appended)") + + organized_prompt = "\n".join(ordered_lines).strip() + + missing_sections = "" + if include_missing_list and missing: + missing_sections = ", ".join(missing) + + section_overview = "\n".join(overview).strip() + + return organized_prompt, missing_sections, section_overview + + +__all__ = ["NoxPromptOrganizer"] diff --git a/noxprompter/nodes/nox_prompt_usage_guide.py b/noxprompter/nodes/nox_prompt_usage_guide.py new file mode 100644 index 0000000..f48cf53 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_usage_guide.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +from textwrap import dedent + +from .nox_prompt_pipeline_combiner import NoxPromptPipelineCombiner + + +def _format_section(title: str, lines: list[str]) -> str: + if not lines: + return "" + header = f"{title}\n{'-' * len(title)}" + body = "\n".join(lines) + return f"{header}\n{body}" + + +class NoxPromptUsageGuide: + """Provide an in-canvas reference explaining how to work with the suite.""" + + CATEGORY = "NoxPrompter/Guides" + FUNCTION = "build_guide" + RETURN_TYPES = ("STRING", "STRING", "STRING") + RETURN_NAMES = ("guide_text", "pipeline_overview", "safety_highlights") + + _DETAIL_BUNDLES = { + "Quickstart": [ + "1. Use **NoxPromptBuilder** to assemble the base cinematic prompt.", + "2. Layer optional specialists (Palette Mixer, Narrative Weaver, Lighting Master) to enrich the builder inputs.", + "3. Run **NoxPromptEnhancer** for quick polish and quality tags.", + "4. Feed everything into **NoxPromptPipelineCombiner** to generate the master prompt and negative block.", + ], + "Extended": [ + "• Start with the Builder, but pre-seed Character Creator, Human Designer, and Wardrobe Designer to lock identity and styling.", + "• Action Director + Lighting Master define motion and atmosphere before you finalize palette overrides.", + "• Use Palette Mixer to create structured overrides that the Builder can ingest (copy the overrides string).", + "• Analyzer is ideal after Enhancer to review density, key phrases, and make iteration notes.", + ], + "Integration": [ + "• Register NODE_CLASSES from `NoxPromptNode` inside ComfyUI; all helpers are exposed there.", + "• Preset-capable nodes (Builder, Palette Mixer, Character/Human Designer, NSFW variants) save to `custom_nodes/NoxPrompter/presets`.", + "• Pipeline Combiner accepts raw strings, so you can splice third-party prompts or post-process with samplers easily.", + "• Each node sticks to standard ComfyUI conventions: `INPUT_TYPES` describe exposed widgets, outputs are plain strings ready for downstream wiring.", + ], + } + + _TIP_LINES = [ + "• Pair Character Creator → Human Designer → Wardrobe Designer for a unified hero bible.", + "• Palette Mixer overrides paste directly into the Builder's palette input block.", + "• Action Director + NSFW Action Director share option tables—swap between them with the same preset names.", + "• Lighting Master outputs both prompt text and short technical notes; send the notes into the Pipeline Combiner for reference cards.", + "• Analyzer helps spot repeated descriptors before the final sampler prompt.", + ] + + _SAFETY_LINES = [ + "• The NSFW Designer and NSFW Action Director automatically append baseline consent and age-negation phrases.", + "• Leave `include_safety_note` enabled in NSFW nodes unless you replace it with a studio policy alternative.", + "• Palette Mixer + Builder share override keys; keep them consistent so lighting or wardrobe selections do not fight each other.", + "• Final pass: run Pipeline Combiner and scan the Safety & Compliance section before triggering renders.", + ] + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "detail_level": (list(cls._DETAIL_BUNDLES.keys()), {"default": "Quickstart"}), + }, + "optional": { + "include_pipeline": ("BOOLEAN", {"default": True}), + "include_safety": ("BOOLEAN", {"default": True}), + "include_tips": ("BOOLEAN", {"default": True}), + }, + } + + @classmethod + def IS_CHANGED(cls, *_args, **_kwargs): # pragma: no cover - ComfyUI metadata hook + return True + + def build_guide( + self, + detail_level: str, + include_pipeline: bool = True, + include_safety: bool = True, + include_tips: bool = True, + ): + detail_key = detail_level if detail_level in self._DETAIL_BUNDLES else "Quickstart" + intro = _format_section( + "Nox Prompter Overview", + [ + "The custom node suite is modular: start from the builder, layer specialists," + " and finish with the Pipeline Combiner for a structured master prompt.", + ], + ) + + detail_section = _format_section( + f"{detail_key} Workflow", + self._DETAIL_BUNDLES.get(detail_key, []), + ) + + tip_section = "" + if include_tips: + tip_section = _format_section("Suggested Pairings", self._TIP_LINES) + + guide_parts = [segment for segment in (intro, detail_section, tip_section) if segment] + guide_text = "\n\n".join(guide_parts) + + pipeline_text = "" + if include_pipeline: + lines = [] + for index, (description, _keys) in enumerate(NoxPromptPipelineCombiner.PIPELINE_SEQUENCE, start=1): + token = f"{index}. {description}" + lines.append(token) + pipeline_text = dedent( + "\n".join([ + "Recommended Pipeline", + "--------------------", + *lines, + ]) + ).strip() + + safety_text = "" + if include_safety: + safety_text = _format_section("Safety & Consistency", self._SAFETY_LINES) + + return (guide_text, pipeline_text, safety_text) + + +__all__ = ["NoxPromptUsageGuide"] diff --git a/noxprompter/nodes/nox_prompt_wardrobe_designer.py b/noxprompter/nodes/nox_prompt_wardrobe_designer.py new file mode 100644 index 0000000..69233a9 --- /dev/null +++ b/noxprompter/nodes/nox_prompt_wardrobe_designer.py @@ -0,0 +1,217 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Tuple +from ..common import PresetMixin, _resolve_with_custom, _split_tokens, _format_notes +from ..constants import * # noqa: F403 + +class NoxPromptWardrobeDesigner(PresetMixin): + """Generate advanced wardrobe breakdowns with styling guidance.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "base_garment": (list(WARDROBE_BASE_GARMENT_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Battle Dress"}), + "silhouette": (list(WARDROBE_SILHOUETTE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "A-Line"}), + "colorway": (list(WARDROBE_COLORWAY_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Monochrome Obsidian"}), + }, + "optional": { + "layering": (list(WARDROBE_LAYER_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Armor Plating"}), + "fabric": (list(WARDROBE_FABRIC_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Ballistic Weave"}), + "pattern": (list(WARDROBE_PATTERN_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Fractal"}), + "wear_state": (list(WARDROBE_WEAR_STATE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Battle-Worn"}), + "culture_inspiration": (list(WARDROBE_CULTURE_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Solar Court"}), + "footwear": (list(WARDROBE_FOOTWEAR_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Combat Boots"}), + "primary_accessory": (list(WARDROBE_ACCESSORY_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Relic Pendant"}), + "secondary_accessory": (list(WARDROBE_ACCESSORY_OPTIONS.keys()) + [CUSTOM_OPTION], {"default": "Utility Belt"}), + "functional_focus": ("STRING", {"default": ""}), + "accent_materials": ("STRING", {"multiline": True, "default": ""}), + "motion_considerations": ("STRING", {"multiline": True, "default": ""}), + "finishing_details": ("STRING", {"multiline": True, "default": ""}), + "additional_accessories": ("STRING", {"multiline": True, "default": ""}), + "base_garment_custom": ("STRING", {"default": ""}), + "silhouette_custom": ("STRING", {"default": ""}), + "colorway_custom": ("STRING", {"default": ""}), + "layering_custom": ("STRING", {"default": ""}), + "fabric_custom": ("STRING", {"default": ""}), + "pattern_custom": ("STRING", {"default": ""}), + "wear_state_custom": ("STRING", {"default": ""}), + "culture_inspiration_custom": ("STRING", {"default": ""}), + "footwear_custom": ("STRING", {"default": ""}), + "primary_accessory_custom": ("STRING", {"default": ""}), + "secondary_accessory_custom": ("STRING", {"default": ""}), + "preset_action": (["none", "save", "load", "list"], {"default": "none"}), + "preset_name": ("STRING", {"default": ""}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ("wardrobe_prompt", "styling_notes", "accessory_list", "preset_status") + FUNCTION = "design" + CATEGORY = "NoxPrompter/Wardrobe" + + def design( + self, + base_garment, + silhouette, + colorway, + layering="Armor Plating", + fabric="Ballistic Weave", + pattern="Fractal", + wear_state="Battle-Worn", + culture_inspiration="Solar Court", + footwear="Combat Boots", + primary_accessory="Relic Pendant", + secondary_accessory="Utility Belt", + functional_focus="", + accent_materials="", + motion_considerations="", + finishing_details="", + additional_accessories="", + base_garment_custom="", + silhouette_custom="", + colorway_custom="", + layering_custom="", + fabric_custom="", + pattern_custom="", + wear_state_custom="", + culture_inspiration_custom="", + footwear_custom="", + primary_accessory_custom="", + secondary_accessory_custom="", + preset_action="none", + preset_name="", + ): + config = { + "base_garment": base_garment, + "silhouette": silhouette, + "colorway": colorway, + "layering": layering, + "fabric": fabric, + "pattern": pattern, + "wear_state": wear_state, + "culture_inspiration": culture_inspiration, + "footwear": footwear, + "primary_accessory": primary_accessory, + "secondary_accessory": secondary_accessory, + "functional_focus": functional_focus, + "accent_materials": accent_materials, + "motion_considerations": motion_considerations, + "finishing_details": finishing_details, + "additional_accessories": additional_accessories, + "base_garment_custom": base_garment_custom, + "silhouette_custom": silhouette_custom, + "colorway_custom": colorway_custom, + "layering_custom": layering_custom, + "fabric_custom": fabric_custom, + "pattern_custom": pattern_custom, + "wear_state_custom": wear_state_custom, + "culture_inspiration_custom": culture_inspiration_custom, + "footwear_custom": footwear_custom, + "primary_accessory_custom": primary_accessory_custom, + "secondary_accessory_custom": secondary_accessory_custom, + } + + config, preset_status = self._apply_preset_action( + "wardrobe_designer", + preset_action, + preset_name, + config, + ) + + base_garment = config.get("base_garment", base_garment) + silhouette = config.get("silhouette", silhouette) + colorway = config.get("colorway", colorway) + layering = config.get("layering", layering) + fabric = config.get("fabric", fabric) + pattern = config.get("pattern", pattern) + wear_state = config.get("wear_state", wear_state) + culture_inspiration = config.get("culture_inspiration", culture_inspiration) + footwear = config.get("footwear", footwear) + primary_accessory = config.get("primary_accessory", primary_accessory) + secondary_accessory = config.get("secondary_accessory", secondary_accessory) + functional_focus = config.get("functional_focus", functional_focus) + accent_materials = config.get("accent_materials", accent_materials) + motion_considerations = config.get("motion_considerations", motion_considerations) + finishing_details = config.get("finishing_details", finishing_details) + additional_accessories = config.get("additional_accessories", additional_accessories) + base_garment_custom = config.get("base_garment_custom", base_garment_custom) + silhouette_custom = config.get("silhouette_custom", silhouette_custom) + colorway_custom = config.get("colorway_custom", colorway_custom) + layering_custom = config.get("layering_custom", layering_custom) + fabric_custom = config.get("fabric_custom", fabric_custom) + pattern_custom = config.get("pattern_custom", pattern_custom) + wear_state_custom = config.get("wear_state_custom", wear_state_custom) + culture_inspiration_custom = config.get("culture_inspiration_custom", culture_inspiration_custom) + footwear_custom = config.get("footwear_custom", footwear_custom) + primary_accessory_custom = config.get("primary_accessory_custom", primary_accessory_custom) + secondary_accessory_custom = config.get("secondary_accessory_custom", secondary_accessory_custom) + + base_prompt, base_notes = _resolve_with_custom(base_garment, base_garment_custom, WARDROBE_BASE_GARMENT_OPTIONS) + silhouette_prompt, silhouette_notes = _resolve_with_custom(silhouette, silhouette_custom, WARDROBE_SILHOUETTE_OPTIONS) + color_prompt, color_notes = _resolve_with_custom(colorway, colorway_custom, WARDROBE_COLORWAY_OPTIONS) + layering_prompt, layering_notes = _resolve_with_custom(layering, layering_custom, WARDROBE_LAYER_OPTIONS) + fabric_prompt, fabric_notes = _resolve_with_custom(fabric, fabric_custom, WARDROBE_FABRIC_OPTIONS) + pattern_prompt, pattern_notes = _resolve_with_custom(pattern, pattern_custom, WARDROBE_PATTERN_OPTIONS) + wear_prompt, wear_notes = _resolve_with_custom(wear_state, wear_state_custom, WARDROBE_WEAR_STATE_OPTIONS) + culture_prompt, culture_notes = _resolve_with_custom(culture_inspiration, culture_inspiration_custom, WARDROBE_CULTURE_OPTIONS) + footwear_prompt, footwear_notes = _resolve_with_custom(footwear, footwear_custom, WARDROBE_FOOTWEAR_OPTIONS) + primary_prompt, primary_notes = _resolve_with_custom(primary_accessory, primary_accessory_custom, WARDROBE_ACCESSORY_OPTIONS) + secondary_prompt, secondary_notes = _resolve_with_custom(secondary_accessory, secondary_accessory_custom, WARDROBE_ACCESSORY_OPTIONS) + + accent_list = _split_tokens(accent_materials) + motion_list = _split_tokens(motion_considerations) + finishing_list = _split_tokens(finishing_details) + extra_accessories = _split_tokens(additional_accessories) + + wardrobe_prompt_parts = [ + base_prompt, + silhouette_prompt, + fabric_prompt, + layering_prompt, + color_prompt, + pattern_prompt, + wear_prompt, + culture_prompt, + footwear_prompt, + ] + if accent_list: + wardrobe_prompt_parts.append("accents: " + ", ".join(accent_list)) + if finishing_list: + wardrobe_prompt_parts.append("finishing: " + ", ".join(finishing_list)) + + wardrobe_prompt = ", ".join(part for part in wardrobe_prompt_parts if part).strip() + if wardrobe_prompt and wardrobe_prompt[-1] not in ".!?": + wardrobe_prompt += "." + if functional_focus.strip(): + wardrobe_prompt += f" Functional focus: {functional_focus.strip()}." + if motion_list: + wardrobe_prompt += f" Movement considerations: {', '.join(motion_list)}." + + styling_segments = [ + _format_notes("Base", base_notes, base_prompt), + _format_notes("Silhouette", silhouette_notes, silhouette_prompt), + _format_notes("Fabric", fabric_notes, fabric_prompt), + _format_notes("Layering", layering_notes, layering_prompt), + _format_notes("Colorway", color_notes, color_prompt), + _format_notes("Pattern", pattern_notes, pattern_prompt), + _format_notes("Wear State", wear_notes, wear_prompt), + _format_notes("Culture", culture_notes, culture_prompt), + _format_notes("Footwear", footwear_notes, footwear_prompt), + ] + styling_notes = " | ".join(segment for segment in styling_segments if segment) + + accessory_items = [ + primary_prompt, + secondary_prompt if secondary_accessory else "", + ] + accessory_notes = [note for note in [primary_notes, secondary_notes] if note] + if extra_accessories: + accessory_items.append("Additional: " + ", ".join(extra_accessories)) + accessory_list = " | ".join(item for item in accessory_items if item) + if accessory_notes: + accessory_list += (" | Notes: " + "; ".join(accessory_notes)) if accessory_list else "Notes: " + "; ".join(accessory_notes) + + return (wardrobe_prompt, styling_notes, accessory_list, preset_status) + +__all__ = ["NoxPromptWardrobeDesigner"] diff --git a/noxprompter/presets/prompt_builder/unit_builder_profile.json b/noxprompter/presets/prompt_builder/unit_builder_profile.json new file mode 100644 index 0000000..6b67836 --- /dev/null +++ b/noxprompter/presets/prompt_builder/unit_builder_profile.json @@ -0,0 +1,35 @@ +{ + "formula": "Advanced Formula", + "subject_focus": "A curious farm girl hero", + "scene_setting": "Sunlit meadow with tall grass swaying in the breeze", + "motion_arc": "Dashes forward laughing, arms outstretched to touch the flowers", + "narrative_hook": "Camera orbits as she reaches toward the horizon", + "model_emphasis": "Precise semantic adherence, cinematic aesthetic control", + "light_source": "Daylight", + "light_quality": "Soft Light", + "time_of_day": "Daytime", + "shot_size": "Medium Close-up", + "composition": "Balanced", + "lens_focal": "Medium", + "lens_angle": "Low Angle", + "lens_type": "Single Shot", + "color_tone": "Warm Tone", + "motion_type": "Running", + "emotion": "Joy", + "camera_basic": "Pan Right", + "camera_advanced": "Following", + "visual_style": "Anime", + "special_effect": "Tilt-shift", + "keyword_style": "inline", + "quality_profile": "Balanced", + "randomize_missing": true, + "random_seed": 7, + "palette_overrides": "light_source: Moonlight\ncolor_tone: Cool Tone", + "extra_descriptors": "Shot on 85mm lens, 16fps preview", + "custom_keywords": "heroic energy, epic storytelling", + "negative_prompt": "blurry, low detail", + "prompt_prefix": "Wan 2.2 cinematic prompt", + "prompt_suffix": "Render ready for upscale", + "prompt_filter_enabled": true, + "prompt_filter_profile": "balanced" +} \ No newline at end of file