diff --git a/README.md b/README.md
index 060a51c..d2ba5d2 100644
--- a/README.md
+++ b/README.md
@@ -57,8 +57,12 @@ Config stored in `~/.config/claudia/config.json`
- **Enter** - Send message
- **Shift+Enter** - New line
-- **Up Arrow** - Edit last user message
-- **Left/Right Arrow** - Swipe between responses
+- **Ctrl+Enter** - Send message (alternative)
+- **Up Arrow** - Edit last user message (when input is at start)
+- **Left/Right Arrow** - Navigate between response alternatives
+- **Escape** - Close panels/modals, cancel editing
+- **Ctrl+K** - Focus message input
+- **Ctrl+/** - Toggle Roleplay Tools panel
## Roadmap
diff --git a/src/main.js b/src/main.js
index 7eb9738..4c32e05 100644
--- a/src/main.js
+++ b/src/main.js
@@ -481,6 +481,27 @@ function addMessage(content, isUser = false, skipActions = false, timestamp = nu
editBtn.addEventListener('click', () => handleEditMessage(messageDiv, content));
actionsDiv.appendChild(editBtn);
+ // Copy message button
+ const copyBtn = document.createElement('button');
+ copyBtn.className = 'message-action-btn';
+ copyBtn.innerHTML = ``;
+ copyBtn.title = 'Copy message';
+ copyBtn.addEventListener('click', () => {
+ navigator.clipboard.writeText(content);
+ // Visual feedback
+ const originalHTML = copyBtn.innerHTML;
+ copyBtn.innerHTML = ``;
+ setTimeout(() => {
+ copyBtn.innerHTML = originalHTML;
+ }, 2000);
+ });
+ actionsDiv.appendChild(copyBtn);
+
// Pin button
const pinBtn = document.createElement('button');
pinBtn.className = 'message-action-btn message-pin-btn';
@@ -557,6 +578,27 @@ function addMessage(content, isUser = false, skipActions = false, timestamp = nu
hideBtn.addEventListener('click', () => handleToggleHidden(messageDiv));
actionsDiv.appendChild(hideBtn);
+ // Copy message button
+ const copyMsgBtn = document.createElement('button');
+ copyMsgBtn.className = 'message-action-btn';
+ copyMsgBtn.innerHTML = ``;
+ copyMsgBtn.title = 'Copy message';
+ copyMsgBtn.addEventListener('click', () => {
+ navigator.clipboard.writeText(content);
+ // Visual feedback
+ const originalHTML = copyMsgBtn.innerHTML;
+ copyMsgBtn.innerHTML = ``;
+ setTimeout(() => {
+ copyMsgBtn.innerHTML = originalHTML;
+ }, 2000);
+ });
+ actionsDiv.appendChild(copyMsgBtn);
+
// Delete button
const deleteBtn = document.createElement('button');
deleteBtn.className = 'message-action-btn message-delete-btn';
@@ -3337,9 +3379,98 @@ window.addEventListener('DOMContentLoaded', () => {
avatarModalOverlay.addEventListener('click', hideAvatarModal);
// ESC key to close modal
+ // Global keyboard shortcuts
document.addEventListener('keydown', (e) => {
- if (e.key === 'Escape' && avatarModal.style.display !== 'none') {
- hideAvatarModal();
+ // Escape key handling
+ if (e.key === 'Escape') {
+ // Close avatar modal
+ if (avatarModal.style.display !== 'none') {
+ hideAvatarModal();
+ return;
+ }
+
+ // Close roleplay panel
+ const roleplayPanel = document.getElementById('roleplay-panel');
+ if (roleplayPanel && roleplayPanel.classList.contains('active')) {
+ document.getElementById('close-roleplay-btn').click();
+ return;
+ }
+
+ // Close settings panel
+ const settingsPanel = document.getElementById('settings-panel');
+ if (settingsPanel && settingsPanel.classList.contains('active')) {
+ document.getElementById('close-settings-btn').click();
+ return;
+ }
+
+ // Cancel message editing
+ const editActions = document.querySelector('.message-edit-actions');
+ if (editActions) {
+ const cancelBtn = editActions.querySelector('.message-edit-cancel');
+ if (cancelBtn) cancelBtn.click();
+ return;
+ }
+ }
+
+ // Up Arrow - Edit last user message (when input is focused and empty/at start)
+ if (e.key === 'ArrowUp' && e.target === messageInput && messageInput.selectionStart === 0) {
+ const messages = document.querySelectorAll('.message.user');
+ if (messages.length > 0) {
+ const lastUserMessage = messages[messages.length - 1];
+ const editBtn = lastUserMessage.querySelector('.message-action-btn[title="Edit message"]');
+ if (editBtn) {
+ e.preventDefault();
+ editBtn.click();
+ }
+ }
+ return;
+ }
+
+ // Left Arrow - Previous swipe (when not in input)
+ if (e.key === 'ArrowLeft' && e.target !== messageInput && !e.target.matches('input, textarea, select')) {
+ const lastAssistantMessage = [...document.querySelectorAll('.message.assistant')].pop();
+ if (lastAssistantMessage) {
+ const prevBtn = lastAssistantMessage.querySelector('.swipe-prev');
+ if (prevBtn && !prevBtn.disabled) {
+ e.preventDefault();
+ prevBtn.click();
+ }
+ }
+ return;
+ }
+
+ // Right Arrow - Next swipe (when not in input)
+ if (e.key === 'ArrowRight' && e.target !== messageInput && !e.target.matches('input, textarea, select')) {
+ const lastAssistantMessage = [...document.querySelectorAll('.message.assistant')].pop();
+ if (lastAssistantMessage) {
+ const nextBtn = lastAssistantMessage.querySelector('.swipe-next');
+ if (nextBtn && !nextBtn.disabled) {
+ e.preventDefault();
+ nextBtn.click();
+ }
+ }
+ return;
+ }
+
+ // Ctrl/Cmd + K - Focus message input
+ if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
+ e.preventDefault();
+ messageInput.focus();
+ return;
+ }
+
+ // Ctrl/Cmd + / - Toggle roleplay panel
+ if ((e.ctrlKey || e.metaKey) && e.key === '/') {
+ e.preventDefault();
+ document.getElementById('roleplay-btn').click();
+ return;
+ }
+
+ // Ctrl/Cmd + Enter - Send message (alternative to Enter)
+ if ((e.ctrlKey || e.metaKey) && e.key === 'Enter' && e.target === messageInput) {
+ e.preventDefault();
+ handleSubmit(e);
+ return;
}
});