// promptBuilder.js const promptBuilderCSS = ` `; const promptBuilderHTML = `
📝
🔍
Start
`; export class PromptBuilder { constructor(container) { this.container = container; this.masterPrompts = []; this.currentPrompt = null; this.segments = []; this.savedSegments = []; this.promptMachines = []; this.universalRandomWords = {}; this.selectedSegments = new Set(); this.draggedSegment = null; this.dragOverIndex = null; this.processingSegmentId = null; this.WIDGET_TYPES = { STEADY_TEXT: 'Steady Text', STATIC_TEXTBOX: 'Static Textbox', DYNAMIC_TEXTBOX: 'Dynamic Textbox', DROPDOWN: 'Dropdown', DROPDOWN_TEXTBOX: 'Dropdown Textbox', CODE: 'Code Widget', OPTION: 'Option Widget', CATALOGUE: 'Catalogue Display' }; this.SOURCE_TYPES = { NONE: 'None', SEGMENT_ABOVE: 'Segment Above', HIGHLIGHTED: 'Highlighted Text', FULL_DOCUMENT: 'Full Document', TEXT_INPUT: 'Text Input', FILE_LINK: 'File Link', CATALOGUE: 'Catalogue Item' }; this.init(); } init() { this.container.innerHTML = promptBuilderCSS + promptBuilderHTML; this.initElements(); this.attachEventListeners(); this.loadData(); } initElements() { this.promptIcon = document.getElementById('promptIcon'); this.promptSelect = document.getElementById('promptSelect'); this.groupFilter = document.getElementById('groupFilter'); this.typeFilter = document.getElementById('typeFilter'); this.newPromptBtn = document.getElementById('newPromptBtn'); this.createMachineBtn = document.getElementById('createMachineBtn'); this.saveBtn = document.getElementById('saveBtn'); this.uploadBtn = document.getElementById('uploadBtn'); this.buildBtn = document.getElementById('buildBtn'); this.promptCanvas = document.getElementById('promptCanvas'); this.segmentsContainer = document.getElementById('segmentsContainer'); this.addSegmentBtn = document.getElementById('addSegmentBtn'); this.addNewBtn = document.getElementById('addNewBtn'); this.addExistingBtn = document.getElementById('addExistingBtn'); this.enabledCount = document.getElementById('enabledCount'); this.totalCount = document.getElementById('totalCount'); this.selectedCount = document.getElementById('selectedCount'); this.machineCount = document.getElementById('machineCount'); } attachEventListeners() { this.promptSelect.addEventListener('change', () => this.loadSelectedPrompt()); this.groupFilter.addEventListener('change', () => this.filterPrompts()); this.typeFilter.addEventListener('change', () => this.filterPrompts()); this.newPromptBtn.addEventListener('click', () => this.showNewPromptModal()); this.createMachineBtn.addEventListener('click', () => this.showPromptMachineModal()); this.saveBtn.addEventListener('click', () => this.saveData()); this.buildBtn.addEventListener('click', () => this.buildAndSendPrompt()); this.addSegmentBtn.addEventListener('click', () => this.showNewSegmentModal()); this.addNewBtn.addEventListener('click', () => this.showNewSegmentModal()); this.addExistingBtn.addEventListener('click', () => this.showExistingSegmentModal()); } async loadData() { try { let data; if (window.pywebview?.api?.load_json) { data = await window.pywebview.api.load_json('box/jsons/promptBuilder.json'); } else { const saved = localStorage.getItem('promptBuilderData'); data = saved ? JSON.parse(saved) : null; } if (data) { this.masterPrompts = data.masterPrompts || []; this.savedSegments = data.savedSegments || []; this.universalRandomWords = data.universalRandomWords || {}; this.updateFilters(); this.updatePromptSelect(); if (this.masterPrompts.length > 0) { this.currentPrompt = this.masterPrompts[0]; this.segments = this.currentPrompt.segments || []; this.promptSelect.value = this.currentPrompt.id; this.renderSegments(); } } } catch (error) { console.error('Error loading data:', error); } } async saveData() { const dataToSave = { masterPrompts: this.masterPrompts, savedSegments: this.savedSegments, universalRandomWords: this.universalRandomWords, version: '3.0' }; try { if (window.pywebview?.api?.save_json) { await window.pywebview.api.save_json('box/jsons/promptBuilder.json', dataToSave); } else { localStorage.setItem('promptBuilderData', JSON.stringify(dataToSave)); } alert('Saved successfully!'); } catch (error) { console.error('Error saving data:', error); alert('Error saving data'); } } updateFilters() { // Update group filter const groups = [...new Set(this.masterPrompts.map(p => p.group).filter(Boolean))]; this.groupFilter.innerHTML = ''; groups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; this.groupFilter.appendChild(option); }); // Update type filter const types = [...new Set(this.masterPrompts.flatMap(p => p.types || []))]; this.typeFilter.innerHTML = ''; types.forEach(type => { const option = document.createElement('option'); option.value = type; option.textContent = type; this.typeFilter.appendChild(option); }); } updatePromptSelect() { const groupFilter = this.groupFilter.value; const typeFilter = this.typeFilter.value; const filtered = this.masterPrompts.filter(p => { const matchesGroup = groupFilter === 'all' || p.group === groupFilter; const matchesType = typeFilter === 'all' || (p.types && p.types.includes(typeFilter)); return matchesGroup && matchesType; }); this.promptSelect.innerHTML = ''; filtered.forEach(prompt => { const option = document.createElement('option'); option.value = prompt.id; option.textContent = prompt.title; this.promptSelect.appendChild(option); }); } filterPrompts() { this.updatePromptSelect(); } loadSelectedPrompt() { const promptId = this.promptSelect.value; if (!promptId) return; this.currentPrompt = this.masterPrompts.find(p => p.id === promptId); if (this.currentPrompt) { this.segments = this.currentPrompt.segments || []; this.promptIcon.textContent = this.currentPrompt.icon || '📝'; this.renderSegments(); } } renderSegments() { this.segmentsContainer.innerHTML = ''; this.segments.forEach((segment, index) => { const segmentEl = this.createSegmentElement(segment, index); this.segmentsContainer.appendChild(segmentEl); }); this.updateFooterInfo(); } createSegmentElement(segment, index) { const wrapper = document.createElement('div'); wrapper.className = 'segment-wrapper'; wrapper.dataset.segmentId = segment.id; wrapper.dataset.index = index; // Top connector if (index > 0) { const topConnector = document.createElement('div'); topConnector.className = 'segment-connector top'; if (this.processingSegmentId === segment.id) { topConnector.classList.add('processing'); } wrapper.appendChild(topConnector); } // Main segment const segmentDiv = document.createElement('div'); segmentDiv.className = 'segment'; if (this.selectedSegments.has(segment.id)) { segmentDiv.classList.add('selected'); } if (!segment.enabled) { segmentDiv.classList.add('disabled'); } if (this.processingSegmentId === segment.id) { segmentDiv.classList.add('processing'); } // Handle segmentDiv.innerHTML = `
${segment.widgetType}
${this.renderWidget(segment)}
${this.renderSources(segment)}
${this.renderOutputOptions(segment)}
${segment.name || ''}
`; wrapper.appendChild(segmentDiv); // Bottom connector if (index < this.segments.length - 1) { const bottomConnector = document.createElement('div'); bottomConnector.className = 'segment-connector bottom'; wrapper.appendChild(bottomConnector); } // Attach segment-specific event listeners this.attachSegmentEventListeners(segmentDiv, segment, index); return wrapper; } renderWidget(segment) { const sourceData = segment.sourcesData?.[0] || ''; switch (segment.widgetType) { case this.WIDGET_TYPES.STEADY_TEXT: return `
${this.escapeHtml(sourceData)}
`; case this.WIDGET_TYPES.DYNAMIC_TEXTBOX: return ``; case this.WIDGET_TYPES.DROPDOWN: const options = sourceData.split('\n').filter(o => o.trim()); return ``; case this.WIDGET_TYPES.DROPDOWN_TEXTBOX: const ddOptions = sourceData.split('\n').filter(o => o.trim()); const firstOption = ddOptions[0] || ''; const [title, text] = firstOption.split(':').map(s => s.trim()); return ` `; case this.WIDGET_TYPES.CODE: return ``; default: return `
Widget: ${segment.widgetType}
`; } } renderSources(segment) { return (segment.sourceTypes || []).map((sourceType, idx) => { let badge = ''; switch (sourceType) { case this.SOURCE_TYPES.SEGMENT_ABOVE: badge = '⬆️ From Above'; break; case this.SOURCE_TYPES.TEXT_INPUT: badge = '📝 Text'; break; case this.SOURCE_TYPES.FILE_LINK: badge = ''; break; case this.SOURCE_TYPES.HIGHLIGHTED: badge = '🔍 Selected'; break; case this.SOURCE_TYPES.FULL_DOCUMENT: badge = '📄 Full Doc'; break; case this.SOURCE_TYPES.CATALOGUE: badge = '📚 Catalogue'; break; default: badge = sourceType; } return `
${badge}
`; }).join(''); } renderOutputOptions(segment) { return ` ${segment.outputOptions?.store ? ` ` : ''} `; } attachSegmentEventListeners(segmentEl, segment, index) { // Click to select (with shift) segmentEl.addEventListener('click', (e) => { if (e.shiftKey) { this.toggleSegmentSelection(segment.id); } }); // Drag handle const handle = segmentEl.querySelector('.segment-handle'); handle.addEventListener('dragstart', (e) => { this.draggedSegment = index; this.promptCanvas.classList.add('zoomed-out'); e.dataTransfer.effectAllowed = 'move'; }); handle.addEventListener('dragend', () => { this.draggedSegment = null; this.dragOverIndex = null; this.promptCanvas.classList.remove('zoomed-out'); this.renderSegments(); }); // Control deck button const cdBtn = segmentEl.querySelector('.control-deck-btn'); cdBtn.addEventListener('click', (e) => { e.stopPropagation(); segment.includeInControlDeck = !segment.includeInControlDeck; this.updateCurrentPrompt(); this.renderSegments(); }); // Segment controls segmentEl.querySelectorAll('.segment-control-btn').forEach(btn => { btn.addEventListener('click', (e) => { e.stopPropagation(); const action = btn.dataset.action; switch (action) { case 'save': this.saveSegment(segment); break; case 'delete': this.deleteSegment(segment.id); break; case 'minimize': segment.minimized = !segment.minimized; this.updateCurrentPrompt(); this.renderSegments(); break; case 'toggle': segment.enabled = !segment.enabled; this.updateCurrentPrompt(); this.renderSegments(); break; } }); }); // Widget editable text const editableDiv = segmentEl.querySelector('[data-editable]'); if (editableDiv) { editableDiv.addEventListener('dblclick', () => { const textarea = document.createElement('textarea'); textarea.className = 'widget-textarea'; textarea.rows = 4; textarea.value = segment.sourcesData[0] || ''; editableDiv.replaceWith(textarea); textarea.focus(); textarea.addEventListener('blur', () => { segment.sourcesData[0] = textarea.value; this.updateCurrentPrompt(); this.renderSegments(); }); }); } // Widget data changes segmentEl.querySelectorAll('[data-segment-data]').forEach(input => { input.addEventListener('input', () => { const idx = parseInt(input.dataset.segmentData); segment.sourcesData[idx] = input.value; this.updateCurrentPrompt(); }); }); // Dropdown changes segmentEl.querySelectorAll('[data-segment-dropdown]').forEach(select => { select.addEventListener('change', () => { // Store selected value if needed }); }); // Dropdown-textbox const ddTextbox = segmentEl.querySelector('[data-segment-dropdown-textbox]'); const ddTextarea = segmentEl.querySelector('[data-segment-textbox]'); if (ddTextbox && ddTextarea) { ddTextbox.addEventListener('change', () => { const [title] = ddTextbox.value.split(':'); ddTextarea.value = ddTextbox.value.split(':')[1]?.trim() || ''; }); ddTextarea.addEventListener('input', () => { const title = ddTextbox.value.split(':')[0]; // Update combined value if needed }); } // Output options segmentEl.querySelectorAll('[data-output]').forEach(input => { input.addEventListener('change', () => { const outputType = input.dataset.output; if (input.type === 'checkbox') { if (outputType === 'randomize') { segment.randomize = input.checked; } else { segment.outputOptions[outputType] = input.checked; } } else { segment.outputOptions[outputType] = input.value; } this.updateCurrentPrompt(); if (outputType === 'store') { this.renderSegments(); // Re-render to show/hide store name input } }); }); // Source badges (for text input editing) segmentEl.querySelectorAll('.source-badge[data-source-idx]').forEach(badge => { badge.addEventListener('dblclick', () => { const idx = parseInt(badge.dataset.sourceIdx); const sourceType = segment.sourceTypes[idx]; if (sourceType === this.SOURCE_TYPES.TEXT_INPUT) { const textarea = document.createElement('textarea'); textarea.className = 'widget-textarea'; textarea.rows = 3; textarea.style.fontSize = '0.75rem'; textarea.value = segment.sourcesData[idx] || ''; badge.replaceWith(textarea); textarea.focus(); textarea.addEventListener('blur', () => { segment.sourcesData[idx] = textarea.value; this.updateCurrentPrompt(); this.renderSegments(); }); } }); }); // File upload buttons segmentEl.querySelectorAll('[data-upload-idx]').forEach(btn => { btn.addEventListener('click', async () => { const idx = parseInt(btn.dataset.uploadIdx); await this.handleFileUpload(segment.id, idx); }); }); // Drag over for reordering const wrapper = segmentEl.parentElement; wrapper.addEventListener('dragover', (e) => { e.preventDefault(); if (this.draggedSegment !== null && this.draggedSegment !== index) { this.dragOverIndex = index; this.showDropIndicator(wrapper); } }); wrapper.addEventListener('drop', (e) => { e.preventDefault(); if (this.draggedSegment !== null && this.draggedSegment !== index) { this.reorderSegments(this.draggedSegment, index); } }); } showDropIndicator(wrapper) { // Remove existing indicators document.querySelectorAll('.drop-indicator').forEach(el => el.remove()); // Add new indicator const indicator = document.createElement('div'); indicator.className = 'drop-indicator'; wrapper.appendChild(indicator); } reorderSegments(fromIndex, toIndex) { const [removed] = this.segments.splice(fromIndex, 1); this.segments.splice(toIndex, 0, removed); this.updateCurrentPrompt(); this.renderSegments(); } toggleSegmentSelection(segmentId) { if (this.selectedSegments.has(segmentId)) { this.selectedSegments.delete(segmentId); } else { this.selectedSegments.add(segmentId); } this.selectedCount.textContent = this.selectedSegments.size; if (this.selectedSegments.size > 1) { this.createMachineBtn.classList.remove('hidden'); } else { this.createMachineBtn.classList.add('hidden'); } this.renderSegments(); } saveSegment(segment) { const existingIndex = this.savedSegments.findIndex(s => s.name === segment.name); if (existingIndex >= 0) { this.savedSegments[existingIndex] = { ...segment }; } else { this.savedSegments.push({ ...segment }); } alert('Segment saved to library!'); this.saveData(); } deleteSegment(segmentId) { if (confirm('Delete this segment?')) { this.segments = this.segments.filter(s => s.id !== segmentId); this.updateCurrentPrompt(); this.renderSegments(); } } updateCurrentPrompt() { if (this.currentPrompt) { this.currentPrompt.segments = this.segments; const promptIndex = this.masterPrompts.findIndex(p => p.id === this.currentPrompt.id); if (promptIndex >= 0) { this.masterPrompts[promptIndex] = this.currentPrompt; } } } updateFooterInfo() { const enabled = this.segments.filter(s => s.enabled).length; this.enabledCount.textContent = enabled; this.totalCount.textContent = this.segments.length; if (this.promptMachines.length > 0) { this.machineCount.textContent = `• ${this.promptMachines.length}machines`; } else { this.machineCount.textContent = ''; } } async handleFileUpload(segmentId, sourceIndex) { try { if (window.pywebview?.api?.upload_file) { const filePath = await window.pywebview.api.upload_file(); if (filePath) { const segment = this.segments.find(s => s.id === segmentId); if (segment) { segment.sourcesData[sourceIndex] = filePath; this.updateCurrentPrompt(); this.renderSegments(); } } } else { alert('File upload not available in this environment'); } } catch (error) { console.error('Error uploading file:', error); } } // Modal methods showNewPromptModal() { const modal = this.createModal('New Master Prompt', 'small', `
`); document.getElementById('modalCreateBtn').addEventListener('click', () => { const title = document.getElementById('modalPromptTitle').value; const icon = document.getElementById('modalPromptIcon').value; const types = document.getElementById('modalPromptTypes').value.split(',').map(t => t.trim()); const group = document.getElementById('modalPromptGroup').value; if (title) { const newPrompt = { id: Date.now().toString(), title, icon, types, group, segments: [] }; this.masterPrompts.push(newPrompt); this.currentPrompt = newPrompt; this.segments = []; this.updateFilters(); this.updatePromptSelect(); this.promptSelect.value = newPrompt.id; this.loadSelectedPrompt(); this.closeModal(); } }); } showNewSegmentModal() { const modal = this.createModal('New Segment', 'large', `
${Object.entries(this.SOURCE_TYPES).map(([key, value]) => ` `).join('')}
`); document.getElementById('modalCreateSegmentBtn').addEventListener('click', () => { const name = document.getElementById('modalSegmentName').value; const widgetType = document.getElementById('modalWidgetType').value; const sourceData = document.getElementById('modalSourceData').value; const checkedBoxes = document.querySelectorAll('#modalSourceTypes input:checked'); const sourceTypes = Array.from(checkedBoxes).map(cb => cb.value); if (name && sourceTypes.length > 0) { const newSegment = { id: Date.now().toString(), name, widgetType, sourceTypes, sourcesData: sourceTypes.map(() => sourceData), enabled: true, minimized: false, includeInControlDeck: false, randomize: false, outputOptions: { sendDown: true, store: false, feedProcessor: true, storeName: '' } }; this.segments.push(newSegment); this.updateCurrentPrompt(); this.renderSegments(); this.closeModal(); } else { alert('Please enter a name and select at least one source type'); } }); } showExistingSegmentModal() { const segmentsList = this.savedSegments.length === 0 ? '
No saved segments yet
' : this.savedSegments.map(segment => `
${segment.name}
${segment.widgetType}
`).join(''); const modal = this.createModal('Add Existing Segment', 'large', segmentsList); document.querySelectorAll('[data-segment-id]').forEach(btn => { btn.addEventListener('click', () => { const segmentId = btn.dataset.segmentId; const segment = this.savedSegments.find(s => s.id === segmentId); if (segment) { const newSegment = { ...segment, id: Date.now().toString() }; this.segments.push(newSegment); this.updateCurrentPrompt(); this.renderSegments(); this.closeModal(); } }); }); } showPromptMachineModal() { const modal = this.createModal('Create Prompt Machine', 'medium', `
${this.selectedSegments.size} segments selected
`); document.getElementById('modalCreateMachineBtn').addEventListener('click', () => { const title = document.getElementById('modalMachineTitle').value; const description = document.getElementById('modalMachineDesc').value; if (title) { const machine = { id: Date.now().toString(), title, description, segmentIds: Array.from(this.selectedSegments) }; this.promptMachines.push(machine); this.selectedSegments.clear(); this.createMachineBtn.classList.add('hidden'); this.updateFooterInfo(); this.renderSegments(); this.closeModal(); alert('Prompt Machine created!'); } }); } createModal(title, size, content) { const overlay = document.createElement('div'); overlay.className = 'modal-overlay'; overlay.id = 'modalOverlay'; overlay.innerHTML = ` `; document.body.appendChild(overlay); document.getElementById('modalClose').addEventListener('click', () => this.closeModal()); overlay.addEventListener('click', (e) => { if (e.target === overlay) this.closeModal(); }); return overlay; } closeModal() { const modal = document.getElementById('modalOverlay'); if (modal) modal.remove(); } // Build and send prompt async buildAndSendPrompt() { if (!this.currentPrompt) { alert('Please select a prompt first'); return; } let builtPrompt = ''; let previousOutput = ''; for (let i = 0; i < this.segments.length; i++) { const segment = this.segments[i]; if (!segment.enabled) { // Pass through continue; } this.processingSegmentId = segment.id; this.renderSegments(); await new Promise(resolve => setTimeout(resolve, 500)); let segmentOutput = ''; // Get source data let sourceText = ''; for (let j = 0; j < segment.sourceTypes.length; j++) { const sourceType = segment.sourceTypes[j]; const sourceData = segment.sourcesData[j] || ''; if (sourceType === this.SOURCE_TYPES.SEGMENT_ABOVE && previousOutput) { sourceText = previousOutput; break; } else if (sourceType === this.SOURCE_TYPES.TEXT_INPUT && sourceData) { sourceText = sourceData; break; } else if (sourceType === this.SOURCE_TYPES.FILE_LINK && sourceData) { sourceText = sourceData; // Would load file content here break; } else if (sourceType === this.SOURCE_TYPES.HIGHLIGHTED) { if (window.EditorSelection?.getUserSelection) { sourceText = window.EditorSelection.getUserSelection(); break; } } else if (sourceType === this.SOURCE_TYPES.FULL_DOCUMENT) { if (window.getFullDocumentText) { sourceText = window.getFullDocumentText(); break; } } } // Process mini-widgets |randomizer| segmentOutput = this.processMiniWidgets(sourceText); if (segment.outputOptions.feedProcessor) { builtPrompt += segmentOutput + '\n\n'; } if (segment.outputOptions.sendDown) { previousOutput = segmentOutput; } if (segment.outputOptions.store && segment.outputOptions.storeName) { // Store for later retrieval window[`stored_${segment.outputOptions.storeName}`] = segmentOutput; } } this.processingSegmentId = null; this.renderSegments(); // Send to LLM console.log('Built Prompt:', builtPrompt); if (window.sendLLMPrompt) { try { await window.sendLLMPrompt(builtPrompt, 'builtPrompts', this.currentPrompt.title); } catch (error) { console.error('Error sending to LLM:', error); } } else { alert('Prompt built! Check console for output.'); } } processMiniWidgets(text) { return text.replace(/\|([^|]+)\|/g, (match, name) => { const randomWord = this.universalRandomWords[name]; if (randomWord?.options?.length > 0) { return randomWord.options[Math.floor(Math.random() * randomWord.options.length)]; } return match; }); } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } }