diff --git a/src/components/PromptEditor.vue b/src/components/PromptEditor.vue index 3e9d9be..b41d825 100644 --- a/src/components/PromptEditor.vue +++ b/src/components/PromptEditor.vue @@ -45,6 +45,8 @@ const selectedFolderId = computed({ // 保存预设时是否同时标记为收藏 const saveAsFavorite = ref(false); const viewMode = ref<'compact' | 'detail'>('compact'); +// 左侧光标所在的 token 序号,用于在右侧映射中定位高亮 +const activeTokenIndex = ref(null); const showPresetDropdown = ref(false); const showTranslationPopup = ref(false); const translationTargetToken = ref(null); @@ -829,6 +831,7 @@ const unmappedTokens = computed(() => { }); function isUnmapped(key: string): boolean { + void store.mappingVersion; // 追踪映射变更以触发响应式刷新 const { core } = parseDetailedToken(key); const tag = store.getTagByKey(core); return !tag || !tag.translation?.[selectedLang.value]; @@ -842,6 +845,7 @@ function handleApplyTranslation(results: { key: string; trans: string }[]) { } function displayTrans(key: string): string { + void store.mappingVersion; // 追踪映射变更以触发响应式刷新 const { core, weight, wrappers, prefix, suffix } = parseDetailedToken(key); const tag = store.getTagByKey(core); const translatedCore = tag?.translation?.[selectedLang.value] ?? tag?.key ?? core; @@ -870,11 +874,13 @@ function isRemoveDisabled(token: string): boolean { :get-suggestions="(prefix, limit) => store.getSuggestions(prefix, limit)" @update-suggestions="updateSuggestionsFromText" @copy="copyLeft" @replace-cn-comma="replaceCnComma" @format-prompt="formatPrompt" @unify-priority="unifyPriorityStyle" @toggle-underscore="toggleUnderscoreSpace" - @add-tag="handleAddTag" @drag-tag-start="handleQuickAddDragStart" @drag-tag-end="handleQuickAddDragEnd" /> + @add-tag="handleAddTag" @drag-tag-start="handleQuickAddDragStart" @drag-tag-end="handleQuickAddDragEnd" + @locate-token="(i) => activeTokenIndex = i >= 0 ? i : null" /> import { ref, computed, nextTick } from 'vue'; -import { splitTokens, parseDetailedToken, constructToken } from '../../stores/promptStore'; +import { splitTokens, parseDetailedToken, constructToken, normalizeSymbols } from '../../stores/promptStore'; import PromptQuickAdd from '../PromptQuickAdd.vue'; const props = defineProps<{ @@ -26,6 +26,7 @@ const emit = defineEmits<{ 'add-tag': [tag: string]; 'drag-tag-start': [tag: string]; 'drag-tag-end': []; + 'locate-token': [index: number]; }>(); const inputEl = ref(null); @@ -154,9 +155,41 @@ async function applySuggestion(s: string) { emit('update-suggestions'); } -function updateSuggestions() { - // 通知父组件更新建议 +// 计算与 splitTokens 对齐的非空 token 区间(用于光标定位) +function computeTokenRanges(txt: string): { start: number; end: number }[] { + const ranges: { start: number; end: number }[] = []; + let depth = 0; + let segStart = 0; + for (let i = 0; i < txt.length; i++) { + const c = txt[i]!; + if (c === '(' || c === '[' || c === '{' || c === '<') depth++; + else if (c === ')' || c === ']' || c === '}' || c === '>') depth = Math.max(0, depth - 1); + if ((c === ',' || c === '\n') && depth === 0) { + ranges.push({ start: segStart, end: i }); + segStart = i + 1; + } + } + ranges.push({ start: segStart, end: txt.length }); + return ranges.filter(r => txt.slice(r.start, r.end).trim().length > 0); +} + +// 根据光标位置定位对应的 token 序号,通知父组件高亮右侧映射 +function emitLocate() { + const el = inputEl.value; + if (!el) return; + const norm = normalizeSymbols(props.text); + const pos = el.selectionStart ?? norm.length; + const ranges = computeTokenRanges(norm); + if (!ranges.length) { emit('locate-token', -1); return; } + let idx = ranges.findIndex(r => pos >= r.start && pos <= r.end); + if (idx === -1) idx = ranges.findIndex(r => pos <= r.end); + if (idx === -1) idx = ranges.length - 1; + emit('locate-token', idx); +} + +function onCursorActivity() { emit('update-suggestions'); + emitLocate(); } defineExpose({ @@ -172,8 +205,8 @@ defineExpose({ class="pe-input" v-model="localText" @keydown="onKeyDown" - @click="updateSuggestions" - @keyup="updateSuggestions" + @click="onCursorActivity" + @keyup="onCursorActivity" placeholder="例如:1girl, aaa, bbb, ccc" >
diff --git a/src/components/editor/TokenMappingPanel.vue b/src/components/editor/TokenMappingPanel.vue index 18abb7b..cf7d260 100644 --- a/src/components/editor/TokenMappingPanel.vue +++ b/src/components/editor/TokenMappingPanel.vue @@ -1,5 +1,5 @@