Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://updategreasyfork.deno.dev/scripts/528763/1549028/Text%20Explainer%20Settings.js
You will need to install an extension such as Stylus to install this style.
You will need to install an extension such as Stylus to install this style.
You will need to install an extension such as Stylus to install this style.
You will need to install a user style manager extension to install this style.
You will need to install a user style manager extension to install this style.
You will need to install a user style manager extension to install this style.
(I already have a user style manager, let me install it!)
Wrap lines
// ==UserScript==
// @name Text Explainer Settings
// @namespace http://tampermonkey.net/
// @version 0.1.5
// @description Settings module for Text Explainer
// @author RoCry
// @license MIT
// ==/UserScript==
class TextExplainerSettings {
constructor(defaultConfig = {}) {
this.defaultConfig = Object.assign({
model: "openai-large",
apiKey: "fake",
baseUrl: "https://text.pollinations.ai/openai#",
provider: "openai",
language: "Chinese",
shortcut: {
key: "d",
ctrlKey: false,
altKey: true,
shiftKey: false,
metaKey: false
},
floatingButton: {
enabled: true,
size: "medium", // small, medium, large
}
}, defaultConfig);
this.config = this.load();
}
/**
* Load settings from storage
*/
load() {
try {
const savedConfig = typeof GM_getValue === 'function'
? GM_getValue('explainerConfig', {})
: JSON.parse(localStorage.getItem('explainerConfig') || '{}');
return Object.assign({}, this.defaultConfig, savedConfig);
} catch (e) {
console.error('Error loading settings:', e);
return Object.assign({}, this.defaultConfig);
}
}
/**
* Save settings to storage
*/
save() {
try {
if (typeof GM_setValue === 'function') {
GM_setValue('explainerConfig', this.config);
} else {
localStorage.setItem('explainerConfig', JSON.stringify(this.config));
}
return true;
} catch (e) {
console.error('Error saving settings:', e);
return false;
}
}
/**
* Get setting value
*/
get(key) {
return this.config[key];
}
/**
* Set setting value
*/
set(key, value) {
this.config[key] = value;
return this;
}
/**
* Update multiple settings at once
*/
update(settings) {
Object.assign(this.config, settings);
return this;
}
/**
* Reset settings to defaults
*/
reset() {
this.config = Object.assign({}, this.defaultConfig);
return this;
}
/**
* Get all settings
*/
getAll() {
return Object.assign({}, this.config);
}
/**
* Open settings dialog
*/
openDialog(onSave = null) {
// First check if dialog already exists and remove it
const existingDialog = document.getElementById('explainer-settings-dialog');
if (existingDialog) existingDialog.remove();
// Create dialog container
const dialog = document.createElement('div');
dialog.id = 'explainer-settings-dialog';
dialog.style = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 12px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
z-index: 10001;
width: 400px;
max-width: 90vw;
max-height: 80vh;
overflow-y: auto;
font-family: system-ui, sans-serif;
font-size: 14px;
`;
// Add dark mode support
const styleElement = document.createElement('style');
styleElement.textContent = `
#explainer-settings-dialog {
color: #333;
}
#explainer-settings-dialog h3 {
margin-top: 0;
margin-bottom: 10px;
font-size: 16px;
}
#explainer-settings-dialog .row {
display: flex;
gap: 10px;
margin-bottom: 8px;
}
#explainer-settings-dialog .col {
flex: 1;
}
#explainer-settings-dialog label {
display: block;
margin: 4px 0 2px;
font-weight: 500;
font-size: 12px;
}
#explainer-settings-dialog input[type="text"],
#explainer-settings-dialog select {
width: 100%;
padding: 4px 6px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
font-size: 13px;
}
/* Fix for shortcut key width */
#explainer-settings-dialog input#explainer-shortcut-key {
width: 30px !important;
min-width: 30px;
max-width: 30px;
text-align: center;
padding: 4px 0;
}
#explainer-settings-dialog .buttons {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 12px;
}
#explainer-settings-dialog button {
padding: 6px 10px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
}
#explainer-settings-dialog button.primary {
background-color: #4285f4;
color: white;
}
#explainer-settings-dialog button.secondary {
background-color: #f1f1f1;
color: #333;
}
#explainer-settings-dialog .shortcut-section {
display: flex;
align-items: flex-end;
gap: 15px;
margin-bottom: 4px;
}
#explainer-settings-dialog .key-container {
display: flex;
flex-direction: column;
}
#explainer-settings-dialog .modifier-group {
display: flex;
align-items: center;
gap: 8px;
height: 28px; /* Match the height of the input field */
}
#explainer-settings-dialog .modifier {
display: flex;
flex-direction: column;
align-items: center;
margin: 0 2px;
}
#explainer-settings-dialog .modifier input[type="checkbox"] {
margin: 0 0 2px;
}
#explainer-settings-dialog .modifier label {
font-size: 11px;
margin: 0;
user-select: none;
}
#explainer-settings-dialog .section-title {
font-weight: 600;
margin-top: 12px;
margin-bottom: 6px;
border-bottom: 1px solid #ddd;
padding-bottom: 2px;
font-size: 13px;
}
#explainer-settings-dialog .checkbox-label {
display: flex;
align-items: center;
margin: 4px 0;
}
#explainer-settings-dialog .checkbox-label input {
margin-right: 6px;
}
@media (prefers-color-scheme: dark) {
#explainer-settings-dialog {
background: #333;
color: #eee;
}
#explainer-settings-dialog input[type="text"],
#explainer-settings-dialog select {
background: #444;
color: #eee;
border-color: #555;
}
#explainer-settings-dialog button.secondary {
background-color: #555;
color: #eee;
}
#explainer-settings-dialog .section-title {
border-bottom-color: #555;
}
}
`;
document.head.appendChild(styleElement);
// Prepare shortcut configuration
const shortcut = this.config.shortcut || this.defaultConfig.shortcut;
const floatingButton = this.config.floatingButton || this.defaultConfig.floatingButton;
// Create dialog content with a more compact layout
dialog.innerHTML = `
<h3>Text Explainer Settings</h3>
<div class="section-title">Language & API Settings</div>
<div class="row">
<div class="col">
<label for="explainer-language">Language</label>
<select id="explainer-language">
<option value="Chinese" ${this.config.language === 'Chinese' ? 'selected' : ''}>Chinese</option>
<option value="English" ${this.config.language === 'English' ? 'selected' : ''}>English</option>
<option value="Japanese" ${this.config.language === 'Japanese' ? 'selected' : ''}>Japanese</option>
</select>
</div>
<div class="col">
<label for="explainer-provider">Provider</label>
<select id="explainer-provider">
<option value="gemini" ${this.config.provider === 'gemini' ? 'selected' : ''}>Gemini</option>
<option value="openai" ${this.config.provider === 'openai' ? 'selected' : ''}>OpenAI</option>
<option value="anthropic" ${this.config.provider === 'anthropic' ? 'selected' : ''}>Anthropic</option>
</select>
</div>
<div class="col">
<label for="explainer-model">Model</label>
<input id="explainer-model" type="text" value="${this.config.model}">
</div>
</div>
<div class="row">
<div class="col">
<label for="explainer-api-key">API Key</label>
<input id="explainer-api-key" type="text" value="${this.config.apiKey || ''}">
<p style="font-size: 11px; color: #666; margin-top: 0; margin-bottom: 8px;">
Multiple keys supported, separated by commas
</p>
</div>
</div>
<div>
<label for="explainer-base-url">API Base URL</label>
<input id="explainer-base-url" type="text" value="${this.config.baseUrl}">
<p style="font-size: 11px; color: #666; margin-top: 0; margin-bottom: 8px;">
Ending with / ignores v1, ending with # forces use of input address
</p>
</div>
<div class="section-title">Shortcut Settings</div>
<div class="shortcut-section">
<div class="key-container">
<label for="explainer-shortcut-key">Key</label>
<input id="explainer-shortcut-key" type="text" maxlength="1" value="${shortcut.key}">
</div>
<div class="modifier-group">
<div class="modifier">
<label for="explainer-shortcut-ctrl">⌃</label>
<input type="checkbox" id="explainer-shortcut-ctrl" ${shortcut.ctrlKey ? 'checked' : ''}>
</div>
<div class="modifier">
<label for="explainer-shortcut-alt">⌥</label>
<input type="checkbox" id="explainer-shortcut-alt" ${shortcut.altKey ? 'checked' : ''}>
</div>
<div class="modifier">
<label for="explainer-shortcut-shift">⇧</label>
<input type="checkbox" id="explainer-shortcut-shift" ${shortcut.shiftKey ? 'checked' : ''}>
</div>
<div class="modifier">
<label for="explainer-shortcut-meta">⌘</label>
<input type="checkbox" id="explainer-shortcut-meta" ${shortcut.metaKey ? 'checked' : ''}>
</div>
</div>
</div>
<p style="font-size: 11px; color: #666; margin-top: 0; margin-bottom: 8px;">
Tip: Choose a letter key (a-z) with at least one modifier key.
</p>
<div class="section-title">Touch Device Settings</div>
<div class="row">
<div class="col">
<div class="checkbox-label">
<input type="checkbox" id="explainer-floating-enabled" ${floatingButton.enabled ? 'checked' : ''}>
<span>Show floating button</span>
</div>
</div>
</div>
<div class="row">
<div class="col">
<label for="explainer-floating-size">Button Size</label>
<select id="explainer-floating-size">
<option value="small" ${floatingButton.size === 'small' ? 'selected' : ''}>Small</option>
<option value="medium" ${floatingButton.size === 'medium' ? 'selected' : ''}>Medium</option>
<option value="large" ${floatingButton.size === 'large' ? 'selected' : ''}>Large</option>
</select>
</div>
</div>
<div class="buttons">
<button id="explainer-settings-cancel" class="secondary">Cancel</button>
<button id="explainer-settings-save" class="primary">Save</button>
</div>
`;
document.body.appendChild(dialog);
// Add event listeners
document.getElementById('explainer-settings-save').addEventListener('click', () => {
// Get shortcut settings
const shortcutSettings = {
key: document.getElementById('explainer-shortcut-key').value.toLowerCase(),
ctrlKey: document.getElementById('explainer-shortcut-ctrl').checked,
altKey: document.getElementById('explainer-shortcut-alt').checked,
shiftKey: document.getElementById('explainer-shortcut-shift').checked,
metaKey: document.getElementById('explainer-shortcut-meta').checked
};
// Get floating button settings
const floatingButtonSettings = {
enabled: document.getElementById('explainer-floating-enabled').checked,
size: document.getElementById('explainer-floating-size').value,
};
// Update config with all form values
this.update({
language: document.getElementById('explainer-language').value,
model: document.getElementById('explainer-model').value,
apiKey: document.getElementById('explainer-api-key').value,
baseUrl: document.getElementById('explainer-base-url').value,
provider: document.getElementById('explainer-provider').value,
shortcut: shortcutSettings,
floatingButton: floatingButtonSettings
});
// Save to storage
this.save();
// Remove dialog
dialog.remove();
styleElement.remove();
// Call save callback if provided
if (typeof onSave === 'function') {
onSave(this.config);
}
});
document.getElementById('explainer-settings-cancel').addEventListener('click', () => {
dialog.remove();
styleElement.remove();
});
// Focus first field
document.getElementById('explainer-language').focus();
// Add validation for the shortcut key
const keyInput = document.getElementById('explainer-shortcut-key');
keyInput.addEventListener('input', () => {
// Ensure it's a single character and convert to lowercase
if (keyInput.value.length > 0) {
keyInput.value = keyInput.value.charAt(0).toLowerCase();
}
});
}
}
// Make available globally and as a module if needed
window.TextExplainerSettings = TextExplainerSettings;
if (typeof module !== 'undefined') {
module.exports = TextExplainerSettings;
}