diff --git a/docs/Features/Prompt Syntax.md b/docs/Features/Prompt Syntax.md index 3be452fc..d81903ae 100644 --- a/docs/Features/Prompt Syntax.md +++ b/docs/Features/Prompt Syntax.md @@ -70,6 +70,19 @@ - Here's a practical full example: `a photo of a woman with > hair standing in the middle of a wide open street. She is smiling and waving at the camera, with beautiful sunlight glinting through her hair. extremely detailed close up shot of a woman with shiny hair` - Notice how the var is called back, even in the segment, to allow for selecting a random hair color but keeping it consistent within the generation +## Macros + +![img](/docs/images/setmacro-cat.jpg) + +- Similar to variables, you can store and reuse chunks of prompt syntax as a macro. This is useful for dynamically repeating complicated randoms. + - Store with the syntax: `` + - For example: `>` + - Call back with the syntax: `` + - For example: `in a room with walls, floors, and carpet` + - Unlike Variables, macros are not evaluated when being set, but instead are evaluated when used via `` + - Here's a full example: `Photo of a woman with > hair, shirt, pants` + - A separate random color will be chosen for hair, shirt, and pants. + ## Trigger Phrase ![img](/docs/images/trigger-arcane-cat.jpg) diff --git a/docs/images/setmacro-cat.jpg b/docs/images/setmacro-cat.jpg new file mode 100644 index 00000000..0896b9e9 Binary files /dev/null and b/docs/images/setmacro-cat.jpg differ diff --git a/src/Text2Image/T2IParamInput.cs b/src/Text2Image/T2IParamInput.cs index b1acc9fd..0fa0531e 100644 --- a/src/Text2Image/T2IParamInput.cs +++ b/src/Text2Image/T2IParamInput.cs @@ -424,7 +424,7 @@ public void PreparsePromptLikes() { T2IPromptHandling.PromptTagContext posContext = new() { Input = this, Param = T2IParamTypes.Prompt.Type.ID }; InternalSet.ValuesInput["prompt"] = ProcessPromptLike(T2IParamTypes.Prompt, posContext); - T2IPromptHandling.PromptTagContext negContext = new() { Input = this, Param = T2IParamTypes.Prompt.Type.ID, Variables = posContext.Variables }; + T2IPromptHandling.PromptTagContext negContext = new() { Input = this, Param = T2IParamTypes.Prompt.Type.ID, Variables = posContext.Variables, Macros = posContext.Macros }; InternalSet.ValuesInput["negativeprompt"] = ProcessPromptLike(T2IParamTypes.NegativePrompt, negContext); } diff --git a/src/Text2Image/T2IPromptHandling.cs b/src/Text2Image/T2IPromptHandling.cs index e9ae1a9d..bf7d33ae 100644 --- a/src/Text2Image/T2IPromptHandling.cs +++ b/src/Text2Image/T2IPromptHandling.cs @@ -20,6 +20,8 @@ public class PromptTagContext public Dictionary Variables = []; + public Dictionary Macros = []; + public int SectionID = 0; public int Depth = 0; @@ -604,6 +606,31 @@ string autoConfine(string data, PromptTagContext context) return val; }; PromptTagLengthEstimators["var"] = estimateEmpty; + PromptTagProcessors["setmacro"] = (data, context) => + { + string name = context.PreData; + if (string.IsNullOrWhiteSpace(name)) + { + context.TrackWarning($"A macro name is required when using setmacro."); + return null; + } + context.Macros[name] = data; + return context.Parse(data); + }; + PromptTagLengthEstimators["setmacro"] = (data, context) => + { + return ProcessPromptLikeForLength(data); + }; + PromptTagProcessors["macro"] = (data, context) => + { + if (!context.Macros.TryGetValue(data, out string val)) + { + context.TrackWarning($"Macro '{data}' is not recognized."); + return ""; + } + return context.Parse(val); + }; + PromptTagLengthEstimators["macro"] = estimateEmpty; PromptTagBasicProcessors["trigger"] = (data, context) => { List phrases = []; diff --git a/src/wwwroot/js/genpage/gentab/prompttools.js b/src/wwwroot/js/genpage/gentab/prompttools.js index 24a8aa9a..861b6cbe 100644 --- a/src/wwwroot/js/genpage/gentab/prompttools.js +++ b/src/wwwroot/js/genpage/gentab/prompttools.js @@ -98,6 +98,26 @@ class PromptTabCompleteClass { } return possible; }); + this.registerPrefix('setmacro[macro_name]', 'Store raw text for reference later in the prompt', (prefix) => { + return ['\nSave the raw content of the tag into the named macro. eg ">", then use like ""', '\nMacro content will be re-evaluated each time it is used eg " hair, eyes" might produce "red hair, blue eyes', '\nReference macros later in the prompt with the tag']; + }); + this.registerPrefix('macro', 'Reference a previously saved macro', (prefix, prompt) => { + let prefixLow = prefix.toLowerCase(); + let possible = []; + let matches = prompt.matchAll(/, use like ""','\n"setmacro" must be used earlier in the prompt, then "macro" later']; + } + return possible; + }); this.registerPrefix('clear', 'Automatically clear part of the image to transparent (by CLIP segmentation matching) (iffy quality, prefer the Remove Background parameter over this)', (prefix) => { return ['\nSpecify before the ">" some text to match against in the image, like ""']; });