diff --git a/html/locale_en.json b/html/locale_en.json
index 8f2ac1e48..b269c3a9e 100644
--- a/html/locale_en.json
+++ b/html/locale_en.json
@@ -763,7 +763,8 @@
{"id":"","label":"Kanvas Settings","localized":"","hint":"","ui":"control"},
{"id":"","label":"Keep Thinking Trace","localized":"","hint":"Include the model's reasoning process in the final output.
Useful for understanding how the model arrived at its answer.
Only works with models that support thinking mode.","ui":"script_prompt_enhance"},
{"id":"","label":"Keep Prefill","localized":"","hint":"Include the prefill text at the beginning of the final output.
If disabled, the prefill text used to guide the model is removed from the result.","ui":"script_prompt_enhance"},
- {"id":"","label":"Keep aspect ratio","localized":"","hint":"","ui":"control"}
+ {"id":"","label":"Keep aspect ratio","localized":"","hint":"","ui":"control"},
+ {"id":"","label":"Keep @ on artist insert","localized":"","hint":"Type @ in the prompt to filter autocomplete to artist tags only.
This setting controls only what gets inserted on accept; the @ filter works for every model.
Enable for models that require the @ prefix in the prompt itself, e.g. Anima. Inserts as @artist name with underscores converted to spaces.
Disable for booru-trained models that take plain artist tags, e.g. SDXL, Pony, Illustrious, NoobAI. The typed @ is consumed and the artist name is inserted as a normal tag.","ui":"script_autocomplete"}
],
"l": [
{"id":"prompt_enhance_load","label":"Load model","localized":"","hint":"","ui":"script_prompt_enhance"},
diff --git a/javascript/autocomplete.js b/javascript/autocomplete.js
index 52c930ada..b1e59f99b 100644
--- a/javascript/autocomplete.js
+++ b/javascript/autocomplete.js
@@ -271,6 +271,7 @@ const engine = {
*
* Returns { word, start, end, mode } where:
* mode === 'tag': ordinary tag completion
+ * mode === 'artist': leading `@` trigger; results filtered to artist category and inserted with `@` preserved
* mode === 'lora': inside an unclosed ` 0 && after.trimStart().startsWith(',')) suffix = ' ';
- const insertion = `${prefix}${escapeParensForPrompt(tagName)}${suffix}`;
+ // Artist mode: optionally keep the `@` prefix (Anima syntax); always convert underscores to spaces
+ // since Anima requires space-separated artist names. The `@` is consumed for non-Anima models.
+ let body = tagName;
+ if (info.mode === 'artist') {
+ body = body.replace(/_/g, ' ');
+ if (window.opts?.autocomplete_at_prefix_artist) body = `@${body}`;
+ }
+ const insertion = `${prefix}${escapeParensForPrompt(body)}${suffix}`;
textarea.value = before.trimEnd() + (before.trimEnd().length > 0 ? ' ' : '') + insertion + after.trimStart();
// Position cursor after the inserted tag + separator
const cursorPos = before.trimEnd().length + (before.trimEnd().length > 0 ? 1 : 0) + insertion.length;
@@ -563,8 +578,10 @@ function onInput(textarea) {
dropdown.hide();
return;
}
- // Extra-network triggers have a zero threshold so ` t.category === ARTIST_CATEGORY_ID);
} else {
const tagResults = engine.searchAll(info.word);
const embedResults = window.autocompleteXn ? window.autocompleteXn.searchEmbeddings(info.word) : [];
diff --git a/scripts/autocomplete.py b/scripts/autocomplete.py
index fea760a08..531e26990 100644
--- a/scripts/autocomplete.py
+++ b/scripts/autocomplete.py
@@ -27,6 +27,7 @@ def get_config_json():
"autocomplete_min_chars": shared.opts.data.get('autocomplete_min_chars', 3),
"autocomplete_replace_underscores": shared.opts.data.get('autocomplete_replace_underscores', True),
"autocomplete_append_comma": shared.opts.data.get('autocomplete_append_comma', True),
+ "autocomplete_at_prefix_artist": shared.opts.data.get('autocomplete_at_prefix_artist', False),
})
@@ -60,6 +61,12 @@ def on_append_comma_change(value):
return get_config_json()
+def on_at_prefix_artist_change(value):
+ shared.opts.data['autocomplete_at_prefix_artist'] = bool(value)
+ shared.opts.save(silent=True)
+ return get_config_json()
+
+
def format_status(local, remote_entries, fetch_ok):
"""Build status HTML showing available dictionaries."""
lines = []
@@ -165,6 +172,11 @@ class AutocompleteScript(scripts_manager.Script):
value=shared.opts.data.get('autocomplete_append_comma', True),
elem_id=self.elem_id("append_comma"),
)
+ at_prefix_artist = gr.Checkbox(
+ label="Keep @ on artist insert",
+ value=shared.opts.data.get('autocomplete_at_prefix_artist', False),
+ elem_id=self.elem_id("at_prefix_artist"),
+ )
min_chars = gr.Slider(
label="Min characters",
minimum=2, maximum=6, step=1,
@@ -184,10 +196,11 @@ class AutocompleteScript(scripts_manager.Script):
min_chars.change(fn=on_min_chars_change, inputs=[min_chars], outputs=[config_json])
replace_underscores.change(fn=on_replace_underscores_change, inputs=[replace_underscores], outputs=[config_json])
append_comma.change(fn=on_append_comma_change, inputs=[append_comma], outputs=[config_json])
+ at_prefix_artist.change(fn=on_at_prefix_artist_change, inputs=[at_prefix_artist], outputs=[config_json])
refresh_btn.click(fn=on_refresh, inputs=[], outputs=[enabled_dd, status])
update_btn.click(fn=on_update, inputs=[enabled_dd], outputs=[status])
- for comp in [enabled_dd, min_chars, replace_underscores, append_comma, config_json, status]:
+ for comp in [enabled_dd, min_chars, replace_underscores, append_comma, at_prefix_artist, config_json, status]:
comp.do_not_save_to_config = True
- return [active_cb, enabled_dd, min_chars, replace_underscores, append_comma, config_json]
+ return [active_cb, enabled_dd, min_chars, replace_underscores, append_comma, at_prefix_artist, config_json]