From 86a9d54e7095bd4c46b3d3ec926a302b1e63cb9f Mon Sep 17 00:00:00 2001 From: matt Date: Thu, 16 Oct 2025 22:28:42 -0700 Subject: [PATCH] feat: replace character creation prompts with themed modal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace native prompt() dialogs with a themed inline form modal for character creation: - Modal displays with name input and system prompt textarea - Includes Cancel and Create buttons - Escape key closes the modal - Auto-focuses name input when opened - Proper event listener cleanup - Matches existing UI theme and styling This provides a much better user experience with the application's dark theme, compared to the browser's default prompt dialogs which looked out of place. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/index.html | 45 +++++++++++++++++++++++++++++ src/main.js | 77 ++++++++++++++++++++++++++++++++++++++++++-------- src/styles.css | 63 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 12 deletions(-) diff --git a/src/index.html b/src/index.html index 4aa7f85..df4009a 100644 --- a/src/index.html +++ b/src/index.html @@ -740,5 +740,50 @@ Avatar + + + diff --git a/src/main.js b/src/main.js index f38626c..7ebb177 100644 --- a/src/main.js +++ b/src/main.js @@ -1773,20 +1773,66 @@ async function handleCharacterSwitch() { // Handle new character creation async function handleNewCharacter() { - const name = prompt('Enter a name for the new character:'); - if (!name) return; + const modal = document.getElementById('new-character-modal'); + const overlay = modal.querySelector('.new-character-overlay'); + const form = document.getElementById('new-character-form'); + const nameInput = document.getElementById('new-character-name'); + const systemPromptInput = document.getElementById('new-character-system-prompt'); + const closeBtn = document.getElementById('close-new-character-btn'); + const cancelBtn = document.getElementById('cancel-new-character-btn'); - const systemPrompt = prompt('Enter the system prompt for the new character:', 'You are a helpful AI assistant.'); - if (!systemPrompt) return; + // Reset form + form.reset(); + systemPromptInput.value = 'You are a helpful AI assistant.'; - try { - const newCharacter = await invoke('create_character', { name, systemPrompt }); - await loadCharacters(); - characterSelect.value = newCharacter.id; - } catch (error) { - console.error('Failed to create character:', error); - addMessage(`Failed to create character: ${error}`, false); - } + // Show modal + modal.style.display = 'flex'; + + // Focus name input after a brief delay to ensure it's visible + setTimeout(() => nameInput.focus(), 100); + + // Handle form submission + const handleSubmit = async (e) => { + e.preventDefault(); + + const name = nameInput.value.trim(); + const systemPrompt = systemPromptInput.value.trim(); + + if (!name || !systemPrompt) return; + + try { + const newCharacter = await invoke('create_character', { name, systemPrompt }); + await loadCharacters(); + characterSelect.value = newCharacter.id; + + // Hide modal + modal.style.display = 'none'; + + // Remove event listeners + form.removeEventListener('submit', handleSubmit); + overlay.removeEventListener('click', handleClose); + closeBtn.removeEventListener('click', handleClose); + cancelBtn.removeEventListener('click', handleClose); + } catch (error) { + console.error('Failed to create character:', error); + addMessage(`Failed to create character: ${error}`, false); + } + }; + + // Handle close + const handleClose = () => { + modal.style.display = 'none'; + form.removeEventListener('submit', handleSubmit); + overlay.removeEventListener('click', handleClose); + closeBtn.removeEventListener('click', handleClose); + cancelBtn.removeEventListener('click', handleClose); + }; + + // Attach event listeners + form.addEventListener('submit', handleSubmit); + overlay.addEventListener('click', handleClose); + closeBtn.addEventListener('click', handleClose); + cancelBtn.addEventListener('click', handleClose); } // Handle character deletion @@ -3528,6 +3574,13 @@ window.addEventListener('DOMContentLoaded', () => { document.addEventListener('keydown', (e) => { // Escape key handling if (e.key === 'Escape') { + // Close new character modal + const newCharacterModal = document.getElementById('new-character-modal'); + if (newCharacterModal && newCharacterModal.style.display !== 'none') { + document.getElementById('cancel-new-character-btn').click(); + return; + } + // Close avatar modal if (avatarModal.style.display !== 'none') { hideAvatarModal(); diff --git a/src/styles.css b/src/styles.css index adc094b..ba49cf0 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1450,6 +1450,69 @@ body { transform: scale(0.95); } +/* New Character Modal */ +.new-character-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 10001; + display: flex; + align-items: center; + justify-content: center; +} + +.new-character-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + cursor: pointer; +} + +.new-character-content { + position: relative; + z-index: 1; + background: var(--bg-primary); + border: 1px solid var(--border); + border-radius: 12px; + padding: 24px; + width: 90%; + max-width: 500px; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); +} + +.new-character-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + padding-bottom: 16px; + border-bottom: 1px solid var(--border); +} + +.new-character-header h3 { + font-size: 20px; + font-weight: 600; + color: var(--text-primary); + margin: 0; +} + +.new-character-actions { + display: flex; + gap: 12px; + margin-top: 20px; +} + +.new-character-actions button { + flex: 1; +} + /* Theme Preview */ .theme-preview-container { margin-top: 20px;