From 3bf396d0032d93cf1c6a47b15622c91c24c5cd7f Mon Sep 17 00:00:00 2001 From: Will Miao Date: Wed, 13 May 2026 11:47:02 +0800 Subject: [PATCH] feat(recipes): add toggle to strip tags when copying prompt/negative_prompt Adds a compact inline toggle in the Generation Parameters section of the Recipe Modal that, when enabled, strips tags and cleans up residual punctuation before copying to clipboard. The setting persists across sessions via localStorage. --- static/css/components/recipe-modal.css | 48 ++++++++++++++++++++++--- static/js/components/RecipeModal.js | 50 ++++++++++++++++++++++++-- static/js/state/index.js | 1 + templates/components/recipe_modal.html | 11 +++++- 4 files changed, 102 insertions(+), 8 deletions(-) diff --git a/static/css/components/recipe-modal.css b/static/css/components/recipe-modal.css index 8d90f0e1..2be60cfd 100644 --- a/static/css/components/recipe-modal.css +++ b/static/css/components/recipe-modal.css @@ -396,14 +396,54 @@ flex-direction: column; } -.recipe-gen-params h3 { - margin-top: 0; +.gen-params-header-row { + display: flex; + align-items: center; + justify-content: space-between; margin-bottom: var(--space-2); - font-size: 1.2em; - color: var(--text-color); padding-bottom: var(--space-1); border-bottom: 1px solid var(--border-color); flex-shrink: 0; + gap: 8px; +} + +.gen-params-header-row h3 { + margin: 0; + font-size: 1.2em; + color: var(--text-color); +} + +/* Inline toggle for lora strip setting */ +.lora-strip-toggle { + flex-shrink: 0; + gap: 6px; +} + +.lora-strip-toggle .inline-toggle-label { + font-size: 0.78em; + white-space: nowrap; + opacity: 0.7; + transition: opacity 0.2s; +} + +.lora-strip-toggle:hover .inline-toggle-label { + opacity: 1; +} + +.lora-strip-toggle .toggle-switch { + width: 32px; + height: 16px; +} + +.lora-strip-toggle .toggle-slider:before { + height: 10px; + width: 10px; + left: 3px; + bottom: 3px; +} + +.lora-strip-toggle .toggle-switch input:checked + .toggle-slider:before { + transform: translateX(16px); } .gen-params-container { diff --git a/static/js/components/RecipeModal.js b/static/js/components/RecipeModal.js index f5209c2d..b461d236 100644 --- a/static/js/components/RecipeModal.js +++ b/static/js/components/RecipeModal.js @@ -2,7 +2,7 @@ import { showToast, copyToClipboard, sendLoraToWorkflow, sendModelPathToWorkflow, openCivitaiByMetadata } from '../utils/uiHelpers.js'; import { translate } from '../utils/i18nHelpers.js'; import { state } from '../state/index.js'; -import { setSessionItem, removeSessionItem } from '../utils/storageHelpers.js'; +import { setSessionItem, removeSessionItem, getStorageItem, setStorageItem } from '../utils/storageHelpers.js'; import { fetchRecipeDetails, updateRecipeMetadata } from '../api/recipeApi.js'; import { downloadManager } from '../managers/DownloadManager.js'; import { MODEL_TYPES } from '../api/apiConfig.js'; @@ -105,6 +105,7 @@ class RecipeModal { init() { this.setupCopyButtons(); + this.setupStripLoraToggle(); this.setupPromptEditors(); // Set up tooltip positioning handlers after DOM is ready document.addEventListener('DOMContentLoaded', () => { @@ -1350,14 +1351,20 @@ class RecipeModal { if (copyPromptBtn) { copyPromptBtn.addEventListener('click', () => { - const promptText = this.currentRecipe?.gen_params?.prompt || ''; + let promptText = this.currentRecipe?.gen_params?.prompt || ''; + if (this.shouldStripLoraOnCopy()) { + promptText = RecipeModal.stripLoraTags(promptText); + } this.copyToClipboard(promptText, 'Prompt copied to clipboard'); }); } if (copyNegativePromptBtn) { copyNegativePromptBtn.addEventListener('click', () => { - const negativePromptText = this.currentRecipe?.gen_params?.negative_prompt || ''; + let negativePromptText = this.currentRecipe?.gen_params?.negative_prompt || ''; + if (this.shouldStripLoraOnCopy()) { + negativePromptText = RecipeModal.stripLoraTags(negativePromptText); + } this.copyToClipboard(negativePromptText, 'Negative prompt copied to clipboard'); }); } @@ -1377,6 +1384,43 @@ class RecipeModal { } } + /** + * Strip tags from prompt text and clean up residual punctuation/whitespace. + * Handles both unescaped () and HTML-escaped (<lora:...>) variants. + * Cleans up artifacts like leading ", ", double commas, and extra whitespace. + */ + static stripLoraTags(text) { + return text + .replace(/]*>/gi, '') + .replace(/<lora:[^&]*>/gi, '') + .replace(/,(\s*,)+/g, ',') + .replace(/^,\s*/, '') + .replace(/,\s*$/, '') + .replace(/\s{2,}/g, ' ') + .trim(); + } + + shouldStripLoraOnCopy() { + const toggle = document.getElementById('stripLoraOnCopyToggle'); + return toggle ? toggle.checked : false; + } + + setupStripLoraToggle() { + const toggle = document.getElementById('stripLoraOnCopyToggle'); + if (!toggle) return; + + const stored = getStorageItem('strip_lora_on_copy'); + if (stored !== null) { + toggle.checked = stored === true; + } + + toggle.addEventListener('change', () => { + const checked = toggle.checked; + setStorageItem('strip_lora_on_copy', checked); + state.global.settings.strip_lora_on_copy = checked; + }); + } + // Fetch recipe syntax from backend and copy to clipboard async fetchAndCopyRecipeSyntax() { if (!this.recipeId) { diff --git a/static/js/state/index.js b/static/js/state/index.js index 3e0166aa..15ead32f 100644 --- a/static/js/state/index.js +++ b/static/js/state/index.js @@ -50,6 +50,7 @@ const DEFAULT_SETTINGS_BASE = Object.freeze({ download_skip_base_models: [], backup_auto_enabled: true, backup_retention_count: 5, + strip_lora_on_copy: false, }); export function createDefaultSettings() { diff --git a/templates/components/recipe_modal.html b/templates/components/recipe_modal.html index adc292d6..74ce975c 100644 --- a/templates/components/recipe_modal.html +++ b/templates/components/recipe_modal.html @@ -22,7 +22,16 @@
-

Generation Parameters

+
+

Generation Parameters

+ +