Promptimizer

AI-powered prompt optimization tool that works with OpenAI-compatible APIs

目前为 2025-02-26 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Promptimizer
  3. // @namespace https://github.com/NoahTheGinger/Promptimizer/
  4. // @version 1.1
  5. // @description AI-powered prompt optimization tool that works with OpenAI-compatible APIs
  6. // @author NoahTheGinger
  7. // @match *://*/*
  8. // @license MIT
  9. // @grant GM_xmlhttpRequest
  10. // @grant GM_setValue
  11. // @grant GM_getValue
  12. // @grant GM_addStyle
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. // Styles for the UI
  19. GM_addStyle(`
  20. #promptimizer-container {
  21. position: fixed;
  22. /* Changed default position to top:20px; left:20px for more natural left/top resizing */
  23. top: 20px;
  24. left: 20px;
  25. width: 400px;
  26. height: auto; /* Let the container's height auto-adjust at the start */
  27. background: #ffffff;
  28. border-radius: 8px;
  29. box-shadow: 0 2px 10px rgba(0,0,0,0.3);
  30. z-index: 2147483647;
  31. font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
  32. display: none;
  33. color: #000000;
  34. transition: width 0.2s ease, height 0.2s ease, top 0.2s ease, left 0.2s ease;
  35. overflow: hidden;
  36. min-height: 300px;
  37. min-width: 300px;
  38. }
  39.  
  40. #promptimizer-header {
  41. padding: 12px;
  42. background: #2196F3;
  43. border-radius: 8px 8px 0 0;
  44. cursor: move;
  45. display: flex;
  46. justify-content: space-between;
  47. align-items: center;
  48. color: #ffffff;
  49. }
  50.  
  51. #promptimizer-header-controls {
  52. display: flex;
  53. gap: 12px;
  54. align-items: center;
  55. }
  56.  
  57. .header-button {
  58. background: none;
  59. border: none;
  60. color: #ffffff;
  61. cursor: pointer;
  62. font-size: 16px;
  63. padding: 0;
  64. display: flex;
  65. align-items: center;
  66. opacity: 0.8;
  67. transition: opacity 0.2s;
  68. }
  69.  
  70. .header-button:hover {
  71. opacity: 1;
  72. }
  73.  
  74. #promptimizer-title {
  75. margin: 0;
  76. font-size: 16px;
  77. font-weight: bold;
  78. color: #ffffff;
  79. }
  80.  
  81. #promptimizer-toggle {
  82. position: fixed;
  83. /* Also changed toggle to top-left for convenience; adapt as desired */
  84. top: 20px;
  85. left: 20px;
  86. width: 50px;
  87. height: 50px;
  88. background: #2196F3;
  89. border-radius: 25px;
  90. color: white;
  91. display: flex;
  92. align-items: center;
  93. justify-content: center;
  94. cursor: pointer;
  95. font-size: 24px;
  96. box-shadow: 0 2px 5px rgba(0,0,0,0.2);
  97. z-index: 2147483647;
  98. border: 2px solid rgba(255,255,255,0.2);
  99. }
  100.  
  101. /* Make the container content area fill the space under the header for better resizing */
  102. #promptimizer-content {
  103. /* We’ll rely on flexible layout to keep the text area resizing well. */
  104. display: flex;
  105. flex-direction: column;
  106. background: #ffffff;
  107. overflow: hidden;
  108. /* Subtract height of header (48px is approximate; adjust if your header is bigger/smaller) */
  109. max-height: calc(100% - 48px);
  110. }
  111.  
  112. /* Use a vertical scroll inside the content if it extends beyond container height: */
  113. .scrollable-flex {
  114. overflow-y: auto;
  115. flex: 1; /* Flexible region that takes leftover space */
  116. padding: 16px; /* Keep the content from brushing edges */
  117. }
  118.  
  119. .promptimizer-input {
  120. width: 100%;
  121. padding: 8px;
  122. margin-bottom: 10px;
  123. border: 1px solid #ddd;
  124. border-radius: 4px;
  125. box-sizing: border-box;
  126. background: #ffffff;
  127. color: #000000;
  128. }
  129.  
  130. /* Textarea specific styles */
  131. textarea.promptimizer-input {
  132. resize: none; /* We'll handle resizing through container or auto-resizing logic */
  133. min-height: 100px;
  134. transition: border-color 0.2s ease, box-shadow 0.2s ease;
  135. overflow-y: auto;
  136. }
  137.  
  138. textarea.promptimizer-input:focus {
  139. border-color: #2196F3;
  140. box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.2);
  141. outline: none;
  142. }
  143.  
  144. /* Custom resize handles for the container—moved to top & left instead of bottom & right */
  145. .resize-handle {
  146. position: absolute;
  147. background: transparent;
  148. z-index: 10;
  149. }
  150. .resize-handle-left {
  151. cursor: ew-resize;
  152. width: 8px;
  153. height: 100%;
  154. left: 0;
  155. top: 0;
  156. }
  157. .resize-handle-top {
  158. cursor: ns-resize;
  159. height: 8px;
  160. width: 100%;
  161. top: 0;
  162. left: 0;
  163. }
  164. .resize-handle-corner {
  165. cursor: nwse-resize;
  166. width: 14px;
  167. height: 14px;
  168. left: 0;
  169. top: 0;
  170. }
  171.  
  172. /* Container resize visual feedback */
  173. #promptimizer-container.resizing {
  174. transition: none;
  175. box-shadow: 0 2px 15px rgba(33, 150, 243, 0.4);
  176. }
  177.  
  178. /* Example highlight for whichever handle is being dragged. Adjust as you like. */
  179. #promptimizer-container.resizing-left {
  180. border-left: 2px solid #2196F3;
  181. }
  182. #promptimizer-container.resizing-top {
  183. border-top: 2px solid #2196F3;
  184. }
  185. #promptimizer-container.resizing-corner {
  186. border-left: 2px solid #2196F3;
  187. border-top: 2px solid #2196F3;
  188. }
  189.  
  190. .prompt-type-select {
  191. width: 100%;
  192. padding: 8px;
  193. margin-bottom: 10px;
  194. border: 1px solid #ddd;
  195. border-radius: 4px;
  196. background: #ffffff;
  197. color: #000000;
  198. font-size: 14px;
  199. cursor: pointer;
  200. }
  201.  
  202. .prompt-type-select:focus {
  203. border-color: #2196F3;
  204. outline: none;
  205. }
  206.  
  207. .input-group {
  208. margin-bottom: 16px;
  209. }
  210.  
  211. .input-label {
  212. display: block;
  213. margin-bottom: 6px;
  214. color: #000000;
  215. font-size: 14px;
  216. font-weight: 500;
  217. }
  218.  
  219. .promptimizer-button {
  220. background: #2196F3;
  221. color: white;
  222. border: none;
  223. padding: 8px 16px;
  224. border-radius: 4px;
  225. cursor: pointer;
  226. font-size: 14px;
  227. margin-bottom: 10px;
  228. width: 100%;
  229. }
  230.  
  231. .promptimizer-button:hover {
  232. background: #1976D2;
  233. }
  234.  
  235. #promptimizer-response-container {
  236. position: relative;
  237. margin-top: 16px;
  238. }
  239.  
  240. #promptimizer-response {
  241. margin-top: 0;
  242. padding: 12px;
  243. background: #f8f9fa;
  244. border-radius: 4px;
  245. white-space: pre-wrap;
  246. max-height: 300px;
  247. overflow-y: auto;
  248. color: #000000;
  249. border: 1px solid #e9ecef;
  250. }
  251.  
  252. #copy-button {
  253. position: absolute;
  254. top: 8px;
  255. right: 8px;
  256. background: #2196F3;
  257. color: white;
  258. border: none;
  259. border-radius: 4px;
  260. padding: 4px 8px;
  261. font-size: 12px;
  262. cursor: pointer;
  263. opacity: 0;
  264. transition: opacity 0.2s;
  265. z-index: 1;
  266. }
  267.  
  268. #copy-button:hover {
  269. background: #1976D2;
  270. }
  271.  
  272. #copy-button.visible {
  273. opacity: 1;
  274. }
  275.  
  276. #copy-button.copied {
  277. background: #4CAF50;
  278. }
  279.  
  280. .promptimizer-error {
  281. color: #dc3545;
  282. margin-top: 8px;
  283. font-size: 14px;
  284. background: #fff;
  285. padding: 8px;
  286. border-radius: 4px;
  287. }
  288.  
  289. .config-section {
  290. margin-bottom: 15px;
  291. overflow: hidden;
  292. transition: max-height 0.3s ease-out;
  293. }
  294.  
  295. .config-section.collapsed {
  296. max-height: 40px;
  297. }
  298.  
  299. .config-section.expanded {
  300. max-height: 300px;
  301. }
  302.  
  303. .config-header {
  304. display: flex;
  305. justify-content: space-between;
  306. align-items: center;
  307. cursor: pointer;
  308. padding: 8px 0;
  309. }
  310.  
  311. .config-header h3 {
  312. margin: 0;
  313. font-size: 16px;
  314. color: #333;
  315. }
  316.  
  317. .config-toggle {
  318. font-size: 18px;
  319. color: #666;
  320. transition: transform 0.3s;
  321. }
  322.  
  323. .config-toggle.collapsed {
  324. transform: rotate(-90deg);
  325. }
  326.  
  327. .config-content {
  328. transition: opacity 0.3s;
  329. }
  330.  
  331. .config-content.collapsed {
  332. opacity: 0;
  333. height: 0;
  334. overflow: hidden;
  335. }
  336.  
  337. .config-content.expanded {
  338. opacity: 1;
  339. height: auto;
  340. }
  341. `);
  342.  
  343. // Default dimensions and position
  344. const defaultDimensions = {
  345. width: '400px',
  346. height: 'auto',
  347. top: '20px',
  348. left: '20px'
  349. };
  350.  
  351. // Create UI elements
  352. function createUI() {
  353. // Toggle button
  354. const toggle = document.createElement('div');
  355. toggle.id = 'promptimizer-toggle';
  356. toggle.innerHTML = '✨';
  357. toggle.title = 'Toggle Promptimizer';
  358. document.body.appendChild(toggle);
  359.  
  360. // Main container
  361. const container = document.createElement('div');
  362. container.id = 'promptimizer-container';
  363. container.innerHTML = `
  364. <div id="promptimizer-header">
  365. <h2 id="promptimizer-title">Promptimizer</h2>
  366. <div id="promptimizer-header-controls">
  367. <button class="header-button" id="promptimizer-reset" title="Reset UI Position and Size">↺</button>
  368. <button class="header-button" id="promptimizer-minimize" title="Minimize">−</button>
  369. </div>
  370. </div>
  371. <!-- Note the new scrollable-flex class to hold inputs, etc. -->
  372. <div id="promptimizer-content">
  373. <div class="scrollable-flex">
  374. <div class="config-section expanded" id="config-section">
  375. <div class="config-header" id="config-header">
  376. <h3>API Configuration</h3>
  377. <span class="config-toggle">▼</span>
  378. </div>
  379. <div class="config-content expanded">
  380. <input type="text" id="api-url" class="promptimizer-input" placeholder="API URL (e.g., https://api.openai.com/v1)" />
  381. <input type="password" id="api-key" class="promptimizer-input" placeholder="API Key" />
  382. <input type="text" id="model-name" class="promptimizer-input" placeholder="Model name" />
  383. <input type="text" id="provider-name" class="promptimizer-input" placeholder="Provider name (optional, for gpt4free)" />
  384. <button id="save-config" class="promptimizer-button">Save Configuration</button>
  385. </div>
  386. </div>
  387. <div class="input-group">
  388. <label class="input-label" for="prompt-type">Prompt Type:</label>
  389. <select id="prompt-type" class="prompt-type-select">
  390. <option value="user">User Prompt</option>
  391. <option value="system">System Prompt</option>
  392. </select>
  393. </div>
  394. <div class="input-group">
  395. <label class="input-label" for="prompt-input">Enter Your Prompt:</label>
  396. <textarea id="prompt-input" class="promptimizer-input" rows="4" placeholder="Enter your prompt here..."></textarea>
  397. </div>
  398. <button id="optimize-button" class="promptimizer-button">Optimize Prompt</button>
  399. <div id="promptimizer-response-container">
  400. <button id="copy-button" style="display: none;">Copy Enhanced Prompt</button>
  401. <div id="promptimizer-response"></div>
  402. </div>
  403. </div>
  404. </div>
  405. <!-- Resizing handles moved to top & left -->
  406. <div class="resize-handle resize-handle-left"></div>
  407. <div class="resize-handle resize-handle-top"></div>
  408. <div class="resize-handle resize-handle-corner"></div>
  409. `;
  410. document.body.appendChild(container);
  411.  
  412. // Make the container draggable
  413. makeDraggable(container);
  414.  
  415. // Add container resizing
  416. makeResizable(container);
  417.  
  418. // Load saved configuration
  419. loadConfiguration();
  420.  
  421. // Event listeners
  422. toggle.addEventListener('click', () => {
  423. // Toggle the entire UI
  424. container.style.display = (container.style.display === 'none' ? 'block' : 'none');
  425. });
  426.  
  427. document.getElementById('promptimizer-minimize').addEventListener('click', () => {
  428. container.style.display = 'none';
  429. });
  430.  
  431. // Config section toggle
  432. const configHeader = document.getElementById('config-header');
  433. const configSection = document.getElementById('config-section');
  434. const configToggle = configHeader.querySelector('.config-toggle');
  435. const configContent = configSection.querySelector('.config-content');
  436.  
  437. configHeader.addEventListener('click', () => {
  438. const isExpanded = configSection.classList.contains('expanded');
  439.  
  440. if (isExpanded) {
  441. configSection.classList.remove('expanded');
  442. configSection.classList.add('collapsed');
  443. configContent.classList.remove('expanded');
  444. configContent.classList.add('collapsed');
  445. configToggle.classList.add('collapsed');
  446. } else {
  447. configSection.classList.remove('collapsed');
  448. configSection.classList.add('expanded');
  449. configContent.classList.remove('collapsed');
  450. configContent.classList.add('expanded');
  451. configToggle.classList.remove('collapsed');
  452. }
  453. });
  454.  
  455. document.getElementById('save-config').addEventListener('click', saveConfiguration);
  456. document.getElementById('optimize-button').addEventListener('click', optimizePrompt);
  457. document.getElementById('copy-button').addEventListener('click', copyEnhancedPrompt);
  458.  
  459. // Initialize auto-height for textarea
  460. const textarea = document.getElementById('prompt-input');
  461. if (textarea) {
  462. autoResizeTextarea(textarea);
  463. textarea.addEventListener('input', function() {
  464. autoResizeTextarea(this);
  465. });
  466. }
  467. }
  468.  
  469. // Make an element draggable
  470. function makeDraggable(element) {
  471. const header = element.querySelector('#promptimizer-header');
  472. let isDragging = false;
  473. let currentX;
  474. let currentY;
  475. let initialX;
  476. let initialY;
  477. let xOffset = 0;
  478. let yOffset = 0;
  479.  
  480. // Function to reset position and size
  481. function resetPosition() {
  482. element.style.transform = 'translate(0, 0)';
  483. element.style.top = defaultDimensions.top;
  484. element.style.left = defaultDimensions.left;
  485. element.style.width = defaultDimensions.width;
  486. element.style.height = defaultDimensions.height;
  487. xOffset = 0;
  488. yOffset = 0;
  489.  
  490. // Reset any set textarea dimensions
  491. const textarea = document.getElementById('prompt-input');
  492. if (textarea) {
  493. textarea.style.height = '';
  494. autoResizeTextarea(textarea);
  495. }
  496. }
  497.  
  498. // Add reset button event listener
  499. document.getElementById('promptimizer-reset').addEventListener('click', (e) => {
  500. e.stopPropagation(); // Prevent drag start
  501. resetPosition();
  502. });
  503.  
  504. header.addEventListener('mousedown', dragStart);
  505. document.addEventListener('mousemove', drag);
  506. document.addEventListener('mouseup', dragEnd);
  507.  
  508. function dragStart(e) {
  509. // If the click was on a button in the header, don't drag
  510. if (e.target.closest('.header-button')) {
  511. return;
  512. }
  513. initialX = e.clientX - xOffset;
  514. initialY = e.clientY - yOffset;
  515.  
  516. if (e.target === header || e.target.closest('#promptimizer-header')) {
  517. isDragging = true;
  518. }
  519. }
  520.  
  521. function drag(e) {
  522. if (isDragging) {
  523. e.preventDefault();
  524. currentX = e.clientX - initialX;
  525. currentY = e.clientY - initialY;
  526. xOffset = currentX;
  527. yOffset = currentY;
  528.  
  529. element.style.transform = `translate(${currentX}px, ${currentY}px)`;
  530. }
  531. }
  532.  
  533. function dragEnd() {
  534. initialX = currentX;
  535. initialY = currentY;
  536. isDragging = false;
  537.  
  538. // After dropping, compute final top/left to keep container in a new position:
  539. const rect = element.getBoundingClientRect();
  540. // The transform we used is purely visual, so we remove it and set real top/left
  541. element.style.transform = '';
  542. // Snap to new absolute left/top to remain at the same visible location
  543. element.style.left = rect.left + 'px';
  544. element.style.top = rect.top + 'px';
  545. // Reset the offsets since transform is removed
  546. xOffset = 0;
  547. yOffset = 0;
  548. }
  549.  
  550. // Initialize at default position
  551. resetPosition();
  552. }
  553.  
  554. // Make container resizable (top, left, corner)
  555. function makeResizable(container) {
  556. const leftHandle = container.querySelector('.resize-handle-left');
  557. const topHandle = container.querySelector('.resize-handle-top');
  558. const cornerHandle = container.querySelector('.resize-handle-corner');
  559.  
  560. let isResizing = false;
  561. let currentResizeType = '';
  562. let startX, startY, startWidth, startHeight, startTop, startLeft;
  563.  
  564. function startResize(e, type) {
  565. e.preventDefault();
  566. e.stopPropagation();
  567.  
  568. isResizing = true;
  569. currentResizeType = type;
  570.  
  571. // Current bounding info
  572. const rect = container.getBoundingClientRect();
  573. startWidth = rect.width;
  574. startHeight = rect.height;
  575. startTop = rect.top;
  576. startLeft = rect.left;
  577. startX = e.clientX;
  578. startY = e.clientY;
  579.  
  580. // Add visual feedback classes
  581. container.classList.add('resizing');
  582. container.classList.add(`resizing-${type}`);
  583.  
  584. document.addEventListener('mousemove', resize);
  585. document.addEventListener('mouseup', stopResize);
  586. }
  587.  
  588. function resize(e) {
  589. if (!isResizing) return;
  590.  
  591. // We’ll compute new top/left/width/height based on which handle
  592. let newWidth = startWidth;
  593. let newHeight = startHeight;
  594. let newTop = startTop;
  595. let newLeft = startLeft;
  596.  
  597. const dx = e.clientX - startX;
  598. const dy = e.clientY - startY;
  599.  
  600. switch (currentResizeType) {
  601. case 'left':
  602. newWidth = startWidth - dx;
  603. newLeft = startLeft + dx;
  604. break;
  605. case 'top':
  606. newHeight = startHeight - dy;
  607. newTop = startTop + dy;
  608. break;
  609. case 'corner':
  610. newWidth = startWidth - dx;
  611. newHeight = startHeight - dy;
  612. newLeft = startLeft + dx;
  613. newTop = startTop + dy;
  614. break;
  615. }
  616.  
  617. // Enforce minimum dimensions
  618. if (newWidth < 300) {
  619. // If it’s going below min, readjust left so we don’t jump
  620. const diff = 300 - newWidth;
  621. newWidth = 300;
  622. newLeft -= (currentResizeType === 'left' || currentResizeType === 'corner') ? diff : 0;
  623. }
  624. if (newHeight < 300) {
  625. const diff = 300 - newHeight;
  626. newHeight = 300;
  627. newTop -= (currentResizeType === 'top' || currentResizeType === 'corner') ? diff : 0;
  628. }
  629.  
  630. // Keep the container within viewport bounds if desired:
  631. const viewportWidth = window.innerWidth;
  632. const viewportHeight = window.innerHeight;
  633.  
  634. // If the top is going above the screen, clamp it
  635. if (newTop < 0) {
  636. const overshoot = 0 - newTop;
  637. newTop = 0;
  638. // Increase height accordingly if we’re pulling from the top
  639. if (currentResizeType === 'top' || currentResizeType === 'corner') {
  640. newHeight -= overshoot;
  641. }
  642. }
  643. // Similarly clamp left
  644. if (newLeft < 0) {
  645. const overshoot = 0 - newLeft;
  646. newLeft = 0;
  647. if (currentResizeType === 'left' || currentResizeType === 'corner') {
  648. newWidth -= overshoot;
  649. }
  650. }
  651. // If the container extends beyond the right edge, clamp it
  652. if (newLeft + newWidth > viewportWidth) {
  653. newWidth = viewportWidth - newLeft;
  654. }
  655. // If the container extends beyond bottom edge, clamp it
  656. if (newTop + newHeight > viewportHeight) {
  657. newHeight = viewportHeight - newTop;
  658. }
  659.  
  660. // Apply new geometry
  661. container.style.width = newWidth + 'px';
  662. container.style.height = newHeight + 'px';
  663. container.style.top = newTop + 'px';
  664. container.style.left = newLeft + 'px';
  665. }
  666.  
  667. function stopResize() {
  668. isResizing = false;
  669.  
  670. container.classList.remove('resizing');
  671. container.classList.remove(`resizing-${currentResizeType}`);
  672.  
  673. document.removeEventListener('mousemove', resize);
  674. document.removeEventListener('mouseup', stopResize);
  675.  
  676. // Re-enable transitions
  677. container.style.transition = 'width 0.2s ease, height 0.2s ease, top 0.2s ease, left 0.2s ease';
  678. }
  679.  
  680. leftHandle.addEventListener('mousedown', (e) => startResize(e, 'left'));
  681. topHandle.addEventListener('mousedown', (e) => startResize(e, 'top'));
  682. cornerHandle.addEventListener('mousedown', (e) => startResize(e, 'corner'));
  683. }
  684.  
  685. // Helper function to auto-resize textarea based on content
  686. function autoResizeTextarea(textarea) {
  687. // Save scroll position
  688. const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
  689. // Reset height temporarily to get accurate scrollHeight
  690. textarea.style.height = 'auto';
  691. // Set new height based on content (with minimum)
  692. const newHeight = Math.max(textarea.scrollHeight, 100) + 'px';
  693. // Apply the new height with transition
  694. textarea.style.transition = 'height 0.15s ease-out';
  695. textarea.style.height = newHeight;
  696. // Restore scroll position to prevent page jump
  697. window.scrollTo(0, scrollTop);
  698. }
  699.  
  700. // Save API configuration
  701. function saveConfiguration() {
  702. const apiUrl = document.getElementById('api-url').value;
  703. const apiKey = document.getElementById('api-key').value;
  704. const modelName = document.getElementById('model-name').value;
  705. const providerName = document.getElementById('provider-name').value;
  706.  
  707. GM_setValue('apiUrl', apiUrl);
  708. GM_setValue('apiKey', apiKey);
  709. GM_setValue('modelName', modelName);
  710. GM_setValue('providerName', providerName);
  711.  
  712. showResponse('Configuration saved!');
  713. }
  714.  
  715. // Load saved configuration
  716. function loadConfiguration() {
  717. const apiUrl = GM_getValue('apiUrl', '');
  718. const apiKey = GM_getValue('apiKey', '');
  719. const modelName = GM_getValue('modelName', '');
  720. const providerName = GM_getValue('providerName', '');
  721.  
  722. document.getElementById('api-url').value = apiUrl;
  723. document.getElementById('api-key').value = apiKey;
  724. document.getElementById('model-name').value = modelName;
  725. document.getElementById('provider-name').value = providerName;
  726. }
  727.  
  728. // Show response or error message
  729. function showResponse(message, isError = false) {
  730. const responseDiv = document.getElementById('promptimizer-response');
  731. const copyButton = document.getElementById('copy-button');
  732. responseDiv.textContent = message;
  733. responseDiv.className = isError ? 'promptimizer-error' : '';
  734.  
  735. // Show/hide copy button based on whether there's a successful response
  736. if (!isError && message !== 'Optimizing prompt...') {
  737. copyButton.style.display = 'block';
  738. setTimeout(() => copyButton.classList.add('visible'), 10);
  739. } else {
  740. copyButton.style.display = 'none';
  741. copyButton.classList.remove('visible');
  742. }
  743. }
  744.  
  745. // Extract enhanced prompt from response
  746. function extractEnhancedPrompt(text) {
  747. const match = text.match(/<enhanced_prompt>([\s\S]*?)<\/enhanced_prompt>/);
  748. return match ? match[1].trim() : text;
  749. }
  750.  
  751. // Copy enhanced prompt to clipboard
  752. function copyEnhancedPrompt() {
  753. const responseDiv = document.getElementById('promptimizer-response');
  754. const copyButton = document.getElementById('copy-button');
  755. const textToCopy = extractEnhancedPrompt(responseDiv.textContent);
  756.  
  757. navigator.clipboard.writeText(textToCopy).then(() => {
  758. copyButton.textContent = 'Copied!';
  759. copyButton.classList.add('copied');
  760. setTimeout(() => {
  761. copyButton.textContent = 'Copy Enhanced Prompt';
  762. copyButton.classList.remove('copied');
  763. }, 2000);
  764. }).catch(err => {
  765. console.error('Failed to copy text: ', err);
  766. });
  767. }
  768.  
  769. // Optimize prompt using the API
  770. async function optimizePrompt() {
  771. const apiUrl = GM_getValue('apiUrl', '');
  772. const apiKey = GM_getValue('apiKey', '');
  773. const modelName = GM_getValue('modelName', '');
  774. const providerName = GM_getValue('providerName', '');
  775. const promptType = document.getElementById('prompt-type').value;
  776. const promptInput = document.getElementById('prompt-input').value;
  777.  
  778. if (!apiUrl || !apiKey || !modelName) {
  779. showResponse('Please configure API settings first!', true);
  780. return;
  781. }
  782.  
  783. if (!promptInput.trim()) {
  784. showResponse('Please enter a prompt to optimize!', true);
  785. return;
  786. }
  787.  
  788. showResponse('Optimizing prompt...');
  789.  
  790. const systemPrompt = `You are a specialized prompt optimization AI focused on enhancing both user prompts and system prompts for AI interactions.
  791.  
  792. <instructions>
  793. 1. You will receive prompts marked with either <user_prompt> or <system_prompt> tags
  794. 2. ALWAYS maintain the same type of prompt in your enhancement
  795. 3. NEVER engage in conversation or provide explanations
  796. 4. ONLY return the enhanced prompt within <enhanced_prompt> tags
  797. 5. Apply appropriate prompt engineering techniques based on the prompt type:
  798.  
  799. For User Prompts:
  800. - Maintain conversational context and flow
  801. - Clarify user intent and expectations
  802. - Add specific parameters for the response
  803. - Include relevant context from prior conversation
  804. - Structure multi-part requests clearly
  805.  
  806. For System Prompts:
  807. - Define clear roles and responsibilities
  808. - Establish behavioral boundaries
  809. - Include success criteria and constraints
  810. - Structure hierarchical instructions
  811. - Define interaction patterns
  812. - Specify output formats and preferences
  813. - Include error handling instructions
  814. </instructions>
  815.  
  816. <examples>
  817. <example>
  818. <input_type>user_prompt</input_type>
  819. <input>Thanks for your help! Can you search the web for more information about this topic?</input>
  820. <enhanced_prompt>Please conduct a comprehensive web search on our current topic with the following parameters:
  821. 1. Focus on authoritative sources from the last 2 years
  822. 2. Include academic and expert perspectives
  823. 3. Compare and contrast different viewpoints
  824. 4. Identify emerging trends and developments
  825. 5. Extract key insights and practical applications
  826.  
  827. Format the response with:
  828. - Main findings in bullet points
  829. - Source citations for each major claim
  830. - Relevance assessment for each source
  831. - Synthesis of conflicting information
  832. - Suggestions for further research</enhanced_prompt>
  833. </example>
  834.  
  835. <example>
  836. <input_type>system_prompt</input_type>
  837. <input>You are a web search AI assistant. Your role is to help users find information.</input>
  838. <enhanced_prompt>You are a specialized web search AI assistant designed to provide comprehensive, accurate, and well-structured information retrieval services.
  839.  
  840. Core Responsibilities:
  841. 1. Execute precise web searches based on user queries
  842. 2. Evaluate source credibility and relevance
  843. 3. Synthesize information from multiple sources
  844. 4. Present findings in clear, structured formats
  845.  
  846. Behavioral Guidelines:
  847. - Maintain objectivity in information presentation
  848. - Clearly distinguish between facts and interpretations
  849. - Acknowledge information gaps or uncertainties
  850. - Proactively suggest related topics for exploration
  851.  
  852. Output Requirements:
  853. 1. Structure all responses with:
  854. - Executive summary
  855. - Detailed findings
  856. - Source citations
  857. - Confidence levels
  858. 2. Use formatting for clarity:
  859. - Bullet points for key facts
  860. - Tables for comparisons
  861. - Markdown for emphasis
  862. - Hierarchical headings
  863.  
  864. Error Handling:
  865. - Acknowledge when information is outdated
  866. - Flag potential misinformation
  867. - Suggest alternative search strategies
  868. - Provide confidence levels for findings</enhanced_prompt>
  869. </example>
  870. </examples>
  871.  
  872. <success_criteria>
  873. For User Prompts:
  874. - Enhanced clarity and specificity
  875. - Maintained conversation context
  876. - Clear parameters for response
  877. - Structured multi-part requests
  878. - Defined output preferences
  879.  
  880. For System Prompts:
  881. - Clear role definition
  882. - Comprehensive behavioral guidelines
  883. - Specific output requirements
  884. - Error handling procedures
  885. - Interaction patterns defined
  886. </success_criteria>`;
  887.  
  888. // Wrap the input with appropriate type tags
  889. const taggedInput = `<${promptType}_prompt>${promptInput}</${promptType}_prompt>`;
  890.  
  891. GM_xmlhttpRequest({
  892. method: 'POST',
  893. url: `${apiUrl}/chat/completions`,
  894. headers: {
  895. 'Content-Type': 'application/json',
  896. 'Authorization': `Bearer ${apiKey}`
  897. },
  898. data: JSON.stringify({
  899. model: modelName,
  900. ...(providerName && { provider: providerName }),
  901. messages: [
  902. {
  903. role: 'system',
  904. content: systemPrompt
  905. },
  906. {
  907. role: 'user',
  908. content: taggedInput
  909. }
  910. ],
  911. temperature: 0.7,
  912. max_tokens: 16384
  913. }),
  914. onload: function(response) {
  915. try {
  916. const result = JSON.parse(response.responseText);
  917. if (result.error) {
  918. showResponse(`Error: ${result.error.message}`, true);
  919. } else if (result.choices && result.choices[0]) {
  920. showResponse(result.choices[0].message.content);
  921. } else {
  922. showResponse('Unexpected API response format', true);
  923. }
  924. } catch (error) {
  925. showResponse(`Error processing response: ${error.message}`, true);
  926. }
  927. },
  928. onerror: function(error) {
  929. showResponse(`Network error: ${error.statusText}`, true);
  930. }
  931. });
  932. }
  933.  
  934. // Initialize the UI
  935. createUI();
  936. })();