您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
(我已经安装了用户样式管理器,让我安装!)
- // ==UserScript==
- // @name Kagi Assistant Enhancements
- // @namespace http://tampermonkey.net/
- // @version 0.3
- // @description Adds prompt library and code copy features to Kagi Assistant
- // @author You
- // @match https://kagi.com/assistant/*
- // @grant none
- // @license MIT
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- // ===== Prompt Library Functions =====
-
- // Load prompts from localStorage
- function loadPrompts() {
- const savedPrompts = localStorage.getItem('kagiPrompts');
- return savedPrompts ? JSON.parse(savedPrompts) : [
- { name: "Example Prompt 1", text: "This is example prompt 1" },
- { name: "Example Prompt 2", text: "This is example prompt 2" }
- ];
- }
-
- // Save prompts to localStorage
- function savePrompts(prompts) {
- localStorage.setItem('kagiPrompts', JSON.stringify(prompts));
- }
-
- // ===== Code Copy Button Functions =====
-
- // Add copy button to code blocks
- function addCopyButton() {
- const codeBlocks = document.querySelectorAll('.codehilite');
-
- codeBlocks.forEach(block => {
- if (block.querySelector('.bottom-copy-btn')) return;
-
- const copyButton = document.createElement('button');
- copyButton.className = 'bottom-copy-btn relative';
- copyButton.title = 'Copy';
- copyButton.setAttribute('data-partial-update-ignore', 'true');
- copyButton.innerHTML = `
- <span class="_0_copied_tooltip">Copied to clipboard</span>
- <i class="icon-sm">
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path d="M7.5 7.5V5.25A2.25 2.25 0 019.75 3H18.75A2.25 2.25 0 0121 5.25V14.25A2.25 2.25 0 0118.75 16.5H16.5M16.5 9.75A2.25 2.25 0 0014.25 7.5H5.25A2.25 2.25 0 003 9.75V18.75A2.25 2.25 0 005.25 21H14.25A2.25 2.25 0 0016.5 18.75V9.75Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
- </svg>
- </i>
- `;
-
- copyButton.addEventListener('click', async () => {
- const code = block.querySelector('code').textContent;
- await navigator.clipboard.writeText(code);
-
- const tooltip = copyButton.querySelector('._0_copied_tooltip');
- tooltip.style.display = 'block';
- setTimeout(() => {
- tooltip.style.display = 'none';
- }, 2000);
- });
-
- block.appendChild(copyButton);
- });
- }
-
- // ===== Create Prompt Library UI =====
- function createPromptLibrary() {
- const button = document.createElement('button');
- button.type = 'button';
- button.id = 'prompt-library-button';
- button.className = 'prompt-library';
- button.innerHTML = `
- <i class="icon-md">
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path d="M19 3H5C3.89543 3 3 3.89543 3 5V19C3 20.1046 3.89543 21 5 21H19C20.1046 21 21 20.1046 21 19V5C21 3.89543 20.1046 3 19 3Z" stroke="currentColor" stroke-width="1.5"/>
- <path d="M7 7H17M7 12H17M7 17H13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
- </svg>
- </i>
- <span>Prompts</span>
- `;
-
- const wrapper = document.createElement('div');
- wrapper.style.position = 'relative';
- wrapper.appendChild(button);
-
- // Create dropdown menu
- const dropdown = document.createElement('div');
- dropdown.className = 'prompt-dropdown';
- dropdown.style.display = 'none';
-
- // Update dropdown content function
- function updateDropdown() {
- const prompts = loadPrompts();
- dropdown.innerHTML = `
- <div class="prompt-header">
- <span>My Prompts</span>
- <div class="prompt-header-actions">
- <button class="export-btn" title="Export">📤</button>
- <button class="import-btn" title="Import">📥</button>
- <button class="add-prompt-btn">+</button>
- </div>
- </div>
- <div class="prompt-list">
- ${prompts.map((prompt, index) => `
- <div class="prompt-item">
- <span class="prompt-name">${prompt.name}</span>
- <div class="prompt-actions">
- <button class="edit-btn" data-index="${index}">✏️</button>
- <button class="delete-btn" data-index="${index}">🗑️</button>
- </div>
- </div>
- `).join('')}
- </div>
- `;
-
- // Add event listeners for dropdown buttons
- attachDropdownListeners(dropdown);
- }
-
- // Handle dropdown visibility
- button.onclick = (e) => {
- e.stopPropagation();
- if (dropdown.style.display === 'none') {
- updateDropdown();
- dropdown.style.display = 'block';
- } else {
- dropdown.style.display = 'none';
- }
- };
-
- document.addEventListener('click', (e) => {
- if (!wrapper.contains(e.target)) {
- dropdown.style.display = 'none';
- }
- });
-
- wrapper.appendChild(dropdown);
- return wrapper;
- }
-
- // Attach event listeners to dropdown elements
- function attachDropdownListeners(dropdown) {
- // Export functionality
- dropdown.querySelector('.export-btn').onclick = (e) => {
- e.stopPropagation();
- const prompts = loadPrompts();
- const blob = new Blob([JSON.stringify(prompts, null, 2)], {type: 'application/json'});
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = 'kagi-prompts.json';
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
- };
-
- // Import functionality
- dropdown.querySelector('.import-btn').onclick = (e) => {
- e.stopPropagation();
- const input = document.createElement('input');
- input.type = 'file';
- input.accept = '.json';
- input.onchange = async (e) => {
- try {
- const file = e.target.files[0];
- const text = await file.text();
- const prompts = JSON.parse(text);
- if (Array.isArray(prompts) && prompts.every(p => p.name && p.text)) {
- if (confirm('Replace all current prompts?')) {
- savePrompts(prompts);
- dropdown.querySelector('.prompt-list').innerHTML = '';
- updateDropdown();
- }
- } else {
- alert('Invalid file format');
- }
- } catch (error) {
- alert('Import error');
- console.error(error);
- }
- };
- input.click();
- };
-
- // Add new prompt
- dropdown.querySelector('.add-prompt-btn').onclick = (e) => {
- e.stopPropagation();
- const name = prompt("Prompt name:");
- const text = prompt("Prompt text:");
- if (name && text) {
- const prompts = loadPrompts();
- prompts.push({ name, text });
- savePrompts(prompts);
- updateDropdown();
- }
- };
-
- // Handle prompt items
- dropdown.querySelectorAll('.prompt-item').forEach(item => {
- const promptBox = document.getElementById('promptBox');
-
- // Click on prompt name to use it
- item.querySelector('.prompt-name').onclick = () => {
- const index = item.querySelector('.edit-btn').dataset.index;
- const prompts = loadPrompts();
- if (promptBox) {
- promptBox.value = prompts[index].text;
- promptBox.focus();
- }
- dropdown.style.display = 'none';
- };
-
- // Edit prompt
- item.querySelector('.edit-btn').onclick = (e) => {
- e.stopPropagation();
- const index = e.target.dataset.index;
- const prompts = loadPrompts();
- const name = prompt("New name:", prompts[index].name);
- const text = prompt("New text:", prompts[index].text);
- if (name && text) {
- prompts[index] = { name, text };
- savePrompts(prompts);
- updateDropdown();
- }
- };
-
- // Delete prompt
- item.querySelector('.delete-btn').onclick = (e) => {
- e.stopPropagation();
- const index = e.target.dataset.index;
- if (confirm("Delete this prompt?")) {
- const prompts = loadPrompts();
- prompts.splice(index, 1);
- savePrompts(prompts);
- updateDropdown();
- }
- };
- });
- }
-
- // ===== Add Styles =====
- function addStyles() {
- const styles = document.createElement('style');
- styles.textContent = `
- .codehilite {
- position: relative;
- }
- .bottom-copy-btn {
- position: absolute;
- bottom: 10px;
- right: 10px;
- background: transparent;
- border: none;
- cursor: pointer;
- padding: 5px;
- opacity: 0.6;
- transition: opacity 0.2s;
- }
- .bottom-copy-btn:hover {
- opacity: 1;
- }
- .bottom-copy-btn ._0_copied_tooltip {
- display: none;
- position: absolute;
- bottom: 100%;
- right: 0;
- background: black;
- color: white;
- padding: 5px 10px;
- border-radius: 4px;
- font-size: 12px;
- white-space: nowrap;
- }
- .prompt-library {
- display: flex;
- align-items: center;
- gap: 8px;
- background: none;
- border: none;
- cursor: pointer;
- padding: 5px 10px;
- color: var(--color-text-primary);
- }
- .prompt-dropdown {
- position: absolute;
- background-color: rgb(255, 255, 255);
- border: 1px solid rgba(0, 0, 0, 0.1);
- border-radius: 8px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
- z-index: 1000;
- bottom: 100%;
- left: 50%;
- transform: translateX(-50%);
- min-width: 250px;
- max-height: 400px;
- overflow-y: auto;
- margin-bottom: 10px;
- }
- .prompt-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 8px 12px;
- border-bottom: 1px solid rgba(0, 0, 0, 0.1);
- background-color: rgb(250, 250, 250);
- }
- .prompt-header-actions {
- display: flex;
- gap: 8px;
- align-items: center;
- }
- .add-prompt-btn, .export-btn, .import-btn {
- padding: 2px 8px;
- border-radius: 4px;
- border: 1px solid rgba(0, 0, 0, 0.1);
- background: white;
- cursor: pointer;
- }
- .prompt-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 8px 12px;
- cursor: pointer;
- background-color: rgb(255, 255, 255);
- }
- .prompt-item:hover {
- background-color: rgb(245, 245, 245);
- }
- .prompt-actions {
- display: flex;
- gap: 4px;
- }
- .prompt-actions button {
- padding: 2px 4px;
- border: none;
- background: none;
- cursor: pointer;
- opacity: 0.6;
- }
- .prompt-actions button:hover {
- opacity: 1;
- }
- .prompt-name {
- flex: 1;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- margin-right: 8px;
- }
- `;
- document.head.appendChild(styles);
- }
-
- // ===== Initialize =====
- function init() {
- // Add styles
- addStyles();
-
- // Add prompt library
- const promptOptions = document.querySelector('.prompt-options');
- if (promptOptions && !document.getElementById('prompt-library-button')) {
- const promptLibrary = createPromptLibrary();
- const toggleSwitch = promptOptions.querySelector('.k_ui_toggle_switch');
- promptOptions.insertBefore(promptLibrary, toggleSwitch);
- }
-
- // Add copy buttons to code blocks
- addCopyButton();
- }
-
- // Observe DOM changes
- const observer = new MutationObserver((mutations) => {
- init();
- });
-
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
-
- // Initial execution
- init();
- })();