优化优先级

This commit is contained in:
2026-06-23 09:53:52 +08:00
parent 875eb21241
commit c3739b18eb
5 changed files with 287 additions and 94 deletions
+69 -66
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { onMounted, onUnmounted, ref, computed, nextTick, watch } from 'vue';
import { usePromptStore, splitTokens, normalizeSymbols, parseDetailedToken, constructToken } from '../stores/promptStore';
import { usePromptStore, splitTokens, normalizeSymbols, parseDetailedToken, constructToken, toNumericForm, toBracketForm, formatWeight } from '../stores/promptStore';
import type { LangCode, PresetFolder } from '../types';
import NotificationToast from './NotificationToast.vue';
import TranslationPopup from './TranslationPopup.vue';
@@ -37,7 +37,13 @@ let rafId: number | null = null;
const DRAG_THRESHOLD = 3; // 像素阈值,避免误触
const editingIndex = ref<number | null>(null);
const presetName = ref('');
const selectedFolderId = ref<string>('');
// 跨页面切换保留所选文件夹(刷新后重置为默认)
const selectedFolderId = computed({
get: () => store.editorSelectedFolderId,
set: (v: string) => { store.editorSelectedFolderId = v; },
});
// 保存预设时是否同时标记为收藏
const saveAsFavorite = ref(false);
const viewMode = ref<'compact' | 'detail'>('compact');
const showPresetDropdown = ref(false);
const showTranslationPopup = ref(false);
@@ -73,9 +79,13 @@ function handleClickOutside(event: Event) {
onMounted(() => {
document.addEventListener('click', handleClickOutside);
const defaultFolder = store.presetManagement?.settings?.defaultFolder;
if (defaultFolder) {
selectedFolderId.value = defaultFolder;
// 仅首次进入时初始化为默认文件夹;后续页面切换保留用户上次的选择
if (!store.editorFolderInitialized) {
const defaultFolder = store.presetManagement?.settings?.defaultFolder;
if (defaultFolder) {
store.editorSelectedFolderId = defaultFolder;
}
store.editorFolderInitialized = true;
}
store.searchQuery = ''; // Reset search query to ensure all tags are shown
});
@@ -134,7 +144,10 @@ const suggestions = ref<string[]>([]);
const editSuggestions = ref<string[]>([]);
const editorInputRef = ref<InstanceType<typeof EditorInput> | null>(null);
const tokenMappingRef = ref<InstanceType<typeof TokenMappingPanel> | null>(null);
const priorityStyle = ref<'{}' | '()' | '[]' | '<>' | 'suffix'>('{}');
// 优先级模式开关:false = 括号嵌套,true = 数字权重后缀
const numericMode = ref(false);
// 括号嵌套时使用的括号样式(默认圆括号)
const bracketStyle = ref<'()' | '{}' | '[]' | '<>'>('()');
const priorityStep = ref(0.1);
function splitTokensLocal(txt: string): string[] {
return splitTokens(txt);
@@ -166,25 +179,38 @@ function roundToDecimals(v: number, decimals: number): number {
const m = Math.pow(10, decimals);
return Math.round(v * m) / m;
}
function adjustWeight(core: string, delta: number): string {
const idx = core.lastIndexOf(':');
let base = core;
let w: number | null = null;
if (idx > -1) {
const num = parseFloat(core.slice(idx + 1).trim());
if (!isNaN(num)) { base = core.slice(0, idx); w = num; }
}
const stepStr = String(priorityStep.value);
const decimals = stepStr.includes('.') ? stepStr.split('.')[1]!.length : 0;
const cur = w == null ? 1.0 : w;
let nw = cur + delta;
function stepDecimals(): number {
const s = String(priorityStep.value);
return s.includes('.') ? (s.split('.')[1]?.length ?? 0) : 0;
}
nw = roundToDecimals(nw, decimals);
// 数字权重模式:确保 () 容器,按步进调整显式权重(默认 1,即无后缀)
function adjustNumericWeight(token: string, deltaSteps: number): string {
const { core, weight, prefix, suffix } = parseDetailedToken(token);
if (prefix || suffix) return token;
const base = weight ?? 1;
let next = roundToDecimals(base + deltaSteps * priorityStep.value, stepDecimals());
if (next < 0) next = 0;
if (next === 1) return `(${core})`;
return `(${core}:${formatWeight(next)})`;
}
// If weight is 1, return base without suffix
if (nw === 1) return base;
// 括号嵌套模式:在最外层再套一层选定括号
function addBracketLayer(token: string): string {
const { core, weight, wrappers, prefix, suffix } = parseDetailedToken(token);
const newWrappers = [bracketStyle.value, ...wrappers];
return constructToken(core, weight, newWrappers, prefix, suffix);
}
return base + ':' + nw;
// 括号嵌套模式:去掉最外层括号;若移除后无 () 承载裸权重则一并清除
function removeBracketLayer(token: string): string {
const { core, weight, wrappers, prefix, suffix } = parseDetailedToken(token);
if (prefix || suffix) return token;
if (wrappers.length === 0) return core;
const newWrappers = wrappers.slice(1);
const hasParen = newWrappers.includes('()');
const keepWeight = weight !== undefined && weight !== 1 && hasParen ? weight : undefined;
return constructToken(core, keepWeight, newWrappers, prefix, suffix);
}
const text = ref('');
@@ -338,25 +364,11 @@ function replaceCnComma() { applyFullPrompt(normalizeSymbols(text.value)); }
function formatPrompt() { applyFullPrompt(normalizePromptLocal(text.value)); }
function unifyPriorityStyle() {
const tokens = splitTokens(text.value);
const processed = tokens.map(token => {
const { core, weight, wrappers } = parseDetailedToken(token);
let result = core;
let currentWrappers = [...wrappers];
if (weight !== undefined && weight !== 1) {
const lastWrapper = currentWrappers[currentWrappers.length - 1];
if (lastWrapper === '()') {
currentWrappers.pop();
}
const wStr = Number.isInteger(weight) ? weight.toString() : weight.toFixed(2).replace(/\.?0+$/, '');
result = `(${result}:${wStr})`;
}
return store.wrapToken(result, currentWrappers);
});
applyFullPrompt(processed.join(', '));
showNotification('已统一优先级样式', 'success');
const list = splitTokens(text.value).map(token =>
numericMode.value ? toNumericForm(token) : toBracketForm(token)
);
applyFullPrompt(list.join(', '));
showNotification(numericMode.value ? '已统一为数字权重' : '已统一为括号样式', 'success');
}
// 新增功能方法
@@ -416,34 +428,18 @@ function addWrapperToToken(index: number) {
const tokens = splitTokensLocal(text.value);
if (index < 0 || index >= tokens.length) return;
const token = tokens[index]!;
const parsed = store.parseTokenWrappers(token);
const core = parsed?.core ?? token;
const wrappers = parsed?.wrappers ?? [];
if (priorityStyle.value === 'suffix') {
const newCore = adjustWeight(core, +priorityStep.value);
tokens[index] = store.wrapToken(newCore, wrappers);
} else {
const newWrappers = [...wrappers, priorityStyle.value];
tokens[index] = store.wrapToken(core, newWrappers);
}
tokens[index] = numericMode.value ? adjustNumericWeight(token, +1) : addBracketLayer(token);
applyFullPrompt(tokens.join(', '));
showNotification('已添加优先级', 'success');
showNotification(numericMode.value ? '已提升权重' : '已添加优先级', 'success');
}
function removeWrapperFromToken(index: number) {
const tokens = splitTokensLocal(text.value);
if (index < 0 || index >= tokens.length) return;
const token = tokens[index]!;
const { core, wrappers } = store.parseTokenWrappers(token);
if (priorityStyle.value === 'suffix') {
const newCore = adjustWeight(core, -priorityStep.value);
tokens[index] = store.wrapToken(newCore, wrappers);
} else if (wrappers.length > 0) {
const newWrappers = wrappers.slice(0, -1);
tokens[index] = store.wrapToken(core, newWrappers);
}
tokens[index] = numericMode.value ? adjustNumericWeight(token, -1) : removeBracketLayer(token);
applyFullPrompt(tokens.join(', '));
showNotification('已调整优先级', 'success');
showNotification(numericMode.value ? '已降低权重' : '已移除优先级', 'success');
}
function getTokenWrapperInfo(token: string) {
@@ -784,10 +780,16 @@ function savePreset() {
type: 'positive',
content: store.promptText,
description: '从编辑器快速保存',
folderId: folderId
folderId: folderId,
isFavorite: saveAsFavorite.value
});
showNotification(`预设「${name}」已保存到预设管理`, 'success');
showNotification(
saveAsFavorite.value
? `预设「${name}」已保存并收藏`
: `预设「${name}」已保存到预设管理`,
'success'
);
presetName.value = '';
}
@@ -857,13 +859,14 @@ function isRemoveDisabled(token: string): boolean {
<div class="pe-root">
<EditorToolbar :languages="store.languages as LangCode[]" v-model:selected-lang="selectedLang"
v-model:preset-name="presetName" v-model:selected-folder-id="selectedFolderId"
v-model:save-as-favorite="saveAsFavorite"
v-model:show-preset-dropdown="showPresetDropdown" :folder-tree="folderTree" :flattened-folders="flattenedFolders"
@copy="copyLeft" @save-preset="savePreset" @preset-load="handlePresetLoad" @preset-save="handlePresetSave"
@preset-delete="handlePresetDelete" @preset-rename="handlePresetRename" />
<div class="pe-main">
<EditorInput ref="editorInputRef" v-model:text="text" v-model:priority-style="priorityStyle"
v-model:priority-step="priorityStep" :suggestions="suggestions"
<EditorInput ref="editorInputRef" v-model:text="text" v-model:numeric-mode="numericMode"
v-model:bracket-style="bracketStyle" v-model:priority-step="priorityStep" :suggestions="suggestions"
: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"
@@ -871,7 +874,7 @@ function isRemoveDisabled(token: string): boolean {
<TokenMappingPanel ref="tokenMappingRef" :tokens="tokens" :selected-lang="selectedLang"
v-model:view-mode="viewMode" :dragging-index="draggingIndex" :over-index="overIndex" :insert-side="insertSide"
:is-dragging="isDragging" :edit-suggestions="editSuggestions" :priority-style="priorityStyle"
:is-dragging="isDragging" :edit-suggestions="editSuggestions" :numeric-mode="numericMode"
:display-trans="displayTrans" :is-unmapped="isUnmapped" :get-token-wrapper-info="getTokenWrapperInfo"
:has-weight-suffix="hasWeightSuffix" :get-suggestions="(prefix, limit) => store.getSuggestions(prefix, limit)"
@pointer-down="onPointerDown" @panel-dragover="handlePanelDragOver" @panel-dragleave="handlePanelDragLeave"