Skip to content

Implement <setmacro> and <macro> #851

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/Features/Prompt Syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,19 @@
- Here's a practical full example: `a photo of a woman with <setvar[color]:<random:blonde, black, red, blue, green, rainbow>> hair standing in the middle of a wide open street. She is smiling and waving at the camera, with beautiful sunlight glinting through her <var:color> hair. <segment:face and hair> extremely detailed close up shot of a woman with shiny <var:color> 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: `<setmacro[macro_name]:data>`
- For example: `<setmacro[color]:<random:red, blue, purple>>`
- Call back with the syntax: `<macro:macro_name>`
- For example: `in a room with <macro:color> walls, <macro:color> floors, and <macro:color> carpet`
- Unlike Variables, macros are not evaluated when being set, but instead are evaluated when used via `<macro:...>`
- Here's a full example: `Photo of a woman with <setmacro[color]:<random:red|white|green|blue|purple|orange|black|brown>> hair, <macro:color> shirt, <macro:color> pants`
- A separate random color will be chosen for hair, shirt, and pants.

## Trigger Phrase

![img](/docs/images/trigger-arcane-cat.jpg)
Expand Down
Binary file added docs/images/setmacro-cat.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/Text2Image/T2IParamInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
27 changes: 27 additions & 0 deletions src/Text2Image/T2IPromptHandling.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public class PromptTagContext

public Dictionary<string, string> Variables = [];

public Dictionary<string, string> Macros = [];

public int SectionID = 0;

public int Depth = 0;
Expand Down Expand Up @@ -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<string> phrases = [];
Expand Down
20 changes: 20 additions & 0 deletions src/wwwroot/js/genpage/gentab/prompttools.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<setmacro[color]:<random:red|blue|green>>", then use like "<macro:color>"', '\nMacro content will be re-evaluated each time it is used eg "<macro:color> hair, <macro:color> eyes" might produce "red hair, blue eyes', '\nReference macros later in the prompt with the <macro:> tag'];
});
this.registerPrefix('macro', 'Reference a previously saved macro', (prefix, prompt) => {
let prefixLow = prefix.toLowerCase();
let possible = [];
let matches = prompt.matchAll(/<setmacro\[(.*?)\]:/g);
if (matches) {
for (let match of matches) {
let varName = match.substring('<setmacro['.length, match.length - ']:'.length);
if (varName.toLowerCase().includes(prefixLow)) {
possible.push(varName);
}
}
}
if (possible.length == 0) {
return ['\nRecall a macro previously saved with <setmacro[name]:...>, use like "<macro:name>"','\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 "<segment:background>"'];
});
Expand Down