-
-
+ initializeComponents() {
+ // 初始化主模态框
+ this.mainModal = new MainModal('提示词管理');
+
+ // 初始化对话框组件
+ this.dialogComponents = new DialogComponents();
+ // 初始化列表组件
+ this.promptList = new PromptList({
+ onEdit: (index, prompt) => this.handleEditPrompt(index, prompt),
+ onDelete: (index, prompt) => this.handleDeletePrompt(index, prompt),
+ onApply: (index, prompt) => this.handleApplyPrompt(index, prompt),
+ onTagClick: (tagName) => this.handleTagClick(tagName)
+ }); // 初始化搜索栏 (在列表组件之后初始化)
+ this.searchBar = new SearchBar(
+ // 兼容旧的搜索回调
+ (keyword) => {
+ this.currentSearchKeyword = keyword;
+
+ // 设置原始数据,然后应用搜索关键词
+ this.promptList.setPrompts(this.promptManager.getPrompts());
+ const searchResultCount = this.promptList.filterPrompts(keyword);
+
+ return searchResultCount;
+ },
+ // 新的标签筛选回调
+ (keyword, selectedTags) => {
+ this.currentSearchKeyword = keyword;
+
+ // 设置原始数据,然后应用搜索关键词和标签筛选
+ this.promptList.setPrompts(this.promptManager.getPrompts());
+ const searchResultCount = this.promptList.filterPrompts(keyword, selectedTags);
+
+ return searchResultCount;
+ }
+ ); // 初始化表单组件
+ this.promptForm = new PromptForm({
+ onSubmit: (formData, isEditing, editingPrompt) => this.handleFormSubmit(formData, isEditing, editingPrompt),
+ onCancel: () => this.handleFormCancel(),
+ getPrompts: () => this.promptManager.getPrompts() // 新增:提供获取提示词数据的回调
+ });
+
+ // 初始化标签管理组件
+ this.tagManager = new TagManager({
+ onClose: () => {
+ // 标签管理关闭后刷新标签筛选区域
+ if (this.searchBar && this.searchBar.refreshTags) {
+ this.searchBar.refreshTags();
+ }
+ }, onTagColorChange: () => {
+ // 标签颜色变更后立即刷新所有组件的标签颜色和标签列表
+ this.refreshAllTagColors();
+ // 同时刷新搜索栏的标签列表
+ if (this.searchBar && this.searchBar.refreshTags) {
+ this.searchBar.refreshTags();
+ }
+ },
+ promptManager: this.promptManager
+ });
+
+ this.assembleUI();
+ }
+
+ assembleUI() {
+ const contentContainer = this.mainModal.getContentContainer();
+
+ // 创建左右分栏布局
+ contentContainer.innerHTML = `
+
`;
-
- // 添加样式
- this.addStyles();
- // 绑定事件
- this.bindEvents();
+ const mainSection = contentContainer.querySelector('.prompt-manager-main');
+ const formSection = contentContainer.querySelector('.prompt-manager-form');
+ // 插入组件
+ mainSection.appendChild(this.promptList.getElement());
+ mainSection.insertBefore(this.searchBar.getElement(), this.promptList.getElement());
+ formSection.appendChild(this.promptForm.getElement());
+ formSection.appendChild(this.tagManager.getElement());
- document.body.appendChild(this.modal);
- }
-
- addStyles() {
- if (document.getElementById('prompt-embedder-styles')) return;
+ // 添加布局样式
+ this.addLayoutStyles();
+ } setupEventHandlers() {
+ // 绑定头部操作按钮事件
+ const headerActions = this.promptList.getHeaderActions();
- const styles = document.createElement('style');
- styles.id = 'prompt-embedder-styles';
- styles.textContent = `
- .prompt-embedder-modal {
- display: none;
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 10000;
- }
-
- .prompt-embedder-overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.7);
- display: flex;
- justify-content: center;
- align-items: center;
- }
-
- .prompt-embedder-container {
- background: #1a1a1a;
- border-radius: 8px;
- border: 1px solid #3d3d3d;
- width: 90%;
- max-width: 800px;
- max-height: 90%;
- overflow: hidden;
- color: #ffffff;
- }
-
- .prompt-embedder-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 15px 20px;
- border-bottom: 1px solid #3d3d3d;
- background: #262626;
- }
-
- .prompt-embedder-header h3 {
- margin: 0;
- color: #ffffff;
- }
-
- .close-btn {
- background: none;
- border: none;
- color: #ffffff;
- font-size: 24px;
- cursor: pointer;
- padding: 0;
- width: 30px;
- height: 30px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- .close-btn:hover {
- background: #ff5733;
- border-radius: 50%;
- }
-
- .prompt-embedder-content {
- padding: 20px;
- max-height: 600px;
- overflow-y: auto;
- }
-
- .prompt-list-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 15px;
- }
-
- .prompt-list-header h4 {
- margin: 0;
- color: #ffffff;
- }
-
- .header-actions {
- display: flex;
- gap: 8px;
- }
-
- .add-prompt-btn, .import-btn, .export-btn {
- background: #2a82e4;
- border: none;
- color: white;
- padding: 8px 16px;
- border-radius: 4px;
- cursor: pointer;
- font-weight: bold;
- font-size: 12px;
- transition: background 0.2s;
- }
-
- .add-prompt-btn:hover {
- background: #1f6bb8;
- }
-
- .import-btn {
- background: #43cf7c;
- }
-
- .import-btn:hover {
- background: #38b569;
- }
-
- .export-btn {
- background: #ffc300;
- color: #000;
- }
-
- .export-btn:hover {
- background: #e6af00;
- }
-
- .prompt-list {
- min-height: 200px;
- }
-
- .search-section {
- margin-bottom: 15px;
- }
-
- .search-container {
- position: relative;
- display: flex;
- align-items: center;
- }
-
- .search-input {
- width: 100%;
- padding: 10px 40px 10px 12px;
- background: #262626;
- border: 1px solid #3d3d3d;
- border-radius: 6px;
- color: #ffffff;
- font-size: 14px;
- outline: none;
- transition: border-color 0.2s;
- }
-
- .search-input:focus {
- border-color: #2a82e4;
- }
-
- .search-input::placeholder {
- color: #888;
- }
-
- .search-icon {
- position: absolute;
- right: 12px;
- color: #888;
- pointer-events: none;
- }
-
- .empty-state {
- text-align: center;
- padding: 40px 20px;
- color: #888;
- }
-
- .empty-state-icon {
- font-size: 48px;
- margin-bottom: 15px;
- opacity: 0.5;
- }
-
- .prompt-item {
- background: #262626;
- border: 1px solid #3d3d3d;
- border-radius: 6px;
- padding: 15px;
- margin-bottom: 10px;
- transition: all 0.2s;
- }
-
- .prompt-item:hover {
- border-color: #2a82e4;
- }
-
- .prompt-item-header {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- margin-bottom: 8px;
- }
-
- .prompt-item-title-section {
- flex: 1;
- min-width: 0;
- }
-
- .prompt-item-name {
- font-weight: bold;
- color: #ffffff;
- font-size: 16px;
- margin-bottom: 4px;
- }
-
- .prompt-item-tags {
- display: flex;
- flex-wrap: wrap;
- gap: 4px;
- margin-top: 4px;
- }
-
- .tag {
- display: inline-block;
- background: #2a82e4;
- color: white;
- padding: 2px 8px;
- border-radius: 12px;
- font-size: 11px;
- font-weight: 500;
- line-height: 1.2;
- white-space: nowrap;
- max-width: 100px;
- overflow: hidden;
- text-overflow: ellipsis;
- cursor: pointer;
- transition: all 0.2s ease;
- user-select: none;
- }
-
- .tag:hover {
- transform: translateY(-1px);
- box-shadow: 0 2px 8px rgba(42, 130, 228, 0.3);
- filter: brightness(1.1);
- }
-
- .tag:nth-child(even) {
- background: #43cf7c;
- }
-
- .tag:nth-child(even):hover {
- box-shadow: 0 2px 8px rgba(67, 207, 124, 0.3);
- }
-
- .tag:nth-child(3n) {
- background: #ff8c42;
- }
-
- .tag:nth-child(3n):hover {
- box-shadow: 0 2px 8px rgba(255, 140, 66, 0.3);
- }
-
- .tag:nth-child(4n) {
- background: #9b59b6;
- }
-
- .tag:nth-child(4n):hover {
- box-shadow: 0 2px 8px rgba(155, 89, 182, 0.3);
- }
-
- .prompt-item-actions {
- display: flex;
- gap: 8px;
- }
-
- .edit-btn, .apply-btn, .delete-btn {
- background: #383838;
- border: none;
- color: white;
- padding: 6px 12px;
- border-radius: 4px;
- cursor: pointer;
- font-size: 12px;
- transition: background 0.2s;
- }
-
- .edit-btn:hover {
- background: #ffc300;
- color: #000;
- }
-
- .apply-btn:hover {
- background: #43cf7c;
- }
-
- .delete-btn {
- background: #8b3a3a;
- }
-
- .delete-btn:hover {
- background: #ff5733;
- }
-
- .prompt-item-content {
- color: #c4c4c4;
- line-height: 1.4;
- background: #1a1a1a;
- padding: 10px;
- border-radius: 4px;
- border: 1px solid #3d3d3d;
- white-space: pre-wrap;
- }
-
- .prompt-form-section {
- border-top: 1px solid #3d3d3d;
- padding-top: 20px;
- margin-top: 20px;
- }
-
- .form-title {
- margin: 0 0 15px 0;
- color: #ffffff;
- }
-
- .form-group {
- margin-bottom: 15px;
- }
-
- .form-group label {
- display: block;
- margin-bottom: 5px;
- color: #ffffff;
- font-weight: bold;
- }
-
- .form-group input,
- .form-group textarea {
- width: 100%;
- padding: 10px;
- background: #262626;
- border: 1px solid #3d3d3d;
- border-radius: 4px;
- color: #ffffff;
- font-size: 14px;
- resize: vertical;
- }
-
- .form-group input:focus,
- .form-group textarea:focus {
- outline: none;
- border-color: #2a82e4;
- }
-
- .form-actions {
- display: flex;
- gap: 10px;
- justify-content: flex-end;
- }
-
- .save-btn {
- background: #43cf7c;
- border: none;
- color: white;
- padding: 10px 20px;
- border-radius: 4px;
- cursor: pointer;
- font-weight: bold;
- }
-
- .save-btn:hover {
- background: #38b569;
- }
-
- .cancel-btn {
- background: #ff5733;
- border: none;
- color: white;
- padding: 10px 20px;
- border-radius: 4px;
- cursor: pointer;
- font-weight: bold;
- }
-
- .cancel-btn:hover {
- background: #e04929;
- }
-
- .search-highlight {
- background-color: #ffc300;
- color: #000;
- padding: 1px 2px;
- border-radius: 2px;
- font-weight: bold;
- }
- `;
- document.head.appendChild(styles);
- }
-
- bindEvents() {
- // 关闭按钮
- this.modal.querySelector('.close-btn').addEventListener('click', () => {
- this.hide();
- });
-
- // 点击遮罩层关闭
- this.modal.querySelector('.prompt-embedder-overlay').addEventListener('click', (e) => {
- if (e.target === e.currentTarget) {
- this.hide();
- }
- });
-
- // 添加提示词按钮
- this.modal.querySelector('.add-prompt-btn').addEventListener('click', () => {
+ headerActions.addBtn.addEventListener('click', () => {
this.showForm();
});
-
- // 导出按钮
- this.modal.querySelector('.export-btn').addEventListener('click', () => {
- this.promptManager.exportPrompts();
- });
-
- // 导入按钮
- this.modal.querySelector('.import-btn').addEventListener('click', () => {
- this.showImportDialog();
- });
-
- // 表单提交
- this.modal.querySelector('.prompt-form').addEventListener('submit', (e) => {
- e.preventDefault();
- this.handleFormSubmit();
- });
-
- // 取消按钮
- this.modal.querySelector('.cancel-btn').addEventListener('click', () => {
- this.hideForm();
- });
-
- // 搜索框事件监听
- const searchInput = this.modal.querySelector('.search-input');
- // 输入事件 - 实时搜索
- searchInput.addEventListener('input', (e) => {
- this.currentSearchKeyword = e.target.value;
- this.renderPromptList();
+ headerActions.importBtn.addEventListener('click', () => {
+ this.handleImport();
});
-
- // 清空搜索框快捷键 (Escape)
- searchInput.addEventListener('keydown', (e) => {
- if (e.key === 'Escape') {
- e.target.value = '';
- this.currentSearchKeyword = '';
- this.renderPromptList();
- }
+
+ headerActions.exportBtn.addEventListener('click', () => {
+ this.handleExport();
});
- }
-
+
+ headerActions.tagManagerBtn.addEventListener('click', () => {
+ this.handleTagManager();
+ });
+ } // 显示管理器
show() {
this.isVisible = true;
- this.modal.style.display = 'block';
+ this.mainModal.show();
+
+ // 确保布局容器初始状态正确(没有展开类)
+ const layoutContainer = this.mainModal.getContentContainer().querySelector('.prompt-manager-layout');
+ if (layoutContainer) {
+ layoutContainer.classList.remove('form-expanded');
+ }
// 重置搜索状态
this.currentSearchKeyword = '';
- const searchInput = this.modal.querySelector('.search-input');
- if (searchInput) {
- searchInput.value = '';
- }
+ this.searchBar.setKeyword('');
- this.renderPromptList();
- }
-
- hide() {
- this.isVisible = false;
- this.modal.style.display = 'none';
- this.hideForm();
- }
-
+ // 刷新数据
+ this.refreshPromptList();
+ } // 显示表单
showForm(prompt = null) {
this.editingPrompt = prompt;
- const formSection = this.modal.querySelector('.prompt-form-section');
- const form = this.modal.querySelector('.prompt-form');
- const title = this.modal.querySelector('.form-title');
+
+ // 隐藏标签管理器(如果正在显示)
+ if (this.tagManager && this.tagManager.isVisible()) {
+ this.tagManager.hide();
+ }
if (prompt) {
- title.textContent = '编辑提示词';
- form.name.value = prompt.name;
- form.content.value = prompt.content;
- form.tags.value = (prompt.tags || []).join(', ');
+ this.promptForm.show('edit', prompt);
} else {
- title.textContent = '添加提示词';
- form.reset();
+ this.promptForm.show('add');
}
-
- formSection.style.display = 'block';
- form.name.focus();
- }
-
+ } // 隐藏表单
hideForm() {
this.editingPrompt = null;
- this.modal.querySelector('.prompt-form-section').style.display = 'none';
- this.modal.querySelector('.prompt-form').reset();
+ this.promptForm.hide();
}
- handleFormSubmit() {
- const form = this.modal.querySelector('.prompt-form');
- const name = form.name.value.trim();
- const content = form.content.value.trim();
- const tagsInput = form.tags.value.trim();
-
- if (!name || !content) {
- alert('请填写完整的提示词名称和内容');
- return;
- }
-
- // 处理标签输入
- let tags = [];
- if (tagsInput) {
- tags = tagsInput.split(',')
- .map(tag => tag.trim())
- .filter(tag => tag.length > 0)
- .slice(0, 4); // 限制最多4个标签
- }
-
- if (this.editingPrompt) {
- this.promptManager.updatePrompt(this.editingPrompt.id, name, content, tags);
- } else {
- this.promptManager.addPrompt(name, content, tags);
- }
-
- this.hideForm();
- this.renderPromptList();
+ // 处理编辑提示词
+ async handleEditPrompt(index, prompt) {
+ this.showForm(prompt);
}
- renderPromptList() {
- const listContainer = this.modal.querySelector('.prompt-list');
-
- // 使用搜索功能获取提示词列表
- const prompts = this.currentSearchKeyword
- ? this.promptManager.searchPrompts(this.currentSearchKeyword)
- : this.promptManager.getPrompts();
-
- // 如果搜索无结果,显示相应提示
- if (this.currentSearchKeyword && prompts.length === 0) {
- const isMultiTagSearch = this.currentSearchKeyword.includes(',');
- const helpText = isMultiTagSearch
- ? '尝试减少标签数量或使用不同的标签组合'
- : '尝试使用其他关键词、标签名称或用逗号分隔多个标签搜索';
+ // 处理删除提示词
+ async handleDeletePrompt(index, prompt) {
+ const confirmed = await this.dialogComponents.showDeleteConfirm(prompt.name);
+ if (confirmed) {
+ try {
+ this.promptManager.deletePrompt(prompt.id);
+ this.refreshPromptList();
- listContainer.innerHTML = `
-
-
🔍
-
未找到匹配"${this.escapeHtml(this.currentSearchKeyword)}"的提示词
-
${helpText}
-
- `;
- return;
- }
-
- // 如果没有提示词,显示空状态
- if (prompts.length === 0) {
- listContainer.innerHTML = `
-
-
📝
-
暂无提示词,点击上方"+ 添加提示词"按钮来添加第一个提示词
-
- `;
- return;
- }
-
- // 渲染提示词列表,如果是搜索结果,高亮显示匹配的关键词
- listContainer.innerHTML = prompts.map(prompt => {
- let displayName = this.escapeHtml(prompt.name);
- let displayContent = this.escapeHtml(prompt.content);
- let displayTags = (prompt.tags || []).map(tag => this.escapeHtml(tag));
-
- // 如果有搜索关键词,高亮显示匹配的部分
- if (this.currentSearchKeyword) {
- const keyword = this.escapeHtml(this.currentSearchKeyword);
- const highlightClass = 'search-highlight';
-
- // 处理多标签搜索的高亮
- const isMultiTagSearch = keyword.includes(',');
- if (isMultiTagSearch) {
- const searchTags = keyword.split(',').map(tag => tag.trim()).filter(tag => tag);
-
- // 高亮匹配的标签
- displayTags = displayTags.map(tag => {
- const tagLower = tag.toLowerCase();
- const matchedSearchTag = searchTags.find(searchTag =>
- tagLower.includes(searchTag.toLowerCase())
- );
- if (matchedSearchTag) {
- const tagRegex = new RegExp(`(${matchedSearchTag.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
- return tag.replace(tagRegex, `
$1`);
- }
- return tag;
- });
- } else {
- // 单关键词搜索的高亮
- const nameRegex = new RegExp(`(${keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
- displayName = displayName.replace(nameRegex, `
$1`);
-
- const contentRegex = new RegExp(`(${keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
- displayContent = displayContent.replace(contentRegex, `
$1`);
-
- // 高亮标签中的匹配文本
- displayTags = displayTags.map(tag => {
- return tag.replace(nameRegex, `
$1`);
- });
- }
+ // 删除成功,不显示弹窗,直接完成操作
+ } catch (error) {
+ console.error('删除提示词失败:', error);
+ await this.dialogComponents.showErrorDialog(
+ '删除失败',
+ error.message || '删除提示词时发生错误'
+ );
}
-
- // 生成标签HTML
- const tagsHtml = displayTags.length > 0
- ? `
${displayTags.map(tag => `${tag}`).join('')}
`
- : '';
-
- return `
-
- `;
- }).join('');
-
- // 移除之前的事件监听器(如果存在)
- const existingListener = this._listClickHandler;
- if (existingListener) {
- listContainer.removeEventListener('click', existingListener);
}
-
- // 创建新的事件处理器
- this._listClickHandler = (e) => {
- const action = e.target.getAttribute('data-action');
- if (!action) return; // 如果没有action属性,则不处理
-
- const promptItem = e.target.closest('.prompt-item');
- if (!promptItem) return; // 如果找不到对应的提示词项,则不处理
-
- const promptId = parseInt(promptItem.getAttribute('data-id'));
- const prompt = prompts.find(p => p.id === promptId);
+ } // 处理应用提示词
+ async handleApplyPrompt(index, prompt) {
+ try {
+ // 获取所有文本组件
+ const textWidgets = this.embedder.getAllTextWidgets();
- if (!prompt) {
- console.error('找不到对应的提示词:', promptId);
+ if (textWidgets.length === 0) {
+ await this.dialogComponents.showErrorDialog(
+ '应用失败',
+ '未找到可用的文本输入框'
+ );
return;
}
- // 防止事件冒泡
- e.stopPropagation();
- e.preventDefault();
+ let targetWidget = null;
- try {
- if (action === 'edit') {
- this.showForm(prompt);
- } else if (action === 'apply') {
- this.embedder.applyPrompt(prompt);
- } else if (action === 'delete') {
- this.deletePrompt(prompt);
+ // 如果有多个文本组件,显示选择器
+ if (textWidgets.length > 1) {
+ targetWidget = await this.dialogComponents.showWidgetSelector(
+ textWidgets,
+ (widget) => this.embedder.getWidgetDisplayName(widget)
+ );
+
+ if (!targetWidget) {
+ return; // 用户取消选择
}
- } catch (error) {
- console.error('操作失败:', error);
- alert('操作失败: ' + error.message);
+ } else {
+ targetWidget = textWidgets[0];
}
- };
- // 绑定新的事件监听器
- listContainer.addEventListener('click', this._listClickHandler);
-
- // 添加标签点击事件处理
- const tagElements = listContainer.querySelectorAll('.tag');
- tagElements.forEach(tagElement => {
- tagElement.addEventListener('click', (e) => {
- e.stopPropagation();
- e.preventDefault();
-
- // 获取标签文本(去除HTML高亮标记)
- const tagText = tagElement.textContent.trim();
-
- // 在搜索框中设置标签文本并执行搜索
- const searchInput = this.modal.querySelector('.search-input');
- searchInput.value = tagText;
- this.currentSearchKeyword = tagText;
- this.renderPromptList();
-
- // 聚焦到搜索框
- searchInput.focus();
- });
+ // 检查是否有现有内容
+ const currentValue = targetWidget.value || '';
+ const hasContent = currentValue.trim().length > 0;
- // 添加标签悬停提示
- tagElement.title = `点击搜索标签: ${tagElement.textContent.trim()}`;
- });
+ let insertMode = 'replace'; // 默认模式
+
+ if (hasContent) {
+ // 显示插入模式选择对话框
+ insertMode = await this.dialogComponents.showInsertModeDialog(
+ '选择插入模式',
+ '检测到输入框中已有内容,请选择插入模式:'
+ );
+
+ if (!insertMode) {
+ return; // 用户取消或关闭对话框
+ }
+ } // 应用提示词
+ this.embedder.applyPromptToWidget(prompt, targetWidget, insertMode);
+
+ // 隐藏管理器
+ this.hide();
+
+ // 不再显示成功提示,直接完成操作
+
+ } catch (error) {
+ console.error('应用提示词失败:', error);
+ await this.dialogComponents.showErrorDialog(
+ '应用失败',
+ error.message || '应用提示词时发生错误'
+ );
+ }
}
- deletePrompt(prompt) {
- // 显示确认删除对话框
- this.showDeleteConfirmDialog(prompt.name, () => {
- this.promptManager.deletePrompt(prompt.id);
- this.renderPromptList();
- });
+ // 标签筛选和搜索组合逻辑
+ filterPromptsWithTagsAndSearch(keyword, selectedTags) {
+ let allPrompts = this.promptManager.getPrompts();
+
+ // 如果有搜索关键词,先进行搜索筛选
+ if (keyword && keyword.trim()) {
+ allPrompts = this.promptManager.searchPrompts(keyword.trim());
+ }
+
+ // 如果有选中的标签,进行标签筛选
+ if (selectedTags && selectedTags.length > 0) {
+ allPrompts = allPrompts.filter(prompt => {
+ if (!prompt.tags || !Array.isArray(prompt.tags)) {
+ return false;
+ }
+
+ // 检查提示词是否包含所有选中的标签(AND逻辑)
+ return selectedTags.every(selectedTag =>
+ prompt.tags.some(tag => tag && tag.trim() === selectedTag)
+ );
+ });
+ }
+
+ return allPrompts;
}
- showImportDialog() {
- const input = document.createElement('input');
- input.type = 'file';
- input.accept = '.json';
- input.addEventListener('change', async (e) => {
- const file = e.target.files[0];
+ // 处理标签点击(从提示词卡片中点击标签)
+ handleTagClick(tagName) {
+ // 将标签添加到搜索栏的筛选中
+ this.searchBar.selectedTags.add(tagName);
+ this.searchBar.renderTags();
+ this.searchBar.updateActiveFiltersDisplay();
+ this.searchBar.handleFilter();
+ }
+
+ // 处理表单提交
+ async handleFormSubmit(formData, isEditing, editingPrompt) {
+ try {
+ const { name, content, tags } = formData; if (isEditing && editingPrompt) {
+ // 编辑模式
+ this.promptManager.updatePrompt(editingPrompt.id, name, content, tags);
+
+ // 更新成功,不显示弹窗,直接完成操作
+ } else {
+ // 添加模式
+ this.promptManager.addPrompt(name, content, tags);
+
+ // 添加成功,不显示弹窗,直接完成操作
+ }
+
+ this.hideForm();
+ this.refreshPromptList();
+
+ } catch (error) {
+ console.error('保存提示词失败:', error);
+ await this.dialogComponents.showErrorDialog(
+ '保存失败',
+ error.message || '保存提示词时发生错误'
+ );
+ }
+ }
+
+ // 处理表单取消
+ handleFormCancel() {
+ this.hideForm();
+ }
+
+ // 处理导入
+ async handleImport() {
+ try {
+ const file = await this.dialogComponents.showFileImportDialog('.json');
+
if (file) {
- try {
- const count = await this.promptManager.importPrompts(file);
- alert(`成功导入 ${count} 个提示词!`);
- this.renderPromptList();
- } catch (error) {
- alert(`导入失败: ${error}`);
- }
+ const count = await this.promptManager.importPrompts(file);
+ this.refreshPromptList();
+ await this.dialogComponents.showSuccessDialog(
+ '导入成功',
+ `成功导入 ${count} 个提示词!`,
+ 1500 // 缩短显示时间到1.5秒
+ );
}
- });
- input.click();
+ } catch (error) {
+ console.error('导入失败:', error);
+ await this.dialogComponents.showErrorDialog(
+ '导入失败',
+ error.message || '导入文件时发生错误'
+ );
+ }
}
- showTextWidgetSelector(prompt) {
- const textWidgets = this.embedder.getAllTextWidgets();
-
- if (textWidgets.length <= 1) {
- return false; // 不需要显示选择器
+ // 处理导出
+ async handleExport() {
+ try {
+ const success = this.promptManager.exportPrompts();
+
+ if (success) { await this.dialogComponents.showSuccessDialog(
+ '导出成功',
+ '提示词数据已导出到文件!',
+ 1500 // 缩短显示时间到1.5秒
+ );
+ }
+ } catch (error) {
+ console.error('导出失败:', error);
+ await this.dialogComponents.showErrorDialog(
+ '导出失败',
+ error.message || '导出文件时发生错误'
+ );
+ } } // 处理标签管理
+ handleTagManager() {
+ // 如果标签管理器已经显示,则隐藏它
+ if (this.tagManager.isVisible()) {
+ this.tagManager.hide();
+ return;
}
- // 创建选择器模态框
- const selectorModal = document.createElement('div');
- selectorModal.className = 'widget-selector-modal';
- selectorModal.innerHTML = `
-
- `;
-
- // 添加选择器样式
- this.addWidgetSelectorStyles();
-
- // 绑定事件
- selectorModal.querySelector('.close-btn').addEventListener('click', () => {
- document.body.removeChild(selectorModal);
- });
-
- selectorModal.querySelector('.widget-selector-overlay').addEventListener('click', (e) => {
- if (e.target === e.currentTarget) {
- document.body.removeChild(selectorModal);
- }
- });
-
- selectorModal.addEventListener('click', (e) => {
- if (e.target.classList.contains('widget-option')) {
- const index = parseInt(e.target.getAttribute('data-index'));
- const selectedWidget = textWidgets[index];
- this.embedder.applyPromptToWidget(prompt, selectedWidget);
- document.body.removeChild(selectorModal);
- }
- });
-
- document.body.appendChild(selectorModal);
- return true;
- }
-
- showInsertDialog(title, message, callback) {
- const insertModal = document.createElement('div');
- insertModal.className = 'confirm-dialog-modal';
- insertModal.innerHTML = `
-
-
-
-
-
${message}
-
-
-
插入 - 在光标位置插入
-
在当前光标位置或文本开头插入提示词
-
-
-
替换 - 替换全部内容
-
删除原有内容,使用新提示词替换
-
-
-
-
-
- `;
-
- // 添加确认对话框样式
- this.addConfirmDialogStyles();
-
- // 绑定事件
- insertModal.querySelector('.dialog-close-btn').addEventListener('click', () => {
- document.body.removeChild(insertModal);
- });
-
- insertModal.querySelectorAll('.option-card').forEach(card => {
- card.addEventListener('click', () => {
- const action = card.getAttribute('data-action');
- callback(action);
- document.body.removeChild(insertModal);
- });
- });
-
- insertModal.querySelector('.confirm-dialog-overlay').addEventListener('click', (e) => {
- if (e.target === e.currentTarget) {
- document.body.removeChild(insertModal);
- }
- });
-
- document.body.appendChild(insertModal);
- }
-
- showConfirmDialog(title, message, callback) {
- const confirmModal = document.createElement('div');
- confirmModal.className = 'confirm-dialog-modal';
- confirmModal.innerHTML = `
-
-
-
-
-
${message}
-
-
-
确定 - 追加到现有内容后面
-
保留原有内容,将新提示词添加到后面
-
-
-
取消 - 替换全部内容
-
删除原有内容,使用新提示词替换
-
-
-
-
-
- `;
-
- // 添加确认对话框样式
- this.addConfirmDialogStyles();
-
- // 绑定事件
- confirmModal.querySelector('.dialog-close-btn').addEventListener('click', () => {
- document.body.removeChild(confirmModal);
- });
-
- confirmModal.querySelectorAll('.option-card').forEach(card => {
- card.addEventListener('click', () => {
- const action = card.getAttribute('data-action');
- callback(action === 'append');
- document.body.removeChild(confirmModal);
- });
- });
-
- confirmModal.querySelector('.confirm-dialog-overlay').addEventListener('click', (e) => {
- if (e.target === e.currentTarget) {
- document.body.removeChild(confirmModal);
- }
- });
-
- document.body.appendChild(confirmModal);
- }
-
- showDeleteConfirmDialog(promptName, callback) {
- // 移除已存在的删除确认对话框
- const existingModal = document.querySelector('.delete-confirm-modal');
- if (existingModal) {
- existingModal.remove();
+ // 隐藏表单(如果正在显示)
+ if (this.promptForm.isVisible()) {
+ this.promptForm.hide();
}
-
- const confirmModal = document.createElement('div');
- confirmModal.className = 'delete-confirm-modal';
- confirmModal.innerHTML = `
-
-
-
-
-
确定要删除提示词 "${this.escapeHtml(promptName)}" 吗?
-
此操作不可撤销!
-
-
-
-
-
-
-
- `;
-
- this.addDeleteConfirmStyles();
-
- // 安全的事件绑定
- const closeBtn = confirmModal.querySelector('.delete-close-btn');
- const confirmBtn = confirmModal.querySelector('.delete-confirm-btn');
- const cancelBtn = confirmModal.querySelector('.delete-cancel-btn');
- const overlay = confirmModal.querySelector('.delete-confirm-overlay');
-
- const removeModal = () => {
- if (confirmModal.parentNode) {
- confirmModal.parentNode.removeChild(confirmModal);
- }
- };
-
- closeBtn.addEventListener('click', removeModal);
- cancelBtn.addEventListener('click', removeModal);
+ this.editingPrompt = null;
- confirmBtn.addEventListener('click', () => {
- try {
- callback();
- } catch (error) {
- console.error('删除操作失败:', error);
- } finally {
- removeModal();
- }
- });
-
- overlay.addEventListener('click', (e) => {
- if (e.target === overlay) {
- removeModal();
- }
- });
-
- document.body.appendChild(confirmModal);
+ // 显示标签管理器
+ this.tagManager.show();
}
- escapeHtml(text) {
- const div = document.createElement('div');
- div.textContent = text;
- return div.innerHTML;
- }
-
- addWidgetSelectorStyles() {
- if (document.getElementById('widget-selector-styles')) return;
-
- const styles = document.createElement('style');
- styles.id = 'widget-selector-styles';
- styles.textContent = `
- .widget-selector-modal {
- display: block;
- position: fixed;
- z-index: 10001;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- }
-
- .widget-selector-overlay {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.8);
+ // 添加布局样式
+ addLayoutStyles() {
+ const style = document.createElement('style');
+ style.textContent = ` .prompt-manager-layout {
display: flex;
- justify-content: center;
- align-items: center;
- }
-
- .widget-selector-container {
- background: #2a2a2a;
- border-radius: 8px;
- min-width: 400px;
- max-width: 500px;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
- }
-
- .widget-selector-header {
- background: #1a1a1a;
- padding: 15px 20px;
- border-radius: 8px 8px 0 0;
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
-
- .widget-selector-content {
- padding: 20px;
- }
-
- .widget-list {
+ height: 100%;
+ max-height: calc(100vh - 160px);
+ background: #1e1e1e;
+ border-radius: 0 0 16px 16px;
+ overflow: hidden;
+ position: relative;
+ } .prompt-manager-main {
+ width: 100%;
display: flex;
flex-direction: column;
- gap: 10px;
- margin-top: 15px;
- }
-
- .widget-option {
- background: #3a3a3a;
- border: 1px solid #555;
- color: white;
- padding: 12px 16px;
- border-radius: 4px;
- cursor: pointer;
- text-align: left;
- transition: background 0.2s;
- }
-
- .widget-option:hover {
- background: #4a4a4a;
- }
- `;
- document.head.appendChild(styles);
- }
-
- addConfirmDialogStyles() {
- if (document.getElementById('confirm-dialog-styles')) return;
-
- const styles = document.createElement('style');
- styles.id = 'confirm-dialog-styles';
- styles.textContent = `
- .confirm-dialog-modal {
- display: block;
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 10002;
+ overflow: hidden;
+ background: #1e1e1e;
+ position: relative;
+ transition: width 0.3s ease-out;
+ will-change: width;
+ transform: translateZ(0);
+ backface-visibility: hidden;
}
- .confirm-dialog-overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.8);
- display: flex;
- justify-content: center;
- align-items: center;
- }
-
- .confirm-dialog-container {
- background: linear-gradient(145deg, #2a2a2a, #1f1f1f);
- border: 1px solid #404040;
- border-radius: 12px;
- min-width: 400px;
- max-width: 500px;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
- animation: confirmDialogFadeIn 0.3s ease-out;
- }
-
- @keyframes confirmDialogFadeIn {
- from {
- opacity: 0;
- transform: scale(0.9) translateY(-20px);
- }
- to {
- opacity: 1;
- transform: scale(1) translateY(0);
- }
- }
-
- .confirm-dialog-header {
- background: linear-gradient(145deg, #333333, #2a2a2a);
- padding: 16px 20px;
- border-radius: 12px 12px 0 0;
- border-bottom: 1px solid #404040;
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
-
- .confirm-dialog-header h4 {
- margin: 0;
- color: #ffffff;
- font-size: 16px;
- font-weight: 600;
- }
-
- .dialog-close-btn {
- background: none;
- border: none;
- color: #999999;
- font-size: 20px;
- cursor: pointer;
- padding: 4px;
- width: 28px;
- height: 28px;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 4px;
- transition: all 0.2s ease;
- }
-
- .dialog-close-btn:hover {
- background: #ff5733;
- color: #ffffff;
- transform: scale(1.1);
- }
-
- .confirm-dialog-content {
- padding: 20px;
- color: #e0e0e0;
- }
-
- .confirm-dialog-content p {
- margin: 0 0 15px 0;
- font-size: 14px;
- line-height: 1.5;
- }
-
- .confirm-dialog-options {
- display: flex;
- flex-direction: column;
- gap: 12px;
- margin-top: 15px;
- }
-
- .option-card {
- background: linear-gradient(145deg, #2a2a2a, #1f1f1f);
- border: 2px solid #404040;
- border-radius: 8px;
- padding: 16px;
- cursor: pointer;
- transition: all 0.3s ease;
+ /* 当表单显示时,左侧区域收缩到固定宽度 */
+ .prompt-manager-layout.form-expanded .prompt-manager-main {
+ width: 800px;
+ } .prompt-manager-form {
+ width: 0;
+ background: linear-gradient(145deg, #2a2a2a, #232323);
+ border-left: 1px solid rgba(255, 255, 255, 0.1);
position: relative;
overflow: hidden;
+ opacity: 0;
+ transition: width 0.3s ease-out,
+ opacity 0.3s ease-out;
+ will-change: width, opacity;
+ transform: translateZ(0);
+ backface-visibility: hidden;
}
- .option-card::before {
+ /* 当表单显示时,右侧区域展开到400px宽度 */
+ .prompt-manager-layout.form-expanded .prompt-manager-form {
+ width: 400px;
+ opacity: 1;
+ } /* 硬件加速优化 */
+ .prompt-manager-layout,
+ .prompt-manager-main,
+ .prompt-manager-form {
+ backface-visibility: hidden;
+ perspective: 1000px;
+ transform: translateZ(0);
+ }
+
+ .prompt-manager-form::before {
content: '';
position: absolute;
top: 0;
- left: -100%;
- width: 100%;
+ left: 0;
+ width: 1px;
height: 100%;
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
- transition: left 0.5s ease;
+ background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+ z-index: 2;
}
- .option-card:hover {
- border-color: #2a82e4;
- background: linear-gradient(145deg, #333333, #2a2a2a);
- transform: translateY(-2px);
- box-shadow: 0 8px 16px rgba(42, 130, 228, 0.2);
+ .prompt-manager-form .prompt-form-section {
+ border-top: none;
+ border-left: none;
+ height: 100%;
+ background: transparent;
}
- .option-card:hover::before {
- left: 100%;
+ /* 光滑滚动 */
+ .prompt-manager-main,
+ .prompt-manager-form {
+ scrollbar-width: thin;
+ scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
}
- .option-card:active {
- transform: translateY(0);
- box-shadow: 0 4px 8px rgba(42, 130, 228, 0.3);
+ .prompt-manager-main::-webkit-scrollbar,
+ .prompt-manager-form::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
}
- .option-title {
- font-size: 14px;
- font-weight: 600;
- color: #ffffff;
- margin-bottom: 6px;
+ .prompt-manager-main::-webkit-scrollbar-track,
+ .prompt-manager-form::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.1);
+ border-radius: 3px;
}
- .option-description {
- font-size: 12px;
- color: #c0c0c0;
- line-height: 1.4;
+ .prompt-manager-main::-webkit-scrollbar-thumb,
+ .prompt-manager-form::-webkit-scrollbar-thumb {
+ background: linear-gradient(145deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.1));
+ border-radius: 3px;
+ border: 1px solid rgba(255, 255, 255, 0.05);
}
- .option-card[data-action="append"] .option-title {
- color: #2ea043;
- }
-
- .option-card[data-action="replace"] .option-title {
- color: #f85149;
+ .prompt-manager-main::-webkit-scrollbar-thumb:hover,
+ .prompt-manager-form::-webkit-scrollbar-thumb:hover {
+ background: linear-gradient(145deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.2));
}
`;
- document.head.appendChild(styles);
- }
-
- addDeleteConfirmStyles() {
- if (document.getElementById('delete-confirm-styles')) return;
- const styles = document.createElement('style');
- styles.id = 'delete-confirm-styles';
- styles.textContent = `
- .delete-confirm-modal {
- display: block;
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 10003;
- }
-
- .delete-confirm-overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.8);
- display: flex;
- justify-content: center;
- align-items: center;
- }
-
- .delete-confirm-container {
- background: linear-gradient(145deg, #2a2a2a, #1f1f1f);
- border: 1px solid #404040;
- border-radius: 12px;
- min-width: 400px;
- max-width: 500px;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
- animation: deleteConfirmFadeIn 0.3s ease-out;
- }
-
- @keyframes deleteConfirmFadeIn {
- from {
- opacity: 0;
- transform: scale(0.9) translateY(-20px);
- }
- to {
- opacity: 1;
- transform: scale(1) translateY(0);
- }
- }
-
- .delete-confirm-header {
- background: linear-gradient(145deg, #333333, #2a2a2a);
- padding: 16px 20px;
- border-radius: 12px 12px 0 0;
- border-bottom: 1px solid #404040;
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
-
- .delete-confirm-header h4 {
- margin: 0;
- color: #ffffff;
- font-size: 16px;
- font-weight: 600;
- }
-
- .delete-close-btn {
- background: none;
- border: none;
- color: #999999;
- font-size: 20px;
- cursor: pointer;
- padding: 4px;
- width: 28px;
- height: 28px;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 4px;
- transition: all 0.2s ease;
- }
-
- .delete-close-btn:hover {
- background: #ff5733;
- color: #ffffff;
- transform: scale(1.1);
- }
-
- .delete-confirm-content {
- padding: 20px;
- color: #e0e0e0;
- }
-
- .delete-confirm-content p {
- margin: 0 0 15px 0;
- font-size: 14px;
- line-height: 1.5;
- }
-
- .warning-text {
- color: #ff9999;
- font-size: 12px;
- margin-top: 10px;
- margin-bottom: 0;
- }
-
- .delete-confirm-actions {
- display: flex;
- gap: 12px;
- justify-content: flex-end;
- margin-top: 20px;
- }
-
- .delete-confirm-btn {
- background: linear-gradient(145deg, #dc3545, #c82333);
- border: none;
- color: white;
- padding: 10px 20px;
- border-radius: 6px;
- cursor: pointer;
- font-weight: 600;
- font-size: 14px;
- transition: all 0.2s ease;
- }
-
- .delete-confirm-btn:hover {
- background: linear-gradient(145deg, #e74c3c, #dc3545);
- transform: translateY(-1px);
- box-shadow: 0 4px 8px rgba(220, 53, 69, 0.4);
- }
-
- .delete-cancel-btn {
- background: linear-gradient(145deg, #6c757d, #5a6268);
- border: none;
- color: white;
- padding: 10px 20px;
- border-radius: 6px;
- cursor: pointer;
- font-weight: 600;
- font-size: 14px;
- transition: all 0.2s ease;
- }
-
- .delete-cancel-btn:hover {
- background: linear-gradient(145deg, #7c8590, #6c757d);
- transform: translateY(-1px);
- box-shadow: 0 4px 8px rgba(108, 117, 125, 0.4);
- }
- `;
- document.head.appendChild(styles);
+ if (!document.head.querySelector('style[data-component="prompt-manager-layout"]')) {
+ style.setAttribute('data-component', 'prompt-manager-layout');
+ document.head.appendChild(style);
+ }
}
-}
+
+ // 销毁组件
+ destroy() {
+ // 销毁所有子组件
+ if (this.mainModal) {
+ this.mainModal.destroy();
+ }
+
+ if (this.searchBar) {
+ this.searchBar.destroy();
+ }
+
+ if (this.promptList) {
+ this.promptList.destroy();
+ }
+
+ if (this.promptForm) {
+ this.promptForm.destroy();
+ }
+ if (this.dialogComponents) {
+ this.dialogComponents.destroy();
+ }
+
+ if (this.tagManager) {
+ this.tagManager.destroy();
+ }
+
+ // 移除布局样式
+ const style = document.head.querySelector('style[data-component="prompt-manager-layout"]');
+ if (style) {
+ style.remove();
+ }
+
+ // 重置状态
+ this.isVisible = false;
+ this.editingPrompt = null;
+ this.currentSearchKeyword = '';
+ }
+
+ // 获取当前状态信息
+ getState() {
+ return {
+ isVisible: this.isVisible,
+ currentSearchKeyword: this.currentSearchKeyword,
+ editingPrompt: this.editingPrompt,
+ totalPrompts: this.promptManager.getPrompts().length
+ };
+ }
+
+ // 设置搜索关键词(外部调用)
+ setSearchKeyword(keyword) {
+ this.searchBar.setKeyword(keyword);
+ }
+
+ // 获取当前搜索关键词
+ getSearchKeyword() {
+ return this.currentSearchKeyword;
+ } // 隐藏管理器
+ hide() {
+ this.isVisible = false;
+ this.hideForm(); // 先隐藏表单
+
+ // 隐藏标签管理器(如果正在显示)
+ if (this.tagManager && this.tagManager.isVisible()) {
+ this.tagManager.hide();
+ }
+
+ this.mainModal.hide();
+ }
+
+ // 切换显示状态
+ toggle() {
+ if (this.isVisible) {
+ this.hide();
+ } else {
+ this.show();
+ }
+ } // 刷新提示词列表
+ refreshPromptList() {
+ // 获取所有提示词用于标签更新
+ const allPrompts = this.promptManager.getPrompts();
+
+ // 更新搜索栏的标签
+ this.searchBar.updateTags(allPrompts);
+ // 设置原始数据,然后应用搜索关键词和标签筛选
+ this.promptList.setPrompts(allPrompts);
+ if (this.currentSearchKeyword || (this.searchBar && this.searchBar.getSelectedTags().length > 0)) {
+ const selectedTags = this.searchBar ? this.searchBar.getSelectedTags() : [];
+ this.promptList.filterPrompts(this.currentSearchKeyword, selectedTags);
+ }
+ } // 新增:刷新所有组件的标签颜色和标签列表
+ refreshAllTagColors() {
+ console.log('开始刷新所有组件的标签颜色和标签列表...');
+
+ // 首先刷新提示词列表的数据(确保已删除的标签从提示词中移除)
+ console.log('刷新提示词列表数据...');
+ this.refreshPromptList();
+
+ // 刷新搜索栏中的标签列表和颜色(标签删除后需要重新生成标签列表)
+ if (this.searchBar) {
+ if (typeof this.searchBar.refreshTags === 'function') {
+ console.log('刷新搜索栏标签列表...');
+ this.searchBar.refreshTags();
+ }
+ if (typeof this.searchBar.refreshTagColors === 'function') {
+ console.log('刷新搜索栏标签颜色...');
+ this.searchBar.refreshTagColors();
+ }
+ }
+
+ // 刷新列表中的标签颜色(此时数据已经是最新的了)
+ if (this.promptList && typeof this.promptList.refreshTagColors === 'function') {
+ console.log('刷新提示词列表标签颜色...');
+ this.promptList.refreshTagColors();
+ }
+
+ // 刷新表单中的标签颜色
+ if (this.promptForm && typeof this.promptForm.refreshTagColors === 'function') {
+ console.log('刷新提示词表单标签颜色...');
+ this.promptForm.refreshTagColors();
+ }
+
+ console.log('所有组件标签颜色和标签列表刷新完成');
+ }
+ // 显示插入模式选择对话框
+ async showInsertDialog(title, message, callback) {
+ try {
+ const result = await this.dialogComponents.showInsertModeDialog(title, message);
+ if (callback && typeof callback === 'function') {
+ callback(result);
+ }
+ return result;
+ } catch (error) {
+ console.error('显示插入对话框失败:', error);
+ // 如果对话框失败,默认使用替换模式
+ if (callback && typeof callback === 'function') {
+ callback('replace');
+ }
+ return 'replace';
+ }
+ }
+
+ // 显示文本组件选择器对话框
+ async showTextWidgetSelector(prompt) {
+ try {
+ const textWidgets = this.embedder.getAllTextWidgets();
+
+ if (textWidgets.length === 0) {
+ await this.dialogComponents.showErrorDialog('错误', '未找到可用的文本输入框');
+ return;
+ }
+
+ if (textWidgets.length === 1) {
+ // 只有一个,直接应用
+ this.embedder.applyPromptToWidget(prompt, textWidgets[0]);
+ return;
+ }
+
+ const selectedWidget = await this.dialogComponents.showWidgetSelector(
+ textWidgets,
+ (widget) => {
+ // 生成显示名称
+ const name = widget.name || widget.options?.property || '未命名';
+ const index = textWidgets.indexOf(widget) + 1;
+ return `${name} (输入框 ${index})`;
+ }
+ );
+
+ if (selectedWidget) {
+ this.embedder.applyPromptToWidget(prompt, selectedWidget);
+ }
+ } catch (error) {
+ console.error('显示文本组件选择器失败:', error);
+ await this.dialogComponents.showErrorDialog('错误', '显示选择器时发生错误');
+ }
+ }
+}
\ No newline at end of file
diff --git a/extensions/Prompt_Manager/styles/components/DialogComponents.js b/extensions/Prompt_Manager/styles/components/DialogComponents.js
new file mode 100644
index 0000000..4da421e
--- /dev/null
+++ b/extensions/Prompt_Manager/styles/components/DialogComponents.js
@@ -0,0 +1,705 @@
+// 对话框组件 - 负责各种确认对话框和选择器
+
+export class DialogComponents {
+ constructor() {
+ this.addStyles();
+ }
+
+ // 删除确认对话框
+ showDeleteConfirm(promptName, onConfirm) {
+ return new Promise((resolve) => {
+ const modal = this.createModal('delete-confirm-modal');
+ modal.innerHTML = `
+
+
+
+
+
确定要删除提示词 "${this.escapeHtml(promptName)}" 吗?
+
此操作不可撤销!
+
+
+
+
+
+
+
+ `;
+
+ this.bindDialogEvents(modal, {
+ '.delete-confirm-btn': () => {
+ if (onConfirm) onConfirm();
+ resolve(true);
+ this.closeModal(modal);
+ },
+ '.delete-cancel-btn': () => {
+ resolve(false);
+ this.closeModal(modal);
+ }
+ });
+
+ this.showModal(modal);
+ });
+ } // 插入模式选择对话框
+ showInsertModeDialog(title = '选择插入模式', message = '检测到输入框中已有内容,请选择插入模式:') {
+ return new Promise((resolve) => {
+ let resolved = false; // 防止多次resolve
+
+ const modal = this.createModal('insert-mode-modal');
+ modal.innerHTML = `
+
+
+
+
+
${message}
+
+
+
📝
+
插入模式
+
在光标位置或文本开头插入提示词
+
+
+
➕
+
追加模式
+
在现有内容后面添加提示词
+
+
+
🔄
+
替换模式
+
删除原有内容,使用新提示词替换
+
+
+
+
+
+ `;
+
+ const resolveOnce = (value) => {
+ if (!resolved) {
+ resolved = true;
+ this.closeModal(modal);
+ resolve(value);
+ }
+ };
+
+ this.bindDialogEvents(modal, {
+ '.option-card': (e) => {
+ const action = e.currentTarget.getAttribute('data-action');
+ console.log('插入模式选择:', action); // 调试信息
+ resolveOnce(action);
+ },
+ '.dialog-close-btn': () => {
+ console.log('对话框关闭按钮点击'); // 调试信息
+ resolveOnce(null);
+ }
+ });
+
+ this.showModal(modal);
+ });
+ }
+
+ // 文本组件选择器对话框
+ showWidgetSelector(widgets, getDisplayName) {
+ return new Promise((resolve) => {
+ if (!Array.isArray(widgets) || widgets.length <= 1) {
+ resolve(widgets[0] || null);
+ return;
+ }
+
+ const modal = this.createModal('widget-selector-modal');
+ modal.innerHTML = `
+
+ `;
+
+ this.bindDialogEvents(modal, {
+ '.widget-option': (e) => {
+ const index = parseInt(e.currentTarget.getAttribute('data-index'));
+ resolve(widgets[index]);
+ this.closeModal(modal);
+ }
+ });
+
+ this.showModal(modal);
+ });
+ }
+
+ // 导入文件选择对话框
+ showFileImportDialog(accept = '.json') {
+ return new Promise((resolve) => {
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = accept;
+ input.style.display = 'none';
+
+ input.addEventListener('change', (e) => {
+ const file = e.target.files[0];
+ resolve(file);
+ document.body.removeChild(input);
+ });
+
+ input.addEventListener('cancel', () => {
+ resolve(null);
+ document.body.removeChild(input);
+ });
+
+ document.body.appendChild(input);
+ input.click();
+ });
+ }
+
+ // 成功提示对话框
+ showSuccessDialog(title, message, autoClose = 3000) {
+ return new Promise((resolve) => {
+ const modal = this.createModal('success-dialog-modal');
+ modal.innerHTML = `
+
+
+
+
+
${message}
+ ${autoClose ? `
+
+
对话框将在 ${Math.ceil(autoClose / 1000)} 秒后自动关闭
+
` : ''}
+
+
+
+ `;
+
+ this.bindDialogEvents(modal, {
+ '.dialog-close-btn': () => {
+ resolve();
+ this.closeModal(modal);
+ }
+ });
+
+ this.showModal(modal);
+
+ // 自动关闭
+ if (autoClose) {
+ setTimeout(() => {
+ resolve();
+ this.closeModal(modal);
+ }, autoClose);
+ }
+ });
+ }
+
+ // 错误提示对话框
+ showErrorDialog(title, message) {
+ return new Promise((resolve) => {
+ const modal = this.createModal('error-dialog-modal');
+ modal.innerHTML = `
+
+ `;
+
+ this.bindDialogEvents(modal, {
+ '.error-ok-btn': () => {
+ resolve();
+ this.closeModal(modal);
+ }
+ });
+
+ this.showModal(modal);
+ });
+ }
+
+ // 通用确认对话框
+ showConfirmDialog(title, message, confirmText = '确定', cancelText = '取消') {
+ return new Promise((resolve) => {
+ const modal = this.createModal('confirm-dialog-modal');
+ modal.innerHTML = `
+
+
+
+
+
${message}
+
+
+
+
+
+
+
+ `;
+
+ this.bindDialogEvents(modal, {
+ '.confirm-ok-btn': () => {
+ resolve(true);
+ this.closeModal(modal);
+ },
+ '.confirm-cancel-btn': () => {
+ resolve(false);
+ this.closeModal(modal);
+ }
+ });
+
+ this.showModal(modal);
+ });
+ }
+
+ // 工具方法
+ createModal(className) {
+ // 移除已存在的相同类型对话框
+ const existing = document.querySelector(`.${className}`);
+ if (existing) {
+ existing.remove();
+ }
+
+ const modal = document.createElement('div');
+ modal.className = className;
+ return modal;
+ }
+
+ showModal(modal) {
+ document.body.appendChild(modal);
+ // 添加显示动画
+ requestAnimationFrame(() => {
+ modal.classList.add('show');
+ });
+ }
+
+ closeModal(modal) {
+ modal.classList.remove('show');
+ setTimeout(() => {
+ if (modal.parentNode) {
+ modal.parentNode.removeChild(modal);
+ }
+ }, 300);
+ } bindDialogEvents(modal, eventMap) {
+ // 绑定自定义事件
+ Object.entries(eventMap).forEach(([selector, handler]) => {
+ const elements = modal.querySelectorAll(selector);
+ elements.forEach(element => {
+ element.addEventListener('click', handler);
+ });
+ });
+
+ // 只有在没有自定义关闭按钮处理时才绑定通用关闭事件
+ if (!eventMap['.dialog-close-btn']) {
+ const closeBtn = modal.querySelector('.dialog-close-btn');
+ if (closeBtn) {
+ closeBtn.addEventListener('click', () => this.closeModal(modal));
+ }
+ }
+ const handleKeydown = (e) => {
+ if (e.key === 'Escape') {
+ // 如果有自定义关闭处理,使用它;否则直接关闭
+ if (eventMap['.dialog-close-btn']) {
+ eventMap['.dialog-close-btn']();
+ } else {
+ this.closeModal(modal);
+ }
+ document.removeEventListener('keydown', handleKeydown);
+ }
+ };
+ document.addEventListener('keydown', handleKeydown);
+ }
+
+ escapeHtml(text) {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+ }
+
+ truncateText(text, maxLength) {
+ if (!text || text.length <= maxLength) return text;
+ return text.substring(0, maxLength) + '...';
+ }
+
+ addStyles() {
+ const style = document.createElement('style');
+ style.textContent = `
+ /* 通用对话框样式 */
+ .delete-confirm-modal,
+ .insert-mode-modal,
+ .widget-selector-modal,
+ .success-dialog-modal,
+ .error-dialog-modal,
+ .confirm-dialog-modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 20000;
+ opacity: 0;
+ visibility: hidden;
+ transition: opacity 0.3s ease, visibility 0.3s ease;
+ }
+
+ .delete-confirm-modal.show,
+ .insert-mode-modal.show,
+ .widget-selector-modal.show,
+ .success-dialog-modal.show,
+ .error-dialog-modal.show,
+ .confirm-dialog-modal.show {
+ opacity: 1;
+ visibility: visible;
+ }
+
+ .dialog-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.8);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ backdrop-filter: blur(2px);
+ }
+
+ .dialog-container {
+ background: #2b2b2b;
+ border-radius: 12px;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
+ max-width: 500px;
+ width: 90%;
+ max-height: 80vh;
+ overflow: hidden;
+ transform: scale(0.9);
+ transition: transform 0.3s ease;
+ }
+
+ .delete-confirm-modal.show .dialog-container,
+ .insert-mode-modal.show .dialog-container,
+ .widget-selector-modal.show .dialog-container,
+ .success-dialog-modal.show .dialog-container,
+ .error-dialog-modal.show .dialog-container,
+ .confirm-dialog-modal.show .dialog-container {
+ transform: scale(1);
+ }
+
+ .dialog-header {
+ padding: 20px 24px;
+ border-bottom: 1px solid #404040;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background: linear-gradient(135deg, #3a3a3a 0%, #2b2b2b 100%);
+ }
+
+ .dialog-header.success {
+ background: linear-gradient(135deg, #43cf7c 0%, #38b86a 100%);
+ }
+
+ .dialog-header.error {
+ background: linear-gradient(135deg, #ff5733 0%, #e04929 100%);
+ }
+
+ .dialog-header h4 {
+ margin: 0;
+ color: #ffffff;
+ font-size: 16px;
+ font-weight: 600;
+ }
+
+ .dialog-close-btn {
+ background: none;
+ border: none;
+ color: #fff;
+ font-size: 20px;
+ cursor: pointer;
+ padding: 4px;
+ width: 28px;
+ height: 28px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 6px;
+ transition: all 0.2s ease;
+ }
+
+ .dialog-close-btn:hover {
+ background-color: rgba(255, 255, 255, 0.2);
+ }
+
+ .dialog-content {
+ padding: 24px;
+ color: #e0e0e0;
+ line-height: 1.6;
+ }
+
+ .dialog-content p {
+ margin: 0 0 16px 0;
+ }
+
+ .warning-text {
+ color: #ff8c42;
+ font-weight: 500;
+ font-size: 14px;
+ }
+
+ .dialog-actions {
+ display: flex;
+ gap: 12px;
+ margin-top: 20px;
+ }
+
+ .btn-primary,
+ .btn-secondary,
+ .btn-danger {
+ padding: 10px 20px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: 500;
+ transition: all 0.2s ease;
+ flex: 1;
+ }
+
+ .btn-primary {
+ background: #2a82e4;
+ color: white;
+ }
+
+ .btn-primary:hover {
+ background: #1c6bb7;
+ transform: translateY(-1px);
+ }
+
+ .btn-secondary {
+ background: #6c757d;
+ color: white;
+ }
+
+ .btn-secondary:hover {
+ background: #5a6268;
+ transform: translateY(-1px);
+ }
+
+ .btn-danger {
+ background: #ff5733;
+ color: white;
+ }
+
+ .btn-danger:hover {
+ background: #e04929;
+ transform: translateY(-1px);
+ }
+
+ /* 选项卡片样式 */
+ .option-cards {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ margin-top: 16px;
+ }
+
+ .option-card {
+ background: #1a1a1a;
+ border: 2px solid #404040;
+ border-radius: 8px;
+ padding: 16px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ }
+
+ .option-card:hover {
+ border-color: #2a82e4;
+ background: #262626;
+ transform: translateY(-2px);
+ }
+
+ .option-icon {
+ font-size: 24px;
+ flex-shrink: 0;
+ }
+
+ .option-title {
+ font-weight: 600;
+ color: #ffffff;
+ margin-bottom: 4px;
+ }
+
+ .option-description {
+ font-size: 13px;
+ color: #888;
+ line-height: 1.4;
+ }
+
+ /* 组件选择器样式 */
+ .widget-list {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin-top: 16px;
+ }
+
+ .widget-option {
+ background: #1a1a1a;
+ border: 2px solid #404040;
+ border-radius: 8px;
+ padding: 12px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ text-align: left;
+ color: #e0e0e0;
+ }
+
+ .widget-option:hover {
+ border-color: #2a82e4;
+ background: #262626;
+ }
+
+ .widget-info {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ }
+
+ .widget-name {
+ font-weight: 500;
+ color: #ffffff;
+ }
+
+ .widget-preview {
+ font-size: 12px;
+ color: #888;
+ font-family: monospace;
+ }
+
+ /* 自动关闭指示器 */
+ .auto-close-indicator {
+ margin-top: 16px;
+ padding-top: 16px;
+ border-top: 1px solid #404040;
+ }
+
+ .progress-bar {
+ width: 100%;
+ height: 3px;
+ background: #404040;
+ border-radius: 2px;
+ overflow: hidden;
+ margin-bottom: 8px;
+ }
+
+ .progress-fill {
+ height: 100%;
+ background: #43cf7c;
+ width: 100%;
+ animation: shrink linear forwards;
+ transform-origin: right;
+ }
+
+ @keyframes shrink {
+ from {
+ transform: scaleX(1);
+ }
+ to {
+ transform: scaleX(0);
+ }
+ }
+
+ /* 响应式设计 */
+ @media (max-width: 768px) {
+ .dialog-container {
+ margin: 20px;
+ max-width: none;
+ width: calc(100% - 40px);
+ }
+
+ .dialog-content {
+ padding: 16px;
+ }
+
+ .option-cards {
+ gap: 8px;
+ }
+
+ .option-card {
+ padding: 12px;
+ flex-direction: column;
+ text-align: center;
+ gap: 8px;
+ }
+
+ .dialog-actions {
+ flex-direction: column;
+ }
+ }
+ `;
+
+ if (!document.head.querySelector('style[data-component="dialog-components"]')) {
+ style.setAttribute('data-component', 'dialog-components');
+ document.head.appendChild(style);
+ }
+ }
+
+ destroy() {
+ // 移除所有对话框
+ const dialogs = document.querySelectorAll(`
+ .delete-confirm-modal,
+ .insert-mode-modal,
+ .widget-selector-modal,
+ .success-dialog-modal,
+ .error-dialog-modal,
+ .confirm-dialog-modal
+ `);
+
+ dialogs.forEach(dialog => {
+ if (dialog.parentNode) {
+ dialog.parentNode.removeChild(dialog);
+ }
+ });
+
+ // 移除样式
+ const style = document.head.querySelector('style[data-component="dialog-components"]');
+ if (style) {
+ style.remove();
+ }
+ }
+}
diff --git a/extensions/Prompt_Manager/styles/components/MainModal.js b/extensions/Prompt_Manager/styles/components/MainModal.js
new file mode 100644
index 0000000..2c29657
--- /dev/null
+++ b/extensions/Prompt_Manager/styles/components/MainModal.js
@@ -0,0 +1,261 @@
+// 主模态框组件 - 负责整体模态框结构和基础样式
+
+export class MainModal {
+ constructor(title = '嵌入提示词管理') {
+ this.title = title;
+ this.modal = null;
+ this.isVisible = false;
+ this.createModal();
+ this.bindEvents();
+ }
+
+ createModal() {
+ this.modal = document.createElement('div');
+ this.modal.className = 'prompt-embedder-modal';
+ this.modal.innerHTML = `
+
+ `;
+
+ // 添加基础样式
+ this.addStyles();
+
+ // 隐藏模态框
+ this.modal.style.display = 'none';
+ document.body.appendChild(this.modal);
+ } bindEvents() {
+ // 关闭按钮事件
+ const closeBtn = this.modal.querySelector('.close-btn');
+ closeBtn.addEventListener('click', () => this.hide());
+
+ // 移除点击遮罩层关闭功能,防止误点击
+ // const overlay = this.modal.querySelector('.prompt-embedder-overlay');
+ // overlay.addEventListener('click', (e) => {
+ // if (e.target === overlay) {
+ // this.hide();
+ // }
+ // });
+
+ // ESC键关闭(保留快捷键)
+ document.addEventListener('keydown', (e) => {
+ if (e.key === 'Escape' && this.isVisible) {
+ this.hide();
+ }
+ });
+ }
+
+ show() {
+ this.modal.style.display = 'block';
+ this.isVisible = true;
+
+ // 添加显示动画
+ requestAnimationFrame(() => {
+ this.modal.classList.add('show');
+ });
+ }
+
+ hide() {
+ this.modal.classList.remove('show');
+ this.isVisible = false;
+
+ // 等待动画完成后隐藏
+ setTimeout(() => {
+ this.modal.style.display = 'none';
+ }, 300);
+ }
+
+ toggle() {
+ if (this.isVisible) {
+ this.hide();
+ } else {
+ this.show();
+ }
+ }
+
+ getContentContainer() {
+ return this.modal.querySelector('.prompt-embedder-content');
+ }
+
+ setTitle(title) {
+ this.title = title;
+ const titleElement = this.modal.querySelector('.prompt-embedder-header h3');
+ if (titleElement) {
+ titleElement.textContent = title;
+ }
+ }
+
+ addStyles() {
+ const style = document.createElement('style');
+ style.textContent = `
+ .prompt-embedder-modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 10000;
+ opacity: 0;
+ visibility: hidden;
+ transition: opacity 0.3s ease, visibility 0.3s ease;
+ }
+
+ .prompt-embedder-modal.show {
+ opacity: 1;
+ visibility: visible;
+ } .prompt-embedder-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(135deg, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.6));
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ backdrop-filter: blur(8px);
+ padding: 20px;
+ box-sizing: border-box;
+ overflow-y: auto;
+ } .prompt-embedder-container {
+ background: linear-gradient(145deg, #2d2d30, #252528);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+ width: 100%;
+ max-width: 1200px;
+ max-height: calc(100vh - 40px);
+ box-shadow:
+ 0 32px 64px rgba(0, 0, 0, 0.4),
+ 0 16px 32px rgba(0, 0, 0, 0.2),
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ transform: scale(0.95) translateY(20px);
+ transition: all 0.4s ease-out;
+ position: relative;
+ margin: auto;
+ box-sizing: border-box;
+ }.prompt-embedder-modal.show .prompt-embedder-container {
+ transform: scale(1) translateY(0);
+ }
+
+ .prompt-embedder-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 24px 32px 20px;
+ background: linear-gradient(135deg, #3a3a3d, #2d2d30);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ position: relative;
+ z-index: 2;
+ }
+
+ .prompt-embedder-header::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+ } .prompt-embedder-header h3 {
+ color: #ffffff;
+ font-size: 20px;
+ font-weight: 600;
+ margin: 0;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
+ letter-spacing: 0.5px;
+ }
+
+ .close-btn {
+ background: none;
+ border: none;
+ font-size: 28px;
+ color: #888;
+ cursor: pointer;
+ padding: 8px;
+ width: 40px;
+ height: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 8px;
+ transition: all 0.3s ease-out;
+ position: relative;
+ overflow: hidden;
+ }
+
+ .close-btn::before {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 0;
+ height: 0;
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.2) 0%, transparent 70%);
+ border-radius: 50%;
+ transform: translate(-50%, -50%);
+ transition: all 0.3s ease;
+ z-index: -1;
+ }
+
+ .close-btn:hover {
+ color: #fff;
+ transform: scale(1.1);
+ }
+
+ .close-btn:hover::before {
+ width: 100%;
+ height: 100%;
+ }
+
+ .close-btn:active {
+ transform: scale(0.95);
+ } .prompt-embedder-content {
+ flex: 1;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ background: #1e1e1e;
+ position: relative;
+ }
+
+ .prompt-embedder-content::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
+ z-index: 1;
+ }
+ `;
+
+ if (!document.head.querySelector('style[data-component="main-modal"]')) {
+ style.setAttribute('data-component', 'main-modal');
+ document.head.appendChild(style);
+ }
+ }
+
+ destroy() {
+ if (this.modal && this.modal.parentNode) {
+ this.modal.parentNode.removeChild(this.modal);
+ }
+
+ // 移除样式
+ const style = document.head.querySelector('style[data-component="main-modal"]');
+ if (style) {
+ style.remove();
+ }
+ }
+}
diff --git a/extensions/Prompt_Manager/styles/components/PromptForm.js b/extensions/Prompt_Manager/styles/components/PromptForm.js
new file mode 100644
index 0000000..893a12a
--- /dev/null
+++ b/extensions/Prompt_Manager/styles/components/PromptForm.js
@@ -0,0 +1,1076 @@
+// 表单组件 - 负责添加/编辑提示词的表单
+
+import { globalTagColorManager } from './TagColorManager.js';
+
+export class PromptForm {
+ constructor(options = {}) {
+ this.onSubmit = options.onSubmit || null;
+ this.onCancel = options.onCancel || null;
+ this.getPrompts = options.getPrompts || null; // 新增:获取提示词数据的回调
+ this.isEditing = false;
+ this.editingPrompt = null;
+
+ this.container = null;
+ this.form = null;
+ this.titleElement = null;
+ this.createForm();
+ } createForm() { this.container = document.createElement('div');
+ this.container.className = 'prompt-form-section';
+ // 初始状态:隐藏表单内容
+ this.container.style.visibility = 'hidden';
+ this.container.innerHTML = `
+
+
+ `;
+
+ this.form = this.container.querySelector('.prompt-form');
+ this.titleElement = this.container.querySelector('.form-title'); this.bindEvents();
+ this.addStyles();
+ }
+
+ bindEvents() {
+ // 表单提交
+ this.form.addEventListener('submit', (e) => {
+ e.preventDefault();
+ this.handleSubmit();
+ });
+
+ // 取消按钮
+ const cancelBtn = this.container.querySelector('.cancel-btn');
+ cancelBtn.addEventListener('click', () => {
+ this.handleCancel();
+ });
+
+ // 输入验证
+ this.bindValidationEvents();
+
+ // 标签输入处理
+ this.bindTagsInput();
+
+ // ESC键取消
+ this.container.addEventListener('keydown', (e) => {
+ if (e.key === 'Escape') {
+ this.handleCancel();
+ }
+ });
+ }
+
+ bindValidationEvents() {
+ const nameInput = this.form.querySelector('input[name="name"]');
+ const contentInput = this.form.querySelector('textarea[name="content"]');
+
+ // 实时验证
+ nameInput.addEventListener('input', () => this.validateInput(nameInput));
+ contentInput.addEventListener('input', () => this.validateInput(contentInput));
+
+ // 失焦验证
+ nameInput.addEventListener('blur', () => this.validateInput(nameInput));
+ contentInput.addEventListener('blur', () => this.validateInput(contentInput));
+ } bindTagsInput() {
+ const tagsInput = this.form.querySelector('input[name="tags"]');
+ const tagsSelectorBtn = this.form.querySelector('.tags-selector-btn');
+ const availableTagsContainer = this.form.querySelector('.available-tags');
+
+ tagsInput.addEventListener('input', (e) => {
+ const value = e.target.value;
+ const tags = this.parseTags(value);
+
+ e.target.classList.remove('error');
+ this.hideInputError(e.target);
+
+ // 实时更新标签预览
+ this.updateTagsPreview();
+ });
+
+ // 标签选择按钮
+ tagsSelectorBtn.addEventListener('click', () => {
+ this.toggleAvailableTags();
+ }); }
+
+ // 辅助方法:将十六进制颜色转换为RGB
+ hexToRgb(hex) {
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ return result ? {
+ r: parseInt(result[1], 16),
+ g: parseInt(result[2], 16),
+ b: parseInt(result[3], 16)
+ } : null;
+ }
+
+ validateInput(input) {
+ const value = input.value.trim();
+
+ if (input.hasAttribute('required') && !value) {
+ input.classList.add('error');
+ this.showInputError(input, '此字段为必填项');
+ return false;
+ } else {
+ input.classList.remove('error');
+ this.hideInputError(input);
+ return true;
+ }
+ }
+
+ showInputError(input, message) {
+ let errorElement = input.parentNode.querySelector('.error-message');
+ if (!errorElement) {
+ errorElement = document.createElement('small');
+ errorElement.className = 'error-message';
+ input.parentNode.appendChild(errorElement);
+ }
+ errorElement.textContent = message;
+ }
+
+ hideInputError(input) {
+ const errorElement = input.parentNode.querySelector('.error-message');
+ if (errorElement) {
+ errorElement.remove();
+ }
+ } updateTagsPreview() {
+ const tagsInput = this.form.querySelector('input[name="tags"]');
+ const tags = this.parseTags(tagsInput.value);
+
+ // 找到标签输入框所在的form-group容器
+ const tagsFormGroup = tagsInput.closest('.form-group');
+
+ let previewElement = tagsFormGroup.querySelector('.tags-preview');
+ if (!previewElement) {
+ previewElement = document.createElement('div');
+ previewElement.className = 'tags-preview';
+ // 将预览元素添加到form-group的末尾
+ tagsFormGroup.appendChild(previewElement);
+ }if (tags.length > 0) {
+ previewElement.innerHTML = `
+
标签预览:
+
+ ${tags.map(tag => {
+ const color = globalTagColorManager.getTagColor(tag);
+ const colorRGB = globalTagColorManager.getTagColorRGB(tag);
+ return `${this.escapeHtml(tag)}`;
+ }).join('')}
+
+ `;
+ previewElement.style.display = 'block';
+ } else {
+ previewElement.style.display = 'none';
+ }
+ }
+
+ handleSubmit() {
+ const formData = this.getFormData();
+
+ // 验证表单
+ if (!this.validateForm(formData)) {
+ return;
+ }
+
+ if (this.onSubmit) {
+ this.onSubmit(formData, this.isEditing, this.editingPrompt);
+ }
+ }
+
+ handleCancel() {
+ if (this.onCancel) {
+ this.onCancel();
+ }
+ this.hide();
+ }
+
+ validateForm(formData) {
+ let isValid = true;
+
+ // 验证名称
+ if (!formData.name.trim()) {
+ const nameInput = this.form.querySelector('input[name="name"]');
+ this.showInputError(nameInput, '请输入提示词名称');
+ nameInput.classList.add('error');
+ isValid = false;
+ }
+
+ // 验证内容
+ if (!formData.content.trim()) {
+ const contentInput = this.form.querySelector('textarea[name="content"]');
+ this.showInputError(contentInput, '请输入提示词内容');
+ contentInput.classList.add('error');
+ isValid = false;
+ } // 验证标签数量 - 已移除限制,允许任意数量的标签
+
+ return isValid;
+ }
+
+ getFormData() {
+ const formData = new FormData(this.form);
+ const tagsValue = formData.get('tags') || '';
+
+ return {
+ name: formData.get('name').trim(),
+ content: formData.get('content').trim(),
+ tags: this.parseTags(tagsValue)
+ };
+ } parseTags(tagsString) {
+ if (!tagsString) return [];
+
+ return tagsString
+ .split(',')
+ .map(tag => tag.trim())
+ .filter(tag => tag.length > 0 && tag.length <= 20); // 只限制单个标签长度,不限制标签数量
+ } show(mode = 'add', prompt = null) {
+ this.isEditing = mode === 'edit';
+ this.editingPrompt = prompt;
+
+ if (this.isEditing && prompt) {
+ this.titleElement.textContent = '编辑提示词';
+ this.populateForm(prompt);
+ } else {
+ this.titleElement.textContent = '添加提示词';
+ this.clearForm();
+ }
+
+ // 确保"从已有标签中选择"界面是隐藏的
+ const availableTagsContainer = this.form.querySelector('.available-tags');
+ if (availableTagsContainer) {
+ availableTagsContainer.style.display = 'none';
+ }
+
+ // 显示表单容器
+ this.container.style.visibility = 'visible';
+
+ // 查找布局容器
+ const layoutContainer = this.container.closest('.prompt-manager-layout');
+ if (layoutContainer) {
+ // 添加展开类以触发布局动画
+ layoutContainer.classList.add('form-expanded');
+ }
+
+ // 等待下一帧确保DOM更新
+ requestAnimationFrame(() => {
+ // 聚焦到第一个输入框
+ setTimeout(() => {
+ const firstInput = this.form.querySelector('input[name="name"]');
+ if (firstInput) {
+ firstInput.focus();
+ }
+ }, 100);
+ });
+ } hide() {
+ // 隐藏表单容器
+ this.container.style.visibility = 'hidden';
+ this.clearForm();
+ this.isEditing = false;
+ this.editingPrompt = null;
+
+ // 确保"从已有标签中选择"界面被隐藏
+ const availableTagsContainer = this.form.querySelector('.available-tags');
+ if (availableTagsContainer) {
+ availableTagsContainer.style.display = 'none';
+ }
+
+ // 查找布局容器,检查是否还有其他内容需要显示
+ const layoutContainer = this.container.closest('.prompt-manager-layout');
+ if (layoutContainer) {
+ // 检查是否还有其他组件在显示(比如 TagManager)
+ const tagManager = layoutContainer.querySelector('.tag-manager-section');
+ const isTagManagerVisible = tagManager && tagManager.classList.contains('active');
+
+ // 只有当没有其他内容显示时才移除展开类
+ if (!isTagManagerVisible) {
+ layoutContainer.classList.remove('form-expanded');
+ }
+ }
+ }
+
+ isVisible() {
+ return this.container.style.visibility !== 'hidden';
+ } populateForm(prompt) {
+ this.form.querySelector('input[name="name"]').value = prompt.name || '';
+ this.form.querySelector('textarea[name="content"]').value = prompt.content || '';
+ this.form.querySelector('input[name="tags"]').value = (prompt.tags || []).join(', ');
+
+ // 确保"从已有标签中选择"界面是隐藏的
+ const availableTagsContainer = this.form.querySelector('.available-tags');
+ if (availableTagsContainer) {
+ availableTagsContainer.style.display = 'none';
+ }
+
+ this.updateTagsPreview();
+ }clearForm() {
+ this.form.reset();
+
+ // 清除错误状态
+ this.form.querySelectorAll('.error').forEach(el => el.classList.remove('error'));
+ this.form.querySelectorAll('.error-message').forEach(el => el.remove());
+
+ // 隐藏标签预览
+ const previewElement = this.container.querySelector('.tags-preview');
+ if (previewElement) {
+ previewElement.style.display = 'none';
+ }
+
+ // 隐藏"从已有标签中选择"界面
+ const availableTagsContainer = this.form.querySelector('.available-tags');
+ if (availableTagsContainer) {
+ availableTagsContainer.style.display = 'none';
+ }
+ }
+
+ getElement() {
+ return this.container;
+ }
+
+ setOnSubmit(callback) {
+ this.onSubmit = callback;
+ }
+
+ setOnCancel(callback) {
+ this.onCancel = callback;
+ }
+
+ // 添加一个公开方法供外部调用清理
+ setGetPrompts(getPromptsCallback) {
+ this.getPrompts = getPromptsCallback;
+ }
+
+ // 手动清理未使用的标签
+ cleanupUnusedTags() {
+ if (this.getPrompts && typeof this.getPrompts === 'function') {
+ const prompts = this.getPrompts();
+ globalTagColorManager.cleanupUnusedTags(prompts);
+ }
+ }
+
+ // 工具函数
+ escapeHtml(text) {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+ }
+
+ debounce(func, wait) {
+ let timeout;
+ return function executedFunction(...args) {
+ const later = () => {
+ clearTimeout(timeout);
+ func(...args);
+ };
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ };
+ } addStyles() {
+ const style = document.createElement('style');
+ style.textContent = ` .prompt-form-section {
+ width: 100%;
+ padding: 24px;
+ background: transparent;
+ border-left: none;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ position: relative;
+ overflow-y: auto;
+ opacity: 0;
+ visibility: visible;
+ transition: opacity 0.3s ease-out 0.1s;
+ }
+
+ /* 当父容器展开时,表单内容同步显示 */
+ .prompt-manager-layout.form-expanded .prompt-form-section {
+ opacity: 1;
+ }
+
+ .prompt-form-section::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 1px;
+ height: 100%;
+ background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+ }
+
+ .form-title {
+ margin: 0 0 24px 0;
+ color: #ffffff;
+ font-size: 18px;
+ font-weight: 600;
+ letter-spacing: 0.5px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ position: relative;
+ }
+
+ .form-title::after {
+ content: '';
+ position: absolute;
+ bottom: -1px;
+ left: 0;
+ width: 60px;
+ height: 2px;
+ background: linear-gradient(90deg, #0078d4, transparent);
+ border-radius: 1px;
+ }
+
+ .prompt-form {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ }
+
+ .form-group {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+
+ .form-group label {
+ color: #e0e0e0;
+ font-weight: 500;
+ font-size: 14px;
+ letter-spacing: 0.25px;
+ margin-bottom: 4px;
+ } .form-group input,
+ .form-group textarea {
+ background: linear-gradient(145deg, #1a1a1a, #232323);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 8px;
+ color: #ffffff;
+ padding: 14px 16px;
+ font-size: 14px;
+ font-family: inherit;
+ transition: all 0.3s ease-out;
+ resize: vertical;
+ position: relative;
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
+ }
+
+ .form-group input:focus,
+ .form-group textarea:focus {
+ outline: none;
+ border-color: rgba(0, 120, 212, 0.6);
+ box-shadow:
+ 0 0 0 3px rgba(0, 120, 212, 0.15),
+ inset 0 1px 3px rgba(0, 0, 0, 0.2);
+ background: linear-gradient(145deg, #1e1e1e, #272727);
+ transform: translateY(-1px);
+ }
+
+ .form-group input::placeholder,
+ .form-group textarea::placeholder {
+ color: #666;
+ font-weight: 300;
+ }
+
+ .form-hint {
+ color: #888;
+ font-size: 12px;
+ font-weight: 400;
+ line-height: 1.4;
+ margin-top: 4px;
+ opacity: 0.8;
+ }
+
+ .tags-input-wrapper {
+ display: flex;
+ gap: 8px;
+ align-items: stretch;
+ }
+
+ .tags-input-wrapper input {
+ flex: 1;
+ } .tags-selector-btn {
+ background: linear-gradient(145deg, #404044, #35353a);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ color: #e0e0e0;
+ padding: 0 16px;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 14px;
+ transition: all 0.3s ease-out;
+ white-space: nowrap;
+ display: flex;
+ align-items: center;
+ position: relative;
+ overflow: hidden;
+ } .tags-selector-btn::before {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 0;
+ height: 0;
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.2) 0%, transparent 70%);
+ border-radius: 50%;
+ transform: translate(-50%, -50%);
+ transition: all 0.3s ease-out;
+ z-index: -1;
+ }
+
+ .tags-selector-btn:hover {
+ background: linear-gradient(145deg, #4a4a4e, #3f3f44);
+ color: #ffffff;
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
+ }
+
+ .tags-selector-btn:hover::before {
+ width: 200%;
+ height: 200%;
+ } .tags-selector-btn:active {
+ transform: translateY(0);
+ } /* 标签预览区域样式 */
+ .tags-preview {
+ display: none;
+ margin-top: 16px;
+ padding: 12px;
+ background: linear-gradient(145deg, #1a1a1a, #252525);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 8px;
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
+ }
+
+ .tags-preview-label {
+ color: #e0e0e0;
+ font-size: 12px;
+ margin-bottom: 8px;
+ font-weight: 500;
+ }
+
+ .tags-preview-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ }
+
+ .tag-preview {
+ display: inline-block;
+ background: var(--tag-color, #2a82e4);
+ color: white;
+ padding: 4px 10px;
+ border-radius: 16px;
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 1.2;
+ white-space: nowrap;
+ max-width: 120px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+ transition: all 0.3s ease-out;
+ position: relative;
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000,
+ 0 -1px 0 #000,
+ 0 1px 0 #000,
+ -1px 0 0 #000,
+ 1px 0 0 #000;
+ }
+
+ .tags-preview-section {
+ margin-top: 12px;
+ padding: 12px;
+ background: linear-gradient(145deg, #1a1a1a, #252525);
+ border-radius: 8px;
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ min-height: 60px;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+
+ .tags-preview-label {
+ color: #ccc;
+ font-size: 12px;
+ font-weight: 500;
+ margin: 0;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ }
+
+ .tags-preview-label::before {
+ content: '👁️';
+ font-size: 11px;
+ }
+
+ .tags-preview-container {
+ flex: 1;
+ min-height: 36px;
+ display: flex;
+ align-items: center;
+ }
+
+ .tags-preview-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ width: 100%;
+ min-height: 36px;
+ align-items: center;
+ }
+
+ .tags-preview-list:empty::after {
+ content: '暂无标签预览';
+ color: #666;
+ font-size: 12px;
+ font-style: italic;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 36px;
+ border: 1px dashed rgba(255, 255, 255, 0.1);
+ border-radius: 6px;
+ background: rgba(0, 0, 0, 0.2);
+ }
+
+ .preview-tag {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--tag-color, #2a82e4);
+ color: white;
+ padding: 6px 12px;
+ border-radius: 14px;
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 1.2;
+ white-space: nowrap;
+ min-width: 60px;
+ max-width: 90px;
+ height: 26px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ position: relative;
+ letter-spacing: 0.25px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+ text-align: center;
+ transition: all 0.2s ease;
+ animation: tagFadeIn 0.3s ease-out;
+ }
+
+ .preview-tag:hover {
+ transform: scale(1.05);
+ filter: brightness(1.1);
+ }
+
+ @keyframes tagFadeIn {
+ from {
+ opacity: 0;
+ transform: scale(0.8) translateY(-10px);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1) translateY(0);
+ }
+ }
+
+ /* 可选标签区域样式 */
+ .available-tags {
+ margin-top: 12px;
+ padding: 16px;
+ background: linear-gradient(145deg, #1a1a1a, #252525);
+ border-radius: 8px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ max-height: 200px;
+ overflow-y: auto;
+ } .available-tags.show {
+ display: block !important;
+ animation: slideDown 0.3s ease-out;
+ }
+
+ @keyframes slideDown {
+ from {
+ opacity: 0;
+ transform: translateY(-10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ } .available-tags h5 {
+ margin: 0 0 12px 0;
+ color: #e0e0e0;
+ font-size: 13px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ }
+
+ .available-tags h5::before {
+ content: '🏷️';
+ font-size: 12px;
+ }
+
+ .available-tags-header {
+ margin: 0 0 12px 0;
+ }
+
+ .available-tags-header span {
+ color: #ccc;
+ font-size: 12px;
+ font-weight: 500;
+ margin: 0;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ }
+
+ .available-tags-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ } .available-tag {
+ display: inline-flex;
+ align-items: center;
+ background: var(--tag-color, #2a82e4);
+ color: white;
+ padding: 6px 12px;
+ border-radius: 14px;
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 1.2;
+ white-space: nowrap;
+ min-width: 60px;
+ max-width: 90px;
+ height: 26px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ position: relative;
+ letter-spacing: 0.25px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+ text-align: center;
+ transition: all 0.2s ease;
+ cursor: pointer;
+ user-select: none;
+ transform: scale(1);
+ justify-content: center;
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000,
+ 0 -1px 0 #000,
+ 0 1px 0 #000,
+ -1px 0 0 #000,
+ 1px 0 0 #000;
+ }.available-tag:hover {
+ transform: scale(1.05);
+ filter: brightness(1.1);
+ }
+
+ .available-tag.selected {
+ background: #666;
+ color: #999;
+ filter: grayscale(1);
+ }
+
+ .available-tag.selected:hover {
+ transform: scale(1.05);
+ filter: grayscale(1) brightness(1.1);
+ } /* 表单操作按钮样式 */
+ .form-actions {
+ display: flex;
+ gap: 12px;
+ margin-top: 24px;
+ padding-top: 20px;
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+ } .form-actions button {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: 500;
+ transition: all 0.3s ease-out;
+ position: relative;
+ overflow: hidden;
+ letter-spacing: 0.25px;
+ padding: 12px 20px;
+ white-space: nowrap;
+ text-align: center;
+ }.form-actions button::before {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 0;
+ height: 0;
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.2) 0%, transparent 70%);
+ border-radius: 50%;
+ transform: translate(-50%, -50%);
+ transition: all 0.3s ease-out;
+ z-index: -1;
+ }
+
+ .form-actions button:hover::before {
+ width: 200%;
+ height: 200%;
+ }
+
+ .btn-icon {
+ font-size: 16px;
+ line-height: 1;
+ }
+
+ .btn-text {
+ white-space: nowrap;
+ }
+
+ .save-btn {
+ background: linear-gradient(135deg, #28a745, #20873a);
+ color: white;
+ box-shadow: 0 2px 8px rgba(40, 167, 69, 0.3);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ }
+
+ .save-btn:hover {
+ background: linear-gradient(135deg, #20873a, #1e7e34);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 16px rgba(40, 167, 69, 0.4);
+ }
+
+ .save-btn:active {
+ transform: translateY(0);
+ }
+
+ .cancel-btn {
+ background: linear-gradient(145deg, #404044, #35353a);
+ color: #e0e0e0;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ }
+
+ .cancel-btn:hover {
+ background: linear-gradient(145deg, #4a4a4e, #3f3f44);
+ color: #ffffff;
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
+ }
+
+ .cancel-btn:active {
+ transform: translateY(0);
+ }
+
+ /* 输入验证样式 */
+ .form-group.error input,
+ .form-group.error textarea {
+ border-color: #dc3545;
+ box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.15);
+ }
+
+ .form-group.error .form-hint {
+ color: #dc3545;
+ font-weight: 500;
+ }
+
+ .form-group.success input,
+ .form-group.success textarea {
+ border-color: #28a745;
+ box-shadow: 0 0 0 3px rgba(40, 167, 69, 0.15);
+ }
+
+ /* 滚动条样式 */
+ .available-tags::-webkit-scrollbar,
+ .prompt-form-section::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ .available-tags::-webkit-scrollbar-track,
+ .prompt-form-section::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 3px;
+ }
+
+ .available-tags::-webkit-scrollbar-thumb,
+ .prompt-form-section::-webkit-scrollbar-thumb {
+ background: linear-gradient(145deg, #3a3a3a, #2a2a2a);
+ border-radius: 3px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ } .available-tags::-webkit-scrollbar-thumb:hover,
+ .prompt-form-section::-webkit-scrollbar-thumb:hover {
+ background: linear-gradient(145deg, #4a4a4a, #3a3a3a);
+ }
+
+ /* 响应式设计 */
+ @media (max-width: 768px) {
+ .prompt-form-section {
+ padding: 16px;
+ }
+
+ .form-actions {
+ flex-direction: column;
+ }
+
+ .save-btn, .cancel-btn {
+ width: 100%;
+ }
+ }
+ `;
+
+ if (!document.head.querySelector('style[data-component="prompt-form"]')) {
+ style.setAttribute('data-component', 'prompt-form');
+ document.head.appendChild(style);
+ }
+ }
+
+ destroy() {
+ if (this.container && this.container.parentNode) {
+ this.container.parentNode.removeChild(this.container);
+ }
+
+ // 移除样式
+ const style = document.head.querySelector('style[data-component="prompt-form"]');
+ if (style) {
+ style.remove();
+ }
+ }
+
+ toggleAvailableTags() {
+ const availableTagsContainer = this.form.querySelector('.available-tags');
+ const isVisible = availableTagsContainer.style.display !== 'none';
+
+ if (isVisible) {
+ availableTagsContainer.style.display = 'none';
+ } else {
+ this.renderAvailableTags();
+ availableTagsContainer.style.display = 'block';
+ }
+ } renderAvailableTags() {
+ const availableTagsContainer = this.form.querySelector('.available-tags');
+
+ // 获取所有可用标签(包括手动创建的和提示词中的)
+ const allTags = globalTagColorManager.getAllTags();
+
+ const currentTags = this.getCurrentSelectedTags();
+
+ if (allTags.length === 0) {
+ availableTagsContainer.innerHTML = `
+
+ 暂无可选标签
+ 先在标签管理中添加标签
+
+ `;
+ return;
+ }availableTagsContainer.innerHTML = `
+
+
${allTags.map(tag => {
+ const color = globalTagColorManager.getTagColor(tag);
+ const colorRGB = globalTagColorManager.getTagColorRGB(tag);
+ const isSelected = currentTags.includes(tag);
+ return `
+
+ ${this.escapeHtml(tag)}
+
+ `;
+ }).join('')}
+
+ `;
+
+ this.bindAvailableTagsEvents();
+ } bindAvailableTagsEvents() {
+ const availableTagsContainer = this.form.querySelector('.available-tags');
+
+ // 标签点击事件
+ availableTagsContainer.querySelectorAll('.available-tag').forEach(tagElement => {
+ tagElement.addEventListener('click', () => {
+ const tagName = tagElement.dataset.tag;
+ this.toggleTagSelection(tagName);
+ });
+ });
+ } toggleTagSelection(tagName) {
+ const tagsInput = this.form.querySelector('input[name="tags"]');
+ const currentTags = this.getCurrentSelectedTags();
+
+ if (currentTags.includes(tagName)) {
+ // 移除标签
+ const newTags = currentTags.filter(tag => tag !== tagName);
+ tagsInput.value = newTags.join(', ');
+ } else {
+ // 添加标签
+ const newTags = [...currentTags, tagName];
+ tagsInput.value = newTags.join(', ');
+ }
+
+ // 触发input事件来更新预览
+ tagsInput.dispatchEvent(new Event('input'));
+
+ // 重新渲染可选标签以更新选中状态
+ this.renderAvailableTags();
+ }
+
+ getCurrentSelectedTags() {
+ const tagsInput = this.form.querySelector('input[name="tags"]');
+ return this.parseTags(tagsInput.value);
+ }
+
+ // 新增:刷新所有标签的颜色
+ refreshTagColors() {
+ // 刷新标签预览的颜色
+ const previewTags = this.container.querySelectorAll('.tag-preview');
+ previewTags.forEach(tagElement => {
+ const tagName = tagElement.textContent;
+ if (tagName) {
+ const color = globalTagColorManager.getTagColor(tagName);
+ const colorRGB = globalTagColorManager.getTagColorRGB(tagName);
+ tagElement.style.setProperty('--tag-color', color);
+ tagElement.style.setProperty('--tag-color-rgb', colorRGB);
+ }
+ });
+
+ // 刷新可选标签的颜色
+ const availableTags = this.container.querySelectorAll('.available-tag');
+ availableTags.forEach(tagElement => {
+ const tagName = tagElement.dataset.tag;
+ if (tagName) {
+ const color = globalTagColorManager.getTagColor(tagName); const colorRGB = globalTagColorManager.getTagColorRGB(tagName);
+ tagElement.style.setProperty('--tag-color', color);
+ tagElement.style.setProperty('--tag-color-rgb', colorRGB);
+ }
+ });
+ }
+}
diff --git a/extensions/Prompt_Manager/styles/components/PromptList.js b/extensions/Prompt_Manager/styles/components/PromptList.js
new file mode 100644
index 0000000..22a9c72
--- /dev/null
+++ b/extensions/Prompt_Manager/styles/components/PromptList.js
@@ -0,0 +1,812 @@
+// 列表组件 - 负责提示词列表的显示和交互
+
+import { globalTagColorManager } from './TagColorManager.js';
+
+export class PromptList { constructor(options = {}) {
+ this.prompts = [];
+ this.filteredPrompts = [];
+ this.currentSearchKeyword = ''; // 存储当前搜索关键词
+ this.currentSelectedTags = []; // 存储当前选中的标签
+ this.onEdit = options.onEdit || null;
+ this.onDelete = options.onDelete || null;
+ this.onApply = options.onApply || null;
+ this.onTagClick = options.onTagClick || null;
+
+ this.container = null;
+ this.listContainer = null;
+ this.headerContainer = null;
+ this.createList();
+ }
+
+ createList() {
+ this.container = document.createElement('div');
+ this.container.className = 'prompt-list-section'; this.container.innerHTML = `
+
+
+ `;
+
+ this.headerContainer = this.container.querySelector('.prompt-list-header');
+ this.listContainer = this.container.querySelector('.prompt-list');
+
+ this.bindEvents();
+ this.addStyles();
+ }
+
+ bindEvents() {
+ // 头部操作按钮事件将在主控制器中绑定
+ // 这里只处理列表项的交互事件
+ } setPrompts(prompts) {
+ this.prompts = Array.isArray(prompts) ? prompts : [];
+ // 重新应用当前搜索关键词和标签筛选
+ if (this.currentSearchKeyword && this.currentSearchKeyword.trim() !== '') {
+ this.filterPrompts(this.currentSearchKeyword, this.currentSelectedTags);
+ } else if (this.currentSelectedTags && this.currentSelectedTags.length > 0) {
+ this.filterPrompts('', this.currentSelectedTags);
+ } else {
+ this.filteredPrompts = [...this.prompts];
+ this.render();
+ }
+ this.updateStats();
+ } addPrompt(prompt) {
+ if (prompt && typeof prompt === 'object') {
+ this.prompts.push(prompt);
+ // 重新应用当前搜索关键词和标签筛选
+ if (this.currentSearchKeyword && this.currentSearchKeyword.trim() !== '') {
+ this.filterPrompts(this.currentSearchKeyword, this.currentSelectedTags);
+ } else if (this.currentSelectedTags && this.currentSelectedTags.length > 0) {
+ this.filterPrompts('', this.currentSelectedTags);
+ } else {
+ this.filteredPrompts = [...this.prompts];
+ this.render();
+ }
+ this.updateStats();
+ }
+ } updatePrompt(index, prompt) {
+ if (index >= 0 && index < this.prompts.length && prompt) {
+ this.prompts[index] = prompt;
+ // 重新应用当前搜索关键词和标签筛选
+ if (this.currentSearchKeyword && this.currentSearchKeyword.trim() !== '') {
+ this.filterPrompts(this.currentSearchKeyword, this.currentSelectedTags);
+ } else if (this.currentSelectedTags && this.currentSelectedTags.length > 0) {
+ this.filterPrompts('', this.currentSelectedTags);
+ } else {
+ this.filteredPrompts = [...this.prompts];
+ this.render();
+ }
+ this.updateStats();
+ }
+ }
+
+ removePrompt(index) {
+ if (index >= 0 && index < this.prompts.length) {
+ this.prompts.splice(index, 1); // 重新应用当前搜索关键词和标签筛选
+ if (this.currentSearchKeyword && this.currentSearchKeyword.trim() !== '') {
+ this.filterPrompts(this.currentSearchKeyword, this.currentSelectedTags);
+ } else if (this.currentSelectedTags && this.currentSelectedTags.length > 0) {
+ this.filterPrompts('', this.currentSelectedTags);
+ } else {
+ this.filteredPrompts = [...this.prompts];
+ this.render();
+ }
+ this.updateStats();
+ } }filterPrompts(keyword, selectedTags = []) {
+ // 存储当前搜索关键词
+ this.currentSearchKeyword = keyword || '';
+ this.currentSelectedTags = selectedTags || [];
+
+ // 应用关键词筛选和标签筛选
+ this.filteredPrompts = this.prompts.filter(prompt => {
+ let keywordMatch = true;
+ let tagMatch = true;
+
+ // 关键词筛选
+ if (keyword && keyword.trim() !== '') {
+ const searchKeyword = keyword.toLowerCase();
+ keywordMatch = false;
+
+ // 搜索名称
+ if (prompt.name && prompt.name.toLowerCase().includes(searchKeyword)) {
+ keywordMatch = true;
+ }
+
+ // 搜索内容
+ if (!keywordMatch && prompt.content && prompt.content.toLowerCase().includes(searchKeyword)) {
+ keywordMatch = true;
+ }
+
+ // 搜索标签
+ if (!keywordMatch && prompt.tags && Array.isArray(prompt.tags)) {
+ keywordMatch = prompt.tags.some(tag =>
+ tag.toLowerCase().includes(searchKeyword)
+ );
+ }
+ }
+ // 标签筛选 - 修复为AND逻辑(必须包含所有选中的标签)
+ if (selectedTags && selectedTags.length > 0) {
+ tagMatch = false;
+ if (prompt.tags && Array.isArray(prompt.tags)) {
+ // 检查是否包含所有选中的标签(AND逻辑)
+ tagMatch = selectedTags.every(selectedTag =>
+ prompt.tags.includes(selectedTag)
+ );
+ }
+ }
+
+ return keywordMatch && tagMatch;
+ });
+
+ this.render();
+ this.updateStats();
+ return this.filteredPrompts.length;
+ }
+
+ render() {
+ if (this.filteredPrompts.length === 0) {
+ this.renderEmptyState();
+ } else {
+ this.renderPrompts();
+ }
+ } renderEmptyState() {
+ // 判断是否在搜索状态
+ const isSearching = this.currentSearchKeyword && this.currentSearchKeyword.trim() !== '';
+ const hasPrompts = this.prompts.length > 0;
+
+ let emptyIcon, emptyTitle, emptyDescription;
+
+ if (isSearching && hasPrompts) {
+ // 有提示词但搜索无结果
+ emptyIcon = '🔍';
+ emptyTitle = '未找到相关内容';
+ emptyDescription = `未找到包含"${this.currentSearchKeyword}"的提示词`;
+ } else {
+ // 没有提示词
+ emptyIcon = '📝';
+ emptyTitle = '暂无提示词';
+ emptyDescription = '点击"+ 添加提示词"开始添加';
+ }
+
+ this.listContainer.innerHTML = `
+
+
${emptyIcon}
+
${emptyTitle}
+
${emptyDescription}
+
+ `;
+ }
+
+ renderPrompts() {
+ this.listContainer.innerHTML = this.filteredPrompts.map((prompt, index) => {
+ const actualIndex = this.prompts.indexOf(prompt);
+ return this.createPromptItemHTML(prompt, actualIndex);
+ }).join('');
+
+ // 绑定列表项事件
+ this.bindPromptItemEvents();
+ }
+
+ createPromptItemHTML(prompt, index) { const tagsHTML = prompt.tags && prompt.tags.length > 0
+ ? `
+ ${prompt.tags.map(tag => {
+ const color = globalTagColorManager.getTagColor(tag);
+ const colorRGB = globalTagColorManager.getTagColorRGB(tag);
+ return `${this.escapeHtml(tag)}`;
+ }).join('')}
+
`
+ : '';
+
+ return `
+
+
+
${this.escapeHtml(prompt.content || '')}
+
+ `;
+ }
+
+ bindPromptItemEvents() {
+ // 编辑按钮
+ this.listContainer.querySelectorAll('.edit-btn').forEach(btn => {
+ btn.addEventListener('click', (e) => {
+ const index = parseInt(e.target.dataset.index);
+ if (this.onEdit && index >= 0) {
+ this.onEdit(index, this.prompts[index]);
+ }
+ });
+ });
+
+ // 应用按钮
+ this.listContainer.querySelectorAll('.apply-btn').forEach(btn => {
+ btn.addEventListener('click', (e) => {
+ const index = parseInt(e.target.dataset.index);
+ if (this.onApply && index >= 0) {
+ this.onApply(index, this.prompts[index]);
+ }
+ });
+ });
+
+ // 删除按钮
+ this.listContainer.querySelectorAll('.delete-btn').forEach(btn => {
+ btn.addEventListener('click', (e) => {
+ const index = parseInt(e.target.dataset.index);
+ if (this.onDelete && index >= 0) {
+ this.onDelete(index, this.prompts[index]);
+ }
+ });
+ });
+
+ // 标签点击事件
+ this.listContainer.querySelectorAll('.tag').forEach(tag => {
+ tag.addEventListener('click', (e) => {
+ const tagName = e.target.dataset.tag;
+ if (this.onTagClick && tagName) {
+ this.onTagClick(tagName);
+ }
+ });
+ });
+ }
+
+ escapeHtml(text) {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+ }
+
+ getElement() {
+ return this.container;
+ } getHeaderActions() {
+ return {
+ importBtn: this.container.querySelector('.import-btn'),
+ exportBtn: this.container.querySelector('.export-btn'),
+ tagManagerBtn: this.container.querySelector('.tag-manager-btn'),
+ addBtn: this.container.querySelector('.add-prompt-btn')
+ };
+ }
+
+ // 设置回调函数
+ setOnEdit(callback) {
+ this.onEdit = callback;
+ }
+
+ setOnDelete(callback) {
+ this.onDelete = callback;
+ }
+
+ setOnApply(callback) {
+ this.onApply = callback;
+ }
+
+ setOnTagClick(callback) {
+ this.onTagClick = callback;
+ }
+
+ addStyles() {
+ const style = document.createElement('style'); style.textContent = `
+ .prompt-list-section {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ background: #1e1e1e;
+ } .prompt-list-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 24px 32px 20px;
+ background: linear-gradient(135deg, #2d2d30, #232326);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ position: relative;
+ z-index: 2;
+ box-sizing: border-box;
+ }
+
+ .prompt-list-header::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+ }
+
+ .header-title {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ }
+
+ .header-title h4 {
+ margin: 0;
+ color: #ffffff;
+ font-size: 18px;
+ font-weight: 600;
+ letter-spacing: 0.5px;
+ }
+
+ .list-stats {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+
+ .total-count {
+ color: #999;
+ font-size: 12px;
+ font-weight: 400;
+ letter-spacing: 0.25px;
+ }
+
+ .header-actions {
+ display: flex;
+ gap: 16px;
+ align-items: center;
+ }
+
+ .action-group {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ }
+
+ .action-group.primary {
+ order: 1;
+ }
+
+ .action-group.secondary {
+ order: 2;
+ position: relative;
+ }
+
+ .action-group.secondary::before {
+ content: '';
+ position: absolute;
+ left: -8px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 1px;
+ height: 24px;
+ background: rgba(255, 255, 255, 0.1);
+ }
+
+ /* 按钮基础样式 */
+ .prompt-list-header button {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 13px;
+ font-weight: 500;
+ transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
+ position: relative;
+ overflow: hidden;
+ letter-spacing: 0.25px;
+ }
+
+ .prompt-list-header button::before {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 0;
+ height: 0;
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.2) 0%, transparent 70%);
+ border-radius: 50%;
+ transform: translate(-50%, -50%);
+ transition: all 0.3s ease;
+ z-index: -1;
+ }
+
+ .prompt-list-header button:hover::before {
+ width: 200%;
+ height: 200%;
+ }
+
+ .btn-icon {
+ font-size: 14px;
+ line-height: 1;
+ }
+
+ .btn-text {
+ white-space: nowrap;
+ } /* 主要按钮样式 */
+ .primary-btn {
+ background: linear-gradient(135deg, #0078d4, #106ebe);
+ color: white;
+ padding: 10px 20px;
+ min-height: 40px;
+ box-shadow: 0 2px 8px rgba(0, 120, 212, 0.3);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ }
+
+ .primary-btn:hover {
+ background: linear-gradient(135deg, #106ebe, #005a9e);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 16px rgba(0, 120, 212, 0.4);
+ }
+
+ .primary-btn:active {
+ transform: translateY(0);
+ box-shadow: 0 2px 8px rgba(0, 120, 212, 0.3);
+ }
+
+ /* 次要按钮样式 */
+ .secondary-btn {
+ background: linear-gradient(135deg, #404044, #35353a);
+ color: #e0e0e0;
+ padding: 10px 16px;
+ min-height: 40px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ }
+
+ .secondary-btn:hover {
+ background: linear-gradient(135deg, #4a4a4e, #3f3f44);
+ color: #ffffff;
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
+ }
+
+ /* 三级按钮样式 */
+ .tertiary-btn {
+ background: rgba(255, 255, 255, 0.05);
+ color: #ccc;
+ padding: 10px 14px;
+ min-height: 40px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ }
+
+ .tertiary-btn:hover {
+ background: rgba(255, 255, 255, 0.1);
+ color: #fff;
+ border-color: rgba(255, 255, 255, 0.2);
+ transform: translateY(-1px);
+ }.tertiary-btn:active {
+ transform: translateY(0);
+ } /* 列表区域样式 */
+ .prompt-list {
+ flex: 1;
+ overflow-y: auto;
+ padding: 24px 32px 24px 40px;
+ background: #1e1e1e;
+ position: relative;
+ box-sizing: border-box;
+ } .prompt-list::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 32px;
+ right: 32px;
+ height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
+ }
+
+ .empty-state {
+ text-align: center;
+ padding: 80px 20px;
+ color: #888;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.02), transparent);
+ border-radius: 12px;
+ border: 1px dashed rgba(255, 255, 255, 0.1);
+ margin: 40px 0;
+ }
+
+ .empty-state-icon {
+ font-size: 64px;
+ margin-bottom: 20px;
+ opacity: 0.6;
+ filter: grayscale(1);
+ }
+
+ .empty-state p {
+ margin: 12px 0;
+ font-size: 14px;
+ line-height: 1.5;
+ }
+
+ .empty-state p:first-of-type {
+ font-size: 16px;
+ font-weight: 500;
+ color: #aaa;
+ }
+
+ .prompt-item {
+ background: linear-gradient(145deg, #2a2a2a, #232323);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ padding: 20px;
+ margin-bottom: 16px;
+ transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
+ position: relative;
+ overflow: hidden;
+ }
+
+ .prompt-item::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ }
+
+ .prompt-item:hover {
+ border-color: rgba(0, 120, 212, 0.4);
+ transform: translateY(-2px);
+ box-shadow:
+ 0 8px 32px rgba(0, 0, 0, 0.3),
+ 0 2px 8px rgba(0, 120, 212, 0.1);
+ background: linear-gradient(145deg, #2e2e2e, #272727);
+ }
+
+ .prompt-item:hover::before {
+ opacity: 1;
+ }
+
+ .prompt-item-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 16px;
+ gap: 16px;
+ }
+
+ .prompt-item-title-section {
+ flex: 1;
+ min-width: 0;
+ }
+
+ .prompt-item-name {
+ font-weight: 600;
+ color: #ffffff;
+ font-size: 16px;
+ margin-bottom: 10px;
+ word-break: break-word;
+ line-height: 1.4;
+ letter-spacing: 0.25px;
+ }
+
+ .prompt-item-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-bottom: 8px;
+ } .tag {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--tag-color, #2a82e4);
+ color: white;
+ padding: 6px 12px;
+ border-radius: 16px;
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 1.2;
+ white-space: nowrap;
+ min-width: 60px;
+ max-width: 90px;
+ height: 28px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ cursor: pointer;
+ transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
+ user-select: none;
+ position: relative;
+ letter-spacing: 0.25px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
+ text-align: center;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ transform: scale(1);
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000,
+ 0 -1px 0 #000,
+ 0 1px 0 #000,
+ -1px 0 0 #000,
+ 1px 0 0 #000;
+ }.tag::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), transparent);
+ border-radius: inherit;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ pointer-events: none;
+ } .tag:hover {
+ box-shadow:
+ 0 0 10px rgba(var(--tag-color-rgb), 0.3),
+ 0 4px 15px rgba(0, 0, 0, 0.2);
+ transform: scale(1.05);
+ border-color: rgba(var(--tag-color-rgb), 0.4);
+ filter: brightness(1.1) saturate(1.05);
+ }.tag:hover::before {
+ opacity: 1;
+ }
+
+ .prompt-item-actions {
+ display: flex;
+ gap: 8px;
+ flex-shrink: 0;
+ }
+
+ .edit-btn, .apply-btn, .delete-btn {
+ background: #383838;
+ border: none;
+ color: white;
+ padding: 8px 12px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 12px;
+ font-weight: 500;
+ transition: all 0.2s ease;
+ white-space: nowrap;
+ }
+
+ .edit-btn:hover {
+ background: #ffc300;
+ color: #000;
+ transform: translateY(-1px);
+ }
+
+ .apply-btn:hover {
+ background: #43cf7c;
+ transform: translateY(-1px);
+ }
+
+ .delete-btn {
+ background: #8b3a3a;
+ }
+
+ .delete-btn:hover {
+ background: #ff5733;
+ transform: translateY(-1px);
+ } .prompt-item-content {
+ color: #c4c4c4;
+ line-height: 1.5;
+ background: linear-gradient(145deg, #1a1a1a, #232323);
+ padding: 16px;
+ border-radius: 8px;
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ white-space: pre-wrap;
+ word-break: break-word;
+ max-height: 200px;
+ overflow-y: auto;
+ margin: 0 auto;
+ text-align: left;
+ font-size: 14px;
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
+ width: 100%;
+ max-width: 100%;
+ }
+
+ /* 响应式设计 */
+ @media (max-width: 768px) {
+ .prompt-list-header {
+ flex-direction: column;
+ gap: 12px;
+ align-items: stretch;
+ }
+
+ .header-actions {
+ justify-content: space-between;
+ }
+
+ .prompt-item {
+ padding: 12px;
+ }
+
+ .prompt-item-header {
+ flex-direction: column;
+ gap: 12px;
+ }
+
+ .prompt-item-actions {
+ justify-content: flex-end;
+ }
+ }
+ `;
+
+ if (!document.head.querySelector('style[data-component="prompt-list"]')) {
+ style.setAttribute('data-component', 'prompt-list');
+ document.head.appendChild(style);
+ }
+ }
+
+ updateStats() {
+ const totalElement = this.container.querySelector('.total-count');
+ if (totalElement) {
+ totalElement.textContent = `${this.filteredPrompts.length} 个项目`;
+ }
+ } // 新增:刷新所有标签的颜色
+ refreshTagColors() {
+ console.log('PromptList: 刷新标签颜色和删除无效标签...');
+
+ // 重新渲染整个列表以确保已删除的标签被正确移除
+ // 这比仅仅更新颜色更可靠,能够处理标签删除的情况
+ this.render();
+
+ console.log('PromptList: 标签颜色刷新和无效标签清理完成');
+ }
+
+ // 新增:完全重新渲染列表(保持当前筛选状态)
+ forceRefresh() {
+ this.render();
+ }
+
+ // 清除所有筛选条件
+ clearFilters() {
+ this.currentSearchKeyword = '';
+ this.currentSelectedTags = [];
+ this.filteredPrompts = [...this.prompts];
+ this.render();
+ this.updateStats();
+ }
+
+ // 获取当前筛选状态
+ getFilterState() {
+ return {
+ keyword: this.currentSearchKeyword,
+ selectedTags: this.currentSelectedTags
+ };
+ }
+
+ destroy() {
+ if (this.container && this.container.parentNode) {
+ this.container.parentNode.removeChild(this.container);
+ }
+
+ // 移除样式
+ const style = document.head.querySelector('style[data-component="prompt-list"]');
+ if (style) {
+ style.remove();
+ }
+ }
+}
diff --git a/extensions/Prompt_Manager/styles/components/SearchBar.js b/extensions/Prompt_Manager/styles/components/SearchBar.js
new file mode 100644
index 0000000..e13b0bb
--- /dev/null
+++ b/extensions/Prompt_Manager/styles/components/SearchBar.js
@@ -0,0 +1,700 @@
+// 搜索栏组件 - 负责提示词搜索功能和标签筛选
+
+import { globalTagColorManager } from './TagColorManager.js';
+
+export class SearchBar {
+ constructor(onSearch = null, onTagFilter = null) {
+ this.onSearch = onSearch;
+ this.onTagFilter = onTagFilter; // 标签筛选回调
+ this.currentKeyword = '';
+ this.selectedTags = new Set(); // 选中的标签
+ this.allTags = new Set(); // 所有可用标签
+ this.searchInput = null;
+ this.container = null; this.tagsContainer = null;
+ this.debounceTimer = null;
+ this.createSearchBar();
+ }createSearchBar() {
+ this.container = document.createElement('div');
+ this.container.className = 'search-section';
+ this.container.innerHTML = `
+
+
+
+
+
+ 找到 0 个结果
+ 筛选: 0 个标签
+
+ `;
+
+ this.searchInput = this.container.querySelector('.search-input');
+ this.clearBtn = this.container.querySelector('.clear-search-btn');
+ this.statsContainer = this.container.querySelector('.search-stats');
+ this.resultsCount = this.container.querySelector('.search-results-count');
+ this.tagsContainer = this.container.querySelector('.tags-filter-container');
+ this.activeFiltersCount = this.container.querySelector('.active-filters-count');
+ this.filterCount = this.container.querySelector('.filter-count');
+
+ this.bindEvents();
+ this.addStyles();
+ }
+
+ bindEvents() {
+ // 搜索输入事件(带防抖)
+ this.searchInput.addEventListener('input', (e) => {
+ const keyword = e.target.value.trim();
+ this.updateClearButton(keyword);
+
+ // 防抖处理
+ if (this.debounceTimer) {
+ clearTimeout(this.debounceTimer);
+ }
+
+ this.debounceTimer = setTimeout(() => {
+ this.handleSearch(keyword);
+ }, 300);
+ });
+
+ // 回车键搜索
+ this.searchInput.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ if (this.debounceTimer) {
+ clearTimeout(this.debounceTimer);
+ }
+ this.handleSearch(this.searchInput.value.trim());
+ }
+ });
+
+ // 清除搜索
+ this.clearBtn.addEventListener('click', () => {
+ this.clearSearch();
+ });
+
+ // 聚焦输入框时的样式
+ this.searchInput.addEventListener('focus', () => {
+ this.container.querySelector('.search-container').classList.add('focused');
+ });
+
+ this.searchInput.addEventListener('blur', () => {
+ this.container.querySelector('.search-container').classList.remove('focused');
+ });
+ } handleSearch(keyword) {
+ this.currentKeyword = keyword;
+
+ // 同时应用搜索和标签筛选
+ if (this.onTagFilter && typeof this.onTagFilter === 'function') {
+ const results = this.onTagFilter(keyword, Array.from(this.selectedTags));
+ this.updateSearchStats(results);
+ } else if (this.onSearch && typeof this.onSearch === 'function') {
+ // 兼容旧的搜索回调
+ const results = this.onSearch(keyword);
+ this.updateSearchStats(results);
+ }
+ }
+
+ updateClearButton(keyword) {
+ if (keyword) {
+ this.clearBtn.style.display = 'block';
+ } else {
+ this.clearBtn.style.display = 'none';
+ }
+ }
+
+ updateSearchStats(results) {
+ if (this.currentKeyword) {
+ this.statsContainer.style.display = 'block';
+ const count = Array.isArray(results) ? results.length : (results || 0);
+ this.resultsCount.textContent = `找到 ${count} 个结果`;
+ } else {
+ this.statsContainer.style.display = 'none';
+ }
+ } clearSearch() {
+ this.searchInput.value = '';
+ this.currentKeyword = '';
+ this.clearBtn.style.display = 'none';
+ this.statsContainer.style.display = 'none';
+ this.selectedTags.clear(); // 同时清除标签筛选
+ this.renderTags();
+ this.updateActiveFiltersDisplay();
+ this.handleFilter();
+ this.searchInput.focus();
+ }
+
+ setKeyword(keyword) {
+ this.searchInput.value = keyword;
+ this.currentKeyword = keyword;
+ this.updateClearButton(keyword);
+ this.handleSearch(keyword);
+ }
+
+ getKeyword() {
+ return this.currentKeyword;
+ }
+
+ setOnSearch(callback) {
+ this.onSearch = callback;
+ }
+
+ focus() {
+ this.searchInput.focus();
+ }
+
+ addStyles() { const style = document.createElement('style');
+ style.textContent = ` .search-section {
+ padding: 24px 32px 20px;
+ background: linear-gradient(135deg, #2d2d30, #232326);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ position: relative;
+ z-index: 2;
+ box-sizing: border-box;
+ }
+
+ .search-section::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+ }
+
+ .search-container {
+ position: relative;
+ display: flex;
+ align-items: center;
+ background: linear-gradient(145deg, #1e1e1e, #2a2a2a);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
+ box-shadow:
+ inset 0 1px 3px rgba(0, 0, 0, 0.2),
+ 0 1px 3px rgba(0, 0, 0, 0.1);
+ overflow: hidden;
+ }
+
+ .search-container::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ }
+
+ .search-container.focused {
+ border-color: rgba(0, 120, 212, 0.6);
+ box-shadow:
+ 0 0 0 3px rgba(0, 120, 212, 0.15),
+ inset 0 1px 3px rgba(0, 0, 0, 0.2),
+ 0 4px 12px rgba(0, 120, 212, 0.1);
+ transform: translateY(-1px);
+ }
+
+ .search-container.focused::before {
+ opacity: 1;
+ }
+
+ .search-input {
+ flex: 1;
+ background: transparent;
+ border: none;
+ color: #ffffff;
+ padding: 14px 16px;
+ font-size: 14px;
+ font-weight: 400;
+ outline: none;
+ min-width: 0;
+ letter-spacing: 0.25px;
+ }
+
+ .search-input::placeholder {
+ color: #888;
+ font-weight: 300;
+ }
+
+ .search-icon {
+ color: #888;
+ padding: 0 16px 0 16px;
+ font-size: 16px;
+ pointer-events: none;
+ transition: color 0.3s ease;
+ }
+
+ .search-container.focused .search-icon {
+ color: #0078d4;
+ }
+
+ .clear-search-btn {
+ background: none;
+ border: none;
+ color: #888;
+ cursor: pointer;
+ padding: 8px 16px;
+ font-size: 16px;
+ transition: all 0.3s ease;
+ border-radius: 6px;
+ margin-right: 4px;
+ opacity: 0;
+ transform: scale(0.8);
+ pointer-events: none;
+ }
+
+ .clear-search-btn.visible {
+ opacity: 1;
+ transform: scale(1);
+ pointer-events: auto;
+ }
+
+ .clear-search-btn:hover {
+ color: #fff;
+ background: rgba(255, 255, 255, 0.1);
+ transform: scale(1.1);
+ } .clear-search-btn:active {
+ transform: scale(0.95);
+ }
+
+ .search-stats {
+ margin-top: 12px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 4px;
+ }
+
+ .search-results-count {
+ color: #999;
+ font-size: 12px;
+ font-weight: 400;
+ letter-spacing: 0.25px;
+ }
+
+ .active-filters-count {
+ color: #4a9eff;
+ font-size: 12px;
+ font-weight: 500;
+ padding: 2px 8px;
+ background: rgba(74, 158, 255, 0.1);
+ border-radius: 6px;
+ border: 1px solid rgba(74, 158, 255, 0.2);
+ } /* 标签筛选样式 */
+ .tags-filter-container {
+ margin-top: 12px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ align-items: center;
+ }
+
+ .tags-filter-container:empty {
+ display: none;
+ } .tags-header {
+ display: none;
+ }
+
+ .tags-title {
+ color: #e0e0e0;
+ font-size: 13px;
+ font-weight: 600;
+ letter-spacing: 0.5px;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ }
+
+ .tags-title::before {
+ content: '🏷️';
+ font-size: 12px;
+ } .clear-tags-btn {
+ background: linear-gradient(145deg, #2a2a2a, #1e1e1e);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ color: #ccc;
+ padding: 6px 12px;
+ font-size: 11px;
+ border-radius: 12px;
+ cursor: pointer;
+ transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
+ font-weight: 500;
+ letter-spacing: 0.25px;
+ height: 26px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 8px;
+ }
+
+ .clear-tags-btn:hover {
+ border-color: rgba(255, 255, 255, 0.2);
+ color: #fff;
+ background: linear-gradient(145deg, #3a3a3a, #2e2e2e);
+ transform: translateY(-1px);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
+ }
+
+ .clear-tags-btn:active {
+ transform: translateY(0);
+ } .tags-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ align-items: center;
+ } .filter-tag {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--tag-color, #2a82e4);
+ color: white;
+ padding: 6px 12px;
+ border-radius: 16px;
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 1.2;
+ white-space: nowrap;
+ cursor: pointer;
+ transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
+ user-select: none;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ min-width: 60px;
+ max-width: 90px;
+ height: 28px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ position: relative;
+ letter-spacing: 0.25px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
+ text-align: center;
+ transform: scale(1);
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000,
+ 0 -1px 0 #000,
+ 0 1px 0 #000,
+ -1px 0 0 #000,
+ 1px 0 0 #000;
+ }.filter-tag::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), transparent);
+ border-radius: inherit;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ pointer-events: none;
+ } .filter-tag:hover {
+ box-shadow:
+ 0 0 10px rgba(var(--tag-color-rgb), 0.3),
+ 0 4px 15px rgba(0, 0, 0, 0.2);
+ transform: scale(1.05);
+ border-color: rgba(var(--tag-color-rgb), 0.4);
+ filter: brightness(1.1) saturate(1.05);
+ }.filter-tag:hover::before {
+ opacity: 1;
+ } .filter-tag.selected {
+ background: var(--tag-color, #2a82e4);
+ box-shadow:
+ 0 0 12px rgba(var(--tag-color-rgb), 0.4),
+ 0 4px 15px rgba(0, 0, 0, 0.3),
+ 0 0 0 2px rgba(255, 255, 255, 0.8);
+ border-color: rgba(var(--tag-color-rgb), 0.8);
+ transform: scale(1.08);
+ font-weight: 600;
+ color: #ffffff;
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000,
+ 0 -1px 0 #000,
+ 0 1px 0 #000,
+ -1px 0 0 #000,
+ 1px 0 0 #000;
+ position: relative;
+ z-index: 2;
+ filter: brightness(1.15) saturate(1.1);
+ border: 2px solid rgba(255, 255, 255, 0.9);
+ animation: tagSelectPulse 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
+ } .filter-tag.selected:hover {
+ background: var(--tag-color, #2a82e4);
+ box-shadow:
+ 0 0 16px rgba(var(--tag-color-rgb), 0.5),
+ 0 6px 20px rgba(0, 0, 0, 0.3),
+ 0 0 0 2px rgba(255, 255, 255, 0.9);
+ transform: scale(1.12);
+ filter: brightness(1.25) saturate(1.15);
+ }
+
+ @keyframes tagSelectPulse {
+ 0% {
+ transform: scale(1);
+ box-shadow:
+ 0 0 8px rgba(var(--tag-color-rgb), 0.3),
+ 0 2px 8px rgba(0, 0, 0, 0.2);
+ }
+ 50% {
+ transform: scale(1.15);
+ box-shadow:
+ 0 0 20px rgba(var(--tag-color-rgb), 0.6),
+ 0 6px 20px rgba(0, 0, 0, 0.3),
+ 0 0 0 3px rgba(255, 255, 255, 1);
+ }
+ 100% {
+ transform: scale(1.08);
+ box-shadow:
+ 0 0 12px rgba(var(--tag-color-rgb), 0.4),
+ 0 4px 15px rgba(0, 0, 0, 0.3),
+ 0 0 0 2px rgba(255, 255, 255, 0.8);
+ } }
+
+ .no-tags {
+ display: none;
+ }
+
+ /* 响应式设计 */
+ @media (max-width: 768px) {
+ .search-input {
+ padding: 10px 12px;
+ font-size: 16px; /* 防止iOS缩放 */
+ }
+
+ .search-input::placeholder {
+ font-size: 14px;
+ }
+ }
+ `;
+
+ if (!document.head.querySelector('style[data-component="search-bar"]')) {
+ style.setAttribute('data-component', 'search-bar');
+ document.head.appendChild(style);
+ }
+ } // 标签相关方法
+ updateTags(prompts) {
+ // 收集所有标签
+ this.allTags.clear();
+
+ // 1. 从提示词中收集标签
+ prompts.forEach(prompt => {
+ if (prompt.tags && Array.isArray(prompt.tags)) {
+ prompt.tags.forEach(tag => {
+ if (tag && tag.trim()) {
+ this.allTags.add(tag.trim());
+ }
+ });
+ }
+ });
+
+ // 2. 从TagColorManager中获取所有手动创建的标签(包括未使用的)
+ const allManagedTags = globalTagColorManager.getAllTags();
+ allManagedTags.forEach(tag => {
+ this.allTags.add(tag);
+ });
+
+ // 更新全局标签颜色管理器
+ globalTagColorManager.updateFromPrompts(prompts);
+
+ this.renderTags();
+ }
+
+ generateRandomColor() {
+ // 使用全局标签颜色管理器
+ return globalTagColorManager.generateRandomColor();
+ } renderTags() {
+ if (!this.tagsContainer) return;
+
+ if (this.allTags.size === 0) {
+ this.tagsContainer.innerHTML = '';
+ return;
+ }
+
+ const sortedTags = Array.from(this.allTags).sort();
+
+ // 如果有选中的标签,在标签列表前添加清除按钮
+ const clearButton = this.selectedTags.size > 0
+ ? `
`
+ : '';
+
+ this.tagsContainer.innerHTML = `
+ ${clearButton}
+ ${sortedTags.map(tag => this.createTagElement(tag)).join('')}
+ `;
+
+ this.bindTagEvents();
+ }createTagElement(tag) {
+ const isSelected = this.selectedTags.has(tag);
+ const color = globalTagColorManager.getTagColor(tag);
+ const colorRGB = globalTagColorManager.getTagColorRGB(tag);
+
+ return `
+
+ ${this.escapeHtml(tag)}
+
+ `;
+ } bindTagEvents() {
+ // 标签点击事件
+ this.tagsContainer.querySelectorAll('.filter-tag').forEach(tagElement => {
+ tagElement.addEventListener('click', () => {
+ const tag = tagElement.dataset.tag;
+ this.toggleTag(tag);
+ });
+ });
+
+ // 清除标签筛选
+ const clearTagsBtn = this.tagsContainer.querySelector('.clear-tags-btn');
+ if (clearTagsBtn) {
+ clearTagsBtn.addEventListener('click', () => {
+ this.clearTagFilters();
+ });
+ }
+ }
+
+ toggleTag(tag) {
+ if (this.selectedTags.has(tag)) {
+ this.selectedTags.delete(tag);
+ } else {
+ this.selectedTags.add(tag);
+ }
+
+ this.renderTags();
+ this.updateActiveFiltersDisplay();
+ this.handleFilter();
+ }
+
+ clearTagFilters() {
+ this.selectedTags.clear();
+ this.renderTags();
+ this.updateActiveFiltersDisplay();
+ this.handleFilter();
+ }
+
+ updateActiveFiltersDisplay() {
+ if (this.selectedTags.size > 0) {
+ this.activeFiltersCount.style.display = 'inline';
+ this.filterCount.textContent = this.selectedTags.size;
+ } else {
+ this.activeFiltersCount.style.display = 'none';
+ }
+ }
+
+ handleFilter() {
+ // 同时应用搜索和标签筛选
+ if (this.onTagFilter && typeof this.onTagFilter === 'function') {
+ const results = this.onTagFilter(this.currentKeyword, Array.from(this.selectedTags));
+ this.updateSearchStats(results);
+ }
+ }
+
+ escapeHtml(text) {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+ }
+
+ destroy() {
+ if (this.debounceTimer) {
+ clearTimeout(this.debounceTimer);
+ }
+
+ if (this.container && this.container.parentNode) {
+ this.container.parentNode.removeChild(this.container);
+ }
+
+ // 移除样式
+ const style = document.head.querySelector('style[data-component="search-bar"]');
+ if (style) {
+ style.remove();
+ }
+ }
+
+ // 公开方法
+ getSelectedTags() {
+ return Array.from(this.selectedTags);
+ }
+
+ hasTagsSelected() {
+ return this.selectedTags.size > 0;
+ }
+
+ selectTag(tag) {
+ if (this.allTags.has(tag) && !this.selectedTags.has(tag)) {
+ this.selectedTags.add(tag);
+ this.renderTags();
+ this.updateActiveFiltersDisplay();
+ this.handleFilter();
+ }
+ }
+
+ getCurrentKeyword() {
+ return this.currentKeyword;
+ }
+
+ // 获取组件元素
+ getElement() {
+ return this.container;
+ } // 刷新标签筛选区域(从TagColorManager获取所有标签)
+ refreshTags() {
+ console.log('SearchBar: 开始刷新标签列表...');
+
+ // 从全局标签颜色管理器获取最新的标签列表
+ const allTags = globalTagColorManager.getAllTags();
+ console.log('SearchBar: 从TagColorManager获取到的标签:', allTags);
+
+ // 更新本地标签集合
+ this.allTags = new Set(allTags);
+
+ // 清理已删除的标签选择状态
+ const deletedTags = [];
+ this.selectedTags.forEach(selectedTag => {
+ if (!this.allTags.has(selectedTag)) {
+ deletedTags.push(selectedTag);
+ }
+ });
+
+ // 移除已删除标签的选择状态
+ deletedTags.forEach(tag => {
+ console.log(`SearchBar: 移除已删除标签的选择状态: ${tag}`);
+ this.selectedTags.delete(tag);
+ });
+
+ // 重新渲染标签UI
+ console.log('SearchBar: 重新渲染标签UI...');
+ this.renderTags();
+
+ // 如果有选中状态变化,重新应用筛选
+ if (deletedTags.length > 0) {
+ console.log('SearchBar: 检测到标签删除,重新应用筛选...');
+ this.updateActiveFiltersDisplay();
+ this.handleFilter();
+ }
+
+ console.log('SearchBar: 标签列表刷新完成,当前标签:', Array.from(this.allTags));
+ }
+
+ // 新增:刷新所有标签的颜色
+ refreshTagColors() {
+ // 重新渲染所有标签的颜色
+ this.tagsContainer.querySelectorAll('.filter-tag').forEach(tagElement => {
+ const tagName = tagElement.dataset.tag;
+ if (tagName) {
+ const color = globalTagColorManager.getTagColor(tagName);
+ const colorRGB = globalTagColorManager.getTagColorRGB(tagName);
+ tagElement.style.setProperty('--tag-color', color);
+ tagElement.style.setProperty('--tag-color-rgb', colorRGB);
+ }
+ });
+ }
+}
diff --git a/extensions/Prompt_Manager/styles/components/TagColorManager.js b/extensions/Prompt_Manager/styles/components/TagColorManager.js
new file mode 100644
index 0000000..f5317bb
--- /dev/null
+++ b/extensions/Prompt_Manager/styles/components/TagColorManager.js
@@ -0,0 +1,288 @@
+// 标签颜色管理器 - 完全基于后端JSON文件同步,移除localStorage逻辑
+
+export class TagColorManager {
+ constructor() {
+ this.colorMap = new Map(); // 标签名 -> 颜色映射
+ this.usedColors = new Set(); // 已使用的颜色
+ this.loadFromBackend(); // 启动时从后端加载
+ }
+
+ // 从后端API加载标签数据
+ async loadFromBackend() {
+ try {
+ const response = await fetch('/dd_nodes/load_tags');
+ if (response.ok) {
+ const result = await response.json();
+ if (result.success && result.tags) {
+ // 更新颜色映射
+ this.colorMap = new Map(Object.entries(result.tags));
+ this.usedColors = new Set(Object.values(result.tags));
+
+ console.log(`从后端加载了 ${Object.keys(result.tags).length} 个标签颜色`);
+ } else {
+ console.log('后端没有标签数据,使用空映射');
+ }
+ } else {
+ console.warn('从后端加载标签失败,使用空映射');
+ }
+ } catch (error) {
+ console.warn('从后端加载标签失败,使用空映射:', error);
+ }
+ }
+
+ // 直接同步到后端,移除localStorage逻辑
+ async saveToBackend() {
+ try {
+ const colorData = Object.fromEntries(this.colorMap);
+ const response = await fetch('/dd_nodes/save_tags', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ tags: colorData
+ })
+ });
+
+ if (response.ok) {
+ const result = await response.json();
+ if (result.success) {
+ console.log('标签数据已同步到后端:', colorData);
+ return true;
+ } else {
+ console.error('后端同步失败:', result.error);
+ return false;
+ }
+ } else {
+ console.error('后端同步请求失败:', response.status);
+ return false;
+ }
+ } catch (error) {
+ console.error('同步标签到后端失败:', error);
+ return false;
+ }
+ }
+
+ // 获取标签颜色(只读取,不自动创建)
+ getTagColor(tagName) {
+ if (!tagName || typeof tagName !== 'string') {
+ return '#666'; // 默认颜色
+ }
+
+ const normalizedTag = tagName.trim();
+
+ // 如果标签存在,返回其颜色
+ if (this.colorMap.has(normalizedTag)) {
+ return this.colorMap.get(normalizedTag);
+ }
+
+ // 如果标签不存在,返回默认颜色,但不自动创建标签
+ return '#666'; // 默认颜色,用于预览
+ }
+
+ // 获取或创建标签颜色(用于实际保存标签时)
+ async getOrCreateTagColor(tagName) {
+ if (!tagName || typeof tagName !== 'string') {
+ return '#666'; // 默认颜色
+ }
+
+ const normalizedTag = tagName.trim();
+
+ // 如果标签已存在,直接返回颜色
+ if (this.colorMap.has(normalizedTag)) {
+ return this.colorMap.get(normalizedTag);
+ }
+
+ // 标签不存在时,创建新颜色
+ const newColor = this.generateUniqueColor();
+ this.colorMap.set(normalizedTag, newColor);
+ this.usedColors.add(newColor);
+
+ console.log(`为新标签 "${normalizedTag}" 生成颜色: ${newColor}`);
+
+ return newColor;
+ }
+
+ // 设置标签颜色(用于标签管理功能)
+ setTagColor(tagName, color) {
+ if (!tagName || typeof tagName !== 'string') return false;
+
+ const normalizedTag = tagName.trim();
+ const oldColor = this.colorMap.get(normalizedTag);
+
+ console.log(`设置标签颜色: ${normalizedTag} -> ${color}`);
+
+ // 移除旧颜色
+ if (oldColor) {
+ this.usedColors.delete(oldColor);
+ }
+
+ // 设置新颜色
+ this.colorMap.set(normalizedTag, color);
+ this.usedColors.add(color);
+
+ return true; // 不立即同步,由调用方统一处理
+ }
+
+ // 获取标签的RGB值(用于CSS动画中的rgba())
+ getTagColorRGB(tagName) {
+ const hslColor = this.getTagColor(tagName);
+ return this.hslToRgb(hslColor);
+ }
+
+ // 将HSL颜色转换为RGB值字符串(如:"255, 100, 50")
+ hslToRgb(hslString) {
+ // 解析HSL字符串,如 "hsl(120, 75%, 50%)"
+ const match = hslString.match(/hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)/);
+ if (!match) return '102, 102, 102'; // 默认灰色的RGB
+
+ const h = parseInt(match[1]) / 360;
+ const s = parseInt(match[2]) / 100;
+ const l = parseInt(match[3]) / 100;
+
+ const hue2rgb = (p, q, t) => {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1/6) return p + (q - p) * 6 * t;
+ if (t < 1/2) return q;
+ if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ return p;
+ };
+
+ let r, g, b;
+
+ if (s === 0) {
+ r = g = b = l; // 无饱和度时为灰色
+ } else {
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ const p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1/3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1/3);
+ }
+
+ return `${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(b * 255)}`;
+ }
+
+ // 生成唯一的随机颜色
+ generateUniqueColor() {
+ let attempts = 0;
+ let color;
+
+ do {
+ color = this.generateRandomColor();
+ attempts++;
+ } while (this.usedColors.has(color) && attempts < 50);
+
+ return color;
+ }
+
+ // 生成较明亮的随机颜色
+ generateRandomColor() {
+ const hue = Math.floor(Math.random() * 360);
+ const saturation = 65 + Math.floor(Math.random() * 25); // 65-90%
+ const lightness = 45 + Math.floor(Math.random() * 15); // 45-60%
+ return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
+ }
+
+ // 获取所有标签颜色映射
+ getAllColors() {
+ return new Map(this.colorMap);
+ }
+
+ // 获取所有标签名称
+ getAllTags() {
+ const tags = Array.from(this.colorMap.keys()).sort();
+ console.log('获取所有标签:', tags);
+ return tags;
+ }
+
+ // 删除标签
+ deleteTag(tagName) {
+ if (!tagName || typeof tagName !== 'string') return false;
+
+ const normalizedTag = tagName.trim();
+ const color = this.colorMap.get(normalizedTag);
+
+ console.log(`尝试删除标签: ${normalizedTag},当前颜色映射:`, Object.fromEntries(this.colorMap));
+
+ if (color) {
+ this.colorMap.delete(normalizedTag);
+ this.usedColors.delete(color);
+
+ console.log(`标签 ${normalizedTag} 已删除,剩余标签:`, Object.fromEntries(this.colorMap));
+ return true;
+ }
+
+ console.log(`标签 ${normalizedTag} 不存在,无法删除`);
+ return false;
+ }
+
+ // 清除所有颜色映射
+ async clear() {
+ this.colorMap.clear();
+ this.usedColors.clear();
+ await this.saveToBackend();
+ }
+
+ // 更新颜色映射(从现有提示词中收集标签)
+ updateFromPrompts(prompts) {
+ if (!Array.isArray(prompts)) return;
+
+ const allTags = new Set();
+
+ prompts.forEach(prompt => {
+ if (prompt.tags && Array.isArray(prompt.tags)) {
+ prompt.tags.forEach(tag => {
+ if (tag && tag.trim()) {
+ allTags.add(tag.trim());
+ }
+ });
+ }
+ });
+
+ // 只为新发现的标签生成颜色
+ for (const tag of allTags) {
+ if (!this.colorMap.has(tag)) {
+ this.getOrCreateTagColor(tag);
+ }
+ }
+ }
+
+ // 清理未使用的标签
+ cleanupUnusedTags(prompts) {
+ if (!Array.isArray(prompts)) return;
+
+ const usedTags = new Set();
+
+ // 收集所有正在使用的标签
+ prompts.forEach(prompt => {
+ if (prompt.tags && Array.isArray(prompt.tags)) {
+ prompt.tags.forEach(tag => {
+ if (tag && tag.trim()) {
+ usedTags.add(tag.trim());
+ }
+ });
+ }
+ });
+
+ // 删除不再使用的标签
+ const tagsToDelete = [];
+ for (const [tagName] of this.colorMap) {
+ if (!usedTags.has(tagName)) {
+ tagsToDelete.push(tagName);
+ }
+ }
+
+ tagsToDelete.forEach(tagName => {
+ this.deleteTag(tagName);
+ });
+
+ if (tagsToDelete.length > 0) {
+ console.log(`清理了 ${tagsToDelete.length} 个未使用的标签:`, tagsToDelete);
+ }
+ }
+}
+
+// 创建全局单例实例
+export const globalTagColorManager = new TagColorManager();
diff --git a/extensions/Prompt_Manager/styles/components/TagManager.js b/extensions/Prompt_Manager/styles/components/TagManager.js
new file mode 100644
index 0000000..1668f5b
--- /dev/null
+++ b/extensions/Prompt_Manager/styles/components/TagManager.js
@@ -0,0 +1,1419 @@
+// 标签管理组件 - 新的气泡交互设计
+// 支持标签气泡点击选择、批量操作等功能
+
+import { globalTagColorManager } from './TagColorManager.js';
+
+export class TagManager {
+ constructor(options = {}) {
+ this.onClose = options.onClose || null;
+ this.promptManager = options.promptManager || null;
+ this.onTagColorChange = options.onTagColorChange || null;
+
+ this.container = null;
+ this.selectedTags = new Set(); // 存储选中的标签名称
+ this.createTagManager();
+ }
+
+ createTagManager() {
+ this.container = document.createElement('div');
+ this.container.className = 'tag-manager-section';
+ // 初始状态:完全隐藏
+ this.container.style.display = 'none';
+ this.container.innerHTML = `
+
+
+
+
+
+
+
+
+
+
+ 已选择 0 个标签
+
+
+
+
+
+
+
+
+
+
+
添加新标签
+
+
+
+
+
+
+
+
+
+
修改标签颜色
+
+
+
+
+
+
+ `;
+
+ this.bindEvents();
+ this.addStyles();
+ }
+
+ bindEvents() {
+ // 关闭按钮
+ const closeBtn = this.container.querySelector('.tag-manager-close-btn');
+ closeBtn.addEventListener('click', () => this.hide());
+
+ // ESC键关闭
+ document.addEventListener('keydown', this.handleKeydown.bind(this));
+
+ // 操作按钮
+ const addBtn = this.container.querySelector('.add-tag-btn');
+ const editColorBtn = this.container.querySelector('.edit-color-btn');
+ const deleteBtn = this.container.querySelector('.delete-selected-btn');
+
+ addBtn.addEventListener('click', () => this.showAddModal());
+ editColorBtn.addEventListener('click', () => this.showEditColorModal());
+ deleteBtn.addEventListener('click', () => this.handleDeleteSelected());
+
+ // 选择控制按钮
+ const selectAllBtn = this.container.querySelector('.select-all-btn');
+ const clearSelectionBtn = this.container.querySelector('.clear-selection-btn');
+
+ selectAllBtn.addEventListener('click', () => this.selectAllTags());
+ clearSelectionBtn.addEventListener('click', () => this.clearSelection());
+
+ // 添加标签弹窗
+ this.bindAddModalEvents();
+
+ // 修改颜色弹窗
+ this.bindEditColorModalEvents();
+ } bindAddModalEvents() {
+ const modal = this.container.querySelector('.add-tag-modal');
+ const form = modal.querySelector('.add-tag-form');
+ const cancelBtn = modal.querySelector('.cancel-btn');
+ const overlay = modal.querySelector('.modal-overlay');
+ const colorInput = modal.querySelector('.tag-color-input');
+ const previewTag = modal.querySelector('#add-preview-tag');
+
+ form.addEventListener('submit', (e) => {
+ e.preventDefault();
+ this.handleAddTag();
+ });
+
+ cancelBtn.addEventListener('click', () => this.hideAddModal());
+
+ overlay.addEventListener('click', (e) => {
+ if (e.target === overlay) {
+ this.hideAddModal();
+ }
+ });
+
+ // 绑定颜色选项点击事件
+ const colorOptions = modal.querySelectorAll('.color-option');
+ colorOptions.forEach(option => {
+ option.addEventListener('click', () => {
+ // 移除其他选项的选中状态
+ colorOptions.forEach(opt => opt.classList.remove('selected'));
+ // 添加当前选项的选中状态
+ option.classList.add('selected');
+
+ const color = option.dataset.color;
+ colorInput.value = color;
+ previewTag.style.background = color;
+ previewTag.style.setProperty('--tag-color', color);
+ });
+ });
+
+ // 绑定自定义颜色输入变化事件
+ colorInput.addEventListener('input', (e) => {
+ // 移除所有预设颜色的选中状态
+ colorOptions.forEach(opt => opt.classList.remove('selected'));
+
+ const color = e.target.value;
+ previewTag.style.background = color;
+ previewTag.style.setProperty('--tag-color', color);
+ });
+
+ // 初始化预览标签颜色
+ previewTag.style.background = colorInput.value;
+ previewTag.style.setProperty('--tag-color', colorInput.value);
+
+ // 默认选中第一个颜色选项
+ if (colorOptions.length > 0) {
+ colorOptions[0].classList.add('selected');
+ }
+ } bindEditColorModalEvents() {
+ const modal = this.container.querySelector('.edit-color-modal');
+ const form = modal.querySelector('.edit-color-form');
+ const cancelBtn = modal.querySelector('.cancel-btn');
+ const overlay = modal.querySelector('.modal-overlay');
+ const colorInput = modal.querySelector('#edit-tag-color');
+ const previewTag = modal.querySelector('#edit-preview-tag');
+
+ form.addEventListener('submit', (e) => {
+ e.preventDefault();
+ this.handleEditColor();
+ });
+
+ cancelBtn.addEventListener('click', () => this.hideEditColorModal());
+
+ overlay.addEventListener('click', (e) => {
+ if (e.target === overlay) {
+ this.hideEditColorModal();
+ }
+ });
+
+ // 绑定颜色选项点击事件
+ const colorOptions = modal.querySelectorAll('.color-option');
+ colorOptions.forEach(option => {
+ option.addEventListener('click', () => {
+ // 移除其他选项的选中状态
+ colorOptions.forEach(opt => opt.classList.remove('selected'));
+ // 添加当前选项的选中状态
+ option.classList.add('selected');
+
+ const color = option.dataset.color;
+ colorInput.value = color;
+ previewTag.style.background = color;
+ previewTag.style.setProperty('--tag-color', color);
+
+ // 更新所有选中标签的预览
+ this.updateSelectedTagsPreview(color);
+ });
+ });
+
+ // 绑定自定义颜色输入变化事件
+ colorInput.addEventListener('input', (e) => {
+ // 移除所有预设颜色的选中状态
+ colorOptions.forEach(opt => opt.classList.remove('selected'));
+
+ const color = e.target.value;
+ previewTag.style.background = color;
+ previewTag.style.setProperty('--tag-color', color);
+
+ // 更新所有选中标签的预览
+ this.updateSelectedTagsPreview(color);
+ });
+
+ // 初始化预览标签颜色
+ previewTag.style.background = colorInput.value;
+ previewTag.style.setProperty('--tag-color', colorInput.value);
+
+ // 默认选中第一个颜色选项
+ if (colorOptions.length > 0) {
+ colorOptions[0].classList.add('selected');
+ }
+ }
+
+ handleKeydown(e) {
+ if (e.key === 'Escape' && this.isVisible()) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ // 如果有弹窗开启,先关闭弹窗
+ const addModal = this.container.querySelector('.add-tag-modal');
+ const editModal = this.container.querySelector('.edit-color-modal');
+
+ if (addModal.style.display !== 'none') {
+ this.hideAddModal();
+ } else if (editModal.style.display !== 'none') {
+ this.hideEditColorModal();
+ } else {
+ this.hide();
+ }
+ }
+ } // 显示添加标签弹窗
+ showAddModal() {
+ const modal = this.container.querySelector('.add-tag-modal');
+ const nameInput = modal.querySelector('.tag-name-input');
+ const colorInput = modal.querySelector('.tag-color-input');
+ const previewTag = modal.querySelector('#add-preview-tag');
+ const colorOptions = modal.querySelectorAll('.color-option');
+
+ // 重置表单
+ nameInput.value = '';
+ const randomColor = this.generateRandomHex();
+ colorInput.value = randomColor;
+
+ // 更新预览标签颜色
+ previewTag.style.background = randomColor;
+ previewTag.style.setProperty('--tag-color', randomColor);
+
+ // 清除所有颜色选项的选中状态
+ colorOptions.forEach(option => option.classList.remove('selected'));
+
+ // 检查是否有匹配的预设颜色并设置选中状态
+ const matchingOption = Array.from(colorOptions).find(option =>
+ option.dataset.color.toLowerCase() === randomColor.toLowerCase()
+ );
+ if (matchingOption) {
+ matchingOption.classList.add('selected');
+ }
+ modal.style.display = 'block';
+
+ // 确保预览标签正确显示 - 在下一帧执行以确保DOM已更新
+ requestAnimationFrame(() => {
+ previewTag.style.background = randomColor;
+ previewTag.style.setProperty('--tag-color', randomColor);
+ previewTag.style.visibility = 'visible';
+ previewTag.style.opacity = '1';
+ nameInput.focus();
+ });
+ }hideAddModal() {
+ const modal = this.container.querySelector('.add-tag-modal');
+ modal.style.display = 'none';
+ }
+
+ // 显示修改颜色弹窗
+ showEditColorModal() {
+ if (this.selectedTags.size === 0) return;
+
+ const modal = this.container.querySelector('.edit-color-modal');
+ const preview = modal.querySelector('.selected-tags-preview');
+ const colorInput = modal.querySelector('#edit-tag-color');
+ const previewTag = modal.querySelector('#edit-preview-tag');
+ const colorOptions = modal.querySelectorAll('.color-option');
+
+ // 显示选中的标签预览
+ preview.innerHTML = '';
+ this.selectedTags.forEach(tagName => {
+ const color = globalTagColorManager.getTagColor(tagName);
+ const tagBubble = document.createElement('div');
+ tagBubble.className = 'preview-tag-bubble';
+ tagBubble.style.setProperty('--tag-color', color);
+ tagBubble.textContent = tagName;
+ preview.appendChild(tagBubble);
+ });
+
+ // 设置当前颜色(使用第一个选中标签的颜色)
+ const firstTag = Array.from(this.selectedTags)[0];
+ const currentColor = globalTagColorManager.getTagColor(firstTag);
+ colorInput.value = currentColor;
+
+ // 更新预览标签颜色
+ previewTag.style.background = currentColor;
+ previewTag.style.setProperty('--tag-color', currentColor);
+
+ // 检查是否有匹配的预设颜色并设置选中状态
+ colorOptions.forEach(option => option.classList.remove('selected'));
+ const matchingOption = Array.from(colorOptions).find(option =>
+ option.dataset.color.toLowerCase() === currentColor.toLowerCase()
+ ); if (matchingOption) {
+ matchingOption.classList.add('selected');
+ }
+
+ modal.style.display = 'block';
+
+ // 确保预览标签正确显示 - 在下一帧执行以确保DOM已更新
+ requestAnimationFrame(() => {
+ previewTag.style.background = currentColor;
+ previewTag.style.setProperty('--tag-color', currentColor);
+ previewTag.style.visibility = 'visible';
+ previewTag.style.opacity = '1';
+ });
+ }
+
+ hideEditColorModal() {
+ const modal = this.container.querySelector('.edit-color-modal');
+ modal.style.display = 'none';
+ }
+
+ // 处理添加标签
+ handleAddTag() {
+ const modal = this.container.querySelector('.add-tag-modal');
+ const nameInput = modal.querySelector('.tag-name-input');
+ const colorInput = modal.querySelector('.tag-color-input');
+
+ const tagName = nameInput.value.trim();
+ const tagColor = colorInput.value;
+
+ if (!tagName) {
+ alert('请输入标签名称');
+ nameInput.focus();
+ return;
+ }
+
+ // 检查标签是否已存在
+ if (globalTagColorManager.getAllTags().includes(tagName)) {
+ alert('标签已存在');
+ nameInput.focus();
+ return;
+ } try {
+ // 添加标签
+ globalTagColorManager.setTagColor(tagName, tagColor);
+
+ // 使用统一的提示词管理器进行同步
+ if (this.promptManager && this.promptManager.savePrompts) {
+ this.promptManager.savePrompts();
+ }
+
+ // 刷新标签列表
+ this.refreshTagsBubbles();
+
+ // 关闭弹窗
+ this.hideAddModal();
+
+ // 通知颜色变更
+ this.notifyTagColorChange();
+
+ } catch (error) {
+ console.error('添加标签失败:', error);
+ alert('添加标签失败: ' + error.message);
+ }
+ }
+
+ // 处理修改颜色
+ handleEditColor() {
+ const modal = this.container.querySelector('.edit-color-modal');
+ const colorInput = modal.querySelector('#edit-tag-color');
+ const newColor = colorInput.value; try {
+ // 批量修改选中标签的颜色
+ this.selectedTags.forEach(tagName => {
+ globalTagColorManager.setTagColor(tagName, newColor);
+ });
+
+ // 使用统一的提示词管理器进行同步
+ if (this.promptManager && this.promptManager.savePrompts) {
+ this.promptManager.savePrompts();
+ }
+
+ // 刷新标签列表
+ this.refreshTagsBubbles();
+
+ // 关闭弹窗
+ this.hideEditColorModal();
+
+ // 通知颜色变更
+ this.notifyTagColorChange();
+
+ } catch (error) {
+ console.error('修改标签颜色失败:', error);
+ alert('修改标签颜色失败: ' + error.message);
+ }
+ } // 处理删除选中标签
+ async handleDeleteSelected() {
+ if (this.selectedTags.size === 0) return;
+
+ const tagNames = Array.from(this.selectedTags).join('、');
+ if (confirm(`确定要删除以下标签吗?\n${tagNames}\n\n注意:这些标签将从所有提示词中移除。`)) {
+ try {
+ // 从所有提示词中移除这些标签
+ if (this.promptManager && this.promptManager.getPrompts) {
+ const prompts = this.promptManager.getPrompts();
+ let hasChanges = false;
+
+ prompts.forEach(prompt => {
+ if (prompt.tags && Array.isArray(prompt.tags)) {
+ const originalLength = prompt.tags.length;
+ prompt.tags = prompt.tags.filter(tag => !this.selectedTags.has(tag));
+ if (prompt.tags.length !== originalLength) {
+ hasChanges = true;
+ }
+ }
+ });
+
+ // 如果有更改,保存提示词数据
+ if (hasChanges) {
+ this.promptManager.savePrompts();
+ console.log('已从提示词中移除删除的标签');
+ }
+ } // 从标签颜色管理器中删除标签
+ console.log('准备删除的标签:', Array.from(this.selectedTags));
+ this.selectedTags.forEach(tagName => {
+ console.log(`删除标签: ${tagName}`);
+ const result = globalTagColorManager.deleteTag(tagName);
+ console.log(`删除结果: ${result}`);
+ });
+
+ // 立即同步标签数据到后端
+ console.log('立即同步标签删除到后端...');
+ await globalTagColorManager.saveToBackend();
+
+ // 使用统一的提示词管理器进行同步(与提示词操作保持一致)
+ if (this.promptManager && this.promptManager.savePrompts) {
+ console.log('通过提示词管理器同步所有数据到后端...');
+ this.promptManager.savePrompts();
+ } else {
+ console.error('提示词管理器不可用,无法同步到后端');
+ }
+
+ this.selectedTags.clear();
+ this.refreshTagsBubbles();
+ this.updateButtonStates();
+
+ // 通知标签变更,触发所有UI组件刷新
+ console.log('通知标签变更,刷新所有UI组件...');
+ this.notifyTagColorChange();
+
+ } catch (error) {
+ console.error('删除标签失败:', error);
+ alert('删除标签失败: ' + error.message);
+ }
+ }
+ }
+
+ // 标签气泡点击处理
+ handleTagBubbleClick(tagName) {
+ if (this.selectedTags.has(tagName)) {
+ // 取消选择
+ this.selectedTags.delete(tagName);
+ } else {
+ // 选择
+ this.selectedTags.add(tagName);
+ }
+
+ this.updateTagBubbleState(tagName);
+ this.updateButtonStates();
+ this.updateSelectionInfo();
+ }
+
+ // 更新标签气泡状态
+ updateTagBubbleState(tagName) {
+ const bubble = this.container.querySelector(`[data-tag="${tagName}"]`);
+ if (bubble) {
+ if (this.selectedTags.has(tagName)) {
+ bubble.classList.add('selected');
+ } else {
+ bubble.classList.remove('selected');
+ }
+ }
+ }
+
+ // 更新选中标签的预览颜色
+ updateSelectedTagsPreview(color) {
+ const preview = this.container.querySelector('.selected-tags-preview');
+ if (preview) {
+ const tagBubbles = preview.querySelectorAll('.preview-tag-bubble');
+ tagBubbles.forEach(bubble => {
+ bubble.style.background = color;
+ bubble.style.setProperty('--tag-color', color);
+ });
+ }
+ }
+
+ // 全选标签
+ selectAllTags() {
+ const allTags = globalTagColorManager.getAllTags();
+ allTags.forEach(tagName => {
+ this.selectedTags.add(tagName);
+ this.updateTagBubbleState(tagName);
+ });
+
+ this.updateButtonStates();
+ this.updateSelectionInfo();
+ } // 清空选择
+ clearSelection() {
+ // 先清空所有选中状态的视觉反馈
+ this.selectedTags.forEach(tagName => {
+ const bubble = this.container.querySelector(`[data-tag="${tagName}"]`);
+ if (bubble) {
+ bubble.classList.remove('selected');
+ }
+ });
+
+ // 然后清空选中标签集合
+ this.selectedTags.clear();
+ this.updateButtonStates();
+ this.updateSelectionInfo();
+ }
+
+ // 更新按钮状态
+ updateButtonStates() {
+ const editColorBtn = this.container.querySelector('.edit-color-btn');
+ const deleteBtn = this.container.querySelector('.delete-selected-btn');
+
+ const hasSelection = this.selectedTags.size > 0;
+
+ editColorBtn.disabled = !hasSelection;
+ deleteBtn.disabled = !hasSelection;
+ }
+
+ // 更新选择信息
+ updateSelectionInfo() {
+ const selectedCount = this.container.querySelector('.selected-count');
+ selectedCount.textContent = `已选择 ${this.selectedTags.size} 个标签`;
+ } // 刷新标签气泡列表
+ refreshTagsBubbles() {
+ console.log('TagManager: 开始刷新标签气泡列表...');
+
+ const container = this.container.querySelector('.tags-bubbles-container');
+ const tagsCount = this.container.querySelector('.tags-count');
+ const allTags = globalTagColorManager.getAllTags();
+
+ console.log('TagManager: 从TagColorManager获取到的标签:', allTags);
+
+ // 更新标签计数
+ tagsCount.textContent = `(${allTags.length})`;
+
+ if (allTags.length === 0) {
+ console.log('TagManager: 无标签,显示空状态...');
+ container.innerHTML = `
+
+ `;
+ return;
+ }
+
+ // 清理已删除标签的选择状态
+ const deletedTags = [];
+ this.selectedTags.forEach(selectedTag => {
+ if (!allTags.includes(selectedTag)) {
+ deletedTags.push(selectedTag);
+ }
+ });
+
+ deletedTags.forEach(tag => {
+ console.log(`TagManager: 清理已删除标签的选择状态: ${tag}`);
+ this.selectedTags.delete(tag);
+ });
+
+ // 生成标签气泡
+ console.log('TagManager: 生成标签气泡...');
+ container.innerHTML = '';
+ allTags.forEach(tagName => {
+ const color = globalTagColorManager.getTagColor(tagName);
+ const bubble = document.createElement('div');
+ bubble.className = 'tag-bubble';
+ bubble.dataset.tag = tagName;
+ bubble.style.setProperty('--tag-color', color);
+ bubble.textContent = tagName;
+
+ // 恢复选中状态
+ if (this.selectedTags.has(tagName)) {
+ bubble.classList.add('selected');
+ }
+
+ // 绑定点击事件
+ bubble.addEventListener('click', () => {
+ this.handleTagBubbleClick(tagName);
+ });
+
+ container.appendChild(bubble);
+ });
+ // 更新按钮状态和选择信息
+ this.updateButtonStates();
+ this.updateSelectionInfo();
+
+ console.log('TagManager: 标签气泡列表刷新完成');
+ }
+
+ // 显示/隐藏/可见性检查方法
+ show() {
+ // 查找布局容器
+ let layoutContainer = this.container.closest('.prompt-manager-layout');
+ if (!layoutContainer) {
+ const formSection = this.container.parentNode?.closest('.prompt-manager-form');
+ if (formSection) {
+ layoutContainer = formSection.closest('.prompt-manager-layout');
+ }
+ }
+
+ if (layoutContainer) {
+ layoutContainer.classList.add('form-expanded');
+ }
+
+ // 显示标签管理器容器
+ this.container.style.display = 'flex';
+ this.container.classList.add('active');
+
+ // 等待下一帧确保DOM更新
+ requestAnimationFrame(() => {
+ // 刷新标签列表
+ this.refreshTagsBubbles();
+ this.updateButtonStates();
+ this.updateSelectionInfo();
+ });
+ }
+
+ hide() {
+ // 隐藏标签管理器容器
+ this.container.classList.remove('active');
+ this.container.style.display = 'none';
+
+ // 清空选择
+ this.selectedTags.clear();
+
+ // 查找布局容器,检查是否还有其他内容需要显示
+ const layoutContainer = this.container.closest('.prompt-manager-layout');
+ if (layoutContainer) {
+ const formSection = layoutContainer.querySelector('.prompt-manager-form');
+ const promptForm = formSection?.querySelector('.prompt-form-section');
+ const isPromptFormVisible = promptForm && promptForm.style.visibility !== 'hidden';
+
+ // 只有当没有其他内容显示时才移除展开类
+ if (!isPromptFormVisible) {
+ layoutContainer.classList.remove('form-expanded');
+ }
+ }
+ // 调用关闭回调
+ if (this.onClose) {
+ this.onClose();
+ }
+
+ // 通知标签变更(确保搜索栏能获取到最新的标签列表)
+ this.notifyTagColorChange();
+ }
+
+ isVisible() {
+ return this.container.classList.contains('active') &&
+ this.container.style.display !== 'none';
+ }
+
+ getElement() {
+ return this.container;
+ }
+
+ destroy() {
+ if (this.isVisible()) {
+ this.hide();
+ }
+
+ if (this.container && this.container.parentNode) {
+ this.container.parentNode.removeChild(this.container);
+ }
+
+ // 移除事件监听器
+ document.removeEventListener('keydown', this.handleKeydown.bind(this));
+ }
+
+ // 工具方法
+ generateRandomHex() {
+ const hue = Math.random() * 360;
+ const saturation = 0.6 + Math.random() * 0.3; // 60%-90%
+ const lightness = 0.4 + Math.random() * 0.2; // 40%-60%
+
+ const c = (1 - Math.abs(2 * lightness - 1)) * saturation;
+ const x = c * (1 - Math.abs((hue / 60) % 2 - 1));
+ const m = lightness - c / 2;
+
+ let r, g, b;
+
+ if (hue < 60) {
+ r = c; g = x; b = 0;
+ } else if (hue < 120) {
+ r = x; g = c; b = 0;
+ } else if (hue < 180) {
+ r = 0; g = c; b = x;
+ } else if (hue < 240) {
+ r = 0; g = x; b = c;
+ } else if (hue < 300) {
+ r = x; g = 0; b = c;
+ } else {
+ r = c; g = 0; b = x;
+ }
+
+ r = Math.round((r + m) * 255);
+ g = Math.round((g + m) * 255);
+ b = Math.round((b + m) * 255);
+
+ return "#" + [r, g, b].map(x => {
+ const hex = x.toString(16);
+ return hex.length === 1 ? "0" + hex : hex;
+ }).join("");
+ }
+
+ notifyTagColorChange() {
+ if (this.onTagColorChange) {
+ this.onTagColorChange();
+ }
+ }
+
+ addStyles() {
+ const style = document.createElement('style');
+ style.textContent = `
+ /* ========== 标签管理器主容器 ========== */
+ .tag-manager-section {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: transparent;
+ display: none;
+ flex-direction: column;
+ overflow: hidden;
+ z-index: 2;
+ }
+
+ .tag-manager-section.active {
+ display: flex;
+ }
+
+ /* ========== 标签管理器头部 ========== */
+ .tag-manager-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px 24px 16px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ background: linear-gradient(135deg, #2a2a2d, #232326);
+ position: relative;
+ flex-shrink: 0;
+ }
+
+ .tag-manager-header::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+ }
+
+ .tag-manager-title {
+ margin: 0;
+ color: #ffffff;
+ font-size: 18px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ letter-spacing: 0.5px;
+ }
+
+ .title-icon {
+ font-size: 20px;
+ opacity: 0.9;
+ }
+
+ .tag-manager-close-btn {
+ background: none;
+ border: none;
+ color: #888;
+ font-size: 18px;
+ cursor: pointer;
+ padding: 8px;
+ border-radius: 6px;
+ transition: all 0.3s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ }
+
+ .tag-manager-close-btn:hover {
+ color: #fff;
+ background: rgba(255, 255, 255, 0.1);
+ } /* ========== 标签管理器主体 ========== */
+ .tag-manager-body {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ padding: 20px;
+ gap: 20px;
+ min-height: 0; /* 确保flex子项能正确收缩 */
+ } /* ========== 操作按钮区域 ========== */
+ .tag-actions-section {
+ background: linear-gradient(145deg, #1e1e21, #2a2a2d);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ padding: 12px;
+ flex-shrink: 0;
+ }.action-buttons {
+ display: flex;
+ gap: 8px;
+ margin-bottom: 12px;
+ flex-wrap: wrap;
+ }
+
+ .action-btn {
+ background: linear-gradient(145deg, #3a3a3d, #2d2d30);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ border-radius: 8px;
+ color: #ffffff;
+ padding: 8px 12px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 13px;
+ font-weight: 500;
+ flex: 1;
+ justify-content: center;
+ min-width: 0;
+ white-space: nowrap;
+ }
+
+ .action-btn:hover:not(:disabled) {
+ background: linear-gradient(145deg, #4a4a4d, #3d3d40);
+ border-color: rgba(255, 255, 255, 0.25);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
+ }
+
+ .action-btn:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ background: linear-gradient(145deg, #2a2a2d, #1e1e21);
+ border-color: rgba(255, 255, 255, 0.05);
+ } .action-btn .btn-icon {
+ font-size: 14px;
+ flex-shrink: 0;
+ }
+
+ .action-btn .btn-text {
+ flex-shrink: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .selection-info {
+ text-align: center;
+ color: #ccc;
+ font-size: 13px;
+ } /* ========== 标签气泡区域 ========== */
+ .tags-bubbles-section {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ background: linear-gradient(145deg, #1e1e21, #2a2a2d);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ min-height: 0; /* 确保flex子项能正确收缩 */
+ } .section-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 20px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ flex-shrink: 0;
+ min-height: 56px; /* 确保有足够的高度避免按钮重叠 */
+ }
+
+ .section-title {
+ margin: 0;
+ color: #e0e0e0;
+ font-size: 15px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ } .section-actions {
+ display: flex;
+ gap: 8px;
+ flex-shrink: 0;
+ align-items: center;
+ } .select-all-btn, .clear-selection-btn {
+ background: linear-gradient(145deg, #3a3a3d, #2d2d30);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ border-radius: 6px;
+ color: #ffffff;
+ padding: 6px 12px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ font-size: 12px;
+ white-space: nowrap;
+ min-width: 80px;
+ text-align: center;
+ }
+
+ .select-all-btn:hover, .clear-selection-btn:hover {
+ background: linear-gradient(145deg, #4a4a4d, #3d3d40);
+ border-color: rgba(255, 255, 255, 0.25);
+ } .tags-bubbles-container {
+ flex: 1;
+ overflow-y: auto;
+ padding: 20px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ align-content: flex-start;
+ align-items: flex-start;
+ justify-content: flex-start;
+ min-height: 0;
+ }.tag-bubble {
+ background: var(--tag-color, #2a82e4);
+ color: white;
+ padding: 8px 16px;
+ border-radius: 20px;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ border: 2px solid transparent;
+ user-select: none;
+ position: relative;
+ overflow: hidden;
+ letter-spacing: 0.25px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
+ text-align: center;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ transform: scale(1);
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000,
+ 0 -1px 0 #000,
+ 0 1px 0 #000,
+ -1px 0 0 #000,
+ 1px 0 0 #000;
+ } .tag-bubble::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), transparent);
+ border-radius: inherit;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ pointer-events: none;
+ }
+
+ .tag-bubble:hover {
+ box-shadow:
+ 0 0 10px rgba(255, 255, 255, 0.3),
+ 0 4px 15px rgba(0, 0, 0, 0.2);
+ transform: scale(1.05);
+ border-color: rgba(255, 255, 255, 0.4);
+ filter: brightness(1.1) saturate(1.05);
+ }
+
+ .tag-bubble:hover::before {
+ opacity: 1;
+ } .tag-bubble.selected {
+ border-color: #ffffff;
+ box-shadow:
+ 0 0 0 2px rgba(255, 255, 255, 0.6),
+ 0 0 15px rgba(255, 255, 255, 0.4),
+ 0 4px 20px rgba(0, 0, 0, 0.3);
+ transform: scale(1.02);
+ filter: brightness(1.15) saturate(1.1);
+ }
+
+ .tag-bubble.selected::before {
+ opacity: 1;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.05));
+ }
+
+ .no-tags-message {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ width: 100%;
+ height: 100%;
+ color: #888;
+ gap: 12px;
+ }
+
+ .empty-icon {
+ font-size: 48px;
+ opacity: 0.6;
+ }
+
+ .no-tags-message p {
+ margin: 0;
+ font-size: 16px;
+ font-weight: 500;
+ }
+
+ .no-tags-message small {
+ font-size: 13px;
+ opacity: 0.8;
+ }
+
+ /* ========== 弹窗样式 ========== */
+ .add-tag-modal, .edit-color-modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 10000;
+ display: none;
+ }
+
+ .modal-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.7);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ backdrop-filter: blur(4px);
+ }
+
+ .modal-content {
+ background: linear-gradient(145deg, #2d2d30, #252528);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+ padding: 24px;
+ max-width: 400px;
+ width: 90%;
+ box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4);
+ }
+
+ .modal-title {
+ margin: 0 0 20px 0;
+ color: #ffffff;
+ font-size: 18px;
+ font-weight: 600;
+ text-align: center;
+ }
+
+ .form-group {
+ margin-bottom: 16px;
+ }
+
+ .form-group label {
+ display: block;
+ color: #e0e0e0;
+ font-size: 14px;
+ font-weight: 500;
+ margin-bottom: 8px;
+ }
+
+ .form-group input {
+ width: 100%;
+ background: linear-gradient(145deg, #141417, #1a1a1d);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ border-radius: 8px;
+ color: #ffffff;
+ padding: 12px 16px;
+ font-size: 14px;
+ transition: all 0.3s ease;
+ box-sizing: border-box;
+ }
+
+ .form-group input:focus {
+ outline: none;
+ border-color: #2a82e4;
+ box-shadow: 0 0 0 2px rgba(42, 130, 228, 0.2);
+ } .form-actions {
+ display: flex;
+ gap: 12px;
+ margin-top: 24px;
+ }.confirm-btn, .cancel-btn {
+ flex: 1;
+ padding: 12px 20px;
+ border: none;
+ border-radius: 8px;
+ font-size: 14px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ white-space: nowrap;
+ text-align: center;
+ }
+
+ .confirm-btn {
+ background: linear-gradient(145deg, #2a82e4, #1e5fb8);
+ color: white;
+ }
+
+ .confirm-btn:hover {
+ background: linear-gradient(145deg, #3a92f4, #2e6fc8);
+ transform: translateY(-1px);
+ }
+
+ .cancel-btn {
+ background: linear-gradient(145deg, #3a3a3d, #2d2d30);
+ color: #ffffff;
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ }
+
+ .cancel-btn:hover {
+ background: linear-gradient(145deg, #4a4a4d, #3d3d40);
+ border-color: rgba(255, 255, 255, 0.25);
+ }
+
+ .selected-tags-preview {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-bottom: 16px;
+ padding: 12px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 8px;
+ } .preview-tag-bubble {
+ background: var(--tag-color, #2a82e4);
+ color: white;
+ padding: 6px 12px;
+ border-radius: 16px;
+ font-size: 12px;
+ font-weight: 500;
+ letter-spacing: 0.25px;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+ text-align: center;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000,
+ 0 -1px 0 #000,
+ 0 1px 0 #000,
+ -1px 0 0 #000,
+ 1px 0 0 #000;
+ } /* ========== 调色板样式 ========== */
+ .color-palette-container {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ }
+
+ .preset-colors {
+ display: grid;
+ grid-template-columns: repeat(6, 1fr);
+ gap: 8px;
+ padding: 12px;
+ background: linear-gradient(145deg, #1a1a1a, #252525);
+ border-radius: 8px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ }
+
+ .color-option {
+ width: 32px;
+ height: 32px;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ border: 2px solid transparent;
+ position: relative;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
+ }
+
+ .color-option:hover {
+ transform: scale(1.1);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+ }
+
+ .color-option.selected {
+ border-color: #ffffff;
+ box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.3);
+ transform: scale(1.1);
+ }
+
+ .color-option.selected::after {
+ content: '✓';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ color: #ffffff;
+ font-size: 14px;
+ font-weight: bold;
+ text-shadow: 0 0 3px rgba(0, 0, 0, 0.8);
+ }
+
+ .custom-color-section {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 8px 12px;
+ background: linear-gradient(145deg, #1a1a1a, #252525);
+ border-radius: 8px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ }
+
+ .custom-color-section label {
+ margin: 0 !important;
+ font-size: 13px;
+ color: #ccc;
+ white-space: nowrap;
+ }
+
+ .custom-color-section .tag-color-input {
+ width: 40px !important;
+ height: 32px !important;
+ padding: 0 !important;
+ border: 2px solid rgba(255, 255, 255, 0.2) !important;
+ border-radius: 6px !important;
+ cursor: pointer;
+ background: none !important;
+ }
+
+ .custom-color-section .tag-color-input:hover {
+ border-color: rgba(255, 255, 255, 0.4) !important;
+ transform: scale(1.05);
+ } .color-preview {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 12px;
+ background: linear-gradient(145deg, #1a1a1a, #252525);
+ border-radius: 8px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ min-height: 60px;
+ width: 100%;
+ box-sizing: border-box;
+ } .preview-tag {
+ background: var(--tag-color, #2a82e4) !important;
+ color: white !important;
+ padding: 8px 16px !important;
+ border-radius: 16px !important;
+ font-size: 13px !important;
+ font-weight: 500 !important;
+ letter-spacing: 0.25px !important;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important;
+ text-align: center !important;
+ border: 1px solid rgba(255, 255, 255, 0.1) !important;
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000,
+ 0 -1px 0 #000,
+ 0 1px 0 #000,
+ -1px 0 0 #000,
+ 1px 0 0 #000 !important;
+ transition: all 0.3s ease !important;
+ display: inline-flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+ white-space: nowrap !important;
+ margin: 0 !important;
+ position: relative !important;
+ z-index: 1 !important;
+ opacity: 1 !important;
+ visibility: visible !important;
+ line-height: 1 !important;
+ vertical-align: middle !important;
+ }
+
+ /* ========== 滚动条样式 ========== */
+ .tags-bubbles-container::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ .tags-bubbles-container::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.1);
+ border-radius: 3px;
+ }
+
+ .tags-bubbles-container::-webkit-scrollbar-thumb {
+ background: linear-gradient(145deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.1));
+ border-radius: 3px;
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ }
+
+ .tags-bubbles-container::-webkit-scrollbar-thumb:hover {
+ background: linear-gradient(145deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.2));
+ }
+ `;
+
+ if (!document.head.querySelector('style[data-component="tag-manager-new"]')) {
+ style.setAttribute('data-component', 'tag-manager-new');
+ document.head.appendChild(style);
+ }
+ }
+}
diff --git a/extensions/Prompt_Manager/styles/components/index.js b/extensions/Prompt_Manager/styles/components/index.js
new file mode 100644
index 0000000..9596ae2
--- /dev/null
+++ b/extensions/Prompt_Manager/styles/components/index.js
@@ -0,0 +1,80 @@
+// 组件统一导出文件
+// 方便在其他地方统一导入所有UI组件
+
+export { MainModal } from './MainModal.js';
+export { SearchBar } from './SearchBar.js';
+export { PromptList } from './PromptList.js';
+export { PromptForm } from './PromptForm.js';
+export { DialogComponents } from './DialogComponents.js';
+export { TagColorManager, globalTagColorManager } from './TagColorManager.js';
+export { TagManager } from './TagManager.js';
+
+// 组件版本信息
+export const COMPONENT_VERSION = '1.0.0';
+
+// 组件元数据
+export const COMPONENT_METADATA = {
+ MainModal: {
+ version: '1.0.0',
+ description: '主模态框组件,负责整体模态框结构和基础样式',
+ dependencies: []
+ },
+ SearchBar: {
+ version: '1.0.0',
+ description: '搜索栏组件,负责提示词搜索功能',
+ dependencies: []
+ },
+ PromptList: {
+ version: '1.0.0',
+ description: '列表组件,负责提示词列表的显示和交互',
+ dependencies: []
+ },
+ PromptForm: {
+ version: '1.0.0',
+ description: '表单组件,负责添加/编辑提示词的表单',
+ dependencies: []
+ },
+ DialogComponents: {
+ version: '1.0.0',
+ description: '对话框组件,负责各种确认对话框和选择器',
+ dependencies: []
+ }
+};
+
+// 验证所有组件是否正确加载
+export function validateComponents() {
+ const components = [
+ 'MainModal',
+ 'SearchBar',
+ 'PromptList',
+ 'PromptForm',
+ 'DialogComponents'
+ ];
+
+ const results = {};
+ let allValid = true;
+
+ components.forEach(componentName => {
+ try {
+ const Component = eval(componentName);
+ results[componentName] = {
+ loaded: true,
+ constructor: typeof Component === 'function',
+ metadata: COMPONENT_METADATA[componentName]
+ };
+ } catch (error) {
+ results[componentName] = {
+ loaded: false,
+ error: error.message,
+ metadata: COMPONENT_METADATA[componentName]
+ };
+ allValid = false;
+ }
+ });
+
+ return {
+ allValid,
+ components: results,
+ version: COMPONENT_VERSION
+ };
+}
diff --git a/pyproject.toml b/pyproject.toml
index 0de7cf7..b30f3a4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "comfyui-dd-nodes"
description = "Advanced utility nodes for ComfyUI with intelligent layout and animation features."
-version = "2.5.0"
+version = "2.6.0"
license = {file = "LICENSE"}
dependencies = ["git+https://github.com/Dontdrunk/ComfyUI-DD-Nodes.git"]