// ==UserScript==
// @name Prompt Manager
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Fixed focus issue with search, import, and export (search below list)
// @author yowaimono
// @match https://grok.com/chat/*
// @match https://askmanyai.cn/chat/*
// @match https://chat.deepseek.com/a/chat/*
// @match https://yuanbao.tencent.com/*
// @match https://kimi.moonshot.cn/chat/*
// @match https://www.wenxiaobai.com/chat/*
// @match https://chatgpt.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=greasyfork.org
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 配置数据
const STORAGE_KEY = 'promptManagerData';
let prompts = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
let filteredPrompts = [...prompts]; // 用于存储搜索结果
let currentEditIndex = null;
let isMinimized = false;
let lastFocusedElement = null; // 新增:记录最后聚焦的元素
// 创建主容器
const container = document.createElement('div');
container.id = 'prompt-manager';
container.innerHTML = `
<div class="pm-header">
<h3>提示词管理</h3>
<div class="pm-header-buttons">
<button class="pm-icon-btn" id="pm-export">
<span class="pm-icon">⬇</span>
</button>
<button class="pm-icon-btn" id="pm-import">
<span class="pm-icon">⬆</span>
</button>
<button class="pm-icon-btn" id="pm-minimize">
<span class="pm-icon">-</span>
</button>
<button class="pm-icon-btn" id="pm-add">
<span class="pm-icon">+</span>
</button>
</div>
</div>
<div class="pm-list" id="pm-list"></div>
<div class="pm-search-container">
<input type="text" id="pm-search" placeholder="搜索提示词" class="pm-input">
</div>
<div class="pm-modal" id="pm-modal">
<div class="pm-modal-content">
<div class="pm-modal-header">
<h4>${currentEditIndex !== null ? '编辑提示词' : '新建提示词'}</h4>
<button class="pm-icon-btn" id="pm-close">
<span class="pm-icon">×</span>
</button>
</div>
<div class="pm-modal-body">
<input type="text" id="pm-title" placeholder="请输入标题" class="pm-input">
<textarea id="pm-content" placeholder="请输入内容" class="pm-textarea"></textarea>
</div>
<div class="pm-modal-footer">
<button class="pm-btn pm-primary" id="pm-save">保存</button>
<button class="pm-btn" id="pm-cancel">取消</button>
</div>
</div>
</div>
<input type="file" id="pm-import-file" style="display: none;" accept=".json">
`;
document.body.appendChild(container);
// 主样式(完全保持原样)
const style = document.createElement('style');
style.textContent = `
#prompt-manager {
position: fixed;
top: 20px;
right: 20px;
width: 320px;
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
z-index: 9999;
font-family: 'Helvetica Neue', Arial, sans-serif;
transition: all 0.3s ease;
}
#prompt-manager.minimized {
width: 40px;
height: 40px;
border-radius: 50%;
overflow: hidden;
cursor: pointer;
}
#prompt-manager.minimized .pm-header {
border-bottom: none;
padding: 0;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
#prompt-manager.minimized .pm-header h3 {
display: none;
}
#prompt-manager.minimized .pm-header-buttons {
display: none;
}
#prompt-manager.minimized .pm-list,
#prompt-manager.minimized .pm-modal,
#prompt-manager.minimized .pm-search-container {
display: none !important;
}
#prompt-manager.minimized .pm-header::before {
content: 'AI';
color: #1890ff;
font-size: 16px;
font-weight: bold;
}
.pm-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
border-bottom: 1px solid #f0f0f0;
}
.pm-header h3 {
margin: 0;
font-size: 16px;
color: #1f1f1f;
}
.pm-header-buttons {
display: flex;
gap: 8px;
align-items: center; /* 垂直居中 */
}
.pm-icon-btn {
width: 32px;
height: 32px;
border: none;
background: #1890ff;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
color: #fff;
}
.pm-icon-btn:hover {
background: #40a9ff;
}
.pm-icon-btn#pm-minimize {
background: #blue;
color: blue;
border: 1px solid #1890ff;
font-size: 14px;
font-weight: bold;
}
.pm-icon {
color: #fff;
font-size: 20px;
line-height: 1;
}
.pm-list {
max-height: 150px; /* 固定高度为200px */
overflow-y: auto;
padding: 8px;
}
.pm-item {
display: flex;
align-items: center;
padding: 12px;
margin: 4px;
background: #f8fafb;
border-radius: 8px;
transition: background 0.2s;
cursor: pointer;
}
.pm-item:hover {
background: #e6f4ff;
}
.pm-item-title {
flex: 1;
font-size: 14px;
color: #434343;
overflow: hidden;
text-overflow: ellipsis;
}
.pm-item-actions {
display: flex;
gap: 8px;
opacity: 0;
transition: opacity 0.2s;
}
.pm-item:hover .pm-item-actions {
opacity: 1;
}
.pm-modal {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
justify-content: center;
align-items: center;
overflow: auto;
}
.pm-modal-content {
background: #fff;
width: 440px;
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
max-height: 90vh;
overflow: auto;
}
.pm-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
border-bottom: 1px solid #f0f0f0;
}
.pm-modal-header h4 {
margin: 0;
font-size: 16px;
color: #1d1d1d;
}
.pm-modal-body {
padding: 16px;
}
.pm-input, .pm-textarea {
width: 90%;
padding: 8px 12px;
border: 1px solid #e0e0e0;
border-radius: 6px;
margin: 8px 0;
font-size: 14px;
transition: border-color 0.2s;
}
.pm-input:focus, .pm-textarea:focus {
border-color: #1890ff;
outline: none;
}
.pm-textarea {
height: 100px;
resize: vertical;
}
.pm-modal-footer {
padding: 16px;
text-align: right;
border-top: 1px solid #f0f0f0;
}
.pm-btn {
padding: 8px 16px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.pm-primary {
background: #1890ff;
color: #fff;
}
.pm-primary:hover {
background: #40a9ff;
}
.pm-btn:not(.pm-primary) {
background: #f5f5f5;
color: #666;
margin-left: 8px;
}
.pm-btn:not(.pm-primary):hover {
background: #e0e0e0;
}
.pm-search-container {
padding: 8px;
border-top: 1px solid #f0f0f0;
}
`;
document.head.appendChild(style);
// 新增:焦点追踪逻辑
document.addEventListener('focusin', (e) => {
if (!container.contains(e.target)) {
const target = e.target;
if (target.matches('input, textarea, [contenteditable="true"]')) {
lastFocusedElement = target;
}
}
});
// 渲染列表
function renderList() {
const list = document.getElementById('pm-list');
list.innerHTML = ''; // 清空列表
filteredPrompts.forEach((item, index) => {
const listItem = document.createElement('div');
listItem.classList.add('pm-item');
listItem.innerHTML = `
<span class="pm-item-title">${item.title}</span>
<div class="pm-item-actions">
<button class="pm-icon-btn" style="background:#52c41a">
<span class="pm-icon">✎</span>
</button>
<button class="pm-icon-btn" style="background:#ff4d4f">
<span class="pm-icon">✕</span>
</button>
</div>
`;
// 修改事件处理
const [editBtn, deleteBtn] = listItem.querySelectorAll('.pm-icon-btn');
// 阻止按钮获取焦点
editBtn.addEventListener('mousedown', e => e.preventDefault());
deleteBtn.addEventListener('mousedown', e => e.preventDefault());
editBtn.addEventListener('click', (e) => {
e.stopPropagation();
editPrompt(prompts.indexOf(item)); // 传递原始索引
});
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
deletePrompt(prompts.indexOf(item)); // 传递原始索引
});
// 阻止列表项获取焦点
listItem.addEventListener('mousedown', e => e.preventDefault());
listItem.addEventListener('click', () => {
if (lastFocusedElement) {
pasteToFocusedElement(item.content);
} else {
alert('请先点击需要输入的位置');
}
});
list.appendChild(listItem);
});
}
// 修改粘贴函数
function pasteToFocusedElement(text) {
if (!lastFocusedElement) return;
try {
// 强制恢复焦点
lastFocusedElement.focus();
if (lastFocusedElement.isContentEditable) {
const selection = window.getSelection();
const range = selection.getRangeAt(0);
range.deleteContents();
const textNode = document.createTextNode(text);
range.insertNode(textNode);
range.setStartAfter(textNode);
selection.collapseToEnd();
} else {
const elem = lastFocusedElement;
const start = elem.selectionStart;
elem.setRangeText(text, start, start, 'end');
elem.selectionStart = elem.selectionEnd = start + text.length;
}
// 触发输入事件
const event = new Event('input', { bubbles: true });
lastFocusedElement.dispatchEvent(event);
} catch (error) {
console.error('粘贴失败,使用剪贴板回退');
navigator.clipboard.writeText(text);
alert('已复制到剪贴板,请手动粘贴');
}
}
// 编辑提示词
window.editPrompt = function(index) {
currentEditIndex = index;
document.getElementById('pm-title').value = prompts[index].title;
document.getElementById('pm-content').value = prompts[index].content;
document.getElementById('pm-modal').style.display = 'flex';
};
// 删除提示词
window.deletePrompt = function(index) {
prompts.splice(index, 1);
localStorage.setItem(STORAGE_KEY, JSON.stringify(prompts));
filterPrompts(); // 重新过滤列表
};
// 添加新提示词
document.getElementById('pm-add').addEventListener('click', () => {
currentEditIndex = null;
document.getElementById('pm-title').value = '';
document.getElementById('pm-content').value = '';
document.getElementById('pm-modal').style.display = 'flex';
});
// 关闭模态框
document.getElementById('pm-close').addEventListener('click', () => {
document.getElementById('pm-modal').style.display = 'none';
});
// 取消操作
document.getElementById('pm-cancel').addEventListener('click', () => {
document.getElementById('pm-modal').style.display = 'none';
});
// 保存提示词
document.getElementById('pm-save').addEventListener('click', () => {
const title = document.getElementById('pm-title').value.trim();
const content = document.getElementById('pm-content').value.trim();
if (!title || !content) {
alert('标题和内容不能为空');
return;
}
if (currentEditIndex !== null) {
prompts[currentEditIndex] = { title, content };
} else {
prompts.push({ title, content });
}
localStorage.setItem(STORAGE_KEY, JSON.stringify(prompts));
filterPrompts(); // 刷新列表
document.getElementById('pm-modal').style.display = 'none';
});
// 最小化功能
document.getElementById('pm-minimize').addEventListener('click', (e) => {
e.stopPropagation();
isMinimized = !isMinimized;
container.classList.toggle('minimized', isMinimized);
});
container.addEventListener('click', () => {
if (isMinimized) {
isMinimized = false;
container.classList.remove('minimized');
}
});
// 导出功能
document.getElementById('pm-export').addEventListener('click', () => {
const json = JSON.stringify(prompts);
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'prompts.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
// 导入功能
document.getElementById('pm-import').addEventListener('click', () => {
document.getElementById('pm-import-file').click();
});
document.getElementById('pm-import-file').addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
try {
const json = JSON.parse(e.target.result);
if (Array.isArray(json)) {
prompts = json;
localStorage.setItem(STORAGE_KEY, JSON.stringify(prompts));
filterPrompts(); // 重新渲染列表
alert('导入成功');
} else {
alert('文件格式不正确,应为JSON数组');
}
} catch (error) {
alert('文件解析失败:' + error);
}
};
reader.readAsText(file);
}
});
// 搜索功能
const searchInput = document.getElementById('pm-search');
searchInput.addEventListener('input', () => {
filterPrompts(searchInput.value.trim());
});
// 过滤提示词
function filterPrompts(searchTerm = '') {
if (searchTerm) {
const lowerSearchTerm = searchTerm.toLowerCase();
filteredPrompts = prompts.filter(item =>
item.title.toLowerCase().includes(lowerSearchTerm) ||
item.content.toLowerCase().includes(lowerSearchTerm)
);
} else {
filteredPrompts = [...prompts]; // 恢复到所有提示词
}
renderList();
}
// 初始化
filterPrompts();
})();