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" />
|
<img id="avatar-modal-img" src="" alt="Avatar" />
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
61
src/main.js
61
src/main.js
@@ -1773,20 +1773,66 @@ async function handleCharacterSwitch() {
|
|||||||
|
|
||||||
// Handle new character creation
|
// Handle new character creation
|
||||||
async function handleNewCharacter() {
|
async function handleNewCharacter() {
|
||||||
const name = prompt('Enter a name for the new character:');
|
const modal = document.getElementById('new-character-modal');
|
||||||
if (!name) return;
|
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.');
|
// Reset form
|
||||||
if (!systemPrompt) return;
|
form.reset();
|
||||||
|
systemPromptInput.value = 'You are a helpful AI assistant.';
|
||||||
|
|
||||||
|
// 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 {
|
try {
|
||||||
const newCharacter = await invoke('create_character', { name, systemPrompt });
|
const newCharacter = await invoke('create_character', { name, systemPrompt });
|
||||||
await loadCharacters();
|
await loadCharacters();
|
||||||
characterSelect.value = newCharacter.id;
|
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) {
|
} catch (error) {
|
||||||
console.error('Failed to create character:', error);
|
console.error('Failed to create character:', error);
|
||||||
addMessage(`Failed to create character: ${error}`, false);
|
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
|
// Handle character deletion
|
||||||
@@ -3528,6 +3574,13 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
// Escape key handling
|
// Escape key handling
|
||||||
if (e.key === 'Escape') {
|
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
|
// Close avatar modal
|
||||||
if (avatarModal.style.display !== 'none') {
|
if (avatarModal.style.display !== 'none') {
|
||||||
hideAvatarModal();
|
hideAvatarModal();
|
||||||
|
|||||||
@@ -1450,6 +1450,69 @@ body {
|
|||||||
transform: scale(0.95);
|
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 */
|
||||||
.theme-preview-container {
|
.theme-preview-container {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
|||||||
Reference in New Issue
Block a user