diff --git a/frontend/src/pages/settings/settings-prompt.tsx b/frontend/src/pages/settings/settings-prompt.tsx index cf7a72a0..dd37a545 100644 --- a/frontend/src/pages/settings/settings-prompt.tsx +++ b/frontend/src/pages/settings/settings-prompt.tsx @@ -214,12 +214,22 @@ function SettingsPrompt() { if (textarea) { const currentValue = field.value || ''; const variablePattern = `{{.${variable}}}`; - const existing = currentValue.match(variableActionRegex(variable)); + const matches = [...currentValue.matchAll(new RegExp(variableActionRegex(variable).source, 'g'))]; - if (existing && existing.index !== undefined) { - const matchStart = existing.index; + if (matches.length > 0) { + // Cycle through occurrences: advance from the one the caret is already on + // (wrapping past the last), else jump to the first occurrence at/after the caret. + const { selectionEnd, selectionStart } = textarea; + const currentIdx = matches.findIndex( + (match) => match.index === selectionStart && match.index + match[0].length === selectionEnd, + ); + const target = + currentIdx >= 0 + ? matches[(currentIdx + 1) % matches.length] + : (matches.find((match) => match.index >= selectionStart) ?? matches[0]); + const matchStart = target.index ?? 0; textarea.focus(); - textarea.setSelectionRange(matchStart, matchStart + existing[0].length); + textarea.setSelectionRange(matchStart, matchStart + target[0].length); textarea.scrollTop = Math.max(0, caretOffsetTop(textarea, matchStart) - textarea.clientHeight / 2); } else { const start = textarea.selectionStart; @@ -1069,9 +1079,11 @@ function Variables({ currentTemplate, onVariableClick, variables }: VariablesPro