Generate creative prompts by mixing words from custom categories
// ==UserScript==
// @name Prompt Generator
// @namespace http://tampermonkey.net/
// @version 0.1903.2.026
// @description Generate creative prompts by mixing words from custom categories
// @author gullampis810
// @match https://grok.com/imagine/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// === STORAGE MANAGER ===
const Storage = {
get(key, defaultValue) {
try {
const data = localStorage.getItem(`promptGen_${key}`);
return data ? JSON.parse(data) : defaultValue;
} catch {
return defaultValue;
}
},
set(key, value) {
localStorage.setItem(`promptGen_${key}`, JSON.stringify(value));
}
};
// === DEFAULT DATA ===
const defaultCategories = {
adjectives: ['красивый', 'быстрый', 'мощный', 'умный', 'яркий'],
nouns: ['дракон', 'робот', 'город', 'океан', 'звезда'],
verbs: ['создает', 'исследует', 'защищает', 'открывает', 'трансформирует'],
objects: ['артефакт', 'портал', 'технологию', 'мир', 'реальность'],
actions: ['в космосе', 'под водой', 'в будущем', 'в киберпанк мире', 'в фэнтези']
};
const defaultTemplates = [
'{adjectives} {nouns} {verbs} {objects}',
'{adjectives} {nouns} {verbs} {objects} {actions}',
'{nouns} {verbs} {adjectives} {objects}',
'Представь {adjectives} {nouns}, который {verbs} {objects} {actions}'
];
// === STATE ===
let categories = Storage.get('categories', defaultCategories);
let templates = Storage.get('templates', defaultTemplates);
let history = Storage.get('history', []);
let currentCategory = Object.keys(categories)[0];
let isOpen = false;
// === SVG ICONS ===
const icons = {
sparkles: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>',
dice: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8" cy="8" r="1" fill="currentColor"/><circle cx="16" cy="8" r="1" fill="currentColor"/><circle cx="12" cy="12" r="1" fill="currentColor"/><circle cx="8" cy="16" r="1" fill="currentColor"/><circle cx="16" cy="16" r="1" fill="currentColor"/></svg>',
save: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z"/><path d="M17 21v-8H7v8M7 3v5h8"/></svg>',
plus: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14m-7-7h14"/></svg>',
download: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4m4-5l5 5 5-5m-5 5V3"/></svg>',
upload: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4m14-7l-5-5-5 5m5-5v12"/></svg>',
edit: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>',
trash: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18m-2 0v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/></svg>',
clipboard: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M16 4h2a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2h2"/><rect x="8" y="2" width="8" height="4" rx="1"/></svg>',
check: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 6L9 17l-5-5"/></svg>',
copy: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>',
paste: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M16 4h2a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2h2"/><rect x="8" y="2" width="8" height="4" rx="1"/><path d="M9 12h6m-6 4h6"/></svg>'
};
function icon(name, className = '') {
return `<span class="prompt-gen-icon ${className}">${icons[name]}</span>`;
}
// === STYLES ===
const styles = `
.prompt-gen-panel {
position: fixed;
top: 20px;
right: 20px;
width: 400px;
max-height: 600px;
background: linear-gradient(135deg, rgb(9 17 49) 0%, rgb(57, 40, 74) 100%);
border: 2px solid rgb(199, 157, 242);
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
z-index: 999999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
color: white;
display: none;
flex-direction: column;
}
.prompt-gen-panel.open {
display: flex;
}
.prompt-gen-header {
padding: 15px;
cursor: move;
background: rgba(0,0,0,0.2);
border-radius: 12px 12px 0 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.prompt-gen-title {
font-weight: 600;
font-size: 16px;
}
.prompt-gen-close {
background: rgba(255,255,255,0.2);
border: none;
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
cursor: pointer;
font-size: 18px;
line-height: 1;
transition: background 0.2s;
}
.prompt-gen-close:hover {
background: rgba(255,255,255,0.3);
}
.prompt-gen-toggle {
position: fixed;
bottom: 800px;
right: 200px;
width: 60px;
height: 60px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: 2px solid rgb(234, 242, 157);
color: white;
font-size: 24px;
cursor: pointer;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
z-index: 999999;
transition: transform 0.2s;
}
.prompt-gen-toggle:hover {
transform: scale(1.1);
}
.prompt-gen-content {
padding: 15px;
overflow-y: auto;
flex: 1;
}
.prompt-gen-section {
margin-bottom: 20px;
}
.prompt-gen-section-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 10px;
opacity: 0.9;
}
.prompt-gen-tabs {
display: flex;
gap: 5px;
margin-bottom: 10px;
flex-wrap: wrap;
}
.prompt-gen-tab {
padding: 6px 12px;
background: rgba(255,255,255,0.15);
border: none;
border-radius: 6px;
color: white;
cursor: pointer;
font-size: 12px;
transition: background 0.2s;
}
.prompt-gen-tab:hover {
background: rgba(255,255,255,0.25);
}
.prompt-gen-tab.active {
background: rgba(255,255,255,0.4);
font-weight: 600;
}
.prompt-gen-list {
background: rgba(0,0,0,0.15);
border-radius: 8px;
padding: 8px;
max-height: 150px;
overflow-y: auto;
margin-bottom: 10px;
}
.prompt-gen-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 8px;
background: rgba(255,255,255,0.1);
border-radius: 6px;
margin-bottom: 4px;
font-size: 13px;
}
.prompt-gen-item:last-child {
margin-bottom: 0;
}
.prompt-gen-item-text {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.prompt-gen-item-actions {
display: flex;
gap: 4px;
}
.prompt-gen-btn {
width: 100%;
padding: 10px;
background: rgba(255,255,255,0.2);
border: 2px solid rgb(174 168 132);
border-radius: 6px;
color: white;
cursor: pointer;
font-size: 13px;
font-weight: 600;
transition: background 0.2s;
margin-bottom: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.prompt-gen-btn-small {
padding: 4px 8px;
background: rgba(255,255,255,0.2);
border: 2px solid rgb(61 109 160);
border-radius: 4px;
color: white;
cursor: pointer;
font-size: 11px;
transition: background 0.2s;
display: inline-flex;
align-items: center;
justify-content: center;
}
.prompt-gen-btn-small:hover {
background: rgba(255,255,255,0.3);
}
.prompt-gen-btn-small .prompt-gen-icon {
margin: 0;
width: 14px;
height: 14px;
}
.prompt-gen-input {
width: 100%;
padding: 8px;
background: rgb(8 10 27) !important;
border: 1px solid rgb(249 217 70 / 51%);
border-radius: 6px;
color: #e7e293 !important;
font-size: 13px;
margin-bottom: 8px;
}
.prompt-gen-input::placeholder {
color: rgba(255,255,255,0.5);
}
.prompt-gen-btn:hover {
background: rgba(255,255,255,0.3);
}
.prompt-gen-btn-primary {
background: rgba(255,255,255,0.3);
}
.prompt-gen-btn-primary:hover {
background: rgba(255,255,255,0.4);
}
.prompt-gen-result {
background: rgba(0,0,0,0.2);
padding: 12px;
border-radius: 8px;
font-size: 14px;
line-height: 1.5;
margin-bottom: 10px;
min-height: 50px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
.prompt-gen-history-item {
background: rgba(0,0,0,0.15);
padding: 8px;
border-radius: 6px;
margin-bottom: 6px;
font-size: 12px;
display: flex;
justify-content: space-between;
align-items: center;
}
.prompt-gen-history-text {
flex: 1;
margin-right: 8px;
}
.prompt-gen-actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.prompt-gen-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
margin-right: 6px;
}
.prompt-gen-icon svg {
width: 100%;
height: 100%;
}
.prompt-gen-toggle .prompt-gen-icon {
width: 24px;
height: 24px;
margin: 0;
}
`;
// === UI CREATION ===
function createUI() {
// Inject styles
const styleEl = document.createElement('style');
styleEl.textContent = styles;
document.head.appendChild(styleEl);
// Toggle button
const toggleBtn = document.createElement('button');
toggleBtn.className = 'prompt-gen-toggle';
toggleBtn.innerHTML = icon('sparkles');
toggleBtn.onclick = togglePanel;
document.body.appendChild(toggleBtn);
// Main panel
const panel = document.createElement('div');
panel.className = 'prompt-gen-panel';
panel.innerHTML = `
<div class="prompt-gen-header">
<div class="prompt-gen-title">Генератор Промптов</div>
<button class="prompt-gen-close">×</button>
</div>
<div class="prompt-gen-content">
<div class="prompt-gen-section">
<div class="prompt-gen-section-title">Генератор</div>
<div class="prompt-gen-result" id="promptResult">Нажми "Генерировать" для создания промпта</div>
<div class="prompt-gen-actions">
<button class="prompt-gen-btn" id="copyPromptBtn">${icon('copy')}Копировать</button>
<button class="prompt-gen-btn" id="pastePromptBtn">${icon('paste')}Вставить</button>
</div>
<button class="prompt-gen-btn prompt-gen-btn-primary" id="generateBtn">${icon('dice')}Генерировать</button>
<button class="prompt-gen-btn" id="savePromptBtn">${icon('save')}Сохранить в историю</button>
</div>
<div class="prompt-gen-section">
<div class="prompt-gen-section-title">Категории слов</div>
<div class="prompt-gen-tabs" id="categoryTabs"></div>
<div class="prompt-gen-list" id="wordList"></div>
<input type="text" class="prompt-gen-input" id="wordInput" placeholder="Новое слово...">
<button class="prompt-gen-btn" id="addWordBtn">${icon('plus')}Добавить слово</button>
</div>
<div class="prompt-gen-section">
<div class="prompt-gen-section-title">Шаблоны</div>
<div class="prompt-gen-list" id="templateList"></div>
<input type="text" class="prompt-gen-input" id="templateInput" placeholder="{adjectives} {nouns}...">
<button class="prompt-gen-btn" id="addTemplateBtn">${icon('plus')}Добавить шаблон</button>
</div>
<div class="prompt-gen-section">
<div class="prompt-gen-section-title">История</div>
<div class="prompt-gen-list" id="historyList"></div>
</div>
<div class="prompt-gen-section">
<div class="prompt-gen-actions">
<button class="prompt-gen-btn" id="importBtn">${icon('upload')}Импорт</button>
<button class="prompt-gen-btn" id="exportBtn">${icon('download')}Экспорт</button>
</div>
</div>
</div>
`;
document.body.appendChild(panel);
// Make draggable
makeDraggable(panel);
// Event listeners
panel.querySelector('.prompt-gen-close').onclick = togglePanel;
document.getElementById('generateBtn').onclick = generatePrompt;
document.getElementById('savePromptBtn').onclick = saveToHistory;
document.getElementById('copyPromptBtn').onclick = copyPrompt;
document.getElementById('pastePromptBtn').onclick = pastePrompt;
document.getElementById('addWordBtn').onclick = addWord;
document.getElementById('addTemplateBtn').onclick = addTemplate;
document.getElementById('importBtn').onclick = importData;
document.getElementById('exportBtn').onclick = exportData;
// Enter key handlers
document.getElementById('wordInput').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
addWord();
}
});
document.getElementById('templateInput').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
addTemplate();
}
});
// Initial render
renderCategoryTabs();
renderWordList();
renderTemplateList();
renderHistory();
}
// === DRAG & DROP ===
function makeDraggable(element) {
const header = element.querySelector('.prompt-gen-header');
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
header.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
element.style.top = (element.offsetTop - pos2) + "px";
element.style.left = (element.offsetLeft - pos1) + "px";
element.style.right = 'auto';
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}
// === PANEL TOGGLE ===
function togglePanel() {
isOpen = !isOpen;
const panel = document.querySelector('.prompt-gen-panel');
panel.classList.toggle('open', isOpen);
}
// === GENERATION ===
function generatePrompt() {
const template = templates[Math.floor(Math.random() * templates.length)];
let result = template;
// Replace all category placeholders
for (const [category, words] of Object.entries(categories)) {
if (words.length === 0) continue;
const regex = new RegExp(`\\{${category}\\}`, 'g');
result = result.replace(regex, () => {
return words[Math.floor(Math.random() * words.length)];
});
}
document.getElementById('promptResult').textContent = result;
return result;
}
function saveToHistory() {
const result = document.getElementById('promptResult').textContent;
if (result && result !== 'Нажми "Генерировать" для создания промпта') {
history.unshift(result);
if (history.length > 50) history.pop(); // Limit history
Storage.set('history', history);
renderHistory();
}
}
// === COPY & PASTE ===
function copyPrompt() {
const result = document.getElementById('promptResult').textContent;
if (result && result !== 'Нажми "Генерировать" для создания промпта') {
navigator.clipboard.writeText(result).then(() => {
const btn = document.getElementById('copyPromptBtn');
const originalHTML = btn.innerHTML;
btn.innerHTML = icon('check') + 'Скопировано!';
setTimeout(() => {
btn.innerHTML = originalHTML;
}, 1500);
}).catch(err => {
console.error('Ошибка копирования:', err);
alert('Не удалось скопировать промпт');
});
}
}
function pastePrompt() {
const result = document.getElementById('promptResult').textContent;
if (result && result !== 'Нажми "Генерировать" для создания промпта') {
// Try to find active input or contenteditable
const activeElement = document.activeElement;
// Check if it's a regular input or textarea
if (activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA')) {
const start = activeElement.selectionStart;
const end = activeElement.selectionEnd;
const text = activeElement.value;
activeElement.value = text.substring(0, start) + result + text.substring(end);
activeElement.selectionStart = activeElement.selectionEnd = start + result.length;
activeElement.focus();
showPasteSuccess();
return;
}
// Check if it's a contenteditable element
if (activeElement && activeElement.isContentEditable) {
// Insert text at cursor position
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
range.deleteContents();
const textNode = document.createTextNode(result);
range.insertNode(textNode);
range.setStartAfter(textNode);
range.setEndAfter(textNode);
selection.removeAllRanges();
selection.addRange(range);
showPasteSuccess();
return;
}
}
// Fallback: try to find any contenteditable on page
const editables = document.querySelectorAll('[contenteditable="true"]');
if (editables.length > 0) {
const editable = editables[0];
editable.focus();
// Try execCommand first
if (document.execCommand) {
document.execCommand('insertText', false, result);
showPasteSuccess();
return;
}
// Fallback: append to the end
if (editable.textContent) {
editable.textContent += result;
} else {
editable.textContent = result;
}
showPasteSuccess();
return;
}
// No suitable input found
alert('Не найдено активное текстовое поле. Кликните на поле ввода и попробуйте снова.');
}
}
function showPasteSuccess() {
const btn = document.getElementById('pastePromptBtn');
const originalHTML = btn.innerHTML;
btn.innerHTML = icon('check') + 'Вставлено!';
setTimeout(() => {
btn.innerHTML = originalHTML;
}, 1500);
}
// === CATEGORY MANAGEMENT ===
function renderCategoryTabs() {
const container = document.getElementById('categoryTabs');
container.innerHTML = '';
for (const category of Object.keys(categories)) {
const tab = document.createElement('button');
tab.className = 'prompt-gen-tab';
tab.textContent = category;
tab.classList.toggle('active', category === currentCategory);
tab.onclick = () => {
currentCategory = category;
renderCategoryTabs();
renderWordList();
};
container.appendChild(tab);
}
// Add new category button
const addTab = document.createElement('button');
addTab.className = 'prompt-gen-tab';
addTab.textContent = '+ Категория';
addTab.onclick = addCategory;
container.appendChild(addTab);
}
function addCategory() {
const name = prompt('Название новой категории:');
if (name && !categories[name]) {
categories[name] = [];
currentCategory = name;
Storage.set('categories', categories);
renderCategoryTabs();
renderWordList();
}
}
// === WORD MANAGEMENT ===
function renderWordList() {
const container = document.getElementById('wordList');
container.innerHTML = '';
const words = categories[currentCategory] || [];
words.forEach((word, index) => {
const item = document.createElement('div');
item.className = 'prompt-gen-item';
item.innerHTML = `
<span class="prompt-gen-item-text">${word}</span>
<div class="prompt-gen-item-actions">
<button class="prompt-gen-btn-small" data-action="edit" data-index="${index}">${icon('edit')}</button>
<button class="prompt-gen-btn-small" data-action="delete" data-index="${index}">${icon('trash')}</button>
</div>
`;
container.appendChild(item);
});
// Event delegation
container.onclick = (e) => {
const btn = e.target.closest('.prompt-gen-btn-small');
if (!btn) return;
const index = parseInt(btn.dataset.index);
const action = btn.dataset.action;
if (action === 'delete') {
deleteWord(index);
} else if (action === 'edit') {
editWord(index);
}
};
}
function addWord() {
const input = document.getElementById('wordInput');
const word = input.value.trim();
if (word) {
categories[currentCategory].push(word);
Storage.set('categories', categories);
input.value = '';
renderWordList();
}
}
function editWord(index) {
const words = categories[currentCategory];
const newWord = prompt('Редактировать слово:', words[index]);
if (newWord !== null && newWord.trim()) {
words[index] = newWord.trim();
Storage.set('categories', categories);
renderWordList();
}
}
function deleteWord(index) {
categories[currentCategory].splice(index, 1);
Storage.set('categories', categories);
renderWordList();
}
// === TEMPLATE MANAGEMENT ===
function renderTemplateList() {
const container = document.getElementById('templateList');
container.innerHTML = '';
templates.forEach((template, index) => {
const item = document.createElement('div');
item.className = 'prompt-gen-item';
item.innerHTML = `
<span class="prompt-gen-item-text">${template}</span>
<div class="prompt-gen-item-actions">
<button class="prompt-gen-btn-small" data-action="edit" data-index="${index}">${icon('edit')}</button>
<button class="prompt-gen-btn-small" data-action="delete" data-index="${index}">${icon('trash')}</button>
</div>
`;
container.appendChild(item);
});
container.onclick = (e) => {
const btn = e.target.closest('.prompt-gen-btn-small');
if (!btn) return;
const index = parseInt(btn.dataset.index);
const action = btn.dataset.action;
if (action === 'delete') {
deleteTemplate(index);
} else if (action === 'edit') {
editTemplate(index);
}
};
}
function addTemplate() {
const input = document.getElementById('templateInput');
const template = input.value.trim();
if (template) {
templates.push(template);
Storage.set('templates', templates);
input.value = '';
renderTemplateList();
}
}
function editTemplate(index) {
const newTemplate = prompt('Редактировать шаблон:', templates[index]);
if (newTemplate !== null && newTemplate.trim()) {
templates[index] = newTemplate.trim();
Storage.set('templates', templates);
renderTemplateList();
}
}
function deleteTemplate(index) {
templates.splice(index, 1);
Storage.set('templates', templates);
renderTemplateList();
}
// === HISTORY ===
function renderHistory() {
const container = document.getElementById('historyList');
container.innerHTML = '';
history.slice(0, 10).forEach((item, index) => {
const historyItem = document.createElement('div');
historyItem.className = 'prompt-gen-history-item';
historyItem.innerHTML = `
<span class="prompt-gen-history-text">${item}</span>
<button class="prompt-gen-btn-small" data-index="${index}">${icon('clipboard')}</button>
`;
container.appendChild(historyItem);
});
container.onclick = (e) => {
const btn = e.target.closest('.prompt-gen-btn-small');
if (!btn) return;
const index = parseInt(btn.dataset.index);
navigator.clipboard.writeText(history[index]).then(() => {
btn.innerHTML = icon('check');
setTimeout(() => btn.innerHTML = icon('clipboard'), 1000);
});
};
}
// === IMPORT/EXPORT ===
function exportData() {
const data = {
categories,
templates,
history,
version: '1.0'
};
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `prompts_${Date.now()}.json`;
a.click();
URL.revokeObjectURL(url);
}
function importData() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
try {
const data = JSON.parse(event.target.result);
if (confirm('Заменить текущие данные импортированными?')) {
categories = data.categories || categories;
templates = data.templates || templates;
history = data.history || history;
Storage.set('categories', categories);
Storage.set('templates', templates);
Storage.set('history', history);
renderCategoryTabs();
renderWordList();
renderTemplateList();
renderHistory();
alert('Данные успешно импортированы!');
}
} catch (err) {
alert('Ошибка чтения файла: ' + err.message);
}
};
reader.readAsText(file);
};
input.click();
}
// === INITIALIZATION ===
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createUI);
} else {
createUI();
}
})();