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]