feat: replace character creation prompts with themed modal
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 <noreply@anthropic.com>
This commit is contained in:
@@ -740,5 +740,50 @@
|
||||
<img id="avatar-modal-img" src="" alt="Avatar" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- New Character modal -->
|
||||
<div id="new-character-modal" class="new-character-modal" style="display: none;">
|
||||
<div class="new-character-overlay"></div>
|
||||
<div class="new-character-content">
|
||||
<div class="new-character-header">
|
||||
<h3>Create New Character</h3>
|
||||
<button id="close-new-character-btn" class="icon-btn">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<line x1="4" y1="4" x2="12" y2="12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<line x1="12" y1="4" x2="4" y2="12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<form id="new-character-form">
|
||||
<div class="form-group">
|
||||
<label for="new-character-name">Character Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="new-character-name"
|
||||
placeholder="Enter a name for the new character"
|
||||
required
|
||||
autofocus
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new-character-system-prompt">System Prompt</label>
|
||||
<textarea
|
||||
id="new-character-system-prompt"
|
||||
placeholder="You are a helpful AI assistant..."
|
||||
rows="6"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="new-character-actions">
|
||||
<button type="button" id="cancel-new-character-btn" class="btn-secondary">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn-primary">
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
77
src/main.js
77
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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user