Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Prompt Generator Web</title>
- <style>
- :root { --bg: #1e1e2e; --surface: #2b2b36; --text: #cdd6f4; --primary: #89b4fa; --hover: #b4befe; --border: #45475a; }
- body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: var(--bg); color: var(--text); margin: 0; padding: 20px; display: flex; flex-direction: column; height: 100vh; box-sizing: border-box; }
- h1 { font-size: 1.5rem; margin-top: 0; }
- .container { display: flex; gap: 20px; flex: 1; min-height: 0; }
- .left-panel, .right-panel { background: var(--surface); padding: 15px; border-radius: 8px; display: flex; flex-direction: column; gap: 10px; overflow: hidden; border: 1px solid var(--border); }
- .left-panel { flex: 1; }
- .right-panel { flex: 1; }
- .toolbar { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
- button, input[type="number"] { background: var(--primary); color: #111; border: none; padding: 8px 12px; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.9rem; }
- button:hover { background: var(--hover); }
- .btn-secondary { background: #45475a; color: var(--text); }
- .btn-secondary:hover { background: #585b70; }
- table { width: 100%; border-collapse: collapse; margin-bottom: 10px; display: block; overflow-y: auto; max-height: 400px; }
- th, td { border: 1px solid var(--border); padding: 8px; text-align: left; }
- th { background: var(--border); position: sticky; top: 0; }
- td[contenteditable="true"] { background: rgba(255,255,255,0.05); outline: none; }
- td[contenteditable="true"]:focus { background: rgba(255,255,255,0.1); }
- .row-select { width: 30px; text-align: center; cursor: pointer; }
- textarea { width: 100%; flex: 1; background: rgba(0,0,0,0.2); color: var(--text); border: 1px solid var(--border); border-radius: 4px; padding: 10px; font-family: monospace; resize: none; box-sizing: border-box; font-size: 14px; }
- label { font-weight: bold; }
- /* Context Menu Styles */
- #contextMenu { position: absolute; background: var(--surface); border: 1px solid var(--border); border-radius: 4px; box-shadow: 0 4px 6px rgba(0,0,0,0.3); display: none; flex-direction: column; min-width: 150px; z-index: 1000; }
- #contextMenu div { padding: 8px 12px; cursor: pointer; border-bottom: 1px solid var(--border); }
- #contextMenu div:last-child { border-bottom: none; }
- #contextMenu div:hover { background: var(--primary); color: #111; }
- .hidden { display: none; }
- </style>
- </head>
- <body>
- <h1>Prompt Generator</h1>
- <div class="toolbar">
- <button class="btn-secondary" onclick="document.getElementById('fileInput').click()">📁 Load Config</button>
- <button class="btn-secondary" onclick="saveConfig()">💾 Save Config</button>
- <input type="file" id="fileInput" class="hidden" accept=".cfg,.txt,.json" onchange="loadConfig(event)">
- </div>
- <div class="container">
- <!-- LEFT PANEL: Table -->
- <div class="left-panel">
- <label>1. Define Variables (Select for Variation)</label>
- <table id="grid">
- <thead>
- <tr>
- <th>Var</th>
- <th>Key Name</th>
- <th>Values (separated by /) (Weight with : e.g. Blonde:3)</th>
- <th>Action</th>
- </tr>
- </thead>
- <tbody id="gridBody">
- <!-- Standard Rows -->
- </tbody>
- </table>
- <button class="btn-secondary" onclick="addRow('', '')">+ Add Row</button>
- <div style="margin-top: 20px; display: flex; gap: 10px; align-items: center;">
- <button onclick="variationBatch()">Variation Batch (Selected Key)</button>
- </div>
- </div>
- <!-- RIGHT PANEL: Text fields -->
- <div class="right-panel">
- <label>2. Template (Right-click for Variables Menu)</label>
- <textarea id="memoTemplate"><Cameraangle> <Lights> photo of a <Age> old man with <*Haircolor> Hair standing next to his <Age> year old wife, also with <*Haircolor> Hair in <Location>. He is wearing a <Clothing>, she is wearing a <Clothing>.</textarea>
- <div style="display: flex; gap: 10px; align-items: center; margin: 10px 0;">
- <button onclick="generateSingle()">Generate Text</button>
- <button class="btn-secondary" onclick="saveOutput('RANDO.txt', document.getElementById('memoTemplate').value)">Save Template</button>
- <span style="margin-left:auto;">Batch Count:</span>
- <input type="number" id="spinEdit" value="500" min="1" max="20000" style="width: 70px;">
- <button onclick="generateBatch()">Generate Batch</button>
- </div>
- <label>3. Output</label>
- <textarea id="memoOutput" readonly></textarea>
- </div>
- </div>
- <!-- Custom Context Menu -->
- <div id="contextMenu"></div>
- <script>
- // --- INITIALIZATION ---
- window.onload = () => {
- addRow('Haircolor', 'Blue/Blonde:3/Brown:0.5');
- addRow('Clothing', 'Shirt/Sweater/T-Shirt');
- addRow('Age', '20/25/30/35');
- addRow('Location', 'Park/Pool/Bedroom/Stage/Supermarket');
- addRow('Cameraangle', 'High-Angle/Low-Angle/Medium-Shot/Close-Up');
- addRow('Lights', 'soft morning daylight/golden hour sunlight/harsh midday sun/afternoon glow/warm sunset');
- selectRow(0); // Select first row
- };
- let selectedRowIndex = -1;
- // --- TABLE LOGIC ---
- function addRow(key, values) {
- const tbody = document.getElementById('gridBody');
- const rowCount = tbody.children.length;
- const tr = document.createElement('tr');
- tr.innerHTML = `
- <td class="row-select" onclick="selectRow(${rowCount})"><input type="radio" name="rowSelector" id="radio_${rowCount}"></td>
- <td contenteditable="true" class="cell-key">${key}</td>
- <td contenteditable="true" class="cell-val">${values}</td>
- <td><button class="btn-secondary" style="padding: 2px 6px;" onclick="this.parentElement.parentElement.remove()">X</button></td>
- `;
- tbody.appendChild(tr);
- }
- function selectRow(index) {
- selectedRowIndex = index;
- const radio = document.getElementById(`radio_${index}`);
- if (radio) radio.checked = true;
- }
- function getGridData() {
- const rows = document.querySelectorAll('#gridBody tr');
- const data = [];
- rows.forEach(tr => {
- const key = tr.querySelector('.cell-key').innerText.trim();
- const val = tr.querySelector('.cell-val').innerText.trim();
- if (key !== '') {
- data.push({ key, val });
- }
- });
- return data;
- }
- // --- ROULETTE WHEEL SELECTION (Weighted Random Selection) ---
- function getRandomValue(valuesStr) {
- if (!valuesStr || valuesStr.trim() === '') return '';
- const parts = valuesStr.split('/');
- let items = [];
- let totalWeight = 0;
- for (let part of parts) {
- part = part.trim();
- let textVal = part;
- let weight = 1.0;
- const splitPos = part.indexOf(':');
- if (splitPos !== -1) {
- textVal = part.substring(0, splitPos).trim();
- let weightStr = part.substring(splitPos + 1).trim().replace(',', '.');
- let parsedWeight = parseFloat(weightStr);
- if (!isNaN(parsedWeight) && parsedWeight >= 0) {
- weight = parsedWeight;
- }
- }
- items.push({ text: textVal, weight: weight });
- totalWeight += weight;
- }
- if (totalWeight <= 0) {
- return items[Math.floor(Math.random() * items.length)].text;
- }
- let randomChoice = Math.random() * totalWeight;
- let runningSum = 0;
- for (let item of items) {
- runningSum += item.weight;
- if (randomChoice <= runningSum) {
- return item.text;
- }
- }
- return items[items.length - 1].text;
- }
- // --- TEXT GENERATION (<*Tag> and <Tag>) ---
- function generateTextBase(templateText, skipKey = "") {
- let resultText = templateText;
- const gridData = getGridData();
- for (let item of gridData) {
- const keyStr = item.key;
- const valStr = item.val;
- // Skip for Variation Batch
- if (skipKey !== "" && keyStr.toLowerCase() === skipKey.toLowerCase()) continue;
- // 1. Check if <*Key> exists (Fixation)
- const searchTagStar = `<*${keyStr}>`;
- if (resultText.includes(searchTagStar)) {
- // Roll once
- const replacement = getRandomValue(valStr);
- // Replace all <*Key>
- resultText = resultText.split(searchTagStar).join(replacement);
- // Replace all normal <Key> with the same value
- const searchTag = `<${keyStr}>`;
- resultText = resultText.split(searchTag).join(replacement);
- } else {
- // 2. Normal behavior: Roll new for each <Key>
- const searchTag = `<${keyStr}>`;
- let index = resultText.indexOf(searchTag);
- while (index !== -1) {
- const replacement = getRandomValue(valStr);
- resultText = resultText.substring(0, index) + replacement + resultText.substring(index + searchTag.length);
- index = resultText.indexOf(searchTag, index + replacement.length);
- }
- }
- }
- return resultText;
- }
- function generateSingle() {
- const template = document.getElementById('memoTemplate').value;
- const final = generateTextBase(template);
- document.getElementById('memoOutput').value = final;
- return final;
- }
- // --- BATCH GENERATION ---
- function generateBatch() {
- const amount = parseInt(document.getElementById('spinEdit').value);
- const template = document.getElementById('memoTemplate').value;
- let batchList = [];
- for (let i = 0; i < amount; i++) {
- let text = generateTextBase(template);
- text = text.replace(/\n/g, ' ').replace(/\r/g, ''); // Linebreaks to spaces
- batchList.push(text);
- }
- downloadFile('Randobatch.txt', batchList.join('\r\n'));
- alert(`${amount} texts generated and downloaded as Randobatch.txt!`);
- }
- // --- VARIATION BATCH ---
- function variationBatch() {
- const gridData = getGridData();
- const rows = document.querySelectorAll('#gridBody tr');
- // Find the selected row (Radio Button)
- let selectedKey = "";
- let selectedValuesStr = "";
- rows.forEach((tr, index) => {
- const radio = document.getElementById(`radio_${index}`);
- if (radio && radio.checked) {
- selectedKey = tr.querySelector('.cell-key').innerText.trim();
- selectedValuesStr = tr.querySelector('.cell-val').innerText.trim();
- }
- });
- if (selectedKey === "") {
- alert("Please select a key first using the radio button on the left in the table.");
- return;
- }
- const templateText = document.getElementById('memoTemplate').value;
- const searchTag = `<${selectedKey}>`;
- const searchTagStar = `<*${selectedKey}>`;
- if (!templateText.includes(searchTag) && !templateText.includes(searchTagStar)) {
- alert(`The tag <${selectedKey}> or <*${selectedKey}> does not appear in your template!`);
- return;
- }
- const valuesArray = selectedValuesStr.split('/');
- if (valuesArray.length === 0 || valuesArray[0] === "") {
- alert("No values found for variation.");
- return;
- }
- let baseText = "";
- let userAccepted = false;
- // Roll loop (corresponds to Retry in MessageDlg)
- while (!userAccepted) {
- baseText = generateTextBase(templateText, selectedKey);
- let msg = "The following text was generated as a fixed base:\n\n" +
- baseText + "\n\n" +
- "OK = Create Batch\nCancel = Reroll (or reload page to abort completely)";
- if (confirm(msg)) {
- userAccepted = true;
- }
- }
- let batchList = [];
- let labelList = [];
- for (let rawVal of valuesArray) {
- // For "Blonde:3", trim the weight for the label name
- let pureVal = rawVal.trim();
- if (pureVal.includes(':')) {
- pureVal = pureVal.substring(0, pureVal.indexOf(':')).trim();
- }
- // Replace both tag variants in BaseText
- let finalText = baseText.split(searchTag).join(pureVal);
- finalText = finalText.split(searchTagStar).join(pureVal);
- finalText = finalText.replace(/\n/g, ' ').replace(/\r/g, '');
- batchList.push(finalText);
- labelList.push(pureVal);
- }
- downloadFile('Variationsbatch.txt', batchList.join('\r\n'));
- downloadFile('Variationslabels.txt', labelList.join('\r\n'));
- alert(`${valuesArray.length} variations generated.\nTwo files have been downloaded (Prompts & Labels).`);
- }
- // --- DOWNLOAD HELPER ---
- function downloadFile(filename, text) {
- const element = document.createElement('a');
- element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
- element.setAttribute('download', filename);
- element.style.display = 'none';
- document.body.appendChild(element);
- element.click();
- document.body.removeChild(element);
- }
- function saveOutput(filename, content) {
- downloadFile(filename, content);
- }
- // --- LOAD & SAVE CONFIGURATION ---
- function saveConfig() {
- const data = {
- template: document.getElementById('memoTemplate').value,
- grid: getGridData()
- };
- downloadFile('config.cfg', JSON.stringify(data, null, 2));
- }
- function loadConfig(event) {
- const file = event.target.files[0];
- if (!file) return;
- const reader = new FileReader();
- reader.onload = function(e) {
- try {
- const data = JSON.parse(e.target.result);
- document.getElementById('memoTemplate').value = data.template;
- // Clear table and refill
- document.getElementById('gridBody').innerHTML = '';
- data.grid.forEach(item => addRow(item.key, item.val));
- if (data.grid.length > 0) selectRow(0);
- document.getElementById('fileInput').value = ""; // Reset input
- alert("Configuration successfully loaded!");
- } catch (err) {
- alert("Error reading file. Is it a valid .cfg (JSON)?");
- }
- };
- reader.readAsText(file);
- }
- // --- CONTEXT MENU (Right-click) ---
- const memo = document.getElementById('memoTemplate');
- const contextMenu = document.getElementById('contextMenu');
- memo.addEventListener('contextmenu', (e) => {
- e.preventDefault();
- const gridData = getGridData();
- contextMenu.innerHTML = ''; // Clear
- if (gridData.length === 0) {
- const div = document.createElement('div');
- div.innerText = "(No keys found)";
- contextMenu.appendChild(div);
- } else {
- gridData.forEach(item => {
- // Normal Key
- const divNorm = document.createElement('div');
- divNorm.innerText = `<${item.key}>`;
- divNorm.onclick = () => insertTextAtCursor(memo, `<${item.key}>`);
- contextMenu.appendChild(divNorm);
- // Fixed Key
- const divStar = document.createElement('div');
- divStar.innerText = `<*${item.key}> (Fixed)`;
- divStar.onclick = () => insertTextAtCursor(memo, `<*${item.key}>`);
- contextMenu.appendChild(divStar);
- });
- }
- contextMenu.style.display = 'flex';
- contextMenu.style.left = `${e.pageX}px`;
- contextMenu.style.top = `${e.pageY}px`;
- });
- // Hide Context Menu on click elsewhere
- document.addEventListener('click', () => {
- contextMenu.style.display = 'none';
- });
- // Helper: Inserts text at cursor position in text field
- function insertTextAtCursor(textarea, text) {
- const startPos = textarea.selectionStart;
- const endPos = textarea.selectionEnd;
- textarea.value = textarea.value.substring(0, startPos) + text + textarea.value.substring(endPos, textarea.value.length);
- textarea.selectionStart = startPos + text.length;
- textarea.selectionEnd = startPos + text.length;
- textarea.focus();
- }
- </script>
- </body>
- </html>
Add Comment
Please, Sign In to add comment